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>
调用函数printf -
148: 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 + b -
168: 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 + b -
178: 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 + 32 -
184: 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>
调用函数printf -
c4: 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 + c -
fc: 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 执行完成之后栈帧的使用情况
文章来源:https://www.toymoban.com/news/detail-670264.html
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>
调用函数printf -
44: 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 + m -
58: 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 + d -
68: f94013e1 ldr x1, [sp, #32]
从栈中取出调用test_fun_b函数时传入的第2个参数放到x1寄存器中 -
6c: 8b000020 add x0, x1, x0
c = c + n -
70: 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
2.3.3 执行完成之后栈帧的使用情况
文章来源地址https://www.toymoban.com/news/detail-670264.html
到了这里,关于ARM64函数调用流程分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!