Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -740,14 +740,42 @@ 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); + const uint8_t Op = Data[Offset - 2]; + const uint8_t ModRm = Data[Offset - 1]; + // Relax mov. + if (Op == 0x8b) + return true; + // Relax call and jmp. + return Op == 0xff && (ModRm == 0x15 || ModRm == 0x25); } void X86_64TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const { - Loc[-2] = 0x8d; + const uint8_t Op = Loc[-2]; + const uint8_t ModRm = Loc[-1]; + + // Convert mov foo@GOTPCREL(%rip), %reg to lea foo(%rip), %reg. + if (Op == 0x8b) { + *(Loc - 2) = 0x8d; + relocateOne(Loc, R_X86_64_PC32, Val); + return; + } + + assert(Op == 0xff); + if (ModRm == 0x15) { + // ABI says we can convert call *foo@GOTPCREL(%rip) to nop call foo. + // Instead we convert to addr32 call foo, where addr32 is instruction + // prefix. That makes result expression to be a single instruction. + *(Loc - 2) = 0x67; // addr32 prefix + *(Loc - 1) = 0xe8; // call + } else { + assert(ModRm == 0x25); + // Convert jmp *foo@GOTPCREL(%rip) to jmp foo nop. + // jmp doesn't return, so it is fine to use nop here, it is just a stub. + *(Loc - 2) = 0xe9; // jmp + *(Loc + 3) = 0x90; // nop + Loc -= 1; + Val += 1; + } relocateOne(Loc, R_X86_64_PC32, Val); } Index: lld/trunk/test/ELF/gotpc-relax.s =================================================================== --- lld/trunk/test/ELF/gotpc-relax.s +++ lld/trunk/test/ELF/gotpc-relax.s @@ -32,8 +32,22 @@ # 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) +# DISASM-NEXT: 11051: 67 e8 a9 ff ff ff callq -87 +# DISASM-NEXT: 11057: 67 e8 a3 ff ff ff callq -93 +# DISASM-NEXT: 1105d: 67 e8 9e ff ff ff callq -98 +# DISASM-NEXT: 11063: 67 e8 98 ff ff ff callq -104 +# DISASM-NEXT: 11069: ff 15 91 0f 00 00 callq *3985(%rip) +# DISASM-NEXT: 1106f: ff 15 8b 0f 00 00 callq *3979(%rip) +# DISASM-NEXT: 11075: e9 86 ff ff ff jmp -122 +# DISASM-NEXT: 1107a: 90 nop +# DISASM-NEXT: 1107b: e9 80 ff ff ff jmp -128 +# DISASM-NEXT: 11080: 90 nop +# DISASM-NEXT: 11081: e9 7b ff ff ff jmp -133 +# DISASM-NEXT: 11086: 90 nop +# DISASM-NEXT: 11087: e9 75 ff ff ff jmp -139 +# DISASM-NEXT: 1108c: 90 nop +# DISASM-NEXT: 1108d: ff 25 6d 0f 00 00 jmpq *3949(%rip) +# DISASM-NEXT: 11093: ff 25 67 0f 00 00 jmpq *3943(%rip) .text .globl foo @@ -70,7 +84,15 @@ 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) + call *foo@GOTPCREL(%rip) + call *hid@GOTPCREL(%rip) + call *hid@GOTPCREL(%rip) + call *ifunc@GOTPCREL(%rip) + call *ifunc@GOTPCREL(%rip) + jmp *foo@GOTPCREL(%rip) + jmp *foo@GOTPCREL(%rip) + jmp *hid@GOTPCREL(%rip) + jmp *hid@GOTPCREL(%rip) + jmp *ifunc@GOTPCREL(%rip) jmp *ifunc@GOTPCREL(%rip)