summaryrefslogtreecommitdiff
path: root/lld/ELF/Thunks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Thunks.cpp')
-rw-r--r--lld/ELF/Thunks.cpp153
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