I am trying to retrieve the return address of an IRQ handler in my code. My aim is to save the value of the PC just before the watchdog timer expires and before the reset for debug purposes, using WDT_IRQHandler(). I am also testing this approach with other IRQs to check if I grasped the idea. But it seems I haven't.
I have read the documentation available. I understood that when an exception happens, 8 registers are pushed to the stack: R0, R1, R2, R3, R12, LR, PC and XPSR.
I have also read that the stack is automatically double word aligned. So in my mind, retrieving the return address is as easy as:
- retrieve the sp address with __builtin_frame_address(0);
- add to it the offset of the stacked PC (0x18), and read the value, which supposedly is the value that will be restored to the PC when the handler returns.
Checking with the debugger attached, this seems not the case, the content at that memory address doesn't always point to a flash area, or even to a valid area, and in any case it is never the value that PC will assume after the POP instruction.
The code works fine, so I think it's a problem I have in understanding how it works.
If I check the disassembly, in some IRQs a constant is added to the sp before POPping (?)
00001924: 0x000009b0 ...TE_IRQHandler+280 add sp, #36 ; 0x24
00001926: 0x0000f0bd ...TE_IRQHandler+282 pop {r4, r5, r6, r7, pc}
In other IRQs this doesn't happen.
I understand that it can happen that more registers are pushed to the stack, so how can I be sure at which offset to retrieve the PC?
If I check the memory dump around the SP when the code is still in the IRQ handler, I can spot the return address, but it's always at a weird location, with a negative offset compared to the SP. I can't understand how to obtain the right address.