Chinaunix首页 | 论坛 | 博客
  • 博客访问: 60021
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-12 21:41
个人简介

路人甲

文章分类

全部博文(5)

文章存档

2015年(5)

我的朋友

分类: LINUX

2015-04-22 19:09:50

ARM Procedure Call Standard定义了各寄存器在函数调用过程中的作用、基础类型的长度、以及函数调用基本准则,包括栈处理、参数传递等。

本文通过实例描述armarm64在函数调用过程中栈帧的处理方法,理解栈帧的特点对于理解反汇编代码和定位bug有重要意义。

下面是一个样例代码,我们将研究它的调用栈:

  1. int func2(int x, int y, int z)
  2. {
  3.         int r;
  4.         r = x + y + z;
  5.         return r;
  6. }
  7. int func1(int i, int j)
  8. {
  9.         int tmp = 3;
  10.         int k = 0;
  11.         k = func2(i, j, tmp);
  12.         return k;
  13. }
  14. int main()
  15. {
  16.         int i = 1;
  17.         int j = 2;
  18.         int k = 0;
  19.         k = func1(i, j);
  20.         return 0;
  21. }

由于在函数调用过程中,最后一层调用与中间调用对栈的处理略有不同,样例中采用两层调用,main -> func1 -> func2

 

将样例代码分别用armarm64 tool chain编译并反汇编。

为了便于分析汇编代码,先把armarm64的寄存器用途列出:

 
注:arm64 r0 ~ r30寄存器有两种使用模式,64位时称为x0 ~ x3032位时称为w0 ~ w30

