static plframe_error_t plframe_cursor_read_dwarf_unwind_int()

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, &current_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;
}