From e7bf9688031427a47cb51aed1d4e7c947e70b358 Mon Sep 17 00:00:00 2001 From: George Rimar Date: Sat, 8 Apr 2017 06:14:14 +0000 Subject: [PATCH] [ELF] - Stop producing broken output for R_386_GOT32[X] relocations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we silently produced broken output for R_386_GOT32X/R_386_GOT32 relocations if they were used to compute the address of the symbol’s global offset table entry without base register when position-independent code is disabled. Situation happened because of recent ABI changes. Released ABI mentions that R_386_GOT32X can be calculated in a two different ways (so we did not follow ABI here before this patch), but draft ABI also mentions R_386_GOT32 relocation here. We should use the same calculations for both relocations. Problem is that we always calculated them as G + A - GOT (offset from end of GOT), but for case when PIC is disabled, according to i386 ABI calculation should be G + A, what should produce just an address in GOT finally. ABI: https://github.com/hjl-tools/x86-psABI/wiki/intel386-psABI-draft.pdf (p36, p60). llvm-svn: 299812 --- lld/ELF/InputSection.cpp | 2 +- lld/ELF/Relocations.cpp | 3 +- lld/ELF/Target.cpp | 73 +++++++++++++------ lld/ELF/Target.h | 3 +- lld/test/ELF/Inputs/i386-got32x-baseless.elf | Bin 0 -> 628 bytes lld/test/ELF/got32-i386.s | 23 ++++++ lld/test/ELF/got32x-i386.s | 47 ++++++++++++ 7 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 lld/test/ELF/Inputs/i386-got32x-baseless.elf create mode 100644 lld/test/ELF/got32-i386.s create mode 100644 lld/test/ELF/got32x-i386.s diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 450a07254875..1067a1e5da3a 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -561,7 +561,7 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { 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) { diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 9b3af89f537d..7cc048f74800 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -851,7 +851,8 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak()) reportUndefined(Body, Sec, Rel.r_offset); - RelExpr Expr = Target->getRelExpr(Type, Body); + RelExpr Expr = + Target->getRelExpr(Type, Body, Sec.Data.begin() + Rel.r_offset); // Ignore "hint" relocations because they are only markers for relaxation. if (isRelExprOneOf(Expr)) diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 0f565c50c08c..017775791a55 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -59,7 +59,7 @@ 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 static std::string getErrorLoc(uint8_t *Loc) { +template static std::string getErrorLoc(const uint8_t *Loc) { for (InputSectionBase *D : InputSections) { auto *IS = dyn_cast_or_null(D); if (!IS || !IS->OutSec) @@ -72,7 +72,7 @@ template static std::string getErrorLoc(uint8_t *Loc) { return ""; } -static std::string getErrorLocation(uint8_t *Loc) { +static std::string getErrorLocation(const uint8_t *Loc) { switch (Config->EKind) { case ELF32LEKind: return getErrorLoc(Loc); @@ -119,7 +119,8 @@ namespace { 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; @@ -143,7 +144,8 @@ public: 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 isTlsInitialExecRel(uint32_t Type) const override; @@ -171,13 +173,15 @@ 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; + 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; @@ -186,7 +190,8 @@ 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; @@ -206,13 +211,15 @@ 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; int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; @@ -233,7 +240,8 @@ public: 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; @@ -353,7 +361,8 @@ X86TargetInfo::X86TargetInfo() { 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: @@ -376,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: @@ -654,8 +681,8 @@ template X86_64TargetInfo::X86_64TargetInfo() { } 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: @@ -1093,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: @@ -1142,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; @@ -1290,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; @@ -1634,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: @@ -1671,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; @@ -2065,8 +2096,8 @@ template MipsTargetInfo::MipsTargetInfo() { } 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; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 410e856890c2..4b88626050b3 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -53,7 +53,8 @@ public: // 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(); diff --git a/lld/test/ELF/Inputs/i386-got32x-baseless.elf b/lld/test/ELF/Inputs/i386-got32x-baseless.elf new file mode 100644 index 0000000000000000000000000000000000000000..824d757f3b8c151c9bde12883073d32a4a01556d GIT binary patch literal 628 zcma)4O-sW-5S?w(svu|u4LjtH+Oe{N%U=o0SEf zqKy@i#C7kczeB$UNBFwmq;kmESD`3_JS>;uZ}ahJnEP>-P1Bj54M%zEVPTgZw!Ei$ zpn9w-N%i&|ia9itfBI-qxp+##;x$NeUxY%Ec~xQ6|8Nc)&(cvl@9;!pik&1 | FileCheck %s --check-prefix=ERR +# ERR: relocation R_386_GOT32 against 'foo' without base register can not be used when PIC enabled diff --git a/lld/test/ELF/got32x-i386.s b/lld/test/ELF/got32x-i386.s new file mode 100644 index 000000000000..9a67d1997f2b --- /dev/null +++ b/lld/test/ELF/got32x-i386.s @@ -0,0 +1,47 @@ +# REQUIRES: x86 + +## i386-got32x-baseless.elf is a file produced using GNU as v.2.27 +## using following code and command line: +## (as --32 -o base.o base.s) +## +## .text +## .globl foo +## .type foo, @function +## foo: +## nop +## +## _start: +## movl foo@GOT, %eax +## movl foo@GOT, %ebx +## movl foo@GOT(%eax), %eax +## movl foo@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. +## +# RUN: ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 +# RUN: llvm-objdump -section-headers -d %t1 | FileCheck %s + +## 73728 == 0x12000 == ADDR(.got) +# CHECK: _start: +# CHECK-NEXT: 11001: 8b 05 {{.*}} movl 73728, %eax +# CHECK-NEXT: 11007: 8b 1d {{.*}} movl 73728, %ebx +# CHECK-NEXT: 1100d: 8b 80 {{.*}} movl -4(%eax), %eax +# CHECK-NEXT: 11013: 8b 83 {{.*}} movl -4(%ebx), %eax +# CHECK: Sections: +# CHECK: Name Size Address +# CHECK: .got 00000004 0000000000012000 + +# 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 'foo' without base register can not be used when PIC enabled +# ERR: relocation R_386_GOT32X against 'foo' without base register can not be used when PIC enabled