diff options
Diffstat (limited to 'lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp')
-rw-r--r-- | lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp new file mode 100644 index 0000000000000..173ce0e6b1a87 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp @@ -0,0 +1,606 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsTargetHandler.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" + +using namespace lld; +using namespace elf; +using namespace llvm::ELF; +using namespace llvm::support; + +namespace { +enum class CrossJumpMode { + None, // Not a jump or non-isa-cross jump + ToRegular, // cross isa jump to regular symbol + ToMicro // cross isa jump to microMips symbol +}; + +struct MipsRelocationParams { + uint8_t _size; // Relocations's size in bytes + uint64_t _mask; // Read/write mask of relocation + uint8_t _shift; // Relocation's addendum left shift size + bool _shuffle; // Relocation's addendum/result needs to be shuffled +}; + +template <class ELFT> class RelocationHandler : public MipsRelocationHandler { +public: + RelocationHandler(MipsLinkingContext &ctx) : _ctx(ctx) {} + + std::error_code applyRelocation(ELFWriter &writer, + llvm::FileOutputBuffer &buf, + const lld::AtomLayout &atom, + const Reference &ref) const override; + + Reference::Addend readAddend(Reference::KindValue kind, + const uint8_t *content) const override; + +private: + MipsLinkingContext &_ctx; +}; +} + +static MipsRelocationParams getRelocationParams(uint32_t rType) { + switch (rType) { + case R_MIPS_NONE: + return {4, 0x0, 0, false}; + case R_MIPS_64: + case R_MIPS_SUB: + return {8, 0xffffffffffffffffull, 0, false}; + case R_MIPS_32: + case R_MIPS_GPREL32: + case R_MIPS_PC32: + return {4, 0xffffffff, 0, false}; + case LLD_R_MIPS_32_HI16: + return {4, 0xffff0000, 0, false}; + case LLD_R_MIPS_64_HI16: + return {8, 0xffffffffffff0000ull, 0, false}; + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_PC18_S3: + return {4, 0x3ffff, 3, false}; + case R_MIPS_PC19_S2: + return {4, 0x7ffff, 2, false}; + case R_MIPS_PC21_S2: + return {4, 0x1fffff, 2, false}; + case R_MIPS_PC26_S2: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MIPS_GPREL16: + case R_MIPS_GOT16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MIPS_GOT_OFST: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case LLD_R_MIPS_HI16: + case LLD_R_MIPS_LO16: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return {4, 0x3ffffff, 1, true}; + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + case R_MICROMIPS_GOT16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_PC16_S1: + return {4, 0xffff, 1, true}; + case R_MICROMIPS_PC7_S1: + return {4, 0x7f, 1, false}; + case R_MICROMIPS_PC10_S1: + return {4, 0x3ff, 1, false}; + case R_MICROMIPS_PC23_S2: + return {4, 0x7fffff, 2, true}; + case R_MIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_CALL16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, true}; + case R_MIPS_JALR: + return {4, 0x0, 0, false}; + case R_MICROMIPS_JALR: + return {4, 0x0, 0, true}; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + // Ignore runtime relocations. + return {4, 0x0, 0, false}; + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + return {8, 0x0, 0, false}; + case LLD_R_MIPS_GLOBAL_GOT: + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return {4, 0x0, 0, false}; + default: + llvm_unreachable("Unknown relocation"); + } +} + +/// \brief R_MIPS_32 +/// local/external: word32 S + A (truncate) +static uint32_t reloc32(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_64 +/// local/external: word64 S + A (truncate) +static uint64_t reloc64(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_SUB +/// local/external: word64 S - A (truncate) +static uint64_t relocSub(uint64_t S, int64_t A) { return S - A; } + +/// \brief R_MIPS_PC32 +/// local/external: word32 S + A i- P (truncate) +static uint32_t relocpc32(uint64_t P, uint64_t S, int64_t A) { + return S + A - P; +} + +/// \brief R_MIPS_26, R_MICROMIPS_26_S1 +/// local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 +static uint32_t reloc26loc(uint64_t P, uint64_t S, int32_t A, uint32_t shift) { + uint32_t result = (A | ((P + 4) & (0xfc000000 << shift))) + S; + return result >> shift; +} + +/// \brief LLD_R_MIPS_GLOBAL_26, LLD_R_MICROMIPS_GLOBAL_26_S1 +/// external: (sign-extend(A) + S) >> 2 +static uint32_t reloc26ext(uint64_t S, int32_t A, uint32_t shift) { + int32_t result = + shift == 1 ? llvm::SignExtend32<27>(A) : llvm::SignExtend32<28>(A); + return (result + S) >> shift; +} + +/// \brief R_MIPS_HI16, R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, +/// R_MICROMIPS_HI16, R_MICROMIPS_TLS_DTPREL_HI16, R_MICROMIPS_TLS_TPREL_HI16, +/// LLD_R_MIPS_HI16 +/// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate) +/// _gp_disp : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify) +static uint32_t relocHi16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { + int32_t result = isGPDisp ? AHL + S - P : AHL + S; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_PCHI16 +/// local/external: hi16 (S + AHL - P) +static uint32_t relocPcHi16(uint64_t P, uint64_t S, int64_t AHL) { + int32_t result = S + AHL - P; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_LO16, R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, +/// R_MICROMIPS_LO16, R_MICROMIPS_TLS_DTPREL_LO16, R_MICROMIPS_TLS_TPREL_LO16, +/// LLD_R_MIPS_LO16 +/// local/external: lo16 AHL + S (truncate) +/// _gp_disp : lo16 AHL + GP - P + 4 (verify) +static uint32_t relocLo16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp, + bool micro) { + int32_t result = isGPDisp ? AHL + S - P + (micro ? 3 : 4) : AHL + S; + return result; +} + +/// \brief R_MIPS_PCLO16 +/// local/external: lo16 (S + AHL - P) +static uint32_t relocPcLo16(uint64_t P, uint64_t S, int64_t AHL) { + AHL = llvm::SignExtend32<16>(AHL); + int32_t result = S + AHL - P; + return result; +} + +/// \brief R_MIPS_GOT16, R_MIPS_CALL16, R_MICROMIPS_GOT16, R_MICROMIPS_CALL16 +/// rel16 G (verify) +static uint64_t relocGOT(uint64_t S, uint64_t GP) { + int64_t G = (int64_t)(S - GP); + return G; +} + +/// R_MIPS_GOT_OFST +/// rel16 offset of (S+A) from the page pointer (verify) +static uint32_t relocGOTOfst(uint64_t S, int64_t A) { + uint64_t page = (S + A + 0x8000) & ~0xffff; + return S + A - page; +} + +/// \brief R_MIPS_GPREL16 +/// local: sign-extend(A) + S + GP0 - GP +/// external: sign-extend(A) + S - GP +static uint64_t relocGPRel16(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return llvm::SignExtend32<16>(A) + S - GP; +} + +/// \brief R_MIPS_GPREL32 +/// local: rel32 A + S + GP0 - GP (truncate) +static uint64_t relocGPRel32(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return A + S - GP; +} + +/// \brief R_MIPS_PC18_S3 +/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) +static uint32_t relocPc18(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 8-byte alignment + int32_t result = S + A - ((P | 7) ^ 7); + return result >> 3; +} + +/// \brief R_MIPS_PC19_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc19(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC21_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc21(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<23>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC26_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc26(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<28>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MICROMIPS_PC7_S1 +static uint32_t relocPc7(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<8>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC10_S1 +static uint32_t relocPc10(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<11>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC16_S1 +static uint32_t relocPc16(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<17>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC23_S2 +static uint32_t relocPc23(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<25>(A); + int32_t result = S + A - P; + + // Check addiupc 16MB range. + if (result + 0x1000000 >= 0x2000000) + llvm::errs() << "The addiupc instruction immediate " + << llvm::format_hex(result, 10) << " is out of range.\n"; + + return result >> 2; +} + +/// \brief LLD_R_MIPS_32_HI16, LLD_R_MIPS_64_HI16 +static uint64_t relocMaskLow16(uint64_t S, int64_t A) { + return S + A + 0x8000; +} + +static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, + CrossJumpMode mode) { + if (mode == CrossJumpMode::None) + return std::error_code(); + + bool toMicro = mode == CrossJumpMode::ToMicro; + uint32_t opNative = toMicro ? 0x03 : 0x3d; + uint32_t opCross = toMicro ? 0x1d : 0x3c; + + if ((tgt & 1) != toMicro) + return make_dynamic_error_code( + Twine("Incorrect bit 0 for the jalx target")); + + if (tgt & 2) + return make_dynamic_error_code(Twine("The jalx target 0x") + + Twine::utohexstr(tgt) + + " is not word-aligned"); + uint8_t op = ins >> 26; + if (op != opNative && op != opCross) + return make_dynamic_error_code(Twine("Unsupported jump opcode (0x") + + Twine::utohexstr(op) + + ") for ISA modes cross call"); + + ins = (ins & ~(0x3f << 26)) | (opCross << 26); + return std::error_code(); +} + +static bool isMicroMipsAtom(const Atom *a) { + if (const auto *da = dyn_cast<DefinedAtom>(a)) + return da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC; + return false; +} + +static CrossJumpMode getCrossJumpMode(const Reference &ref) { + if (!isa<DefinedAtom>(ref.target())) + return CrossJumpMode::None; + bool isTgtMicro = isMicroMipsAtom(ref.target()); + switch (ref.kindValue()) { + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return isTgtMicro ? CrossJumpMode::ToMicro : CrossJumpMode::None; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return isTgtMicro ? CrossJumpMode::None : CrossJumpMode::ToRegular; + default: + return CrossJumpMode::None; + } +} + +static uint32_t microShuffle(uint32_t ins) { + return ((ins & 0xffff) << 16) | ((ins & 0xffff0000) >> 16); +} + +static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, + Reference::Addend addend, + uint64_t tgtAddr, uint64_t relAddr, + uint64_t gpAddr, bool isGP, + CrossJumpMode jumpMode) { + bool isCrossJump = jumpMode != CrossJumpMode::None; + switch (kind) { + case R_MIPS_NONE: + return 0; + case R_MIPS_32: + return reloc32(tgtAddr, addend); + case R_MIPS_64: + return reloc64(tgtAddr, addend); + case R_MIPS_SUB: + return relocSub(tgtAddr, addend); + case R_MIPS_26: + return reloc26loc(relAddr, tgtAddr, addend, 2); + case R_MICROMIPS_26_S1: + return reloc26loc(relAddr, tgtAddr, addend, isCrossJump ? 2 : 1); + case R_MIPS_HI16: + case R_MICROMIPS_HI16: + return relocHi16(relAddr, tgtAddr, addend, isGP); + case R_MIPS_PCHI16: + return relocPcHi16(relAddr, tgtAddr, addend); + case R_MIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, false); + case R_MIPS_PCLO16: + return relocPcLo16(relAddr, tgtAddr, addend); + case R_MICROMIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, true); + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MICROMIPS_GOT16: + case R_MICROMIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return relocGOT(tgtAddr, gpAddr); + case R_MIPS_GOT_OFST: + return relocGOTOfst(tgtAddr, addend); + case R_MIPS_PC18_S3: + return relocPc18(relAddr, tgtAddr, addend); + case R_MIPS_PC19_S2: + return relocPc19(relAddr, tgtAddr, addend); + case R_MIPS_PC21_S2: + return relocPc21(relAddr, tgtAddr, addend); + case R_MIPS_PC26_S2: + return relocPc26(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC7_S1: + return relocPc7(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC10_S1: + return relocPc10(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC16_S1: + return relocPc16(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC23_S2: + return relocPc23(relAddr, tgtAddr, addend); + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_TPREL_HI16: + return relocHi16(0, tgtAddr, addend, false); + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, false); + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, true); + case R_MIPS_GPREL16: + return relocGPRel16(tgtAddr, addend, gpAddr); + case R_MIPS_GPREL32: + return relocGPRel32(tgtAddr, addend, gpAddr); + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + // We do not do JALR optimization now. + return 0; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + // Ignore runtime relocations. + return 0; + case R_MIPS_PC32: + return relocpc32(relAddr, tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_GOT: + // Do nothing. + case LLD_R_MIPS_32_HI16: + case LLD_R_MIPS_64_HI16: + return relocMaskLow16(tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_26: + return reloc26ext(tgtAddr, addend, 2); + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return reloc26ext(tgtAddr, addend, isCrossJump ? 2 : 1); + case LLD_R_MIPS_HI16: + return relocHi16(0, tgtAddr, 0, false); + case LLD_R_MIPS_LO16: + return relocLo16(0, tgtAddr, 0, false, false); + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return 0; + default: + return make_unhandled_reloc_error(); + } +} + +template <class ELFT> +static uint64_t relocRead(const MipsRelocationParams ¶ms, + const uint8_t *loc) { + uint64_t data; + switch (params._size) { + case 4: + data = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); + break; + case 8: + data = endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(loc); + break; + default: + llvm_unreachable("Unexpected size"); + } + if (params._shuffle) + data = microShuffle(data); + return data; +} + +template <class ELFT> +static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, + uint8_t *loc) { + if (params._shuffle) + data = microShuffle(data); + switch (params._size) { + case 4: + endian::write<uint32_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + case 8: + endian::write<uint64_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + default: + llvm_unreachable("Unexpected size"); + } +} + +template <class ELFT> +std::error_code RelocationHandler<ELFT>::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::Mips); + + auto &targetLayout = static_cast<MipsTargetLayout<ELFT> &>( + _ctx.getTargetHandler<ELFT>().getTargetLayout()); + + AtomLayout *gpAtom = targetLayout.getGP(); + uint64_t gpAddr = gpAtom ? gpAtom->_virtualAddr : 0; + + AtomLayout *gpDispAtom = targetLayout.getGPDisp(); + bool isGpDisp = gpDispAtom && ref.target() == gpDispAtom->_atom; + + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t tgtAddr = writer.addressOfAtom(ref.target()); + uint64_t relAddr = atom._virtualAddr + ref.offsetInAtom(); + + if (isMicroMipsAtom(ref.target())) + tgtAddr |= 1; + + CrossJumpMode jumpMode = getCrossJumpMode(ref); + + ErrorOr<uint64_t> res = + calculateRelocation(ref.kindValue(), ref.addend(), tgtAddr, relAddr, + gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + + Reference::KindValue op = ref.kindValue(); + + // FIXME (simon): Handle r_ssym value. + for (auto tag = (ref.tag() & 0xffff); tag & 0xff; tag >>= 8) { + op = tag & 0xff; + res = calculateRelocation(op, *res, 0, relAddr, gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + } + + auto params = getRelocationParams(op); + uint64_t ins = relocRead<ELFT>(params, location); + + if (auto ec = adjustJumpOpCode(ins, tgtAddr, jumpMode)) + return ec; + + ins = (ins & ~params._mask) | (*res & params._mask); + relocWrite<ELFT>(ins, params, location); + + return std::error_code(); +} + +template <class ELFT> +Reference::Addend +RelocationHandler<ELFT>::readAddend(Reference::KindValue kind, + const uint8_t *content) const { + auto params = getRelocationParams(kind); + uint64_t ins = relocRead<ELFT>(params, content); + return (ins & params._mask) << params._shift; +} + +namespace lld { +namespace elf { + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips32ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips32ELType>(ctx)); +} + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips64ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips64ELType>(ctx)); +} + +} // elf +} // lld |