1、栈
栈:是一种具有特殊的访问方式的存储空间(后进先出, Last In Out Firt,LIFO)
1.1、SP和FP寄存器
sp寄存器在任意时刻会保存我们栈顶的地址.
fp寄存器也称为x29寄存器属于通用寄存器,但是在某些时刻我们利用它保存栈底的地址!
注意:ARM64开始,取消32位的 LDM,STM,PUSH,POP指令! 取而代之的是ldr\ldp str\stp
ARM64里面 对栈的操作是16字节对齐的!!
1.2、函数调用栈
常见的函数调用开辟和恢复的栈空间
sub sp, sp, #0x40 ; 拉伸0x40(64字节)空间
stp x29, x30, [sp, #0x30] ;x29\x30 寄存器入栈保护
add x29, sp, #0x30 ; x29指向栈帧的底部
...
ldp x29, x30, [sp, #0x30] ;恢复x29/x30 寄存器的值
add sp, sp, #0x40 ; 栈平衡
ret
1.3、关于内存读写指令
注意:读/写 数据是都是往高地址读/写
1、str(store register)指令
将数据从寄存器中读出来,存到内存中.
2、ldr(load register)指令
将数据从内存中读出来,存到寄存器中
此ldr 和 str 的变种ldp 和 stp 还可以操作2个寄存器.
1.4、bl和ret指令
1、bl标号
- 将下一条指令的地址放入lr(x30)寄存器
- 转到标号处执行指令
2、ret
默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址!
ARM64平台的特色指令,它面向硬件做了优化处理的
3、x30寄存器
x30寄存器存放的是函数的返回地址.当ret指令执行时刻,会寻找x30寄存器保存的地址值!
注意:在函数嵌套调用的时候.需要讲x30入栈!
2、函数的参数和返回值
ARM64下,函数的参数是存放在X0到X7(W0到W7)这8个寄存器里面的.如果超过8个参数,就会入栈.
函数的返回值是放在X0 寄存器里面的.
因此函数的参数最好不要超过8个
2.1、实现test函数超过8个参数调用
int test(int a,int b,int c,int d,int e,int f,int g,int h,int i){
return a+b+c+d+e+f+g+h+i;
}
- (void)viewDidLoad {
[super viewDidLoad];
test(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
ViewDidLoad汇编实现
001--Demo`-[ViewController viewDidLoad]:
// 拉伸栈空间
0x10400a3a0 <+0>: sub sp, sp, #0x40 ; =0x40
0x10400a3a4 <+4>: stp x29, x30, [sp, #0x30]
0x10400a3a8 <+8>: add x29, sp, #0x30 ; =0x30
0x10400a3ac <+12>: stur x0, [x29, #-0x8]
0x10400a3b0 <+16>: stur x1, [x29, #-0x10]
0x10400a3b4 <+20>: ldur x8, [x29, #-0x8]
0x10400a3b8 <+24>: add x0, sp, #0x10 ; =0x10
0x10400a3bc <+28>: str x8, [sp, #0x10]
0x10400a3c0 <+32>: adrp x8, 6
0x10400a3c4 <+36>: ldr x8, [x8, #0xc58]
0x10400a3c8 <+40>: str x8, [sp, #0x18]
0x10400a3cc <+44>: adrp x8, 6
0x10400a3d0 <+48>: ldr x1, [x8, #0xc40]
0x10400a3d4 <+52>: bl 0x10400a7c4 ; symbol stub for: objc_msgSendSuper2
0x10400a3d8 <+56>: mov w0, #0x1
0x10400a3dc <+60>: mov w1, #0x2
0x10400a3e0 <+64>: mov w2, #0x3
0x10400a3e4 <+68>: mov w3, #0x4
0x10400a3e8 <+72>: mov w4, #0x5
0x10400a3ec <+76>: mov w5, #0x6
0x10400a3f0 <+80>: mov w6, #0x7
0x10400a3f4 <+84>: mov w7, #0x8
-> 0x10400a3f8 <+88>: mov x9, sp
0x10400a3fc <+92>: mov w8, #0x9
// 将W8的值保存在上一个栈空间中
0x10400a400 <+96>: str w8, [x9]
0x10400a404 <+100>: bl 0x10400a328 ; test at ViewController.m:17
0x10400a408 <+104>: ldp x29, x30, [sp, #0x30]
0x10400a40c <+108>: add sp, sp, #0x40 ; =0x40
0x10400a410 <+112>: ret
test函数的执行
001--Demo`test:
0x10400a328 <+0>: sub sp, sp, #0x30 ; =0x30
// 从上一个栈空间从内存读参数到寄存器
-> 0x10400a32c <+4>: ldr w8, [sp, #0x30]
0x10400a330 <+8>: str w0, [sp, #0x2c]
0x10400a334 <+12>: str w1, [sp, #0x28]
0x10400a338 <+16>: str w2, [sp, #0x24]
0x10400a33c <+20>: str w3, [sp, #0x20]
0x10400a340 <+24>: str w4, [sp, #0x1c]
0x10400a344 <+28>: str w5, [sp, #0x18]
0x10400a348 <+32>: str w6, [sp, #0x14]
0x10400a34c <+36>: str w7, [sp, #0x10]
0x10400a350 <+40>: str w8, [sp, #0xc]
0x10400a354 <+44>: ldr w8, [sp, #0x2c]
0x10400a358 <+48>: ldr w9, [sp, #0x28]
0x10400a35c <+52>: add w8, w8, w9
0x10400a360 <+56>: ldr w9, [sp, #0x24]
0x10400a364 <+60>: add w8, w8, w9
0x10400a368 <+64>: ldr w9, [sp, #0x20]
0x10400a36c <+68>: add w8, w8, w9
0x10400a370 <+72>: ldr w9, [sp, #0x1c]
0x10400a374 <+76>: add w8, w8, w9
0x10400a378 <+80>: ldr w9, [sp, #0x18]
0x10400a37c <+84>: add w8, w8, w9
0x10400a380 <+88>: ldr w9, [sp, #0x14]
0x10400a384 <+92>: add w8, w8, w9
0x10400a388 <+96>: ldr w9, [sp, #0x10]
0x10400a38c <+100>: add w8, w8, w9
0x10400a390 <+104>: ldr w9, [sp, #0xc]
0x10400a394 <+108>: add w0, w8, w9
0x10400a398 <+112>: add sp, sp, #0x30 ; =0x30
0x10400a39c <+116>: ret
2.2、返回结构体实现栈传递
OC返回结构体
struct str getStr(int a, int b, int c, int d, int e, int f){
struct str str1;
str1.a = a;
str1.b = b;
str1.c = c;
str1.d = d;
str1.e = e;
str1.f = f;
return str1;
}
- (void)viewDidLoad {
[super viewDidLoad];
getStr(1, 2, 3, 4, 5, 6);
}
viewDidLoad汇编实现
001--Demo`-[ViewController viewDidLoad]:
0x104e2e3e4 <+0>: sub sp, sp, #0x50 ; =0x50
0x104e2e3e8 <+4>: stp x29, x30, [sp, #0x40]
0x104e2e3ec <+8>: add x29, sp, #0x40 ; =0x40
0x104e2e3f0 <+12>: stur x0, [x29, #-0x8]
0x104e2e3f4 <+16>: stur x1, [x29, #-0x10]
0x104e2e3f8 <+20>: ldur x8, [x29, #-0x8]
0x104e2e3fc <+24>: add x0, sp, #0x20 ; =0x20
0x104e2e400 <+28>: str x8, [sp, #0x20]
0x104e2e404 <+32>: adrp x8, 6
0x104e2e408 <+36>: ldr x8, [x8, #0xc58]
0x104e2e40c <+40>: str x8, [sp, #0x28]
0x104e2e410 <+44>: adrp x8, 6
0x104e2e414 <+48>: ldr x1, [x8, #0xc40]
0x104e2e418 <+52>: bl 0x104e2e7c4 ; symbol stub for: objc_msgSendSuper2
// sp栈顶指针向下偏移8个字节,指针赋值给x8寄存器
0x104e2e41c <+56>: add x8, sp, #0x8 ; =0x8
// 参数赋值给 w0~w5
0x104e2e420 <+60>: mov w0, #0x1
0x104e2e424 <+64>: mov w1, #0x2
0x104e2e428 <+68>: mov w2, #0x3
0x104e2e42c <+72>: mov w3, #0x4
0x104e2e430 <+76>: mov w4, #0x5
0x104e2e434 <+80>: mov w5, #0x6
-> 0x104e2e438 <+84>: bl 0x104e2e38c ; getStr at ViewController.m:30
0x104e2e43c <+88>: ldp x29, x30, [sp, #0x40]
0x104e2e440 <+92>: add sp, sp, #0x50 ; =0x50
0x104e2e444 <+96>: ret
getStr汇编实现
001--Demo`getStr:
0x100426594 <+0>: sub sp, sp, #0x20 ; =0x20
0x100426598 <+4>: mov x9, x8
0x10042659c <+8>: str w0, [sp, #0x1c]
0x1004265a0 <+12>: str w1, [sp, #0x18]
-> 0x1004265a4 <+16>: str w2, [sp, #0x14]
0x1004265a8 <+20>: str w3, [sp, #0x10]
0x1004265ac <+24>: str w4, [sp, #0xc]
0x1004265b0 <+28>: str w5, [sp, #0x8]
0x1004265b4 <+32>: ldr w8, [sp, #0x1c]
0x1004265b8 <+36>: str w8, [x9]
0x1004265bc <+40>: ldr w8, [sp, #0x18]
0x1004265c0 <+44>: str w8, [x9, #0x4]
0x1004265c4 <+48>: ldr w8, [sp, #0x14]
0x1004265c8 <+52>: str w8, [x9, #0x8]
0x1004265cc <+56>: ldr w8, [sp, #0x10]
0x1004265d0 <+60>: str w8, [x9, #0xc]
0x1004265d4 <+64>: ldr w8, [sp, #0xc]
0x1004265d8 <+68>: str w8, [x9, #0x10]
0x1004265dc <+72>: ldr w8, [sp, #0x8]
0x1004265e0 <+76>: str w8, [x9, #0x14]
0x1004265e4 <+80>: add sp, sp, #0x20 ; =0x20
0x1004265e8 <+84>: ret
2.3、汇编实现a + b的函数
.text
.global _funcA, _sum
_funcA:
stp x29, x30, [sp, #-0x10]!
// sub sp, sp, #0x10
// stp x29, x30, [sp]
bl _sum
// ldp x29, x30, [sp]
// add sp, sp, #0x10
ldp x29, x30, [sp], #0x10
ret
_sum:
add x0, x0, x1
ret
3、函数的局部变量
函数的局部变量放在栈里面!
3.1、局部变量传递
局部变量c
int funcB(int a, int b) {
int c = 6;
return a + b + c;
}
int main(int argc, char * argv[]) {
funcB(10, 20);
}
funcB的汇编调用
001--Demo`funcB:
-> 0x104b4e428 <+0>: sub sp, sp, #0x10 ; =0x10
0x104b4e42c <+4>: str w0, [sp, #0xc]
0x104b4e430 <+8>: str w1, [sp, #0x8]
// 局部变量c
0x104b4e434 <+12>: mov w8, #0x6
// 将 c的值写入到内存中,入栈
0x104b4e438 <+16>: str w8, [sp, #0x4]
0x104b4e43c <+20>: ldr w8, [sp, #0xc]
0x104b4e440 <+24>: ldr w9, [sp, #0x8]
0x104b4e444 <+28>: add w8, w8, w9
0x104b4e448 <+32>: ldr w9, [sp, #0x4]
0x104b4e44c <+36>: add w0, w8, w9
0x104b4e450 <+40>: add sp, sp, #0x10 ; =0x10
0x104b4e454 <+44>: ret
4、函数的嵌套调用
函数嵌套
int funcB(int a, int b) {
int c = 6;
int d = funcSum(a, b, c);
int e = funcSum(a, b, c);
return e;
}
int funcSum(int a, int b, int c) {
int d = a + b + c;
printf("%d", d);
return d;
}
int main(int argc, char * argv[]) {
funcB(10, 20);
}
funcB的汇编
001--Demo`funcB:
0x100766398 <+0>: sub sp, sp, #0x30 ; =0x30
0x10076639c <+4>: stp x29, x30, [sp, #0x20]
0x1007663a0 <+8>: add x29, sp, #0x20 ; =0x20
0x1007663a4 <+12>: stur w0, [x29, #-0x4]
0x1007663a8 <+16>: stur w1, [x29, #-0x8]
0x1007663ac <+20>: mov w8, #0x6
0x1007663b0 <+24>: stur w8, [x29, #-0xc]
0x1007663b4 <+28>: ldur w0, [x29, #-0x4]
0x1007663b8 <+32>: ldur w1, [x29, #-0x8]
0x1007663bc <+36>: ldur w2, [x29, #-0xc]
0x1007663c0 <+40>: bl 0x1007663ec ; funcSum at main.m:28
0x1007663c4 <+44>: str w0, [sp, #0x10]
0x1007663c8 <+48>: ldur w0, [x29, #-0x4]
0x1007663cc <+52>: ldur w1, [x29, #-0x8]
0x1007663d0 <+56>: ldur w2, [x29, #-0xc]
0x1007663d4 <+60>: bl 0x1007663ec ; funcSum at main.m:28
-> 0x1007663d8 <+64>: str w0, [sp, #0xc]
0x1007663dc <+68>: ldr w0, [sp, #0xc]
0x1007663e0 <+72>: ldp x29, x30, [sp, #0x20]
0x1007663e4 <+76>: add sp, sp, #0x30 ; =0x30
0x1007663e8 <+80>: ret
5、全局变量和常量
全局变量和常量的取值
当前的偏移页数+当前页号+偏移量
adrp x0, 2
add x0, x0, #0xe27
// 全局变量g
int g = 12;
int func(int a, int b) {
printf("haha"); // 常量 haha
int c = a + g;
return c;
}
int main(int argc, char * argv[]) {
func(1, 2);
return 0;
}
func的汇编
002-Demo`func:
0x100d71fd0 <+0>: sub sp, sp, #0x20 ; =0x20
0x100d71fd4 <+4>: stp x29, x30, [sp, #0x10]
0x100d71fd8 <+8>: add x29, sp, #0x10 ; =0x10
0x100d71fdc <+12>: stur w0, [x29, #-0x4]
0x100d71fe0 <+16>: str w1, [sp, #0x8]
// 2>>12 + 0x100d71000 + 0xe27
// 2(当前的偏移页数)向右偏移12位 + 0x100d71000(当前页号)+ 0xe27(偏移量)
0x100d71fe4 <+20>: adrp x0, 2
0x100d71fe8 <+24>: add x0, x0, #0xe27 ; =0xe27
0x100d71fec <+28>: bl 0x100d7234c ; symbol stub for: printf
0x100d71ff0 <+32>: ldur w8, [x29, #-0x4]
0x100d71ff4 <+36>: adrp x9, 8
0x100d71ff8 <+40>: ldr w9, [x9, #0x4a0]
0x100d71ffc <+44>: add w8, w8, w9
0x100d72000 <+48>: str w8, [sp, #0x4]
0x100d72004 <+52>: ldr w0, [sp, #0x4]
0x100d72008 <+56>: ldp x29, x30, [sp, #0x10]
0x100d7200c <+60>: add sp, sp, #0x20 ; =0x20
0x100d72010 <+64>: ret