cancel
Showing results for 
Search instead for 
Did you mean: 

How did I get here? (unwinding the stack)

hbarta2
Associate III
Posted on February 02, 2016 at 16:05

Hi folks,

I'd like to implement some checks in the SysTick handler that will help to determine what code was executing when the SysTick interrupt fired. (What I'm actually doing is counting ticks since last IWDG service and logging information on what was executing when IWDG reset is imminent.) 

I can look at the link register (LR, via __get_LR() in the IAR toolkit) but I'm not sure where to go from there. The problem is that the immediate return address may be deep in some library and I really need to work my way back up (down?) the call stack to see where this was called from my application. It might be one or several calls deep.

I've tried looking at the stack and can see the same value there as is stored in LR but past that point I am unable to relate the callstack displayed by the debugger with other values I see on the stack.

To be honest I'm not even certain what direction the stack builds in. (Yes... one of those stinkin' C programmers who hasn't done any assembler coding since the 8080 and PDP-11 were popular. 😉 )

Any pointers to information I need to walk/unwind the callstack (or better yet, example code in C :D ) would be most welcome.

Thanks!

#stack-callstack-unwind
3 REPLIES 3
Posted on February 02, 2016 at 17:01

The stack grows down, same as x86, etc. 32-bit aligned, executable addresses are ODD, most variables are at 32-bit aligned addresses.

Problem with ARM is that the ABI primarily uses registers, and a function not calling something else doesn't push a return address, just holds LR. Basically the prolog/epilog code isn't anywhere near as clear. There might be options to force some specific prolog code, and stack-checking along with function names. You might want to check what resources your tools provide.

If looking for return addresses, I'd probably just walk back up the stack, looking for ODD addresses in the 0x08000000..0x080FFFFF range (or whatever the flash size is), this could then be correlated against functions in the .MAP, or symbols in the .ELF/AXF and express as foo+0x124, bar+0x842. Some tools scope that with the size of the functions code, so you can perceive a percentage depth into your executing code. ie if you have a memcpy() at the very end.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
hbarta2
Associate III
Posted on February 02, 2016 at 21:21

Many thanks for the info. I saw that return addresses appeared to be odd and that led me to think I had something wrong. (I find that odd. <groan> 😉 )

carl2399
Associate II
Posted on February 03, 2016 at 02:27

Groan.

Indeed, if you find any addresses that are not 'odd' your code will crash. STM32 is completely 'odd'.

That 'odd' bit is how the ARM core knows it's running the extended (or one should say truncated) instruction set (THUMB 2 in the case of the STM32). If the address was 'even', then the processor core would expect to run 32-bit ARM assembly instructions - which aren't supported on the STM32.

This extended from the branch instructions, and onto the call stack - as you've discovered.