in Source/PLCrashFrameDWARFUnwind.cpp [63:200]
static plframe_error_t plframe_cursor_read_dwarf_unwind_int (task_t task,
machine_ptr pc,
plcrash_async_macho_t *image,
const plframe_stackframe_t *current_frame,
const plframe_stackframe_t *previous_frame,
plframe_stackframe_t *next_frame)
{
gnu_ehptr_reader<machine_ptr> ptr_state(image->byteorder);
/* Mapped DWARF sections; only one of eh_frame/debug_frame will be mapped */
plcrash_async_mobject_t eh_frame;
plcrash_async_mobject_t debug_frame;
plcrash_async_mobject_t *dwarf_section = NULL;
bool is_debug_frame = false;
/* Reader state */
dwarf_frame_reader reader;
plcrash_async_dwarf_fde_info_t fde_info;
bool did_init_fde = false;
plcrash_async_dwarf_cie_info_t cie_info;
bool did_init_cie = false;
/* CFA evaluation stack */
plcrash::async::dwarf_cfa_state<machine_ptr, machine_ptr_s> cfa_state;
plframe_error_t result;
plcrash_error_t err;
/*
* Map the eh_frame or debug_frame DWARF sections. Apple doesn't seem to use debug_frame at all;
* as such, we prefer eh_frame, but allow falling back on debug_frame.
*/
{
err = plcrash_async_macho_map_section(image, "__TEXT", "__eh_frame", &eh_frame);
if (err == PLCRASH_ESUCCESS) {
dwarf_section = &eh_frame;
}
if (dwarf_section == NULL) {
err = plcrash_async_macho_map_section(image, "__DWARF", "__debug_frame", &debug_frame);
if (err == PLCRASH_ESUCCESS) {
dwarf_section = &debug_frame;
is_debug_frame = true;
}
}
/* If neither, there's nothing to do */
if (dwarf_section == NULL) {
/* The lack of debug_frame/eh_frame is not an error, but we can't proceed. */
result = PLFRAME_ENOFRAME;
goto cleanup;
}
}
/* Initialize the reader. */
if ((err = reader.init(dwarf_section, image->byteorder, image->m64, is_debug_frame)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Could not initialize a %s DWARF parser for the current frame pc 0x%" PRIx64 " in %s: %d", (is_debug_frame ? "debug_frame" : "eh_frame"), (uint64_t) pc, PLCF_DEBUG_IMAGE_NAME(image), err);
result = PLFRAME_EINVAL;
goto cleanup;
}
/* Find the FDE (if any) */
{
err = reader.find_fde(0x0 /* offset hint */, (pl_vm_address_t) pc, &fde_info);
if (err != PLCRASH_ESUCCESS) {
if (err != PLCRASH_ENOTFOUND)
PLCF_DEBUG("Failed to find FDE the current frame pc 0x%" PRIx64 " in %s: %d", (uint64_t) pc, PLCF_DEBUG_IMAGE_NAME(image), err);
result = PLFRAME_ENOTSUP;
goto cleanup;
}
did_init_fde = true;
}
/* Initialize pointer state */
{
// TODO - configure the pointer state */
}
/* Parse CIE info */
{
err = plcrash_async_dwarf_cie_info_init(&cie_info, dwarf_section, image->byteorder, &ptr_state, plcrash_async_mobject_base_address(dwarf_section) + fde_info.cie_offset);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to parse CIE at offset of 0x%" PRIx64 ": %d", (uint64_t) fde_info.cie_offset, err);
result = PLFRAME_ENOTSUP;
plcrash_async_dwarf_fde_info_free(&fde_info);
goto cleanup;
}
did_init_cie = true;
}
/* Evaluate the CFA instruction opcodes */
{
/* Assert that pc_start won't overflow machine_ptr. This could only occur if we were to use a 64-bit FDE parser with 32-bit CFA evaluation
* TODO: The FDE pc_start value should probably by typed for the target architecture. */
PLCF_ASSERT(fde_info.pc_start < std::numeric_limits<machine_ptr>::max());
/* Initial instructions */
err = cfa_state.eval_program(dwarf_section, pc, (uint32_t)fde_info.pc_start, &cie_info, &ptr_state, image->byteorder, plcrash_async_mobject_base_address(dwarf_section), cie_info.initial_instructions_offset, cie_info.initial_instructions_length);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to evaluate CFA at offset of 0x%" PRIx64 ": %d", (uint64_t) fde_info.instructions_offset, err);
result = PLFRAME_ENOTSUP;
goto cleanup;
}
/* FDE instructions */
err = cfa_state.eval_program(dwarf_section, pc, (uint32_t)fde_info.pc_start, &cie_info, &ptr_state, image->byteorder, plcrash_async_mobject_base_address(dwarf_section), fde_info.instructions_offset, fde_info.instructions_length);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to evaluate CFA at offset of 0x%" PRIx64 ": %d", (uint64_t) fde_info.instructions_offset, err);
result = PLFRAME_ENOTSUP;
goto cleanup;
}
}
/* Apply the frame delta -- this may fail. */
if ((err = cfa_state.apply_state(task, &cie_info, ¤t_frame->thread_state, image->byteorder, &next_frame->thread_state)) == PLCRASH_ESUCCESS) {
result = PLFRAME_ESUCCESS;
} else {
PLCF_DEBUG("Failed to apply CFA state for PC 0x%" PRIx64 ": %d", (uint64_t) pc, err);
result = PLFRAME_ENOFRAME;
}
// Fall-through
cleanup:
if (dwarf_section != NULL)
plcrash_async_mobject_free(dwarf_section);
if (did_init_cie)
plcrash_async_dwarf_cie_info_free(&cie_info);
if (did_init_fde)
plcrash_async_dwarf_fde_info_free(&fde_info);
return result;
}