初识汇编(三)

函数的本质


关于CPU的补充

寄存器

CPU由: 控制器, 计算器和寄存器组成. 其中寄存器的作用就是将数据进行临时存储

CPU的运行速度非常快,为了性能CPU会在内部开辟一小块临时存储区域, 并在运算时先将数据从内存复制到这小块临时存储区域中, 在这小块区域进行运算,这小块临时区域就称作寄存器.

  • 对于ARM64架构的CPU, 以 X 开头的就是64位的寄存器, 以 W 开头的就是32位的寄存器, 其中32位寄存器就是64位寄存器的低32位部分


    Snip20180422_33.png

    Snip20180422_34.png

系统中没有提供16位 和 8位的寄存器供以访问(注意: 查看时要使用真机调试, 模拟器使用的X86的架构)

高速缓存

iPhoneX搭载的ARM64处理器A11, 它的一级缓存容量是64KB, 二级缓存容量是8M (现在普遍也只有二级缓存)

CPU每执行一条指令都需要从内存中将指令读取到CPU中执行, 而寄存器运行速度要比内存的读写速度快很多, CPU还集成了一个高速缓存存储区域来提高性能. 当程序运行时, 先将要执行的指令代码存储到高速缓存存储区域中(由操作系统完成), CPU直接从高速缓存中依次读取指令来执行.


寄存器的补充

数据地址存储器

数据地址存储器通常作为数据计算的临时存储, 做累加, 计数地址保存等功能. 这些寄存器的作用主要用于在CPU指令中保存操作数, 在CPU中当做常规变量使用.

ARM64中

  • 64位: X0 - X30 , XZR(零寄存器)
  • 32位: W0 - W30 , WZR(零寄存器)

注意: 8086汇编中有一种寄存器, 段寄存器: CS, ES, DS, SS四个寄存器用来保存段的基地址, 这属于Intel CPU中, 在ARM中没有.

浮点和向量寄存器

Snip20180422_35.png

现在CPU支持向量运算(向量运算在图形处理中使用比较广泛), 为支持向量运算, 故提供了更多向量寄存器.

  • 向量寄存器: 128位 v0 - v128
Snip20180422_36.png

因为浮点数以及其运算的特殊性, CPU专门提供浮点寄存器来计算浮点数

  • 浮点寄存器: 64位s0 - s31 和 32位d0 - d31

  • 栈: 是一种具有特殊方式的访问存储空间(先进后出)


    15193998892055.jpg

sp, fp寄存器

  • sp寄存器会在任意时刻保存栈顶的地址
  • fp寄存器也成为x29寄存器属于通用寄存器, 但是在某些时刻我们会用它保存栈底的地址

注意: ARM64开始取消32位的LDM, STM, PUSH, POP指令. 与之替代的是 ldr/ ldp, str/ stp
ARM64里面 对栈的操作都是16进制对齐的!!!

Snip20180424_41.png

关于内存的读写指令

注意: 读/写 数据都是往高地址读/写

str(store register)写入指令
将数据从寄存器中读出来, 写入到内存中

ldr(load register)读取指令
将数据从内存中读取出来, 存到寄存器中

ldp/stp 是 ldr/str 的衍生, 可以同时读/写两个寄存器, ldr/str只能读写一个

汇编代码--堆栈操作练习

使用32字节空间作为这段程序的栈空间, 然后利用栈将x0, x1的值进行交换

sub sp, sp, #0x20 ; 拉伸栈空间32(20 = 2*16)个字节
stp x0 , x1, [sp, #0x10] ; sp往上加16(10 = 1 * 16)个字节,存放x0 和 x1
ldp x1 , x0, [sp, #0x10] ; 将sp偏移16个字节的值取出来,放入x1 和 x0

注意: 拉伸栈空间是往低地址拉伸, 拉伸的字节数只能是16的倍数, 否则会崩溃(对照上面 16进制对齐理解 )

第一句代码: sp = sp - #0x20, 将sp的地址往低地址偏移32个字节

Snip20180424_43.png

有sub就会有add, 这就是栈平衡, 如果只有sub没有add, 会导致内存不够用, 堆栈溢出

第二,三句代码: sp指向的地址不会变, 只是将x0, x1的值从相应地址存入或读取.


bl 和 ret 指令

bl: 跳转指令
1. 将下一条指令的地址存放到 lr(x30) 寄存器中
2. 跳转到对应函数中执行指令

Snip20180424_44.png

ret: 返回指令
默认使用 lr(x30) 寄存器的值, 通过底层指令提示CPU这是下一条指令的地址.
ARM64架构中的特色指令, 面向硬件做了优化处理

Snip20180424_46.png
lr (x30) 寄存器
  • x30寄存器存放的是函数返回的地址, 当ret执行指令时, 会寻找这个寄存器中保存的地址

注意: 在函数嵌套调用中, 需要将x30入栈, 否则可能会造成死循环.

函数的参数和返回值

ARM64下, 参数都是保存在x0-x7 (w0-w7) 这8个寄存器中, 如果超过这8个参数就会入栈, 函数的返回值是放在 x0寄存器中的.

函数的局部变量

函数的局部变量都是保存在栈里面.

好了, 第三篇对于初识汇编的复习和梳理就到这了

推荐阅读更多精彩内容