dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / test-cases / dyld_abort_payload.dtest / main.cpp
1
2 // BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
3 // BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib
4 // BUILD: $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib -o $BUILD_DIR/prog_missing_dylib.exe
5 // BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
6 // BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
7 // BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
8 // BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_abort_tests.exe -DRUN_DIR="$RUN_DIR"
9
10 // NO_CRASH_LOG: prog_missing_dylib.exe
11 // NO_CRASH_LOG: prog_missing_symbol.exe
12
13 // RUN: ./dyld_abort_tests.exe
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <dlfcn.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <spawn.h>
21 #include <errno.h>
22 #include <mach/mach.h>
23 #include <mach/machine.h>
24 #include <err.h>
25 #include <System/sys/reason.h>
26 #include <System/sys/proc_info.h>
27 #include <System/kern/kern_cdata.h>
28 #include <libproc.h>
29 #include <mach-o/dyld_priv.h>
30
31 #include "test_support.h"
32
33
34 void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) {
35 dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
36 LOG("Running test for %s / %s / %s", prog, expectedDylibPath, expectedSymbol);
37 _process process;
38 process.set_executable_path(prog);
39 process.set_crash_handler(^(task_t task) {
40 LOG("Crash for task=%u", task);
41 mach_vm_address_t corpse_data;
42 mach_vm_size_t corpse_size;
43 kern_return_t kr = task_map_corpse_info_64(mach_task_self(), task, &corpse_data, &corpse_size);
44 if (kr != KERN_SUCCESS) {
45 FAIL("Could not read corpse data kr=(%d)", kr);
46 }
47
48 kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
49 if (!kcdata_iter_valid(autopsyData)) {
50 FAIL("Corpse Data Invalid");
51 }
52
53 kcdata_iter_t pidIter = kcdata_iter_find_type(autopsyData, TASK_CRASHINFO_PID);
54 pid_t pid = *(pid_t *) (kcdata_iter_payload(pidIter));
55 LOG("Crash for pid=%u", pid);
56
57 kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
58 if (!kcdata_iter_valid(exitReasonData)) {
59 FAIL("Could not find exit data");
60 }
61 struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData);
62
63 if ( ers->ers_namespace != OS_REASON_DYLD ) {
64 FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace);
65 }
66 if ( ers->ers_code != dyldReason ) {
67 FAIL("eri_code (%llu) != dyldReason (%lld)", ers->ers_code, dyldReason);
68 }
69 kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
70 bool foundOSReason = false;
71
72 KCDATA_ITER_FOREACH(iter) {
73 if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
74 kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter));
75 if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
76 return;
77 }
78 foundOSReason = true;
79 kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD);
80 if ( !kcdata_iter_valid(payloadIter) ) {
81 FAIL("invalid kcdata payload iterator from payload data");
82 }
83 const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
84
85 if ( dyldInfo->version != 1 ) {
86 FAIL("dyld payload is not version 1");
87 }
88
89 if ( (dyldInfo->flags & 1) == 0 ) {
90 FAIL("dyld flags should have low bit set to indicate process terminated during launch");
91 }
92
93 if ( expectedDylibPath != NULL ) {
94 if ( dyldInfo->targetDylibPathOffset != 0 ) {
95 const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
96 if ( strstr(targetDylib, expectedDylibPath) == NULL ) {
97 FAIL("dylib path (%s) not what expected (%s)", targetDylib, expectedDylibPath);
98 }
99 } else {
100 FAIL("dylib path (%s) not provided by dyld", expectedDylibPath);
101 }
102 }
103
104 if ( expectedSymbol != NULL ) {
105 if ( dyldInfo->targetDylibPathOffset != 0 ) {
106 const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
107 if ( strcmp(expectedSymbol, missingSymbol) != 0 ) {
108 FAIL("symbol (%s) not what expected (%s)", missingSymbol, expectedSymbol);
109 }
110 } else {
111 FAIL("symbol (%s) not provided by dyld", expectedSymbol);
112 }
113 }
114 }
115 }
116 if (!foundOSReason) {
117 FAIL("Did not find KCDATA_BUFFER_BEGIN_OS_REASON");
118 }
119 oneShotSemaphoreBlock();
120 });
121
122 pid_t pid = process.launch();
123 LOG("Launch pid: %u", pid);
124 // We need to wait for the task to crash in before we call PASS(). Ideally we would return the block and wait
125 // in main(), that way we could run multiple processes simultaneously, but when we do that there are strange crashes
126 // and deadlocks, I suspect it has something to do with block capture of parameters sometimes being references.
127 // but it is not entirely clear what the intended semantics are.
128 dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER);
129 }
130
131
132 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
133 // test launch program with missing library
134 runTest(RUN_DIR "/prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL);
135 runTest(RUN_DIR "/prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol");
136 PASS("Success");
137 }
138