rust-embedded / cortex-m

Low level access to Cortex-M processors
Apache License 2.0
835 stars 152 forks source link

`HardFault` trampoline makes it difficult for `probe-rs` debugger to unwind past the exception handler. #514

Open noppej opened 8 months ago

noppej commented 8 months ago

The HardFault trampoline overwrites the EXC_RETURN magic values when it writes to the lr register.

Currently, the probe-rs debugger relies on those magic values during stack unwinding, to identify exception handlers, and unwind the stack that caused the exception. For instance, it used to be possible to get a stack like this:

__cortex_m_rt_HardFault (/Users/jn/dev/debug/probe-rs-debugger-test/src/thumbv7em-none-eabihf/nRF52833_xxAA.rs:43)
"HardFault handler. Cause: Escalated BusFault (Precise data access error) at location: 0x3ffffffc." (Unknown Source:0)
div_half_u128 (/rustc/0ecbd0605770f45c9151715e66ba2b3cae367fcb/library/core/src/ptr/mod.rs:1636)
__cortex_m_rt_SysTick (/Users/jn/dev/debug/probe-rs-debugger-test/src/thumbv7em-none-eabihf/nRF52833_xxAA.rs:32)
__cortex_m_rt_SysTick_trampoline (/Users/jn/dev/debug/probe-rs-debugger-test/src/thumbv7em-none-eabihf/nRF52833_xxAA.rs:17)
"Systick handler." (Unknown Source:0)
__delay #[inline] (/Users/jn/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cortex-m-0.7.7/asm/inline.rs:62)
delay (/Users/jn/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cortex-m-0.7.7/src/asm.rs:29)
__cortex_m_rt_main (/Users/jn/dev/debug/probe-rs-debugger-test/src/thumbv7em-none-eabihf/nRF52833_xxAA.rs:85)
__cortex_m_rt_main (/Users/jn/dev/debug/probe-rs-debugger-test/src/thumbv7em-none-eabihf/nRF52833_xxAA.rs:62)

With the trampoline, the debugger has no way of knowing that it is an exception handler that needs register substitutions before the calling frames can be identified, and it stops unwinding at the first frame.

I've notice in the unreleased code, that users can bypass the trampoline, with#[exception(trampoline = false)], and this restores the previous behaviour of the stack unwind.

Unfortunately, this means that the users have to modify their code before they can use the debugger in these situations.

Is there any way we can make the default to be trampoline = false?

adamgreig commented 8 months ago

I think there's fairly broad support for removing the trampline from cortex-m-rt, but we can't do it in the 0.7 series as it's a breaking change, and we're reluctant to release 0.8 because it massively fractures the ecosystem (every cortex-m PAC depends on cortex-m-rt 0.7, so the PACs have to update, and then the HALs on top of those have to update, etc).

I think the best option we have at the moment is to get the "trampolines are optional" change out in 0.7, then plan for 0.8 (or more likely 1.0) without the trampoline and with our other planned changes, including decoupling PACs from this crate a bit to make it easier to update later.

noppej commented 8 months ago

That seems like a sensible approach. In the mean time, I will add some docs for probe-rs to help users use the opt-out of trampoline. Thanks for the quick response.

jonathanpallant commented 8 months ago

Disabling the trampoline means you no longer get the ef: &ExceptionFrame argument passed to the hard fault handler, right? Or I might be confused about how this works.