FreeRTOS硬故障排查求助:getRegistersFromStack函数未调用问题
getRegistersFromStack() Isn't Being Called in Your HardFault Handler Let's walk through the key issues with your current code and fix them step by step. The core problem boils down to how compiler-generated stack operations and function calls are disrupting the HardFault context you're trying to capture.
Key Issues in Your Current Implementation
Non-naked
prepareRegistersFromStack()corrupts context
Since this function isn't marked asnaked, the compiler automatically adds prologue/epilogue code (like pushing registers to the stack and adjusting the stack pointer) when it's called. This modifies the stack pointer (SP) and link register (LR) values that your assembly relies on to detect whether to useMSPorPSP. As a result, your logic to grab the fault stack address is working with invalid data.Calling C functions in a naked
HardFault_Handler()breaks stack state
Your original naked handler calls__disable_fault_irq()and__disable_irq()—these are C functions, and calling them triggers compiler-generated stack operations. Naked functions don't set up a proper stack frame, so these calls corrupt the exact stack context you need to analyze the HardFault.Potential linker/optimization issues
Without explicit hints, the compiler might optimizegetRegistersFromStack()away (even withvolatilevariables) or thehandler2_address_constlabel might resolve to an incorrect address.
Fixed Implementation
Here's how to rewrite your code to reliably trigger getRegistersFromStack():
1. Pure Assembly Naked HardFault Handler
Move all context-preserving logic directly into the naked handler, using assembly to disable interrupts (avoiding C function calls) and capture the correct stack pointer:
extern "C" __attribute__((naked)) void HardFault_Handler() { __asm volatile ( "cpsid i \n" // Disable IRQ interrupts "cpsid f \n" // Disable Fault interrupts "tst lr, #4 \n" // Check LR bit 2 to determine stack used "ite eq \n" "mrseq r0, msp \n" // If bit 2 is 0, use Main Stack Pointer "mrsne r0, psp \n" // If bit 2 is 1, use Process Stack Pointer "ldr r2, =getRegistersFromStack \n" // Load address of handler function "bx r2 \n" // Jump to handler with r0 as stack pointer argument ); }
2. Protected getRegistersFromStack() Function
Add attributes to prevent optimization and ensure the function isn't stripped by the linker:
extern "C" __attribute__((used, optimize("O0"))) void getRegistersFromStack(uint32_t *pulFaultStackAddress) { // Volatile variables to preserve register values through debugging volatile uint32_t r0 = pulFaultStackAddress[0]; volatile uint32_t r1 = pulFaultStackAddress[1]; volatile uint32_t r2 = pulFaultStackAddress[2]; volatile uint32_t r3 = pulFaultStackAddress[3]; volatile uint32_t r12 = pulFaultStackAddress[4]; volatile uint32_t lr = pulFaultStackAddress[5]; volatile uint32_t pc = pulFaultStackAddress[6]; // The address that triggered the fault volatile uint32_t psr = pulFaultStackAddress[7]; // When the debugger hits this loop, inspect the volatile variables for register values for(;;); }
3. Verify Interrupt Vector Table
Double-check your startup assembly file (e.g., startup_stm32xxxx.s) to ensure HardFault_Handler is correctly linked in the vector table, with no weak definitions overriding it:
DCD HardFault_Handler ; Hard Fault Handler
Debugging Tips
- Set a breakpoint on the
for(;;);line ingetRegistersFromStack()—it should trigger when a HardFault occurs. - In your debugger, check the value of
r0when enteringgetRegistersFromStack()—it should point to a valid stack address containing the register snapshot from the fault.
内容的提问来源于stack exchange,提问作者user654789384

