ARM64函数调用流程分析

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函数的入口
  • 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_func_b函数的地址到x0寄存器中
  • 138: 91000001 add x1, x0, #0x0将x0 + 0的值保存到x1寄存器中
  • 13c: 90000000 adrp x0, 0 加载test_func_b函数的地址到x0寄存器中
  • 140: 91000000 add x0, x0, #0x0将x0 + 0的值保存到x0寄存器中
  • 144: 94000000 bl 0 调用函数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_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 执行完成之后栈的存放情况

ARM64函数调用流程分析_第1张图片

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函数的入口
  • 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函数的地址加载到x0寄存器中
  • b4: 91000001 add x1, x0, #0x0 x1 = x0 + 0,其中x0保存的是test_fun_b的起始地址
  • b8: 90000000 adrp x0, 0 将test_fun_b函数的地址加载到x0寄存器中
  • bc: 91000000 add x0, x0, #0x0 x0 = x0 + 0,其中x0保存的是test_fun_b的起始地址
  • c0: 94000000 bl 0 调用函数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函数,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, x0c = 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 执行完成之后栈帧的使用情况

ARM64函数调用流程分析_第2张图片

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函数的入口
  • 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函数的地址加载到x0寄存器中
  • 34: 91000001 add x1, x0, #0x0x1 = x0 + 0,其中x0保存的是test_fun_b的起始地址
  • 38: 90000000 adrp x0, 0 将test_fun_b函数的地址加载到x0寄存器中
  • 3c: 91000000 add x0, x0, #0x0x0 = x0 + 0,其中x0保存的是test_fun_b的起始地址
  • 40: 94000000 bl 0 调用函数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 执行完成之后栈帧的使用情况

ARM64函数调用流程分析_第3张图片

你可能感兴趣的:(arm64,ARM64函数调用流程分析,AAPCS,栈帧使用)