diff options
Diffstat (limited to 'ELF/Target.cpp')
-rw-r--r-- | ELF/Target.cpp | 437 |
1 files changed, 261 insertions, 176 deletions
diff --git a/ELF/Target.cpp b/ELF/Target.cpp index 55fcf1734d1fa..664dcd1ed44e6 100644 --- a/ELF/Target.cpp +++ b/ELF/Target.cpp @@ -45,7 +45,10 @@ using namespace llvm::support::endian; using namespace llvm::ELF; std::string lld::toString(uint32_t Type) { - return getELFRelocationTypeName(elf::Config->EMachine, Type); + StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type); + if (S == "Unknown") + return ("Unknown (" + Twine(Type) + ")").str(); + return S; } namespace lld { @@ -56,20 +59,20 @@ TargetInfo *Target; static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } static void or32be(uint8_t *P, int32_t V) { write32be(P, read32be(P) | V); } -template <class ELFT> static std::string getErrorLoc(uint8_t *Loc) { - for (InputSectionData *D : Symtab<ELFT>::X->Sections) { - auto *IS = dyn_cast_or_null<InputSection<ELFT>>(D); +template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) { + for (InputSectionBase *D : InputSections) { + auto *IS = dyn_cast_or_null<InputSection>(D); if (!IS || !IS->OutSec) continue; - uint8_t *ISLoc = cast<OutputSection<ELFT>>(IS->OutSec)->Loc + IS->OutSecOff; + uint8_t *ISLoc = cast<OutputSection>(IS->OutSec)->Loc + IS->OutSecOff; if (ISLoc <= Loc && Loc < ISLoc + IS->getSize()) - return IS->getLocation(Loc - ISLoc) + ": "; + return IS->template getLocation<ELFT>(Loc - ISLoc) + ": "; } return ""; } -static std::string getErrorLocation(uint8_t *Loc) { +static std::string getErrorLocation(const uint8_t *Loc) { switch (Config->EKind) { case ELF32LEKind: return getErrorLoc<ELF32LE>(Loc); @@ -116,17 +119,17 @@ namespace { class X86TargetInfo final : public TargetInfo { public: X86TargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; - uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; + int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; uint32_t getDynRel(uint32_t Type) const override; bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsGlobalDynamicRel(uint32_t Type) const override; bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -141,15 +144,15 @@ public: template <class ELFT> class X86_64TargetInfo final : public TargetInfo { public: X86_64TargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; bool isPicRel(uint32_t Type) const override; bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsGlobalDynamicRel(uint32_t Type) const override; bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPltHeader(uint8_t *Buf) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -170,14 +173,16 @@ class PPCTargetInfo final : public TargetInfo { public: PPCTargetInfo(); void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; }; class PPC64TargetInfo final : public TargetInfo { public: PPC64TargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; }; @@ -185,12 +190,13 @@ public: class AArch64TargetInfo final : public TargetInfo { public: AArch64TargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; bool isPicRel(uint32_t Type) const override; bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; bool usesOnlyLowPageBits(uint32_t Type) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -205,44 +211,47 @@ class AMDGPUTargetInfo final : public TargetInfo { public: AMDGPUTargetInfo(); void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; }; class ARMTargetInfo final : public TargetInfo { public: ARMTargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; bool isPicRel(uint32_t Type) const override; uint32_t getDynRel(uint32_t Type) const override; - uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; + int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsGlobalDynamicRel(uint32_t Type) const override; bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File, - const SymbolBody &S) const override; + void addPltSymbols(InputSectionBase *IS, uint64_t Off) const override; + void addPltHeaderSymbols(InputSectionBase *ISD) const override; + bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, + const SymbolBody &S) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; }; template <class ELFT> class MipsTargetInfo final : public TargetInfo { public: MipsTargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; - uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; + int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; bool isPicRel(uint32_t Type) const override; uint32_t getDynRel(uint32_t Type) const override; bool isTlsLocalDynamicRel(uint32_t Type) const override; - bool isTlsGlobalDynamicRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; - void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File, - const SymbolBody &S) const override; + bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, + const SymbolBody &S) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; bool usesOnlyLowPageBits(uint32_t Type) const override; }; @@ -286,25 +295,21 @@ TargetInfo *createTarget() { TargetInfo::~TargetInfo() {} -uint64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, - uint32_t Type) const { +int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const { return 0; } bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; } -RelExpr TargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType, - const InputFile &File, - const SymbolBody &S) const { - return Expr; +bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType, + const InputFile *File, const SymbolBody &S) const { + return false; } bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; } bool TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return false; } -bool TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { return false; } - void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const { writeGotPlt(Buf, S); } @@ -352,10 +357,14 @@ X86TargetInfo::X86TargetInfo() { PltEntrySize = 16; PltHeaderSize = 16; TlsGdRelaxSkip = 2; + // 0xCC is the "int3" (call debug exception handler) instruction. + TrapInstr = 0xcccccccc; } -RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { + case R_386_8: case R_386_16: case R_386_32: case R_386_TLS_LDO_32: @@ -366,6 +375,7 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { return R_TLSLD; case R_386_PLT32: return R_PLT_PC; + case R_386_PC8: case R_386_PC16: case R_386_PC32: return R_PC; @@ -375,6 +385,24 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { return R_GOT; case R_386_GOT32: case R_386_GOT32X: + // These relocations can be calculated in two different ways. + // Usual calculation is G + A - GOT what means an offset in GOT table + // (R_GOT_FROM_END). When instruction pointed by relocation has no base + // register, then relocations can be used when PIC code is disabled. In that + // case calculation is G + A, it resolves to an address of entry in GOT + // (R_GOT) and not an offset. + // + // To check that instruction has no base register we scan ModR/M byte. + // See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte" + // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/ + // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) + if ((Loc[-1] & 0xc7) != 0x5) + return R_GOT_FROM_END; + if (Config->Pic) + error(toString(S.File) + ": relocation " + toString(Type) + " against '" + + S.getName() + + "' without base register can not be used when PIC enabled"); + return R_GOT; case R_386_TLS_GOTIE: return R_GOT_FROM_END; case R_386_GOTOFF: @@ -384,10 +412,9 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { case R_386_TLS_LE_32: return R_NEG_TLS; case R_386_NONE: - return R_HINT; + return R_NONE; default: - error("do not know how to handle relocation '" + toString(Type) + "' (" + - Twine(Type) + ")"); + error(toString(S.File) + ": unknown relocation type: " + toString(Type)); return R_HINT; } } @@ -411,12 +438,12 @@ void X86TargetInfo::writeGotPltHeader(uint8_t *Buf) const { void X86TargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const { // Entries in .got.plt initially points back to the corresponding // PLT entries with a fixed offset to skip the first instruction. - write32le(Buf, S.getPltVA<ELF32LE>() + 6); + write32le(Buf, S.getPltVA() + 6); } void X86TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const { // An x86 entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA<ELF32LE>()); + write32le(Buf, S.getVA()); } uint32_t X86TargetInfo::getDynRel(uint32_t Type) const { @@ -427,10 +454,6 @@ uint32_t X86TargetInfo::getDynRel(uint32_t Type) const { return Type; } -bool X86TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { - return Type == R_386_TLS_GD; -} - bool X86TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return Type == R_386_TLS_LDO_32 || Type == R_386_TLS_LDM; } @@ -440,30 +463,33 @@ bool X86TargetInfo::isTlsInitialExecRel(uint32_t Type) const { } void X86TargetInfo::writePltHeader(uint8_t *Buf) const { - // Executable files and shared object files have - // separate procedure linkage tables. if (Config->Pic) { const uint8_t V[] = { - 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx) - 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx) - 0x90, 0x90, 0x90, 0x90 // nop; nop; nop; nop + 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx) + 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx) + 0x90, 0x90, 0x90, 0x90 // nop }; memcpy(Buf, V, sizeof(V)); + + uint32_t Ebx = In<ELF32LE>::Got->getVA() + In<ELF32LE>::Got->getSize(); + uint32_t GotPlt = In<ELF32LE>::GotPlt->getVA() - Ebx; + write32le(Buf + 2, GotPlt + 4); + write32le(Buf + 8, GotPlt + 8); return; } const uint8_t PltData[] = { - 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOT+4) - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOT+8) - 0x90, 0x90, 0x90, 0x90 // nop; nop; nop; nop + 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOTPLT+4) + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOTPLT+8) + 0x90, 0x90, 0x90, 0x90 // nop }; memcpy(Buf, PltData, sizeof(PltData)); - uint32_t Got = In<ELF32LE>::GotPlt->getVA(); - write32le(Buf + 2, Got + 4); - write32le(Buf + 8, Got + 8); + uint32_t GotPlt = In<ELF32LE>::GotPlt->getVA(); + write32le(Buf + 2, GotPlt + 4); + write32le(Buf + 8, GotPlt + 8); } -void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Inst[] = { @@ -473,22 +499,32 @@ void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, }; memcpy(Buf, Inst, sizeof(Inst)); - // jmp *foo@GOT(%ebx) or jmp *foo_in_GOT - Buf[1] = Config->Pic ? 0xa3 : 0x25; - uint32_t Got = In<ELF32LE>::GotPlt->getVA(); - write32le(Buf + 2, Config->Shared ? GotEntryAddr - Got : GotEntryAddr); + if (Config->Pic) { + // jmp *foo@GOT(%ebx) + uint32_t Ebx = In<ELF32LE>::Got->getVA() + In<ELF32LE>::Got->getSize(); + Buf[1] = 0xa3; + write32le(Buf + 2, GotPltEntryAddr - Ebx); + } else { + // jmp *foo_in_GOT + Buf[1] = 0x25; + write32le(Buf + 2, GotPltEntryAddr); + } + write32le(Buf + 7, RelOff); write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } -uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf, - uint32_t Type) const { +int64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf, + uint32_t Type) const { switch (Type) { default: return 0; + case R_386_8: + case R_386_PC8: + return SignExtend64<8>(*Buf); case R_386_16: case R_386_PC16: - return read16le(Buf); + return SignExtend64<16>(read16le(Buf)); case R_386_32: case R_386_GOT32: case R_386_GOT32X: @@ -497,21 +533,36 @@ uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf, case R_386_PC32: case R_386_PLT32: case R_386_TLS_LE: - return read32le(Buf); + return SignExtend64<32>(read32le(Buf)); } } void X86TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const { - checkInt<32>(Loc, Val, Type); - - // R_386_PC16 and R_386_16 are not part of the current i386 psABI. They are - // used by 16-bit x86 objects, like boot loaders. - if (Type == R_386_16 || Type == R_386_PC16) { + // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are + // being used for some 16-bit programs such as boot loaders, so + // we want to support them. + switch (Type) { + case R_386_8: + checkUInt<8>(Loc, Val, Type); + *Loc = Val; + break; + case R_386_PC8: + checkInt<8>(Loc, Val, Type); + *Loc = Val; + break; + case R_386_16: + checkUInt<16>(Loc, Val, Type); write16le(Loc, Val); - return; + break; + case R_386_PC16: + checkInt<16>(Loc, Val, Type); + write16le(Loc, Val); + break; + default: + checkInt<32>(Loc, Val, Type); + write32le(Loc, Val); } - write32le(Loc, Val); } void X86TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, @@ -527,7 +578,7 @@ void X86TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, 0x81, 0xe8, 0x00, 0x00, 0x00, 0x00 // subl 0(%ebx), %eax }; memcpy(Loc - 3, Inst, sizeof(Inst)); - relocateOne(Loc + 5, R_386_32, Val); + write32le(Loc + 5, Val); } void X86TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, @@ -543,7 +594,7 @@ void X86TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, 0x03, 0x83, 0x00, 0x00, 0x00, 0x00 // addl 0(%ebx), %eax }; memcpy(Loc - 3, Inst, sizeof(Inst)); - relocateOne(Loc + 5, R_386_32, Val); + write32le(Loc + 5, Val); } // In some conditions, relocations can be optimized to avoid using GOT. @@ -583,13 +634,13 @@ void X86TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, Loc[-1] = 0x80 | (Reg << 3) | Reg; } } - relocateOne(Loc, R_386_TLS_LE, Val); + write32le(Loc, Val); } void X86TargetInfo::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const { if (Type == R_386_TLS_LDO_32) { - relocateOne(Loc, R_386_TLS_LE, Val); + write32le(Loc, Val); return; } @@ -625,12 +676,16 @@ template <class ELFT> X86_64TargetInfo<ELFT>::X86_64TargetInfo() { // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. DefaultImageBase = 0x200000; + // 0xCC is the "int3" (call debug exception handler) instruction. + TrapInstr = 0xcccccccc; } template <class ELFT> -RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type, - const SymbolBody &S) const { +RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { + case R_X86_64_8: + case R_X86_64_16: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: @@ -660,10 +715,9 @@ RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type, case R_X86_64_GOTTPOFF: return R_GOT_PC; case R_X86_64_NONE: - return R_HINT; + return R_NONE; default: - error("do not know how to handle relocation '" + toString(Type) + "' (" + - Twine(Type) + ")"); + error(toString(S.File) + ": unknown relocation type: " + toString(Type)); return R_HINT; } } @@ -681,25 +735,25 @@ template <class ELFT> void X86_64TargetInfo<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const { // See comments in X86TargetInfo::writeGotPlt. - write32le(Buf, S.getPltVA<ELFT>() + 6); + write32le(Buf, S.getPltVA() + 6); } template <class ELFT> void X86_64TargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const { const uint8_t PltData[] = { - 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOT+8(%rip) - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOT+16(%rip) - 0x0f, 0x1f, 0x40, 0x00 // nopl 0x0(rax) + 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOTPLT+8(%rip) + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOTPLT+16(%rip) + 0x0f, 0x1f, 0x40, 0x00 // nop }; memcpy(Buf, PltData, sizeof(PltData)); - uint64_t Got = In<ELFT>::GotPlt->getVA(); + uint64_t GotPlt = In<ELFT>::GotPlt->getVA(); uint64_t Plt = In<ELFT>::Plt->getVA(); - write32le(Buf + 2, Got - Plt + 2); // GOT+8 - write32le(Buf + 8, Got - Plt + 4); // GOT+16 + write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 + write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 } template <class ELFT> -void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Inst[] = { @@ -709,7 +763,7 @@ void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, }; memcpy(Buf, Inst, sizeof(Inst)); - write32le(Buf + 2, GotEntryAddr - PltEntryAddr - 6); + write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6); write32le(Buf + 7, Index); write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } @@ -725,11 +779,6 @@ bool X86_64TargetInfo<ELFT>::isTlsInitialExecRel(uint32_t Type) const { } template <class ELFT> -bool X86_64TargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const { - return Type == R_X86_64_TLSGD; -} - -template <class ELFT> bool X86_64TargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const { return Type == R_X86_64_DTPOFF32 || Type == R_X86_64_DTPOFF64 || Type == R_X86_64_TLSLD; @@ -752,9 +801,10 @@ void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, 0x48, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00 // lea x@tpoff,%rax }; memcpy(Loc - 4, Inst, sizeof(Inst)); + // The original code used a pc relative relocation and so we have to // compensate for the -4 in had in the addend. - relocateOne(Loc + 8, R_X86_64_TPOFF32, Val + 4); + write32le(Loc + 8, Val + 4); } template <class ELFT> @@ -774,9 +824,10 @@ void X86_64TargetInfo<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, 0x48, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00 // addq x@tpoff,%rax }; memcpy(Loc - 4, Inst, sizeof(Inst)); + // Both code sequences are PC relatives, but since we are moving the constant // forward by 8 bytes we have to subtract the value by 8. - relocateOne(Loc + 8, R_X86_64_PC32, Val - 8); + write32le(Loc + 8, Val - 8); } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to @@ -821,7 +872,7 @@ void X86_64TargetInfo<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, // The original code used a PC relative relocation. // Need to compensate for the -4 it had in the addend. - relocateOne(Loc, R_X86_64_TPOFF32, Val + 4); + write32le(Loc, Val + 4); } template <class ELFT> @@ -841,7 +892,7 @@ void X86_64TargetInfo<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, return; } if (Type == R_X86_64_DTPOFF32) { - relocateOne(Loc, R_X86_64_TPOFF32, Val); + write32le(Loc, Val); return; } @@ -857,6 +908,14 @@ template <class ELFT> void X86_64TargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const { switch (Type) { + case R_X86_64_8: + checkUInt<8>(Loc, Val, Type); + *Loc = Val; + break; + case R_X86_64_16: + checkUInt<16>(Loc, Val, Type); + write16le(Loc, Val); + break; case R_X86_64_32: checkUInt<32>(Loc, Val, Type); write32le(Loc, Val); @@ -898,12 +957,14 @@ RelExpr X86_64TargetInfo<ELFT>::adjustRelaxExpr(uint32_t Type, return RelExpr; const uint8_t Op = Data[-2]; const uint8_t ModRm = Data[-1]; + // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into // immediate operand. Otherwise, mov must be changed to lea. We support only // latter relaxation at this moment. if (Op == 0x8b) return R_RELAX_GOT_PC; + // Relax call and jmp. if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25)) return R_RELAX_GOT_PC; @@ -961,7 +1022,7 @@ void X86_64TargetInfo<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, // SIB.base field. // See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A). Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - relocateOne(Loc, R_X86_64_PC32, Val); + write32le(Loc, Val); return; } @@ -982,7 +1043,7 @@ void X86_64TargetInfo<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, // descriptions about each operation. Loc[-2] = 0x81; Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2; - relocateOne(Loc, R_X86_64_PC32, Val); + write32le(Loc, Val); } template <class ELFT> @@ -993,7 +1054,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const { // Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". if (Op == 0x8b) { Loc[-2] = 0x8d; - relocateOne(Loc, R_X86_64_PC32, Val); + write32le(Loc, Val); return; } @@ -1012,7 +1073,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const { // prefix. That makes result expression to be a single instruction. Loc[-2] = 0x67; // addr32 prefix Loc[-1] = 0xe8; // call - relocateOne(Loc, R_X86_64_PC32, Val); + write32le(Loc, Val); return; } @@ -1021,7 +1082,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const { assert(ModRm == 0x25); Loc[-2] = 0xe9; // jmp Loc[3] = 0x90; // nop - relocateOne(Loc - 1, R_X86_64_PC32, Val + 1); + write32le(Loc - 1, Val + 1); } // Relocation masks following the #lo(value), #hi(value), #ha(value), @@ -1059,7 +1120,8 @@ void PPCTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, } } -RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { case R_PPC_REL24: case R_PPC_REL32: @@ -1108,7 +1170,8 @@ uint64_t getPPC64TocBase() { return TocVA + PPC64TocOffset; } -RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { default: return R_ABS; @@ -1126,10 +1189,10 @@ RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { } } -void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { - uint64_t Off = GotEntryAddr - getPPC64TocBase(); + uint64_t Off = GotPltEntryAddr - getPPC64TocBase(); // FIXME: What we should do, in theory, is get the offset of the function // descriptor in the .opd section, and use that as the offset from %r2 (the @@ -1256,8 +1319,8 @@ AArch64TargetInfo::AArch64TargetInfo() { TcbSize = 16; } -RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type, - const SymbolBody &S) const { +RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { default: return R_ABS; @@ -1289,6 +1352,8 @@ RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type, case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: return R_GOT_PAGE_PC; + case R_AARCH64_NONE: + return R_NONE; } } @@ -1361,7 +1426,7 @@ void AArch64TargetInfo::writePltHeader(uint8_t *Buf) const { relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); } -void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const uint8_t Inst[] = { @@ -1373,9 +1438,9 @@ void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, memcpy(Buf, Inst, sizeof(Inst)); relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(GotEntryAddr) - getAArch64Page(PltEntryAddr)); - relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotEntryAddr); - relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotEntryAddr); + getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); + relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); } static void write32AArch64Addr(uint8_t *L, uint64_t Imm) { @@ -1598,7 +1663,8 @@ void AMDGPUTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, } } -RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { case R_AMDGPU_ABS32: case R_AMDGPU_ABS64: @@ -1612,7 +1678,8 @@ RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { case R_AMDGPU_GOTPCREL32_HI: return R_GOT_PC; default: - fatal("do not know how to handle relocation " + Twine(Type)); + error(toString(S.File) + ": unknown relocation type: " + toString(Type)); + return R_HINT; } } @@ -1634,7 +1701,8 @@ ARMTargetInfo::ARMTargetInfo() { NeedsThunks = true; } -RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { default: return R_ABS; @@ -1683,7 +1751,7 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { case R_ARM_THM_MOVT_PREL: return R_PC; case R_ARM_NONE: - return R_HINT; + return R_NONE; case R_ARM_TLS_LE32: return R_TLS; } @@ -1709,7 +1777,7 @@ void ARMTargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &) const { void ARMTargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const { // An ARM entry is the address of the ifunc resolver function. - write32le(Buf, S.getVA<ELF32LE>()); + write32le(Buf, S.getVA()); } void ARMTargetInfo::writePltHeader(uint8_t *Buf) const { @@ -1726,7 +1794,13 @@ void ARMTargetInfo::writePltHeader(uint8_t *Buf) const { write32le(Buf + 16, GotPlt - L1 - 8); } -void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void ARMTargetInfo::addPltHeaderSymbols(InputSectionBase *ISD) const { + auto *IS = cast<InputSection>(ISD); + addSyntheticLocal<ELF32LE>("$a", STT_NOTYPE, 0, 0, IS); + addSyntheticLocal<ELF32LE>("$d", STT_NOTYPE, 16, 0, IS); +} + +void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { // FIXME: Using simple code sequence with simple relocations. @@ -1740,18 +1814,24 @@ void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, }; memcpy(Buf, PltData, sizeof(PltData)); uint64_t L1 = PltEntryAddr + 4; - write32le(Buf + 12, GotEntryAddr - L1 - 8); + write32le(Buf + 12, GotPltEntryAddr - L1 - 8); +} + +void ARMTargetInfo::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const { + auto *IS = cast<InputSection>(ISD); + addSyntheticLocal<ELF32LE>("$a", STT_NOTYPE, Off, 0, IS); + addSyntheticLocal<ELF32LE>("$d", STT_NOTYPE, Off + 12, 0, IS); } -RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType, - const InputFile &File, - const SymbolBody &S) const { +bool ARMTargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType, + const InputFile *File, + const SymbolBody &S) const { // If S is an undefined weak symbol in an executable we don't need a Thunk. // In a DSO calls to undefined symbols, including weak ones get PLT entries // which may need a thunk. - if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() - && !Config->Shared) - return Expr; + if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() && + !Config->Shared) + return false; // A state change from ARM to Thumb and vice versa must go through an // interworking thunk if the relocation type is not R_ARM_CALL or // R_ARM_THM_CALL. @@ -1761,20 +1841,18 @@ RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType, case R_ARM_JUMP24: // Source is ARM, all PLT entries are ARM so no interworking required. // Otherwise we need to interwork if Symbol has bit 0 set (Thumb). - if (Expr == R_PC && ((S.getVA<ELF32LE>() & 1) == 1)) - return R_THUNK_PC; + if (Expr == R_PC && ((S.getVA() & 1) == 1)) + return true; break; case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP24: // Source is Thumb, all PLT entries are ARM so interworking is required. // Otherwise we need to interwork if Symbol has bit 0 clear (ARM). - if (Expr == R_PLT_PC) - return R_THUNK_PLT_PC; - if ((S.getVA<ELF32LE>() & 1) == 0) - return R_THUNK_PC; + if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0)) + return true; break; } - return Expr; + return false; } void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, @@ -1796,6 +1874,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, case R_ARM_TLS_LDO32: case R_ARM_TLS_LE32: case R_ARM_TLS_TPOFF32: + case R_ARM_TLS_DTPOFF32: write32le(Loc, Val); break; case R_ARM_TLS_DTPMOD32: @@ -1911,8 +1990,8 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, } } -uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf, - uint32_t Type) const { +int64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf, + uint32_t Type) const { switch (Type) { default: return 0; @@ -1990,10 +2069,6 @@ bool ARMTargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return Type == R_ARM_TLS_LDO32 || Type == R_ARM_TLS_LDM32; } -bool ARMTargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { - return Type == R_ARM_TLS_GD32; -} - bool ARMTargetInfo::isTlsInitialExecRel(uint32_t Type) const { return Type == R_ARM_TLS_IE32; } @@ -2022,8 +2097,8 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() { } template <class ELFT> -RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type, - const SymbolBody &S) const { +RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { // See comment in the calculateMipsRelChain. if (ELFT::Is64Bits || Config->MipsN32Abi) Type &= 0xff; @@ -2039,13 +2114,16 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type, return R_PLT; case R_MIPS_HI16: case R_MIPS_LO16: - case R_MIPS_GOT_OFST: // R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate // offset between start of function and 'gp' value which by default // equal to the start of .got section. In that case we consider these // relocations as relative. - if (&S == ElfSym<ELFT>::MipsGpDisp) - return R_PC; + if (&S == ElfSym::MipsGpDisp) + return R_MIPS_GOT_GP_PC; + if (&S == ElfSym::MipsLocalGp) + return R_MIPS_GOT_GP; + // fallthrough + case R_MIPS_GOT_OFST: return R_ABS; case R_MIPS_PC32: case R_MIPS_PC16: @@ -2092,11 +2170,6 @@ bool MipsTargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const { } template <class ELFT> -bool MipsTargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const { - return Type == R_MIPS_TLS_GD; -} - -template <class ELFT> void MipsTargetInfo<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const { write32<ELFT::TargetEndianness>(Buf, In<ELFT>::Plt->getVA()); } @@ -2161,18 +2234,20 @@ void MipsTargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const { write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28 } + write32<E>(Buf + 16, 0x03e07825); // move $15, $31 write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2 write32<E>(Buf + 24, 0x0320f809); // jalr $25 write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2 - uint64_t Got = In<ELFT>::GotPlt->getVA(); - writeMipsHi16<E>(Buf, Got); - writeMipsLo16<E>(Buf + 4, Got); - writeMipsLo16<E>(Buf + 8, Got); + + uint64_t GotPlt = In<ELFT>::GotPlt->getVA(); + writeMipsHi16<E>(Buf, GotPlt); + writeMipsLo16<E>(Buf + 4, GotPlt); + writeMipsLo16<E>(Buf + 8, GotPlt); } template <class ELFT> -void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, +void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const endianness E = ELFT::TargetEndianness; @@ -2181,37 +2256,37 @@ void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, // jr $25 write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008); write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeMipsHi16<E>(Buf, GotEntryAddr); - writeMipsLo16<E>(Buf + 4, GotEntryAddr); - writeMipsLo16<E>(Buf + 12, GotEntryAddr); + writeMipsHi16<E>(Buf, GotPltEntryAddr); + writeMipsLo16<E>(Buf + 4, GotPltEntryAddr); + writeMipsLo16<E>(Buf + 12, GotPltEntryAddr); } template <class ELFT> -RelExpr MipsTargetInfo<ELFT>::getThunkExpr(RelExpr Expr, uint32_t Type, - const InputFile &File, - const SymbolBody &S) const { +bool MipsTargetInfo<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, + const InputFile *File, + const SymbolBody &S) const { // Any MIPS PIC code function is invoked with its address in register $t9. // So if we have a branch instruction from non-PIC code to the PIC one // we cannot make the jump directly and need to create a small stubs // to save the target function address. // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf if (Type != R_MIPS_26) - return Expr; - auto *F = dyn_cast<ELFFileBase<ELFT>>(&File); + return false; + auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File); if (!F) - return Expr; + return false; // If current file has PIC code, LA25 stub is not required. if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC) - return Expr; - auto *D = dyn_cast<DefinedRegular<ELFT>>(&S); + return false; + auto *D = dyn_cast<DefinedRegular>(&S); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. - return D && D->isMipsPIC() ? R_THUNK_ABS : Expr; + return D && D->isMipsPIC<ELFT>(); } template <class ELFT> -uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf, - uint32_t Type) const { +int64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf, + uint32_t Type) const { const endianness E = ELFT::TargetEndianness; switch (Type) { default: @@ -2220,7 +2295,7 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf, case R_MIPS_GPREL32: case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_TPREL32: - return read32<E>(Buf); + return SignExtend64<32>(read32<E>(Buf)); case R_MIPS_26: // FIXME (simon): If the relocation target symbol is not a PLT entry // we should use another expression for calculation: @@ -2303,9 +2378,19 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, case R_MIPS_26: write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff)); break; + case R_MIPS_GOT16: + // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode + // is updated addend (not a GOT index). In that case write high 16 bits + // to store a correct addend value. + if (Config->Relocatable) + writeMipsHi16<E>(Loc, Val); + else { + checkInt<16>(Loc, Val, Type); + writeMipsLo16<E>(Loc, Val); + } + break; case R_MIPS_GOT_DISP: case R_MIPS_GOT_PAGE: - case R_MIPS_GOT16: case R_MIPS_GPREL16: case R_MIPS_TLS_GD: case R_MIPS_TLS_LDM: |