Skip to content

Commit 3913931

Browse files
authored
[lld-macho] icf objc stubs (#79730)
This supports icf for objc stubs.
1 parent 1bc7be6 commit 3913931

10 files changed

+96
-71
lines changed

lld/MachO/Arch/ARM64.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ struct ARM64 : ARM64Common {
3737
uint64_t entryAddr) const override;
3838

3939
void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
40-
uint64_t &stubOffset, uint64_t selrefsVA,
41-
uint64_t selectorIndex,
40+
uint64_t &stubOffset, uint64_t selrefVA,
4241
Symbol *objcMsgSend) const override;
4342
void populateThunk(InputSection *thunk, Symbol *funcSym) override;
4443
void applyOptimizationHints(uint8_t *, const ObjFile &) const override;
@@ -124,8 +123,7 @@ static constexpr uint32_t objcStubsSmallCode[] = {
124123
};
125124

126125
void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
127-
uint64_t &stubOffset, uint64_t selrefsVA,
128-
uint64_t selectorIndex,
126+
uint64_t &stubOffset, uint64_t selrefVA,
129127
Symbol *objcMsgSend) const {
130128
uint64_t objcMsgSendAddr;
131129
uint64_t objcStubSize;
@@ -136,8 +134,8 @@ void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
136134
objcMsgSendAddr = in.got->addr;
137135
objcMsgSendIndex = objcMsgSend->gotIndex;
138136
::writeObjCMsgSendFastStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
139-
stubOffset, selrefsVA, selectorIndex,
140-
objcMsgSendAddr, objcMsgSendIndex);
137+
stubOffset, selrefVA, objcMsgSendAddr,
138+
objcMsgSendIndex);
141139
} else {
142140
assert(config->objcStubsMode == ObjCStubsMode::small);
143141
objcStubSize = target->objcStubsSmallSize;
@@ -149,8 +147,8 @@ void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
149147
objcMsgSendIndex = objcMsgSend->stubsIndex;
150148
}
151149
::writeObjCMsgSendSmallStub<LP64>(buf, objcStubsSmallCode, sym, stubsAddr,
152-
stubOffset, selrefsVA, selectorIndex,
153-
objcMsgSendAddr, objcMsgSendIndex);
150+
stubOffset, selrefVA, objcMsgSendAddr,
151+
objcMsgSendIndex);
154152
}
155153
stubOffset += objcStubSize;
156154
}

lld/MachO/Arch/ARM64Common.h

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,21 @@ inline void writeStubHelperEntry(uint8_t *buf8,
153153
}
154154

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

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

168-
uint64_t selectorOffset = selectorIndex * LP::wordSize;
169168
encodePage21(&buf32[0], d, objcStubsFastCode[0],
170-
pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
171-
encodePageOff12(&buf32[1], d, objcStubsFastCode[1],
172-
selrefsVA + selectorOffset);
169+
pageBits(selrefVA) - pcPageBits(0));
170+
encodePageOff12(&buf32[1], d, objcStubsFastCode[1], selrefVA);
173171
uint64_t gotOffset = msgSendIndex * LP::wordSize;
174172
encodePage21(&buf32[2], d, objcStubsFastCode[2],
175173
pageBits(gotAddr + gotOffset) - pcPageBits(2));
@@ -184,20 +182,18 @@ template <class LP>
184182
inline void
185183
writeObjCMsgSendSmallStub(uint8_t *buf, const uint32_t objcStubsSmallCode[3],
186184
Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset,
187-
uint64_t selrefsVA, uint64_t selectorIndex,
188-
uint64_t msgSendAddr, uint64_t msgSendIndex) {
185+
uint64_t selrefVA, uint64_t msgSendAddr,
186+
uint64_t msgSendIndex) {
189187
SymbolDiagnostic d = {sym, sym->getName()};
190188
auto *buf32 = reinterpret_cast<uint32_t *>(buf);
191189

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

196-
uint64_t selectorOffset = selectorIndex * LP::wordSize;
197194
encodePage21(&buf32[0], d, objcStubsSmallCode[0],
198-
pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
199-
encodePageOff12(&buf32[1], d, objcStubsSmallCode[1],
200-
selrefsVA + selectorOffset);
195+
pageBits(selrefVA) - pcPageBits(0));
196+
encodePageOff12(&buf32[1], d, objcStubsSmallCode[1], selrefVA);
201197
uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize;
202198
uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t);
203199
encodeBranch26(&buf32[2], {nullptr, "objc_msgSend stub"},

lld/MachO/Arch/ARM64_32.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ struct ARM64_32 : ARM64Common {
3434
void writeStubHelperEntry(uint8_t *buf, const Symbol &,
3535
uint64_t entryAddr) const override;
3636
void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
37-
uint64_t &stubOffset, uint64_t selrefsVA,
38-
uint64_t selectorIndex,
37+
uint64_t &stubOffset, uint64_t selrefVA,
3938
Symbol *objcMsgSend) const override;
4039
};
4140

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

