diff options
Diffstat (limited to 'lld/ELF/Thunks.cpp')
-rw-r--r-- | lld/ELF/Thunks.cpp | 153 |
1 files changed, 111 insertions, 42 deletions
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp index 7b927a434e36..ea74d343ebb2 100644 --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -40,9 +40,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { @@ -245,8 +244,7 @@ public: // 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 +255,14 @@ private: 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: @@ -273,6 +279,20 @@ public: void addSymbols(ThunkSection &isec) override; }; +// PPC64 R2 Save Stub +// When the caller requires a valid R2 TOC pointer but the callee does not +// require a TOC pointer and the callee cannot guarantee that it doesn't +// clobber R2 then we need to save R2. This stub: +// 1) Saves the TOC pointer to the stack. +// 2) Tail calls the callee. +class PPC64R2SaveStub final : public Thunk { +public: + PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {} + uint32_t size() override { return 8; } + void writeTo(uint8_t *buf) override; + void addSymbols(ThunkSection &isec) override; +}; + // 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 @@ -345,7 +365,7 @@ void AArch64ABSLongThunk::writeTo(uint8_t *buf) { }; uint64_t s = getAArch64ThunkDestVA(destination, addend); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 8, R_AARCH64_ABS64, s); + target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s); } void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -369,9 +389,9 @@ void AArch64ADRPThunk::writeTo(uint8_t *buf) { uint64_t s = getAArch64ThunkDestVA(destination, addend); uint64_t p = getThunkTargetSym()->getVA(); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(s) - getAArch64Page(p)); - target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s); + target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(s) - getAArch64Page(p)); + target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s); } void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { @@ -415,7 +435,7 @@ void ARMThunk::writeTo(uint8_t *buf) { 0x00, 0x00, 0x00, 0xea, // b S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_JUMP24, offset); + target->relocateNoSym(buf, R_ARM_JUMP24, offset); } bool ARMThunk::isCompatibleWith(const InputSection &isec, @@ -453,7 +473,7 @@ void ThumbThunk::writeTo(uint8_t *buf) { 0x00, 0xf0, 0x00, 0xb0, // b.w S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_JUMP24, offset); + target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset); } bool ThumbThunk::isCompatibleWith(const InputSection &isec, @@ -470,8 +490,8 @@ void ARMV7ABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s); - target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s); + target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s); + target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s); } void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -488,8 +508,8 @@ void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_MOVW_ABS_NC, s); - target->relocateOne(buf + 4, R_ARM_THM_MOVT_ABS, s); + target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s); + target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s); } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -509,8 +529,8 @@ void ARMV7PILongThunk::writeLong(uint8_t *buf) { uint64_t p = getThunkTargetSym()->getVA(); int64_t offset = s - p - 16; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset); - target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset); + target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset); + target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset); } void ARMV7PILongThunk::addSymbols(ThunkSection &isec) { @@ -530,8 +550,8 @@ void ThumbV7PILongThunk::writeLong(uint8_t *buf) { uint64_t p = getThunkTargetSym()->getVA() & ~0x1; int64_t offset = s - p - 12; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset); - target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset); + target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset); + target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset); } void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) { @@ -546,7 +566,7 @@ void ARMV5ABSLongThunk::writeLong(uint8_t *buf) { 0x00, 0x00, 0x00, 0x00, // L1: .word S }; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); + target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination)); } void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) { @@ -572,7 +592,7 @@ void ARMV5PILongThunk::writeLong(uint8_t *buf) { uint64_t s = getARMThunkDestVA(destination); uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); } void ARMV5PILongThunk::addSymbols(ThunkSection &isec) { @@ -602,7 +622,7 @@ void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) { }; uint64_t s = getARMThunkDestVA(destination); memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 8, R_ARM_ABS32, s); + target->relocateNoSym(buf + 8, R_ARM_ABS32, s); } void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) { @@ -628,7 +648,7 @@ void ThumbV6MPILongThunk::writeLong(uint8_t *buf) { uint64_t s = getARMThunkDestVA(destination); uint64_t p = getThunkTargetSym()->getVA() & ~0x1; memcpy(buf, data, sizeof(data)); - target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12); + target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12); } void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) { @@ -645,8 +665,8 @@ void MipsThunk::writeTo(uint8_t *buf) { write32(buf + 4, 0x08000000 | (s >> 2)); // j func write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func) write32(buf + 12, 0x00000000); // nop - target->relocateOne(buf, R_MIPS_HI16, s); - target->relocateOne(buf + 8, R_MIPS_LO16, s); + target->relocateNoSym(buf, R_MIPS_HI16, s); + target->relocateNoSym(buf + 8, R_MIPS_LO16, s); } void MipsThunk::addSymbols(ThunkSection &isec) { @@ -667,9 +687,9 @@ void MicroMipsThunk::writeTo(uint8_t *buf) { write16(buf + 4, 0xd400); // j func write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func) write16(buf + 12, 0x0c00); // nop - target->relocateOne(buf, R_MICROMIPS_HI16, s); - target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s); - target->relocateOne(buf + 8, R_MICROMIPS_LO16, s); + target->relocateNoSym(buf, R_MICROMIPS_HI16, s); + target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s); + target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s); } void MicroMipsThunk::addSymbols(ThunkSection &isec) { @@ -691,9 +711,9 @@ void MicroMipsR6Thunk::writeTo(uint8_t *buf) { write16(buf, 0x1320); // lui $25, %hi(func) write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func) write16(buf + 8, 0x9400); // bc func - target->relocateOne(buf, R_MICROMIPS_HI16, s); - target->relocateOne(buf + 4, R_MICROMIPS_LO16, s); - target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12); + target->relocateNoSym(buf, R_MICROMIPS_HI16, s); + target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s); + target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12); } void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) { @@ -707,8 +727,8 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const { return dyn_cast<InputSection>(dr.section); } -void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, - const InputFile *file, int64_t addend) { +void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, + const InputFile *file, int64_t addend) { if (!config->isPic) { write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11) @@ -765,7 +785,34 @@ bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec, return !config->isPic || (isec.file == file && rel.addend == addend); } -void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { +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 elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { uint16_t offHa = (offset + 0x8000) >> 16; uint16_t offLo = offset & 0xffff; @@ -789,6 +836,21 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) { s->file = destination.file; } +void PPC64R2SaveStub::writeTo(uint8_t *buf) { + int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4); + // The branch offset needs to fit in 26 bits. + if (!isInt<26>(offset)) + fatal("R2 save stub branch offset is too large: " + Twine(offset)); + write32(buf + 0, 0xf8410018); // std r2,24(r1) + write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset> +} + +void PPC64R2SaveStub::addSymbols(ThunkSection &isec) { + Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()), + STT_FUNC, 0, isec); + s->needsTocRestore = true; +} + void PPC64LongBranchThunk::writeTo(uint8_t *buf) { int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - getPPC64TocBase(); @@ -805,7 +867,8 @@ Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {} Thunk::~Thunk() = default; static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { - if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26) + if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 && + type != R_AARCH64_PLT32) fatal("unrecognized relocation type"); if (config->picThunk) return make<AArch64ADRPThunk>(s, a); @@ -902,23 +965,32 @@ static Thunk *addThunkMips(RelType type, Symbol &s) { 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<PPC32PltCallStub>(isec, rel, s); + if (s.isInPlt()) + return make<PPC32PltCallStub>(isec, rel, s); + return make<PPC32LongThunk>(s, rel.addend); } static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) { - assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk"); + assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) && + "unexpected relocation type for thunk"); if (s.isInPlt()) return make<PPC64PltCallStub>(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) + return make<PPC64R2SaveStub>(s); + if (config->picThunk) return make<PPC64PILongBranchThunk>(s, a); return make<PPC64PDLongBranchThunk>(s, a); } -Thunk *addThunk(const InputSection &isec, Relocation &rel) { +Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) { Symbol &s = *rel.sym; int64_t a = rel.addend; @@ -939,6 +1011,3 @@ Thunk *addThunk(const InputSection &isec, Relocation &rel) { llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC"); } - -} // end namespace elf -} // end namespace lld |