TA的每日心情 | 衰 2019-11-19 15:32 |
---|
签到天数: 1 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
]0 P& A8 ]- k; i4 b
首先说明一下,ARM中函数调用不同的编译器可能差别很大,即使都是arm-linux的交叉编译器,也有差别,有的编译器把r7寄存器作为栈帧寄存器(fp),有的把r11寄存器作为栈帧指针(fp),例如arm-linux-gnueabihf-gcc用的r7和arm-linux-gnueabi-gcc用的r11,另外在函数执行开头的处理也不一样% V# T S) B, m2 A& o1 u
8 h. }- M3 ~- m8 ]1. arm-linux-gnueabihf-gcc编译器先给函数中变量分配栈空间,然后将fp和sp指向栈顶
7 _# B% T1 n# y- V1 i9 Q6 y2 W) y, @2 ?
6 ^2 I9 K7 r* x7 T2 z$ J) p- {) _, o2. arm-linux-gnueabi-gcc编译器先让fp和sp指向栈顶(栈底),然后再给函数中的变量分配栈空间,之后sp指向新的栈顶,而fp指向的老的栈顶变成了函数的栈底。
2 @7 t1 b# U) R7 m& M$ H
; l; `4 G; p: Q& \& i个人感觉第二种比较好理解,但是总而言之,每次函数开始执行都需要保存fp指针到栈空间,这里的被保存fp指针是上一个函数的数据(一般是栈底数据),然后立即将本函数的栈底数据保存到fp指针, 函数调用结束,根据fp的数据恢复sp指针,从而释放结束函数的栈空间。7 O( ]! z# c% j6 f( k
. d, j* g+ X; n/ W7 V这么看来,函数调用需要入栈的数据只有前一个函数的fp指针,然后跳转的时候会将跳转指令bl的下一条指令地址保存到 lr寄存器,方便函数调用结束后返回。当然如果函数入参很多,寄存器不够用,这些参数同样需要入栈。
# ]8 v e% @' R( U1 V4 `! }8 F. m0 k# _; Q9 R
下面贴出两种编译器的C代码以及反汇编:; J% i( W: S5 C3 v" s+ C1 r
. M8 n( i: x0 r5 Y, ~下面我们看C程序:$ a, ]( R" H5 H7 s7 A
$ I6 T* S; Z* j0 x4 v5 _$ t% |7 G- int m = 8;
- int fun(int a,int b)
- {
- int c = 0;
- c = a + b;
- return c;
- }
- int main()
- {
- int i = 4;
- int j = 5;
- m = fun(i, j);
- return 0;
- }
- j: J- w, i% H ) t; W5 o0 d9 b3 \9 s
2 D* W7 D; J6 @* y- n i. v使用的编译器是arm-linux-gnueabihf-gcc,针对的芯片是I.Mx6ull,对应的反汇编代码:1 E& n1 d, z, l3 y
& C. s6 W+ ?2 @# x$ f# D0 B z- w7 {
- Disassembly of section .text:
- 00010094 <fun>:
- 10094: b480 push {r7}
- 10096: b085 sub sp, #20
- 10098: af00 add r7, sp, #0
- 1009a: 6078 str r0, [r7, #4]
- 1009c: 6039 str r1, [r7, #0]
- 1009e: 2300 movs r3, #0
- 100a0: 60fb str r3, [r7, #12]
- 100a2: 687a ldr r2, [r7, #4]
- 100a4: 683b ldr r3, [r7, #0]
- 100a6: 4413 add r3, r2
- 100a8: 60fb str r3, [r7, #12]
- 100aa: 68fb ldr r3, [r7, #12]
- 100ac: 4618 mov r0, r3
- 100ae: 3714 adds r7, #20
- 100b0: 46bd mov sp, r7
- 100b2: f85d 7b04 ldr.w r7, [sp], #4
- 100b6: 4770 bx lr
- 000100b8 <main>:
- 100b8: b580 push {r7, lr}
- 100ba: b082 sub sp, #8
- 100bc: af00 add r7, sp, #0
- 100be: 2304 movs r3, #4
- 100c0: 607b str r3, [r7, #4]
- 100c2: 2305 movs r3, #5
- 100c4: 603b str r3, [r7, #0]
- 100c6: 6839 ldr r1, [r7, #0]
- 100c8: 6878 ldr r0, [r7, #4]
- 100ca: f7ff ffe3 bl 10094 <fun>
- 100ce: 4602 mov r2, r0
- 100d0: f240 03e4 movw r3, #228 ; 0xe4 全局变量m的地址
- 100d4: f2c0 0302 movt r3, #2
- 100d8: 601a str r2, [r3, #0]
- 100da: 2300 movs r3, #0
- 100dc: 4618 mov r0, r3
- 100de: 3708 adds r7, #8
- 100e0: 46bd mov sp, r7
- 100e2: bd80 pop {r7, pc}
- Disassembly of section .data:
- 000200e4 <m>:
- 200e4: 00000008 andeq r0, r0, r8 /* 这里00000008是全局变量m的值,被解读成andeq指令了 */
0 |% Y! r+ x% \- T; q R ; }) n7 F2 }6 z5 `; S0 Z7 p
0 U3 @3 I2 ~* r; b' H+ r使用arm-linux-gnueabi-gcc编译器对应的反汇编代码:' I z% J) p" [6 T7 y0 N% J7 @
1 G2 y' c# A% f# T Z
- 00010400 <fun>:
- 10400: e52db004 push {fp} ; (str fp, [sp, #-4]!)
- 10404: e28db000 add fp, sp, #0
- 10408: e24dd014 sub sp, sp, #20
- 1040c: e50b0010 str r0, [fp, #-16]
- 10410: e50b1014 str r1, [fp, #-20] ; 0xffffffec
- 10414: e3a03000 mov r3, #0
- 10418: e50b3008 str r3, [fp, #-8]
- 1041c: e51b2010 ldr r2, [fp, #-16]
- 10420: e51b3014 ldr r3, [fp, #-20] ; 0xffffffec
- 10424: e0823003 add r3, r2, r3
- 10428: e50b3008 str r3, [fp, #-8]
- 1042c: e51b3008 ldr r3, [fp, #-8]
- 10430: e1a00003 mov r0, r3
- 10434: e24bd000 sub sp, fp, #0
- 10438: e49db004 pop {fp} ; (ldr fp, [sp], #4)
- 1043c: e12fff1e bx lr
- 00010440 <main>:
- 10440: e92d4800 push {fp, lr}
- 10444: e28db004 add fp, sp, #4
- 10448: e24dd008 sub sp, sp, #8
- 1044c: e3a03004 mov r3, #4
- 10450: e50b300c str r3, [fp, #-12]
- 10454: e3a03005 mov r3, #5
- 10458: e50b3008 str r3, [fp, #-8]
- 1045c: e51b1008 ldr r1, [fp, #-8]
- 10460: e51b000c ldr r0, [fp, #-12]
- 10464: ebffffe5 bl 10400 <fun>
- 10468: e1a02000 mov r2, r0
- 1046c: e59f3010 ldr r3, [pc, #16] ; 10484 <main+0x44> 全局变量m的地址
- 10470: e5832000 str r2, [r3]
- 10474: e3a03000 mov r3, #0
- 10478: e1a00003 mov r0, r3
- 1047c: e24bd004 sub sp, fp, #4
- 10480: e8bd8800 pop {fp, pc}
- 10484: 00021024 andeq r1, r2, r4, lsr #32
- y& s2 H/ T+ V$ O8 r3 B ! Q0 t! M2 \% |6 x
2 I% s8 f5 y/ }1 @) _ 个人感觉参考第二份反汇编代码比较好理解堆栈的变化过程,其实函数调用就两个要点:: d4 f' D+ p3 S' ?8 U2 g8 |2 R
: Z) j2 h8 K) u( b
1. 要保存好函数自己的栈底地址,保证函数结束栈空间能够正常释放,但只有一个fp指针,所以使用fp指针存储本函数的栈底地址之前,要将上一个函数的栈底地址入栈也就是,现将fp入栈,然后再使用fp保存新的栈底指针。/ M2 ]! ~- s7 N
' v* @3 T: o+ _ {8 h2. 使用bl指令,保证函数调用结束能跳回。将bl指令下一条地址保存到lr寄存器中。 |
|