102101
void ARM64_32::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
103102
uint64_t stubsAddr, uint64_t &stubOffset,
104-
uint64_t selrefsVA, uint64_t selectorIndex,
103+
uint64_t selrefVA,
105104
Symbol *objcMsgSend) const {
106105
fatal("TODO: implement this");
107106
}

lld/MachO/Arch/X86_64.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ struct X86_64 : TargetInfo {
3838
uint64_t entryAddr) const override;
3939

4040
void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
41-
uint64_t &stubOffset, uint64_t selrefsVA,
42-
uint64_t selectorIndex,
41+
uint64_t &stubOffset, uint64_t selrefVA,
4342
Symbol *objcMsgSend) const override;
4443

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

184183
void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
185-
uint64_t &stubOffset, uint64_t selrefsVA,
186-
uint64_t selectorIndex,
184+
uint64_t &stubOffset, uint64_t selrefVA,
187185
Symbol *objcMsgSend) const {
188186
uint64_t objcMsgSendAddr = in.got->addr;
189187
uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;
190188

191189
memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));
192190
SymbolDiagnostic d = {sym, sym->getName()};
193191
uint64_t stubAddr = stubsAddr + stubOffset;
194-
writeRipRelative(d, buf, stubAddr, 7,
195-
selrefsVA + selectorIndex * LP64::wordSize);
192+
writeRipRelative(d, buf, stubAddr, 7, selrefVA);
196193
writeRipRelative(d, buf, stubAddr, 0xd,
197194
objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);
198195
stubOffset += target->objcStubsFastSize;

lld/MachO/SyntheticSections.cpp

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -825,10 +825,60 @@ StringRef ObjCStubsSection::getMethname(Symbol *sym) {
825825
return methname;
826826
}
827827

