函数的本质

134 阅读3分钟

寄存器的补充

数据地址寄存器

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

ARM64中

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

需要注意的是,8086 汇编中有一种特殊的寄存器段寄存器:CS, DS, SS, ES 四个寄存器来保存这些段的基地址,这个属于 Intel 架构 CPU 中,在 ARM 中并没有。

浮点和向量寄存器

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

  • 浮点寄存器 64位: D0 - D31,32位: S0 - S31

现在的 CPU 支持向量运算,(向量运算在图形处理相关的领域用得非常的多)为了支持向量计算系统了也提供了众多的向量寄存器。

  • 向量寄存器 128位:V0-V31

  • 栈:是一种具有特殊的访问方式的存储空间(后进先出, Last In Out Firt,LIFO

SP和FP寄存器

  • sp 寄存器在任意时刻会保存我们栈顶的地址。

  • fp 寄存器也称为 x29 寄存器属于通用寄存器,但是在某些时刻我们利用它保存栈底的地址!

注意:ARM64 开始,取消 32 位的 LDM, STM, PUSH, POP 指令! 取而代之的是 ldr\ldp str\stpARM64 里面,对栈的操作是 16 字节对齐的。

函数调用栈

常见的函数调用开辟和恢复的栈空间

sub    sp, sp, #0x40             ; 拉伸0x4064字节)空间
stp    x29, x30, [sp, #0x30]     ;x29\x30 寄存器入栈保护
add    x29, sp, #0x30            ; x29指向栈帧的底部
... 
ldp    x29, x30, [sp, #0x30]     ;恢复x29/x30 寄存器的值
add    sp, sp, #0x40             ; 栈平衡
ret

image.png

关于内存读写指令

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

str(store register)指令

将数据从寄存器中读出来,存到内存中。

ldr(load register)指令

将数据从内存中读出来,存到寄存器中,ldrstr 的变种 ldpstp 还可以操作2个寄存器。

堆栈操作练习

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

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

bl和ret指令

bl标号

  • 将下一条指令的地址放入 lr(x30) 寄存器
  • 转到标号处执行指令

ret

  • 默认使用 lr(x30) 寄存器的值,通过底层指令提示 CPU 此处作为下条指令地址。

ARM64 平台的特色指令,它面向硬件做了优化处理的。

x30寄存器

x30 寄存器存放的是函数的返回地址,当 ret 指令执行时刻,会寻找 x30 寄存器保存的地址值。

注意:在函数嵌套调用的时候,需要将 x30 入栈。

函数的参数和返回值

ARM64下,函数的参数是存放在 X0X7W0W7) 这 8 个寄存器里面的,如果超过 8个参数,就会入栈。 函数的返回值是放在 X0 寄存器里面的。

函数的局部变量

函数的局部变量放在栈里面。