diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -326,7 +326,7 @@ // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte // alignment. This gives a possible 26 bits of 'reach'. If the call offset is -// larger then that we need to emit a long-branch thunk. The target address +// larger than that we need to emit a long-branch thunk. The target address // of the callee is stored in a table to be accessed TOC-relative. Since the // call must be local (a non-local call will have a PltCallStub instead) the // table stores the address of the callee's local entry point. For @@ -337,6 +337,8 @@ uint32_t size() override { return 16; } void writeTo(uint8_t *buf) override; void addSymbols(ThunkSection &isec) override; + bool isCompatibleWith(const InputSection &isec, + const Relocation &rel) const override; protected: PPC64LongBranchThunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} @@ -365,6 +367,24 @@ } }; +// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte +// alignment. This gives a possible 26 bits of 'reach'. If the caller and +// callee do not use toc and the call offset is larger than 26 bits, +// we need to emit a pc-rel based long-branch thunk. The target address of +// the callee is computed with a PC-relative offset. +class PPC64PCRelLongBranchThunk final : public Thunk { +public: + PPC64PCRelLongBranchThunk(Symbol &dest, int64_t addend) + : Thunk(dest, addend) { + alignment = 16; + } + uint32_t size() override { return 16; } + void writeTo(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; + bool isCompatibleWith(const InputSection &isec, + const Relocation &rel) const override; +}; + } // end anonymous namespace Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value, @@ -937,6 +957,33 @@ isec); } +bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14; +} + +void PPC64PCRelLongBranchThunk::writeTo(uint8_t *buf) { + int64_t offset = destination.getVA() - getThunkTargetSym()->getVA(); + if (!isInt<34>(offset)) + fatal("offset overflow 34 bits, please compile using the large code model"); + uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) | + (offset & 0xffff); + + writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1 + write32(buf + 8, MTCTR_R12); // mtctr r12 + write32(buf + 12, BCTR); // bctr +} + +void PPC64PCRelLongBranchThunk::addSymbols(ThunkSection &isec) { + addSymbol(saver.save("__long_branch_pcrel_" + destination.getName()), + STT_FUNC, 0, isec); +} + +bool PPC64PCRelLongBranchThunk::isCompatibleWith(const InputSection &isec, + const Relocation &rel) const { + return rel.type == R_PPC64_REL24_NOTOC; +} + Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {} Thunk::~Thunk() = default; @@ -1057,12 +1104,15 @@ : (Thunk *)make(s); // This check looks at the st_other bits of the callee. If the value is 1 - // then the callee clobbers the TOC and we need an R2 save stub. - if ((s.stOther >> 5) == 1) + // then the callee clobbers the TOC and we need an R2 save stub when RelType + // is R_PPC64_REL14 or R_PPC64_REL24. + if ((type == R_PPC64_REL14 || type == R_PPC64_REL24) && (s.stOther >> 5) == 1) return make(s); - if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1) - return make(s); + if (type == R_PPC64_REL24_NOTOC) + return (s.stOther >> 5) > 1 + ? (Thunk *)make(s) + : (Thunk *)make(s, a); if (config->picThunk) return make(s, a); diff --git a/lld/test/ELF/ppc64-pcrel-long-branch-error.s b/lld/test/ELF/ppc64-pcrel-long-branch-error.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-pcrel-long-branch-error.s @@ -0,0 +1,31 @@ +# REQUIRES: ppc +# RUN: echo 'SECTIONS { \ +# RUN: .text_low 0x2000: { *(.text_low) } \ +# RUN: .text_high 0x800002000 : { *(.text_high) } \ +# RUN: }' > %t.script + +# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t.o +# RUN: not ld.lld -T %t.script %t.o -o %t 2>&1 >/dev/null | FileCheck %s +# RUN: llvm-mc -filetype=obj -triple=ppc64le -defsym HIDDEN=1 %s -o %t.o +# RUN: not ld.lld -shared -T %t.script %t.o -o %t.so 2>&1 >/dev/null | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=ppc64 %s -o %t.o +# RUN: not ld.lld -T %t.script %t.o -o %t 2>&1 >/dev/null | FileCheck %s +# RUN: llvm-mc -filetype=obj -triple=ppc64 -defsym HIDDEN=1 %s -o %t.o +# RUN: not ld.lld -shared -T %t.script %t.o -o %t.so 2>&1 >/dev/null | FileCheck %s + +# CHECK: error: offset overflow 34 bits, please compile using the large code model + +.section .text_low, "ax", %progbits +.globl _start +_start: + bl high@notoc + blr + +.section .text_high, "ax", %progbits +.ifdef HIDDEN +.hidden high +.endif +.globl high +high: + blr diff --git a/lld/test/ELF/ppc64-pcrel-long-branch.s b/lld/test/ELF/ppc64-pcrel-long-branch.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-pcrel-long-branch.s @@ -0,0 +1,50 @@ +# 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=ppc64le %s -o %t.o +# RUN: ld.lld -T %t.script %t.o -o %t +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=ppc64le -defsym HIDDEN=1 %s -o %t.o +# RUN: ld.lld -shared -T %t.script %t.o -o %t.so +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t.so | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=ppc64 %s -o %t.o +# RUN: ld.lld -T %t.script %t.o -o %t +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=ppc64 -defsym HIDDEN=1 %s -o %t.o +# RUN: ld.lld -shared -T %t.script %t.o -o %t.so +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t.so | FileCheck %s + +# CHECK-LABEL: <_start>: +# CHECK-NEXT: 2000: bl 0x2010 +# CHECK-NEXT: blr +# CHECK-NEXT: trap +# CHECK-NEXT: trap + +## Callee address - program counter = 0x2002000 - 0x2010 = 33554416 +# CHECK-LABEL: <__long_branch_pcrel_high>: +# CHECK-NEXT: 2010: paddi 12, 0, 33554416, 1 +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr + +# CHECK-LABEL: : +# CHECK-NEXT: 2002000: blr + +.section .text_low, "ax", %progbits +.globl _start +_start: + bl high@notoc + blr + +.section .text_high, "ax", %progbits +.ifdef HIDDEN +.hidden high +.endif +.globl high +high: + blr