Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0f3795c
[llvm][MachO] Add arm64e chained fixup pointer structures
oskarwirga Mar 3, 2026
abb8525
[lld][MachO] Add AUTH relocation attribute and extend Reloc for arm64e
oskarwirga Mar 3, 2026
bd01a21
[lld][MachO] Add ARM64e target with authenticated stubs
oskarwirga Mar 3, 2026
3a95ff7
[lld][MachO] Add AuthGotSection for arm64e
oskarwirga Mar 3, 2026
a2569a2
[lld][MachO] Route arm64e symbols to authgot
oskarwirga Mar 3, 2026
0dc8730
[lld][MachO] Implement arm64e chained fixup encoding and dual GOT sup…
oskarwirga Mar 3, 2026
2dfcae4
[lld][MachO] Use bitfield struct for auth pointer decoding
oskarwirga Mar 25, 2026
3a0fc77
[lld][MachO] Handle authenticated pointers in ICF
oskarwirga Mar 25, 2026
cc833db
[lld][MachO][NFC] Improve readability in MapFile and Writer
oskarwirga Mar 25, 2026
44114e2
[lld][MachO] Use correct chained fixup stride for arm64e
oskarwirga Mar 25, 2026
8652225
[lld][MachO][NFC] Remove unused DenseSet include from Writer.cpp
oskarwirga Mar 30, 2026
721f422
[lld][MachO] Address review feedback for arm64e support
oskarwirga Apr 3, 2026
85e13c4
[lld][MachO] Address review feedback for arm64e support (round 2)
oskarwirga Apr 10, 2026
a595ff5
[lld][MachO][NFC] Drop unused segmentBase parameter from chained-fixu…
oskarwirga Apr 17, 2026
7ce207b
[lld][MachO][NFC] Use inline constexpr for shared ARM64 code arrays
oskarwirga Apr 17, 2026
c7d686c
[lld][MachO] Introduce PtrAuthKey enum for ARM64e PAC key indices
oskarwirga Apr 17, 2026
ea5c6e7
[lld][MachO][NFC] Use Symbol::getAuthGotVA helper instead of raw auth…
oskarwirga Apr 17, 2026
b945f0c
[lld][MachO] Encode high8 from targetValue (after USERLAND24 normaliz…
oskarwirga Apr 17, 2026
d10151a
[lld][MachO] Preserve AuthInfo when decoding non-extern AUTH relocations
oskarwirga Apr 17, 2026
81cac8f
[lld][MachO] Restore sizeof(Symbol)==56 by bitfield-packing symbolKind
oskarwirga Apr 17, 2026
fa63aba
[lld][MachO][NFC] Add Relocation::setAddend() and migrate raw addend …
oskarwirga Apr 24, 2026
ac89434
[lld][MachO] Address review feedback for arm64e support (round 3)
oskarwirga Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 3 additions & 89 deletions lld/MachO/Arch/ARM64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,10 @@ namespace {
struct ARM64 : ARM64Common {
ARM64();
void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override;
void writeStubHelperHeader(uint8_t *buf) const override;
void writeStubHelperEntry(uint8_t *buf, const Symbol &,
uint64_t entryAddr) const override;

void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const override;
void populateThunk(InputSection *thunk, Symbol *funcSym,
int64_t addend) override;

void initICFSafeThunkBody(InputSection *thunk,
Symbol *targetSym) const override;
Symbol *getThunkBranchTarget(InputSection *thunk) const override;
uint32_t getICFSafeThunkSize() const override;
};

} // namespace
Expand Down Expand Up @@ -80,30 +70,6 @@ void ARM64::writeStub(uint8_t *buf8, const Symbol &sym,
::writeStub(buf8, stubCode, sym, pointerVA);
}

static constexpr uint32_t stubHelperHeaderCode[] = {
0x90000011, // 00: adrp x17, _dyld_private@page
0x91000231, // 04: add x17, x17, _dyld_private@pageoff
0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]!
0x90000010, // 0c: adrp x16, dyld_stub_binder@page
0xf9400210, // 10: ldr x16, [x16, dyld_stub_binder@pageoff]
0xd61f0200, // 14: br x16
};

void ARM64::writeStubHelperHeader(uint8_t *buf8) const {
::writeStubHelperHeader<LP64>(buf8, stubHelperHeaderCode);
}

static constexpr uint32_t stubHelperEntryCode[] = {
0x18000050, // 00: ldr w16, l0
0x14000000, // 04: b stubHelperHeader
0x00000000, // 08: l0: .long 0
};

