// PLFrameWalker API plframe_error_t plframe_cursor_thread_init (plframe_cursor_t *cursor, thread_t thread, plcrash_async_image_list_t *image_list) { kern_return_t kr; ucontext_t *uap; /* Note: This code has been left untouched when implementing libunwind(3) usage, as 1) Apple's implementation of libunwind on x86_64 doesn't handle floating-point and vector registers, 2) libunwind's general API doesn't provide access to some of the other information retrieved here. */ /* Perform basic initialization */ uap = &cursor->_uap_data; uap->uc_mcontext = (void *) &cursor->_mcontext_data; /* Zero the signal mask */ sigemptyset(&uap->uc_sigmask); /* Fetch the thread states */ mach_msg_type_number_t state_count; /* Sanity check */ assert(sizeof(cursor->_mcontext_data.__ss) == sizeof(x86_thread_state64_t)); assert(sizeof(cursor->_mcontext_data.__es) == sizeof(x86_exception_state64_t)); assert(sizeof(cursor->_mcontext_data.__fs) == sizeof(x86_float_state64_t)); // thread state state_count = x86_THREAD_STATE64_COUNT; kr = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t) &cursor->_mcontext_data.__ss, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 thread state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } // floating point state state_count = x86_FLOAT_STATE64_COUNT; kr = thread_get_state(thread, x86_FLOAT_STATE64, (thread_state_t) &cursor->_mcontext_data.__fs, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 float state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } // exception state state_count = x86_EXCEPTION_STATE64_COUNT; kr = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t) &cursor->_mcontext_data.__es, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 exception state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } /* Perform standard initialization and return result */ return plframe_cursor_init(cursor, uap, image_list); }
// PLFrameWalker API plframe_error_t plframe_cursor_thread_init (plframe_cursor_t *cursor, thread_t thread) { kern_return_t kr; ucontext_t *uap; /* Perform basic initialization */ uap = &cursor->_uap_data; uap->uc_mcontext = (void *) &cursor->_mcontext_data; /* Zero the signal mask */ sigemptyset(&uap->uc_sigmask); /* Fetch the thread states */ mach_msg_type_number_t state_count; /* Sanity check */ assert(sizeof(cursor->_mcontext_data.__ss) == sizeof(x86_thread_state64_t)); assert(sizeof(cursor->_mcontext_data.__es) == sizeof(x86_exception_state64_t)); assert(sizeof(cursor->_mcontext_data.__fs) == sizeof(x86_float_state64_t)); // thread state state_count = x86_THREAD_STATE64_COUNT; kr = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t) &cursor->_mcontext_data.__ss, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 thread state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } // floating point state state_count = x86_FLOAT_STATE64_COUNT; kr = thread_get_state(thread, x86_FLOAT_STATE64, (thread_state_t) &cursor->_mcontext_data.__fs, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 float state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } // exception state state_count = x86_EXCEPTION_STATE64_COUNT; kr = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t) &cursor->_mcontext_data.__es, &state_count); if (kr != KERN_SUCCESS) { PLCF_DEBUG("Fetch of x86-64 exception state failed with mach error: %d", kr); return PLFRAME_INTERNAL; } /* Perform standard initialization */ plframe_cursor_init(cursor, uap); return PLFRAME_ESUCCESS; }
plcrash_error_t unwind_current_state (plcrash_async_thread_state_t *state, void *context) { plframe_cursor_t cursor; plcrash_async_image_list_t image_list; plframe_cursor_frame_reader_t **readers = global_harness_state.test_case->frame_readers_dwarf; size_t reader_count = 0; plframe_error_t err; /* Determine the number of frame readers */ if (readers != NULL) { for (reader_count = 0; readers[reader_count] != NULL; reader_count++) { } } /* Initialize the image list */ plcrash_nasync_image_list_init(&image_list, mach_task_self()); for (uint32_t i = 0; i < _dyld_image_count(); i++) plcrash_nasync_image_list_append(&image_list, _dyld_get_image_header(i), _dyld_get_image_name(i)); /* Initialie our cursor */ plframe_cursor_init(&cursor, mach_task_self(), state, &image_list); /* Walk the frames until we hit the test function */ for (uint32_t i = 0; i < global_harness_state.test_case->intermediate_frames; i++) { if ((err = plframe_cursor_next(&cursor)) != PLFRAME_ESUCCESS) { PLCF_DEBUG("Step failed: %d", err); return PLCRASH_EINVAL; } } /* Now in test function; Unwind using the specified readers */ if (readers != NULL) { /* Issue the read */ err = plframe_cursor_next_with_readers(&cursor, global_harness_state.test_case->frame_readers_dwarf, reader_count); } else { /* Use default readers */ err = plframe_cursor_next(&cursor); } if (err != PLFRAME_ESUCCESS) { PLCF_DEBUG("Step within test function failed: %d", err); return PLFRAME_EINVAL; } /* Now in unwind_tester; verify that we unwound to the correct IP */ plcrash_greg_t ip; if (plframe_cursor_get_reg(&cursor, PLCRASH_REG_IP, &ip) != PLFRAME_ESUCCESS) { PLCF_DEBUG("Could not fetch IP from register state"); __builtin_trap(); } if (ip != (plcrash_greg_t) unwind_tester_target_ip) { PLCF_DEBUG("Incorrect IP. ip=%" PRIx64 " target_ip=%" PRIx64, (uint64_t) ip, (uint64_t) unwind_tester_target_ip); __builtin_trap(); } /* * For tests using DWARF or compact unwinding, verify that non-volatile registers have been restored. * This replaces the use of thread state restoration in the original libunwind tests; rather * than letting the unwind_tester() perform these register value tests, * we just do so ourselves */ if (!global_harness_state.test_case->restores_callee_registers) return PLCRASH_ESUCCESS; VERIFY_NV_REG(&cursor, PLCRASH_REG_SP, (plcrash_greg_t)global_harness_state.test_case->expected_sp); #ifdef __x86_64__ VERIFY_NV_REG(&cursor, PLCRASH_X86_64_RBX, 0x1234567887654321); VERIFY_NV_REG(&cursor, PLCRASH_X86_64_R12, 0x02468ACEECA86420); VERIFY_NV_REG(&cursor, PLCRASH_X86_64_R13, 0x13579BDFFDB97531); VERIFY_NV_REG(&cursor, PLCRASH_X86_64_R14, 0x1122334455667788); VERIFY_NV_REG(&cursor, PLCRASH_X86_64_R15, 0x0022446688AACCEE); #elif (__i386__) VERIFY_NV_REG(&cursor, PLCRASH_X86_EBX, 0x12344321); VERIFY_NV_REG(&cursor, PLCRASH_X86_ESI, 0x56788765); VERIFY_NV_REG(&cursor, PLCRASH_X86_EDI, 0xABCDDCBA); #elif (__arm64__) VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X19, 0x1234567887654321); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X20, 0x02468ACEECA86420); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X21, 0x13579BDFFDB97531); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X22, 0x1122334455667788); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X23, 0x0022446688AACCEE); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X24, 0x0033557799BBDDFF); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X25, 0x00446688AACCEE00); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X26, 0x006688AACCEEFF11); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X27, 0x0088AACCEEFF1133); VERIFY_NV_REG(&cursor, PLCRASH_ARM64_X28, 0xCAFEDEADF00DBEEF); #endif return PLCRASH_ESUCCESS; }