diff options
author | Angelina Zhao <xuemingzhao@asrmicro.com> | 2020-07-23 19:31:23 +0800 |
---|---|---|
committer | Jerome Forissier <jerome@forissier.org> | 2020-07-31 11:00:34 +0200 |
commit | 19b3fe6c5a72925c394f5eb1397cfcf17553f01d (patch) | |
tree | e2a0d917d35a9feedf0d78754cd2a3c85fd49a93 | |
parent | f37217405046af890731e33fe8b84c43bcff1b01 (diff) |
core: arm: fix the unwind stack failure with __no_return function
unwind operation use LR instead of PC to locate unwind data. In some
case, the compiler removes all the extra instrustions after a branch to
__no_return function, and then LR saves the address of next function,
rather than the caller of the __no_return function, leading to unwind
failure.
The fix manually adjust the LR value to match the search algorithm so
as to locate the correct caller in unwind stack operation.
Signed-off-by: Angelina Zhao <xuemingzhao@asrmicro.com>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
[jf: reformat the commit description]
Signed-off-by: Jerome Forissier <jerome@forissier.org>
-rw-r--r-- | core/arch/arm/kernel/unwind_arm32.c | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/core/arch/arm/kernel/unwind_arm32.c b/core/arch/arm/kernel/unwind_arm32.c index 17cc48c2..0d35d003 100644 --- a/core/arch/arm/kernel/unwind_arm32.c +++ b/core/arch/arm/kernel/unwind_arm32.c @@ -369,8 +369,13 @@ bool unwind_stack_arm32(struct unwind_state_arm32 *state, vaddr_t exidx, /* The pc value is correct and will be overwritten, save it */ state->start_pc = state->registers[PC]; - /* Find the item to run */ - index = find_index(state->start_pc, exidx, exidx_sz); + /* + * Find the item to run. Subtract 2 from PC to make sure that we're + * still inside the calling function in case a __no_return function + * (typically panic()) is called unconditionally and may cause LR and + * thus this PC to point into the next and entirely unrelated function. + */ + index = find_index(state->start_pc - 2, exidx, exidx_sz); finished = false; if (index->insn != EXIDX_CANTUNWIND) { @@ -462,7 +467,13 @@ void print_kernel_stack(int level) state.registers[FP] = read_fp(); state.registers[SP] = read_sp(); state.registers[LR] = read_lr(); - state.registers[PC] = (uint32_t)print_kernel_stack; + + /* + * Add 4 to make sure that we have an address well inside this function. + * This is needed because we're subtracting 2 from PC when calling + * find_index() above. See a comment there for more details. + */ + state.registers[PC] = (uint32_t)print_kernel_stack + 4; get_stack_limits(&stack_start, &stack_end); print_stack_arm32(level, &state, exidx, exidx_sz, stack_start, @@ -494,7 +505,13 @@ vaddr_t *unw_get_kernel_stack(void) state.registers[FP] = read_fp(); state.registers[SP] = read_sp(); state.registers[LR] = read_lr(); - state.registers[PC] = (uint32_t)unw_get_kernel_stack; + + /* + * Add 4 to make sure that we have an address well inside this function. + * This is needed because we're subtracting 2 from PC when calling + * find_index() above. See a comment there for more details. + */ + state.registers[PC] = (uint32_t)unw_get_kernel_stack + 4; while (unwind_stack_arm32(&state, exidx, exidx_sz, stack, stack_size)) { tmp = unw_grow(addr, &size, (n + 1) * sizeof(vaddr_t)); |