void ARM64::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,
uint64_t entryVA) const {
::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA);
}

static constexpr uint32_t objcStubsFastCode[] = {
0x90000001, // adrp x1, __objc_selrefs@page
0xf9400021, // ldr x1, [x1, @selector("foo")@pageoff]
Expand Down Expand Up @@ -152,64 +118,12 @@ void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
stubOffset += objcStubSize;
}

// A thunk is the relaxed variation of stubCode. We don't need the
// extra indirection through a lazy pointer because the target address
// is known at link time.
static constexpr uint32_t thunkCode[] = {
0x90000010, // 00: adrp x16, <thunk.ptr>@page
0x91000210, // 04: add x16, [x16,<thunk.ptr>@pageoff]
0xd61f0200, // 08: br x16
};

void ARM64::populateThunk(InputSection *thunk, Symbol *funcSym,
int64_t addend) {
thunk->align = 4;
thunk->data = {reinterpret_cast<const uint8_t *>(thunkCode),
sizeof(thunkCode)};
thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGEOFF12,
/*pcrel=*/false, /*length=*/2,
/*offset=*/4, /*addend=*/addend,
/*referent=*/funcSym);
thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGE21,
/*pcrel=*/true, /*length=*/2,
/*offset=*/0, /*addend=*/addend,
/*referent=*/funcSym);
}
// Just a single direct branch to the target function.
static constexpr uint32_t icfSafeThunkCode[] = {
0x14000000, // 08: b target
};

void ARM64::initICFSafeThunkBody(InputSection *thunk, Symbol *targetSym) const {
// The base data here will not be itself modified, we'll just be adding a
// reloc below. So we can directly use the constexpr above as the data.
thunk->data = {reinterpret_cast<const uint8_t *>(icfSafeThunkCode),
sizeof(icfSafeThunkCode)};

thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_BRANCH26,
/*pcrel=*/true, /*length=*/2,
/*offset=*/0, /*addend=*/0,
/*referent=*/targetSym);
}

Symbol *ARM64::getThunkBranchTarget(InputSection *thunk) const {
assert(thunk->relocs.size() == 1 &&
"expected a single reloc on ARM64 ICF thunk");
auto &reloc = thunk->relocs[0];
assert(isa<Symbol *>(reloc.referent) &&
"ARM64 thunk reloc is expected to point to a Symbol");

return cast<Symbol *>(reloc.referent);
}

uint32_t ARM64::getICFSafeThunkSize() const { return sizeof(icfSafeThunkCode); }

ARM64::ARM64() : ARM64Common(LP64()) {
cpuType = CPU_TYPE_ARM64;
cpuSubtype = CPU_SUBTYPE_ARM64_ALL;

stubSize = sizeof(stubCode);
thunkSize = sizeof(thunkCode);
thunkSize = sizeof(arm64ThunkCode);

objcStubsFastSize = sizeof(objcStubsFastCode);
objcStubsFastAlignment = 32;
Expand All @@ -226,8 +140,8 @@ ARM64::ARM64() : ARM64Common(LP64()) {
subtractorRelocType = ARM64_RELOC_SUBTRACTOR;
unsignedRelocType = ARM64_RELOC_UNSIGNED;

stubHelperHeaderSize = sizeof(stubHelperHeaderCode);
stubHelperEntrySize = sizeof(stubHelperEntryCode);
stubHelperHeaderSize = sizeof(arm64StubHelperHeaderCode);
stubHelperEntrySize = sizeof(arm64StubHelperEntryCode);

relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
}
Expand Down
56 changes: 55 additions & 1 deletion lld/MachO/Arch/ARM64Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,58 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::macho;

void ARM64Common::writeStubHelperHeader(uint8_t *buf) const {
::writeStubHelperHeader<LP64>(buf, arm64StubHelperHeaderCode);
}

void ARM64Common::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
uint64_t entryVA) const {
::writeStubHelperEntry(buf, arm64StubHelperEntryCode, sym, entryVA);
}

void ARM64Common::populateThunk(InputSection *thunk, Symbol *funcSym,
int64_t addend) {
thunk->align = 4;
thunk->data = {reinterpret_cast<const uint8_t *>(arm64ThunkCode),
sizeof(arm64ThunkCode)};
thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGEOFF12,
/*pcrel=*/false, /*length=*/2,
/*offset=*/4, /*addend=*/addend,
/*referent=*/funcSym);
thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_PAGE21,
/*pcrel=*/true, /*length=*/2,
/*offset=*/0, /*addend=*/addend,
/*referent=*/funcSym);
}

