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
7 // RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
16 #include <mach/mach.h>
17 #include <mach/machine.h>
18 #include <mach-o/dyld_process_info.h>
21 extern char** environ
;
24 cpu_type_t otherArch
[] = { CPU_TYPE_I386
};
26 cpu_type_t otherArch
[] = { CPU_TYPE_X86_64
};
28 cpu_type_t otherArch
[] = { CPU_TYPE_ARM
};
30 cpu_type_t otherArch
[] = { CPU_TYPE_ARM64
};
38 static struct task_and_pid
launchTest(const char* testProgPath
, bool launchOtherArch
, bool launchSuspended
)
40 posix_spawnattr_t attr
= 0;
41 if ( posix_spawnattr_init(&attr
) != 0 ) {
42 printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
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");
51 if ( launchOtherArch
) {
53 if ( posix_spawnattr_setbinpref_np(&attr
, 1, otherArch
, &copied
) != 0 ) {
54 printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
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
);
66 if (posix_spawnattr_destroy(&attr
) != 0) {
67 printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
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
);
77 // wait until process is up and has suspended itself
78 struct task_basic_info info
;
80 unsigned count
= TASK_BASIC_INFO_COUNT
;
81 kern_return_t kr
= task_info(child
.task
, TASK_BASIC_INFO
, (task_info_t
)&info
, &count
);
83 } while ( info
.suspend_count
== 0 );
88 static void killTest(struct task_and_pid tp
) {
89 int r
= kill(tp
.pid
, SIGKILL
);
90 waitpid(tp
.pid
, &r
, 0);
93 static bool alwaysGetImages(struct task_and_pid tp
, bool launchedSuspended
)
96 for (int i
=0; i
< 100; ++i
) {
98 dyld_process_info info
= _dyld_process_info_create(tp
.task
, 0, &result
);
99 //fprintf(stderr, "info=%p, result=%08X\n", info, result);
101 (void)kill(tp
.pid
, SIGCONT
);
102 if ( info
== NULL
) {
104 //fprintf(stderr, "info=%p, result=%08X\n", info, result);
108 _dyld_process_info_release(info
);
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.
118 int main(int argc
, const char* argv
[])
120 printf("[BEGIN] dyld_process_info_unload\n");
123 printf("[FAIL] dyld_process_info_unload missing argument\n");
126 const char* testProgPath
= argv
[1];
127 struct task_and_pid child
;
129 // launch test program suspended
130 child
= launchTest(testProgPath
, false, true);
131 if ( ! alwaysGetImages(child
, true) ) {
137 printf("[PASS] dyld_process_info_unload\n");