dyld-625.13.tar.gz
[apple/dyld.git] / testing / test-cases / dyld_process_info.dtest / main.c
1
2 // BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
3 // BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe
4 // BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe
5
6 // RUN: $SUDO ./dyld_process_info.exe $RUN_DIR/linksWithCF.exe
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <dlfcn.h>
11 #include <unistd.h>
12 #include <signal.h>
13 #include <spawn.h>
14 #include <errno.h>
15 #include <sys/uio.h>
16 #include <sys/wait.h>
17 #include <sys/types.h>
18 #include <mach/mach.h>
19 #include <mach/machine.h>
20 #include <mach-o/dyld_process_info.h>
21 #include <Availability.h>
22
23
24 extern char** environ;
25
26 #if __x86_64__
27 cpu_type_t otherArch[] = { CPU_TYPE_I386 };
28 #elif __i386__
29 cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
30 #elif __arm64__
31 cpu_type_t otherArch[] = { CPU_TYPE_ARM };
32 #elif __arm__
33 cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
34 #endif
35
36 struct task_and_pid {
37 pid_t pid;
38 task_t task;
39 };
40
41 static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
42 {
43 posix_spawnattr_t attr = 0;
44 if ( posix_spawnattr_init(&attr) != 0 ) {
45 printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
46 exit(0);
47 }
48 if ( launchSuspended ) {
49 if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
50 printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n");
51 exit(0);
52 }
53 }
54 if ( launchOtherArch ) {
55 size_t copied;
56 if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
57 printf("[FAIL] dyld_process_info posix_spawnattr_setbinpref_np()\n");
58 exit(0);
59 }
60 }
61
62 struct task_and_pid child = {0, 0};
63 const char* argv[] = { testProgPath, NULL };
64 int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
65 if ( psResult != 0 ) {
66 printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
67 exit(0);
68 }
69 if (posix_spawnattr_destroy(&attr) != 0) {
70 printf("[FAIL] dyld_process_info posix_spawnattr_destroy()\n");
71 exit(0);
72 }
73
74 if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
75 printf("[FAIL] dyld_process_info task_for_pid()\n");
76 kill(child.pid, SIGKILL);
77 exit(0);
78 }
79
80 #if __x86_64__
81 //printf("child pid=%d task=%d (%s, %s)\n", child.pid, child.task, launchOtherArch ? "i386" : "x86_64", launchSuspended ? "suspended" : "active");
82 #endif
83
84 // wait until process is up and has suspended itself
85 struct task_basic_info info;
86 do {
87 unsigned count = TASK_BASIC_INFO_COUNT;
88 kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
89 sleep(1);
90 } while ( info.suspend_count == 0 );
91
92 return child;
93 }
94
95 static void killTest(struct task_and_pid tp) {
96 int r = kill(tp.pid, SIGKILL);
97 waitpid(tp.pid, &r, 0);
98 }
99
100 static bool hasCF(task_t task, bool launchedSuspended)
101 {
102 kern_return_t result;
103 dyld_process_info info = _dyld_process_info_create(task, 0, &result);
104 if ( info == NULL ) {
105 printf("[FAIL] dyld_process_info _dyld_process_info_create(), kern_return_t=%d\n", result);
106 return false;
107 }
108
109 dyld_process_state_info stateInfo;
110 _dyld_process_info_get_state(info, &stateInfo);
111 bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started);
112 if ( valueSaysLaunchedSuspended != launchedSuspended ) {
113 printf("[FAIL] dyld_process_info suspend state mismatch\n");
114 _dyld_process_info_release(info);
115 return false;
116 }
117
118 __block bool foundDyld = false;
119 _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
120 //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
121 if ( strstr(path, "/dyld") != NULL )
122 foundDyld = true;
123 });
124
125 if ( launchedSuspended ) {
126 // fprintf(stderr, "launched suspended image list:\n");
127 __block bool foundMain = false;
128 _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
129 //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
130 if ( strstr(path, "/linksWithCF.exe") != NULL )
131 foundMain = true;
132 });
133 _dyld_process_info_release(info);
134 return foundMain && foundDyld;
135 }
136
137 __block bool foundCF = false;
138 _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
139 //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
140 if ( strstr(path, "/CoreFoundation.framework/") != NULL )
141 foundCF = true;
142 });
143
144 _dyld_process_info_release(info);
145
146 return foundCF && foundDyld;
147 }
148
149 static void checkForLeaks(const char *name) {
150 printf("[BEGIN] %s checkForLeaks\n", name);
151 pid_t child;
152 int stat_loc;
153 char buffer[PAGE_SIZE];
154 (void)snprintf(&buffer[0], 128, "%d", getpid());
155
156 const char* argv[] = { "/usr/bin/leaks", buffer, NULL };
157 int psResult = posix_spawn(&child, "/usr/bin/leaks", NULL, NULL, (char**)argv, environ);
158 if ( psResult != 0 ) {
159 printf("[FAIL] %s checkForLeaks posix_spawn failed, err=%d\n", name, psResult);
160 exit(0);
161 }
162
163 (void)wait4(child, &stat_loc, 0, NULL);
164 ssize_t readBytes = 0;
165 if (WIFEXITED(stat_loc) == 0) {
166 printf("[FAIL] %s checkForLeaks leaks did not exit\n", name);
167 exit(0);
168 }
169 if (WEXITSTATUS(stat_loc) == 1) {
170 printf("[FAIL] %s checkForLeaks found leaks\n", name);
171 exit(0);
172 }
173 if (WEXITSTATUS(stat_loc) != 0) {
174 printf("[FAIL] %s checkForLeaks leaks errored out\n", name);
175 exit(0);
176 }
177 printf("[PASS] %s checkForLeaks\n", name);
178 }
179
180
181 int main(int argc, const char* argv[])
182 {
183 kern_return_t kr = KERN_SUCCESS;
184 printf("[BEGIN] dyld_process_info\n");
185
186 if ( argc < 2 ) {
187 printf("[FAIL] dyld_process_info missing argument\n");
188 exit(0);
189 }
190
191 const char* testProgPath = argv[1];
192 struct task_and_pid child;
193
194 // launch test program same arch as this program
195 child = launchTest(testProgPath, false, false);
196 if ( ! hasCF(child.task, false) ) {
197 printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n");
198 killTest(child);
199 exit(0);
200 }
201 killTest(child);
202
203 // launch test program suspended
204 child = launchTest(testProgPath, false, true);
205 if ( ! hasCF(child.task, true) ) {
206 printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
207 killTest(child);
208 exit(0);
209 }
210 (void)kill(child.pid, SIGCONT);
211 killTest(child);
212
213 #if __MAC_OS_X_VERSION_MIN_REQUIRED
214 // only mac supports multiple architectures, run test program as other arch too
215 child = launchTest(testProgPath, true, false);
216 if ( ! hasCF(child.task, false) ) {
217 printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n");
218 killTest(child);
219 exit(0);
220 }
221 killTest(child);
222
223 // launch test program suspended
224 child = launchTest(testProgPath, true, true);
225 if ( ! hasCF(child.task, true) ) {
226 printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
227 killTest(child);
228 exit(0);
229 }
230 (void)kill(child.pid, SIGCONT);
231 killTest(child);
232 #endif
233
234 // verify this program does not use CF
235 if ( hasCF(mach_task_self(), false) ) {
236 printf("[FAIL] dyld_process_info self links with CF and dyld\n");
237 exit(0);
238 }
239
240 printf("[PASS] dyld_process_info\n");
241 checkForLeaks("dyld_process_info");
242
243 return 0;
244 }