//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp -------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines the relocation processing pass for ARM. This includes /// GOT and PLT entries, TLS, COPY, and ifunc. /// /// This also includes additional behavior that gnu-ld and gold implement but /// which is not specified anywhere. /// //===----------------------------------------------------------------------===// #include "ARMRelocationPass.h" #include "ARMLinkingContext.h" #include "Atoms.h" #include "lld/Core/Simple.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" using namespace lld; using namespace lld::elf; using namespace llvm::ELF; namespace { // ARM B/BL instructions of absolute relocation veneer. // TODO: consider different instruction set for archs below ARMv5 // (one as for Thumb may be used though it's less optimal). static const uint8_t Veneer_ARM_B_BL_Abs_a_AtomContent[4] = { 0x04, 0xf0, 0x1f, 0xe5 // ldr pc, [pc, #-4] }; static const uint8_t Veneer_ARM_B_BL_Abs_d_AtomContent[4] = { 0x00, 0x00, 0x00, 0x00 // }; // Thumb B/BL instructions of absolute relocation veneer. // TODO: consider different instruction set for archs above ARMv5 // (one as for ARM may be used since it's more optimal). static const uint8_t Veneer_THM_B_BL_Abs_t_AtomContent[4] = { 0x78, 0x47, // bx pc 0x00, 0x00 // nop }; static const uint8_t Veneer_THM_B_BL_Abs_a_AtomContent[4] = { 0xfe, 0xff, 0xff, 0xea // b }; // .got values static const uint8_t ARMGotAtomContent[4] = {0}; // .plt value (entry 0) static const uint8_t ARMPlt0_a_AtomContent[16] = { 0x04, 0xe0, 0x2d, 0xe5, // push {lr} 0x04, 0xe0, 0x9f, 0xe5, // ldr lr, [pc, #4] 0x0e, 0xe0, 0x8f, 0xe0, // add lr, pc, lr 0x00, 0xf0, 0xbe, 0xe5 // ldr pc, [lr, #0]! }; static const uint8_t ARMPlt0_d_AtomContent[4] = { 0x00, 0x00, 0x00, 0x00 // }; // .plt values (other entries) static const uint8_t ARMPltAtomContent[12] = { 0x00, 0xc0, 0x8f, 0xe2, // add ip, pc, #offset[G0] 0x00, 0xc0, 0x8c, 0xe2, // add ip, ip, #offset[G1] 0x00, 0xf0, 0xbc, 0xe5 // ldr pc, [ip, #offset[G2]]! }; // Veneer for switching from Thumb to ARM code for PLT entries. static const uint8_t ARMPltVeneerAtomContent[4] = { 0x78, 0x47, // bx pc 0x00, 0x00 // nop }; // Determine proper names for mapping symbols. static std::string getMappingAtomName(DefinedAtom::CodeModel model, const std::string &part) { switch (model) { case DefinedAtom::codeARM_a: return part.empty() ? "$a" : "$a." + part; case DefinedAtom::codeARM_d: return part.empty() ? "$d" : "$d." + part; case DefinedAtom::codeARM_t: return part.empty() ? "$t" : "$t." + part; default: llvm_unreachable("Wrong code model of mapping atom"); } } /// \brief Atoms that hold veneer code. class VeneerAtom : public SimpleELFDefinedAtom { StringRef _section; public: VeneerAtom(const File &f, StringRef secName, const std::string &name = "") : SimpleELFDefinedAtom(f), _section(secName), _name(name) {} Scope scope() const override { return DefinedAtom::scopeTranslationUnit; } SectionChoice sectionChoice() const override { return DefinedAtom::sectionBasedOnContent; } StringRef customSectionName() const override { return _section; } ContentType contentType() const override { return DefinedAtom::typeCode; } uint64_t size() const override { return rawContent().size(); } ContentPermissions permissions() const override { return permR_X; } Alignment alignment() const override { return 4; } StringRef name() const override { return _name; } private: std::string _name; }; /// \brief Atoms that hold veneer for relocated ARM B/BL instructions /// in absolute code. class Veneer_ARM_B_BL_Abs_a_Atom : public VeneerAtom { public: Veneer_ARM_B_BL_Abs_a_Atom(const File &f, StringRef secName, const std::string &name) : VeneerAtom(f, secName, name) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_a_AtomContent); } }; class Veneer_ARM_B_BL_Abs_d_Atom : public VeneerAtom { public: Veneer_ARM_B_BL_Abs_d_Atom(const File &f, StringRef secName) : VeneerAtom(f, secName) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_d_AtomContent); } }; /// \brief Atoms that hold veneer for relocated Thumb B/BL instructions /// in absolute code. class Veneer_THM_B_BL_Abs_t_Atom : public VeneerAtom { public: Veneer_THM_B_BL_Abs_t_Atom(const File &f, StringRef secName, const std::string &name) : VeneerAtom(f, secName, name) {} DefinedAtom::CodeModel codeModel() const override { return DefinedAtom::codeARMThumb; } ArrayRef rawContent() const override { return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_t_AtomContent); } }; class Veneer_THM_B_BL_Abs_a_Atom : public VeneerAtom { public: Veneer_THM_B_BL_Abs_a_Atom(const File &f, StringRef secName) : VeneerAtom(f, secName) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_a_AtomContent); } }; template class ARMVeneerMappingAtom : public VeneerAtom { public: ARMVeneerMappingAtom(const File &f, StringRef secName, StringRef name) : VeneerAtom(f, secName, getMappingAtomName(Model, name)) { static_assert((Model == DefinedAtom::codeARM_a || Model == DefinedAtom::codeARM_d || Model == DefinedAtom::codeARM_t), "Only mapping atom types are allowed"); } uint64_t size() const override { return 0; } ArrayRef rawContent() const override { return ArrayRef(); } DefinedAtom::CodeModel codeModel() const override { return Model; } }; template class BaseMappingAtom : public BaseAtom { public: BaseMappingAtom(const File &f, StringRef secName, StringRef name) : BaseAtom(f, secName) { static_assert((Model == DefinedAtom::codeARM_a || Model == DefinedAtom::codeARM_d || Model == DefinedAtom::codeARM_t), "Only mapping atom types are allowed"); #ifndef NDEBUG _name = name; #else _name = getMappingAtomName(Model, name); #endif } DefinedAtom::CodeModel codeModel() const override { #ifndef NDEBUG return isThumbCode(Model) ? DefinedAtom::codeARMThumb : DefinedAtom::codeNA; #else return Model; #endif } StringRef name() const override { return _name; } private: std::string _name; }; /// \brief Atoms that are used by ARM dynamic linking class ARMGOTAtom : public GOTAtom { public: ARMGOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef rawContent() const override { return llvm::makeArrayRef(ARMGotAtomContent); } Alignment alignment() const override { return 4; } protected: // Constructor for PLTGOT atom. ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} }; class ARMGOTPLTAtom : public ARMGOTAtom { public: ARMGOTPLTAtom(const File &f) : ARMGOTAtom(f, ".got.plt") {} }; /// \brief Proxy class to keep type compatibility with PLT0Atom. class ARMPLT0Atom : public PLT0Atom { public: ARMPLT0Atom(const File &f, StringRef) : PLT0Atom(f) {} }; /// \brief PLT0 entry atom. /// Serves as a mapping symbol in the release mode. class ARMPLT0_a_Atom : public BaseMappingAtom { public: ARMPLT0_a_Atom(const File &f, const std::string &name) : BaseMappingAtom(f, ".plt", name) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(ARMPlt0_a_AtomContent); } Alignment alignment() const override { return 4; } }; class ARMPLT0_d_Atom : public BaseMappingAtom { public: ARMPLT0_d_Atom(const File &f, const std::string &name) : BaseMappingAtom(f, ".plt", name) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(ARMPlt0_d_AtomContent); } Alignment alignment() const override { return 4; } }; /// \brief PLT entry atom. /// Serves as a mapping symbol in the release mode. class ARMPLTAtom : public BaseMappingAtom { public: ARMPLTAtom(const File &f, const std::string &name) : BaseMappingAtom(f, ".plt", name) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(ARMPltAtomContent); } Alignment alignment() const override { return 4; } }; /// \brief Veneer atom for PLT entry. /// Serves as a mapping symbol in the release mode. class ARMPLTVeneerAtom : public BaseMappingAtom { public: ARMPLTVeneerAtom(const File &f, const std::string &name) : BaseMappingAtom(f, ".plt", name) {} ArrayRef rawContent() const override { return llvm::makeArrayRef(ARMPltVeneerAtomContent); } Alignment alignment() const override { return 4; } }; /// \brief Atom which represents an object for which a COPY relocation will /// be generated. class ARMObjectAtom : public ObjectAtom { public: ARMObjectAtom(const File &f) : ObjectAtom(f) {} Alignment alignment() const override { return 4; } }; class ELFPassFile : public SimpleFile { public: ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { setOrdinal(eti.getNextOrdinalAndIncrement()); } llvm::BumpPtrAllocator _alloc; }; /// \brief CRTP base for handling relocations. template class ARMRelocationPass : public Pass { /// \brief Handle a specific reference. void handleReference(const DefinedAtom &atom, const Reference &ref) { DEBUG_WITH_TYPE( "ARM", llvm::dbgs() << "\t" << LLVM_FUNCTION_NAME << "()" << ": Name of Defined Atom: " << atom.name().str(); llvm::dbgs() << " kindValue: " << ref.kindValue() << "\n"); if (ref.kindNamespace() != Reference::KindNamespace::ELF) return; assert(ref.kindArch() == Reference::KindArch::ARM); switch (ref.kindValue()) { case R_ARM_ABS32: case R_ARM_REL32: case R_ARM_TARGET1: case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: static_cast(this)->handlePlain(isThumbCode(&atom), ref); break; case R_ARM_THM_CALL: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_THM_JUMP24: case R_ARM_THM_JUMP11: { const auto actualModel = actualSourceCodeModel(atom, ref); const bool fromThumb = isThumbCode(actualModel); static_cast(this)->handlePlain(fromThumb, ref); static_cast(this)->handleVeneer(atom, fromThumb, ref); } break; case R_ARM_TLS_IE32: static_cast(this)->handleTLSIE32(ref); break; case R_ARM_GOT_BREL: static_cast(this)->handleGOT(ref); break; default: break; } } protected: /// \brief Determine source atom's actual code model. /// /// Actual code model may differ from the existing one if fixup /// is possible on the later stages for given relocation type. DefinedAtom::CodeModel actualSourceCodeModel(const DefinedAtom &atom, const Reference &ref) { const auto kindValue = ref.kindValue(); if (kindValue != R_ARM_CALL && kindValue != R_ARM_THM_CALL) return atom.codeModel(); // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) // fixup isn't possible without veneer generation for archs below ARMv5. auto actualModel = atom.codeModel(); if (const auto *da = dyn_cast(ref.target())) { actualModel = da->codeModel(); } else if (const auto *sla = dyn_cast(ref.target())) { if (sla->type() == SharedLibraryAtom::Type::Code) { // PLT entry will be generated here - assume we don't want a veneer // on top of it and prefer instruction fixup if needed. actualModel = DefinedAtom::codeNA; } } return actualModel; } std::error_code handleVeneer(const DefinedAtom &atom, bool fromThumb, const Reference &ref) { // Actual instruction mode differs meaning that further fixup will be // applied. if (isThumbCode(&atom) != fromThumb) return std::error_code(); const VeneerAtom *(Derived::*getVeneer)(const DefinedAtom *, StringRef) = nullptr; const auto kindValue = ref.kindValue(); switch (kindValue) { case R_ARM_JUMP24: getVeneer = &Derived::getVeneer_ARM_B_BL; break; case R_ARM_THM_JUMP24: getVeneer = &Derived::getVeneer_THM_B_BL; break; default: return std::error_code(); } // Target symbol and relocated place should have different // instruction sets in order a veneer to be generated in between. const auto *target = dyn_cast(ref.target()); if (!target || isThumbCode(target) == isThumbCode(&atom)) return std::error_code(); // Veneers may only be generated for STT_FUNC target symbols // or for symbols located in sections different to the place of relocation. StringRef secName = atom.customSectionName(); if (DefinedAtom::typeCode != target->contentType() && !target->customSectionName().equals(secName)) { StringRef kindValStr; if (!this->_ctx.registry().referenceKindToString( ref.kindNamespace(), ref.kindArch(), kindValue, kindValStr)) { kindValStr = "unknown"; } std::string errStr = (Twine("Reference of type ") + Twine(kindValue) + " (" + kindValStr + ") from " + atom.name() + "+" + Twine(ref.offsetInAtom()) + " to " + ref.target()->name() + "+" + Twine(ref.addend()) + " cannot be effected without a veneer").str(); llvm_unreachable(errStr.c_str()); } assert(getVeneer && "The veneer handler is missing"); const Atom *veneer = (static_cast(this)->*getVeneer)(target, secName); assert(veneer && "The veneer is not set"); const_cast(ref).setTarget(veneer); return std::error_code(); } /// \brief Get the veneer for ARM B/BL instructions /// in absolute code. const VeneerAtom *getVeneer_ARM_B_BL_Abs(const DefinedAtom *da, StringRef secName) { auto veneer = _veneerAtoms.lookup(da); if (!veneer.empty()) return veneer._veneer; std::string name = "__"; name += da->name(); name += "_from_arm"; // Create parts of veneer with mapping symbols. auto v_a = new (_file._alloc) Veneer_ARM_B_BL_Abs_a_Atom(_file, secName, name); addVeneerWithMapping(da, v_a, name); auto v_d = new (_file._alloc) Veneer_ARM_B_BL_Abs_d_Atom(_file, secName); addVeneerWithMapping(v_a, v_d, name); // Fake reference to show connection between parts of veneer. v_a->addReferenceELF_ARM(R_ARM_NONE, 0, v_d, 0); // Real reference to fixup. v_d->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); return v_a; } /// \brief Get the veneer for Thumb B/BL instructions /// in absolute code. const VeneerAtom *getVeneer_THM_B_BL_Abs(const DefinedAtom *da, StringRef secName) { auto veneer = _veneerAtoms.lookup(da); if (!veneer.empty()) return veneer._veneer; std::string name = "__"; name += da->name(); name += "_from_thumb"; // Create parts of veneer with mapping symbols. auto v_t = new (_file._alloc) Veneer_THM_B_BL_Abs_t_Atom(_file, secName, name); addVeneerWithMapping(da, v_t, name); auto v_a = new (_file._alloc) Veneer_THM_B_BL_Abs_a_Atom(_file, secName); addVeneerWithMapping(v_t, v_a, name); // Fake reference to show connection between parts of veneer. v_t->addReferenceELF_ARM(R_ARM_NONE, 0, v_a, 0); // Real reference to fixup. v_a->addReferenceELF_ARM(R_ARM_JUMP24, 0, da, 0); return v_t; } std::error_code handleTLSIE32(const Reference &ref) { if (const auto *target = dyn_cast(ref.target())) { const_cast(ref) .setTarget(static_cast(this)->getTLSTPOFF32(target)); return std::error_code(); } llvm_unreachable("R_ARM_TLS_IE32 reloc targets wrong atom type"); } /// \brief Create a GOT entry for TLS with reloc type and addend specified. template const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) { StringRef source; #ifndef NDEBUG source = "_tls_"; #endif return getGOT(da, source); } /// \brief Add veneer with mapping symbol. template void addVeneerWithMapping(const DefinedAtom *da, VeneerAtom *va, const std::string &name) { assert(_veneerAtoms.lookup(da).empty() && "Veneer or mapping already exists"); auto *ma = new (_file._alloc) ARMVeneerMappingAtom(_file, va->customSectionName(), name); // Fake reference to show connection between the mapping symbol and veneer. va->addReferenceELF_ARM(R_ARM_NONE, 0, ma, 0); _veneerAtoms[da] = VeneerWithMapping(va, ma); } /// \brief get a veneer for a PLT entry. const PLTAtom *getPLTVeneer(const Atom *da, PLTAtom *pa, StringRef source) { std::string name = "__plt_from_thumb"; name += source.empty() ? "_" : source; name += da->name(); // Create veneer for PLT entry. auto va = new (_file._alloc) ARMPLTVeneerAtom(_file, name); // Fake reference to show connection between veneer and PLT entry. va->addReferenceELF_ARM(R_ARM_NONE, 0, pa, 0); _pltAtoms[da] = PLTWithVeneer(pa, va); return va; } typedef const GOTAtom *(Derived::*GOTFactory)(const Atom *); /// \brief get a PLT entry referencing PLTGOT entry. /// /// If the entry does not exist, both GOT and PLT entry are created. const PLTAtom *getPLT(const Atom *da, bool fromThumb, GOTFactory gotFactory, StringRef source = "") { auto pltVeneer = _pltAtoms.lookup(da); if (!pltVeneer.empty()) { // Return clean PLT entry provided it is ARM code. if (!fromThumb) return pltVeneer._plt; // Check if veneer is present for Thumb to ARM transition. if (pltVeneer._veneer) return pltVeneer._veneer; // Create veneer for existing PLT entry. return getPLTVeneer(da, pltVeneer._plt, source); } // Create specific GOT entry. const auto *ga = (static_cast(this)->*gotFactory)(da); assert(_gotpltAtoms.lookup(da) == ga && "GOT entry should be added to the PLTGOT map"); assert(ga->customSectionName() == ".got.plt" && "GOT entry should be in a special section"); std::string name = "__plt"; name += source.empty() ? "_" : source; name += da->name(); // Create PLT entry for the GOT entry. auto pa = new (_file._alloc) ARMPLTAtom(_file, name); pa->addReferenceELF_ARM(R_ARM_ALU_PC_G0_NC, 0, ga, -8); pa->addReferenceELF_ARM(R_ARM_ALU_PC_G1_NC, 4, ga, -4); pa->addReferenceELF_ARM(R_ARM_LDR_PC_G2, 8, ga, 0); // Since all PLT entries are in ARM code, Thumb to ARM // switching should be added if the relocated place contais Thumb code. if (fromThumb) return getPLTVeneer(da, pa, source); // Otherwise just add PLT entry and return it to the caller. _pltAtoms[da] = PLTWithVeneer(pa); return pa; } /// \brief Create the GOT entry for a given IFUNC Atom. const GOTAtom *createIFUNCGOT(const Atom *da) { assert(!_gotpltAtoms.lookup(da) && "IFUNC GOT entry already exists"); auto g = new (_file._alloc) ARMGOTPLTAtom(_file); g->addReferenceELF_ARM(R_ARM_ABS32, 0, da, 0); g->addReferenceELF_ARM(R_ARM_IRELATIVE, 0, da, 0); #ifndef NDEBUG g->_name = "__got_ifunc_"; g->_name += da->name(); #endif _gotpltAtoms[da] = g; return g; } /// \brief get the PLT entry for a given IFUNC Atom. const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da, bool fromThumb) { return getPLT(da, fromThumb, &Derived::createIFUNCGOT, "_ifunc_"); } /// \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. std::error_code handleIFUNC(bool fromThumb, const Reference &ref) { auto target = dyn_cast(ref.target()); if (target && target->contentType() == DefinedAtom::typeResolver) { const_cast(ref) .setTarget(getIFUNCPLTEntry(target, fromThumb)); } return std::error_code(); } /// \brief Create a GOT entry containing 0. const GOTAtom *getNullGOT() { if (!_null) { _null = new (_file._alloc) ARMGOTPLTAtom(_file); #ifndef NDEBUG _null->_name = "__got_null"; #endif } return _null; } /// \brief Create regular GOT entry which cannot be used in PLTGOT operation. template const GOTAtom *getGOT(const Atom *da, StringRef source = "") { if (auto got = _gotAtoms.lookup(da)) return got; auto g = new (_file._alloc) ARMGOTAtom(_file); g->addReferenceELF_ARM(R_ARM_REL, 0, da, A); #ifndef NDEBUG g->_name = "__got"; g->_name += source.empty() ? "_" : source; g->_name += da->name(); #endif _gotAtoms[da] = g; return g; } /// \brief get GOT entry for a regular defined atom. const GOTAtom *getGOTEntry(const DefinedAtom *da) { return getGOT(da); } std::error_code handleGOT(const Reference &ref) { if (isa(ref.target())) const_cast(ref).setTarget(getNullGOT()); else if (const auto *da = dyn_cast(ref.target())) const_cast(ref).setTarget(getGOTEntry(da)); return std::error_code(); } public: ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} /// \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. std::error_code perform(SimpleFile &mf) override { ScopedTask task(getDefaultDomain(), "ARM GOT/PLT Pass"); DEBUG_WITH_TYPE( "ARM", llvm::dbgs() << "Undefined Atoms" << "\n"; for (const auto &atom : mf.undefined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Shared Library Atoms" << "\n"; for (const auto &atom : mf.sharedLibrary()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Absolute Atoms" << "\n"; for (const auto &atom : mf.absolute()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; } llvm::dbgs() << "Defined Atoms" << "\n"; for (const auto &atom : mf.defined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; }); // Process all references. for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } } // Add all created atoms to the link. uint64_t ordinal = 0; if (_plt0) { _plt0->setOrdinal(ordinal++); mf.addAtom(*_plt0); _plt0_d->setOrdinal(ordinal++); mf.addAtom(*_plt0_d); } for (auto &pltKV : _pltAtoms) { auto &plt = pltKV.second; if (auto *v = plt._veneer) { v->setOrdinal(ordinal++); mf.addAtom(*v); } auto *p = plt._plt; p->setOrdinal(ordinal++); mf.addAtom(*p); } if (_null) { _null->setOrdinal(ordinal++); mf.addAtom(*_null); } if (_plt0) { _got0->setOrdinal(ordinal++); mf.addAtom(*_got0); _got1->setOrdinal(ordinal++); mf.addAtom(*_got1); } for (auto &gotKV : _gotAtoms) { auto &got = gotKV.second; got->setOrdinal(ordinal++); mf.addAtom(*got); } for (auto &gotKV : _gotpltAtoms) { auto &got = gotKV.second; got->setOrdinal(ordinal++); mf.addAtom(*got); } for (auto &objectKV : _objectAtoms) { auto &obj = objectKV.second; obj->setOrdinal(ordinal++); mf.addAtom(*obj); } for (auto &veneerKV : _veneerAtoms) { auto &veneer = veneerKV.second; auto *m = veneer._mapping; m->setOrdinal(ordinal++); mf.addAtom(*m); auto *v = veneer._veneer; v->setOrdinal(ordinal++); mf.addAtom(*v); } return std::error_code(); } protected: /// \brief Owner of all the Atoms created by this pass. ELFPassFile _file; const ELFLinkingContext &_ctx; /// \brief Map Atoms to their GOT entries. llvm::MapVector _gotAtoms; /// \brief Map Atoms to their PLTGOT entries. llvm::MapVector _gotpltAtoms; /// \brief Map Atoms to their Object entries. llvm::MapVector _objectAtoms; /// \brief Map Atoms to their PLT entries depending on the code model. struct PLTWithVeneer { PLTWithVeneer(PLTAtom *p = nullptr, PLTAtom *v = nullptr) : _plt(p), _veneer(v) {} bool empty() const { assert((_plt || !_veneer) && "Veneer appears without PLT entry"); return !_plt && !_veneer; } PLTAtom *_plt; PLTAtom *_veneer; }; llvm::MapVector _pltAtoms; /// \brief Map Atoms to their veneers. struct VeneerWithMapping { VeneerWithMapping(VeneerAtom *v = nullptr, VeneerAtom *m = nullptr) : _veneer(v), _mapping(m) {} bool empty() const { assert(((bool)_veneer == (bool)_mapping) && "Mapping symbol should always be paired with veneer"); return !_veneer && !_mapping; } VeneerAtom *_veneer; VeneerAtom *_mapping; }; llvm::MapVector _veneerAtoms; /// \brief GOT entry that is always 0. Used for undefined weaks. 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 = nullptr; PLT0Atom *_plt0_d = nullptr; GOTAtom *_got0 = nullptr; GOTAtom *_got1 = nullptr; /// @} }; /// This implements the 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 ARMStaticRelocationPass final : public ARMRelocationPass { public: ARMStaticRelocationPass(const elf::ARMLinkingContext &ctx) : ARMRelocationPass(ctx) {} /// \brief Handle ordinary relocation references. std::error_code handlePlain(bool fromThumb, const Reference &ref) { return handleIFUNC(fromThumb, ref); } /// \brief Get the veneer for ARM B/BL instructions. const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, StringRef secName) { return getVeneer_ARM_B_BL_Abs(da, secName); } /// \brief Get the veneer for Thumb B/BL instructions. const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, StringRef secName) { return getVeneer_THM_B_BL_Abs(da, secName); } /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { return getGOTTLSEntry(da); } }; /// This implements the dynamic relocation model. GOT and PLT entries are /// created for references that cannot be directly resolved. class ARMDynamicRelocationPass final : public ARMRelocationPass { public: ARMDynamicRelocationPass(const elf::ARMLinkingContext &ctx) : ARMRelocationPass(ctx) {} /// \brief get the PLT entry for a given atom. const PLTAtom *getPLTEntry(const SharedLibraryAtom *sla, bool fromThumb) { return getPLT(sla, fromThumb, &ARMDynamicRelocationPass::createPLTGOT); } /// \brief Create the GOT entry for a given atom. const GOTAtom *createPLTGOT(const Atom *da) { assert(!_gotpltAtoms.lookup(da) && "PLTGOT entry already exists"); auto g = new (_file._alloc) ARMGOTPLTAtom(_file); g->addReferenceELF_ARM(R_ARM_ABS32, 0, getPLT0(), 0); g->addReferenceELF_ARM(R_ARM_JUMP_SLOT, 0, da, 0); #ifndef NDEBUG g->_name = "__got_plt0_"; g->_name += da->name(); #endif _gotpltAtoms[da] = g; return g; } const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { if (auto obj = _objectAtoms.lookup(a)) return obj; auto oa = new (_file._alloc) ARMObjectAtom(_file); oa->addReferenceELF_ARM(R_ARM_COPY, 0, oa, 0); oa->_name = a->name(); oa->_size = a->size(); _objectAtoms[a] = oa; return oa; } /// \brief Handle ordinary relocation references. std::error_code handlePlain(bool fromThumb, const Reference &ref) { if (auto sla = dyn_cast(ref.target())) { if (sla->type() == SharedLibraryAtom::Type::Data && _ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { const_cast(ref).setTarget(getObjectEntry(sla)); } else if (sla->type() == SharedLibraryAtom::Type::Code) { const_cast(ref).setTarget(getPLTEntry(sla, fromThumb)); } return std::error_code(); } return handleIFUNC(fromThumb, ref); } /// \brief Get the veneer for ARM B/BL instructions. const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, StringRef secName) { if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { return getVeneer_ARM_B_BL_Abs(da, secName); } llvm_unreachable("Handle ARM veneer for DSOs"); } /// \brief Get the veneer for Thumb B/BL instructions. const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, StringRef secName) { if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { return getVeneer_THM_B_BL_Abs(da, secName); } llvm_unreachable("Handle Thumb veneer for DSOs"); } /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { return getGOTTLSEntry(da); } const PLT0Atom *getPLT0() { if (_plt0) return _plt0; // Fill in the null entry. getNullGOT(); _plt0 = new (_file._alloc) ARMPLT0_a_Atom(_file, "__PLT0"); _plt0_d = new (_file._alloc) ARMPLT0_d_Atom(_file, "__PLT0_d"); _got0 = new (_file._alloc) ARMGOTPLTAtom(_file); _got1 = new (_file._alloc) ARMGOTPLTAtom(_file); _plt0_d->addReferenceELF_ARM(R_ARM_REL32, 0, _got1, 0); // Fake reference to show connection between the GOT and PLT entries. _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _got0, 0); // Fake reference to show connection between parts of PLT entry. _plt0->addReferenceELF_ARM(R_ARM_NONE, 0, _plt0_d, 0); #ifndef NDEBUG _got0->_name = "__got0"; _got1->_name = "__got1"; #endif return _plt0; } const GOTAtom *getSharedGOTEntry(const SharedLibraryAtom *sla) { return getGOT(sla); } std::error_code handleGOT(const Reference &ref) { if (const auto sla = dyn_cast(ref.target())) { const_cast(ref).setTarget(getSharedGOTEntry(sla)); return std::error_code(); } return ARMRelocationPass::handleGOT(ref); } }; } // end of anon namespace std::unique_ptr lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) { switch (ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: if (ctx.isDynamic()) return llvm::make_unique(ctx); return llvm::make_unique(ctx); case llvm::ELF::ET_DYN: return llvm::make_unique(ctx); default: llvm_unreachable("Unhandled output file type"); } }