Skip to content
Merged
32 changes: 32 additions & 0 deletions lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

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?

// 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dyn_cast since &s cannot be nullptr.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

if (d == nullptr || d->section == nullptr ||
d->section->kind() != InputSectionBase::Regular)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!isa_and_nonnull<InputSection>(d->section)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

// 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sum has a type of uint64_t. Using uint64_t can avoid off < 0 below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

// 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return instr == ... || ...

or perhaps return is_contained(/*PACIASP*/0xd503233f, ...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. I've also used the common parts of the HINT encoding to avoid testing all combinations.

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:
Expand Down
23 changes: 23 additions & 0 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer try_emplace over operator[]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

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
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that targetSec could be nullptr in erroneous non-InputSection cases (MergeInputSection) and would cause a null pointer dereference in getISThunkSec.

If we don't write code guarding against such cases, perhaps just use cast<>.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used cast<>

The code in isAArch64BTILandingPad has a test

if (!isa_and_nonnull<InputSection>(d->section))

so t->needsSyntheticLandingPad() should return false in those cases.

ts = getISThunkSec(targetsec);
ts->addThunk(lpt);
}
t->landingPad = lpt->getThunkTargetSym();
}
}

// Redirect relocation to Thunk, we never go via the PLT to a Thunk
Expand Down
11 changes: 11 additions & 0 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <vector>

namespace lld::elf {
class Defined;
class Symbol;
class InputSection;
class InputSectionBase;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -200,6 +203,14 @@ class ThunkCreator {
// The Mips LA25 Thunk is an example of an inline ThunkSection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps update this comment to mention AArch64BTILandingPadThunk

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.
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
void addPPC64SaveRestore();
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t expr);
bool isAArch64BTILandingPad(Symbol &s, int64_t a);
template <typename ELFT> void writeARMCmseImportLib();
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type);
void riscvFinalizeRelax(int passes);
Expand Down
116 changes: 107 additions & 9 deletions lld/ELF/Thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -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[] = {
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If

// 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);
}
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If

// 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,
Expand All @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not needed.

Relocations.cpp calls ts = getISThunkSec(targetsec) without checking getTargetInputSection.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is called in mergeCmp() but I've removed it anyway as it is not a strict requirement that the landing pads are before the target section, they just need to be within direct branch range.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrap column is smaller than 80.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. I've reformatted.

// 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();
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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");
}
}
11 changes: 10 additions & 1 deletion lld/ELF/Thunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,17 @@ class Thunk {
return true;
}

// Thunks that indirectly branch to targets may need a synthetic landing
// pad generated close to the target. For example AArch64 when BTI is
// enabled.
virtual bool needsSyntheticLandingPad() { return false; }

Defined *getThunkTargetSym() const { return syms[0]; }

Symbol &destination;
int64_t addend;
// Alternative target when indirect branch to destination can't be used.
Symbol *landingPad = nullptr;
llvm::SmallVector<Defined *, 3> syms;
uint64_t offset = 0;
// The alignment requirement for this Thunk, defaults to the size of the
Expand All @@ -68,7 +75,9 @@ class Thunk {
// For a Relocation to symbol S create a Thunk to be added to a synthetic
// ThunkSection.
Thunk *addThunk(const InputSection &isec, Relocation &rel);

// Create a landing pad Thunk for use when indirect branches from Thunks
// are restricted.
Thunk *addLandingPadThunk(Symbol &s, int64_t a);
void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
const InputFile *file, int64_t addend);
void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset);
Expand Down
Loading