in kernel/unwind.c [700:891]
static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
signed ptrType, struct unwind_state *state)
{
union {
const u8 *p8;
const u16 *p16;
const u32 *p32;
} ptr;
int result = 1;
u8 opcode;
if (start != state->cieStart) {
state->loc = state->org;
result =
processCFI(state->cieStart, state->cieEnd, 0, ptrType,
state);
if (targetLoc == 0 && state->label == NULL)
return result;
}
for (ptr.p8 = start; result && ptr.p8 < end;) {
switch (*ptr.p8 >> 6) {
uleb128_t value;
case 0:
opcode = *ptr.p8++;
switch (opcode) {
case DW_CFA_nop:
unw_debug("cfa nop ");
break;
case DW_CFA_set_loc:
state->loc = read_pointer(&ptr.p8, end,
ptrType);
if (state->loc == 0)
result = 0;
unw_debug("cfa_set_loc: 0x%lx ", state->loc);
break;
case DW_CFA_advance_loc1:
unw_debug("\ncfa advance loc1:");
result = ptr.p8 < end
&& advance_loc(*ptr.p8++, state);
break;
case DW_CFA_advance_loc2:
value = *ptr.p8++;
value += *ptr.p8++ << 8;
unw_debug("\ncfa advance loc2:");
result = ptr.p8 <= end + 2
/* && advance_loc(*ptr.p16++, state); */
&& advance_loc(value, state);
break;
case DW_CFA_advance_loc4:
unw_debug("\ncfa advance loc4:");
result = ptr.p8 <= end + 4
&& advance_loc(*ptr.p32++, state);
break;
case DW_CFA_offset_extended:
value = get_uleb128(&ptr.p8, end);
unw_debug("cfa_offset_extended: ");
set_rule(value, Memory,
get_uleb128(&ptr.p8, end), state);
break;
case DW_CFA_val_offset:
value = get_uleb128(&ptr.p8, end);
set_rule(value, Value,
get_uleb128(&ptr.p8, end), state);
break;
case DW_CFA_offset_extended_sf:
value = get_uleb128(&ptr.p8, end);
set_rule(value, Memory,
get_sleb128(&ptr.p8, end), state);
break;
case DW_CFA_val_offset_sf:
value = get_uleb128(&ptr.p8, end);
set_rule(value, Value,
get_sleb128(&ptr.p8, end), state);
break;
case DW_CFA_restore_extended:
unw_debug("cfa_restore_extended: ");
case DW_CFA_undefined:
unw_debug("cfa_undefined: ");
case DW_CFA_same_value:
unw_debug("cfa_same_value: ");
set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0,
state);
break;
case DW_CFA_register:
unw_debug("cfa_register: ");
value = get_uleb128(&ptr.p8, end);
set_rule(value,
Register,
get_uleb128(&ptr.p8, end), state);
break;
case DW_CFA_remember_state:
unw_debug("cfa_remember_state: ");
if (ptr.p8 == state->label) {
state->label = NULL;
return 1;
}
if (state->stackDepth >= MAX_STACK_DEPTH)
return 0;
state->stack[state->stackDepth++] = ptr.p8;
break;
case DW_CFA_restore_state:
unw_debug("cfa_restore_state: ");
if (state->stackDepth) {
const uleb128_t loc = state->loc;
const u8 *label = state->label;
state->label =
state->stack[state->stackDepth - 1];
memcpy(&state->cfa, &badCFA,
sizeof(state->cfa));
memset(state->regs, 0,
sizeof(state->regs));
state->stackDepth = 0;
result =
processCFI(start, end, 0, ptrType,
state);
state->loc = loc;
state->label = label;
} else
return 0;
break;
case DW_CFA_def_cfa:
state->cfa.reg = get_uleb128(&ptr.p8, end);
unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg);
fallthrough;
case DW_CFA_def_cfa_offset:
state->cfa.offs = get_uleb128(&ptr.p8, end);
unw_debug("cfa_def_cfa_offset: 0x%lx ",
state->cfa.offs);
break;
case DW_CFA_def_cfa_sf:
state->cfa.reg = get_uleb128(&ptr.p8, end);
fallthrough;
case DW_CFA_def_cfa_offset_sf:
state->cfa.offs = get_sleb128(&ptr.p8, end)
* state->dataAlign;
break;
case DW_CFA_def_cfa_register:
unw_debug("cfa_def_cfa_register: ");
state->cfa.reg = get_uleb128(&ptr.p8, end);
break;
/*todo case DW_CFA_def_cfa_expression: */
/*todo case DW_CFA_expression: */
/*todo case DW_CFA_val_expression: */
case DW_CFA_GNU_args_size:
get_uleb128(&ptr.p8, end);
break;
case DW_CFA_GNU_negative_offset_extended:
value = get_uleb128(&ptr.p8, end);
set_rule(value,
Memory,
(uleb128_t) 0 - get_uleb128(&ptr.p8,
end),
state);
break;
case DW_CFA_GNU_window_save:
default:
unw_debug("UNKNOWN OPCODE 0x%x\n", opcode);
result = 0;
break;
}
break;
case 1:
unw_debug("\ncfa_adv_loc: ");
result = advance_loc(*ptr.p8++ & 0x3f, state);
break;
case 2:
unw_debug("cfa_offset: ");
value = *ptr.p8++ & 0x3f;
set_rule(value, Memory, get_uleb128(&ptr.p8, end),
state);
break;
case 3:
unw_debug("cfa_restore: ");
set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
break;
}
if (ptr.p8 > end)
result = 0;
if (result && targetLoc != 0 && targetLoc < state->loc)
return 1;
}
return result && ptr.p8 == end && (targetLoc == 0 || (
/*todo While in theory this should apply, gcc in practice omits
everything past the function prolog, and hence the location
never reaches the end of the function.
targetLoc < state->loc && */ state->label == NULL));
}