[lld] [llvm] [lld-macho] Clean up chained fixup entry emission (NFC) (PR #149483)
Daniel Bertalan via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 18 05:14:52 PDT 2025
https://github.com/BertalanD updated https://github.com/llvm/llvm-project/pull/149483
>From 72644419f7b8490936a0233cd2c06adb07a28b88 Mon Sep 17 00:00:00 2001
From: Daniel Bertalan <dani at danielbertalan.dev>
Date: Fri, 18 Jul 2025 11:00:41 +0200
Subject: [PATCH 1/2] [lld-macho] Clean up chained fixup entry emission (NFC)
These changes are in preparation for arm64e support:
- Store inline addends as `uint32_t` instead of `uint8_t`, as arm64e
fixup formats store these in more than 8 bits.
- Do not assume `DYLD_CHAINED_PTR_64` format.
- Construct the bit-fields in the fixup entries by hand instead of
using `reinterpret_cast`, as the latter is a code smell and will work
incorrectly on big-endian hosts.
---
lld/MachO/SyntheticSections.cpp | 78 ++++++++++++++++++--------
lld/MachO/SyntheticSections.h | 7 ++-
lld/MachO/Writer.cpp | 8 ++-
llvm/include/llvm/BinaryFormat/MachO.h | 2 +-
4 files changed, 67 insertions(+), 28 deletions(-)
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index 979a4ee6d8133..b3a10524158d0 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -345,17 +345,28 @@ void NonLazyPointerSectionBase::addEntry(Symbol *sym) {
void macho::writeChainedRebase(uint8_t *buf, uint64_t targetVA) {
assert(config->emitChainedFixups);
- assert(target->wordSize == 8 && "Only 64-bit platforms are supported");
- auto *rebase = reinterpret_cast<dyld_chained_ptr_64_rebase *>(buf);
- rebase->target = targetVA & 0xf'ffff'ffff;
- rebase->high8 = (targetVA >> 56);
- rebase->reserved = 0;
- rebase->next = 0;
- rebase->bind = 0;
-
- // The fixup format places a 64 GiB limit on the output's size.
- // Should we handle this gracefully?
- uint64_t encodedVA = rebase->target | ((uint64_t)rebase->high8 << 56);
+
+ uint64_t encodedVA = 0;
+ switch (in.chainedFixups->pointerFormat()) {
+ case DYLD_CHAINED_PTR_64: {
+ // struct dyld_chained_ptr_64_rebase {
+ // uint64_t target : 36; // lower 36 bits of target VA
+ // uint64_t high8 : 8; // upper 8 bits of target VA
+ // uint64_t reserved : 7; // set to 0
+ // uint64_t next : 12; // filled in by Writer later
+ // uint64_t bind : 1; // set to 0
+ // };
+
+ uint64_t target36 = targetVA & 0xf'ffff'ffff;
+ uint64_t high8 = targetVA >> 56;
+ write64le(buf, target36 | (high8 << 36));
+ encodedVA = target36 | (high8 << 56);
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported chained fixup pointer format");
+ }
+
if (encodedVA != targetVA)
error("rebase target address 0x" + Twine::utohexstr(targetVA) +
" does not fit into chained fixup. Re-link with -no_fixup_chains");
@@ -363,14 +374,35 @@ void macho::writeChainedRebase(uint8_t *buf, uint64_t targetVA) {
static void writeChainedBind(uint8_t *buf, const Symbol *sym, int64_t addend) {
assert(config->emitChainedFixups);
- assert(target->wordSize == 8 && "Only 64-bit platforms are supported");
- auto *bind = reinterpret_cast<dyld_chained_ptr_64_bind *>(buf);
auto [ordinal, inlineAddend] = in.chainedFixups->getBinding(sym, addend);
- bind->ordinal = ordinal;
- bind->addend = inlineAddend;
- bind->reserved = 0;
- bind->next = 0;
- bind->bind = 1;
+
+ if (!isUInt<24>(ordinal))
+ error("Import ordinal for symbol '" + sym->getName() + "' (" +
+ utohexstr(ordinal) +
+ ") is larger than maximum import count (0xffffff). Re-link with "
+ "-no_fixup_chains");
+
+ ordinal &= 0xffffff;
+
+ switch (in.chainedFixups->pointerFormat()) {
+ case DYLD_CHAINED_PTR_64: {
+ // struct dyld_chained_ptr_64_bind {
+ // uint64_t ordinal : 24; // import ordinal
+ // uint64_t addend : 8;
+ // uint64_t reserved : 19; // set to 0
+ // uint64_t next : 12; // filled in by Writer later
+ // uint64_t bind : 1; // set to 1
+ // };
+
+ assert(isUInt<8>(inlineAddend) &&
+ "large addend should be stored out-of-line");
+
+ write64le(buf, ordinal | (inlineAddend << 24) | (1ULL << 63));
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported chained fixup pointer format");
+ }
}
void macho::writeChainedFixup(uint8_t *buf, const Symbol *sym, int64_t addend) {
@@ -2300,7 +2332,10 @@ void macho::createSyntheticSymbols() {
}
ChainedFixupsSection::ChainedFixupsSection()
- : LinkEditSection(segment_names::linkEdit, section_names::chainFixups) {}
+ : LinkEditSection(segment_names::linkEdit, section_names::chainFixups) {
+ // FIXME: ld64 uses DYLD_CHAINED_PTR_64_OFFSET on macOS 12+.
+ ptrFormat = DYLD_CHAINED_PTR_64;
+}
bool ChainedFixupsSection::isNeeded() const {
assert(config->emitChainedFixups);
@@ -2328,7 +2363,7 @@ void ChainedFixupsSection::addBinding(const Symbol *sym,
}
}
-std::pair<uint32_t, uint8_t>
+std::pair<uint32_t, uint32_t>
ChainedFixupsSection::getBinding(const Symbol *sym, int64_t addend) const {
int64_t outlineAddend = (addend < 0 || addend > 0xFF) ? addend : 0;
auto it = bindings.find({sym, outlineAddend});
@@ -2379,8 +2414,7 @@ size_t ChainedFixupsSection::SegmentInfo::writeTo(uint8_t *buf) const {
auto *segInfo = reinterpret_cast<dyld_chained_starts_in_segment *>(buf);
segInfo->size = getSize();
segInfo->page_size = target->getPageSize();
- // FIXME: Use DYLD_CHAINED_PTR_64_OFFSET on newer OS versions.
- segInfo->pointer_format = DYLD_CHAINED_PTR_64;
+ segInfo->pointer_format = in.chainedFixups->pointerFormat();
segInfo->segment_offset = oseg->addr - in.header->addr;
segInfo->max_valid_pointer = 0; // not used on 64-bit
segInfo->page_count = pageStarts.back().first + 1;
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index 5796b0790c83a..1370b51381baf 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -800,14 +800,16 @@ class ChainedFixupsSection final : public LinkEditSection {
void setHasNonWeakDefinition() { hasNonWeakDef = true; }
// Returns an (ordinal, inline addend) tuple used by dyld_chained_ptr_64_bind.
- std::pair<uint32_t, uint8_t> getBinding(const Symbol *sym,
- int64_t addend) const;
+ std::pair<uint32_t, uint32_t> getBinding(const Symbol *sym,
+ int64_t addend) const;
const std::vector<Location> &getLocations() const { return locations; }
bool hasWeakBinding() const { return hasWeakBind; }
bool hasNonWeakDefinition() const { return hasNonWeakDef; }
+ llvm::MachO::ChainedPointerFormat pointerFormat() const { return ptrFormat; }
+
private:
// Location::offset initially stores the offset within an InputSection, but
// contains output segment offsets after finalizeContents().
@@ -835,6 +837,7 @@ class ChainedFixupsSection final : public LinkEditSection {
bool hasWeakBind = false;
bool hasNonWeakDef = false;
llvm::MachO::ChainedImportFormat importFormat;
+ llvm::MachO::ChainedPointerFormat ptrFormat;
};
void writeChainedRebase(uint8_t *buf, uint64_t targetVA);
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index f288fadc0d14f..65ba7895239f1 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -36,6 +36,7 @@
using namespace llvm;
using namespace llvm::MachO;
+using namespace llvm::support::endian;
using namespace llvm::sys;
using namespace lld;
using namespace lld::macho;
@@ -1283,9 +1284,10 @@ void Writer::buildFixupChains() {
"fixups are unaligned (offset " + Twine(offset) +
" is not a multiple of the stride). Re-link with -no_fixup_chains");
- // The "next" field is in the same location for bind and rebase entries.
- reinterpret_cast<dyld_chained_ptr_64_bind *>(buf + loc[i - 1].offset)
- ->next = offset / stride;
+ // Set the previous fixup's next offset (bits 51-62) to point to this.
+ void *prev = buf + loc[i - 1].offset;
+ write64le(prev, read64le(prev) | ((offset / stride) << 51));
+
++i;
}
}
diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h
index 5dbdfb13d1a5f..0552e18132c2d 100644
--- a/llvm/include/llvm/BinaryFormat/MachO.h
+++ b/llvm/include/llvm/BinaryFormat/MachO.h
@@ -1043,7 +1043,7 @@ enum {
};
// Values for dyld_chained_starts_in_segment::pointer_format.
-enum {
+enum ChainedPointerFormat {
DYLD_CHAINED_PTR_ARM64E = 1,
DYLD_CHAINED_PTR_64 = 2,
DYLD_CHAINED_PTR_32 = 3,
>From e9a8bba7f76729dae5f42271b7342acb5d54dc81 Mon Sep 17 00:00:00 2001
From: Daniel Bertalan <dani at danielbertalan.dev>
Date: Fri, 18 Jul 2025 14:14:26 +0200
Subject: [PATCH 2/2] fixup! re-add deleted comment
---
lld/MachO/SyntheticSections.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index b3a10524158d0..5ca59ae9f2960 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -367,6 +367,8 @@ void macho::writeChainedRebase(uint8_t *buf, uint64_t targetVA) {
llvm_unreachable("unsupported chained fixup pointer format");
}
+ // The fixup format places a 64 GiB limit on the output's size.
+ // Should we handle this gracefully?
if (encodedVA != targetVA)
error("rebase target address 0x" + Twine::utohexstr(targetVA) +
" does not fit into chained fixup. Re-link with -no_fixup_chains");
More information about the llvm-commits
mailing list