-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[LLD][ELF][AArch64] Add BTI Aware long branch thunks #108989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
7c542b2
c076400
a0810ab
be6939c
57e3f79
dd9a222
abfc415
bbf78ae
99a1ae4
dc545ca
b68b6cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,38 @@ uint64_t elf::getAArch64Page(uint64_t expr) { | |
| return expr & ~static_cast<uint64_t>(0xFFF); | ||
| } | ||
|
|
||
| // A BTI landing pad is a valid target for an indirect branch | ||
| // when the Branch Target Identification has been enabled. | ||
| // As linker generated branches are via x16 the | ||
| // BTI landing pads are defined as: | ||
| // BTI C, BTI J, BTI JC, PACIASP, PACIBSP. | ||
| bool elf::isAArch64BTILandingPad(Symbol &s, int64_t a) { | ||
| // PLT entries accessed indirectly have a BTI c. | ||
| if (s.isInPlt()) | ||
| return true; | ||
| Defined *d = dyn_cast_or_null<Defined>(&s); | ||
|
||
| if (d == nullptr || d->section == nullptr || | ||
| d->section->kind() != InputSectionBase::Regular) | ||
|
||
| // All places that we cannot disassemble are responsible for making | ||
| // the target a BTI landing pad. | ||
| return true; | ||
| InputSection *isec = cast<InputSection>(d->section); | ||
| int64_t off = d->value + a; | ||
|
||
| // Likely user error, but protect ourselves against out of bounds | ||
| // access. | ||
| if (off < 0 || static_cast<uint64_t>(off) >= isec->getSize()) | ||
| return true; | ||
| const uint8_t *buf = isec->content().begin(); | ||
| const uint32_t instr = read32le(buf + off); | ||
| if (instr == 0xd503233f || // PACIASP. | ||
|
||
| instr == 0xd503237f || // PACIBSP. | ||
| instr == 0xd503245f || // BTI C. | ||
| instr == 0xd503249f || // BTI J. | ||
| instr == 0xd50324df) // BTI JC. | ||
| return true; | ||
| return false; | ||
| } | ||
|
|
||
| namespace { | ||
| class AArch64 : public TargetInfo { | ||
| public: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2243,6 +2243,14 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec, | |
| return std::make_pair(t, true); | ||
| } | ||
|
|
||
| std::pair<Thunk *, bool> ThunkCreator::getSyntheticLandingPad(Defined &d, | ||
| int64_t a) { | ||
| Thunk *lp = landingPadsBySectionAndAddend[{{d.section, d.value}, a}]; | ||
|
||
| if (lp) | ||
| return std::make_pair(lp, false); | ||
| return std::make_pair(addLandingPadThunk(d, a), true); | ||
| } | ||
|
|
||
| // Return true if the relocation target is an in range Thunk. | ||
| // Return false if the relocation is not to a Thunk. If the relocation target | ||
| // was originally to a Thunk, but is no longer in range we revert the | ||
|
|
@@ -2326,6 +2334,21 @@ bool ThunkCreator::createThunks(uint32_t pass, | |
| ts = getISDThunkSec(os, isec, isd, rel, src); | ||
| ts->addThunk(t); | ||
| thunks[t->getThunkTargetSym()] = t; | ||
|
|
||
| // When indirect branches are restricted, such as AArch64 BTI | ||
| // Thunks may need to target a linker generated landing pad | ||
| // instead of the target. | ||
| if (t->needsSyntheticLandingPad()) { | ||
| Thunk *lpt; | ||
| auto &dr = cast<Defined>(t->destination); | ||
| std::tie(lpt, isNew) = getSyntheticLandingPad(dr, t->addend); | ||
| if (isNew) { | ||
| InputSection *targetsec = dyn_cast<InputSection>(dr.section); | ||
|
||
| ts = getISThunkSec(targetsec); | ||
| ts->addThunk(lpt); | ||
| } | ||
| t->landingPad = lpt->getThunkTargetSym(); | ||
| } | ||
| } | ||
|
|
||
| // Redirect relocation to Thunk, we never go via the PLT to a Thunk | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| #include <vector> | ||
|
|
||
| namespace lld::elf { | ||
| class Defined; | ||
| class Symbol; | ||
| class InputSection; | ||
| class InputSectionBase; | ||
|
|
@@ -173,6 +174,8 @@ class ThunkCreator { | |
| std::pair<Thunk *, bool> getThunk(InputSection *isec, Relocation &rel, | ||
| uint64_t src); | ||
|
|
||
| std::pair<Thunk *, bool> getSyntheticLandingPad(Defined &d, int64_t a); | ||
|
|
||
| ThunkSection *addThunkSection(OutputSection *os, InputSectionDescription *, | ||
| uint64_t off); | ||
|
|
||
|
|
@@ -200,6 +203,14 @@ class ThunkCreator { | |
| // The Mips LA25 Thunk is an example of an inline ThunkSection. | ||
|
||
| llvm::DenseMap<InputSection *, ThunkSection *> thunkedSections; | ||
|
|
||
| // Record landing pads, generated for a section + offset destination. | ||
| // Landling pads are alternative entry points for destinations that need | ||
| // to be reached via thunks that use indirect branches. A destination | ||
| // needs at most one landing pad as that can be reused by all callers. | ||
| llvm::DenseMap<std::pair<std::pair<SectionBase *, uint64_t>, int64_t>, | ||
| Thunk *> | ||
| landingPadsBySectionAndAddend; | ||
|
|
||
| // The number of completed passes of createThunks this permits us | ||
| // to do one time initialization on Pass 0 and put a limit on the | ||
| // number of times it can be called to prevent infinite loops. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,12 +51,20 @@ namespace { | |
| // distance from the thunk to the target is less than 128MB. Long thunks can | ||
| // branch to any virtual address and they are implemented in the derived | ||
| // classes. This class tries to create a short thunk if the target is in range, | ||
| // otherwise it creates a long thunk. | ||
| // otherwise it creates a long thunk. When BTI is enabled indirect branches | ||
| // must land on a BTI instruction. If the destination does not have a BTI | ||
| // instruction mayNeedLandingPad is set to true and Thunk::landingPad points | ||
| // to an alternative entry point with a BTI. | ||
| class AArch64Thunk : public Thunk { | ||
| public: | ||
| AArch64Thunk(Symbol &dest, int64_t addend) : Thunk(dest, addend) {} | ||
| AArch64Thunk(Symbol &dest, int64_t addend, bool mayNeedLandingPad) | ||
| : Thunk(dest, addend), mayNeedLandingPad(mayNeedLandingPad) {} | ||
| bool getMayUseShortThunk(); | ||
| void writeTo(uint8_t *buf) override; | ||
| bool needsSyntheticLandingPad() override; | ||
|
|
||
| protected: | ||
| bool mayNeedLandingPad; | ||
|
|
||
| private: | ||
| bool mayUseShortThunk = true; | ||
|
|
@@ -66,8 +74,8 @@ class AArch64Thunk : public Thunk { | |
| // AArch64 long range Thunks. | ||
| class AArch64ABSLongThunk final : public AArch64Thunk { | ||
| public: | ||
| AArch64ABSLongThunk(Symbol &dest, int64_t addend) | ||
| : AArch64Thunk(dest, addend) {} | ||
| AArch64ABSLongThunk(Symbol &dest, int64_t addend, bool mayNeedLandingPad) | ||
| : AArch64Thunk(dest, addend, mayNeedLandingPad) {} | ||
| uint32_t size() override { return getMayUseShortThunk() ? 4 : 16; } | ||
| void addSymbols(ThunkSection &isec) override; | ||
|
|
||
|
|
@@ -77,14 +85,36 @@ class AArch64ABSLongThunk final : public AArch64Thunk { | |
|
|
||
| class AArch64ADRPThunk final : public AArch64Thunk { | ||
| public: | ||
| AArch64ADRPThunk(Symbol &dest, int64_t addend) : AArch64Thunk(dest, addend) {} | ||
| AArch64ADRPThunk(Symbol &dest, int64_t addend, bool mayNeedLandingPad) | ||
| : AArch64Thunk(dest, addend, mayNeedLandingPad) {} | ||
| uint32_t size() override { return getMayUseShortThunk() ? 4 : 12; } | ||
| void addSymbols(ThunkSection &isec) override; | ||
|
|
||
| private: | ||
| void writeLong(uint8_t *buf) override; | ||
| }; | ||
|
|
||
| // AArch64 BTI Landing Pad | ||
| // When BTI is enabled indirect branches must land on a BTI | ||
| // compatible instruction. When the destination does not have a | ||
| // BTI compatible instruction a Thunk doing an indirect branch | ||
| // targets a Landing Pad Thunk that direct branches to the target. | ||
| class AArch64BTILandingPadThunk final : public Thunk { | ||
| public: | ||
| AArch64BTILandingPadThunk(Symbol &dest, int64_t addend) | ||
| : Thunk(dest, addend) {} | ||
|
|
||
| uint32_t size() override { return getMayUseShortThunk() ? 4 : 8; } | ||
| void addSymbols(ThunkSection &isec) override; | ||
| InputSection *getTargetInputSection() const override; | ||
| void writeTo(uint8_t *buf) override; | ||
|
|
||
| private: | ||
| bool getMayUseShortThunk(); | ||
| void writeLong(uint8_t *buf); | ||
| bool mayUseShortThunk = true; | ||
| }; | ||
|
|
||
| // Base class for ARM thunks. | ||
| // | ||
| // An ARM thunk may be either short or long. A short thunk is simply a branch | ||
|
|
@@ -532,6 +562,12 @@ void AArch64Thunk::writeTo(uint8_t *buf) { | |
| ctx.target->relocateNoSym(buf, R_AARCH64_CALL26, s - p); | ||
| } | ||
|
|
||
| bool AArch64Thunk::needsSyntheticLandingPad() { | ||
| // Short Thunks use a direct branch, no synthetic landing pad | ||
| // required. | ||
| return mayNeedLandingPad && !getMayUseShortThunk(); | ||
| } | ||
|
|
||
| // AArch64 long range Thunks. | ||
| void AArch64ABSLongThunk::writeLong(uint8_t *buf) { | ||
| const uint8_t data[] = { | ||
|
|
@@ -540,7 +576,11 @@ void AArch64ABSLongThunk::writeLong(uint8_t *buf) { | |
| 0x00, 0x00, 0x00, 0x00, // L0: .xword S | ||
| 0x00, 0x00, 0x00, 0x00, | ||
| }; | ||
| uint64_t s = getAArch64ThunkDestVA(destination, addend); | ||
| // if mayNeedLandingPad is true then destination is an | ||
|
||
| // AArch64BTILandingPadThunk that defines landingPad. | ||
| assert(!mayNeedLandingPad || landingPad != nullptr); | ||
| uint64_t s = mayNeedLandingPad ? landingPad->getVA(0) | ||
| : getAArch64ThunkDestVA(destination, addend); | ||
| memcpy(buf, data, sizeof(data)); | ||
| ctx.target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s); | ||
| } | ||
|
|
@@ -564,7 +604,11 @@ void AArch64ADRPThunk::writeLong(uint8_t *buf) { | |
| 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest) | ||
| 0x00, 0x02, 0x1f, 0xd6, // br x16 | ||
| }; | ||
| uint64_t s = getAArch64ThunkDestVA(destination, addend); | ||
| // if mayNeedLandingPad is true then destination is an | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| // AArch64BTILandingPadThunk that defines landingPad. | ||
| assert(!mayNeedLandingPad || landingPad != nullptr); | ||
| uint64_t s = mayNeedLandingPad ? landingPad->getVA(0) | ||
| : getAArch64ThunkDestVA(destination, addend); | ||
| uint64_t p = getThunkTargetSym()->getVA(); | ||
| memcpy(buf, data, sizeof(data)); | ||
| ctx.target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21, | ||
|
|
@@ -578,6 +622,48 @@ void AArch64ADRPThunk::addSymbols(ThunkSection &isec) { | |
| addSymbol("$x", STT_NOTYPE, 0, isec); | ||
| } | ||
|
|
||
| void AArch64BTILandingPadThunk::addSymbols(ThunkSection &isec) { | ||
| addSymbol(saver().save("__AArch64BTIThunk_" + destination.getName()), | ||
| STT_FUNC, 0, isec); | ||
| addSymbol("$x", STT_NOTYPE, 0, isec); | ||
| } | ||
|
|
||
| InputSection *AArch64BTILandingPadThunk::getTargetInputSection() const { | ||
|
||
| auto &dr = cast<Defined>(destination); | ||
| return dyn_cast<InputSection>(dr.section); | ||
| } | ||
|
|
||
| void AArch64BTILandingPadThunk::writeTo(uint8_t *buf) { | ||
| if (!getMayUseShortThunk()) { | ||
| writeLong(buf); | ||
| return; | ||
| } | ||
| write32(buf, 0xd503245f); // BTI c | ||
| // Control falls through to target in following section. | ||
| } | ||
|
|
||
| bool AArch64BTILandingPadThunk::getMayUseShortThunk() { | ||
| if (!mayUseShortThunk) | ||
| return false; | ||
| // If the target is the following instruction then | ||
|
||
| // we can fall through without the indirect branch. | ||
| uint64_t s = destination.getVA(addend); | ||
| uint64_t p = getThunkTargetSym()->getVA(); | ||
| // <= 4 as Thunks start off with the same offset | ||
| // within the section as the destination, so | ||
| // s - p == 0 until addresses are assigned. | ||
| mayUseShortThunk = (s - p <= 4); | ||
| return mayUseShortThunk; | ||
| } | ||
|
|
||
| void AArch64BTILandingPadThunk::writeLong(uint8_t *buf) { | ||
| uint64_t s = destination.getVA(addend); | ||
| uint64_t p = getThunkTargetSym()->getVA() + 4; | ||
| write32(buf, 0xd503245f); // BTI c | ||
| write32(buf + 4, 0x14000000); // B S | ||
| ctx.target->relocateNoSym(buf + 4, R_AARCH64_CALL26, s - p); | ||
| } | ||
|
|
||
| // ARM Target Thunks | ||
| static uint64_t getARMThunkDestVA(const Symbol &s) { | ||
| uint64_t v = s.isInPlt() ? s.getPltVA() : s.getVA(); | ||
|
|
@@ -1264,9 +1350,12 @@ static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) { | |
| if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 && | ||
| type != R_AARCH64_PLT32) | ||
| fatal("unrecognized relocation type"); | ||
| bool mayNeedLandingPad = | ||
| (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) && | ||
| !isAArch64BTILandingPad(s, a); | ||
| if (config->picThunk) | ||
| return make<AArch64ADRPThunk>(s, a); | ||
| return make<AArch64ABSLongThunk>(s, a); | ||
| return make<AArch64ADRPThunk>(s, a, mayNeedLandingPad); | ||
| return make<AArch64ABSLongThunk>(s, a, mayNeedLandingPad); | ||
| } | ||
|
|
||
| // Creates a thunk for long branches or Thumb-ARM interworking. | ||
|
|
@@ -1480,3 +1569,12 @@ Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) { | |
| llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC"); | ||
| } | ||
| } | ||
|
|
||
| Thunk *elf::addLandingPadThunk(Symbol &s, int64_t a) { | ||
| switch (config->emachine) { | ||
| case EM_AARCH64: | ||
| return make<AArch64BTILandingPadThunk>(s, a); | ||
| default: | ||
| llvm_unreachable("add landing pad only supported for AArch64"); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reflow the comment after adopting 80-column wrap?