ARM64函数调用流程分析
- 1 ARM64 函数调用实例
- 2 对应代码的分析
- 2.1 main函数及其对应的汇编程序
- 2.1.1 main的C代码实现
- 2.1.2 main函数对应汇编及其分析
- 2.1.3 执行完成之后栈的存放情况
- 2.2 test_fun_a函数及其对应的汇编程序
- 2.2.1 test_fun_a函数的C实现
- 2.2.2 test_fun_a函数对应汇编及其分析
- 2.2.3 执行完成之后栈帧的使用情况
- 2.3 test_fun_b函数及其对应的汇编程序
- 2.3.1 test_func_b函数的C实现
- 2.3.2 test_fun_b函数对应汇编及其分析
- 2.3.3 执行完成之后栈帧的使用情况
ARM64 程序调用标准
1 ARM64 函数调用实例
下图是介绍一个简单函数调用的示例,在该示例中简单介绍了栈的使用。
2 对应代码的分析
2.1 main函数及其对应的汇编程序
2.1.1 main的C代码实现
int main(void)
{
long a = 1;
long b = 2;
printf("The current function is %s a:%ld b:%ld\r\n", __func__, a, b);
test_fun_a(a, b, 0, 1);
a = a + b;
b = a + b;
return 0;
}
2.1.2 main函数对应汇编及其分析
0000000000000114 <main>:
main函数的入口114: a9be7bfd stp x29, x30, [sp, #-32]!
将sp = sp - 32,为main函数开一个32Byte的栈空间,然后将x29(FP),X30(LR)寄存器的值存放在SP和SP + 8的位置处。118: 910003fd mov x29, sp
将SP寄存器的值存放到X29(FP)寄存器中,即FP寄存器指向当前main函数的栈顶。11c: d2800020 mov x0, #0x1 // #1
将局部变量a的值保存到x0寄存器中120: f9000be0 str x0, [sp, #16]
将局部变量a的值保存到sp + 16的位置处。124: d2800040 mov x0, #0x2 // #2
将局部变量b的值保存到x0寄存器中128: f9000fe0 str x0, [sp, #24]
将局部变量b的值保存到sp + 24栈内存处12c: f9400fe3 ldr x3, [sp, #24]
从栈中加载局部变量b的值到x3寄存器中130: f9400be2 ldr x2, [sp, #16]
从栈中加载局部变量a的值到x2寄存器中134: 90000000 adrp x0, 0 <test_fun_b>
加载test_func_b函数的地址到x0寄存器中138: 91000001 add x1, x0, #0x0
将x0 + 0的值保存到x1寄存器中13c: 90000000 adrp x0, 0 <test_fun_b>
加载test_func_b函数的地址到x0寄存器中140: 91000000 add x0, x0, #0x0
将x0 + 0的值保存到x0寄存器中144: 94000000 bl 0 <printf>
调用函数printf148: d2800023 mov x3, #0x1 // #1
将1保存到x3寄存器中,作为调用test_fun_a函数的第4个参数14c: d2800002 mov x2, #0x0 // #0
将0保存到寄存器x2中,作为调用test_fun_a函数的第3个参数150: f9400fe1 ldr x1, [sp, #24]
从栈中取出局部变量b的值,放到x1寄存器中,作为调用test_fun_a的第2个参数154: f9400be0 ldr x0, [sp, #16]
从栈中取出局部变量a的值,放到x0寄存器中,作为调用test_fun_a的第1个参数158: 94000000 bl 80 <test_fun_a>
调用test_func_a函数,其参数分别为前面的x0 ~ x3寄存器的值15c: f9400be1 ldr x1, [sp, #16]
加载局部变量a的值到x1寄存器160: f9400fe0 ldr x0, [sp, #24]
加载局部变量b的值到x0寄存器164: 8b000020 add x0, x1, x0
a = a + b168: f9000be0 str x0, [sp, #16]
将计算到的局部变量a的值重新存到栈中16c: f9400fe1 ldr x1, [sp, #24]
从栈中取出局部变量b的值170: f9400be0 ldr x0, [sp, #16]
从栈中取出局部变量a的值174: 8b000020 add x0, x1, x0
b = a + b178: f9000fe0 str x0, [sp, #24]
将新计算得到的局部变量b的值重新保存到栈中17c: 52800000 mov w0, #0x0 // #0
给w0寄存器赋值为0,该操作是用在ret指令执行时,返回0值。180: a8c27bfd ldp x29, x30, [sp], #32
恢复x29(FP)和X30(LR)的值,同时SP = SP + 32184: d65f03c0 ret
返回调用的指令,该指令执行的时候会返回lr寄存器指向的函数中。
0000000000000114 <main>:
114: a9be7bfd stp x29, x30, [sp, #-32]!
118: 910003fd mov x29, sp
11c: d2800020 mov x0, #0x1 // #1
120: f9000be0 str x0, [sp, #16]
124: d2800040 mov x0, #0x2 // #2
128: f9000fe0 str x0, [sp, #24]
12c: f9400fe3 ldr x3, [sp, #24]
130: f9400be2 ldr x2, [sp, #16]
134: 90000000 adrp x0, 0 <test_fun_b>
138: 91000001 add x1, x0, #0x0
13c: 90000000 adrp x0, 0 <test_fun_b>
140: 91000000 add x0, x0, #0x0
144: 94000000 bl 0 <printf>
148: d2800023 mov x3, #0x1 // #1
14c: d2800002 mov x2, #0x0 // #0
150: f9400fe1 ldr x1, [sp, #24]
154: f9400be0 ldr x0, [sp, #16]
158: 94000000 bl 80 <test_fun_a>
15c: f9400be1 ldr x1, [sp, #16]
160: f9400fe0 ldr x0, [sp, #24]
164: 8b000020 add x0, x1, x0
168: f9000be0 str x0, [sp, #16]
16c: f9400fe1 ldr x1, [sp, #24]
170: f9400be0 ldr x0, [sp, #16]
174: 8b000020 add x0, x1, x0
178: f9000fe0 str x0, [sp, #24]
17c: 52800000 mov w0, #0x0 // #0
180: a8c27bfd ldp x29, x30, [sp], #32
184: d65f03c0 ret
2.1.3 执行完成之后栈的存放情况
2.2 test_fun_a函数及其对应的汇编程序
2.2.1 test_fun_a函数的C实现
void test_fun_a(long m, long n, long x, long y)
{
long b = 2;
long c = 3;
printf("The current function is %s b:%ld c:%ld\r\n", __func__, b, c);
test_fun_b(b, c, 0, 2);
b = b + c + m;
c = b + c + n;
}
2.2.2 test_fun_a函数对应汇编及其分析
0000000000000080 <test_fun_a>:
test_fun_a函数的入口80: a9bc7bfd stp x29, x30, [sp, #-64]!
为test_fun_a函数开栈64B,同时把X29(FP),X30(LR)保存到栈顶sp和sp + 8的栈内存位置处84: 910003fd mov x29, sp
将sp保存到x29(FP)寄存器中,相当于FP指向栈的栈顶88: f90017e0 str x0, [sp, #40]
将参数1保存到栈的sp + 40栈内存位置处8c: f90013e1 str x1, [sp, #32]
将参数2保存到栈sp + 32的栈内存位置处90: f9000fe2 str x2, [sp, #24]
将参数3保存到栈sp + 24栈内存位置处94: f9000be3 str x3, [sp, #16]
将参数4保存到栈sp + 16栈内存位置处98: d2800040 mov x0, #0x2 // #2
将test_fun_a函数的局部变量b保存到x0寄存器中9c: f9001be0 str x0, [sp, #48]
将test_fun_a函数的局部变量b保存到sp + 48栈内存位置处a0: d2800060 mov x0, #0x3 // #3
将test_fun_a函数的局部变量c保存到x1寄存器中a4: f9001fe0 str x0, [sp, #56]
将test_fun_a函数的局部变量c保存到栈sp + 56栈内存位置处a8: f9401fe3 ldr x3, [sp, #56]
从栈中取出局部变量c的值放到x3寄存器中ac: f9401be2 ldr x2, [sp, #48]
从栈中取出局部变量b的值放到x2寄存器中b0: 90000000 adrp x0, 0 <test_fun_b>
将test_fun_b函数的地址加载到x0寄存器中b4: 91000001 add x1, x0, #0x0
x1 = x0 + 0,其中x0保存的是test_fun_b的起始地址b8: 90000000 adrp x0, 0 <test_fun_b>
将test_fun_b函数的地址加载到x0寄存器中bc: 91000000 add x0, x0, #0x0
x0 = x0 + 0,其中x0保存的是test_fun_b的起始地址c0: 94000000 bl 0 <printf>
调用函数printfc4: d2800043 mov x3, #0x2 // #2
给x3寄存器赋值为2,作为test_fun_b的第4个参数c8: d2800002 mov x2, #0x0 // #0
给x2寄存器赋值为0,作为test_func_b的第三个参数cc: f9401fe1 ldr x1, [sp, #56]
从栈中取出局部变量c,存放到x1寄存器,作为test_fun_b的第二个参数d0: f9401be0 ldr x0, [sp, #48]
从栈中取出局部变量b,存放到x0寄存器,作为test_fun_b的第一个参数d4: 94000000 bl 0 <test_fun_b>
调用test_fun_b函数,x0 ~ x3作为test_fun_a的四个参数d8: f9401be1 ldr x1, [sp, #48]
从栈中取出test_fun_a的局部变量b,放到x1寄存器中dc: f9401fe0 ldr x0, [sp, #56]
从栈中取出test_fun_a的局部变量c,放到x0寄存器中e0: 8b000020 add x0, x1, x0
c = b + c,将c的结果保存到x0寄存器中。e4: f94017e1 ldr x1, [sp, #40]
从栈中取出调用test_fun_a时传入的第1个参数取出,放到x1寄存器中e8: 8b000020 add x0, x1, x0
c = c + m,将计算的结果放到x0寄存器中ec: f9001be0 str x0, [sp, #48]
将计算的结果x0的值重新保存到局部变量b的栈内存位置处f0: f9401be1 ldr x1, [sp, #48]
从栈中取出局部变量b的值放到x1寄存器中。f4: f9401fe0 ldr x0, [sp, #56]
从栈中取出局部变量x的值放到x0寄存器中f8: 8b000020 add x0, x1, x0
c = b + cfc: f94013e1 ldr x1, [sp, #32]
从栈中取出调用test_fun_a函数时传入的第2个参数放到x1寄存器中100: 8b000020 add x0, x1, x0
c = c + n,计算的结果放到x0寄存器中104: f9001fe0 str x0, [sp, #56]
将计算的新值存放到原局部变量c的栈内存位置处108: d503201f nop
空操作10c: a8c47bfd ldp x29, x30, [sp], #64
恢复X29(FP),X30(LR)寄存器的值,同时sp = sp + 64栈指针寄存器110: d65f03c0 ret
返回X30(LR)寄存器保存的返回函数处
0000000000000080 <test_fun_a>:
80: a9bc7bfd stp x29, x30, [sp, #-64]!
84: 910003fd mov x29, sp
88: f90017e0 str x0, [sp, #40]
8c: f90013e1 str x1, [sp, #32]
90: f9000fe2 str x2, [sp, #24]
94: f9000be3 str x3, [sp, #16]
98: d2800040 mov x0, #0x2 // #2
9c: f9001be0 str x0, [sp, #48]
a0: d2800060 mov x0, #0x3 // #3
a4: f9001fe0 str x0, [sp, #56]
a8: f9401fe3 ldr x3, [sp, #56]
ac: f9401be2 ldr x2, [sp, #48]
b0: 90000000 adrp x0, 0 <test_fun_b>
b4: 91000001 add x1, x0, #0x0
b8: 90000000 adrp x0, 0 <test_fun_b>
bc: 91000000 add x0, x0, #0x0
c0: 94000000 bl 0 <printf>
c4: d2800043 mov x3, #0x2 // #2
c8: d2800002 mov x2, #0x0 // #0
cc: f9401fe1 ldr x1, [sp, #56]
d0: f9401be0 ldr x0, [sp, #48]
d4: 94000000 bl 0 <test_fun_b>
d8: f9401be1 ldr x1, [sp, #48]
dc: f9401fe0 ldr x0, [sp, #56]
e0: 8b000020 add x0, x1, x0
e4: f94017e1 ldr x1, [sp, #40]
e8: 8b000020 add x0, x1, x0
ec: f9001be0 str x0, [sp, #48]
f0: f9401be1 ldr x1, [sp, #48]
f4: f9401fe0 ldr x0, [sp, #56]
f8: 8b000020 add x0, x1, x0
fc: f94013e1 ldr x1, [sp, #32]
100: 8b000020 add x0, x1, x0
104: f9001fe0 str x0, [sp, #56]
108: d503201f nop
10c: a8c47bfd ldp x29, x30, [sp], #64
110: d65f03c0 ret
2.2.3 执行完成之后栈帧的使用情况
2.3 test_fun_b函数及其对应的汇编程序
2.3.1 test_func_b函数的C实现
void test_fun_b(long m, long n, long x, long y)
{
long c = 3;
long d = 4;
printf("The current function is %s c:%ld d:%ld\r\n", __func__, c, d);
c = c + d + m;
d = c + d + n;
}
2.3.2 test_fun_b函数对应汇编及其分析
0000000000000000 <test_fun_b>:
test_fun_b函数的入口0: a9bc7bfd stp x29, x30, [sp, #-64]!
为test_fun_b函数开栈64B,同时把X29(FP),X30(LR)保存到栈顶sp和sp + 8的栈内存位置处4: 910003fd mov x29, sp
将sp保存到x29(FP)寄存器中,相当于FP指向栈的栈顶8: f90017e0 str x0, [sp, #40]
将参数1保存到栈的sp + 40栈内存位置处c: f90013e1 str x1, [sp, #32]
将参数2保存到栈sp + 32的栈内存位置处10: f9000fe2 str x2, [sp, #24]
将参数3保存到栈sp + 24栈内存位置处14: f9000be3 str x3, [sp, #16]
将参数4保存到栈sp + 16栈内存位置处18: d2800060 mov x0, #0x3 // #3
将test_fun_b函数的局部变量c保存到x0寄存器中1c: f9001be0 str x0, [sp, #48]
将test_fun_b函数的局部变量c保存到sp + 48栈内存位置处20: d2800080 mov x0, #0x4 // #4
将test_fun_b函数的局部变量d保存到x1寄存器中24: f9001fe0 str x0, [sp, #56]
将test_fun_b函数的局部变量d保存到栈sp + 56栈内存位置处28: f9401fe3 ldr x3, [sp, #56]
从栈中取出局部变量d的值放到x3寄存器中2c: f9401be2 ldr x2, [sp, #48]
从栈中取出局部变量c的值放到x2寄存器中30: 90000000 adrp x0, 0 <test_fun_b>
将test_fun_b函数的地址加载到x0寄存器中34: 91000001 add x1, x0, #0x0
x1 = x0 + 0,其中x0保存的是test_fun_b的起始地址38: 90000000 adrp x0, 0 <test_fun_b>
将test_fun_b函数的地址加载到x0寄存器中3c: 91000000 add x0, x0, #0x0
x0 = x0 + 0,其中x0保存的是test_fun_b的起始地址40: 94000000 bl 0 <printf>
调用函数printf44: f9401be1 ldr x1, [sp, #48]
从栈中取出局部变量c,存放到x1寄存器48: f9401fe0 ldr x0, [sp, #56]
从栈中取出局部变量d,存放到x0寄存器4c: 8b000020 add x0, x1, x0
d = c + d,将d的结果保存到x0寄存器中。50: f94017e1 ldr x1, [sp, #40]
从栈中取出调用test_fun_b时传入的第1个参数取出,放到x1寄存器中54: 8b000020 add x0, x1, x0
d = d + m58: f9001be0 str x0, [sp, #48]
将计算的结果x0的值重新保存到局部变量c的栈内存位置处5c: f9401be1 ldr x1, [sp, #48]
从栈中取出局部变量c的值放到x1寄存器中。60: f9401fe0 ldr x0, [sp, #56]
从栈中取出局部变量d的值放到x0寄存器中64: 8b000020 add x0, x1, x0
c = c + d68: f94013e1 ldr x1, [sp, #32]
从栈中取出调用test_fun_b函数时传入的第2个参数放到x1寄存器中6c: 8b000020 add x0, x1, x0
c = c + n70: f9001fe0 str x0, [sp, #56]
将计算的新值存放到原局部变量d的栈内存位置处74: d503201f nop
空操作78: a8c47bfd ldp x29, x30, [sp], #64
恢复X29(FP),X30(LR)寄存器的值,同时sp = sp + 64栈指针寄存器7c: d65f03c0 ret
返回X30(LR)寄存器保存的返回函数处
0000000000000000 <test_fun_b>:
0: a9bc7bfd stp x29, x30, [sp, #-64]!
4: 910003fd mov x29, sp
8: f90017e0 str x0, [sp, #40]
c: f90013e1 str x1, [sp, #32]
10: f9000fe2 str x2, [sp, #24]
14: f9000be3 str x3, [sp, #16]
18: d2800060 mov x0, #0x3 // #3
1c: f9001be0 str x0, [sp, #48]
20: d2800080 mov x0, #0x4 // #4
24: f9001fe0 str x0, [sp, #56]
28: f9401fe3 ldr x3, [sp, #56]
2c: f9401be2 ldr x2, [sp, #48]
30: 90000000 adrp x0, 0 <test_fun_b>
34: 91000001 add x1, x0, #0x0
38: 90000000 adrp x0, 0 <test_fun_b>
3c: 91000000 add x0, x0, #0x0
40: 94000000 bl 0 <printf>
44: f9401be1 ldr x1, [sp, #48]
48: f9401fe0 ldr x0, [sp, #56]
4c: 8b000020 add x0, x1, x0
50: f94017e1 ldr x1, [sp, #40]
54: 8b000020 add x0, x1, x0
58: f9001be0 str x0, [sp, #48]
5c: f9401be1 ldr x1, [sp, #48]
60: f9401fe0 ldr x0, [sp, #56]
64: 8b000020 add x0, x1, x0
68: f94013e1 ldr x1, [sp, #32]
6c: 8b000020 add x0, x1, x0
70: f9001fe0 str x0, [sp, #56]
74: d503201f nop
78: a8c47bfd ldp x29, x30, [sp], #64
7c: d65f03c0 ret