in Source/PLCrashAsyncDwarfCFAStateEvaluation.cpp [618:759]
static plcrash_error_t plcrash_async_dwarf_cfa_state_apply_register (task_t task,
const plcrash_async_thread_state_t *thread_state,
const plcrash_async_byteorder_t *byteorder,
plcrash_async_thread_state_t *new_thread_state,
machine_ptr cfa_val,
plcrash_regnum_t pl_regnum,
plcrash_dwarf_cfa_reg_rule_t dw_rule,
machine_ptr dw_value)
{
plcrash_error_t err;
uint8_t greg_size = plcrash_async_thread_state_get_greg_size(thread_state);
bool m64 = (greg_size == 8);
union {
uint32_t v32;
uint64_t v64;
} rvalue;
void *vptr = &rvalue;
/* Apply the rule */
switch (dw_rule) {
case PLCRASH_DWARF_CFA_REG_RULE_OFFSET: {
if ((err = plcrash_async_task_memcpy(task, (pl_vm_address_t) cfa_val, (pl_vm_off_t) dw_value, vptr, greg_size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read offset(N) register value: %d", err);
return err;
}
if (m64) {
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, rvalue.v64);
} else {
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, rvalue.v32);
}
break;
}
case PLCRASH_DWARF_CFA_REG_RULE_VAL_OFFSET:
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, cfa_val + ((machine_ptr_s) dw_value));
break;
case PLCRASH_DWARF_CFA_REG_RULE_REGISTER: {
/* The previous value of this register is stored in another register numbered R. */
plcrash_regnum_t src_pl_regnum;
if (!plcrash_async_thread_state_map_dwarf_to_reg(thread_state, dw_value, &src_pl_regnum)) {
PLCF_DEBUG("Register rule references an unsupported DWARF register: 0x%" PRIx64, (uint64_t) dw_value);
return PLCRASH_EINVAL;
}
if (!plcrash_async_thread_state_has_reg(thread_state, src_pl_regnum)) {
PLCF_DEBUG("Register rule references a register that is not available from the current thread state: %s", plcrash_async_thread_state_get_reg_name(thread_state, src_pl_regnum));
return PLCRASH_ENOTFOUND;
}
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, plcrash_async_thread_state_get_reg(thread_state, src_pl_regnum));
break;
}
case PLCRASH_DWARF_CFA_REG_RULE_VAL_EXPRESSION:
case PLCRASH_DWARF_CFA_REG_RULE_EXPRESSION: {
pl_vm_address_t expr_addr = (pl_vm_address_t) dw_value;
/* Fetch the expression's length */
uint64_t expr_len;
pl_vm_size_t uleb128_len;
if ((err = plcrash_async_dwarf_read_task_uleb128(task, expr_addr, 0, &expr_len, &uleb128_len)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read uleb128 length header for rule expression");
return err;
}
/* Skip the ULEB128 length header; expr_addr will not point at the expression opcodes. */
if (!plcrash_async_address_apply_offset(expr_addr, uleb128_len, &expr_addr)) {
PLCF_DEBUG("Overflow applying the ULEB128 length to our expression base address");
return PLCRASH_EINVAL;
}
/* Map the expression data */
plcrash_async_mobject_t mobj;
if ((err = plcrash_async_mobject_init(&mobj, task, expr_addr, (pl_vm_size_t) expr_len, true)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Could not map CFA expression range");
return err;
}
/* Perform the evaluation */
plcrash_greg_t regval;
if (m64) {
uint64_t initial_state[] = { cfa_val };
if ((err = plcrash_async_dwarf_expression_eval<uint64_t, int64_t>(&mobj, task, thread_state, byteorder, expr_addr, 0, (pl_vm_size_t) expr_len, initial_state, 1, &rvalue.v64)) != PLCRASH_ESUCCESS) {
plcrash_async_mobject_free(&mobj);
PLCF_DEBUG("CFA eval_64 failed");
return err;
}
regval = rvalue.v64;
} else {
uint32_t initial_state[] = { static_cast<uint32_t>(cfa_val) };
if ((err = plcrash_async_dwarf_expression_eval<uint32_t, int32_t>(&mobj, task, thread_state, byteorder, expr_addr, 0, (pl_vm_size_t) expr_len, initial_state, 1, &rvalue.v32)) != PLCRASH_ESUCCESS) {
plcrash_async_mobject_free(&mobj);
PLCF_DEBUG("CFA eval_32 failed");
return err;
}
regval = rvalue.v32;
}
/* Clean up the memory mapping */
plcrash_async_mobject_free(&mobj);
/* Dereference the target address, if using the non-value EXPRESSION rule */
if (dw_rule == PLCRASH_DWARF_CFA_REG_RULE_EXPRESSION) {
if ((err = plcrash_async_task_memcpy(task, (pl_vm_address_t) regval, 0, vptr, greg_size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read register value from expression result: %d", err);
return err;
}
if (m64) {
regval = rvalue.v64;
} else {
regval = rvalue.v32;
}
}
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, regval);
break;
}
case PLCRASH_DWARF_CFA_REG_RULE_SAME_VALUE:
/* This register has not been modified from the previous frame. (By convention, it is preserved by the callee, but
* the callee has not modified it.)
*
* The register's value may be found in the frame's thread state. For frames other than the first, the
* register may not have been restored, and thus may be unavailable. */
if (!plcrash_async_thread_state_has_reg(thread_state, pl_regnum)) {
PLCF_DEBUG("Same-value rule references a register that is not available from the current thread state");
return PLCRASH_ENOTFOUND;
}
/* Copy the register value from the previous state */
plcrash_async_thread_state_set_reg(new_thread_state, pl_regnum, plcrash_async_thread_state_get_reg(thread_state, pl_regnum));
break;
}
return PLCRASH_ESUCCESS;
}