in Source/PLCrashFrameCompactUnwind.c [47:146]
plframe_error_t plframe_cursor_read_compact_unwind (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)
{
plframe_error_t result;
plcrash_error_t err;
/* Fetch the IP. It should always be available */
if (!plcrash_async_thread_state_has_reg(¤t_frame->thread_state, PLCRASH_REG_IP)) {
PLCF_DEBUG("Frame is missing a valid IP register, skipping compact unwind encoding");
return PLFRAME_EBADFRAME;
}
plcrash_greg_t pc = plcrash_async_thread_state_get_reg(¤t_frame->thread_state, PLCRASH_REG_IP);
if (pc == 0) {
return PLFRAME_ENOTSUP;
}
/* Find the corresponding image */
plcrash_async_image_list_set_reading(image_list, true);
plcrash_async_image_t *image = plcrash_async_image_containing_address(image_list, (pl_vm_address_t) pc);
if (image == NULL) {
PLCF_DEBUG("Could not find a loaded image for the current frame pc: 0x%" PRIx64, (uint64_t) pc);
result = PLFRAME_ENOTSUP;
goto cleanup;
}
/* Map the unwind section */
plcrash_async_mobject_t unwind_mobj;
err = plcrash_async_macho_map_section(&image->macho_image, SEG_TEXT, "__unwind_info", &unwind_mobj);
if (err != PLCRASH_ESUCCESS) {
if (err != PLCRASH_ENOTFOUND)
PLCF_DEBUG("Could not map the compact unwind info section for image %s: %d", image->macho_image.name, err);
result = PLFRAME_ENOTSUP;
goto cleanup;
}
/* Initialize the CFE reader. */
cpu_type_t cputype = image->macho_image.byteorder->swap32(image->macho_image.header.cputype);
plcrash_async_cfe_reader_t reader;
err = plcrash_async_cfe_reader_init(&reader, &unwind_mobj, cputype);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Could not parse the compact unwind info section for image '%s': %d", image->macho_image.name, err);
result = PLFRAME_EINVAL;
goto cleanup_mobject;
}
/* Find the encoding entry (if any) and free the reader */
pl_vm_address_t function_base;
uint32_t encoding;
err = plcrash_async_cfe_reader_find_pc(&reader, (pl_vm_address_t)(pc - image->macho_image.header_addr), &function_base, &encoding);
plcrash_async_cfe_reader_free(&reader);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Did not find CFE entry for PC 0x%" PRIx64 ": %d", (uint64_t) pc, err);
result = PLFRAME_ENOTSUP;
goto cleanup_mobject;
}
/* Decode the entry */
plcrash_async_cfe_entry_t entry;
err = plcrash_async_cfe_entry_init(&entry, cputype, encoding);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Could not decode CFE encoding 0x%" PRIx32 " for PC 0x%" PRIx64 ": %d", encoding, (uint64_t) pc, err);
result = PLFRAME_ENOTSUP;
goto cleanup_mobject;
}
/* Skip entries for which no unwind information is unavailable */
if (plcrash_async_cfe_entry_type(&entry) == PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE) {
result = PLFRAME_ENOFRAME;
goto cleanup_cfe_entry;
}
/* Compute the in-core function address */
pl_vm_address_t function_address;
if (!plcrash_async_address_apply_offset(image->macho_image.header_addr, function_base, &function_address)) {
PLCF_DEBUG("The provided function base (0x%" PRIx64 ") plus header address (0x%" PRIx64 ") will overflow pl_vm_address_t",
(uint64_t) function_base, (uint64_t) image->macho_image.header_addr);
result = PLFRAME_EINVAL;
goto cleanup_cfe_entry;
}
/* Apply the frame delta -- this may fail. */
if ((err = plcrash_async_cfe_entry_apply(task, function_address, ¤t_frame->thread_state, &entry, &next_frame->thread_state)) == PLCRASH_ESUCCESS) {
result = PLFRAME_ESUCCESS;
} else {
PLCF_DEBUG("Failed to apply CFE encoding 0x%" PRIx32 " for PC 0x%" PRIx64 ": %d", encoding, (uint64_t) pc, err);
result = PLFRAME_ENOFRAME;
}
cleanup_cfe_entry:
plcrash_async_cfe_entry_free(&entry);
cleanup_mobject:
plcrash_async_mobject_free(&unwind_mobj);
cleanup:
plcrash_async_image_list_set_reading(image_list, false);
return result;
}