static int processCFI()

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));
}