[lld] r174155 - [ELF][x86-64] Implement static relocation model for TLS.

Michael J. Spencer bigcheesegs at gmail.com
Thu Jan 31 23:14:15 PST 2013


Author: mspencer
Date: Fri Feb  1 01:14:14 2013
New Revision: 174155

URL: http://llvm.org/viewvc/llvm-project?rev=174155&view=rev
Log:
[ELF][x86-64] Implement static relocation model for TLS.

This implements the static relocation model for GOT accesses to TLS.

Added:
    lld/trunk/test/elf/Inputs/tls.S
    lld/trunk/test/elf/Inputs/undef-gotpcrel.S
    lld/trunk/test/elf/Inputs/undef-gotpcrel.x86-64
    lld/trunk/test/elf/undef-gotpcrel.test
Modified:
    lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp
    lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetInfo.cpp
    lld/trunk/test/elf/Inputs/tls.x86-64
    lld/trunk/test/elf/ifunc.test
    lld/trunk/test/elf/tls.test

Modified: lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp?rev=174155&r1=174154&r2=174155&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp (original)
+++ lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp Fri Feb  1 01:14:14 2013
@@ -73,6 +73,7 @@ ErrorOr<void> X86_64TargetRelocationHand
   case R_X86_64_32S:
     reloc32S(location, relocVAddress, targetVAddress, ref.addend());
     break;
+  case R_X86_64_TPOFF64:
   case R_X86_64_TPOFF32: {
     // Get the start and end of the TLS segment.
     if (_tlsSize == 0) {
@@ -87,8 +88,13 @@ ErrorOr<void> X86_64TargetRelocationHand
       if (tbss)
         _tlsSize += tbss->memSize();
     }
-    int32_t result = (int32_t)(targetVAddress - _tlsSize);
-    *reinterpret_cast<llvm::support::little32_t *>(location) = result;
+    if (ref.kind() == R_X86_64_TPOFF32) {
+      int32_t result = (int32_t)(targetVAddress - _tlsSize);
+      *reinterpret_cast<llvm::support::little32_t *>(location) = result;
+    } else {
+      int64_t result = (int64_t)(targetVAddress - _tlsSize);
+      *reinterpret_cast<llvm::support::little64_t *>(location) = result;
+    }
     break;
   }
   // Runtime only relocations. Ignore here.

