Reputation: 665
I am developing an embedded application on an STM32F427. This MCU is using the ARM Cortex M4F core.
I am currently working on a debug tool, where it keeps a snapshot of the CPU state during a crash for later analysis. Specifically I am interested on what was the last instuction executed, before the error handler was called.
Initially I though that I can just log the value of the LR, as after the handler returns this is going to be the new PC. But I realized that this is not always the case.
I found out that in some cases indeed my handler works. The compiler calls the function with:
bl 0x60238
And then, the function returns with:
bx lr
Just as I was expecting.
However, in some other cases the compiler calls the function with:
bl 0x60250
Then the function performs:
push {r7, lr}
And then it returns with:
pop {r7, pc}
In the later case, since the LR may be used for another nested function call, the contents are overwritten and I lose the needed information.
Why is GCC producing such code, and why isn't code generated for a function call always the same? Taking the above into consideration, is there any other way to achieve what I want?
EDIT In my case a "system crash" may have numerous causes. This is why I am trying to read the actual register contents, providing a generic solution in my problem. Same cases may be:
Upvotes: 0
Views: 276
Reputation: 844
There are numerous optimizations a compiler can make to save code size / improve speed. A class of these optimizations center around whether or not functions are leaf functions. You could go through the list of compiler flag optimizations and try to disable some of the optimizations that will impact how branches are generated (such -fno-optimize-sibling-calls) but then you would impact the performance and unnecessarily bloat the code size.
As others pointed out, what you mean by "crash" is also important. If you have asserts in your code and would like to save state whenever they are hit, there's a solution presented here which captures the pc & lr.
A Cortex-M4F can also experience a hardware "fault" for many different scenarios (bad bus access, unaligned memory access, divide by 0, etc). When this happens an interrupt handler will be invoked and you cannot just read the $lr
or $pc
to figure out what caused the crash. There is a section in this article which has a walk through of how you can instrument your code to recover the instruction that triggered the fault in the first place.
Note that even if you have the $pc
& $lr
, it can still be very difficult to pinpoint what callchain actually caused the crash. In these situations you want to save off a couple hundred bytes from the active stack along with register state. Using the debug information emitted in the ELF (as long as you compile with -g
) you can recover the stack trace with multiple frames that led to the crash.
Upvotes: 4