寄存器

寄存器是CPU的组成成分, 存放着指令、数据和地址,速度比内存块

分为通用寄存器状况寄存器浮点寄存器

arm64汇编指南

通用寄存器

arm64汇编指南
ARM64有31个通用寄存器, 每个寄存器可以存储64位数据(8个字节)

运用 X0-X30 表明64位的数

运用 W0-W30 时,访问这些寄存器的低32位, 会将高32位清0。

arm64汇编指南

  • X0-X7

    传递函数的参数, 如果有更多的参数需要运用栈来传递, X0 用来存储函数的回来值

  • SP(Stack Pointer)

    指针寄存器, 指向栈的顶部, WSP表明栈指针的最低32位

  • FP(Frame Pointer)

    即X29, 栈指针寄存器, 指向栈的底部

  • LR(Link Register)

    X30 链接寄存器, 存储着函数调用完成时的回来地址

    用来存储函数调用栈盯梢, 程序在崩溃时能够将函数调用栈打印便是借助LR寄存器来完成的

  • PC(Program Counter)

    保存下一条指令的内存地址, 通常在调试状况下看到的 PC 值都是当时断点处的地址

状况寄存器(CPSR)

保存指令运行成果的信息

依据运算的成果来设置状况寄存器的标志位

CPU内部进行置位, 不能用程序赋值

arm64汇编指南
28-31位(最终四位 0011 == 6)分别代表 V C Z N

  • N(Negative)

    符号整数进行运算时, N=1表明成果为负数, N=0表明成果>=0

  • Z(Zero)

    Z=1表明运算成果为0, Z=0表明成果为非0

  • C(Carry)

    加减法运算是否发生进位

  • V(Overflow)

    加减法运算时发生符号位溢出

浮点寄存器(FPU)

用于浮点数存储和运算, V0-V31

arm64汇编指南

常用指令

arm64汇编指南

算法指令

  • ADD 加

    ADD X0,X1,X2: X0=X1+X2

  • SUB 减

    SUB X0,X1,X2: X0==X1-X2

  • MUL 乘

    MUL X0,X1,X2: X0=X1*X2

  • SDIV 有符号数除法运算

    SDIV X0,X1,X2: X0=X1/X2

  • UDIV 无符号

  • CMP X28,X0

    X28,X0相减,更新CPSR中条件标志位

  • CMN CMN X28,X0

    X28,X0相加,更新CPSR中条件标志位

  • ADDS/SUBS

    S的指令运算成果会影响 CPSR中的条件标志位

条件跳转

  • B.cond

    B.cond label 若cond为真,则跳转到 label

  • CBNZ

    CBNZ Xn,label 若Xn!=0,则跳转到label

  • CBZ

    CBZ Xn,label 若Xn==0, 则跳转到label

无条件跳转

  • B

    B label 无条件跳转

  • BL

    BL label 无条件跳转,回来地址保存到LR寄存器中

  • BLR

    BLR Xn 无条件跳转到Xn寄存器的地址,回来地址保存到LR寄存器中

  • BR

    BR Xn 无条件跳转到Xn寄存器的地址

  • RET

    回来到LR中存储地址

逻辑指令

  • AND

    AND X0,X1,X2: X0=X1&X2

  • EOR

    X0=X1^X2

  • ORR

    X0=X1|X2

赋值指令

MOV

MOV X19,X1 X19=X1

地址偏移

ADR

ADR Xn,label Xn=PC+label

Load/Store指令

LDR Xn,addr Load

内存地址addr -> Xn

STR Xn,addr

Xn -> 内存地址addr

LDP Xn1,Xn2,addr

内存地址addr -> Xn1和Xn2

P(Pair)表明一对,一起操作两个寄存器

STP Xn1,Xn2,addr

Xn1和Xn2 -> addr

LDUR Xn,[base, #XXX]

addr(base+#XXX) -> Xn

STUR Xn,[base, #XXX]

Xn -> base+#XXX的内存地址

扩展含义

B 无符号8bit

SB 有符号8bit

H: 无符号116bit

SH 有符号16位

W 无符号32位

SW 有符号32位

函数调用汇编

界说一个最简略的函数调用

int addTwoValue(int a, int b) {
    return a + b;
}
int main() {
    return addTwoValue(1, 2);
}

查看汇编源码

main 函数

ZZDemo`main:
    0x1027ba1e8 <+0>:  sub    sp, sp, #0x20 ; 开辟0x30大小的栈空间
    0x1027ba1ec <+4>:  stp    x29, x30, [sp, #0x10] ;保存当时函数的x29(FP)和X30(LR)到栈中
    0x1027ba1f0 <+8>:  add    x29, sp, #0x10 ; 举高栈底,切换到子函数栈空间
    0x1027ba1f4 <+12>: stur   wzr, [x29, #-0x4]
    0x1027ba1f8 <+16>: mov    w0, #0x1 ; 第一个参数1保存到w0
    0x1027ba1fc <+20>: mov    w1, #0x2 ; 第二个参数2保存到w1
->  0x1027ba200 <+24>: bl     0x1027ba1c8  ;调用 addTwoValue函数
    0x1027ba204 <+28>: ldp    x29, x30, [sp, #0x10] ;从栈中康复当时函数的x29(FP)和X30(LR)
    0x1027ba208 <+32>: add    sp, sp, #0x20 ; 回收栈空间
    0x1027ba20c <+36>: ret  
ZZDemo`addTwoValue:
    0x100c3e1a8 <+0>:  sub    sp, sp, #0x10
    0x100c3e1ac <+4>:  str    w0, [sp, #0xc]
    0x100c3e1b0 <+8>:  str    w1, [sp, #0x8]
->  0x100c3e1b4 <+12>: ldr    w8, [sp, #0xc]
    0x100c3e1b8 <+16>: ldr    w9, [sp, #0x8]
    0x100c3e1bc <+20>: add    w0, w8, w9
    0x100c3e1c0 <+24>: add    sp, sp, #0x10
    0x100c3e1c4 <+28>: ret    

一般函数执行流程如下

  1. 分配栈空间
  2. 如果有子函数调用, 保存当时函数的形参到栈中
  3. 如果有子函数调用, 保存当时函数的FP和LR
  4. 举高栈底, 切换到子函数栈
  5. 设置子函数的实参
  6. 调用子函数,回来成果
  7. 康复当时函数形参
  8. 康复当时函数的FP和LR
  9. 其他操作
  10. 回收栈空间
  11. 回来

Objective-C汇编

OC中办法调用是通过消息机制来完成的

@implementation AppDelegate
- (void)printValue1:(NSString*)value1 value2:(NSString*)value2 {
    NSLog(@"%@, %@", value1, value2);
}
@end
int main() {
    AppDelegate *delegate = [[AppDelegate alloc] init];
    [delegate printValue1:@"111" value2:@"222"];
}

断点到函数处执行

register read
        x0 = 0x000000028261c0e0
        x1 = 0x00000001040d3109  "printValue1:value2:"
        x2 = 0x00000001040d4070  @"111"
        x3 = 0x00000001040d4090  @"222"
        x4 = 0x0000000000000004
        x5 = 0x0000000007600000
        x6 = 0x0000000000000000
        x7 = 0x0000000000000000
po $x0
<AppDelegate: 0x28261c0e0>

在 OC 中

x0 寄存器保存了对象自身

x1保存了办法名,

x2-x7保存了函数的其他参数