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