diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp')
| -rw-r--r-- | lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp | 814 |
1 files changed, 713 insertions, 101 deletions
diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp index 27ec66ac5557..fc2ae75cd7a7 100644 --- a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -20,7 +20,7 @@ #include "ARMLinkingContext.h" #include "Atoms.h" #include "lld/Core/Simple.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" @@ -28,34 +28,77 @@ using namespace lld; using namespace lld::elf; using namespace llvm::ELF; -// ARM B/BL instructions of static relocation veneer. +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_StaticAtomContent[8] = { - 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] +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 // <target_symbol_address> }; -// Thumb B/BL instructions of static relocation veneer. +// 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_StaticAtomContent[8] = { +static const uint8_t Veneer_THM_B_BL_Abs_t_AtomContent[4] = { 0x78, 0x47, // bx pc - 0x00, 0x00, // nop + 0x00, 0x00 // nop +}; +static const uint8_t Veneer_THM_B_BL_Abs_a_AtomContent[4] = { 0xfe, 0xff, 0xff, 0xea // b <target_symbol_address> }; // .got values static const uint8_t ARMGotAtomContent[4] = {0}; -namespace { +// .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 // <got1_symbol_address> +}; + +// .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) - : SimpleELFDefinedAtom(f), _section(secName) {} + VeneerAtom(const File &f, StringRef secName, const std::string &name = "") + : SimpleELFDefinedAtom(f), _section(secName), _name(name) {} Scope scope() const override { return DefinedAtom::scopeTranslationUnit; } @@ -65,58 +108,208 @@ public: StringRef customSectionName() const override { return _section; } - ContentType contentType() const override { - return DefinedAtom::typeCode; - } + 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 Alignment(2); } + Alignment alignment() const override { return 4; } StringRef name() const override { return _name; } + +private: std::string _name; }; -/// \brief Atoms that hold veneer for statically relocated -/// ARM B/BL instructions. -class Veneer_ARM_B_BL_StaticAtom : public VeneerAtom { +/// \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_StaticAtom(const File &f, StringRef secName) - : VeneerAtom(f, secName) {} + Veneer_ARM_B_BL_Abs_a_Atom(const File &f, StringRef secName, + const std::string &name) + : VeneerAtom(f, secName, name) {} ArrayRef<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_ARM_B_BL_StaticAtomContent); + return llvm::makeArrayRef(Veneer_ARM_B_BL_Abs_a_AtomContent); } }; -/// \brief Atoms that hold veneer for statically relocated -/// Thumb B/BL instructions. -class Veneer_THM_B_BL_StaticAtom : public VeneerAtom { +class Veneer_ARM_B_BL_Abs_d_Atom : public VeneerAtom { public: - Veneer_THM_B_BL_StaticAtom(const File &f, StringRef secName) + Veneer_ARM_B_BL_Abs_d_Atom(const File &f, StringRef secName) : VeneerAtom(f, secName) {} + ArrayRef<uint8_t> 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<uint8_t> rawContent() const override { - return llvm::makeArrayRef(Veneer_THM_B_BL_StaticAtomContent); + 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<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_THM_B_BL_Abs_a_AtomContent); } }; +template <DefinedAtom::CodeModel Model> +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<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + DefinedAtom::CodeModel codeModel() const override { return Model; } +}; + +template <class BaseAtom, DefinedAtom::CodeModel Model> +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, StringRef secName) : GOTAtom(f, secName) {} + ARMGOTAtom(const File &f) : GOTAtom(f, ".got") {} ArrayRef<uint8_t> rawContent() const override { return llvm::makeArrayRef(ARMGotAtomContent); } - Alignment alignment() const override { return Alignment(2); } + 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<ARMPLT0Atom, DefinedAtom::codeARM_a> { +public: + ARMPLT0_a_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMPlt0_a_AtomContent); + } + + Alignment alignment() const override { return 4; } +}; + +class ARMPLT0_d_Atom + : public BaseMappingAtom<ARMPLT0Atom, DefinedAtom::codeARM_d> { +public: + ARMPLT0_d_Atom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> 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<PLTAtom, DefinedAtom::codeARM_a> { +public: + ARMPLTAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> 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<PLTAtom, DefinedAtom::codeARM_t> { +public: + ARMPLTVeneerAtom(const File &f, const std::string &name) + : BaseMappingAtom(f, ".plt", name) {} + + ArrayRef<uint8_t> 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 { @@ -140,30 +333,92 @@ template <class Derived> class ARMRelocationPass : public Pass { 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<Derived *>(this)->handlePlain(isThumbCode(&atom), ref); + break; + case R_ARM_THM_CALL: + case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_THM_JUMP24: - static_cast<Derived *>(this)->handleVeneer(atom, ref); - break; + case R_ARM_THM_JUMP11: { + const auto actualModel = actualSourceCodeModel(atom, ref); + const bool fromThumb = isThumbCode(actualModel); + static_cast<Derived *>(this)->handlePlain(fromThumb, ref); + static_cast<Derived *>(this)->handleVeneer(atom, fromThumb, ref); + } break; case R_ARM_TLS_IE32: static_cast<Derived *>(this)->handleTLSIE32(ref); break; + case R_ARM_GOT_BREL: + static_cast<Derived *>(this)->handleGOT(ref); + break; + default: + break; } } protected: - std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) { + /// \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<DefinedAtom>(ref.target())) { + actualModel = da->codeModel(); + } else if (const auto *sla = dyn_cast<SharedLibraryAtom>(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<DefinedAtom>(ref.target()); - if (!target || target->codeModel() == atom.codeModel()) + if (!target || isThumbCode(target) == isThumbCode(&atom)) return std::error_code(); - // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) - // fixup isn't possible without veneer generation for archs below ARMv5. - // Veneers may only be generated for STT_FUNC target symbols // or for symbols located in sections different to the place of relocation. - const auto kindValue = ref.kindValue(); StringRef secName = atom.customSectionName(); if (DefinedAtom::typeCode != target->contentType() && !target->customSectionName().equals(secName)) { @@ -182,29 +437,69 @@ protected: llvm_unreachable(errStr.c_str()); } - const Atom *veneer = nullptr; - switch (kindValue) { - case R_ARM_JUMP24: - veneer = static_cast<Derived *>(this) - ->getVeneer_ARM_B_BL(target, secName); - break; - case R_ARM_THM_JUMP24: - veneer = static_cast<Derived *>(this) - ->getVeneer_THM_B_BL(target, secName); - break; - default: - llvm_unreachable("Unhandled reference type for veneer generation"); - } + assert(getVeneer && "The veneer handler is missing"); + const Atom *veneer = + (static_cast<Derived *>(this)->*getVeneer)(target, secName); assert(veneer && "The veneer is not set"); const_cast<Reference &>(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<DefinedAtom::codeARM_a>(da, v_a, name); + auto v_d = new (_file._alloc) Veneer_ARM_B_BL_Abs_d_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_d>(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<DefinedAtom::codeARM_t>(da, v_t, name); + auto v_a = new (_file._alloc) Veneer_THM_B_BL_Abs_a_Atom(_file, secName); + addVeneerWithMapping<DefinedAtom::codeARM_a>(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<DefinedAtom>(ref.target())) { - const_cast<Reference &>(ref).setTarget( - static_cast<Derived *>(this)->getTLSTPOFF32(target)); + const_cast<Reference &>(ref) + .setTarget(static_cast<Derived *>(this)->getTLSTPOFF32(target)); return std::error_code(); } llvm_unreachable("R_ARM_TLS_IE32 reloc targets wrong atom type"); @@ -213,20 +508,160 @@ protected: /// \brief Create a GOT entry for TLS with reloc type and addend specified. template <Reference::KindValue R_ARM_TLS, Reference::Addend A = 0> const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) { - auto got = _gotMap.find(da); - if (got != _gotMap.end()) - return got->second; - auto g = new (_file._alloc) ARMGOTAtom(_file, ".got"); - g->addReferenceELF_ARM(R_ARM_TLS, 0, da, A); + StringRef source; +#ifndef NDEBUG + source = "_tls_"; +#endif + return getGOT<R_ARM_TLS, A>(da, source); + } + + /// \brief Add veneer with mapping symbol. + template <DefinedAtom::CodeModel Model> + 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<Model>(_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<Derived *>(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<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) { + const_cast<Reference &>(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 - g->_name = "__got_tls_"; + _null->_name = "__got_null"; +#endif + } + return _null; + } + + /// \brief Create regular GOT entry which cannot be used in PLTGOT operation. + template <Reference::KindValue R_ARM_REL, Reference::Addend A = 0> + 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 - _gotMap[da] = g; - _gotVector.push_back(g); + _gotAtoms[da] = g; return g; } + /// \brief get GOT entry for a regular defined atom. + const GOTAtom *getGOTEntry(const DefinedAtom *da) { + return getGOT<R_ARM_ABS32>(da); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const auto *da = dyn_cast<DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOTEntry(da)); + return std::error_code(); + } + public: ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} @@ -238,35 +673,35 @@ 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(), "ARM GOT/PLT Pass"); DEBUG_WITH_TYPE( "ARM", 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"; } llvm::dbgs() << "Defined Atoms" << "\n"; for (const auto &atom - : mf->defined()) { + : mf.defined()) { llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; }); // Process all references. - for (const auto &atom : mf->defined()) { + for (const auto &atom : mf.defined()) { for (const auto &ref : *atom) { handleReference(*atom, *ref); } @@ -274,14 +709,58 @@ public: // Add all created atoms to the link. uint64_t ordinal = 0; - for (auto &got : _gotVector) { + 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); + mf.addAtom(*got); } - for (auto &veneer : _veneerVector) { - veneer->setOrdinal(ordinal++); - mf->addAtom(*veneer); + 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: @@ -290,16 +769,56 @@ protected: const ELFLinkingContext &_ctx; /// \brief Map Atoms to their GOT entries. - llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + llvm::MapVector<const Atom *, GOTAtom *> _gotAtoms; + + /// \brief Map Atoms to their PLTGOT entries. + llvm::MapVector<const Atom *, GOTAtom *> _gotpltAtoms; + + /// \brief Map Atoms to their Object entries. + llvm::MapVector<const Atom *, ObjectAtom *> _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<const Atom *, PLTWithVeneer> _pltAtoms; /// \brief Map Atoms to their veneers. - llvm::DenseMap<const Atom *, VeneerAtom *> _veneerMap; + 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; + } - /// \brief the list of GOT/PLT atoms - std::vector<GOTAtom *> _gotVector; + VeneerAtom *_veneer; + VeneerAtom *_mapping; + }; + llvm::MapVector<const Atom *, VeneerWithMapping> _veneerAtoms; - /// \brief the list of veneer atoms. - std::vector<VeneerAtom *> _veneerVector; + /// \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 @@ -314,47 +833,138 @@ 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) { - auto veneer = _veneerMap.find(da); - if (_veneerMap.end() != veneer) - return veneer->second; + 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); + } - auto v = new (_file._alloc) Veneer_ARM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_ABS32, 4, da, 0); + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + } +}; - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_arm"; +/// This implements the dynamic relocation model. GOT and PLT entries are +/// created for references that cannot be directly resolved. +class ARMDynamicRelocationPass final + : public ARMRelocationPass<ARMDynamicRelocationPass> { +public: + ARMDynamicRelocationPass(const elf::ARMLinkingContext &ctx) + : ARMRelocationPass(ctx) {} - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + /// \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<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data && + _ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + } else if (sla->type() == SharedLibraryAtom::Type::Code) { + const_cast<Reference &>(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) { - auto veneer = _veneerMap.find(da); - if (_veneerMap.end() != veneer) - return veneer->second; + if (_ctx.getOutputELFType() == llvm::ELF::ET_EXEC) { + return getVeneer_THM_B_BL_Abs(da, secName); + } + llvm_unreachable("Handle Thumb veneer for DSOs"); + } - auto v = new (_file._alloc) Veneer_THM_B_BL_StaticAtom(_file, secName); - v->addReferenceELF_ARM(R_ARM_JUMP24, 4, da, 0); + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_TPOFF32>(da); + } - v->_name = "__"; - v->_name += da->name(); - v->_name += "_from_thumb"; + 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; + } - _veneerMap[da] = v; - _veneerVector.push_back(v); - return v; + const GOTAtom *getSharedGOTEntry(const SharedLibraryAtom *sla) { + return getGOT<R_ARM_GLOB_DAT>(sla); } - /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. - const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { - return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + std::error_code handleGOT(const Reference &ref) { + if (const auto sla = dyn_cast<const SharedLibraryAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget(getSharedGOTEntry(sla)); + return std::error_code(); + } + return ARMRelocationPass::handleGOT(ref); } }; @@ -365,8 +975,10 @@ lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) { switch (ctx.getOutputELFType()) { case llvm::ELF::ET_EXEC: if (ctx.isDynamic()) - llvm_unreachable("Unhandled output file type"); + return llvm::make_unique<ARMDynamicRelocationPass>(ctx); return llvm::make_unique<ARMStaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<ARMDynamicRelocationPass>(ctx); default: llvm_unreachable("Unhandled output file type"); } |
