Hello,
I am trying to understand how the frame pointer works because I want to unwind the stack in a HardFault handler..
I am looking at a dissassembly that runs perfectly for an Atmel ATSAMV71Q21 Cortex M7. It was compiled with GCC in the AtmelStudio 7 IDE. To get the frame pointer, I compiled with -fno-omit-frame-pointer -mtpcs-frame -mtpcs-leaf-frame. It looks like that GCC used register r7 for the frame poitner thumb2 mode.
The function prologue has a push, a sub and an add. I like to confirm if the Cortex M7 superscalar 6 stage pipeline waits because of the dependency on SP at instruction at code address 0x00401dce from the push at 0x00401dcc?
Why doesn't the frame pointer point to something more predictable like the previously pushed r7 frame pointer or the previous SP value before entering the function?
int I2cHW::endTransmission(){ 401dcc: b5b0 push {r4, r5, r7, lr} /* SAVE REGISTERS. The stack moves down by 4*8 = 32 bytes. The frame is pointer is r7, the link register is LR*/ 401dce: b086 sub sp, #24 /*allocate 24 bytes on stack. Lower stack pointer by 24 bytes.*/ 401dd0: af04 add r7, sp, #16 /* frame pointer = stack pointer + 16. Why???*/ .... BODY REMOVED .... if (isAckValid){ 401edc: 7a63 ldrb r3, [r4, #9] 401ede: b11b cbz r3, 401ee8 <_ZN5I2cHW15endTransmissionEv+0x11c> return 0; 401ee0: 2000 movs r0, #0 }else{ return 1; } } 401ee2: 3708 adds r7, #8 /* move frame pointer by 8*/ 401ee4: 46bd mov sp, r7 /* stack poitner = frame pointer*/ 401ee6: bdb0 pop {r4, r5, r7, pc} /* restore registers. move stack up by 4*8= 32 bytes*/ return 1; 401ee8: 2001 movs r0, #1 401eea: e7fa b.n 401ee2 <_ZN5I2cHW15endTransmissionEv+0x116> 401eec: 2044f31c .word 0x2044f31c 401ef0: 00451e94 .word 0x00451e94 401ef4: 00451de4 .word 0x00451de4 401ef8: 00451fac .word 0x00451fac 401efc: 00451e5c .word 0x00451e5c 401f00: 0044e5ed .word 0x0044e5ed 401f04: 00447a71 .word 0x00447a71 401f08: 00440f71 .word 0x00440f71 401f0c: 00451f24 .word 0x00451f24 401f10: 00451fc4 .word 0x00451fc4 401f14: 00405421 .word 0x00405421 401f18: 00452004 .word 0x00452004 401f1c: 00451fec .word 0x00451fec
When you say "unwind the stack in a HF handler", is this in order to infer just the function call stack leading to the fault, or do you want more than that, i.e. values of all local variables in all stack frames leading to the faullt?
If the former, I have done something similar. I extract the stack in use, from the LR register. I then search in memory from that stack top to some upper stack address, perhaps top of ram. I skip the first 32 bytes since those were stacked by the fault handler entry itself (I'm working on Cortex M3). I then just compare each 32 bit word I see against what COULD be pushed LR values. Such a value must be between known bounds on .text section start and end, and must have bit 0 set (i.e.is an odd value). I stop at some count of matches, say 8, since 8 is quite a deep call stack, assuming no recursion. Yes, I may get some false positives, but those are easily weeded out once I consult the .map file for the running binary.
I have some code I can share if that helps.