summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp')
-rw-r--r--lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp814
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");
}