arm64函数源码和汇编解析(objdump)
简单无参数的例子:
gcc -g a.c
objdump -d a.out
get_xx函数:
0000000000400634 <get_xx>:400634: a9be7bfd stp x29, x30, [sp, #-32]! //stp指令将x29,x30保存在sp-32地址,后面的!,是完成后将sp-32的地址值给sp。意味着sp向地地址移动400638: 910003fd mov x29, sp //将sp的值保存到x29寄存器。40063c: d2800100 mov x0, #0x8 // #8400640: 97ffffac bl 4004f0 <malloc@plt> //调malloc函数,返回的地址保存到x0寄存器。也就是get_xx返回的地址。400644: f9000fe0 str x0, [sp, #24] // 将get_xx函数返回的地址p,放入sp+24位置。 因为后面会用到这个地址,因此需要保存在这。 400648: f9400fe0 ldr x0, [sp, #24] //从sp+24位置取出地址p40064c: 52800141 mov w1, #0xa //将0xa也就是10,放入w1寄存器。400650: b9000001 str w1, [x0] //将w1寄存器的值,放入x0寄存器的地址,结构体中的a变量在结构体的起始位置 ,因此刚好设置p->a400654: f9400fe0 ldr x0, [sp, #24] //从sp+24位置取出地址p, 前面把x0存入sp+24就是这个目的。400658: 528000a1 mov w1, #0x5 //同理将5保存到w1,为了下一步设置p->b // #540065c: b9000401 str w1, [x0, #4] //将w1 存入x0偏移4字节的位置,结构体成员b在结构体中偏移4字节。400660: f9400fe0 ldr x0, [sp, #24] //最后取出指针p到x0400664: a8c27bfd ldp x29, x30, [sp], #32 //将在sp所在的地址的值给x29,x30寄存器,并且sp的值=sp+32 。刚好还原函数开始时的sp。400668: d65f03c0 ret //返回,将x30保存的下一条指令给pc
main函数:
000000000040066c <main>:40066c: a9be7bfd stp x29, x30, [sp, #-32]!400670: 910003fd mov x29, sp400674: 97fffff0 bl 400634 <get_xx> //bl指令将下一条指令的地址保存到lr(x30)寄存器,ret的时候会将其设置到pc以继续执行400678: f9000fe0 str x0, [sp, #24] //参考get_xx 说明,将函数返回的地址px保存40067c: f9400fe0 ldr x0, [sp, #24] //参考get_xx 说明,将函数返回的地址px保存400680: b9400001 ldr w1, [x0] //将px指针的4字节内容放入w1。由于a只占4字节,后面的内容不拷贝400684: f9400fe0 ldr x0, [sp, #24] //重新取出px指针400688: b9400400 ldr w0, [x0, #4] //在x0+4字节的地址取出b。放入w040068c: 4b000020 sub w0, w1, w0 //减法 w0=w1-w0 ,即p->a - p->b400690: b90017e0 str w0, [sp, #20] //保存结果400694: b94017e0 ldr w0, [sp, #20] //取出结果400698: a8c27bfd ldp x29, x30, [sp], #32 //还原x29,x30,sp40069c: d65f03c0 ret //返回
Arm64的所有函数,一开始都是这两句:
400634: a9be7bfd stp x29, x30, [sp, #-32]! //stp指令将x29,x30保存在sp-32地址,后面的!,是完成后将sp-32的地址值给sp。意味着sp向地地址移动
400638: 910003fd mov x29, sp //将sp的值保存到x29寄存器。
最后都有这一句:
400698: a8c27bfd ldp x29, x30, [sp], #32 //还原x29,x30,sp
因此函数调用都过stp和ldp来配对使用,保存和恢复某些寄存器:
每次函数都会开辟自己的栈。这个栈的大小根据函数的本地变量或者参数来决定。
带参数的例子,
带5个参数的函数get_xx:
汇编代码:
0000000000400684 <get_xx>:400684: a9bc7bfd stp x29, x30, [sp, #-64]! //开辟的栈空间加大值64字节,用来存储参数。400688: 910003fd mov x29, sp40068c: b9002fe0 str w0, [sp, #44] //其中w0-w4为5个参数,分别存储。400690: b9002be1 str w1, [sp, #40]400694: b90027e2 str w2, [sp, #36]400698: b90023e3 str w3, [sp, #32]40069c: b9001fe4 str w4, [sp, #28]4006a0: d2800100 mov x0, #0x8 // #84006a4: 97ffffa3 bl 400530 <malloc@plt>4006a8: f9001fe0 str x0, [sp, #56]4006ac: f9401fe0 ldr x0, [sp, #56]4006b0: 52800141 mov w1, #0xa // #104006b4: b9000001 str w1, [x0]4006b8: f9401fe0 ldr x0, [sp, #56]4006bc: 528000a1 mov w1, #0x5 // #54006c0: b9000401 str w1, [x0, #4]4006c4: b9402fe0 ldr w0, [sp, #44]4006c8: 7100001f cmp w0, #0x04006cc: 54000081 b.ne 4006dc <get_xx+0x58> // b.any4006d0: d2955760 mov x0, #0xaabb // #437074006d4: f9001fe0 str x0, [sp, #56]4006d8: 14000002 b 4006e0 <get_xx+0x5c>4006dc: d503201f nop4006e0: f9401fe0 ldr x0, [sp, #56]4006e4: a8c47bfd ldp x29, x30, [sp], #644006e8: d65f03c0 ret00000000004006ec <main>:4006ec: a9bd7bfd stp x29, x30, [sp, #-48]!4006f0: 910003fd mov x29, sp4006f4: b9001fe0 str w0, [sp, #28]4006f8: f9000be1 str x1, [sp, #16]4006fc: b9401fe0 ldr w0, [sp, #28]400700: 7100081f cmp w0, #0x2400704: 1a9fd7e0 cset w0, gt400708: 12001c00 and w0, w0, #0xff40070c: b9002fe0 str w0, [sp, #44]400710: 52800084 mov w4, #0x4 // #4 在调用get_xx函数之前准备参数,并依次存储在w0-w4这5个寄存器中400714: 52800063 mov w3, #0x3 // #3400718: 52800042 mov w2, #0x2 // #240071c: 52800021 mov w1, #0x1 // #1400720: b9402fe0 ldr w0, [sp, #44]400724: 97ffffd8 bl 400684 <get_xx>400728: f90013e0 str x0, [sp, #32]40072c: f94013e0 ldr x0, [sp, #32]400730: aa0003e1 mov x1, x0400734: 90000000 adrp x0, 400000 <_init-0x4f0>400738: 911fe000 add x0, x0, #0x7f840073c: 97ffff8d bl 400570 <printf@plt>400740: 52800000 mov w0, #0x0 // #0400744: a8c37bfd ldp x29, x30, [sp], #48400748: d65f03c0 ret40074c: 00000000 .inst 0x00000000 ; undefined
参考指令:
03_ARMv8指令集介绍加载与存储指令 - Carlos·Wei - 博客园
ARM的六大类指令集---LDR、LDRB、LDRH、STR、STRB、STRH - blogernice - 博客园
https://blog.csdn.net/wmzjzwlzs/article/details/124513127