[ELF] Fix functionality of merging similar strings.
authorShankar Easwaran <shankarke@gmail.com>
Mon, 20 Oct 2014 02:59:06 +0000 (02:59 +0000)
committerShankar Easwaran <shankarke@gmail.com>
Mon, 20 Oct 2014 02:59:06 +0000 (02:59 +0000)
For PC relative accesses, negative addends were to be ignored. The linker was
not ignoring it and would fail with an assert. This fixes the issue and is able
to get Helloworld working.

llvm-svn: 220179

lld/lib/ReaderWriter/ELF/ELFFile.h
lld/test/elf/X86_64/mergesimilarstrings.test [new file with mode: 0644]

index a240bcf..39e3755 100644 (file)
@@ -52,11 +52,11 @@ template <class ELFT> class ELFFile : public File {
   // A Map is used to hold the atoms that have been divided up
   // after reading the section that contains Merge String attributes
   struct MergeSectionKey {
-    MergeSectionKey(const Elf_Shdr *shdr, int32_t offset)
+    MergeSectionKey(const Elf_Shdr *shdr, int64_t offset)
         : _shdr(shdr), _offset(offset) {}
     // Data members
     const Elf_Shdr *_shdr;
-    int32_t _offset;
+    int64_t _offset;
   };
   struct MergeSectionEq {
     int64_t operator()(const MergeSectionKey &k) const {
@@ -71,12 +71,12 @@ template <class ELFT> class ELFFile : public File {
   };
 
   struct MergeString {
-    MergeString(int32_t offset, StringRef str, const Elf_Shdr *shdr,
+    MergeString(int64_t offset, StringRef str, const Elf_Shdr *shdr,
                 StringRef sectionName)
         : _offset(offset), _string(str), _shdr(shdr),
           _sectionName(sectionName) {}
     // the offset of this atom
-    int32_t _offset;
+    int64_t _offset;
     // The content
     StringRef _string;
     // Section header
@@ -92,13 +92,13 @@ template <class ELFT> class ELFFile : public File {
   /// \brief find a mergeAtom given a start offset
   struct FindByOffset {
     const Elf_Shdr *_shdr;
-    uint64_t _offset;
-    FindByOffset(const Elf_Shdr *shdr, uint64_t offset)
+    int64_t _offset;
+    FindByOffset(const Elf_Shdr *shdr, int64_t offset)
         : _shdr(shdr), _offset(offset) {}
     bool operator()(const ELFMergeAtom<ELFT> *a) {
-      uint64_t off = a->offset();
+      int64_t off = a->offset();
       return (_shdr->sh_name == a->section()) &&
-             ((_offset >= off) && (_offset <= off + a->size()));
+             ((_offset >= off) && (_offset <= off + (int64_t)a->size()));
     }
   };
 
@@ -180,6 +180,12 @@ protected:
   /// Reference's target with the Atom pointer it refers to.
   virtual void updateReferences();
 
+  /// \brief Update the reference if the access corresponds to a merge string
+  /// section.
+  void updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref,
+                                           const Elf_Sym *symbol,
+                                           const Elf_Shdr *shdr);
+
   /// \brief Return true if the symbol is corresponding to an architecture
   /// specific section. We will let the TargetHandler handle such atoms.
   virtual bool isTargetSpecificAtom(const Elf_Shdr *shdr, const Elf_Sym *sym);
@@ -793,6 +799,35 @@ void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym &symbol,
   }
 }
 
+template <class ELFT>
+void ELFFile<ELFT>::updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref,
+                                                        const Elf_Sym *symbol,
+                                                        const Elf_Shdr *shdr) {
+  // If the target atom is mergeable strefng atom, the atom might have been
+  // merged with other atom having the same contents. Try to find the
+  // merged one if that's the case.
+  int64_t addend = ref->addend();
+  if (addend < 0)
+    addend = 0;
+
+  const MergeSectionKey ms(shdr, addend);
+  auto msec = _mergedSectionMap.find(ms);
+  if (msec != _mergedSectionMap.end()) {
+    ref->setTarget(msec->second);
+    return;
+  }
+
+  // The target atom was not merged. Mergeable atoms are not in
+  // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We
+  // instead call findMergeAtom().
+  if (symbol->getType() != llvm::ELF::STT_SECTION)
+    addend = symbol->st_value + addend;
+  ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend);
+  ref->setOffset(addend - mergedAtom->offset());
+  ref->setAddend(0);
+  ref->setTarget(mergedAtom);
+}
+
 template <class ELFT> void ELFFile<ELFT>::updateReferences() {
   for (auto &ri : _references) {
     if (ri->kindNamespace() == lld::Reference::KindNamespace::ELF) {
@@ -805,27 +840,7 @@ template <class ELFT> void ELFFile<ELFT>::updateReferences() {
         ri->setTarget(findAtom(symbol));
         continue;
       }
-
-      // If the target atom is mergeable string atom, the atom might have been
-      // merged with other atom having the same contents. Try to find the
-      // merged one if that's the case.
-      uint64_t addend = ri->addend();
-      const MergeSectionKey ms(shdr, addend);
-      auto msec = _mergedSectionMap.find(ms);
-      if (msec != _mergedSectionMap.end()) {
-        ri->setTarget(msec->second);
-        continue;
-      }
-
-      // The target atom was not merged. Mergeable atoms are not in
-      // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We
-      // instead call findMergeAtom().
-      if (symbol->getType() != llvm::ELF::STT_SECTION)
-        addend = symbol->st_value + addend;
-      ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend);
-      ri->setOffset(addend - mergedAtom->offset());
-      ri->setAddend(0);
-      ri->setTarget(mergedAtom);
+      updateReferenceForMergeStringAccess(ri, symbol, shdr);
     }
   }
 }
diff --git a/lld/test/elf/X86_64/mergesimilarstrings.test b/lld/test/elf/X86_64/mergesimilarstrings.test
new file mode 100644 (file)
index 0000000..5c868ac
--- /dev/null
@@ -0,0 +1,40 @@
+# Check that relocations to section that contains strings is properly handled
+# when merging strings is enabled.
+#
+# RUN: yaml2obj -format=elf %s > %t.o
+# RUN: lld -flavor gnu -target x86_64 %t.o --noinhibit-exec -o %t1.out
+# RUN: llvm-readobj -sections %t1.out | FileCheck %s
+
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x04
+    Content:         54889e5488d3d00000000e80000000088d3d00000000e800000000b8000000005dc3
+  - Name:            .rela.text
+    Type:            SHT_RELA
+    Link:            .symtab
+    AddressAlign:    0x04
+    Info:            .text
+    Relocations:
+      - Offset:          0x07
+        Symbol:          .rodata
+        Type:            R_X86_64_PC32
+        Addend:          -4
+  - Name:            .rodata
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x01
+    Content:         48656c6c6f20576f726c6400576f726c6400
+Symbols:
+  Global:
+    - Name:            .rodata
+      Section:         .rodata
+
+#CHECK:    Name: .rodata
+#CHECK:    Size: 18