2 // BUILD: $CC linked1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc
3 // BUILD: $CC linked2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc
4 // BUILD: $CC main.m -o $BUILD_DIR/_dyld_for_each_objc_protocol.exe $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib -lobjc
6 // RUN: ./_dyld_for_each_objc_protocol.exe
8 // The preoptimized objc protocol information is available via _dyld_for_each_objc_protocol().
9 // This test ensures that we match the objc behaviour when there are duplicates.
10 // For objc today, it walks the images in reverse load order, so the deepest library will be
11 // the canonical definition of a protocol.
13 #include <mach-o/dyld_priv.h>
14 #include <objc/runtime.h>
20 #include "test_support.h"
22 static bool objcOptimizedByDyld() {
23 extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo");
24 return (objcInfo[1] & 0x80);
27 static bool haveDyldCache() {
28 size_t unusedCacheLen;
29 return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
32 // All the libraries have a copy of DyldProtocol
33 @protocol DyldProtocol
36 // Only the main executable has DyldMainProtocol
37 @protocol DyldMainProtocol
41 static void* useDyldProtocol() {
42 return (void*)@protocol(DyldProtocol);
46 static void* useDyldMainProtocol() {
47 return (void*)@protocol(DyldMainProtocol);
50 extern id objc_getProtocol(const char *name);
52 static bool gotDyldProtocolMain = false;
53 static bool gotDyldProtocolLinked = false;
54 static bool gotDyldProtocolLinked2 = false;
56 static bool isInImage(void* ptr, const char* name) {
58 if ( dladdr(ptr, &info) == 0 ) {
59 FAIL("dladdr(protocol, xx) failed");
62 return strstr(info.dli_fname, name) != NULL;
65 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
66 if (!objcOptimizedByDyld() || !haveDyldCache()) {
67 __block bool sawClass = false;
68 _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
72 FAIL("dyld2 shouldn't see any classes");
74 PASS("no shared cache or no dyld optimized objc");
77 // Check that DyldProtocol comes from liblinked2 as it is last in load order
78 id runtimeDyldProtocol = objc_getProtocol("DyldProtocol");
79 if (!isInImage(runtimeDyldProtocol, "liblinked2")) {
80 FAIL("DyldProtocol should have come from liblinked2");
83 // Check that DyldLinkedProtocol comes from liblinked2 as it is last in load order
84 id runtimeDyldLinkedProtocol = objc_getProtocol("DyldLinkedProtocol");
85 if (!isInImage(runtimeDyldLinkedProtocol, "liblinked2")) {
86 FAIL("DyldLinkedProtocol should have come from liblinked2");
89 // Walk all the implementations of "DyldProtocol"
90 _dyld_for_each_objc_protocol("DyldProtocol", ^(void* protocolPtr, bool isLoaded, bool* stop) {
91 // We should walk these in the order liblinked2, liblinked, main exe
92 if (!gotDyldProtocolLinked2) {
93 if (!isInImage(protocolPtr, "liblinked2")) {
94 FAIL("Optimized DyldProtocol should have come from liblinked2");
97 FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked2");
99 gotDyldProtocolLinked2 = true;
102 if (!gotDyldProtocolLinked) {
103 if (!isInImage(protocolPtr, "liblinked1")) {
104 FAIL("Optimized DyldProtocol should have come from liblinked");
107 FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked");
109 gotDyldProtocolLinked = true;
112 if (!gotDyldProtocolMain) {
113 if (!isInImage(protocolPtr, "_dyld_for_each_objc_protocol.exe")) {
114 FAIL("Optimized DyldProtocol should have come from main exe");
117 FAIL("Optimized DyldProtocol isLoaded should have been set on main exe");
119 gotDyldProtocolMain = true;
122 FAIL("Unexpected Optimized DyldProtocol");
126 // Visit again, and return liblinked2's DyldProtocol
127 __block void* DyldProtocolImpl = nil;
128 _dyld_for_each_objc_protocol("DyldProtocol", ^(void* protocolPtr, bool isLoaded, bool* stop) {
129 DyldProtocolImpl = protocolPtr;
132 if (!isInImage(DyldProtocolImpl, "liblinked2")) {
133 FAIL("_dyld_for_each_objc_protocol should have returned DyldProtocol from liblinked2");
136 // Visit DyldMainProtocol and make sure it makes the callback for just the result from main.exe
137 __block void* DyldMainProtocolImpl = nil;
138 _dyld_for_each_objc_protocol("DyldMainProtocol", ^(void* protocolPtr, bool isLoaded, bool* stop) {
139 DyldMainProtocolImpl = protocolPtr;
142 if (!isInImage(DyldMainProtocolImpl, "_dyld_for_each_objc_protocol.exe")) {
143 FAIL("_dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe");
146 #if __has_feature(ptrauth_calls)
147 // Check the ISA was signed correctly on arm64e
148 id dyldMainProtocol = @protocol(DyldMainProtocol);
149 void* originalISA = *(void **)dyldMainProtocol;
150 void* strippedISA = __builtin_ptrauth_strip(originalISA, ptrauth_key_asda);
151 uint64_t discriminator = __builtin_ptrauth_blend_discriminator((void*)dyldMainProtocol, 27361);
152 void* signedISA = __builtin_ptrauth_sign_unauthenticated((void*)strippedISA, 2, discriminator);
153 if ( originalISA != signedISA ) {
154 FAIL("_dyld_for_each_objc_protocol DyldMainProtocol ISA is not signed correctly: %p vs %p",
155 originalISA, signedISA);