void ARM64Common::initICFSafeThunkBody(InputSection *thunk,
Symbol *targetSym) const {
thunk->data = {reinterpret_cast<const uint8_t *>(arm64ICFSafeThunkCode),
sizeof(arm64ICFSafeThunkCode)};
thunk->relocs.emplace_back(/*type=*/ARM64_RELOC_BRANCH26,
/*pcrel=*/true, /*length=*/2,
/*offset=*/0, /*addend=*/0,
/*referent=*/targetSym);
}

Symbol *ARM64Common::getThunkBranchTarget(InputSection *thunk) const {
assert(thunk->relocs.size() == 1 &&
"expected a single reloc on ARM64 ICF thunk");
auto &reloc = thunk->relocs[0];
assert(isa<Symbol *>(reloc.referent) &&
"ARM64 thunk reloc is expected to point to a Symbol");
return cast<Symbol *>(reloc.referent);
}

uint32_t ARM64Common::getICFSafeThunkSize() const {
return sizeof(arm64ICFSafeThunkCode);
}

Comment thread
oskarwirga marked this conversation as resolved.
int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
const relocation_info rel) const {
if (rel.r_type != ARM64_RELOC_UNSIGNED &&
rel.r_type != ARM64_RELOC_SUBTRACTOR) {
rel.r_type != ARM64_RELOC_SUBTRACTOR &&
rel.r_type != ARM64_RELOC_AUTHENTICATED_POINTER) {
// All other reloc types should use the ADDEND relocation to store their
// addends.
// TODO(gkm): extract embedded addend just so we can assert that it is 0
Expand All @@ -28,6 +76,12 @@ int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,

const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
const uint8_t *loc = buf + offset + rel.r_address;

if (rel.r_type == ARM64_RELOC_AUTHENTICATED_POINTER) {
// Only the low 32 bits are the addend; upper bits hold ptrauth fields.
return llvm::SignExtend64<32>(read32le(loc));
}

switch (rel.r_length) {
case 2:
return static_cast<int32_t>(read32le(loc));
Expand Down
40 changes: 40 additions & 0 deletions lld/MachO/Arch/ARM64Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,36 @@

namespace lld::macho {

// Shared stub helper code — identical for ARM64 and ARM64e.
inline constexpr uint32_t arm64StubHelperHeaderCode[] = {
0x90000011, // 00: adrp x17, _dyld_private@page
0x91000231, // 04: add x17, x17, _dyld_private@pageoff
0xa9bf47f0, // 08: stp x16/x17, [sp, #-16]!
0x90000010, // 0c: adrp x16, dyld_stub_binder@page
0xf9400210, // 10: ldr x16, [x16, dyld_stub_binder@pageoff]
0xd61f0200, // 14: br x16
};

inline constexpr uint32_t arm64StubHelperEntryCode[] = {
0x18000050, // 00: ldr w16, l0
0x14000000, // 04: b stubHelperHeader
0x00000000, // 08: l0: .long 0
};

// A thunk is the relaxed variation of stubCode. We don't need the
// extra indirection through a lazy pointer because the target address
// is known at link time.
inline constexpr uint32_t arm64ThunkCode[] = {
0x90000010, // 00: adrp x16, <thunk.ptr>@page
0x91000210, // 04: add x16, [x16,<thunk.ptr>@pageoff]
0xd61f0200, // 08: br x16
};

// Just a single direct branch to the target function.
inline constexpr uint32_t arm64ICFSafeThunkCode[] = {
0x14000000, // 00: b target
};

struct ARM64Common : TargetInfo {
template <class LP> ARM64Common(LP lp) : TargetInfo(lp) {}

Expand All @@ -31,6 +61,16 @@ struct ARM64Common : TargetInfo {

void handleDtraceReloc(const Symbol *sym, const Relocation &r,
uint8_t *loc) const override;

void writeStubHelperHeader(uint8_t *buf) const override;
void writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
uint64_t entryVA) const override;
void populateThunk(InputSection *thunk, Symbol *funcSym,
int64_t addend) override;
void initICFSafeThunkBody(InputSection *thunk,
Symbol *targetSym) const override;
Symbol *getThunkBranchTarget(InputSection *thunk) const override;
uint32_t getICFSafeThunkSize() const override;
};

inline uint64_t bitField(uint64_t value, int right, int width, int left) {
Expand Down
Loading