forked from OSchip/llvm-project
986 lines
32 KiB
C++
986 lines
32 KiB
C++
//===--------- 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 // <target_symbol_address>
|
|
};
|
|
|
|
// 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 <target_symbol_address>
|
|
};
|
|
|
|
// .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 // <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, 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<uint8_t> 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<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_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) : GOTAtom(f, ".got") {}
|
|
|
|
ArrayRef<uint8_t> 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<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 {
|
|
public:
|
|
ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") {
|
|
setOrdinal(eti.getNextOrdinalAndIncrement());
|
|
}
|
|
|
|
llvm::BumpPtrAllocator _alloc;
|
|
};
|
|
|
|
/// \brief CRTP base for handling relocations.
|
|
template <class Derived> 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<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:
|
|
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:
|
|
/// \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 || 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<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));
|
|
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 <Reference::KindValue R_ARM_TLS, Reference::Addend A = 0>
|
|
const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) {
|
|
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
|
|
_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
|
|
_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) {}
|
|
|
|
/// \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<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.
|
|
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<const Atom *, VeneerWithMapping> _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<ARMStaticRelocationPass> {
|
|
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<R_ARM_TLS_LE32>(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<ARMDynamicRelocationPass> {
|
|
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<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) {
|
|
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<R_ARM_TLS_TPOFF32>(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<R_ARM_GLOB_DAT>(sla);
|
|
}
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
} // end of anon namespace
|
|
|
|
std::unique_ptr<Pass>
|
|
lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) {
|
|
switch (ctx.getOutputELFType()) {
|
|
case llvm::ELF::ET_EXEC:
|
|
if (ctx.isDynamic())
|
|
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");
|
|
}
|
|
}
|