in Source/PLCrashFrameStackUnwind.c [42:118]
plframe_error_t plframe_cursor_read_frame_ptr (task_t task,
plcrash_async_image_list_t *image_list,
const plframe_stackframe_t *current_frame,
const plframe_stackframe_t *previous_frame,
plframe_stackframe_t *next_frame)
{
/* Determine the appropriate type width for the target thread */
bool x64 = plcrash_async_thread_state_get_greg_size(¤t_frame->thread_state) == sizeof(uint64_t);
union {
uint64_t greg64[2];
uint32_t greg32[2];
} regs;
void *dest;
size_t len;
if (x64) {
dest = regs.greg64;
len = sizeof(regs.greg64);
} else {
dest = regs.greg32;
len = sizeof(regs.greg32);
}
/* Verify that we have a frame pointer to work with */
if (!plcrash_async_thread_state_has_reg(¤t_frame->thread_state, PLCRASH_REG_FP)) {
PLCF_DEBUG("The frame pointer is unavailable, can't read saved register.")
return PLFRAME_EBADFRAME;
}
/* Fetch the current frame's frame pointer */
plcrash_greg_t fp = plcrash_async_thread_state_get_reg(¤t_frame->thread_state, PLCRASH_REG_FP);
/* A NULL FP means a terminated frame */
if (fp == 0x0)
return PLFRAME_ENOFRAME;
/* Verify that the stack is growing in the right direction. */
if (previous_frame != NULL && plcrash_async_thread_state_has_reg(&previous_frame->thread_state, PLCRASH_REG_FP)) {
plcrash_greg_t prev_fp = plcrash_async_thread_state_get_reg(&previous_frame->thread_state, PLCRASH_REG_FP);
plcrash_async_thread_stack_direction_t stack_direction = plcrash_async_thread_state_get_stack_direction(¤t_frame->thread_state);
if ((stack_direction == PLCRASH_ASYNC_THREAD_STACK_DIRECTION_DOWN && fp < prev_fp) ||
(stack_direction == PLCRASH_ASYNC_THREAD_STACK_DIRECTION_UP && fp > prev_fp))
{
PLCF_DEBUG("Stack growing in wrong direction, terminating stack walk");
return PLFRAME_EBADFRAME;
}
}
/* Read the registers off the stack via the frame pointer */
plcrash_greg_t new_fp;
plcrash_greg_t new_pc;
plcrash_error_t err;
err = plcrash_async_task_memcpy(task, (pl_vm_address_t) fp, 0, dest, len);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read frame: %d", err);
return PLFRAME_EBADFRAME;
}
if (x64) {
new_fp = regs.greg64[0];
new_pc = regs.greg64[1];
} else {
new_fp = regs.greg32[0];
new_pc = regs.greg32[1];
}
/* Initialize the new frame, deriving state from the previous frame. */
*next_frame = *current_frame;
plcrash_async_thread_state_clear_all_regs(&next_frame->thread_state);
plcrash_async_thread_state_set_reg(&next_frame->thread_state, PLCRASH_REG_FP, new_fp);
plcrash_async_thread_state_set_reg(&next_frame->thread_state, PLCRASH_REG_IP, new_pc);
return PLFRAME_ESUCCESS;
}