问题
While trying to analyze a simple Assembly file generated through the msp430-gcc, I stumbled upon a set of instructions that I don't understand dealing with the frame pointer and the MSP430's stack pointer.
C Program:
#include "msp430g2553.h"
int main()
{
int i;
for(i = 0; i < 3; i++);
}
Assembly minus directives:
main:
mov r1, r4 ;Stores address of stack pointer in r4(frame pointer)
add #2, r4 ; ?
sub #2, r1 ; subtract 2 to allocate int i
mov #0, -4(r4) ; assign 0 to i
jmp .L2 ; start loop
.L3:
add #1, -4(r4) ; Adds one to int i
.L2:
cmp #3, -4(r4) ; Compare to #3
jl .L3 ; jump to .L3 if true
add #2, r1 ; deallocate int i
.Lfe1:
.size main,.Lfe1-main
I tried commenting the code to trace the program's execution, but I don't understand the line add #2, r4
. What exactly is happening here, and why is int i
being referenced at -4(r4)
?
回答1:
Normally the first thing you'll do in a function is:
push r4
to save the current frame pointer on the stack, so you can set a new frame pointer for the function you're about to call, and then restore the old one afterwards. push
will automatically decrement the stack pointer by 2, so when you then do:
mov r1, r4
the address pushed on to r4
will be above the value you just pushed on the stack ("above" here in the sense that the stack grows downwards - it's actually below in terms of numeric memory addresses). You'll want the frame pointer to actually point below the value you just pushed onto the stack, so you increment it by two to achieve this with:
add #2, r4
Because main()
is the first function executed, you don't have an existing frame pointer to save, so what you're seeing here is the mov
and the add
in the absence of the push
.
It'll make more sense when you make an actual function call, and you'll see the whole thing:
push r4
mov r1, r4
add #2, r4
After you've done this, -2(r4)
will refer to the previous value of the frame pointer that you just pushed onto the stack, and since you haven't added two to the value of the stack pointer, -2(r4)
will equal that, too.
When you now allocate 16 bits for your local variable i
, you'll have to subtract 2
from the stack pointer to make room for it, and the address of i
will therefore be -4(r4)
.
Example
As an example, suppose the stack pointer contains 0x200
and the frame pointer contains 0x202
, and you then want to call a function. You start off with a stack like this:
r4 --> 0x202 ---------------------
<empty>
r1 --> 0x200 ---------------------
After you return from your function, you're going to want to restore the current value of the frame pointer, so the first thing you do is push it onto the stack to save it. After push r4
, the value 0x202
therefore gets pushed onto memory location 0x200
(i.e. the top of the stack pointed to by r1
), and the stack pointer gets decremented by 2
to make room for it, so you get:
r4 --> 0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
Having pushed the previous value of the frame pointer onto the stack, you now want to set the current value of the frame pointer to the base of your current stack frame, so you start this off by moving the new stack pointer into r4
, and you get:
0x202 ---------------------
<empty>
0x200 ---------------------
0x202
r1 == r4 --> 0x1FE ---------------------
The old value of the frame pointer is the first thing in your current stack frame, so you want r4
to point before that, not after it. To do this you add 2
to r4
, and you get:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
The position you're in now is that r4
points to the bottom of your stack frame, and r1
points to the top of it, which is where you want to be. The only thing actually in your current stack frame at this point is the previous value of the frame pointer that you pushed onto it at the start of the function.
Then you decrement the stack pointer by 2
to make room for your new local variable, and you end up with:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
0x1FE ---------------------
<uninitialized i>
r1 --> 0x1FC ---------------------
and you can see that i
is stored at 0x1FC
, which is -4(r4)
. Once again you're in the position where r4
points to the bottom of your stack frame, and r1
points to the top of it, but now you have two 16 bit values in your current stack frame, so the two pointers are 4 bytes apart.
When you're ready to return at the end of your function, you'll add #2, r1
to "deallocate" the memory for your local variable i
. This will give you:
0x202 ---------------------
<empty>
r4 --> 0x200 ---------------------
0x202
r1 --> 0x1FE ---------------------
You'll then pop r4
, which will pop the last value off the stack (which is now 0x202
, the original value of the frame pointer), put it into r4
, and increment the stack pointer by 2
to reflect that that value has been removed from the stack, which will leave you at:
r4 --> 0x202 ---------------------
<empty>
r1 --> 0x200 ---------------------
which is right where you started, and you've cleaned up the stack perfectly after your function call.
Note this is slightly simplified since when you do make a function call, the program counter is also going to get pushed onto the stack, and then popped and restored again when you return, and the above example doesn't show that, but exactly the same kind of thing is going on there.
来源:https://stackoverflow.com/questions/26374287/msp430-assembly-stack-pointer-behavior