-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[lld-macho][arm64] implement -objc_stubs_small #78665
Conversation
This patch implements `-objc_stubs_small` targeting arm64, aiming to align with ld64's behavior. 1. `-objc_stubs_fast`: As previously implemented, this always uses the Global Offset Table (GOT) to invoke objc_msgSend. The alignment of the objc stub is 32 bytes. 2. `-objc_stubs_small`: This behavior depends on whether objc_msgSend is defined. If it is, it directly jumps to objc_msgSend. If not, it creates another stub to indirectly jump to objc_msgSend, minimizing the size. The alignment of the objc stub in this case is 4 bytes.
@llvm/pr-subscribers-lld Author: Kyungwoo Lee (kyulee-com) ChangesThis patch implements
Full diff: https://github.com/llvm/llvm-project/pull/78665.diff 9 Files Affected:
diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp
index e3781763c6102b..7b18292da6d8c0 100644
--- a/lld/MachO/Arch/ARM64.cpp
+++ b/lld/MachO/Arch/ARM64.cpp
@@ -117,13 +117,24 @@ static constexpr uint32_t objcStubsFastCode[] = {
0xd4200020, // brk #0x1
};
+static constexpr uint32_t objcStubsSmallCode[] = {
+ 0x90000001, // adrp x1, __objc_selrefs@page
+ 0xf9400021, // ldr x1, [x1, @selector("foo")@pageoff]
+ 0x14000000, // b _objc_msgSend
+};
+
void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t stubOffset, uint64_t selrefsVA,
- uint64_t selectorIndex, uint64_t gotAddr,
+ uint64_t selectorIndex, uint64_t msgSendAddr,
uint64_t msgSendIndex) const {
- ::writeObjCMsgSendStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
- stubOffset, selrefsVA, selectorIndex, gotAddr,
- msgSendIndex);
+ if (config->objcStubsMode == ObjCStubsMode::fast)
+ ::writeObjCMsgSendFastStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
+ stubOffset, selrefsVA, selectorIndex,
+ msgSendAddr, msgSendIndex);
+ else
+ ::writeObjCMsgSendSmallStub<LP64>(buf, objcStubsSmallCode, sym, stubsAddr,
+ stubOffset, selrefsVA, selectorIndex,
+ msgSendAddr, msgSendIndex);
}
// A thunk is the relaxed variation of stubCode. We don't need the
@@ -157,7 +168,9 @@ ARM64::ARM64() : ARM64Common(LP64()) {
thunkSize = sizeof(thunkCode);
objcStubsFastSize = sizeof(objcStubsFastCode);
- objcStubsAlignment = 32;
+ objcStubsFastAlignment = 32;
+ objcStubsSmallSize = sizeof(objcStubsSmallCode);
+ objcStubsSmallAlignment = 4;
// Branch immediate is two's complement 26 bits, which is implicitly
// multiplied by 4 (since all functions are 4-aligned: The branch range
diff --git a/lld/MachO/Arch/ARM64Common.h b/lld/MachO/Arch/ARM64Common.h
index 9cfccb6cec7614..b038b6200f4d57 100644
--- a/lld/MachO/Arch/ARM64Common.h
+++ b/lld/MachO/Arch/ARM64Common.h
@@ -154,10 +154,10 @@ inline void writeStubHelperEntry(uint8_t *buf8,
template <class LP>
inline void
-writeObjCMsgSendStub(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) {
+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) {
SymbolDiagnostic d = {sym, sym->getName()};
auto *buf32 = reinterpret_cast<uint32_t *>(buf);
@@ -180,6 +180,30 @@ writeObjCMsgSendStub(uint8_t *buf, const uint32_t objcStubsFastCode[8],
buf32[7] = objcStubsFastCode[7];
}
+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) {
+ 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);
+ uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize;
+ uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t);
+ encodeBranch26(&buf32[2], {nullptr, "objc_msgSend stub"},
+ objcStubsSmallCode[2], msgSendStubVA - pcVA);
+}
+
} // namespace lld::macho
#endif
diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp
index a0d4e1a28a14a2..55fbdefb75db28 100644
--- a/lld/MachO/Arch/X86_64.cpp
+++ b/lld/MachO/Arch/X86_64.cpp
@@ -214,7 +214,7 @@ X86_64::X86_64() : TargetInfo(LP64()) {
stubHelperEntrySize = sizeof(stubHelperEntry);
objcStubsFastSize = sizeof(objcStubsFastCode);
- objcStubsAlignment = 1;
+ objcStubsFastAlignment = 1;
relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
}
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 401459a054394e..d3d9a56f85fe0e 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -823,9 +823,13 @@ static ObjCStubsMode getObjCStubsMode(const ArgList &args) {
if (!arg)
return ObjCStubsMode::fast;
- if (arg->getOption().getID() == OPT_objc_stubs_small)
- warn("-objc_stubs_small is not yet implemented, defaulting to "
- "-objc_stubs_fast");
+ if (arg->getOption().getID() == OPT_objc_stubs_small) {
+ if (is_contained({AK_arm64e, AK_arm64}, config->arch()))
+ return ObjCStubsMode::small;
+ else
+ warn("-objc_stubs_small is not yet implemented, defaulting to "
+ "-objc_stubs_fast");
+ }
return ObjCStubsMode::fast;
}
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index e123dcb6803c1e..6e9effe3d3f3f5 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -809,7 +809,9 @@ void StubHelperSection::setUp() {
ObjCStubsSection::ObjCStubsSection()
: SyntheticSection(segment_names::text, section_names::objcStubs) {
flags = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
- align = target->objcStubsAlignment;
+ align = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastAlignment
+ : target->objcStubsSmallAlignment;
}
void ObjCStubsSection::addEntry(Symbol *sym) {
@@ -817,10 +819,14 @@ void ObjCStubsSection::addEntry(Symbol *sym) {
StringRef methname = sym->getName().drop_front(symbolPrefix.size());
offsets.push_back(
in.objcMethnameSection->getStringOffset(methname).outSecOff);
+
+ auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastSize
+ : target->objcStubsSmallSize;
Defined *newSym = replaceSymbol<Defined>(
sym, sym->getName(), nullptr, isec,
- /*value=*/symbols.size() * target->objcStubsFastSize,
- /*size=*/target->objcStubsFastSize,
+ /*value=*/symbols.size() * stubSize,
+ /*size=*/stubSize,
/*isWeakDef=*/false, /*isExternal=*/true, /*isPrivateExtern=*/true,
/*includeInSymtab=*/true, /*isReferencedDynamically=*/false,
/*noDeadStrip=*/false);
@@ -828,12 +834,25 @@ void ObjCStubsSection::addEntry(Symbol *sym) {
}
void ObjCStubsSection::setUp() {
- Symbol *objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr,
- /*isWeakRef=*/false);
+ objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr,
+ /*isWeakRef=*/false);
+ if (auto *undefined = dyn_cast<Undefined>(objcMsgSend))
+ treatUndefinedSymbol(*undefined,
+ "lazy binding (normally in libobjc.dylib)");
objcMsgSend->used = true;
- in.got->addEntry(objcMsgSend);
- assert(objcMsgSend->isInGot());
- objcMsgSendGotIndex = objcMsgSend->gotIndex;
+ if (config->objcStubsMode == ObjCStubsMode::fast) {
+ in.got->addEntry(objcMsgSend);
+ assert(objcMsgSend->isInGot());
+ } else {
+ assert(config->objcStubsMode == ObjCStubsMode::small);
+ // In line with ld64's behavior, when objc_msgSend is a direct symbol,
+ // we directly reference it.
+ // In other cases, typically when binding in libobjc.dylib,
+ // we generate a stub to invoke objc_msgSend.
+ auto *d = dyn_cast<Defined>(objcMsgSend);
+ if (!d)
+ in.stubs->addEntry(objcMsgSend);
+ }
size_t size = offsets.size() * target->wordSize;
uint8_t *selrefsData = bAlloc().Allocate<uint8_t>(size);
@@ -863,7 +882,10 @@ void ObjCStubsSection::setUp() {
}
uint64_t ObjCStubsSection::getSize() const {
- return target->objcStubsFastSize * symbols.size();
+ auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastSize
+ : target->objcStubsSmallSize;
+ return stubSize * symbols.size();
}
void ObjCStubsSection::writeTo(uint8_t *buf) const {
@@ -871,12 +893,31 @@ void ObjCStubsSection::writeTo(uint8_t *buf) const {
assert(in.objcSelrefs->isFinal);
uint64_t stubOffset = 0;
+ uint64_t objcMsgSendAddr;
+ uint64_t objcStubSize;
+ uint64_t objcMsgSendIndex;
+ if (config->objcStubsMode == ObjCStubsMode::fast) {
+ objcStubSize = target->objcStubsFastSize;
+ objcMsgSendAddr = in.got->addr;
+ objcMsgSendIndex = objcMsgSend->gotIndex;
+ } else {
+ assert(config->objcStubsMode == ObjCStubsMode::small);
+ objcStubSize = target->objcStubsSmallSize;
+ if (auto *d = dyn_cast<Defined>(objcMsgSend)) {
+ objcMsgSendAddr = d->getVA();
+ objcMsgSendIndex = 0;
+ } else {
+ objcMsgSendAddr = in.stubs->addr;
+ objcMsgSendIndex = objcMsgSend->stubsIndex;
+ }
+ }
+
for (size_t i = 0, n = symbols.size(); i < n; ++i) {
Defined *sym = symbols[i];
target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr,
stubOffset, in.objcSelrefs->getVA(), i,
- in.got->addr, objcMsgSendGotIndex);
- stubOffset += target->objcStubsFastSize;
+ objcMsgSendAddr, objcMsgSendIndex);
+ stubOffset += objcStubSize;
}
}
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index e9d564f3c83615..5fb7b6e09e8e63 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -336,7 +336,7 @@ class ObjCStubsSection final : public SyntheticSection {
private:
std::vector<Defined *> symbols;
std::vector<uint32_t> offsets;
- int objcMsgSendGotIndex = 0;
+ Symbol *objcMsgSend = nullptr;
};
// Note that this section may also be targeted by non-lazy bindings. In
diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h
index bc7e09d394d24f..e55e28082c4fea 100644
--- a/lld/MachO/Target.h
+++ b/lld/MachO/Target.h
@@ -121,7 +121,9 @@ class TargetInfo {
size_t stubHelperHeaderSize;
size_t stubHelperEntrySize;
size_t objcStubsFastSize;
- size_t objcStubsAlignment;
+ size_t objcStubsSmallSize;
+ size_t objcStubsFastAlignment;
+ size_t objcStubsSmallAlignment;
uint8_t p2WordSize;
size_t wordSize;
diff --git a/lld/test/MachO/arm64-objc-stubs-dyn.s b/lld/test/MachO/arm64-objc-stubs-dyn.s
new file mode 100644
index 00000000000000..9358fc5b31c2ba
--- /dev/null
+++ b/lld/test/MachO/arm64-objc-stubs-dyn.s
@@ -0,0 +1,76 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -dead_strip
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -objc_stubs_fast
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -objc_stubs_small
+# RUN: llvm-otool -vs __TEXT __stubs %t.out | FileCheck %s --check-prefix=STUB
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s --check-prefix=SMALL
+
+# Unlike arm64-objc-stubs.s, in this test, _objc_msgSend is not defined,
+# which usually binds with libobjc.dylib.
+# 1. -objc_stubs_fast: No change as it uses GOT.
+# 2. -objc_stubs_small: Create a (shared) stub to invoke _objc_msgSend, to minimize the size.
+
+# CHECK: Contents of (__TEXT,__objc_stubs) section
+
+# CHECK-NEXT: _objc_msgSend$foo:
+# CHECK-NEXT: adrp x1, 8 ; 0x100008000
+# CHECK-NEXT: ldr x1, [x1, #0x10]
+# CHECK-NEXT: adrp x16, 4 ; 0x100004000
+# CHECK-NEXT: ldr x16, [x16]
+# CHECK-NEXT: br x16
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+
+# CHECK-NEXT: _objc_msgSend$length:
+# CHECK-NEXT: adrp x1, 8 ; 0x100008000
+# CHECK-NEXT: ldr x1, [x1, #0x18]
+# CHECK-NEXT: adrp x16, 4 ; 0x100004000
+# CHECK-NEXT: ldr x16, [x16]
+# CHECK-NEXT: br x16
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+
+# CHECK-EMPTY:
+
+# STUB: Contents of (__TEXT,__stubs) section
+# STUB-NEXT: adrp x16, 8 ; 0x100008000
+# STUB-NEXT: ldr x16, [x16]
+# STUB-NEXT: br x16
+
+# SMALL: Contents of (__TEXT,__objc_stubs) section
+# SMALL-NEXT: _objc_msgSend$foo:
+# SMALL-NEXT: adrp x1, 8 ; 0x100008000
+# SMALL-NEXT: ldr x1, [x1, #0x18]
+# SMALL-NEXT: b
+# SMALL-NEXT: _objc_msgSend$length:
+# SMALL-NEXT: adrp x1, 8 ; 0x100008000
+# SMALL-NEXT: ldr x1, [x1, #0x20]
+# SMALL-NEXT: b
+
+.section __TEXT,__objc_methname,cstring_literals
+lselref1:
+ .asciz "foo"
+lselref2:
+ .asciz "bar"
+
+.section __DATA,__objc_selrefs,literal_pointers,no_dead_strip
+.p2align 3
+.quad lselref1
+.quad lselref2
+
+.text
+
+.globl _main
+_main:
+ bl _objc_msgSend$length
+ bl _objc_msgSend$foo
+ bl _objc_msgSend$foo
+ ret
diff --git a/lld/test/MachO/arm64-objc-stubs.s b/lld/test/MachO/arm64-objc-stubs.s
index feba40ac36d840..1b8ebff9243004 100644
--- a/lld/test/MachO/arm64-objc-stubs.s
+++ b/lld/test/MachO/arm64-objc-stubs.s
@@ -7,10 +7,10 @@
# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
# RUN: %lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_fast
# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
-# RUN: %no-fatal-warnings-lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_small 2>&1 | FileCheck %s --check-prefix=WARNING
-# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
-
-# WARNING: warning: -objc_stubs_small is not yet implemented, defaulting to -objc_stubs_fast
+# RUN: llvm-otool -l %t.out | FileCheck %s --check-prefix=FASTALIGN
+# RUN: %lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_small
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s --check-prefix=SMALL
+# RUN: llvm-otool -l %t.out | FileCheck %s --check-prefix=SMALLALIGN
# CHECK: Contents of (__TEXT,__objc_stubs) section
@@ -36,6 +36,30 @@
# CHECK-EMPTY:
+# FASTALIGN: sectname __objc_stubs
+# FASTALIGN-NEXT: segname __TEXT
+# FASTALIGN-NEXT: addr
+# FASTALIGN-NEXT: size
+# FASTALIGN-NEXT: offset
+# FASTALIGN-NEXT: align 2^5 (32)
+
+# SMALL: _objc_msgSend$foo:
+# SMALL-NEXT: adrp x1, 4 ; 0x100004000
+# SMALL-NEXT: ldr x1, [x1, #0x10]
+# SMALL-NEXT: b
+
+# SMALL-NEXT: _objc_msgSend$length:
+# SMALL-NEXT: adrp x1, 4 ; 0x100004000
+# SMALL-NEXT: ldr x1, [x1, #0x18]
+# SMALL-NEXT: b
+
+# SMALLALIGN: sectname __objc_stubs
+# SMALLALIGN-NEXT: segname __TEXT
+# SMALLALIGN-NEXT: addr
+# SMALLALIGN-NEXT: size
+# SMALLALIGN-NEXT: offset
+# SMALLALIGN-NEXT: align 2^2 (4)
+
.section __TEXT,__objc_methname,cstring_literals
lselref1:
.asciz "foo"
|
@llvm/pr-subscribers-lld-macho Author: Kyungwoo Lee (kyulee-com) ChangesThis patch implements
Full diff: https://github.com/llvm/llvm-project/pull/78665.diff 9 Files Affected:
diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp
index e3781763c6102b..7b18292da6d8c0 100644
--- a/lld/MachO/Arch/ARM64.cpp
+++ b/lld/MachO/Arch/ARM64.cpp
@@ -117,13 +117,24 @@ static constexpr uint32_t objcStubsFastCode[] = {
0xd4200020, // brk #0x1
};
+static constexpr uint32_t objcStubsSmallCode[] = {
+ 0x90000001, // adrp x1, __objc_selrefs@page
+ 0xf9400021, // ldr x1, [x1, @selector("foo")@pageoff]
+ 0x14000000, // b _objc_msgSend
+};
+
void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
uint64_t stubOffset, uint64_t selrefsVA,
- uint64_t selectorIndex, uint64_t gotAddr,
+ uint64_t selectorIndex, uint64_t msgSendAddr,
uint64_t msgSendIndex) const {
- ::writeObjCMsgSendStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
- stubOffset, selrefsVA, selectorIndex, gotAddr,
- msgSendIndex);
+ if (config->objcStubsMode == ObjCStubsMode::fast)
+ ::writeObjCMsgSendFastStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
+ stubOffset, selrefsVA, selectorIndex,
+ msgSendAddr, msgSendIndex);
+ else
+ ::writeObjCMsgSendSmallStub<LP64>(buf, objcStubsSmallCode, sym, stubsAddr,
+ stubOffset, selrefsVA, selectorIndex,
+ msgSendAddr, msgSendIndex);
}
// A thunk is the relaxed variation of stubCode. We don't need the
@@ -157,7 +168,9 @@ ARM64::ARM64() : ARM64Common(LP64()) {
thunkSize = sizeof(thunkCode);
objcStubsFastSize = sizeof(objcStubsFastCode);
- objcStubsAlignment = 32;
+ objcStubsFastAlignment = 32;
+ objcStubsSmallSize = sizeof(objcStubsSmallCode);
+ objcStubsSmallAlignment = 4;
// Branch immediate is two's complement 26 bits, which is implicitly
// multiplied by 4 (since all functions are 4-aligned: The branch range
diff --git a/lld/MachO/Arch/ARM64Common.h b/lld/MachO/Arch/ARM64Common.h
index 9cfccb6cec7614..b038b6200f4d57 100644
--- a/lld/MachO/Arch/ARM64Common.h
+++ b/lld/MachO/Arch/ARM64Common.h
@@ -154,10 +154,10 @@ inline void writeStubHelperEntry(uint8_t *buf8,
template <class LP>
inline void
-writeObjCMsgSendStub(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) {
+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) {
SymbolDiagnostic d = {sym, sym->getName()};
auto *buf32 = reinterpret_cast<uint32_t *>(buf);
@@ -180,6 +180,30 @@ writeObjCMsgSendStub(uint8_t *buf, const uint32_t objcStubsFastCode[8],
buf32[7] = objcStubsFastCode[7];
}
+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) {
+ 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);
+ uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize;
+ uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t);
+ encodeBranch26(&buf32[2], {nullptr, "objc_msgSend stub"},
+ objcStubsSmallCode[2], msgSendStubVA - pcVA);
+}
+
} // namespace lld::macho
#endif
diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp
index a0d4e1a28a14a2..55fbdefb75db28 100644
--- a/lld/MachO/Arch/X86_64.cpp
+++ b/lld/MachO/Arch/X86_64.cpp
@@ -214,7 +214,7 @@ X86_64::X86_64() : TargetInfo(LP64()) {
stubHelperEntrySize = sizeof(stubHelperEntry);
objcStubsFastSize = sizeof(objcStubsFastCode);
- objcStubsAlignment = 1;
+ objcStubsFastAlignment = 1;
relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
}
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 401459a054394e..d3d9a56f85fe0e 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -823,9 +823,13 @@ static ObjCStubsMode getObjCStubsMode(const ArgList &args) {
if (!arg)
return ObjCStubsMode::fast;
- if (arg->getOption().getID() == OPT_objc_stubs_small)
- warn("-objc_stubs_small is not yet implemented, defaulting to "
- "-objc_stubs_fast");
+ if (arg->getOption().getID() == OPT_objc_stubs_small) {
+ if (is_contained({AK_arm64e, AK_arm64}, config->arch()))
+ return ObjCStubsMode::small;
+ else
+ warn("-objc_stubs_small is not yet implemented, defaulting to "
+ "-objc_stubs_fast");
+ }
return ObjCStubsMode::fast;
}
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index e123dcb6803c1e..6e9effe3d3f3f5 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -809,7 +809,9 @@ void StubHelperSection::setUp() {
ObjCStubsSection::ObjCStubsSection()
: SyntheticSection(segment_names::text, section_names::objcStubs) {
flags = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
- align = target->objcStubsAlignment;
+ align = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastAlignment
+ : target->objcStubsSmallAlignment;
}
void ObjCStubsSection::addEntry(Symbol *sym) {
@@ -817,10 +819,14 @@ void ObjCStubsSection::addEntry(Symbol *sym) {
StringRef methname = sym->getName().drop_front(symbolPrefix.size());
offsets.push_back(
in.objcMethnameSection->getStringOffset(methname).outSecOff);
+
+ auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastSize
+ : target->objcStubsSmallSize;
Defined *newSym = replaceSymbol<Defined>(
sym, sym->getName(), nullptr, isec,
- /*value=*/symbols.size() * target->objcStubsFastSize,
- /*size=*/target->objcStubsFastSize,
+ /*value=*/symbols.size() * stubSize,
+ /*size=*/stubSize,
/*isWeakDef=*/false, /*isExternal=*/true, /*isPrivateExtern=*/true,
/*includeInSymtab=*/true, /*isReferencedDynamically=*/false,
/*noDeadStrip=*/false);
@@ -828,12 +834,25 @@ void ObjCStubsSection::addEntry(Symbol *sym) {
}
void ObjCStubsSection::setUp() {
- Symbol *objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr,
- /*isWeakRef=*/false);
+ objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr,
+ /*isWeakRef=*/false);
+ if (auto *undefined = dyn_cast<Undefined>(objcMsgSend))
+ treatUndefinedSymbol(*undefined,
+ "lazy binding (normally in libobjc.dylib)");
objcMsgSend->used = true;
- in.got->addEntry(objcMsgSend);
- assert(objcMsgSend->isInGot());
- objcMsgSendGotIndex = objcMsgSend->gotIndex;
+ if (config->objcStubsMode == ObjCStubsMode::fast) {
+ in.got->addEntry(objcMsgSend);
+ assert(objcMsgSend->isInGot());
+ } else {
+ assert(config->objcStubsMode == ObjCStubsMode::small);
+ // In line with ld64's behavior, when objc_msgSend is a direct symbol,
+ // we directly reference it.
+ // In other cases, typically when binding in libobjc.dylib,
+ // we generate a stub to invoke objc_msgSend.
+ auto *d = dyn_cast<Defined>(objcMsgSend);
+ if (!d)
+ in.stubs->addEntry(objcMsgSend);
+ }
size_t size = offsets.size() * target->wordSize;
uint8_t *selrefsData = bAlloc().Allocate<uint8_t>(size);
@@ -863,7 +882,10 @@ void ObjCStubsSection::setUp() {
}
uint64_t ObjCStubsSection::getSize() const {
- return target->objcStubsFastSize * symbols.size();
+ auto stubSize = config->objcStubsMode == ObjCStubsMode::fast
+ ? target->objcStubsFastSize
+ : target->objcStubsSmallSize;
+ return stubSize * symbols.size();
}
void ObjCStubsSection::writeTo(uint8_t *buf) const {
@@ -871,12 +893,31 @@ void ObjCStubsSection::writeTo(uint8_t *buf) const {
assert(in.objcSelrefs->isFinal);
uint64_t stubOffset = 0;
+ uint64_t objcMsgSendAddr;
+ uint64_t objcStubSize;
+ uint64_t objcMsgSendIndex;
+ if (config->objcStubsMode == ObjCStubsMode::fast) {
+ objcStubSize = target->objcStubsFastSize;
+ objcMsgSendAddr = in.got->addr;
+ objcMsgSendIndex = objcMsgSend->gotIndex;
+ } else {
+ assert(config->objcStubsMode == ObjCStubsMode::small);
+ objcStubSize = target->objcStubsSmallSize;
+ if (auto *d = dyn_cast<Defined>(objcMsgSend)) {
+ objcMsgSendAddr = d->getVA();
+ objcMsgSendIndex = 0;
+ } else {
+ objcMsgSendAddr = in.stubs->addr;
+ objcMsgSendIndex = objcMsgSend->stubsIndex;
+ }
+ }
+
for (size_t i = 0, n = symbols.size(); i < n; ++i) {
Defined *sym = symbols[i];
target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr,
stubOffset, in.objcSelrefs->getVA(), i,
- in.got->addr, objcMsgSendGotIndex);
- stubOffset += target->objcStubsFastSize;
+ objcMsgSendAddr, objcMsgSendIndex);
+ stubOffset += objcStubSize;
}
}
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index e9d564f3c83615..5fb7b6e09e8e63 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -336,7 +336,7 @@ class ObjCStubsSection final : public SyntheticSection {
private:
std::vector<Defined *> symbols;
std::vector<uint32_t> offsets;
- int objcMsgSendGotIndex = 0;
+ Symbol *objcMsgSend = nullptr;
};
// Note that this section may also be targeted by non-lazy bindings. In
diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h
index bc7e09d394d24f..e55e28082c4fea 100644
--- a/lld/MachO/Target.h
+++ b/lld/MachO/Target.h
@@ -121,7 +121,9 @@ class TargetInfo {
size_t stubHelperHeaderSize;
size_t stubHelperEntrySize;
size_t objcStubsFastSize;
- size_t objcStubsAlignment;
+ size_t objcStubsSmallSize;
+ size_t objcStubsFastAlignment;
+ size_t objcStubsSmallAlignment;
uint8_t p2WordSize;
size_t wordSize;
diff --git a/lld/test/MachO/arm64-objc-stubs-dyn.s b/lld/test/MachO/arm64-objc-stubs-dyn.s
new file mode 100644
index 00000000000000..9358fc5b31c2ba
--- /dev/null
+++ b/lld/test/MachO/arm64-objc-stubs-dyn.s
@@ -0,0 +1,76 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -dead_strip
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -objc_stubs_fast
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
+# RUN: %lld -arch arm64 -lSystem -U _objc_msgSend -o %t.out %t.o -objc_stubs_small
+# RUN: llvm-otool -vs __TEXT __stubs %t.out | FileCheck %s --check-prefix=STUB
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s --check-prefix=SMALL
+
+# Unlike arm64-objc-stubs.s, in this test, _objc_msgSend is not defined,
+# which usually binds with libobjc.dylib.
+# 1. -objc_stubs_fast: No change as it uses GOT.
+# 2. -objc_stubs_small: Create a (shared) stub to invoke _objc_msgSend, to minimize the size.
+
+# CHECK: Contents of (__TEXT,__objc_stubs) section
+
+# CHECK-NEXT: _objc_msgSend$foo:
+# CHECK-NEXT: adrp x1, 8 ; 0x100008000
+# CHECK-NEXT: ldr x1, [x1, #0x10]
+# CHECK-NEXT: adrp x16, 4 ; 0x100004000
+# CHECK-NEXT: ldr x16, [x16]
+# CHECK-NEXT: br x16
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+
+# CHECK-NEXT: _objc_msgSend$length:
+# CHECK-NEXT: adrp x1, 8 ; 0x100008000
+# CHECK-NEXT: ldr x1, [x1, #0x18]
+# CHECK-NEXT: adrp x16, 4 ; 0x100004000
+# CHECK-NEXT: ldr x16, [x16]
+# CHECK-NEXT: br x16
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+# CHECK-NEXT: brk #0x1
+
+# CHECK-EMPTY:
+
+# STUB: Contents of (__TEXT,__stubs) section
+# STUB-NEXT: adrp x16, 8 ; 0x100008000
+# STUB-NEXT: ldr x16, [x16]
+# STUB-NEXT: br x16
+
+# SMALL: Contents of (__TEXT,__objc_stubs) section
+# SMALL-NEXT: _objc_msgSend$foo:
+# SMALL-NEXT: adrp x1, 8 ; 0x100008000
+# SMALL-NEXT: ldr x1, [x1, #0x18]
+# SMALL-NEXT: b
+# SMALL-NEXT: _objc_msgSend$length:
+# SMALL-NEXT: adrp x1, 8 ; 0x100008000
+# SMALL-NEXT: ldr x1, [x1, #0x20]
+# SMALL-NEXT: b
+
+.section __TEXT,__objc_methname,cstring_literals
+lselref1:
+ .asciz "foo"
+lselref2:
+ .asciz "bar"
+
+.section __DATA,__objc_selrefs,literal_pointers,no_dead_strip
+.p2align 3
+.quad lselref1
+.quad lselref2
+
+.text
+
+.globl _main
+_main:
+ bl _objc_msgSend$length
+ bl _objc_msgSend$foo
+ bl _objc_msgSend$foo
+ ret
diff --git a/lld/test/MachO/arm64-objc-stubs.s b/lld/test/MachO/arm64-objc-stubs.s
index feba40ac36d840..1b8ebff9243004 100644
--- a/lld/test/MachO/arm64-objc-stubs.s
+++ b/lld/test/MachO/arm64-objc-stubs.s
@@ -7,10 +7,10 @@
# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
# RUN: %lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_fast
# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
-# RUN: %no-fatal-warnings-lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_small 2>&1 | FileCheck %s --check-prefix=WARNING
-# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s
-
-# WARNING: warning: -objc_stubs_small is not yet implemented, defaulting to -objc_stubs_fast
+# RUN: llvm-otool -l %t.out | FileCheck %s --check-prefix=FASTALIGN
+# RUN: %lld -arch arm64 -lSystem -o %t.out %t.o -objc_stubs_small
+# RUN: llvm-otool -vs __TEXT __objc_stubs %t.out | FileCheck %s --check-prefix=SMALL
+# RUN: llvm-otool -l %t.out | FileCheck %s --check-prefix=SMALLALIGN
# CHECK: Contents of (__TEXT,__objc_stubs) section
@@ -36,6 +36,30 @@
# CHECK-EMPTY:
+# FASTALIGN: sectname __objc_stubs
+# FASTALIGN-NEXT: segname __TEXT
+# FASTALIGN-NEXT: addr
+# FASTALIGN-NEXT: size
+# FASTALIGN-NEXT: offset
+# FASTALIGN-NEXT: align 2^5 (32)
+
+# SMALL: _objc_msgSend$foo:
+# SMALL-NEXT: adrp x1, 4 ; 0x100004000
+# SMALL-NEXT: ldr x1, [x1, #0x10]
+# SMALL-NEXT: b
+
+# SMALL-NEXT: _objc_msgSend$length:
+# SMALL-NEXT: adrp x1, 4 ; 0x100004000
+# SMALL-NEXT: ldr x1, [x1, #0x18]
+# SMALL-NEXT: b
+
+# SMALLALIGN: sectname __objc_stubs
+# SMALLALIGN-NEXT: segname __TEXT
+# SMALLALIGN-NEXT: addr
+# SMALLALIGN-NEXT: size
+# SMALLALIGN-NEXT: offset
+# SMALLALIGN-NEXT: align 2^2 (4)
+
.section __TEXT,__objc_methname,cstring_literals
lselref1:
.asciz "foo"
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
This patch implements
-objc_stubs_small
targeting arm64, aiming to align with ld64's behavior.-objc_stubs_fast
: As previously implemented, this always uses the Global Offset Table (GOT) to invokeobjc_msgSend
. The alignment of the objc stub is 32 bytes.-objc_stubs_small
: This behavior depends on whetherobjc_msgSend
is defined. If it is, it directly jumps toobjc_msgSend
. If not, it creates another stub to indirectly jump toobjc_msgSend
, minimizing the size. The alignment of the objc stub in this case is 4 bytes.