[lld] [WIP/RFC][ELF][PowerPC] Don't assume TOC pointer is valid in IPLT entries (PR #207555)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 4 20:15:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld
Author: Jessica Clarke (jrtc27)
<details>
<summary>Changes</summary>
Unlike normal PLT entries, IPLT entries can be called indirectly even when in PIEs/DSOs, and so there's no guarantee on what's in the TOC pointer register at that time. Therefore we must emit variants of the existing code that work without it, whether r12-relative (playing the same role as MIPS's $25) in the same number of instructions, or first retrieving PC in an i386-like manner, being careful not to clobber LR.
Normal canonical PLTs still look broken on 64-bit PowerPC as they use the TOC pointer register too (whereas on 32-bit PowerPC, since they should only exist in PDEs, the non-PIC absolute addressing case should handle them), but that's more awkward as they live in the normal PLT. However, the extensive use of the TOC may mean in practice we rarely, if ever, have need for normal canonical PLTs. We should probably treat this case the same as on i386, where it's an error due to the use of %ebx in PLT entries.
---
Full diff: https://github.com/llvm/llvm-project/pull/207555.diff
6 Files Affected:
- (modified) lld/ELF/Arch/PPC.cpp (+6-6)
- (modified) lld/ELF/Arch/PPC64.cpp (+3-3)
- (modified) lld/ELF/Thunks.cpp (+39-17)
- (modified) lld/ELF/Thunks.h (+4-3)
- (modified) lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s (+4-4)
- (modified) lld/test/ELF/ppc64-ifunc.s (+9-7)
``````````diff
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index fa64271e12380..a16176b7e0ea3 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -91,7 +91,7 @@ void elf::writePPC32GlinkSection(Ctx &ctx, uint8_t *buf, size_t numEntries) {
if (!ctx.arg.isPic) {
for (const Symbol *sym :
cast<PPC32GlinkSection>(*ctx.in.plt).canonical_plts) {
- writePPC32PltCallStub(ctx, buf, sym->getGotPltVA(ctx), nullptr, 0);
+ writePPC32PltCallStub(ctx, buf, glink, sym->getGotPltVA(ctx), nullptr, 0);
buf += 16;
glink += 16;
}
@@ -179,7 +179,7 @@ PPC::PPC(Ctx &ctx) : TargetInfo(ctx) {
gotPltHeaderEntriesNum = 0;
pltHeaderSize = 0;
pltEntrySize = 4;
- ipltEntrySize = 16;
+ ipltEntrySize = ctx.arg.isPic ? 32 : 16;
needsThunks = true;
@@ -199,10 +199,10 @@ void PPC::initTargetSpecificSections() {
}
void PPC::writeIplt(uint8_t *buf, const Symbol &sym,
- uint64_t /*pltEntryAddr*/) const {
- // In -pie or -shared mode, assume r30 points to .got2+0x8000, and use a
- // .got2.plt_pic32. thunk.
- writePPC32PltCallStub(ctx, buf, sym.getGotPltVA(ctx), sym.file, 0x8000);
+ uint64_t pltEntryAddr) const {
+ // In -pie or -shared mode we can't rely on r30 for indirect calls.
+ writePPC32PltCallStub(ctx, buf, pltEntryAddr, sym.getGotPltVA(ctx),
+ sym.file, std::nullopt);
}
void PPC::writeGotHeader(uint8_t *buf) const {
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 9f49605d89c58..3753920187a3d 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -1061,9 +1061,9 @@ void PPC64::writePlt(uint8_t *buf, const Symbol &sym,
}
void PPC64::writeIplt(uint8_t *buf, const Symbol &sym,
- uint64_t /*pltEntryAddr*/) const {
- writePPC64LoadAndBranch(ctx, buf,
- sym.getGotPltVA(ctx) - getPPC64TocBase(ctx));
+ uint64_t pltEntryAddr) const {
+ writePPC64LoadAndBranch(ctx, buf, pltEntryAddr, sym.getGotPltVA(ctx),
+ /*toc=*/false);
}
static bool isTocOptType(RelType type) {
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 1f161685f178d..34f78ec08d4f7 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -1316,8 +1316,9 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(dr.section);
}
-void elf::writePPC32PltCallStub(Ctx &ctx, uint8_t *buf, uint64_t gotPltVA,
- const InputFile *file, int64_t addend) {
+void elf::writePPC32PltCallStub(Ctx &ctx, uint8_t *buf, uint64_t p,
+ uint64_t gotPltVA, const InputFile *file,
+ std::optional<int64_t> addend) {
if (!ctx.arg.isPic) {
write32(ctx, buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
write32(ctx, buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
@@ -1326,13 +1327,23 @@ void elf::writePPC32PltCallStub(Ctx &ctx, uint8_t *buf, uint64_t gotPltVA,
return;
}
uint32_t offset;
- if (addend >= 0x8000) {
+ if (!addend) {
+ // We're a position-independent IPLT entry, so cannot assume anything about
+ // what value the caller left in r30 as this could be an indirect call.
+ write32(ctx, buf + 0, 0x7d6802a6); // mflr r11
+ write32(ctx, buf + 4, 0x429f0005); // bcl 20, 31, 1f
+ write32(ctx, buf + 8, 0x7fc802a6); // 1: mflr r30
+ write32(ctx, buf + 12, 0x7d6803a6); // mtlr r11
+ offset = gotPltVA - p - 8;
+ buf += 16;
+ p += 16;
+ } else if (*addend >= 0x8000) {
// The stub loads an address relative to r30 (.got2+Addend). Addend is
// almost always 0x8000. The address of .got2 is different in another object
// file, so a stub cannot be shared.
offset = gotPltVA -
(ctx.in.ppc32Got2->getParent()->getVA() +
- (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend);
+ (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + *addend);
} else {
// The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
// currently the address of .got).
@@ -1353,7 +1364,8 @@ void elf::writePPC32PltCallStub(Ctx &ctx, uint8_t *buf, uint64_t gotPltVA,
}
void PPC32PltCallStub::writeTo(uint8_t *buf) {
- writePPC32PltCallStub(ctx, buf, destination.getGotPltVA(ctx), file, addend);
+ writePPC32PltCallStub(ctx, buf, getThunkTargetSym()->getVA(ctx),
+ destination.getGotPltVA(ctx), file, addend);
}
void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
@@ -1402,21 +1414,32 @@ void PPC32LongThunk::writeTo(uint8_t *buf) {
write32(ctx, buf + 4, 0x4e800420); // bctr
}
-void elf::writePPC64LoadAndBranch(Ctx &ctx, uint8_t *buf, int64_t offset) {
+void elf::writePPC64LoadAndBranch(Ctx &ctx, uint8_t *buf, uint64_t p,
+ uint64_t addr, bool toc) {
+ uint64_t offset;
+ uint32_t reg;
+ if (toc) {
+ offset = addr - getPPC64TocBase(ctx);
+ reg = 2;
+ } else {
+ offset = addr - p;
+ reg = 12;
+ }
uint16_t offHa = (offset + 0x8000) >> 16;
uint16_t offLo = offset & 0xffff;
- write32(ctx, buf + 0, 0x3d820000 | offHa); // addis r12, r2, OffHa
+ write32(ctx, buf + 0,
+ 0x3d800000 | (reg << 16) | offHa); // addis r12, r[2|12], OffHa
write32(ctx, buf + 4, 0xe98c0000 | offLo); // ld r12, OffLo(r12)
write32(ctx, buf + 8, 0x7d8903a6); // mtctr r12
write32(ctx, buf + 12, 0x4e800420); // bctr
}
void PPC64PltCallStub::writeTo(uint8_t *buf) {
- int64_t offset = destination.getGotPltVA(ctx) - getPPC64TocBase(ctx);
// Save the TOC pointer to the save-slot reserved in the call frame.
write32(ctx, buf + 0, 0xf8410018); // std r2,24(r1)
- writePPC64LoadAndBranch(ctx, buf + 4, offset);
+ writePPC64LoadAndBranch(ctx, buf + 4, getThunkTargetSym()->getVA(ctx) + 4,
+ destination.getGotPltVA(ctx));
}
void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
@@ -1456,10 +1479,10 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) {
write32(ctx, buf + nextInstOffset + 4, BCTR); // bctr
} else {
ctx.in.ppc64LongBranchTarget->addEntry(&destination, addend);
- const int64_t offsetFromTOC =
- ctx.in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
- getPPC64TocBase(ctx);
- writePPC64LoadAndBranch(ctx, buf + 4, offsetFromTOC);
+ const uint64_t addr =
+ ctx.in.ppc64LongBranchTarget->getEntryVA(&destination, addend);
+ writePPC64LoadAndBranch(ctx, buf + 4, getThunkTargetSym()->getVA(ctx) + 4,
+ addr);
}
}
@@ -1519,10 +1542,9 @@ bool PPC64R12SetupStub::isCompatibleWith(const InputSection &isec,
}
void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
- int64_t offset =
- ctx.in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
- getPPC64TocBase(ctx);
- writePPC64LoadAndBranch(ctx, buf, offset);
+ uint64_t addr =
+ ctx.in.ppc64LongBranchTarget->getEntryVA(&destination, addend);
+ writePPC64LoadAndBranch(ctx, buf, getThunkTargetSym()->getVA(ctx), addr);
}
void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
diff --git a/lld/ELF/Thunks.h b/lld/ELF/Thunks.h
index 446345b8517f9..5fa6b959f1b9b 100644
--- a/lld/ELF/Thunks.h
+++ b/lld/ELF/Thunks.h
@@ -83,9 +83,10 @@ std::unique_ptr<Thunk> addThunk(Ctx &, const InputSection &isec,
// are restricted.
std::unique_ptr<Thunk> addLandingPadThunk(Ctx &, Symbol &s, int64_t a);
-void writePPC32PltCallStub(Ctx &, uint8_t *buf, uint64_t gotPltVA,
- const InputFile *file, int64_t addend);
-void writePPC64LoadAndBranch(Ctx &, uint8_t *buf, int64_t offset);
+void writePPC32PltCallStub(Ctx &, uint8_t *buf, uint64_t p, uint64_t gotPltVA,
+ const InputFile *file, std::optional<int64_t> addend);
+void writePPC64LoadAndBranch(Ctx &, uint8_t *buf, uint64_t p, uint64_t addr,
+ bool toc = true);
} // namespace lld::elf
diff --git a/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s b/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s
index c9a0381b610a4..62c46586ed1a3 100644
--- a/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s
+++ b/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s
@@ -10,16 +10,16 @@
# RUN: llvm-readelf -x .got2 %t | FileCheck --check-prefix=HEX2 %s
# RELOC: .rela.dyn {
-# RELOC-NEXT: 0x3022C R_PPC_RELATIVE - 0x101A0
-# RELOC-NEXT: 0x30230 R_PPC_IRELATIVE - 0x10188
+# RELOC-NEXT: 0x3023C R_PPC_RELATIVE - 0x101A0
+# RELOC-NEXT: 0x30240 R_PPC_IRELATIVE - 0x10188
# RELOC-NEXT: }
# SYM: 000101a0 0 FUNC GLOBAL DEFAULT {{.*}} func
# HEX: Hex dump of section '.got2':
-# HEX-NEXT: 0x0003022c 00000000 ....
+# HEX-NEXT: 0x0003023c 00000000 ....
# HEX2: Hex dump of section '.got2':
-# HEX2-NEXT: 0x0003022c 000101a0 ....
+# HEX2-NEXT: 0x0003023c 000101a0 ....
.section .got2,"aw"
.long func
diff --git a/lld/test/ELF/ppc64-ifunc.s b/lld/test/ELF/ppc64-ifunc.s
index d024c5d5e36c4..2b0cc4c75171d 100644
--- a/lld/test/ELF/ppc64-ifunc.s
+++ b/lld/test/ELF/ppc64-ifunc.s
@@ -55,22 +55,24 @@
# CHECK-EMPTY:
## .glink has 3 IPLT entries for ifunc1, ifunc2 and ifunc3.
-## ifunc2 and ifunc3 have the same code sequence as their PLT call stubs.
+## ifunc1 at toc - ifunc1 at iplt = 0x100302a0 - 0x10010268 = (2<<16) + 56
+## ifunc2 at toc - ifunc2 at iplt = 0x100302a8 - 0x10010278 = (2<<16) + 48
+## ifunc3 at toc - ifunc3 at iplt = 0x100302b0 - 0x10010288 = (2<<16) + 40
# CHECK: Disassembly of section .glink:
# CHECK-EMPTY:
# CHECK-NEXT: 0000000010010268 <ifunc1>:
-# CHECK-NEXT: addis 12, 2, 1
-# CHECK-NEXT: ld 12, -32760(12)
+# CHECK-NEXT: addis 12, 12, 2
+# CHECK-NEXT: ld 12, 56(12)
# CHECK-NEXT: mtctr 12
# CHECK-NEXT: bctr
-# CHECK-NEXT: addis 12, 2, 1
-# CHECK-NEXT: ld 12, -32752(12)
+# CHECK-NEXT: addis 12, 12, 2
+# CHECK-NEXT: ld 12, 48(12)
# CHECK-NEXT: mtctr 12
# CHECK-NEXT: bctr
# CHECK-EMPTY:
# CHECK-NEXT: 0000000010010288 <ifunc3>:
-# CHECK-NEXT: addis 12, 2, 1
-# CHECK-NEXT: ld 12, -32744(12)
+# CHECK-NEXT: addis 12, 12, 2
+# CHECK-NEXT: ld 12, 40(12)
# CHECK-NEXT: mtctr 12
# CHECK-NEXT: bctr
``````````
</details>
https://github.com/llvm/llvm-project/pull/207555
More information about the llvm-commits
mailing list