diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp --- a/lld/ELF/Arch/PPC.cpp +++ b/lld/ELF/Arch/PPC.cpp @@ -198,21 +198,21 @@ } bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, - uint64_t branchAddr, const Symbol &s, int64_t /*a*/) const { - if (type != R_PPC_REL24 && type != R_PPC_PLTREL24) + uint64_t branchAddr, const Symbol &s, int64_t a) const { + if (type != R_PPC_LOCAL24PC && type != R_PPC_REL24 && type != R_PPC_PLTREL24) return false; if (s.isInPlt()) return true; if (s.isUndefWeak()) return false; - return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA())); + return !PPC::inBranchRange(type, branchAddr, s.getVA(a)); } uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { uint64_t offset = dst - src; - if (type == R_PPC_REL24 || type == R_PPC_PLTREL24) + if (type == R_PPC_LOCAL24PC || type == R_PPC_REL24 || type == R_PPC_PLTREL24) return isInt<26>(offset); llvm_unreachable("unsupported relocation type used in branch"); } @@ -235,13 +235,13 @@ return R_DTPREL; case R_PPC_REL14: case R_PPC_REL32: - case R_PPC_LOCAL24PC: case R_PPC_REL16_LO: case R_PPC_REL16_HI: case R_PPC_REL16_HA: return R_PC; case R_PPC_GOT16: return R_GOT_OFF; + case R_PPC_LOCAL24PC: case R_PPC_REL24: return R_PLT_PC; case R_PPC_PLTREL24: diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1304,10 +1304,10 @@ if (expr == R_GOT_PC && !isAbsoluteValue(sym)) { expr = target->adjustRelaxExpr(type, relocatedAddr, expr); } else { - // Addend of R_PPC_PLTREL24 is used to choose call stub type. It should be - // ignored if optimized to R_PC. + // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call + // stub type. It should be ignored if optimized to R_PC. if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL) - addend = 0; + addend &= ~0x8000; expr = fromPlt(expr); } } @@ -1857,9 +1857,7 @@ rel.sym->getVA(rel.addend) + getPCBias(rel.type))) return true; rel.sym = &t->destination; - // TODO Restore addend on all targets. - if (config->emachine == EM_AARCH64 || config->emachine == EM_PPC64) - rel.addend = t->addend; + rel.addend = t->addend; if (rel.sym->isInPlt()) rel.expr = toPlt(rel.expr); } @@ -1937,16 +1935,11 @@ rel.sym = t->getThunkTargetSym(); rel.expr = fromPlt(rel.expr); - // On AArch64 and PPC64, a jump/call relocation may be encoded as + // On AArch64 and PPC, a jump/call relocation may be encoded as // STT_SECTION + non-zero addend, clear the addend after // redirection. - // - // The addend of R_PPC_PLTREL24 should be ignored after changing to - // R_PC. - if (config->emachine == EM_AARCH64 || - config->emachine == EM_PPC64 || - (config->emachine == EM_PPC && rel.type == R_PPC_PLTREL24)) - rel.addend = 0; + if (config->emachine != EM_MIPS) + rel.addend = -getPCBias(rel.type); } for (auto &p : isd->thunkSections) diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -245,8 +245,7 @@ // decide the offsets in the call stub. PPC32PltCallStub(const InputSection &isec, const Relocation &rel, Symbol &dest) - : Thunk(dest, rel.type == R_PPC_PLTREL24 ? rel.addend : 0), - file(isec.file) {} + : Thunk(dest, rel.addend), file(isec.file) {} uint32_t size() override { return 16; } void writeTo(uint8_t *buf) override; void addSymbols(ThunkSection &isec) override; @@ -257,6 +256,14 @@ const InputFile *file; }; +class PPC32LongThunk final : public Thunk { +public: + PPC32LongThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} + uint32_t size() override { return config->isPic ? 32 : 16; } + void writeTo(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + // PPC64 Plt call stubs. // Any call site that needs to call through a plt entry needs a call stub in // the .text section. The call stub is responsible for: @@ -765,6 +772,33 @@ return !config->isPic || (isec.file == file && rel.addend == addend); } +void PPC32LongThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0, + isec); +} + +void PPC32LongThunk::writeTo(uint8_t *buf) { + auto ha = [](uint32_t v) -> uint16_t { return (v + 0x8000) >> 16; }; + auto lo = [](uint32_t v) -> uint16_t { return v; }; + uint32_t d = destination.getVA(addend); + if (config->isPic) { + uint32_t off = d - (getThunkTargetSym()->getVA() + 8); + write32(buf + 0, 0x7c0802a6); // mflr r12,0 + write32(buf + 4, 0x429f0005); // bcl r20,r31,.+4 + write32(buf + 8, 0x7d8802a6); // mtctr r12 + write32(buf + 12, 0x3d8c0000 | ha(off)); // addis r12,r12,off@ha + write32(buf + 16, 0x398c0000 | lo(off)); // addi r12,r12,off@l + write32(buf + 20, 0x7c0803a6); // mtlr r0 + buf += 24; + } else { + write32(buf + 0, 0x3d800000 | ha(d)); // lis r12,d@ha + write32(buf + 4, 0x398c0000 | lo(d)); // addi r12,r12,d@l + buf += 8; + } + write32(buf + 0, 0x7d8903a6); // mtctr r12 + write32(buf + 4, 0x4e800420); // bctr +} + void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { uint16_t offHa = (offset + 0x8000) >> 16; uint16_t offLo = offset & 0xffff; @@ -902,9 +936,12 @@ static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel, Symbol &s) { - assert((rel.type == R_PPC_REL24 || rel.type == R_PPC_PLTREL24) && + assert((rel.type == R_PPC_LOCAL24PC || rel.type == R_PPC_REL24 || + rel.type == R_PPC_PLTREL24) && "unexpected relocation type for thunk"); - return make(isec, rel, s); + if (s.isInPlt()) + return make(isec, rel, s); + return make(s, rel.addend); } static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { diff --git a/lld/test/ELF/ppc32-long-thunk.s b/lld/test/ELF/ppc32-long-thunk.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc32-long-thunk.s @@ -0,0 +1,87 @@ +# REQUIRES: ppc +# RUN: echo 'SECTIONS { \ +# RUN: .text_low 0x2000: { *(.text_low) } \ +# RUN: .text_high 0x2002000 : { *(.text_high) } \ +# RUN: }' > %t.script + +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o +# RUN: ld.lld -T %t.script %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefixes=CHECK,PD %s + +# RUN: ld.lld -T %t.script -pie %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefixes=CHECK,PI %s + +# SEC: There are no relocations in this file. + +# CHECK: _start: +# CHECK-NEXT: 2000: bl .+24 +# CHECK-NEXT: bl .+20 +# CHECK-NEXT: bl .+16 +# CHECK-NEXT: bl .+33554428 +# PD-NEXT: bl .+24 +# PI-NEXT: bl .+40 + +## high = 0x02002008 = 65536*512+8200 +# PD: __LongThunk_high: +# PD-NEXT: 2018: lis 12, 512 +# PD-NEXT: addi 12, 12, 8200 +# PD-NEXT: mtctr 12 +# PD-NEXT: bctr + +## .text_high+16 = 0x02002010 = 65536*512+8208 +# PD: __LongThunk_: +# PD-NEXT: 2028: lis 12, 512 +# PD-NEXT: addi 12, 12, 8208 +# PD-NEXT: mtctr 12 +# PD-NEXT: bctr + +## high-0x2028 = 0x02002008-0x2020 = 65536*512-24 +# PI: __LongThunk_high: +# PI-NEXT: 2018: mflr 0 +# PI-NEXT: bcl 20, 31, .+4 +# PI-NEXT: 2020: mflr 12 +# PI-NEXT: addis 12, 12, 512 +# PI-NEXT: addi 12, 12, -24 +# PI-NEXT: mtlr 0 +# PI-NEXT: mtctr 12 +# PI-NEXT: bctr + +## .text_high+16-0x2048 = 0x02002010-0x2048 = 65536*512-48 +# PI: __LongThunk_: +# PI-NEXT: 2038: mflr 0 +# PI-NEXT: bcl 20, 31, .+4 +# PI-NEXT: 2040: mflr 12 +# PI-NEXT: addis 12, 12, 512 +# PI-NEXT: addi 12, 12, -48 +# PI-NEXT: mtlr 0 +# PI-NEXT: mtctr 12 +# PI-NEXT: bctr + +.section .text_low, "ax", %progbits +.globl _start +_start: +bl high@local # Need a thunk +bl high@local # Need a thunk +bl high+32768@plt # Need a thunk +bl high +bl .text_high+16 # Need a thunk +blr + +# PD: 02002008 high: +# PD-NEXT: bl .-33554432 +# PD-NEXT: bl .+4 +# PD: __LongThunk_: +# PD-NEXT: 2002010: lis 12, 0 +# PD-NEXT: addi 12, 12, 8200 +# PD-NEXT: mtctr 12 +# PD-NEXT: bctr + +.section .text_high, "ax", %progbits +nop +nop +.globl high +high: +bl .text_low+8 +bl .text_low+8 # Need a thunk