dyld-551.3.tar.gz
[apple/dyld.git] / testing / test-cases / dyld_process_info_unload.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 -install_name $RUN_DIR/libfoo.dylib
4 // BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_unload.exe
5 // BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe
6
7 // RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <dlfcn.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <spawn.h>
15 #include <errno.h>
16 #include <mach/mach.h>
17 #include <mach/machine.h>
18 #include <mach-o/dyld_process_info.h>
19
20
21 extern char** environ;
22
23 #if __x86_64__
24 cpu_type_t otherArch[] = { CPU_TYPE_I386 };
25 #elif __i386__
26 cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
27 #elif __arm64__
28 cpu_type_t otherArch[] = { CPU_TYPE_ARM };
29 #elif __arm__
30 cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
31 #endif
32
33 struct task_and_pid {
34 pid_t pid;
35 task_t task;
36 };
37
38 static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
39 {
40 posix_spawnattr_t attr = 0;
41 if ( posix_spawnattr_init(&attr) != 0 ) {
42 printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
43 exit(0);
44 }
45 if ( launchSuspended ) {
46 if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
47 printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n");
48 exit(0);
49 }
50 }
51 if ( launchOtherArch ) {
52 size_t copied;
53 if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
54 printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
55 exit(0);
56 }
57 }
58
59 struct task_and_pid child;
60 const char* argv[] = { testProgPath, NULL };
61 int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
62 if ( psResult != 0 ) {
63 printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
64 exit(0);
65 }
66 if (posix_spawnattr_destroy(&attr) != 0) {
67 printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
68 exit(0);
69 }
70
71 if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
72 printf("[FAIL] dyld_process_info_unload task_for_pid()\n");
73 kill(child.pid, SIGKILL);
74 exit(0);
75 }
76
77 // wait until process is up and has suspended itself
78 struct task_basic_info info;
79 do {
80 unsigned count = TASK_BASIC_INFO_COUNT;
81 kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
82 sleep(1);
83 } while ( info.suspend_count == 0 );
84
85 return child;
86 }
87
88 static void killTest(struct task_and_pid tp) {
89 int r = kill(tp.pid, SIGKILL);
90 waitpid(tp.pid, &r, 0);
91 }
92
93 static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended)
94 {
95 int failCount = 0;
96 for (int i=0; i < 100; ++i ) {
97 kern_return_t result;
98 dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result);
99 //fprintf(stderr, "info=%p, result=%08X\n", info, result);
100 if ( i == 0 )
101 (void)kill(tp.pid, SIGCONT);
102 if ( info == NULL ) {
103 failCount++;
104 //fprintf(stderr, "info=%p, result=%08X\n", info, result);
105 }
106 else {
107 usleep(100);
108 _dyld_process_info_release(info);
109 }
110 }
111 // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
112 // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
113 // The important thing is to not crash. Getting NULL back is ok.
114 return true;
115 }
116
117
118 int main(int argc, const char* argv[])
119 {
120 printf("[BEGIN] dyld_process_info_unload\n");
121
122 if ( argc < 2 ) {
123 printf("[FAIL] dyld_process_info_unload missing argument\n");
124 exit(0);
125 }
126 const char* testProgPath = argv[1];
127 struct task_and_pid child;
128
129 // launch test program suspended
130 child = launchTest(testProgPath, false, true);
131 if ( ! alwaysGetImages(child, true) ) {
132 killTest(child);
133 exit(0);
134 }
135 killTest(child);
136
137 printf("[PASS] dyld_process_info_unload\n");
138 return 0;
139 }