[ELF] Add .gnu.linkonce support.
authorShankar Easwaran <shankare@codeaurora.org>
Mon, 23 Feb 2015 00:04:49 +0000 (00:04 +0000)
committerShankar Easwaran <shankare@codeaurora.org>
Mon, 23 Feb 2015 00:04:49 +0000 (00:04 +0000)
When the GNU linker sees two input sections with the same name, and the name
starts with ".gnu.linkonce.", the linker will only keep one copy and discard the
other. Any section whose name starts with “.gnu.linkonce.” is a COMDAT section.

Some architectures like Hexagon use this section to store floating point constants,
that need be deduped.

This patch adds gnu.linkonce functionality to the ELFReader.

llvm-svn: 230194

lld/lib/ReaderWriter/ELF/Atoms.h
lld/lib/ReaderWriter/ELF/ELFFile.h
lld/lib/ReaderWriter/ELF/SectionChunks.h
lld/lib/ReaderWriter/ELF/TODO.txt
lld/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test [new file with mode: 0644]
lld/test/elf/gnulinkonce/gnulinkonce-report-undef.test [new file with mode: 0644]
lld/test/elf/gnulinkonce/gnulinkonce.test [new file with mode: 0644]

index 7860086..b80c469 100644 (file)
@@ -179,14 +179,16 @@ public:
   uint64_t size() const override {
     // Common symbols are not allocated in object files,
     // so use st_size to tell how many bytes are required.
-    if ((_symbol->getType() == llvm::ELF::STT_COMMON) ||
-        _symbol->st_shndx == llvm::ELF::SHN_COMMON)
+    if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON ||
+                    _symbol->st_shndx == llvm::ELF::SHN_COMMON))
       return (uint64_t) _symbol->st_size;
 
     return _contentData.size();
   }
 
   Scope scope() const override {
+    if (!_symbol)
+      return scopeGlobal;
     if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN)
       return scopeLinkageUnit;
     else if (_symbol->getBinding() != llvm::ELF::STB_LOCAL)
@@ -199,6 +201,9 @@ public:
   Interposable interposable() const override { return interposeNo; }
 
   Merge merge() const override {
+    if (!_symbol)
+      return mergeNo;
+
     if (_symbol->getBinding() == llvm::ELF::STB_WEAK)
       return mergeAsWeak;
 
@@ -216,6 +221,9 @@ public:
     ContentType ret = typeUnknown;
     uint64_t flags = _section->sh_flags;
 
+    if (!_symbol && _sectionName.startswith(".gnu.linkonce"))
+      return typeGnuLinkOnce;
+
     if (!(flags & llvm::ELF::SHF_ALLOC))
       return _contentType = typeNoAlloc;
 
@@ -286,6 +294,9 @@ public:
   }
 
   Alignment alignment() const override {
+    if (!_symbol)
+      return Alignment(0);
+
     // Obtain proper value of st_value field.
     const auto symValue = getSymbolValue(_symbol);
 
@@ -323,7 +334,7 @@ public:
 
   StringRef customSectionName() const override {
     if ((contentType() == typeZeroFill) ||
-        (_symbol->st_shndx == llvm::ELF::SHN_COMMON))
+        (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON))
       return ".bss";
     return _sectionName;
   }
index d50c091..6d1ac49 100644 (file)
@@ -168,8 +168,23 @@ public:
     return _absoluteAtoms;
   }
 
-  Atom *findAtom(const Elf_Sym *symbol) {
-    return _symbolToAtomMapping.lookup(symbol);
+  Atom *findAtom(const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) {
+    // All references to atoms inside a group are through undefined atoms.
+    Atom *targetAtom = _symbolToAtomMapping.lookup(targetSymbol);
+    if (targetAtom->definition() != Atom::definitionRegular)
+      return targetAtom;
+    if ((llvm::dyn_cast<DefinedAtom>(targetAtom))->scope() ==
+        DefinedAtom::scopeTranslationUnit)
+      return targetAtom;
+    if (!redirectReferenceUsingUndefAtom(sourceSymbol, targetSymbol))
+      return targetAtom;
+    auto undefForGroupchild = _undefAtomsForgroupChild.find(targetAtom->name());
+    if (undefForGroupchild != _undefAtomsForgroupChild.end())
+      return undefForGroupchild->getValue();
+    auto undefGroupChildAtom =
+        new (_readerStorage) SimpleUndefinedAtom(*this, targetAtom->name());
+    _undefinedAtoms._atoms.push_back(undefGroupChildAtom);
+    return (_undefAtomsForgroupChild[targetAtom->name()] = undefGroupChildAtom);
   }
 
 protected:
@@ -258,6 +273,12 @@ protected:
     return shdr && (shdr->sh_type == llvm::ELF::SHT_PROGBITS) && syms.empty();
   }
 
