Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -459,7 +459,7 @@ Addend += Target->getImplicitAddend(BufLoc, Type); SymbolBody &Sym = this->getFile()->getRelocTargetSym(Rel); - RelExpr Expr = Target->getRelExpr(Type, Sym); + RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc); if (Expr == R_NONE) continue; if (Expr != R_ABS) { Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -682,7 +682,7 @@ if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak()) reportUndefined(Body, C, RI.r_offset); - RelExpr Expr = Target->getRelExpr(Type, Body); + RelExpr Expr = Target->getRelExpr(Type, Body, Buf + RI.r_offset); // Ignore "hint" relocations because they are only markers for relaxation. if (isRelExprOneOf(Expr)) Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -54,7 +54,8 @@ // targeting S. virtual bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File, const SymbolBody &S) const; - virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const = 0; + virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const = 0; virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0; virtual ~TargetInfo(); Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -119,7 +119,8 @@ class X86TargetInfo final : public TargetInfo { public: X86TargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) 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; @@ -144,7 +145,8 @@ template 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; @@ -173,13 +175,15 @@ 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; + RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const override; void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -188,7 +192,8 @@ 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; @@ -208,13 +213,15 @@ 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; int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; @@ -236,7 +243,8 @@ template class MipsTargetInfo final : public TargetInfo { public: MipsTargetInfo(); - RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) 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; @@ -357,7 +365,39 @@ TlsGdRelaxSkip = 2; } -RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { + // R_386_GOT32X usually calculated as G + A - GOT. + // According to i386 ABI, "The R_386_GOT32X relocation can be used to compute + // the address of the symbol's global offset table entry without base register + // when position - independent code is disabled". + // In that case formula is G + A. + if (Type == R_386_GOT32X) { + uint8_t ModRM = Loc[-1]; + // ModR/M byte has form XX YYY ZZZ, where + // YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1). + // XX has different meanings: + // 00: The operand's memory address is in reg1. + // 01: The operand's memory address is reg1 + a byte-sized displacement. + // 10: The operand's memory address is reg1 + a word-sized displacement. + // 11: The operand is reg1 itself. + // Without base register ModR/M byte will have value of 00XXX101, 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) + // 11000111 binary is 0xc7. + bool HasBaseRegister = (ModRM & 0xc7) == 0x5; + if (HasBaseRegister) { + 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; + } + return R_GOT_FROM_END; + } + switch (Type) { case R_386_8: case R_386_16: @@ -379,7 +419,6 @@ case R_386_TLS_IE: return R_GOT; case R_386_GOT32: - case R_386_GOT32X: case R_386_TLS_GOTIE: return R_GOT_FROM_END; case R_386_GOTOFF: @@ -650,8 +689,8 @@ } template -RelExpr X86_64TargetInfo::getRelExpr(uint32_t Type, - const SymbolBody &S) const { +RelExpr X86_64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { switch (Type) { case R_X86_64_8: case R_X86_64_16: @@ -1090,7 +1129,8 @@ } } -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: @@ -1139,7 +1179,8 @@ 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; @@ -1287,8 +1328,8 @@ 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; @@ -1629,7 +1670,8 @@ } } -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: @@ -1666,7 +1708,8 @@ 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; @@ -2064,8 +2107,8 @@ } template -RelExpr MipsTargetInfo::getRelExpr(uint32_t Type, - const SymbolBody &S) const { +RelExpr MipsTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, + const uint8_t *Loc) const { // See comment in the calculateMipsRelChain. if (ELFT::Is64Bits || Config->MipsN32Abi) Type &= 0xff; Index: test/ELF/got32x-i386.s =================================================================== --- test/ELF/got32x-i386.s +++ test/ELF/got32x-i386.s @@ -0,0 +1,46 @@ +# REQUIRES: x86 + +## i386-got32x-baseless.elf is a file produced using GNU as v.2.27 +## using following code: +## +## .text +## .type ifunc STT_GNU_IFUNC +## .globl ifunc +## .type ifunc, @function +## ifunc: +## ret +## +## _start: +## movl ifunc@GOT, %eax +## movl ifunc@GOT, %ebx +## movl ifunc@GOT(%eax), %eax +## movl ifunc@GOT(%ebx), %eax +## +## Result file contains four R_386_GOT32X relocations. Generated code +## is also a four mov instructions. And first two has no base register: +## <_start>: +## 1: 8b 05 00 00 00 00 mov 0x0,%eax +## 7: 8b 1d 00 00 00 00 mov 0x0,%ebx +## d: 8b 80 00 00 00 00 mov 0x0(%eax),%eax +## 13: 8b 83 00 00 00 00 mov 0x0(%ebx),%eax +## +## R_386_GOT32X is computed as G + A - GOT, but if it used without base +## register, it should be calculated as G + A. Using without base register +## is only allowed for non-PIC code. +## +## STT_GNU_IFUNC was used specially because R_386_GOT32X can be optimized +## by linker. Optimization is not applicable if it is used against +## STT_GNU_IFUNC symbol. + +# RUN: ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 +# RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s + +# DISASM: _start: +# DISASM-NEXT: 11001: 8b 05 {{.*}} movl 73728, %eax +# DISASM-NEXT: 11007: 8b 1d {{.*}} movl 73728, %ebx +# DISASM-NEXT: 1100d: 8b 80 {{.*}} movl -4(%eax), %eax +# DISASM-NEXT: 11013: 8b 83 {{.*}} movl -4(%ebx), %eax + +# RUN: not ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 -pie 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR +# ERR: relocation R_386_GOT32X against 'ifunc' without base register can not be used when PIC enabled