When a hardfault occurs, you can check the LR and PC registers to find the offending instruction.
For example, in this fault frame the PC is 0x00002681, thread is main and type of error is a stack overflow.
So in this case, there is no need to look up the PC or LR to understand the issue.
The main stack size needs to be increased.
For more information on how to debug hardfaults, see Memfault Cortex Hardfault debug.
*** Using Zephyr OS v4.0.99-7607c6585566 ***
[00:00:00.756,317] <dbg> main: main: Main has started
[00:00:00.764,770] <err> os: ***** USAGE FAULT *****
[00:00:00.772,552] <err> os: Stack overflow (context area not valid)
[00:00:00.781,951] <err> os: r0/a1: 0x0000267e r1/a2: 0x0007b6f7 r2/a3: 0x0000267f
[00:00:00.792,785] <err> os: r3/a4: 0x0007b6f7 r12/ip: 0x00002680 r14/lr: 0x0007b6f7
[00:00:00.803,619] <err> os: xpsr: 0x0007b600
[00:00:00.811,035] <err> os: s[ 0]: 0x00002682 s[ 1]: 0x0007b6f7 s[ 2]: 0x00002683 s[ 3]: 0x0007b6f7
[00:00:00.823,608] <err> os: s[ 4]: 0x00002684 s[ 5]: 0x0007b6f7 s[ 6]: 0x00002685 s[ 7]: 0x0007b6f7
[00:00:00.836,212] <err> os: s[ 8]: 0x00002686 s[ 9]: 0x0007b6f7 s[10]: 0x00002687 s[11]: 0x0007b6f7
[00:00:00.848,815] <err> os: s[12]: 0x00002688 s[13]: 0x0007b6f7 s[14]: 0x00002689 s[15]: 0x0007b6f7
[00:00:00.861,389] <err> os: fpscr: 0x0000268a
[00:00:00.868,774] <err> os: Faulting instruction address (r15/pc): 0x00002681
[00:00:00.878,845] <err> os: >>> ZEPHYR FATAL ERROR 2: Stack overflow on CPU 0
[00:00:00.888,916] <err> os: Current thread: 0x200132b8 (main)
[00:00:00.897,583] <err> os: Halting system
However, if the fault source is more ambiguous, you might need to use addr2line to look up the offending function.
In this example, the LR address is used to find the function stored in the LR register.
That function is the caller in the callstack of the address the PC points to.
<path-to-zephyr-sdk>/arm-zephyr-eabi/bin/arm-zephyr-eabi-addr2line -e build/app/zephyr/zephyr.elf 0x0007b6f7
<path-to-app-dir>/app/src/main.c:771
The template is configured to forward logging in TF-M (Secure image) to UART 0 (application log output). If a secure fault occurs, the fault frame from TF-M will look like this:
uart:~$ FATAL ERROR: SecureFault
Here is some context for the exception:
EXC_RETURN (LR): 0xFFFFFFAD
Exception came from non-secure FW in thread mode.
xPSR: 0x60000007
MSP: 0x20000BF8
PSP: 0x20001CF8
MSP_NS: 0x2002C580
PSP_NS: 0x2002CD40
Exception frame at: 0x2002CD40
R0: 0x00000000
R1: 0x00000000
R2: 0x20013288
R3: 0x00000000
R12: 0x00000000
LR: 0x00044181
PC: 0x0003D7B6
xPSR: 0x61000000
Callee saved register state: R4: 0x2000D414
R5: 0x0008A0B8
R6: 0x00088835
R7: 0x00000000
R8: 0x00000000
R9: 0x00000008
R10: 0x00048A04
R11: 0x00048A04
CFSR: 0x00000000
BFSR: 0x00000000
BFAR: Not Valid
MMFSR: 0x00000000
MMFAR: Not Valid
UFSR: 0x00000000
HFSR: 0x00000000
SFSR: 0x00000048
SFAR: 0x00000000
Here we can again look up the PC and LR in the non-secure image to find the offending function:
~/dev/projects/att/Asset-Tracker-Template/app add-sensor-docs *18 !5 ❯ a2l 0x0003D7B6
/dev/projects/att/Asset-Tracker-Template/app/src/main.c:789
Secure faults will display:
- Fault frame information.
- Non-secure SP and LR registers.
- Violation details.
For more information, refer to the following documentation:
Note
On hardfault, the fault frame might not be printed due to the device rebooting before the log buffer is flushed. To circumvent this issue add the following configurations:
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_RESET_ON_FATAL_ERROR=n
When enabling immediate logging, it might be necessary to increase the stack size of certain threads, because log messages are emitted in the calling thread's context, which increases stack usage.