summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp')
-rw-r--r--lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp606
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 &params,
+ 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 &params,
+ 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