//===- 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" #include "lld/ReaderWriter/RelocationHelperFunctions.h" using namespace lld; using namespace elf; using namespace llvm::ELF; static inline void applyReloc(uint8_t *loc, uint32_t result, uint32_t mask) { auto target = reinterpret_cast(loc); *target = (uint32_t(*target) & ~mask) | (result & mask); } template inline T signExtend(T val) { if (val & (T(1) << (BITS - 1))) val |= T(-1) << BITS; return val; } /// \brief R_MIPS_32 /// local/external: word32 S + A (truncate) static void reloc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { applyReloc(location, S + A, 0xffffffff); } /// \brief R_MIPS_PC32 /// local/external: word32 S + A i- P (truncate) void relocpc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { applyReloc(location, S + A - P, 0xffffffff); } /// \brief R_MIPS_26 /// local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 static void reloc26loc(uint8_t *location, uint64_t P, uint64_t S, int32_t A) { uint32_t result = ((A << 2) | ((P + 4) & 0x3f000000)) + S; applyReloc(location, result >> 2, 0x03ffffff); } /// \brief LLD_R_MIPS_GLOBAL_26 /// external: (sign-extend(A) + S) >> 2 static void reloc26ext(uint8_t *location, uint64_t S, int32_t A) { uint32_t result = signExtend<28>(A << 2) + S; applyReloc(location, result >> 2, 0x03ffffff); } /// \brief R_MIPS_HI16 /// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate) /// _gp_disp : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify) static void relocHi16(uint8_t *location, uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { int32_t result = 0; if (isGPDisp) result = (AHL + S - P) - (int16_t)(AHL + S - P); else result = (AHL + S) - (int16_t)(AHL + S); applyReloc(location, result >> 16, 0xffff); } /// \brief R_MIPS_LO16 /// local/external: lo16 AHL + S (truncate) /// _gp_disp : lo16 AHL + GP - P + 4 (verify) static void relocLo16(uint8_t *location, uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { int32_t result = 0; if (isGPDisp) result = AHL + S - P + 4; else result = AHL + S; applyReloc(location, result, 0xffff); } /// \brief R_MIPS_GOT16, R_MIPS_CALL16 /// rel16 G (verify) static void relocGOT(uint8_t *location, uint64_t P, uint64_t S, int64_t A, uint64_t GP) { int32_t G = (int32_t)(S - GP); applyReloc(location, G, 0xffff); } /// \brief R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, LLD_R_MIPS_HI16 /// (S + A) >> 16 static void relocGeneralHi16(uint8_t *location, uint64_t S, int64_t A) { int32_t result = S + A + 0x8000; applyReloc(location, result >> 16, 0xffff); } /// \brief R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, LLD_R_MIPS_LO16 /// S + A static void relocGeneralLo16(uint8_t *location, uint64_t S, int64_t A) { int32_t result = S + A; applyReloc(location, result, 0xffff); } /// \brief R_MIPS_GPREL32 /// local: rel32 A + S + GP0 – GP (truncate) static void relocGPRel32(uint8_t *location, uint64_t P, uint64_t S, int64_t A, uint64_t GP) { int32_t result = A + S + 0 - GP; applyReloc(location, result, 0xffffffff); } /// \brief LLD_R_MIPS_32_HI16 static void reloc32hi16(uint8_t *location, uint64_t S, int64_t A) { applyReloc(location, (S + A + 0x8000) & 0xffff0000, 0xffffffff); } std::error_code MipsTargetRelocationHandler::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); AtomLayout *gpAtom = _mipsTargetLayout.getGP(); uint64_t gpAddr = gpAtom ? gpAtom->_virtualAddr : 0; AtomLayout *gpDispAtom = _mipsTargetLayout.getGPDisp(); bool isGpDisp = gpDispAtom && ref.target() == gpDispAtom->_atom; uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; uint8_t *location = atomContent + ref.offsetInAtom(); uint64_t targetVAddress = writer.addressOfAtom(ref.target()); uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); switch (ref.kindValue()) { case R_MIPS_NONE: break; case R_MIPS_32: reloc32(location, relocVAddress, targetVAddress, ref.addend()); break; case R_MIPS_26: reloc26loc(location, relocVAddress, targetVAddress, ref.addend()); break; case R_MIPS_HI16: relocHi16(location, relocVAddress, targetVAddress, ref.addend(), isGpDisp); break; case R_MIPS_LO16: relocLo16(location, relocVAddress, targetVAddress, ref.addend(), isGpDisp); break; case R_MIPS_GOT16: relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr); break; case R_MIPS_CALL16: relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr); break; case R_MIPS_TLS_GD: relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr); break; case R_MIPS_TLS_LDM: case R_MIPS_TLS_GOTTPREL: relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr); break; case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_TPREL_HI16: relocGeneralHi16(location, targetVAddress, ref.addend()); break; case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_TPREL_LO16: relocGeneralLo16(location, targetVAddress, ref.addend()); break; case R_MIPS_GPREL32: relocGPRel32(location, relocVAddress, targetVAddress, ref.addend(), gpAddr); break; case R_MIPS_JALR: // We do not do JALR optimization now. break; 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. break; case R_MIPS_PC32: relocpc32(location, relocVAddress, targetVAddress, ref.addend()); break; case LLD_R_MIPS_GLOBAL_GOT: // Do nothing. break; case LLD_R_MIPS_32_HI16: reloc32hi16(location, targetVAddress, ref.addend()); break; case LLD_R_MIPS_GLOBAL_26: reloc26ext(location, targetVAddress, ref.addend()); break; case LLD_R_MIPS_HI16: relocGeneralHi16(location, targetVAddress, 0); break; case LLD_R_MIPS_LO16: relocGeneralLo16(location, targetVAddress, 0); break; case LLD_R_MIPS_STO_PLT: // Do nothing. break; default: { std::string str; llvm::raw_string_ostream s(str); s << "Unhandled Mips relocation: " << ref.kindValue(); llvm_unreachable(s.str().c_str()); } } return std::error_code(); }