[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