ARM函数切换栈帧分析

HarderHeng Lv5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func(int, int):
push {r7}
sub sp, sp, #20
add r7, sp, #0
str r0, [r7, #4]
str r1, [r7]
ldr r3, [r7, #4]
ldr r2, [r7]
mul r3, r2, r3
ldr r2, [r7]
add r3, r3, r2
str r3, [r7, #12]
ldr r2, [r7, #12]
ldr r3, [r7, #4]
add r3, r3, r2
str r3, [r7, #12]
ldr r3, [r7, #12]
mov r0, r3
adds r7, r7, #20
mov sp, r7
ldr r7, [sp], #4
bx lr
main:
push {r7, lr}
sub sp, sp, #16
add r7, sp, #0
movs r3, #1
str r3, [r7, #12]
movs r3, #2
str r3, [r7, #8]
ldr r1, [r7, #8]
ldr r0, [r7, #12]
bl func(int, int)
str r0, [r7, #4]
  1. lr寄存器和fp(r7)寄存器压栈,lr寄存器现在是main函数的返回地址,fp寄存器是上一个函数的栈帧基址。
  2. 中开辟一块空间用来存放局部变量传参变量,将sp指针的值赋给fp寄存器,作为main函数的栈帧基址。此处的fp寄存器,只保存局部变量,如果是传参变量则相当于是另一个函数的栈帧。
  3. 将局部变量存储到,以fp寄存器为基址的栈帧中。
  4. ARM规定默认传参放在r0,r1,r2,r3寄存器中,多于4个的参数会被压入栈中。
  5. 跳转到调用的函数。
  6. 完成函数调用。
  7. 恢复sp指针到函数调用前的位置,恢复fp寄存器,跳转到lr寄存器保存的位置。

Note

不同的编译器,不同的优化等级都会有不同的栈帧操作。

但是很多时候在caller中都不需要保存r0-r3,因为变量都直接存放在栈中了,r4-r11寄存器在简单逻辑中很少使用,具体如何操作都依赖于编译器的优化。

但是不管如何,在直接编写汇编代码时,还是需要根据AAPCS进行正确的寄存器保存。

总的来说,进行函数调用时,栈帧的操作大致为:

  • 保存fp栈帧基址和lr链接寄存器。
  • 局部变量保存到栈中。
  • 进行函数操作。
  • 还原fp寄存器。
  • 跳转回调用函数。
  • Title: ARM函数切换栈帧分析
  • Author: HarderHeng
  • Created at : 2025-01-20 13:47:02
  • Updated at : 2025-03-18 14:28:16
  • Link: https://harderheng.life/2025/01/20/ARM函数切换栈帧分析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
ARM函数切换栈帧分析