+  /// Handle creation of atoms for .gnu.linkonce sections.
+  std::error_code handleGnuLinkOnceSection(
+      StringRef sectionName,
+      llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection,
+      const Elf_Shdr *shdr);
+
   /// Process the Undefined symbol and create an atom for it.
   ErrorOr<ELFUndefinedAtom<ELFT> *>
   handleUndefinedSymbol(StringRef symName, const Elf_Sym *sym) {
@@ -286,6 +307,11 @@ protected:
            symbol->st_shndx == llvm::ELF::SHN_COMMON;
   }
 
+  /// Returns true if the section is a gnulinkonce section.
+  bool isGnuLinkOnceSection(StringRef sectionName) const {
+    return sectionName.startswith(".gnu.linkonce");
+  }
+
   /// Returns correct st_value for the symbol depending on the architecture.
   /// For most architectures it's just a regular st_value with no changes.
   virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const {
@@ -333,6 +359,10 @@ protected:
     return mergeAtom;
   }
 
+  /// Does the atom need to be redirected using a separate undefined atom ?
+  bool redirectReferenceUsingUndefAtom(const Elf_Sym *sourceSymbol,
+                                       const Elf_Sym *targetSymbol) const;
+
   llvm::BumpPtrAllocator _readerStorage;
   std::unique_ptr<llvm::object::ELFFile<ELFT> > _objFile;
   atom_collection_vector<DefinedAtom> _definedAtoms;
@@ -350,6 +380,11 @@ protected:
   std::unordered_map<StringRef, range<Elf_Rel_Iter>> _relocationReferences;
   std::vector<ELFReference<ELFT> *> _references;
   llvm::DenseMap<const Elf_Sym *, Atom *> _symbolToAtomMapping;
+  // Group child atoms have a pair corresponding to the signature and the
+  // section header of the section that was used for generating the signature.
+  llvm::DenseMap<const Elf_Sym *, std::pair<StringRef, const Elf_Shdr *>>
+      _groupChild;
+  llvm::StringMap<Atom *> _undefAtomsForgroupChild;
 
   /// \brief Atoms that are created for a section that has the merge property
   /// set
@@ -623,6 +658,11 @@ std::error_code ELFFile<ELFT>::createSymbolsFromAtomizableSections() {
 }
 
 template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() {
+  // Holds all the atoms that are part of the section. They are the targets of
+  // the kindGroupChild reference.
+  llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> atomsForSection;
+  // group sections have a mapping of the section header to the signature.
+  llvm::DenseMap<const Elf_Shdr *, StringRef> groupSections;
   for (auto &i : _sectionSymbols) {
     const Elf_Shdr *section = i.first;
     std::vector<Elf_Sym_Iter> &symbols = i.second;
@@ -641,11 +681,21 @@ template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() {
     if (std::error_code ec = sectionContents.getError())
       return ec;
 
+    bool addAtoms = true;
+
+    if (isGnuLinkOnceSection(*sectionName)) {
+      groupSections.insert(std::make_pair(section, *sectionName));
+      addAtoms = false;
+    }
+
     if (handleSectionWithNoSymbols(section, symbols)) {
       ELFDefinedAtom<ELFT> *newAtom =
           createSectionAtom(section, *sectionName, *sectionContents);
-      _definedAtoms._atoms.push_back(newAtom);
       newAtom->setOrdinal(++_ordinal);
+      if (addAtoms)
+        _definedAtoms._atoms.push_back(newAtom);
+      else
+        atomsForSection[*sectionName].push_back(newAtom);
       continue;
     }
 
@@ -693,8 +743,11 @@ template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() {
           auto definedMergeAtom = handleDefinedSymbol(
               symbolName, *sectionName, &**si, section, symbolData,
               _references.size(), _references.size(), _references);
-          _definedAtoms._atoms.push_back(*definedMergeAtom);
           (*definedMergeAtom)->setOrdinal(++_ordinal);
+          if (addAtoms)
+            _definedAtoms._atoms.push_back(*definedMergeAtom);
+          else
+            atomsForSection[*sectionName].push_back(*definedMergeAtom);
         }
         continue;
       }
@@ -740,19 +793,60 @@ template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() {
       // is a weak atom.
       previousAtom = anonAtom ? anonAtom : newAtom;
 
-      _definedAtoms._atoms.push_back(newAtom);
+      if (addAtoms)
+        _definedAtoms._atoms.push_back(newAtom);
+      else
+        atomsForSection[*sectionName].push_back(newAtom);
+
       _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom));
       if (anonAtom) {
         anonAtom->setOrdinal(++_ordinal);
-        _definedAtoms._atoms.push_back(anonAtom);
+        if (addAtoms)
+          _definedAtoms._atoms.push_back(anonAtom);
+        else
+          atomsForSection[*sectionName].push_back(anonAtom);
       }
     }
   }
 
