diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp')
| -rw-r--r-- | lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp | 241 |
1 files changed, 163 insertions, 78 deletions
diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp index 0bd12958b27b..4d94a793665c 100644 --- a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp @@ -28,52 +28,79 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -namespace { // .got values -const uint8_t AArch64GotAtomContent[8] = {0}; +static const uint8_t AArch64GotAtomContent[8] = {0}; + +// tls descriptor .got values, the layout is: +// struct tlsdesc { +// ptrdiff_t (*entry) (struct tlsdesc *); +// void *arg; +// }; +static const uint8_t AArch64TlsdescGotAtomContent[16] = {0}; // .plt value (entry 0) -const uint8_t AArch64Plt0AtomContent[32] = { - 0xf0, 0x7b, 0xbf, - 0xa9, // stp x16, x30, [sp,#-16]! - 0x10, 0x00, 0x00, - 0x90, // adrp x16, Page(eh_frame) - 0x11, 0x02, 0x40, - 0xf9, // ldr x17, [x16,#offset] - 0x10, 0x02, 0x00, - 0x91, // add x16, x16, #offset - 0x20, 0x02, 0x1f, - 0xd6, // br x17 - 0x1f, 0x20, 0x03, - 0xd5, // nop - 0x1f, 0x20, 0x03, - 0xd5, // nop - 0x1f, 0x20, 0x03, - 0xd5 // nop +static const uint8_t AArch64Plt0AtomContent[32] = { + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(eh_frame) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop }; // .plt values (other entries) -const uint8_t AArch64PltAtomContent[16] = { - 0x10, 0x00, 0x00, - 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) - 0x11, 0x02, 0x40, - 0xf9, // ldr x17, [x16,#offset] - 0x10, 0x02, 0x00, - 0x91, // add x16, x16, #offset - 0x20, 0x02, 0x1f, - 0xd6 // br x17 +static const uint8_t AArch64PltAtomContent[16] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, 0xd6 // br x17 +}; + +// .plt tlsdesc values +static const uint8_t AArch64PltTlsdescAtomContent[32] = { + 0xe2, 0x0f, 0xbf, 0xa9, // stp x2, x3, [sp, #-16] + 0x02, 0x00, 0x00, 0x90, // adpr x2, 0 + 0x03, 0x00, 0x00, 0x90, // adpr x3, 0 + 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2, #0] + 0x63, 0x00, 0x00, 0x91, // add x3, x3, 0 + 0x40, 0x00, 0x1f, 0xd6, // br x2 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop }; +namespace { + /// \brief Atoms that are used by AArch64 dynamic linking class AArch64GOTAtom : public GOTAtom { public: - AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + AArch64GOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64GotAtomContent, 8); } + +protected: + // Constructor for AArch64GOTAtom + AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} +}; + +class AArch64GOTPLTAtom : public AArch64GOTAtom { +public: + AArch64GOTPLTAtom(const File &f) : AArch64GOTAtom(f, ".got.plt") {} }; +class AArch64TLSDESCGOTAtom : public AArch64GOTPLTAtom { +public: + AArch64TLSDESCGOTAtom(const File &f) : AArch64GOTPLTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64TlsdescGotAtomContent, 16); + } +}; + + class AArch64PLT0Atom : public PLT0Atom { public: AArch64PLT0Atom(const File &f) : PLT0Atom(f) {} @@ -84,13 +111,22 @@ public: class AArch64PLTAtom : public PLTAtom { public: - AArch64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + AArch64PLTAtom(const File &f) : PLTAtom(f, ".plt") {} ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(AArch64PltAtomContent, 16); } }; +class AArch64PLTTLSDESCAtom : public PLTAtom { +public: + AArch64PLTTLSDESCAtom(const File &f) : PLTAtom(f, ".plt") {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64PltTlsdescAtomContent, 32); + } +}; + class ELFPassFile : public SimpleFile { public: ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { @@ -149,9 +185,16 @@ template <class Derived> class AArch64RelocationPass : public Pass { break; case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_LD64_GOT_LO12_NC: + static_cast<Derived *>(this)->handleGOT(ref); + break; case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: - static_cast<Derived *>(this)->handleGOT(ref); + static_cast<Derived *>(this)->handleGOTTPREL(ref); + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + case R_AARCH64_TLSDESC_LD64_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12_NC: + static_cast<Derived *>(this)->handleTLSDESC(ref); break; } } @@ -164,9 +207,9 @@ protected: auto plt = _pltMap.find(da); if (plt != _pltMap.end()) return plt->second; - auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_IRELATIVE, 0, da, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); pa->addReferenceELF_AArch64(R_AARCH64_PREL32, 2, ga, -4); #ifndef NDEBUG ga->_name = "__got_ifunc_"; @@ -193,11 +236,11 @@ protected: } /// \brief Create a GOT entry for the TP offset of a TLS atom. - const GOTAtom *getGOTTPOFF(const Atom *atom) { + const GOTAtom *getGOTTPREL(const Atom *atom) { auto got = _gotMap.find(atom); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); - g->addReferenceELF_AArch64(R_AARCH64_GOTREL64, 0, atom, 0); + auto g = new (_file._alloc) AArch64GOTAtom(_file); + g->addReferenceELF_AArch64(R_AARCH64_TLS_TPREL64, 0, atom, 0); #ifndef NDEBUG g->_name = "__got_tls_"; g->_name += atom->name(); @@ -209,17 +252,53 @@ protected: 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_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); - const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + /// \brief Create a GOT TPREL entry to local or external TLS variable. + std::error_code handleGOTTPREL(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTTPREL(ref.target())); + return std::error_code(); + } + + /// \brief Generates a double GOT entry with R_AARCH64_TLSDESC dynamic + /// relocation reference. Since the dynamic relocation is resolved + /// lazily so the GOT associated should be in .got.plt. + const GOTAtom *getTLSDESCPLTEntry(const Atom *da) { + auto got = _gotMap.find(da); + if (got != _gotMap.end()) + return got->second; + auto ga = new (_file._alloc) AArch64TLSDESCGOTAtom(_file); + ga->addReferenceELF_AArch64(R_AARCH64_TLSDESC, 0, da, 0); + auto pa = new (_file._alloc) AArch64PLTTLSDESCAtom(_file); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 4, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_PREL_PG_HI21, 8, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_LDST64_ABS_LO12_NC, 12, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_ADD_ABS_LO12_NC, 16, ga, 0); +#ifndef NDEBUG + ga->_name = "__got_tlsdesc_"; + ga->_name += da->name(); + pa->_name = "__plt_tlsdesc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _tlsdescVector.push_back(ga); + _pltVector.push_back(pa); + return ga; + } + + std::error_code handleTLSDESC(const Reference &ref) { + if (isa<DefinedAtom>(ref.target()) || + isa<SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getTLSDESCPLTEntry(ref.target())); + } + return std::error_code(); } /// \brief Create a GOT entry containing 0. const GOTAtom *getNullGOT() { if (!_null) { - _null = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _null = new (_file._alloc) AArch64GOTPLTAtom(_file); #ifndef NDEBUG _null->_name = "__got_null"; #endif @@ -230,7 +309,7 @@ protected: const GOTAtom *getGOT(const DefinedAtom *da) { auto got = _gotMap.find(da); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_ABS64, 0, da, 0); #ifndef NDEBUG g->_name = "__got_"; @@ -244,9 +323,7 @@ protected: } public: - AArch64RelocationPass(const ELFLinkingContext &ctx) - : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), - _got1(nullptr) {} + AArch64RelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \brief Do the pass. /// @@ -256,32 +333,32 @@ public: /// /// After all references are handled, the atoms created during that are all /// added to mf. - void perform(std::unique_ptr<MutableFile> &mf) override { + std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "AArch64 GOT/PLT Pass"); DEBUG_WITH_TYPE( "AArch64", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom - : mf->undefined()) { + : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom - : mf->sharedLibrary()) { + : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom - : mf->absolute()) { + : mf.absolute()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } // Process all references. llvm::dbgs() << "Defined Atoms" << "\n"); - for (const auto &atom : mf->defined()) { + for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -289,32 +366,39 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - if (_PLT0) { - _PLT0->setOrdinal(ordinal++); - mf->addAtom(*_PLT0); + if (_plt0) { + _plt0->setOrdinal(ordinal++); + mf.addAtom(*_plt0); } for (auto &plt : _pltVector) { plt->setOrdinal(ordinal++); - mf->addAtom(*plt); + mf.addAtom(*plt); } if (_null) { _null->setOrdinal(ordinal++); - mf->addAtom(*_null); + mf.addAtom(*_null); } - if (_PLT0) { + if (_plt0) { _got0->setOrdinal(ordinal++); _got1->setOrdinal(ordinal++); - mf->addAtom(*_got0); - mf->addAtom(*_got1); + mf.addAtom(*_got0); + mf.addAtom(*_got1); } for (auto &got : _gotVector) { got->setOrdinal(ordinal++); - mf->addAtom(*got); + mf.addAtom(*got); + } + // Add any tlsdesc GOT relocation after default PLT and iFUNC entries. + for (auto &tlsdesc : _tlsdescVector) { + tlsdesc->setOrdinal(ordinal++); + mf.addAtom(*tlsdesc); } for (auto obj : _objectVector) { obj->setOrdinal(ordinal++); - mf->addAtom(*obj); + mf.addAtom(*obj); } + + return std::error_code(); } protected: @@ -333,18 +417,19 @@ protected: /// \brief the list of GOT/PLT atoms std::vector<GOTAtom *> _gotVector; + std::vector<GOTAtom *> _tlsdescVector; std::vector<PLTAtom *> _pltVector; std::vector<ObjectAtom *> _objectVector; /// \brief GOT entry that is always 0. Used for undefined weaks. - GOTAtom *_null; + GOTAtom *_null = nullptr; /// \brief The got and plt entries for .PLT0. This is used to call into the /// dynamic linker for symbol resolution. /// @{ - PLT0Atom *_PLT0; - GOTAtom *_got0; - GOTAtom *_got1; + PLT0Atom *_plt0 = nullptr; + GOTAtom *_got0 = nullptr; + GOTAtom *_got1 = nullptr; /// @} }; @@ -394,31 +479,31 @@ public: : AArch64RelocationPass(ctx) {} const PLT0Atom *getPLT0() { - if (_PLT0) - return _PLT0; + if (_plt0) + return _plt0; // Fill in the null entry. getNullGOT(); - _PLT0 = new (_file._alloc) AArch64PLT0Atom(_file); - _got0 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); - _got1 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); - _PLT0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); - _PLT0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); - _PLT0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); + _plt0 = new (_file._alloc) AArch64PLT0Atom(_file); + _got0 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _got1 = new (_file._alloc) AArch64GOTPLTAtom(_file); + _plt0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); + _plt0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); + _plt0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); #ifndef NDEBUG - _PLT0->_name = "__PLT0"; + _plt0->_name = "__PLT0"; _got0->_name = "__got0"; _got1->_name = "__got1"; #endif - return _PLT0; + return _plt0; } const PLTAtom *getPLTEntry(const Atom *a) { auto plt = _pltMap.find(a); if (plt != _pltMap.end()) return plt->second; - auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + auto ga = new (_file._alloc) AArch64GOTPLTAtom(_file); ga->addReferenceELF_AArch64(R_AARCH64_JUMP_SLOT, 0, a, 0); - auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + auto pa = new (_file._alloc) AArch64PLTAtom(_file); pa->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 0, ga, 0); pa->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 4, ga, 0); pa->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 8, ga, 0); @@ -485,7 +570,7 @@ public: const GOTAtom *getSharedGOT(const SharedLibraryAtom *sla) { auto got = _gotMap.find(sla); if (got == _gotMap.end()) { - auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + auto g = new (_file._alloc) AArch64GOTAtom(_file); g->addReferenceELF_AArch64(R_AARCH64_GLOB_DAT, 0, sla, 0); #ifndef NDEBUG g->_name = "__got_"; |
