Skip to content

[lld-macho] Clean up chained fixup entry emission (NFC) #149483

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 55 additions & 19 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,32 +345,66 @@ 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;

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");
}

// 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);
if (encodedVA != targetVA)
error("rebase target address 0x" + Twine::utohexstr(targetVA) +
" does not fit into chained fixup. Re-link with -no_fixup_chains");
}

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) {
Expand Down Expand Up @@ -2300,7 +2334,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);
Expand Down Expand Up @@ -2328,7 +2365,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});
Expand Down Expand Up @@ -2379,8 +2416,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;
Expand Down
7 changes: 5 additions & 2 deletions lld/MachO/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/BinaryFormat/MachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down