in Source/PLCrashAsyncDwarfCFAStateEvaluation.cpp [473:601]
plcrash_error_t dwarf_cfa_state<machine_ptr, machine_ptr_s>::apply_state (task_t task,
plcrash_async_dwarf_cie_info_t *cie_info,
const plcrash_async_thread_state_t *thread_state,
const plcrash_async_byteorder_t *byteorder,
plcrash_async_thread_state_t *new_thread_state)
{
plcrash_error_t err;
/* Initialize the new thread state */
plcrash_async_thread_state_copy(new_thread_state, thread_state);
plcrash_async_thread_state_clear_volatile_regs(new_thread_state);
/*
* Restore the canonical frame address
*/
dwarf_cfa_rule<machine_ptr, machine_ptr_s> cfa_rule = get_cfa_rule();
machine_ptr cfa_val;
switch (cfa_rule.type()) {
case DWARF_CFA_STATE_CFA_TYPE_UNDEFINED:
/** Missing canonical frame address! */
PLCF_DEBUG("No canonical frame address specified in the CFA state; can't apply state");
return PLCRASH_EINVAL;
case DWARF_CFA_STATE_CFA_TYPE_REGISTER:
case DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED: {
plcrash_regnum_t regnum;
/* Map to a plcrash register number */
if (!plcrash_async_thread_state_map_dwarf_to_reg(thread_state, cfa_rule.register_number(), ®num)) {
PLCF_DEBUG("CFA rule references an unsupported DWARF register: 0x%" PRIx32, cfa_rule.register_number());
return PLCRASH_ENOTSUP;
}
/* Verify that the requested register is available */
if (!plcrash_async_thread_state_has_reg(thread_state, regnum)) {
PLCF_DEBUG("CFA rule references a register that is not available from the current thread state: %s", plcrash_async_thread_state_get_reg_name(thread_state, regnum));
return PLCRASH_ENOTFOUND;
}
/* Fetch the current value, apply the offset, and save as the new thread's CFA. */
cfa_val = (machine_ptr) plcrash_async_thread_state_get_reg(thread_state, regnum);
if (cfa_rule.type() == DWARF_CFA_STATE_CFA_TYPE_REGISTER)
cfa_val += cfa_rule.register_offset();
else
cfa_val += cfa_rule.register_offset_signed();
break;
}
case DWARF_CFA_STATE_CFA_TYPE_EXPRESSION: {
plcrash_async_mobject_t mobj;
if ((err = plcrash_async_mobject_init(&mobj, task, cfa_rule.expression_address(), cfa_rule.expression_length(), true)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Could not map CFA expression range");
return err;
}
if ((err = plcrash_async_dwarf_expression_eval<machine_ptr, machine_ptr_s>(&mobj, task, thread_state, byteorder, cfa_rule.expression_address(), 0x0, cfa_rule.expression_length(), NULL, 0, &cfa_val)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("CFA eval_64 failed");
return err;
}
/* Clean up the memory mapping */
plcrash_async_mobject_free(&mobj);
break;
}
}
/* Apply the CFA to the new state */
plcrash_async_thread_state_set_reg(new_thread_state, PLCRASH_REG_SP, cfa_val);
/*
* Restore register values
*/
dwarf_cfa_state_iterator<machine_ptr, machine_ptr_s> iter = dwarf_cfa_state_iterator<machine_ptr, machine_ptr_s>(this);
dwarf_cfa_state_regnum_t dw_regnum;
plcrash_dwarf_cfa_reg_rule_t dw_rule;
machine_ptr dw_value;
while (iter.next(&dw_regnum, &dw_rule, &dw_value)) {
/* Map the register number */
plcrash_regnum_t pl_regnum;
if (!plcrash_async_thread_state_map_dwarf_to_reg(thread_state, dw_regnum, &pl_regnum)) {
/* Some DWARF ABIs (such as x86-64) define the return address using a pseudo-register. In that case, the
* register will not have a vaid DWARF -> PLCrashReporter mapping; we simply target the IP in this case,
* which results in the expected behavior of setting the IP in the new thread state. */
if (cie_info->return_address_register == dw_regnum) {
pl_regnum = PLCRASH_REG_IP;
} else {
PLCF_DEBUG("Register rule references an unsupported DWARF register: 0x%" PRIx64, (uint64_t) dw_regnum);
return PLCRASH_EINVAL;
}
}
/* Apply the register rule */
if ((err = plcrash_async_dwarf_cfa_state_apply_register<machine_ptr, machine_ptr_s>(task, thread_state, byteorder, new_thread_state, cfa_val, pl_regnum, dw_rule, dw_value)) != PLCRASH_ESUCCESS)
return err;
/* If the target register is defined as the return address (and is not already the IP), copy the value to the IP. */
if (cie_info->return_address_register == dw_regnum && pl_regnum != PLCRASH_REG_IP) {
PLCF_ASSERT(plcrash_async_thread_state_has_reg(new_thread_state, pl_regnum));
plcrash_async_thread_state_set_reg(new_thread_state, PLCRASH_REG_IP, plcrash_async_thread_state_get_reg(new_thread_state, pl_regnum));
}
}
/*
* If the IP was not restored via a saved register above, the CIE's return_address_register may reference a live register in the
* current thread state. This will occur in leaf frames on platforms where lr has not been saved to the stack.
*/
if (!plcrash_async_thread_state_has_reg(new_thread_state, PLCRASH_REG_IP)) {
/* Map the return_address_register number */
plcrash_regnum_t pl_regnum;
if (!plcrash_async_thread_state_map_dwarf_to_reg(thread_state, cie_info->return_address_register, &pl_regnum)) {
PLCF_DEBUG("Return address register value references an unsupported DWARF register: 0x%" PRIx64, (uint64_t) cie_info->return_address_register);
return PLCRASH_EINVAL;
}
/* Verify that the register is available */
if (!plcrash_async_thread_state_has_reg(thread_state, pl_regnum)) {
PLCF_DEBUG("CIE return_address_register references a register that is not available from the current thread state: %s", plcrash_async_thread_state_get_reg_name(thread_state, pl_regnum));
return PLCRASH_EINVAL;
}
/* Copy the value to the new state's IP. */
plcrash_async_thread_state_set_reg(new_thread_state, PLCRASH_REG_IP, plcrash_async_thread_state_get_reg(thread_state, pl_regnum));
}
return PLCRASH_ESUCCESS;
}