828+
void ObjCStubsSection::initialize() {
829+
// Do not fold selrefs without ICF.
830+
if (config->icfLevel == ICFLevel::none)
831+
return;
832+
833+
// Search methnames already referenced in __objc_selrefs
834+
// Map the name to the corresponding selref entry
835+
// which we will reuse when creating objc stubs.
836+
for (ConcatInputSection *isec : inputSections) {
837+
if (isec->shouldOmitFromOutput())
838+
continue;
839+
if (isec->getName() != section_names::objcSelrefs)
840+
continue;
841+
// We expect a single relocation per selref entry to __objc_methname that
842+
// might be aggregated.
843+
assert(isec->relocs.size() == 1);
844+
auto Reloc = isec->relocs[0];
845+
if (const auto *sym = Reloc.referent.dyn_cast<Symbol *>()) {
846+
if (const auto *d = dyn_cast<Defined>(sym)) {
847+
auto *cisec = cast<CStringInputSection>(d->isec);
848+
auto methname = cisec->getStringRefAtOffset(d->value);
849+
methnameToSelref[CachedHashStringRef(methname)] = isec;
850+
}
851+
}
852+
}
853+
}
854+
828855
void ObjCStubsSection::addEntry(Symbol *sym) {
829856
StringRef methname = getMethname(sym);
830-
offsets.push_back(
831-
in.objcMethnameSection->getStringOffset(methname).outSecOff);
857+
// We create a selref entry for each unique methname.
858+
if (!methnameToSelref.count(CachedHashStringRef(methname))) {
859+
auto methnameOffset =
860+
in.objcMethnameSection->getStringOffset(methname).outSecOff;
861+
862+
size_t wordSize = target->wordSize;
863+
uint8_t *selrefData = bAlloc().Allocate<uint8_t>(wordSize);
864+
write64le(selrefData, methnameOffset);
865+
auto *objcSelref = makeSyntheticInputSection(
866+
segment_names::data, section_names::objcSelrefs,
867+
S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP,
868+
ArrayRef<uint8_t>{selrefData, wordSize},
869+
/*align=*/wordSize);
870+
objcSelref->live = true;
871+
objcSelref->relocs.push_back(
872+
{/*type=*/target->unsignedRelocType,
873+
/*pcrel=*/false, /*length=*/3,
874+
/*offset=*/0,
875+
/*addend=*/static_cast<int64_t>(methnameOffset),
876+
/*referent=*/in.objcMethnameSection->isec});
877+
objcSelref->parent = ConcatOutputSection::getOrCreateForInput(objcSelref);
878+
inputSections.push_back(objcSelref);
879+
objcSelref->isFinal = true;
880+
methnameToSelref[CachedHashStringRef(methname)] = objcSelref;
881+
}
832882

833883
auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
834884
? target->objcStubsFastSize
@@ -862,32 +912,6 @@ void ObjCStubsSection::setUp() {
862912
if (!isa<Defined>(objcMsgSend))
863913
in.stubs->addEntry(objcMsgSend);
864914
}
865-
866-
size_t size = offsets.size() * target->wordSize;
867-
uint8_t *selrefsData = bAlloc().Allocate<uint8_t>(size);
868-
for (size_t i = 0, n = offsets.size(); i < n; ++i)
869-
write64le(&selrefsData[i * target->wordSize], offsets[i]);
870-
871-
in.objcSelrefs =
872-
makeSyntheticInputSection(segment_names::data, section_names::objcSelrefs,
873-
S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP,
874-
ArrayRef<uint8_t>{selrefsData, size},
875-
/*align=*/target->wordSize);
876-
in.objcSelrefs->live = true;
877-
878-
for (size_t i = 0, n = offsets.size(); i < n; ++i) {
879-
in.objcSelrefs->relocs.push_back(
880-
{/*type=*/target->unsignedRelocType,
881-
/*pcrel=*/false, /*length=*/3,
882-
/*offset=*/static_cast<uint32_t>(i * target->wordSize),
883-
/*addend=*/offsets[i] * in.objcMethnameSection->align,
884-
/*referent=*/in.objcMethnameSection->isec});
885-
}
886-
887-
in.objcSelrefs->parent =
888-
ConcatOutputSection::getOrCreateForInput(in.objcSelrefs);
889-
inputSections.push_back(in.objcSelrefs);
890-
in.objcSelrefs->isFinal = true;
891915
}
892916

893917
uint64_t ObjCStubsSection::getSize() const {
@@ -898,15 +922,16 @@ uint64_t ObjCStubsSection::getSize() const {
898922
}
899923

900924
void ObjCStubsSection::writeTo(uint8_t *buf) const {
901-
assert(in.objcSelrefs->live);
902-
assert(in.objcSelrefs->isFinal);
903-
904925
uint64_t stubOffset = 0;
905926
for (size_t i = 0, n = symbols.size(); i < n; ++i) {
906927
Defined *sym = symbols[i];
928+
929+
auto methname = getMethname(sym);
930+
auto j = methnameToSelref.find(CachedHashStringRef(methname));
931+
assert(j != methnameToSelref.end());
932+
auto selrefAddr = j->second->getVA(0);
907933
target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr,
908-
stubOffset, in.objcSelrefs->getVA(), i,
909-
objcMsgSend);
934+
stubOffset, selrefAddr, objcMsgSend);
910935
}
911936
}
912937

