From cd0c3ea964473e05a4bbee1348507a77c9ac3707 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
Date: Wed, 26 Aug 2020 15:47:04 +0300
Subject: [PATCH] [LLD] [COFF] Check the aux section definition size for
 IMAGE_COMDAT_SELECT_SAME_SIZE

Binutils generated sections seem to be padded to a multiple of 4 bytes,
but the section definition contains the original, unpadded section
length.

This fixes statically linking clang-built object files with C++
typeinfo against libstdc++ built with GCC.
---
 lld/COFF/Chunks.cpp                          |  2 +-
 lld/COFF/Chunks.h                            |  4 +++
 lld/COFF/InputFiles.cpp                      | 18 ++++++++----
 lld/COFF/InputFiles.h                        |  7 +++--
 lld/test/COFF/Inputs/comdat-binutils.yaml    | 30 ++++++++++++++++++++
 lld/test/COFF/Inputs/comdat-llvm.yaml        | 30 ++++++++++++++++++++
 lld/test/COFF/comdat-gcc-compatibility2.test | 15 ++++++++++
 7 files changed, 96 insertions(+), 10 deletions(-)
 create mode 100644 lld/test/COFF/Inputs/comdat-binutils.yaml
 create mode 100644 lld/test/COFF/Inputs/comdat-llvm.yaml
 create mode 100644 lld/test/COFF/comdat-gcc-compatibility2.test

diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index b4b6b1cca03..3ff52c2a143 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -55,7 +55,7 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
 // SectionChunk is one of the most frequently allocated classes, so it is
 // important to keep it as compact as possible. As of this writing, the number
 // below is the size of this class on x64 platforms.
-static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly");
+static_assert(sizeof(SectionChunk) <= 96, "SectionChunk grew unexpectedly");
 
 static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }
 static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); }
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 0528143383c..f0555a4b39f 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -311,6 +311,10 @@ public:
   // Auxiliary Format 5: Section Definitions. Used for ICF.
   uint32_t checksum = 0;
 
+  // The section chunk length as defined in the section aux definition,
+  // if available.
+  uint32_t sectionDefLength = 0;
+
   // Used by the garbage collector.
   bool live;
 
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 71af41da4d6..79c0086b01d 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -275,8 +275,10 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
   if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
     return nullptr;
   auto *c = make<SectionChunk>(this, sec);
-  if (def)
+  if (def) {
     c->checksum = def->CheckSum;
+    c->sectionDefLength = def->Length;
+  }
 
   // CodeView sections are stored to a different vector because they are not
   // linked in the regular manner.
@@ -476,8 +478,10 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
   return symtab->addUndefined(name, this, sym.isWeakExternal());
 }
 
-void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
-                                    bool &prevailing, DefinedRegular *leader) {
+void ObjFile::handleComdatSelection(
+    COFFSymbolRef sym, COMDATType &selection, bool &prevailing,
+    DefinedRegular *leader,
+    const llvm::object::coff_aux_section_definition *def) {
   if (prevailing)
     return;
   // There's already an existing comdat for this symbol: `Leader`.
@@ -544,8 +548,10 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
     break;
 
   case IMAGE_COMDAT_SELECT_SAME_SIZE:
-    if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData)
-      symtab->reportDuplicate(leader, this);
+    if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
+      if (!config->mingw || leaderChunk->sectionDefLength != def->Length)
+        symtab->reportDuplicate(leader, this);
+    }
     break;
 
   case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
@@ -661,7 +667,7 @@ Optional<Symbol *> ObjFile::createDefined(
     COMDATType selection = (COMDATType)def->Selection;
 
     if (leader->isCOMDAT)
-      handleComdatSelection(sym, selection, prevailing, leader);
+      handleComdatSelection(sym, selection, prevailing, leader, def);
 
     if (prevailing) {
       SectionChunk *c = readSection(sectionNumber, def, getName());
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 1e0b97a82be..0a5114b165f 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -255,9 +255,10 @@ private:
   // match the existing symbol and its selection. If either old or new
   // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace
   // the existing leader. In that case, Prevailing is set to true.
-  void handleComdatSelection(COFFSymbolRef sym,
-                             llvm::COFF::COMDATType &selection,
-                             bool &prevailing, DefinedRegular *leader);
+  void
+  handleComdatSelection(COFFSymbolRef sym, llvm::COFF::COMDATType &selection,
+                        bool &prevailing, DefinedRegular *leader,
+                        const llvm::object::coff_aux_section_definition *def);
 
   llvm::Optional<Symbol *>
   createDefined(COFFSymbolRef sym,
diff --git a/lld/test/COFF/Inputs/comdat-binutils.yaml b/lld/test/COFF/Inputs/comdat-binutils.yaml
new file mode 100644
index 00000000000..70fff87e1a4
--- /dev/null
+++ b/lld/test/COFF/Inputs/comdat-binutils.yaml
@@ -0,0 +1,30 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [ IMAGE_FILE_RELOCS_STRIPPED, IMAGE_FILE_LINE_NUMS_STRIPPED ]
+sections:
+  - Name:            '.rdata$mysymbol'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+    Alignment:       16
+    SectionData:     2A000000000000000000000000000000
+symbols:
+  - Name:            '.rdata$mysymbol'
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          1
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+      Selection:       IMAGE_COMDAT_SELECT_SAME_SIZE
+  - Name:            mysymbol
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/comdat-llvm.yaml b/lld/test/COFF/Inputs/comdat-llvm.yaml
new file mode 100644
index 00000000000..eef117a9233
--- /dev/null
+++ b/lld/test/COFF/Inputs/comdat-llvm.yaml
@@ -0,0 +1,30 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.rdata$mysymbol'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+    Alignment:       1
+    SectionData:     2A
+symbols:
+  - Name:            '.rdata$mysymbol'
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          1
+      NumberOfRelocations: 0
+      NumberOfLinenumbers: 0
+      CheckSum:        3686517206
+      Number:          1
+      Selection:       IMAGE_COMDAT_SELECT_SAME_SIZE
+  - Name:            mysymbol
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/comdat-gcc-compatibility2.test b/lld/test/COFF/comdat-gcc-compatibility2.test
new file mode 100644
index 00000000000..49386764636
--- /dev/null
+++ b/lld/test/COFF/comdat-gcc-compatibility2.test
@@ -0,0 +1,15 @@
+# RUN: yaml2obj %p/Inputs/comdat-llvm.yaml > %t.llvm.o
+# RUN: yaml2obj %p/Inputs/comdat-binutils.yaml > %t.binutils.o
+# RUN: lld-link -lldmingw -noentry -dll %t.llvm.o %t.binutils.o -out:%t.dll
+# RUN: lld-link -lldmingw -noentry -dll %t.binutils.o %t.llvm.o -out:%t.dll
+# RUN: not lld-link -noentry -dll %t.llvm.o %t.binutils.o -out:%t.dll
+# RUN: not lld-link -noentry -dll %t.binutils.o %t.llvm.o -out:%t.dll
+
+# The test object files have been generated by assembling the following
+# snippet using binutils and llvm.
+
+#        .section .rdata$mysymbol, "dr"
+#        .linkonce same_size
+#        .globl mysymbol
+#mysymbol:
+#        .byte 42
-- 
2.17.1