+  // Iterate over all the group sections to create parent atoms pointing to
+  // group-child atoms.
+  for (auto &sect : groupSections) {
+    StringRef signature = sect.second;
+    if (isGnuLinkOnceSection(signature))
+      handleGnuLinkOnceSection(signature, atomsForSection, sect.first);
+  }
+
   updateReferences();
   return std::error_code();
 }
 
+template <class ELFT>
+std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection(
+    StringRef signature,
+    llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection,
+    const Elf_Shdr *shdr) {
+  unsigned int referenceStart = _references.size();
+  std::vector<ELFReference<ELFT> *> refs;
+  for (auto ha : atomsForSection[signature]) {
+    _groupChild[ha->symbol()] = std::make_pair(signature, shdr);
+    ELFReference<ELFT> *ref =
+        new (_readerStorage) ELFReference<ELFT>(lld::Reference::kindGroupChild);
+    ref->setTarget(ha);
+    refs.push_back(ref);
+  }
+  atomsForSection[signature].clear();
+  // Create a gnu linkonce atom.
+  auto gnuLinkOnceAtom = handleDefinedSymbol(
+      signature, signature, nullptr, shdr, ArrayRef<uint8_t>(), referenceStart,
+      _references.size(), _references);
+  (*gnuLinkOnceAtom)->setOrdinal(++_ordinal);
+  _definedAtoms._atoms.push_back(*gnuLinkOnceAtom);
+  for (auto reference : refs)
+    (*gnuLinkOnceAtom)->addReference(reference);
+  return std::error_code();
+}
+
 template <class ELFT> std::error_code ELFFile<ELFT>::createAtomsFromContext() {
   if (!_useWrap)
     return std::error_code();
@@ -882,7 +976,7 @@ template <class ELFT> void ELFFile<ELFT>::updateReferences() {
       // If the atom is not in mergeable string section, the target atom is
       // simply that atom.
       if (!isMergeableStringSection(shdr)) {
-        ri->setTarget(findAtom(symbol));
+        ri->setTarget(findAtom(ri->symbol(), symbol));
         continue;
       }
       updateReferenceForMergeStringAccess(ri, symbol, shdr);
@@ -954,6 +1048,24 @@ void ELFFile<ELFT>::createEdge(ELFDefinedAtom<ELFT> *from,
   from->addReference(reference);
 }
 
+/// Does the atom need to be redirected using a separate undefined atom ?
+template <class ELFT>
+bool ELFFile<ELFT>::redirectReferenceUsingUndefAtom(
+    const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) const {
+  auto groupChild = _groupChild.find(targetSymbol);
+
+  // If the reference is not to a group child atom, there is no need to redirect
+  // using a undefined atom.
+  if (groupChild == _groupChild.end())
+    return false;
+
+  if (sourceSymbol->st_shndx != targetSymbol->st_shndx) {
+    return true;
+  }
+
+  return false;
+}
+
 } // end namespace elf
 } // end namespace lld
 