arm反汇编:

  1. 000083bc <func2>:
  2.     83bc: b480 push {r7} r7会被使用故入栈
  3.     83be: b087 sub sp, #28 栈预留局部变量空间
  4.     83c0: af00 add r7, sp, #0
  5.     83c2: 60f8 str r0, [r7, #12] x
  6.     83c4: 60b9 str r1, [r7, #8] y
  7.     83c6: 607a str r2, [r7, #4] z
  8.     83c8: 68fa ldr r2, [r7, #12]
  9.     83ca: 68bb ldr r3, [r7, #8]
  10.     83cc: 441a add r2, r3
  11.     83ce: 687b ldr r3, [r7, #4]
  12.     83d0: 4413 add r3, r2
  13.     83d2: 617b str r3, [r7, #20] r
  14.     83d4: 697b ldr r3, [r7, #20]
  15.     83d6: 4618 mov r0, r3
  16.     83d8: 371c adds r7, #28
  17.     83da: 46bd mov sp, r7
  18.     83dc: f85d 7b04 ldr.w r7, [sp], #4
  19.     83e0: 4770 bx lr
  20.     83e2: bf00 nop

  21. 000083e4 <func1>:
  22.     83e4: b580 push {r7, lr} lr入栈;r7会被使用故入栈
  23.     83e6: b084 sub sp, #16 栈预留局部变量空间
  24.     83e8: af00 add r7, sp, #0
  25.     83ea: 6078 str r0, [r7, #4] i
  26.     83ec: 6039 str r1, [r7, #0] j
  27.     83ee: 2303 movs r3, #3
  28.     83f0: 60bb str r3, [r7, #8] tmp
  29.     83f2: 2300 movs r3, #0
  30.     83f4: 60fb str r3, [r7, #12] k
  31.     83f6: 6878 ldr r0, [r7, #4] 第一个参数放入r0
  32.     83f8: 6839 ldr r1, [r7, #0] 第二个参数放入r1
  33.     83fa: 68ba ldr r2, [r7, #8] 第三个参数放入r2
  34.     83fc: f7ff ffde bl 83bc <func2>
  35.     8400: 60f8 str r0, [r7, #12]
  36.     8402: 68fb ldr r3, [r7, #12]
  37.     8404: 4618 mov r0, r3
  38.     8406: 3710 adds r7, #16
  39.     8408: 46bd mov sp, r7
  40.     840a: bd80 pop {r7, pc}

  41. 0000840c <main>:
  42.     840c: b580 push {r7, lr} lr入栈;r7会被使用故入栈
  43.     840e: b084 sub sp, #16 栈预留局部变量空间
  44.     8410: af00 add r7, sp, #0
  45.     8412: 2301 movs r3, #1
  46.     8414: 607b str r3, [r7, #4] int i
  47.     8416: 2302 movs r3, #2
  48.     8418: 60bb str r3, [r7, #8] int j
  49.     841a: 2300 movs r3, #0
  50.     841c: 60fb str r3, [r7, #12] int k
  51.     841e: 6878 ldr r0, [r7, #4] 第一个参数放入r0
  52.     8420: 68b9 ldr r1, [r7, #8] 第二个参数放入r1
  53.     8422: f7ff ffdf bl 83e4 <func1>
  54.     8426: 60f8 str r0, [r7, #12]
  55.     8428: 2300 movs r3, #0
  56.     842a: 4618 mov r0, r3
  57.     842c: 3710 adds r7, #16
  58.     842e: 46bd mov sp, r7
  59.     8430: bd80 pop {r7, pc}
  60. 8432: bf00 nop

arm64反汇编:


  1. 0000000000400540 <func2>:
  2.   400540: d10083ff sub sp, sp, #0x20 sp-32
  3.   400544: b9000fe0 str w0, [sp,#12] x
  4.   400548: b9000be1 str w1, [sp,#8] y
  5.   40054c: b90007e2 str w2, [sp,#4] z
  6.   400550: b9400fe1 ldr w1, [sp,#12]
  7.   400554: b9400be0 ldr w0, [sp,#8]
  8.   400558: 0b000021 add w1, w1, w0
  9.   40055c: b94007e0 ldr w0, [sp,#4]
  10.   400560: 0b000020 add w0, w1, w0
  11.   400564: b9001fe0 str w0, [sp,#28] r
  12.   400568: b9401fe0 ldr w0, [sp,#28]
  13.   40056c: 910083ff add sp, sp, #0x20
  14.   400570: d65f03c0 ret

  15. 0000000000400574 <func1>:
  16.   400574: a9bd7bfd stp x29, x30, [sp,#-48]! sp-48, lr fp入栈
  17.   400578: 910003fd mov x29, sp 新sp放入fp
  18.   40057c: b9001fa0 str w0, [x29,#28] i
  19.   400580: b9001ba1 str w1, [x29,#24] j
  20.   400584: 52800060 mov w0, #0x3 // #3
  21.   400588: b9002fa0 str w0, [x29,#44] tmp
  22.   40058c: b9002bbf str wzr, [x29,#40] k
  23.   400590: b9401fa0 ldr w0, [x29,#28] 第一个参数放入r0
  24.   400594: b9401ba1 ldr w1, [x29,#24] 第二个参数放入r1
  25.   400598: b9402fa2 ldr w2, [x29,#44] 第三个参数放入r2
  26.   40059c: 97ffffe9 bl 400540 <func2>
  27.   4005a0: b9002ba0 str w0, [x29,#40]
  28.   4005a4: b9402ba0 ldr w0, [x29,#40]
  29.   4005a8: a8c37bfd ldp x29, x30, [sp],#48
  30.   4005ac: d65f03c0 ret

  31. 00000000004005b0 <main>:
  32.   4005b0: a9be7bfd stp x29, x30, [sp,#-32]! sp-32, lr fp入栈
  33.   4005b4: 910003fd mov x29, sp 新sp放入fp
  34.   4005b8: 52800020 mov w0, #0x1 // #1
  35.   4005bc: b9001fa0 str w0, [x29,#28] i
  36.   4005c0: 52800040 mov w0, #0x2 // #2
  37.   4005c4: b9001ba0 str w0, [x29,#24] j
  38.   4005c8: b90017bf str wzr, [x29,#20] k
  39.   4005cc: b9401fa0 ldr w0, [x29,#28] 第一个参数放入r0
  40.   4005d0: b9401ba1 ldr w1, [x29,#24] 第二个参数放入r1
  41.   4005d4: 97ffffe8 bl 400574 <func1>
  42.   4005d8: b90017a0 str w0, [x29,#20]
  43.   4005dc: 52800000 mov w0, #0x0 // #0
  44.   4005e0: a8c27bfd ldp x29, x30, [sp],#32
  45.   4005e4: d65f03c0 ret

调用栈对比示意图



从以上对比可以看出,armarm64的调用栈有以下区别:

1.  arm64lrfp放在栈顶,而arm放在栈底;

2.  arm64中当前fpsp相同,都是栈顶指针,与x86bpsp不同,x86bpsp之间是整个函数栈帧;

3.  函数返回时,arm64先将栈中的lr值放入当前lr,再ret;而arm直接将栈中的lr值放入pc

 

附: 使用gcc编译选项-fomit-frame-pointer,可以使arm64不使用fp寄存器,这时栈帧稍有变化,栈不在保存fp,局部变量寻址过程也不使用该寄存器


阅读(13469) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~