Skip to content

[lld-macho] icf objc stubs #79730

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

Merged
merged 4 commits into from
Feb 1, 2024
Merged
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
14 changes: 6 additions & 8 deletions lld/MachO/Arch/ARM64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ struct ARM64 : ARM64Common {
uint64_t entryAddr) const override;

void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t &stubOffset, uint64_t selrefsVA,
uint64_t selectorIndex,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const override;
void populateThunk(InputSection *thunk, Symbol *funcSym) override;
void applyOptimizationHints(uint8_t *, const ObjFile &) const override;
Expand Down Expand Up @@ -124,8 +123,7 @@ static constexpr uint32_t objcStubsSmallCode[] = {
};

void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t &stubOffset, uint64_t selrefsVA,
uint64_t selectorIndex,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const {
uint64_t objcMsgSendAddr;
uint64_t objcStubSize;
Expand All @@ -136,8 +134,8 @@ void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
objcMsgSendAddr = in.got->addr;
objcMsgSendIndex = objcMsgSend->gotIndex;
::writeObjCMsgSendFastStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
stubOffset, selrefsVA, selectorIndex,
objcMsgSendAddr, objcMsgSendIndex);
stubOffset, selrefVA, objcMsgSendAddr,
objcMsgSendIndex);
} else {
assert(config->objcStubsMode == ObjCStubsMode::small);
objcStubSize = target->objcStubsSmallSize;
Expand All @@ -149,8 +147,8 @@ void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
objcMsgSendIndex = objcMsgSend->stubsIndex;
}
::writeObjCMsgSendSmallStub<LP64>(buf, objcStubsSmallCode, sym, stubsAddr,
stubOffset, selrefsVA, selectorIndex,
objcMsgSendAddr, objcMsgSendIndex);
stubOffset, selrefVA, objcMsgSendAddr,
objcMsgSendIndex);
}
stubOffset += objcStubSize;
}
Expand Down
26 changes: 11 additions & 15 deletions lld/MachO/Arch/ARM64Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,21 @@ inline void writeStubHelperEntry(uint8_t *buf8,
}

