Index: lld/trunk/ELF/InputSection.cpp =================================================================== --- lld/trunk/ELF/InputSection.cpp +++ lld/trunk/ELF/InputSection.cpp @@ -250,6 +250,7 @@ return SymVA - P; } case R_PC: + case R_RELAX_GOT_PC: return Body.getVA(A) - P; case R_PAGE_PC: return getAArch64Page(Body.getVA(A)) - getAArch64Page(P); @@ -318,6 +319,9 @@ getSymVA(Type, A, AddrLoc, *Rel.Sym, BufLoc, *File, Expr)); switch (Expr) { + case R_RELAX_GOT_PC: + Target->relaxGot(BufLoc, SymVA); + break; case R_RELAX_TLS_IE_TO_LE: Target->relaxTlsIeToLe(BufLoc, Type, SymVA); break; Index: lld/trunk/ELF/Relocations.h =================================================================== --- lld/trunk/ELF/Relocations.h +++ lld/trunk/ELF/Relocations.h @@ -38,6 +38,7 @@ R_PPC_OPD, R_PPC_PLT_OPD, R_PPC_TOC, + R_RELAX_GOT_PC, R_RELAX_TLS_GD_TO_IE, R_RELAX_TLS_GD_TO_LE, R_RELAX_TLS_IE_TO_LE, Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -227,7 +227,8 @@ // True if this expression is of the form Sym - X, where X is a position in the // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { - return Expr == R_PC || Expr == R_GOTREL || Expr == R_PAGE_PC; + return Expr == R_PC || Expr == R_GOTREL || Expr == R_PAGE_PC || + Expr == R_RELAX_GOT_PC; } template @@ -343,14 +344,19 @@ template static RelExpr adjustExpr(const elf::ObjectFile &File, SymbolBody &Body, - bool IsWrite, RelExpr Expr, uint32_t Type) { + bool IsWrite, RelExpr Expr, uint32_t Type, + const uint8_t *Data, typename ELFT::uint Offset) { if (Target->needsThunk(Type, File, Body)) return R_THUNK; bool Preemptible = Body.isPreemptible(); - if (Body.isGnuIFunc()) + if (Body.isGnuIFunc()) { Expr = toPlt(Expr); - else if (needsPlt(Expr) && !Preemptible) - Expr = fromPlt(Expr); + } else if (!Preemptible) { + if (needsPlt(Expr)) + Expr = fromPlt(Expr); + if (Expr == R_GOT_PC && Target->canRelaxGot(Type, Data, Offset)) + Expr = R_RELAX_GOT_PC; + } if (IsWrite || isStaticLinkTimeConstant(Expr, Type, Body)) return Expr; @@ -480,7 +486,7 @@ continue; bool Preemptible = Body.isPreemptible(); - Expr = adjustExpr(File, Body, IsWrite, Expr, Type); + Expr = adjustExpr(File, Body, IsWrite, Expr, Type, Buf, Offset); if (HasError) continue; Index: lld/trunk/ELF/Target.h =================================================================== --- lld/trunk/ELF/Target.h +++ lld/trunk/ELF/Target.h @@ -88,6 +88,9 @@ uint32_t ThunkSize = 0; + virtual bool canRelaxGot(uint32_t Type, const uint8_t *Data, + uint64_t Offset) const; + virtual void relaxGot(uint8_t *Loc, uint64_t Val) const; virtual void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const; virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const; virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const; Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -113,6 +113,9 @@ int32_t Index, unsigned RelOff) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; + bool canRelaxGot(uint32_t Type, const uint8_t *Data, + uint64_t Offset) const override; + void relaxGot(uint8_t *Loc, uint64_t Val) const override; void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; @@ -232,6 +235,15 @@ return false; } +bool TargetInfo::canRelaxGot(uint32_t Type, const uint8_t *Data, + uint64_t Offset) const { + return false; +} + +void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const { + llvm_unreachable("Should not have claimed to be relaxable"); +} + void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const { llvm_unreachable("Should not have claimed to be relaxable"); @@ -724,6 +736,21 @@ } } +bool X86_64TargetInfo::canRelaxGot(uint32_t Type, const uint8_t *Data, + uint64_t Offset) const { + if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX) + return false; + + // Converting mov foo@GOTPCREL(%rip), %reg to lea foo(%rip), %reg + // is the only supported relaxation for now. + return (Offset >= 2 && Data[Offset - 2] == 0x8b); +} + +void X86_64TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const { + Loc[-2] = 0x8d; + relocateOne(Loc, R_X86_64_PC32, Val); +} + // Relocation masks following the #lo(value), #hi(value), #ha(value), // #higher(value), #highera(value), #highest(value), and #highesta(value) // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi Index: lld/trunk/test/ELF/Inputs/gotpc-relax-und-dso.s =================================================================== --- lld/trunk/test/ELF/Inputs/gotpc-relax-und-dso.s +++ lld/trunk/test/ELF/Inputs/gotpc-relax-und-dso.s @@ -0,0 +1,4 @@ +.globl dsofoo +.type dsofoo, @function +dsofoo: + nop Index: lld/trunk/test/ELF/gotpc-relax-und-dso.s =================================================================== --- lld/trunk/test/ELF/gotpc-relax-und-dso.s +++ lld/trunk/test/ELF/gotpc-relax-und-dso.s @@ -0,0 +1,72 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-pc-linux %S/Inputs/gotpc-relax-und-dso.s -o %tdso.o +# RUN: ld.lld -shared %tdso.o -o %t.so +# RUN: ld.lld -shared %t.o %t.so -o %tout +# RUN: llvm-readobj -r -s %tout | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %tout | FileCheck --check-prefix=DISASM %s + +# RELOC: Relocations [ +# RELOC-NEXT: Section ({{.*}}) .rela.dyn { +# RELOC-NEXT: 0x20A8 R_X86_64_GLOB_DAT dsofoo 0x0 +# RELOC-NEXT: 0x20B0 R_X86_64_GLOB_DAT foo 0x0 +# RELOC-NEXT: 0x20A0 R_X86_64_GLOB_DAT und 0x0 +# RELOC-NEXT: } +# RELOC-NEXT: ] + +# 0x101e + 7 - 36 = 0x1001 +# 0x1025 + 7 - 43 = 0x1001 +# DISASM: Disassembly of section .text: +# DISASM-NEXT: foo: +# DISASM-NEXT: 1000: 90 nop +# DISASM: hid: +# DISASM-NEXT: 1001: 90 nop +# DISASM: _start: +# DISASM-NEXT: 1002: 48 8b 05 97 10 00 00 movq 4247(%rip), %rax +# DISASM-NEXT: 1009: 48 8b 05 90 10 00 00 movq 4240(%rip), %rax +# DISASM-NEXT: 1010: 48 8b 05 91 10 00 00 movq 4241(%rip), %rax +# DISASM-NEXT: 1017: 48 8b 05 8a 10 00 00 movq 4234(%rip), %rax +# DISASM-NEXT: 101e: 48 8d 05 dc ff ff ff leaq -36(%rip), %rax +# DISASM-NEXT: 1025: 48 8d 05 d5 ff ff ff leaq -43(%rip), %rax +# DISASM-NEXT: 102c: 48 8b 05 7d 10 00 00 movq 4221(%rip), %rax +# DISASM-NEXT: 1033: 48 8b 05 76 10 00 00 movq 4214(%rip), %rax +# DISASM-NEXT: 103a: 8b 05 60 10 00 00 movl 4192(%rip), %eax +# DISASM-NEXT: 1040: 8b 05 5a 10 00 00 movl 4186(%rip), %eax +# DISASM-NEXT: 1046: 8b 05 5c 10 00 00 movl 4188(%rip), %eax +# DISASM-NEXT: 104c: 8b 05 56 10 00 00 movl 4182(%rip), %eax +# DISASM-NEXT: 1052: 8d 05 a9 ff ff ff leal -87(%rip), %eax +# DISASM-NEXT: 1058: 8d 05 a3 ff ff ff leal -93(%rip), %eax +# DISASM-NEXT: 105e: 8b 05 4c 10 00 00 movl 4172(%rip), %eax +# DISASM-NEXT: 1064: 8b 05 46 10 00 00 movl 4166(%rip), %eax + +.text +.globl foo +.type foo, @function +foo: + nop + +.globl hid +.hidden hid +.type hid, @function +hid: + nop + +.globl _start +.type _start, @function +_start: + movq und@GOTPCREL(%rip), %rax + movq und@GOTPCREL(%rip), %rax + movq dsofoo@GOTPCREL(%rip), %rax + movq dsofoo@GOTPCREL(%rip), %rax + movq hid@GOTPCREL(%rip), %rax + movq hid@GOTPCREL(%rip), %rax + movq foo@GOTPCREL(%rip), %rax + movq foo@GOTPCREL(%rip), %rax + movl und@GOTPCREL(%rip), %eax + movl und@GOTPCREL(%rip), %eax + movl dsofoo@GOTPCREL(%rip), %eax + movl dsofoo@GOTPCREL(%rip), %eax + movl hid@GOTPCREL(%rip), %eax + movl hid@GOTPCREL(%rip), %eax + movl foo@GOTPCREL(%rip), %eax + movl foo@GOTPCREL(%rip), %eax Index: lld/trunk/test/ELF/gotpc-relax.s =================================================================== --- lld/trunk/test/ELF/gotpc-relax.s +++ lld/trunk/test/ELF/gotpc-relax.s @@ -0,0 +1,76 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t1 +# RUN: llvm-readobj -r %t1 | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s + +## There is no relocations. +# RELOC: Relocations [ +# RELOC: ] + +# 0x11003 + 7 - 10 = 0x11000 +# 0x1100a + 7 - 17 = 0x11000 +# 0x11011 + 7 - 23 = 0x11001 +# 0x11018 + 7 - 30 = 0x11001 +# DISASM: Disassembly of section .text: +# DISASM-NEXT: foo: +# DISASM-NEXT: 11000: 90 nop +# DISASM: hid: +# DISASM-NEXT: 11001: 90 nop +# DISASM: ifunc: +# DISASM-NEXT: 11002: c3 retq +# DISASM: _start: +# DISASM-NEXT: 11003: 48 8d 05 f6 ff ff ff leaq -10(%rip), %rax +# DISASM-NEXT: 1100a: 48 8d 05 ef ff ff ff leaq -17(%rip), %rax +# DISASM-NEXT: 11011: 48 8d 05 e9 ff ff ff leaq -23(%rip), %rax +# DISASM-NEXT: 11018: 48 8d 05 e2 ff ff ff leaq -30(%rip), %rax +# DISASM-NEXT: 1101f: 48 8b 05 da 0f 00 00 movq 4058(%rip), %rax +# DISASM-NEXT: 11026: 48 8b 05 d3 0f 00 00 movq 4051(%rip), %rax +# DISASM-NEXT: 1102d: 8d 05 cd ff ff ff leal -51(%rip), %eax +# DISASM-NEXT: 11033: 8d 05 c7 ff ff ff leal -57(%rip), %eax +# DISASM-NEXT: 11039: 8d 05 c2 ff ff ff leal -62(%rip), %eax +# DISASM-NEXT: 1103f: 8d 05 bc ff ff ff leal -68(%rip), %eax +# DISASM-NEXT: 11045: 8b 05 b5 0f 00 00 movl 4021(%rip), %eax +# DISASM-NEXT: 1104b: 8b 05 af 0f 00 00 movl 4015(%rip), %eax +# DISASM-NEXT: 11051: ff 15 b1 0f 00 00 callq *4017(%rip) +# DISASM-NEXT: 11057: ff 25 a3 0f 00 00 jmpq *4003(%rip) + +.text +.globl foo +.type foo, @function +foo: + nop + +.globl hid +.hidden hid +.type hid, @function +hid: + nop + +.text +.type ifunc STT_GNU_IFUNC +.globl ifunc +.type ifunc, @function +ifunc: + ret + +.globl _start +.type _start, @function +_start: + movq foo@GOTPCREL(%rip), %rax + movq foo@GOTPCREL(%rip), %rax + movq hid@GOTPCREL(%rip), %rax + movq hid@GOTPCREL(%rip), %rax + movq ifunc@GOTPCREL(%rip), %rax + movq ifunc@GOTPCREL(%rip), %rax + movl foo@GOTPCREL(%rip), %eax + movl foo@GOTPCREL(%rip), %eax + movl hid@GOTPCREL(%rip), %eax + movl hid@GOTPCREL(%rip), %eax + movl ifunc@GOTPCREL(%rip), %eax + movl ifunc@GOTPCREL(%rip), %eax + +## We check few other possible instructions +## to see that they are not "relaxed" by mistake to lea. + call *foo@GOTPCREL(%rip) + jmp *ifunc@GOTPCREL(%rip)