index 785c942..e7d8587 100644 (file)
@@ -361,6 +361,9 @@ const lld::AtomLayout &AtomSection<ELFT>::appendAtom(const Atom *atom) {
     _atoms.push_back(new (_alloc) lld::AtomLayout(atom, mOffset, 0));
     this->_msize = mOffset + definedAtom->size();
     break;
+  case DefinedAtom::typeGnuLinkOnce:
+    // Discard gnu linkonce atoms as they are just used to identify signature.
+    break;
   default:
     llvm::dbgs() << definedAtom->contentType() << "\n";
     llvm_unreachable("Uexpected content type.");
index 7047692..aa35c23 100644 (file)
@@ -14,6 +14,4 @@ lib/ReaderWriter/ELF
 
 - Section Groups.
 
-- Gnu linkonce sections.
-
 - Fix section flags as they appear in input (update content permissions)
diff --git a/lld/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test b/lld/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test
new file mode 100644 (file)
index 0000000..8caffd4
--- /dev/null
@@ -0,0 +1,147 @@
+# Tests that the linker is able to read .gnu.linkonce sections and link them
+# appropriately. The testcase has been created by using the following source
+# code.
+# TODO: This test should produce a discarded reference error message which it
+# doesnot currently.
+# linkoncea.s
+#        .section .gnu.linkonce.d.dummy,"aw"
+#bar:
+#        .long    0
+# linkonceb.s
+#        .section .gnu.linkonce.d.dummy,"aw"
+#foo:
+#        .long    0
+#        .section .blah, "aw"
+#        .long    foo
+#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o
+#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o
+#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml
+#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN: --noinhibit-exec -o %t2.out
+#RUN: FileCheck %s -check-prefix=CHECKGNULINKONCE < %t2.out.yaml
+#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGNULINKONCESECTIONS
+#CHECKGNULINKONCE: - name:            .gnu.linkonce.d.dummy
+#CHECKGNULINKONCE:   scope:           global
+#CHECKGNULINKONCE:   type:            gnu-linkonce
+#CHECKGNULINKONCE:   section-choice:  custom-required
+#CHECKGNULINKONCE:   section-name:    .gnu.linkonce.d.dummy
+#CHECKGNULINKONCE:   permissions:     rw-
+#CHECKGNULINKONCE:   references:
+#CHECKGNULINKONCE:     - kind:            group-child
+#CHECKGNULINKONCE:       offset:          0
+#CHECKGNULINKONCE:       target:          bar
+#CHECKGNULINKONCESECTIONS:   Section {
+#CHECKGNULINKONCESECTIONS:     Name: .gnu.linkonce.d.dummy
+#CHECKGNULINKONCESECTIONS:     Type: SHT_PROGBITS
+#CHECKGNULINKONCESECTIONS:     Flags [ (0x3)
+#CHECKGNULINKONCESECTIONS:       SHF_ALLOC (0x2)
+#CHECKGNULINKONCESECTIONS:       SHF_WRITE (0x1)
+#CHECKGNULINKONCESECTIONS:     ]
+#CHECKGNULINKONCESECTIONS:     Size: 4
+#CHECKGNULINKONCESECTIONS:   }
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+Symbols:
+  Local:
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            bar
+      Section:         .gnu.linkonce.d.dummy
+...
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+  - Name:            .blah
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+  - Name:            .rela.blah
+    Type:            SHT_RELA
+    Link:            .symtab
+    AddressAlign:    0x0000000000000008
+    Info:            .blah
+    Relocations:
+      - Offset:          0x0000000000000000
+        Symbol:          foo
+        Type:            R_X86_64_32
+Symbols:
+  Local:
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            foo
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            .blah
+      Type:            STT_SECTION
+      Section:         .blah
+...
diff --git a/lld/test/elf/gnulinkonce/gnulinkonce-report-undef.test b/lld/test/elf/gnulinkonce/gnulinkonce-report-undef.test
new file mode 100644 (file)
index 0000000..c6d050d
--- /dev/null
@@ -0,0 +1,129 @@
+# Tests that the linker is able to read .gnu.linkonce sections and link them
+# appropriately. The testcase has been created by using the following source
+# code. This test checks that the linker produces an undefined error.
+# linkoncea.s
+#        .section .gnu.linkonce.d.dummy,"aw"
+#bar:
+#        .long    0
+# linkonceb.s
+#        .section .gnu.linkonce.d.dummy,"aw"
+#        .global foo
+#foo:
+#        .long    0
+#        .section .blah, "aw"
+#        .long    foo
+#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o
+#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o
+#RUN: not lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN:  --output-filetype=yaml -o %t2.out.yaml 2>&1 | FileCheck \
+#RUN: -check-prefix=UNDEFS %s
+#RUN: not lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN: -o %t2.out 2>&1 | FileCheck -check-prefix=UNDEFS %s
+#UNDEFS: Undefined symbol: {{.*}} foo
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+Symbols:
+  Local:
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            bar
+      Section:         .gnu.linkonce.d.dummy
+...
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  OSABI:           ELFOSABI_GNU
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+  - Name:            .blah
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+  - Name:            .rela.blah
+    Type:            SHT_RELA
+    Link:            .symtab
+    AddressAlign:    0x0000000000000008
+    Info:            .blah
+    Relocations:
+      - Offset:          0x0000000000000000
+        Symbol:          foo
+        Type:            R_X86_64_32
+Symbols:
+  Local:
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            .blah
+      Type:            STT_SECTION
+      Section:         .blah
+  Global:
+    - Name:            foo
+      Section:         .gnu.linkonce.d.dummy
+...
diff --git a/lld/test/elf/gnulinkonce/gnulinkonce.test b/lld/test/elf/gnulinkonce/gnulinkonce.test
new file mode 100644 (file)
index 0000000..17559f6
--- /dev/null
@@ -0,0 +1,151 @@
+# Tests that the linker is able to read .gnu.linkonce sections and link them
+# appropriately. The testcase has been created by using the following source
+# code
+# linkonce1a.s
+# ------------
+#        .section .gnu.linkonce.d.dummy,"aw"
+#bar:
+#        .long    0
+# linkonce1b.s
+# ------------
+#    .globl main
+#    .globl start
+#    .globl _start
+#    .globl __start
+#    .text
+#main:
+#start:
+#_start:
+#__start:
+#    .long    0
+#
+#        .section .gnu.linkonce.d.dummy,"aw"
+#foo:
+#        .long    0
+#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o
+#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o
+#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml
+#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \
+#RUN: --noinhibit-exec -o %t2.out
+#RUN: FileCheck %s -check-prefix=CHECKGNULINKONCE < %t2.out.yaml
+#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGNULINKONCESECTIONS
+#CHECKGNULINKONCE: - name:            .gnu.linkonce.d.dummy
+#CHECKGNULINKONCE:   scope:           global
+#CHECKGNULINKONCE:   type:            gnu-linkonce
+#CHECKGNULINKONCE:   section-choice:  custom-required
+#CHECKGNULINKONCE:   section-name:    .gnu.linkonce.d.dummy
+#CHECKGNULINKONCE:   permissions:     rw-
+#CHECKGNULINKONCE:   references:
+#CHECKGNULINKONCE:     - kind:            group-child
+#CHECKGNULINKONCE:       offset:          0
+#CHECKGNULINKONCE:       target:          bar
+#CHECKGNULINKONCE:     - kind:            group-child
+#CHECKGNULINKONCE:       offset:          0
+#CHECKGNULINKONCESECTIONS:   Section {
+#CHECKGNULINKONCESECTIONS:     Name: .gnu.linkonce.d.dummy
+#CHECKGNULINKONCESECTIONS:     Type: SHT_PROGBITS
+#CHECKGNULINKONCESECTIONS:     Flags [ (0x3)
+#CHECKGNULINKONCESECTIONS:       SHF_ALLOC (0x2)
+#CHECKGNULINKONCESECTIONS:       SHF_WRITE (0x1)
+#CHECKGNULINKONCESECTIONS:     ]
+#CHECKGNULINKONCESECTIONS:     Size: 4
+#CHECKGNULINKONCESECTIONS:   }
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  OSABI:           ELFOSABI_GNU
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+Symbols:
+  Local:
+    - Name:            bar
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+...
+---
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000004
+    Content:         '00000000'
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000004
+    Content:         ''
+  - Name:            .gnu.linkonce.d.dummy
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000001
+    Content:         '00000000'
+Symbols:
+  Local:
+    - Name:            .text
+      Type:            STT_SECTION
+      Section:         .text
+    - Name:            .data
+      Type:            STT_SECTION
+      Section:         .data
+    - Name:            .bss
+      Type:            STT_SECTION
+      Section:         .bss
+    - Name:            .gnu.linkonce.d.dummy
+      Type:            STT_SECTION
+      Section:         .gnu.linkonce.d.dummy
+    - Name:            foo
+      Section:         .gnu.linkonce.d.dummy
+  Global:
+    - Name:            main
+      Section:         .text
+    - Name:            start
+      Section:         .text
+    - Name:            _start
+      Section:         .text
+    - Name:            __start
+      Section:         .text
+...