template <class LP>
inline void
writeObjCMsgSendFastStub(uint8_t *buf, const uint32_t objcStubsFastCode[8],
Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset,
uint64_t selrefsVA, uint64_t selectorIndex,
uint64_t gotAddr, uint64_t msgSendIndex) {
inline void writeObjCMsgSendFastStub(uint8_t *buf,
const uint32_t objcStubsFastCode[8],
Symbol *sym, uint64_t stubsAddr,
uint64_t stubOffset, uint64_t selrefVA,
uint64_t gotAddr, uint64_t msgSendIndex) {
SymbolDiagnostic d = {sym, sym->getName()};
auto *buf32 = reinterpret_cast<uint32_t *>(buf);

auto pcPageBits = [stubsAddr, stubOffset](int i) {
return pageBits(stubsAddr + stubOffset + i * sizeof(uint32_t));
};

uint64_t selectorOffset = selectorIndex * LP::wordSize;
encodePage21(&buf32[0], d, objcStubsFastCode[0],
pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
encodePageOff12(&buf32[1], d, objcStubsFastCode[1],
selrefsVA + selectorOffset);
pageBits(selrefVA) - pcPageBits(0));
encodePageOff12(&buf32[1], d, objcStubsFastCode[1], selrefVA);
uint64_t gotOffset = msgSendIndex * LP::wordSize;
encodePage21(&buf32[2], d, objcStubsFastCode[2],
pageBits(gotAddr + gotOffset) - pcPageBits(2));
Expand All @@ -184,20 +182,18 @@ template <class LP>
inline void
writeObjCMsgSendSmallStub(uint8_t *buf, const uint32_t objcStubsSmallCode[3],
Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset,
uint64_t selrefsVA, uint64_t selectorIndex,
uint64_t msgSendAddr, uint64_t msgSendIndex) {
uint64_t selrefVA, uint64_t msgSendAddr,
uint64_t msgSendIndex) {
SymbolDiagnostic d = {sym, sym->getName()};
auto *buf32 = reinterpret_cast<uint32_t *>(buf);

auto pcPageBits = [stubsAddr, stubOffset](int i) {
return pageBits(stubsAddr + stubOffset + i * sizeof(uint32_t));
};

uint64_t selectorOffset = selectorIndex * LP::wordSize;
encodePage21(&buf32[0], d, objcStubsSmallCode[0],
pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
encodePageOff12(&buf32[1], d, objcStubsSmallCode[1],
selrefsVA + selectorOffset);
pageBits(selrefVA) - pcPageBits(0));
encodePageOff12(&buf32[1], d, objcStubsSmallCode[1], selrefVA);
uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize;
uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t);
encodeBranch26(&buf32[2], {nullptr, "objc_msgSend stub"},
Expand Down
5 changes: 2 additions & 3 deletions lld/MachO/Arch/ARM64_32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ struct ARM64_32 : ARM64Common {
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 selrefsVA,
uint64_t selectorIndex,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const override;
};

Expand Down Expand Up @@ -101,7 +100,7 @@ void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,

void ARM64_32::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
uint64_t stubsAddr, uint64_t &stubOffset,
uint64_t selrefsVA, uint64_t selectorIndex,
uint64_t selrefVA,
Symbol *objcMsgSend) const {
fatal("TODO: implement this");
}
Expand Down
9 changes: 3 additions & 6 deletions lld/MachO/Arch/X86_64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ struct X86_64 : TargetInfo {
uint64_t entryAddr) const override;

void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t &stubOffset, uint64_t selrefsVA,
uint64_t selectorIndex,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const override;

void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
Expand Down Expand Up @@ -182,17 +181,15 @@ static constexpr uint8_t objcStubsFastCode[] = {
};

void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t &stubOffset, uint64_t selrefsVA,
uint64_t selectorIndex,
uint64_t &stubOffset, uint64_t selrefVA,
Symbol *objcMsgSend) const {
uint64_t objcMsgSendAddr = in.got->addr;
uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;

memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));
SymbolDiagnostic d = {sym, sym->getName()};
uint64_t stubAddr = stubsAddr + stubOffset;
writeRipRelative(d, buf, stubAddr, 7,
selrefsVA + selectorIndex * LP64::wordSize);
writeRipRelative(d, buf, stubAddr, 7, selrefVA);
writeRipRelative(d, buf, stubAddr, 0xd,
objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);
stubOffset += target->objcStubsFastSize;
Expand Down
91 changes: 58 additions & 33 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -825,10 +825,60 @@ StringRef ObjCStubsSection::getMethname(Symbol *sym) {
return methname;
}

void ObjCStubsSection::initialize() {
// Do not fold selrefs without ICF.
if (config->icfLevel == ICFLevel::none)
return;

// Search methnames already referenced in __objc_selrefs
// Map the name to the corresponding selref entry
// which we will reuse when creating objc stubs.
for (ConcatInputSection *isec : inputSections) {
if (isec->shouldOmitFromOutput())
continue;
if (isec->getName() != section_names::objcSelrefs)
continue;
// We expect a single relocation per selref entry to __objc_methname that
// might be aggregated.
assert(isec->relocs.size() == 1);
auto Reloc = isec->relocs[0];
if (const auto *sym = Reloc.referent.dyn_cast<Symbol *>()) {
if (const auto *d = dyn_cast<Defined>(sym)) {
auto *cisec = cast<CStringInputSection>(d->isec);
auto methname = cisec->getStringRefAtOffset(d->value);
methnameToSelref[CachedHashStringRef(methname)] = isec;
}
}
}
}