lld/MachO/SyntheticSections.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ class StubHelperSection final : public SyntheticSection {
324324
class ObjCStubsSection final : public SyntheticSection {
325325
public:
326326
ObjCStubsSection();
327+
void initialize();
327328
void addEntry(Symbol *sym);
328329
uint64_t getSize() const override;
329330
bool isNeeded() const override { return !symbols.empty(); }
@@ -337,7 +338,7 @@ class ObjCStubsSection final : public SyntheticSection {
337338

338339
private:
339340
std::vector<Defined *> symbols;
340-
std::vector<uint32_t> offsets;
341+
llvm::DenseMap<llvm::CachedHashStringRef, InputSection *> methnameToSelref;
341342
Symbol *objcMsgSend = nullptr;
342343
};
343344

@@ -794,7 +795,6 @@ struct InStruct {
794795
StubsSection *stubs = nullptr;
795796
StubHelperSection *stubHelper = nullptr;
796797
ObjCStubsSection *objcStubs = nullptr;
797-
ConcatInputSection *objcSelrefs = nullptr;
798798
UnwindInfoSection *unwindInfo = nullptr;
799799
ObjCImageInfoSection *objCImageInfo = nullptr;
800800
ConcatInputSection *imageLoaderCache = nullptr;

lld/MachO/Target.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class TargetInfo {
7171

7272
virtual void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
7373
uint64_t stubsAddr, uint64_t &stubOffset,
74-
uint64_t selrefsVA, uint64_t selectorIndex,
74+
uint64_t selrefVA,
7575
Symbol *objcMsgSend) const = 0;
7676

7777
// Symbols may be referenced via either the GOT or the stubs section,

lld/MachO/Writer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ static void addNonWeakDefinition(const Defined *defined) {
720720

721721
void Writer::scanSymbols() {
722722
TimeTraceScope timeScope("Scan symbols");
723+
in.objcStubs->initialize();
723724
for (Symbol *sym : symtab->getSymbols()) {
724725
if (auto *defined = dyn_cast<Defined>(sym)) {
725726
if (!defined->isLive())

lld/test/MachO/objc-selrefs.s

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
# SELREFS-NEXT: __TEXT:__objc_methname:length
2525
# SELREFS-EMPTY:
2626

27-
## We don't yet support dedup'ing implicitly-defined selrefs.
2827
# RUN: %lld -dylib -arch arm64 -lSystem --icf=all -o %t/explicit-and-implicit \
2928
# RUN: %t/explicit-selrefs-1.o %t/explicit-selrefs-2.o %t/implicit-selrefs.o \
3029
# RUN: -no_fixup_chains
@@ -44,8 +43,6 @@
4443
# EXPLICIT-AND-IMPLICIT: Contents of (__DATA,__objc_selrefs) section
4544
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:foo
4645
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:bar
47-
# NOTE: Ideally this wouldn't exist, but while it does it needs to point to the deduplicated string
48-
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:foo
4946
# EXPLICIT-AND-IMPLICIT-NEXT: __TEXT:__objc_methname:length
5047

5148
#--- explicit-selrefs-1.s

lld/test/MachO/x86-64-objc-stubs.s

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

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

13+
# RUN: %lld -arch x86_64 -lSystem -o %t-icfsafe.out --icf=safe %t.o
14+
# RUN: llvm-otool -vs __DATA __objc_selrefs %t-icfsafe.out | FileCheck %s --check-prefix=ICF
15+
# RUN: %lld -arch x86_64 -lSystem -o %t-icfall.out --icf=all %t.o
16+
# RUN: llvm-otool -vs __DATA __objc_selrefs %t-icfall.out | FileCheck %s --check-prefix=ICF
17+
1318
# CHECK: Sections:
1419
# CHECK: __got {{[0-9a-f]*}} [[#%x, GOTSTART:]] DATA
1520
# CHECK: __objc_selrefs {{[0-9a-f]*}} [[#%x, SELSTART:]] DATA
@@ -21,6 +26,13 @@
2126
# CHECK-NEXT: [[#%x, FOOSELREF:]] __TEXT:__objc_methname:foo
2227
# CHECK-NEXT: [[#%x, LENGTHSELREF:]] __TEXT:__objc_methname:length
2328

29+
# ICF: Contents of (__DATA,__objc_selrefs) section
30+
31+
# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:foo
32+
# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:bar
33+
# ICF-NEXT: {{[0-9a-f]*}} __TEXT:__objc_methname:length
34+
# ICF-EMPTY:
35+
2436
# CHECK: Contents of (__TEXT,__objc_stubs) section
2537

2638
# CHECK-NEXT: _objc_msgSend$foo:

0 commit comments

Comments
 (0)