aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngelina Zhao <xuemingzhao@asrmicro.com>2020-07-23 19:31:23 +0800
committerJerome Forissier <jerome@forissier.org>2020-07-31 11:00:34 +0200
commit19b3fe6c5a72925c394f5eb1397cfcf17553f01d (patch)
treee2a0d917d35a9feedf0d78754cd2a3c85fd49a93
parentf37217405046af890731e33fe8b84c43bcff1b01 (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.c25
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));