void ObjCStubsSection::addEntry(Symbol *sym) {
StringRef methname = getMethname(sym);
offsets.push_back(
in.objcMethnameSection->getStringOffset(methname).outSecOff);
// We create a selref entry for each unique methname.
if (!methnameToSelref.count(CachedHashStringRef(methname))) {
auto methnameOffset =
in.objcMethnameSection->getStringOffset(methname).outSecOff;

size_t wordSize = target->wordSize;
uint8_t *selrefData = bAlloc().Allocate<uint8_t>(wordSize);
write64le(selrefData, methnameOffset);
auto *objcSelref = makeSyntheticInputSection(
segment_names::data, section_names::objcSelrefs,
S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP,
ArrayRef<uint8_t>{selrefData, wordSize},
/*align=*/wordSize);
objcSelref->live = true;
objcSelref->relocs.push_back(
{/*type=*/target->unsignedRelocType,
/*pcrel=*/false, /*length=*/3,
/*offset=*/0,
/*addend=*/static_cast<int64_t>(methnameOffset),
/*referent=*/in.objcMethnameSection->isec});
objcSelref->parent = ConcatOutputSection::getOrCreateForInput(objcSelref);
inputSections.push_back(objcSelref);
objcSelref->isFinal = true;
methnameToSelref[CachedHashStringRef(methname)] = objcSelref;
}

auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
? target->objcStubsFastSize
Expand Down Expand Up @@ -862,32 +912,6 @@ void ObjCStubsSection::setUp() {
if (!isa<Defined>(objcMsgSend))
in.stubs->addEntry(objcMsgSend);
}

size_t size = offsets.size() * target->wordSize;
uint8_t *selrefsData = bAlloc().Allocate<uint8_t>(size);
for (size_t i = 0, n = offsets.size(); i < n; ++i)
write64le(&selrefsData[i * target->wordSize], offsets[i]);

in.objcSelrefs =
makeSyntheticInputSection(segment_names::data, section_names::objcSelrefs,
S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP,
ArrayRef<uint8_t>{selrefsData, size},
/*align=*/target->wordSize);
in.objcSelrefs->live = true;

for (size_t i = 0, n = offsets.size(); i < n; ++i) {
in.objcSelrefs->relocs.push_back(
{/*type=*/target->unsignedRelocType,
/*pcrel=*/false, /*length=*/3,
/*offset=*/static_cast<uint32_t>(i * target->wordSize),
/*addend=*/offsets[i] * in.objcMethnameSection->align,
/*referent=*/in.objcMethnameSection->isec});
}

in.objcSelrefs->parent =
ConcatOutputSection::getOrCreateForInput(in.objcSelrefs);
inputSections.push_back(in.objcSelrefs);
in.objcSelrefs->isFinal = true;
}

