/** * Initialize the DWARF opcode stream. * * @param mobj The memory object from which the expression opcodes will be read. This object must * remain valid for the lifetime of the opstream instance. * @param byteorder The byte order of the data referenced by @a mobj and @a thread_state. * @param address The task-relative address within @a mobj at which the opcodes will be fetched. * @param offset An offset to be applied to @a address. * @param length The total length of the opcodes readable at @a address + @a offset. */ plcrash_error_t dwarf_opstream::init (plcrash_async_mobject_t *mobj, const plcrash_async_byteorder_t *byteorder, pl_vm_address_t address, pl_vm_off_t offset, pl_vm_size_t length) { _mobj = mobj; _byteorder = byteorder; /* Calculate the start and end addresses */ if (!plcrash_async_address_apply_offset(address, offset, &_start)) { PLCF_DEBUG("Offset overflows base address"); return PLCRASH_EINVAL; } if (length > PL_VM_OFF_MAX || !plcrash_async_address_apply_offset(_start, length, &_end)) { PLCF_DEBUG("Length overflows base address"); return PLCRASH_EINVAL; } /* Map in the full instruction range */ _instr = plcrash_async_mobject_remap_address(mobj, _start, 0, _end-_start); _instr_max = (uint8_t *)_instr + (_end - _start); _p = _instr; if (_instr == NULL) { PLCF_DEBUG("Could not map the DWARF instructions; range falls outside mapped pages"); return PLCRASH_EINVAL; } return PLCRASH_ESUCCESS; }
PLCR_CPP_BEGIN_ASYNC_NS /** * @internal * @ingroup plcrash_async_dwarf_private * @defgroup plcrash_async_dwarf_private_opstream Generic DWARF Opcode Stream * @{ */ /** * Initialize the DWARF opcode stream. * * @param mobj The memory object from which the expression opcodes will be read. This object must * remain valid for the lifetime of the opstream instance. * @param byteorder The byte order of the data referenced by @a mobj and @a thread_state. * @param address The task-relative address within @a mobj at which the opcodes will be fetched. * @param offset An offset to be applied to @a address. * @param length The total length of the opcodes readable at @a address + @a offset. */ plcrash_error_t dwarf_opstream::init (plcrash_async_mobject_t *mobj, const plcrash_async_byteorder_t *byteorder, pl_vm_address_t address, pl_vm_off_t offset, pl_vm_size_t length) { _mobj = mobj; _byteorder = byteorder; /* Calculate the start and end addresses */ if (!plcrash_async_address_apply_offset(address, offset, &_start)) { PLCF_DEBUG("Offset overflows base address"); return PLCRASH_EINVAL; } if (length > PL_VM_OFF_MAX || !plcrash_async_address_apply_offset(_start, length, &_end)) { PLCF_DEBUG("Length overflows base address"); return PLCRASH_EINVAL; } /* Map in the full instruction range */ _instr = plcrash_async_mobject_remap_address(mobj, _start, 0, _end-_start); _instr_max = (uint8_t *)_instr + (_end - _start); _p = _instr; if (_instr == NULL) { PLCF_DEBUG("Could not map the DWARF instructions; range falls outside mapped pages"); return PLCRASH_EINVAL; } return PLCRASH_ESUCCESS; }
/** * Attempt to fetch next frame using compact frame unwinding data from @a image_list. * * @param task The task containing the target frame stack. * @param image_list The list of images loaded in the target @a task. * @param current_frame The current stack frame. * @param previous_frame The previous stack frame, or NULL if this is the first frame. * @param next_frame The new frame to be initialized. * * @return Returns PLFRAME_ESUCCESS on success, PLFRAME_ENOFRAME is no additional frames are available, or a standard plframe_error_t code if an error occurs. */ 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); /* Find the corresponding image */ plcrash_async_macho_t *image = plcrash_async_image_containing_address(image_list, pc); if (image == NULL) { PLCF_DEBUG("Could not find a loaded image for the current frame pc: 0x%" PRIx64, (uint64_t) pc); return PLFRAME_ENOTSUP; } /* Map the unwind section */ plcrash_async_mobject_t unwind_mobj; err = plcrash_async_macho_map_section(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->name, err); return PLFRAME_ENOTSUP; } /* Initialize the CFE reader. */ cpu_type_t cputype = image->byteorder->swap32(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->name, err); return PLFRAME_EINVAL; } /* 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, pc - 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); return PLFRAME_ENOTSUP; } /* 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); return PLFRAME_ENOTSUP; } /* Skip entries for which no unwind information is unavailable */ if (plcrash_async_cfe_entry_type(&entry) == PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE) { plcrash_async_cfe_entry_free(&entry); return PLFRAME_ENOFRAME; } /* Compute the in-core function address */ pl_vm_address_t function_address; if (!plcrash_async_address_apply_offset(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->header_addr); plcrash_async_cfe_entry_free(&entry); return PLFRAME_EINVAL; } /* 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; } plcrash_async_cfe_entry_free(&entry); return result; }