EDA365电子论坛网

标题: armv7指令集 C函数调用 堆栈指针SP的变化 [打印本页]

作者: ubeautqq    时间: 2021-2-2 17:58
标题: armv7指令集 C函数调用 堆栈指针SP的变化
2 z% Q" o  V% Y$ A9 X# W
首先说明一下,arm中函数调用不同的编译器可能差别很大,即使都是arm-linux的交叉编译器,也有差别,有的编译器把r7寄存器作为栈帧寄存器(fp),有的把r11寄存器作为栈帧指针(fp),例如arm-linux-gnueabihf-gcc用的r7和arm-linux-gnueabi-gcc用的r11,另外在函数执行开头的处理也不一样
7 i# Y9 X- T- C+ F4 D
% \1 W. s3 r, y' v- O5 M1 f1. arm-linux-gnueabihf-gcc编译器先给函数中变量分配栈空间,然后将fp和sp指向栈顶
+ D( b- W! K/ ]; ^" [' W& P+ j& t/ r, @* `. X6 H; m' M
2. arm-linux-gnueabi-gcc编译器先让fp和sp指向栈顶(栈底),然后再给函数中的变量分配栈空间,之后sp指向新的栈顶,而fp指向的老的栈顶变成了函数的栈底。
% l% I; S2 c6 g: `% _2 Q) O, u% d! u( e  Z) u, R
个人感觉第二种比较好理解,但是总而言之,每次函数开始执行都需要保存fp指针到栈空间,这里的被保存fp指针是上一个函数的数据(一般是栈底数据),然后立即将本函数的栈底数据保存到fp指针, 函数调用结束,根据fp的数据恢复sp指针,从而释放结束函数的栈空间。
* q& R1 D& D. |- p# S4 n* ]# R7 h$ |. _& B0 [) G9 z8 F
这么看来,函数调用需要入栈的数据只有前一个函数的fp指针,然后跳转的时候会将跳转指令bl的下一条指令地址保存到 lr寄存器,方便函数调用结束后返回。当然如果函数入参很多,寄存器不够用,这些参数同样需要入栈。
# B6 {8 F, i0 W2 D
' Y0 C3 c$ ~4 N) j$ D下面贴出两种编译器的C代码以及反汇编:1 L4 p# ^! d& ~  ]* Q

, S, c4 @9 e/ U! V+ q( G下面我们看C程序:* B" L5 V: E* k2 ]. f

) E7 ?- p  S/ ^, C* w+ j7 q6 y& p! y2 n8 w7 F9 [- Q

! n( O" C' _' y, y$ l使用的编译器是arm-linux-gnueabihf-gcc,针对的芯片是I.Mx6ull,对应的反汇编代码:) ~4 s! b3 l" E, i2 ]
' Z7 j% F0 F2 g3 g
    ) L9 L: Q0 H6 d7 |4 D4 R) ]
  D5 Y$ E+ g, d0 D
使用arm-linux-gnueabi-gcc编译器对应的反汇编代码:
1 Z9 T2 B4 V* j  Y0 V" P
1 S; w4 X3 {" g& k) T$ P, @! M- }5 I5 X$ i; ~
, {- Q6 I7 ^# Z+ n0 [: p
  个人感觉参考第二份反汇编代码比较好理解堆栈的变化过程,其实函数调用就两个要点:
: S$ c2 e2 N# h0 y) _+ _. M/ r4 `" S8 Z; c
1. 要保存好函数自己的栈底地址,保证函数结束栈空间能够正常释放,但只有一个fp指针,所以使用fp指针存储本函数的栈底地址之前,要将上一个函数的栈底地址入栈也就是,现将fp入栈,然后再使用fp保存新的栈底指针。
; V# k2 N& W6 q# ~- V. M
) D" e, r& D2 F: m, F2. 使用bl指令,保证函数调用结束能跳回。将bl指令下一条地址保存到lr寄存器中。
作者: StepPeng33    时间: 2021-2-2 18:06
学习                              




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2