I mentioned that ARM toolchains could generate different function prologs. Actually, i saw two obj files (vmlinux) with completely different function prologs:
The first case looks like:
push {some registers maybe, fp, lr} (lr ommited in leaf function)
The second case looks like:
push {some registers maybe, fp, sp, lr, pc} (i can confuse the order)
So as i see the second one pushes additionally pc and sp. Also i saw some comments in crash utility (kdump project) where was stated, that kernel stackframe should have format {..., fp, sp, lr, pc} what confuse me more, because i see that in some cases it is not true.
1.) Am i right about that some gcc extra flags are needed for pushing additionally pc and sp in function prolog? If yes what are they?.
2.) What is this used for? Basically, as i understand i can unwind stack with FP and LR only, why do i need this additional values?
3.) If this things dealth nothing with compilation flags - how can i force generation of this extended function prolog and again what is the purpose?
Thank you.
There are many gcc options that will affect stack frames (
-march
,-mtune
, etc may affect the instructions used for instance). In your case, it was-mapcs-frame
. Also,-fomit-frame-pointer
will remove frames from leaf functions. Several static functions maybe merged together into a single generated function further reducing the number of frames. The APCS can cause slightly slower code but is needed for stack traces.All registers that are not parameters (r0-r3) need to be saved as they need to be restored when returning to the caller. The compiler will allocate additional locals on the stack so
sp
will almost always change whenfp
changes. For why thepc
is stored, see below.It is compiler flags as you had guessed.
A typical save is
stm sp!, {fp, ip, lr, pc}
and a restore ofldm sp, {fp, sp, lr}
. This is correct if you examine the ABI/APCS documents. Note, there is no '!' to try and fix the stack. It is loaded explicitly from the storedip
value.Also, the saved
pc
is not used in the epilogue. It is just discarded data on the stack. So why do this? Exception handlers (interrupts, signals or C++ exceptions) and other stack trace mechanisms want to know who saved a frame. The ARM always only have one function prologue (one point of entry). However, there are multiple exits. In some cases, a return likereturn function();
may actually turn into ab function
in the maybe more stuff here. This is known as a tail call. Also when a leaf function is called in the middle of a routine and an exception occurs, it will see aPC
range of leaf, but the leaf may have no call frame. By saving thepc
, the call frame can be examined when an exception occurs in leaf to know who really saved the stack. Tables ofpc
versus destructor, etc. maybe stored to allow objects to be freed or to figure out how to call a signal handler. The extrapc
is just plain nice when tracing a stack and the operation is almost free due to pipe lining.See also: ARM Link and frame register question for how the compiler uses these registers.