前言
最近准备学习汇编,然后在B站上看到叫iOS小贤的作者发的视频挺不错,打算跟着学,文章是看视频的笔记,最后有原视频链接,想看视频的可以看看通过链接查看视频。
栈
- 栈:是一种具有特殊的访问方式的存储空间(后进先出, Last In Out Firt,LIFO)
Stack 是由内存区域的结束地址开始,从高位(地址)向低位(地址)分配。比如,内存区域的结束地址是0x8000,第一帧假定是16字节,那么下一次分配的地址就会从0x7FF0开始;第二帧假定需要64字节,那么地址就会移动到0x7FB0。
注:用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。所谓的堆栈溢出是指堆栈开辟的空间碰到了一起,重叠,说明空间不够了。
SP和FP寄存器
- sp寄存器在任意时刻会保存我们栈顶的地址.
- fp寄存器也称为x29寄存器属于通用寄存器,但是在某些时刻我们利用它保存栈底的地址!()
注意:ARM64开始,取消32位的 LDM,STM,PUSH,POP指令! 取而代之的是ldr\ldp str\stp
ARM64里面 对栈的操作是16字节对齐的!!
注:以前push和pop操作的时候需要对寄存器做保护,也就是sp。做压栈的操作sp会挪动,之后需要恢复的时候sp是需要保护的,通过fp保护。现在push和pop取消了fp就显得比较鸡肋,但是要兼容32位,这个寄存器还保留着。
关于内存读写指令
注意:读/写 数据是都是往高地址读/写
str(store register)指令
将数据从寄存器中读出来,存到内存中。
ldr(load register)指令
将数据从内存中读出来,存到寄存器中
此ldr 和 str 的变种ldp(pair) 和 stp(pair) 还可以操作2个寄存器。
堆栈操作练习
使用32个字节空间作为这段程序的栈空间,然后利用栈将x0和x1的值进行交换。
sub sp, sp, #0x20 ;拉伸栈空间32个字节
stp x0, x1, [sp, #0x10] ;sp往上加16个字节,存放x0 和 x1
ldp x1, x0, [sp, #0x10] ;将sp偏移16个字节的值取出来,放入x1 和 x0
注:sub是减,sub sp, sp, #0x20
是将ap向底地址挪动32个字节,然后赋值给sp。str是将寄存器里面的值放到内存中,但是str只能操作一个寄存器,stp可以操作两个,stp x0, x1, [sp, #0x10]
是将x0和x1放在[sp, #0x10]
,[sp, #0x10]
是一块内存区域,左边操作数加上右边操作数,[sp, #-0x10]
就是左边操作数减去右边操作数。
我们来写一下
然后A()出打断点,并进入A方法。
我们看到在执行完
sub sp, sp, #0x20
后sp是0x000000016d57b8e0
,然后查看一下我们来看一下内存地址,输入0x000000016d57b8e0
,看到内存开辟了16个字节的空间,当前sp是在6129432800处,前面讲到,写内存是往高地址写,所以stp x0, x1, [sp, #0x10]
是往高地址写。
现在我们往x0和x1中写入0xffffffff,再ni。
register write x0 0xffffffff
register write x1 0xffffffff
因为stp x0, x1, [sp, #0x10]
所以是从sp加16个字节出开始存数据。
注: add sp, sp, #0x20
是为了栈平衡,如果不这么做,栈会一直往低地址走,最终内存不够用,但是当函数调用完局部变量是不需要了,所以要回收栈空间。