uint64_t ObjCStubsSection::getSize() const {
Expand All @@ -898,15 +922,16 @@ uint64_t ObjCStubsSection::getSize() const {
}

void ObjCStubsSection::writeTo(uint8_t *buf) const {
assert(in.objcSelrefs->live);
assert(in.objcSelrefs->isFinal);

uint64_t stubOffset = 0;
for (size_t i = 0, n = symbols.size(); i < n; ++i) {
Defined *sym = symbols[i];

auto methname = getMethname(sym);
auto j = methnameToSelref.find(CachedHashStringRef(methname));
assert(j != methnameToSelref.end());
auto selrefAddr = j->second->getVA(0);
target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr,
stubOffset, in.objcSelrefs->getVA(), i,
objcMsgSend);
stubOffset, selrefAddr, objcMsgSend);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lld/MachO/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ class StubHelperSection final : public SyntheticSection {
class ObjCStubsSection final : public SyntheticSection {
public:
ObjCStubsSection();
void initialize();
void addEntry(Symbol *sym);
uint64_t getSize() const override;
bool isNeeded() const override { return !symbols.empty(); }
Expand All @@ -337,7 +338,7 @@ class ObjCStubsSection final : public SyntheticSection {

private:
std::vector<Defined *> symbols;
std::vector<uint32_t> offsets;
llvm::DenseMap<llvm::CachedHashStringRef, InputSection *> methnameToSelref;
Symbol *objcMsgSend = nullptr;
};

Expand Down Expand Up @@ -794,7 +795,6 @@ struct InStruct {
StubsSection *stubs = nullptr;
StubHelperSection *stubHelper = nullptr;
ObjCStubsSection *objcStubs = nullptr;
ConcatInputSection *objcSelrefs = nullptr;
UnwindInfoSection *unwindInfo = nullptr;
ObjCImageInfoSection *objCImageInfo = nullptr;
ConcatInputSection *imageLoaderCache = nullptr;
Expand Down
2 changes: 1 addition & 1 deletion lld/MachO/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class TargetInfo {

virtual void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
uint64_t stubsAddr, uint64_t &stubOffset,
uint64_t selrefsVA, uint64_t selectorIndex,
uint64_t selrefVA,
Symbol *objcMsgSend) const = 0;

// Symbols may be referenced via either the GOT or the stubs section,
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ static void addNonWeakDefinition(const Defined *defined) {

void Writer::scanSymbols() {
TimeTraceScope timeScope("Scan symbols");
in.objcStubs->initialize();
for (Symbol *sym : symtab->getSymbols()) {
if (auto *defined = dyn_cast<Defined>(sym)) {
if (!defined->isLive())
Expand Down
3 changes: 0 additions & 3 deletions lld/test/MachO/objc-selrefs.s
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
# SELREFS-NEXT: __TEXT:__objc_methname:length
# SELREFS-EMPTY:

## We don't yet support dedup'ing implicitly-defined selrefs.
# RUN: %lld -dylib -arch arm64 -lSystem --icf=all -o %t/explicit-and-implicit \
# RUN: %t/explicit-selrefs-1.o %t/explicit-selrefs-2.o %t/implicit-selrefs.o
# RUN: llvm-otool -vs __DATA __objc_selrefs %t/explicit-and-implicit \
Expand All @@ -43,8 +42,6 @@
# EXPLICIT-AND-IMPLICIT: Contents of (__DATA,__objc_selrefs) section
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:foo
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:bar
# NOTE: Ideally this wouldn't exist, but while it does it needs to point to the deduplicated string
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:foo
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:length

#--- explicit-selrefs-1.s
Expand Down
12 changes: 12 additions & 0 deletions lld/test/MachO/x86-64-objc-stubs.s
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

# WARNING: warning: -objc_stubs_small is not yet implemented, defaulting to -objc_stubs_fast

# RUN: %lld -arch x86_64 -lSystem -o %t-icfsafe.out --icf=safe %t.o
# RUN: llvm-otool -vs __DATA __objc_selrefs %t-icfsafe.out | FileCheck %s --check-prefix=ICF
# RUN: %lld -arch x86_64 -lSystem -o %t-icfall.out --icf=all %t.o
# RUN: llvm-otool -vs __DATA __objc_selrefs %t-icfall.out | FileCheck %s --check-prefix=ICF

# CHECK: Sections:
# CHECK: __got {{[0-9a-f]*}} [[#%x, GOTSTART:]] DATA
# CHECK: __objc_selrefs {{[0-9a-f]*}} [[#%x, SELSTART:]] DATA
Expand All @@ -21,6 +26,13 @@
# CHECK-NEXT: [[#%x, FOOSELREF:]] __TEXT:__objc_methname:foo
# CHECK-NEXT: [[#%x, LENGTHSELREF:]] __TEXT:__objc_methname:length

# ICF: Contents of (__DATA,__objc_selrefs) section

# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:foo
# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:bar
# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:length
# ICF-EMPTY:

# CHECK: Contents of (__TEXT,__objc_stubs) section

# CHECK-NEXT: _objc_msgSend$foo:
Expand Down