简单无参数的例子:

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 // #8
400640: 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位置取出地址p
40064c: 52800141 mov w1, #0xa //将0xa也就是10,放入w1寄存器。
400650: b9000001 str w1, [x0] //将w1寄存器的值,放入x0寄存器的地址,结构体中的a变量在结构体的起始位置 ,因此刚好设置p->a
400654: f9400fe0 ldr x0, [sp, #24] //从sp+24位置取出地址p, 前面把x0存入sp+24就是这个目的。
400658: 528000a1 mov w1, #0x5 //同理将5保存到w1,为了下一步设置p->b // #5
40065c: b9000401 str w1, [x0, #4] //将w1 存入x0偏移4字节的位置,结构体成员b在结构体中偏移4字节。
400660: f9400fe0 ldr x0, [sp, #24] //最后取出指针p到x0
400664: 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, sp
400674: 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。放入w0
40068c: 4b000020 sub w0, w1, w0 //减法 w0=w1-w0 ,即p->a - p->b
400690: b90017e0 str w0, [sp, #20] //保存结果
400694: b94017e0 ldr w0, [sp, #20] //取出结果
400698: a8c27bfd ldp x29, x30, [sp], #32 //还原x29,x30,sp
40069c: 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, sp
40068c: 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 // #8
4006a4: 97ffffa3 bl 400530 <malloc@plt>
4006a8: f9001fe0 str x0, [sp, #56]
4006ac: f9401fe0 ldr x0, [sp, #56]
4006b0: 52800141 mov w1, #0xa // #10
4006b4: b9000001 str w1, [x0]
4006b8: f9401fe0 ldr x0, [sp, #56]
4006bc: 528000a1 mov w1, #0x5 // #5
4006c0: b9000401 str w1, [x0, #4]
4006c4: b9402fe0 ldr w0, [sp, #44]
4006c8: 7100001f cmp w0, #0x0
4006cc: 54000081 b.ne 4006dc <get_xx+0x58> // b.any
4006d0: d2955760 mov x0, #0xaabb // #43707
4006d4: f9001fe0 str x0, [sp, #56]
4006d8: 14000002 b 4006e0 <get_xx+0x5c>
4006dc: d503201f nop
4006e0: f9401fe0 ldr x0, [sp, #56]
4006e4: a8c47bfd ldp x29, x30, [sp], #64
4006e8: d65f03c0 ret
00000000004006ec <main>:
4006ec: a9bd7bfd stp x29, x30, [sp, #-48]!
4006f0: 910003fd mov x29, sp
4006f4: b9001fe0 str w0, [sp, #28]
4006f8: f9000be1 str x1, [sp, #16]
4006fc: b9401fe0 ldr w0, [sp, #28]
400700: 7100081f cmp w0, #0x2
400704: 1a9fd7e0 cset w0, gt
400708: 12001c00 and w0, w0, #0xff
40070c: b9002fe0 str w0, [sp, #44]
400710: 52800084 mov w4, #0x4 // #4 在调用get_xx函数之前准备参数,并依次存储在w0-w4这5个寄存器中
400714: 52800063 mov w3, #0x3 // #3
400718: 52800042 mov w2, #0x2 // #2
40071c: 52800021 mov w1, #0x1 // #1
400720: 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, x0
400734: 90000000 adrp x0, 400000 <_init-0x4f0>
400738: 911fe000 add x0, x0, #0x7f8
40073c: 97ffff8d bl 400570 <printf@plt>
400740: 52800000 mov w0, #0x0 // #0
400744: a8c37bfd ldp x29, x30, [sp], #48
400748: d65f03c0 ret
40074c: 00000000 .inst 0x00000000 ; undefined
复杂函数参数(结构体)
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
struct st
{
int a;
int b;
int c;
int d;
int e;
int f;
int g;
int h;
int i;
int j;
int k;
};
static int is_ok(int a,struct st s)
{
return s.a+s.k+a;
}
int main()
{
struct st s;
int x = 99;
s.a = 1;
s.b = 2;
s.c = 3;
s.d = 4;
s.e = 5;
is_ok(x,s);
return 0;
}
此时,参数将不使用寄存器来传递,因为也没有足够多的寄存器。采用的是栈空间。比如在main函数中,SP会开辟272字节的空间来存储结构体。用寄存器来保存:先看main函数:
is_ok函数:

参考指令:
03_ARMv8指令集介绍加载与存储指令 - Carlos·Wei - 博客园