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"
10 // NO_CRASH_LOG: prog_missing_dylib.exe
11 // NO_CRASH_LOG: prog_missing_symbol.exe
13 // RUN: ./dyld_abort_tests.exe
22 #include <mach/mach.h>
23 #include <mach/machine.h>
25 #include <System/sys/reason.h>
26 #include <System/sys/proc_info.h>
27 #include <System/kern/kern_cdata.h>
29 #include <mach-o/dyld_priv.h>
31 #include "test_support.h"
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
);
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
);
48 kcdata_iter_t autopsyData
= kcdata_iter((void*)corpse_data
, corpse_size
);
49 if (!kcdata_iter_valid(autopsyData
)) {
50 FAIL("Corpse Data Invalid");
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
);
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");
61 struct exit_reason_snapshot
*ers
= (struct exit_reason_snapshot
*)kcdata_iter_payload(exitReasonData
);
63 if ( ers
->ers_namespace
!= OS_REASON_DYLD
) {
64 FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers
->ers_namespace
);
66 if ( ers
->ers_code
!= dyldReason
) {
67 FAIL("eri_code (%llu) != dyldReason (%lld)", ers
->ers_code
, dyldReason
);
69 kcdata_iter_t iter
= kcdata_iter((void*)corpse_data
, corpse_size
);
70 bool foundOSReason
= false;
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
){
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");
83 const dyld_abort_payload
* dyldInfo
= (dyld_abort_payload
*)kcdata_iter_payload(payloadIter
);
85 if ( dyldInfo
->version
!= 1 ) {
86 FAIL("dyld payload is not version 1");
89 if ( (dyldInfo
->flags
& 1) == 0 ) {
90 FAIL("dyld flags should have low bit set to indicate process terminated during launch");
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
);
100 FAIL("dylib path (%s) not provided by dyld", expectedDylibPath
);
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
);
111 FAIL("symbol (%s) not provided by dyld", expectedSymbol
);
116 if (!foundOSReason
) {
117 FAIL("Did not find KCDATA_BUFFER_BEGIN_OS_REASON");
119 oneShotSemaphoreBlock();
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
);
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");