Modified: lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetInfo.cpp?rev=174155&r1=174154&r2=174155&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetInfo.cpp (original)
+++ lld/trunk/lib/ReaderWriter/ELF/X86_64/X86_64TargetInfo.cpp Fri Feb  1 01:14:14 2013
@@ -21,23 +21,22 @@
 using namespace lld;
 
 namespace {
+using namespace llvm::ELF;
+
 class GOTAtom : public SimpleDefinedAtom {
   static const uint8_t _defaultContent[8];
+  StringRef _section;
 
 public:
-  GOTAtom(const File &f, const DefinedAtom *target) : SimpleDefinedAtom(f) {
-    if (target->contentType() == typeResolver) {
-      DEBUG_WITH_TYPE("GOTAtom", llvm::dbgs() << "IRELATIVE relocation to "
-                                              << target->name());
-      addReference(llvm::ELF::R_X86_64_IRELATIVE, 0, target, 0);
-    }
+  GOTAtom(const File &f, StringRef secName)
+      : SimpleDefinedAtom(f), _section(secName) {
   }
 
-  virtual StringRef name() const { return "ELF-GOTAtom"; }
+  virtual Scope scope() const { return scopeTranslationUnit; }
 
   virtual SectionChoice sectionChoice() const { return sectionCustomRequired; }
 
-  virtual StringRef customSectionName() const { return ".got.plt"; }
+  virtual StringRef customSectionName() const { return _section; }
 
   virtual ContentType contentType() const { return typeGOT; }
 
@@ -48,23 +47,32 @@ public:
   virtual ArrayRef<uint8_t> rawContent() const {
     return ArrayRef<uint8_t>(_defaultContent, 8);
   }
+
+#ifndef NDEBUG
+  virtual StringRef name() const { return _name; }
+
+  std::string _name;
+#else
+  virtual StringRef name() const { return ""; }
+#endif
 };
 
 const uint8_t GOTAtom::_defaultContent[8] = { 0 };
 
 class PLTAtom : public SimpleDefinedAtom {
   static const uint8_t _defaultContent[16];
+  StringRef _section;
 
 public:
-  PLTAtom(const File &f, GOTAtom *ga) : SimpleDefinedAtom(f) {
-    addReference(llvm::ELF::R_X86_64_PC32, 2, ga, -4);
+  PLTAtom(const File &f, StringRef secName)
+      : SimpleDefinedAtom(f), _section(secName) {
   }
 
-  virtual StringRef name() const { return "ELF-PLTAtom"; }
+  virtual Scope scope() const { return scopeTranslationUnit; }
 
   virtual SectionChoice sectionChoice() const { return sectionCustomRequired; }
 
-  virtual StringRef customSectionName() const { return ".plt"; }
+  virtual StringRef customSectionName() const { return _section; }
 
   virtual ContentType contentType() const { return typeStub; }
 
@@ -75,6 +83,14 @@ public:
   virtual ArrayRef<uint8_t> rawContent() const {
     return ArrayRef<uint8_t>(_defaultContent, 16);
   }
+
+#ifndef NDEBUG
+  virtual StringRef name() const { return _name; }
+
+  std::string _name;
+#else
+  virtual StringRef name() const { return ""; }
+#endif
 };
 
 const uint8_t PLTAtom::_defaultContent[16] = {
@@ -90,46 +106,157 @@ public:
   llvm::BumpPtrAllocator _alloc;
 };
 
-class PLTPass : public Pass {
+/// \brief Create GOT and PLT entries for relocations. Handles standard GOT/PLT
+/// along with IFUNC and TLS.
+///
+/// This currently assumes a static relocation model. Meaning GOT and PLT
+/// entries are not created for references that can be directly resolved. These
+/// are converted to a direct relocation. For entries that do require a GOT or
+/// PLT entry, that entry is statically bound.
+///
+/// TLS always assumes module 1 and attempts to remove indirection.
+class GOTPLTPass LLVM_FINAL : public Pass {
+  /// \brief Handle a specific reference.
+  ///
+  /// There are multiple different types of references and just the reference
+  /// kind is not enough to know if a got entry has to be created. We have the
+  /// following non-standard cases:
+  ///   Relocation    -> target type
+  ///   R_X86_64_PC32 -> typeResover = a call to an IFUNC function. Needs PLT.
+  void handleReference(const DefinedAtom &atom, const Reference &ref) {
+    const DefinedAtom *da = dyn_cast_or_null<const DefinedAtom>(ref.target());
+    switch (ref.kind()) {
+    case R_X86_64_PLT32:
+      // Static code doesn't need PLTs.
+      const_cast<Reference &>(ref).setKind(R_X86_64_PC32);
+      break;
+    case R_X86_64_PC32: // IFUNC
+      if (da && da->contentType() == DefinedAtom::typeResolver)
+        handlePC32IFUNC(ref, *da);
+      break;
+    case R_X86_64_GOTTPOFF: // GOT Thread Pointer Offset
+      if (da)
+        handleGOTTPOFF(ref, *da);
+      break;
+    case R_X86_64_GOTPCREL: // GOTPCREL to an undefined weak symbol.
+      // Always convert it to a non-got reference.
+      const_cast<Reference &>(ref).setKind(R_X86_64_PC32);
+      if (isa<const UndefinedAtom>(ref.target()))
+        handleUndefGOTPCREL(ref);
+      break;
+    }
+  }
+
+  /// \brief get the PLT entry for a given IFUNC Atom.
+  ///
+  /// If the entry does not exist. Both the GOT and PLT entry is created.
+  const PLTAtom *getIFUNCPLTEntry(const DefinedAtom &da) {
+    auto plt = _pltMap.find(&da);
+    if (plt != _pltMap.end())
+      return plt->second;
+    auto ga = new (_file._alloc) GOTAtom(_file, ".got.plt");
+    ga->addReference(R_X86_64_IRELATIVE, 0, &da, 0);
+    auto pa = new (_file._alloc) PLTAtom(_file, ".plt");
+    pa->addReference(R_X86_64_PC32, 2, ga, 0);
+#ifndef NDEBUG
+    ga->_name = "__got_ifunc_";
+    ga->_name += da.name();
+    pa->_name = "__plt_ifunc_";
+    pa->_name += da.name();
+#endif
+    _gotMap[&da] = ga;
+    _pltMap[&da] = pa;
+    return pa;
+  }
+
+  /// \brief Redirect the call to the PLT stub for the target IFUNC.
+  ///
+  /// This create a PLT and GOT entry for the IFUNC if one does not exist. The
+  /// GOT entry and a IRELATIVE relocation to the original target resolver.
+  void handlePC32IFUNC(const Reference &ref, const DefinedAtom &target) {
+    const_cast<Reference &>(ref).setTarget(getIFUNCPLTEntry(target));
+  }
+
+  /// \brief Create a GOT entry for the TP offset of a TLS atom.
+  const GOTAtom *getGOTTPOFF(const DefinedAtom &atom) {
+    auto got = _gotMap.find(&atom);
+    if (got == _gotMap.end()) {
+      auto g = new (_file._alloc) GOTAtom(_file, ".got");
+      g->addReference(R_X86_64_TPOFF64, 0, &atom, 0);
+#ifndef NDEBUG
+      g->_name = "__got_tls_";
+      g->_name += atom.name();
+#endif
+      _gotMap[&atom] = g;
+      return g;
+    }
+    return got->second;
+  }
+
+  /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to
+  /// the GOT.
+  void handleGOTTPOFF(const Reference &ref, const DefinedAtom &target) {
+    const_cast<Reference &>(ref).setTarget(getGOTTPOFF(target));
+    const_cast<Reference &>(ref).setKind(R_X86_64_PC32);
+  }
+
+  /// \brief Create a GOT entry containing 0.
+  const GOTAtom *getNullGOT() {
+    if (!_null) {
+      _null = new (_file._alloc) GOTAtom(_file, ".got");
+#ifndef NDEBUG
+      _null->_name = "__got_null";
+#endif
+    }
+    return _null;
+  }
+
+  /// \brief Handle a GOTPCREL relocation to an undefined weak atom by using a
+  /// null GOT entry.
+  void handleUndefGOTPCREL(const Reference &ref) {
+    const_cast<Reference &>(ref).setTarget(getNullGOT());
+  }
+
 public:
-  PLTPass(const ELFTargetInfo &ti) : _file(ti) {}
+  GOTPLTPass(const ELFTargetInfo &ti) : _file(ti), _null(nullptr) {}
 
+  /// \brief Do the pass.
+  ///
+  /// The goal here is to first process each reference individually. Each call
+  /// to handleReference may modify the reference itself and/or create new
+  /// atoms which must be stored in one of the maps below.
+  ///
+  /// After all references are handled, the atoms created during that are all
+  /// added to mf.
   virtual void perform(MutableFile &mf) {
+    // Process all references.
     for (const auto &atom : mf.defined())
-      for (const auto &ref : *atom) {
-        if (ref->kind() != llvm::ELF::R_X86_64_PC32)
-          continue;
-        if (const DefinedAtom *da =
-                dyn_cast<const DefinedAtom>(ref->target())) {
-          if (da->contentType() != DefinedAtom::typeResolver)
-            continue;
-          // We have a PC32 call to a IFUNC. Create a plt and got entry.
-          // Look it up first.
-          const PLTAtom *pa;
-          auto plt = _pltMap.find(da);
-          if (plt == _pltMap.end()) {
-            // Add an entry.
-            auto ga = new (_file._alloc) GOTAtom(_file, da);
-            mf.addAtom(*ga);
-            pa = new (_file._alloc) PLTAtom(_file, ga);
-            mf.addAtom(*pa);
-            _pltMap[da] = pa;
-          } else
-            pa = plt->second;
-          // This is dirty.
-          const_cast<Reference *>(ref)->setTarget(pa);
-        }
-      }
+      for (const auto &ref : *atom)
+        handleReference(*atom, *ref);
+
+    // Add all created atoms to the link.
+    if (_null)
+      mf.addAtom(*_null);
+    for (const auto &got : _gotMap)
+      mf.addAtom(*got.second);
+    for (const auto &plt : _pltMap)
+      mf.addAtom(*plt.second);
   }
 
 private:
-  llvm::DenseMap<const DefinedAtom *, const PLTAtom *> _pltMap;
+  /// \brief Owner of all the Atoms created by this pass.
   ELFPassFile _file;
+  /// \brief Map Atoms to their GOT entries.
+  llvm::DenseMap<const DefinedAtom *, const GOTAtom *> _gotMap;
+  /// \brief Map Atoms to their PLT entries.
+  llvm::DenseMap<const DefinedAtom *, const PLTAtom *> _pltMap;
+  /// \brief GOT entry that is always 0. Used for undefined weaks.
+  GOTAtom *_null;
 };
 } // end anon namespace
 
 void elf::X86_64TargetInfo::addPasses(PassManager &pm) const {
-  pm.add(std::unique_ptr<Pass>(new PLTPass(*this)));
+  pm.add(std::unique_ptr<Pass>(new GOTPLTPass(*this)));
 }
 
 #define LLD_CASE(name) .Case(#name, llvm::ELF::name)

Added: lld/trunk/test/elf/Inputs/tls.S
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/Inputs/tls.S?rev=174155&view=auto
==============================================================================
--- lld/trunk/test/elf/Inputs/tls.S (added)
+++ lld/trunk/test/elf/Inputs/tls.S Fri Feb  1 01:14:14 2013
@@ -0,0 +1,41 @@
+        .text
+        .globl  main
+        .align  16, 0x90
+        .type   main, at function
+main:                                   # @main
+        callq   GOTTPOFF
+        addl    %fs:tls1 at TPOFF, %eax
+        addl    %fs:tls0 at TPOFF, %eax
+        addl    %fs:tls2 at TPOFF, %eax
+        ret
+
+        .text
+        .globl GOTTPOFF
+        .type  GOTTPOFF, at function
+GOTTPOFF:
+        movq tls2 at GOTTPOFF(%rip), %rax
+        movl %fs:0(%rax), %eax
+        ret
+
+        .type   tls0, at object            # @tls0
+        .section        .tbss,"awT", at nobits
+        .globl  tls0
+        .align  4
+tls0:
+        .long   0                       # 0x0
+        .size   tls0, 4
+
+        .type   tls1, at object            # @tls1
+        .globl  tls1
+        .align  4
+tls1:
+        .long   0                       # 0x0
+        .size   tls1, 4
+
+        .type   tls2, at object            # @tls2
+        .section        .tdata,"awT", at progbits
+        .globl  tls2
+        .align  4
+tls2:
+        .long   1                       # 0x1
+        .size   tls2, 4

Modified: lld/trunk/test/elf/Inputs/tls.x86-64
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/Inputs/tls.x86-64?rev=174155&r1=174154&r2=174155&view=diff
==============================================================================
Binary files lld/trunk/test/elf/Inputs/tls.x86-64 (original) and lld/trunk/test/elf/Inputs/tls.x86-64 Fri Feb  1 01:14:14 2013 differ

Added: lld/trunk/test/elf/Inputs/undef-gotpcrel.S
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/Inputs/undef-gotpcrel.S?rev=174155&view=auto
==============================================================================
--- lld/trunk/test/elf/Inputs/undef-gotpcrel.S (added)
+++ lld/trunk/test/elf/Inputs/undef-gotpcrel.S Fri Feb  1 01:14:14 2013
@@ -0,0 +1,10 @@
+        .text
+        .globl  main
+        .align  16, 0x90
+        .type   main, at function
+main:                                   # @main
+        movq  blah at GOTPCREL(%rip), %rax
+        ret
+
+        .weak blah
+        .type blah, at function

Added: lld/trunk/test/elf/Inputs/undef-gotpcrel.x86-64
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/Inputs/undef-gotpcrel.x86-64?rev=174155&view=auto
==============================================================================
Binary files lld/trunk/test/elf/Inputs/undef-gotpcrel.x86-64 (added) and lld/trunk/test/elf/Inputs/undef-gotpcrel.x86-64 Fri Feb  1 01:14:14 2013 differ

Modified: lld/trunk/test/elf/ifunc.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/ifunc.test?rev=174155&r1=174154&r2=174155&view=diff
==============================================================================
--- lld/trunk/test/elf/ifunc.test (original)
+++ lld/trunk/test/elf/ifunc.test Fri Feb  1 01:14:14 2013
@@ -12,7 +12,7 @@ PLT: name: main
 PLT: scope: global
 PLT: references
 PLT: kind: R_X86_64_PC32
-PLT: target: [[PLTNAME:[a-zA-Z0-9-]+]]
+PLT: target: [[PLTNAME:[-a-zA-Z0-9_]+]]
 
 // Make sure there's a got entry with a IRELATIVE relocation.
 PLT: type: got

Modified: lld/trunk/test/elf/tls.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/tls.test?rev=174155&r1=174154&r2=174155&view=diff
==============================================================================
--- lld/trunk/test/elf/tls.test (original)
+++ lld/trunk/test/elf/tls.test Fri Feb  1 01:14:14 2013
@@ -1,8 +1,33 @@
+RUN: lld -core -target x86_64-linux %p/Inputs/tls.x86-64 -output=- \
+RUN:   -noinhibit-exec -entry=main -emit-yaml | FileCheck %s -check-prefix=YAML
+
 RUN: lld -core -target x86_64-linux %p/Inputs/tls.x86-64 -output=%t \
 RUN:   -noinhibit-exec -entry=main && llvm-objdump -d %t | FileCheck %s
 
 // Verify that the TLS accesses have the correct offsets.
 
-CHECK: movl %fs:-8
-CHECK: movl %fs:-4
-CHECK: movl %fs:-12
+YAML: name: main
+YAML: kind: R_X86_64_TPOFF32
+YAML: offset: 9
+YAML: target: tls1
+YAML: kind: R_X86_64_TPOFF32
+YAML: offset: 17
+YAML: target: tls0
+YAML: kind: R_X86_64_TPOFF32
+YAML: offset: 25
+YAML: target: tls2
+
+YAML: name: GOTTPOFF
+YAML: kind: R_X86_64_PC32
+YAML: target: [[GOTNAME:[a-zA-Z0-9_]+]]
+
+YAML: name: [[GOTNAME]]
+YAML: type: got
+YAML: kind: R_X86_64_TPOFF64
+YAML: target: tls2
+
+CHECK: addl %fs:-4
+CHECK: addl %fs:-8
+CHECK: addl %fs:-12
+
+CHECK: movq {{[0-9]+}}(%rip)

Added: lld/trunk/test/elf/undef-gotpcrel.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/elf/undef-gotpcrel.test?rev=174155&view=auto
==============================================================================
--- lld/trunk/test/elf/undef-gotpcrel.test (added)
+++ lld/trunk/test/elf/undef-gotpcrel.test Fri Feb  1 01:14:14 2013
@@ -0,0 +1,10 @@
+RUN: lld -core -target x86_64-linux -output=- -entry=main \
+RUN:   %p/Inputs/undef-gotpcrel.x86-64 -emit-yaml -noinhibit-exec \
+RUN: | FileCheck %s -check-prefix=YAML
+
+YAML: name: main
+YAML: kind: R_X86_64_PC32
+YAML: target: [[NULLGOT:[a-zA-Z0-9_]+]]
+
+YAML: name: [[NULLGOT]]
+YAML: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]





More information about the llvm-commits mailing list