]> git.saurik.com Git - apple/dyld.git/blob - testing/test-cases/dyld_process_info_notify.dtest/main.c
dyld-433.5.tar.gz
[apple/dyld.git] / testing / test-cases / dyld_process_info_notify.dtest / main.c
1
2 // BUILD: $CC target.c -o $BUILD_DIR/target.exe
3 // BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib
4 // BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe
5 // BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
6
7 // RUN_TIMEOUT: 2400
8 // RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <dlfcn.h>
13 #include <unistd.h>
14 #include <signal.h>
15 #include <spawn.h>
16 #include <errno.h>
17 #include <mach/mach.h>
18 #include <mach/machine.h>
19 #include <mach-o/dyld_process_info.h>
20 #include <dispatch/dispatch.h>
21
22
23 extern char** environ;
24
25 #if __x86_64__
26 cpu_type_t otherArch[] = { CPU_TYPE_I386 };
27 #elif __i386__
28 cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
29 #elif __arm64__
30 cpu_type_t otherArch[] = { CPU_TYPE_ARM };
31 #elif __arm__
32 cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
33 #endif
34
35 static task_t launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
36 {
37 //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
38 posix_spawnattr_t attr;
39 if ( posix_spawnattr_init(&attr) != 0 ) {
40 printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
41 exit(0);
42 }
43 if ( launchSuspended ) {
44 if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
45 printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n");
46 exit(0);
47 }
48 }
49 if ( launchOtherArch ) {
50 size_t copied;
51 if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
52 printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n");
53 exit(0);
54 }
55 }
56
57 pid_t childPid;
58 const char* argv[] = { testProgPath, arg1, NULL };
59 int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
60 if ( psResult != 0 ) {
61 printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
62 exit(0);
63 }
64 //printf("child pid=%d\n", childPid);
65
66 task_t childTask = 0;
67 if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) {
68 printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
69 kill(childPid, SIGKILL);
70 exit(0);
71 }
72
73 return childTask;
74 }
75
76 static void wait_util_task_suspended(task_t task)
77 {
78 struct task_basic_info info;
79 do {
80 unsigned count = TASK_BASIC_INFO_COUNT;
81 kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
82 //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count);
83 sleep(1);
84 } while ( info.suspend_count == 0 );
85 }
86
87
88 static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
89 {
90 kern_return_t kr;
91 __block bool sawMainExecutable = false;
92 __block bool sawlibSystem = false;
93 __block bool gotTerminationNotice = false;
94 __block bool gotEarlyNotice = false;
95 __block bool gotMainNotice = false;
96 __block bool gotMainNoticeBeforeAllInitialDylibs = false;
97 __block bool gotFooNoticeBeforeMain = false;
98
99 __block int libFooLoadCount = 0;
100 __block int libFooUnloadCount = 0;
101 dispatch_semaphore_t taskDone = dispatch_semaphore_create(0);
102
103 dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
104
105 unsigned count = 0;
106 dyld_process_info_notify handle;
107 do {
108 handle = _dyld_process_info_notify(task, serviceQueue,
109 ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
110 if ( strstr(path, "/target.exe") != NULL )
111 sawMainExecutable = true;
112 if ( strstr(path, "/libSystem") != NULL )
113 sawlibSystem = true;
114 if ( strstr(path, "/libfoo.dylib") != NULL ) {
115 if ( !gotMainNotice )
116 gotFooNoticeBeforeMain = true;
117 if ( unload )
118 ++libFooUnloadCount;
119 else
120 ++libFooLoadCount;
121 if ( disconnectEarly ) {
122 gotEarlyNotice = true;
123 dispatch_semaphore_signal(taskDone);
124 }
125 }
126 //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
127 // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
128 // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
129 },
130 ^{
131 //fprintf(stderr, "target exited\n");
132 gotTerminationNotice = true;
133 dispatch_semaphore_signal(taskDone);
134 },
135 &kr);
136 ++count;
137 if ( handle == NULL )
138 fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count);
139 } while ( (handle == NULL) && (count < 5) );
140
141 if ( handle == NULL ) {
142 return false;
143 }
144
145 // register for notification that it is entrying main()
146 _dyld_process_info_notify_main(handle, ^{
147 //fprintf(stderr, "target entering main()\n");
148 gotMainNotice = true;
149 if ( !sawMainExecutable || !sawlibSystem )
150 gotMainNoticeBeforeAllInitialDylibs = true;
151 });
152
153 // if process suspends itself, wait until it has done so
154 if ( attachLate )
155 wait_util_task_suspended(task);
156
157 // resume from initial suspend
158 task_resume(task);
159
160 // block waiting for notification that target has exited
161 bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
162 _dyld_process_info_notify_release(handle);
163
164
165 if ( !gotSignal ) {
166 fprintf(stderr, "did not get exit signal\n");
167 return false;
168 }
169
170 if ( !attachLate && !sawMainExecutable ) {
171 fprintf(stderr, "did not get load notification of main executable\n");
172 return false;
173 }
174
175 if ( !gotMainNotice ) {
176 fprintf(stderr, "did not get notification of main()\n");
177 return false;
178 }
179
180 if ( gotMainNoticeBeforeAllInitialDylibs ) {
181 fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
182 return false;
183 }
184
185 if ( gotFooNoticeBeforeMain ) {
186 fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
187 return false;
188 }
189
190 if ( !attachLate && !sawlibSystem ) {
191 fprintf(stderr, "did not get load notification of libSystem\n");
192 return false;
193 }
194
195 if ( disconnectEarly ) {
196 if ( libFooLoadCount != 1 ) {
197 fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount);
198 return false;
199 }
200 if ( libFooUnloadCount != 0 ) {
201 fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount);
202 return false;
203 }
204 }
205 else {
206 if ( libFooLoadCount != 3 ) {
207 fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount);
208 return false;
209 }
210 if ( libFooUnloadCount != 3 ) {
211 fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount);
212 return false;
213 }
214 }
215
216 return true;
217 }
218
219 static void validateMaxNotifies(task_t task)
220 {
221 dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
222 dyld_process_info_notify handles[10];
223 for (int i=0; i < 10; ++i) {
224 kern_return_t kr;
225 handles[i] = _dyld_process_info_notify(task, serviceQueue,
226 ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
227 //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
228 // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
229 // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
230 },
231 ^{
232 //fprintf(stderr, "target exited\n");
233 },
234 &kr);
235 if ( handles[i] == NULL ) {
236 if ( i == 8 ) {
237 // expected failure, because only 8 simultaneous connections allowed
238 // release one and try again
239 _dyld_process_info_notify_release(handles[4]);
240 handles[4] = NULL;
241 }
242 else {
243 fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
244 task_terminate(task);
245 exit(0);
246 }
247 }
248 }
249 // release all
250 for (int i=0; i < 10; ++i) {
251 if ( handles[i] != NULL ) {
252 _dyld_process_info_notify_release(handles[i]);
253 }
254 }
255 dispatch_release(serviceQueue);
256 }
257
258 int main(int argc, const char* argv[])
259 {
260 printf("[BEGIN] dyld_process_info_notify\n");
261 if ( argc < 2 ) {
262 printf("[FAIL] dyld_process_info_notify missing argument\n");
263 exit(0);
264 }
265 const char* testProgPath = argv[1];
266
267 dispatch_async(dispatch_get_main_queue(), ^{
268 task_t childTask;
269
270 // test 1) launch test program suspended in same arch as this program
271 childTask = launchTest(testProgPath, "", false, true);
272 if ( ! monitor(childTask, false, false) ) {
273 printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
274 task_terminate(childTask);
275 exit(0);
276 }
277 task_terminate(childTask);
278
279 // test 2) launch test program in same arch as this program where it sleeps itself
280 childTask = launchTest(testProgPath, "suspend-in-main", false, false);
281 validateMaxNotifies(childTask);
282 if ( ! monitor(childTask, false, true) ) {
283 printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
284 task_terminate(childTask);
285 exit(0);
286 }
287 task_terminate(childTask);
288
289 #if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__
290 // test 3) launch test program suspended in opposite arch as this program
291 childTask = launchTest(testProgPath, "", true, true);
292 if ( ! monitor(childTask, false, false) ) {
293 printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
294 task_terminate(childTask);
295 exit(0);
296 }
297 task_terminate(childTask);
298
299 // test 4) launch test program in opposite arch as this program where it sleeps itself
300 childTask = launchTest(testProgPath, "suspend-in-main", true, false);
301 if ( ! monitor(childTask, false, true) ) {
302 printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
303 task_terminate(childTask);
304 exit(0);
305 }
306 task_terminate(childTask);
307 #endif
308
309 // test 5) launch test program where we disconnect from it after first dlopen
310 childTask = launchTest(testProgPath, "", false, true);
311 if ( ! monitor(childTask, true, false) ) {
312 printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
313 task_terminate(childTask);
314 exit(0);
315 }
316 task_terminate(childTask);
317
318 printf("[PASS] dyld_process_info_notify\n");
319 exit(0);
320 });
321
322 dispatch_main();
323 }