首页 11-ARM64汇编
文章
取消

11-ARM64汇编

iOS汇编

  • 真机: Arm64汇编
    • 寄存器
    • 指令
    • 堆栈
  • 模拟器: x86汇编

寄存器

  • 通用寄存器

    • 64bit: x0 ~ x28

    • 32bit: w0 ~ w28(属于x0 ~ x28的低32bit)

    • x0 ~ x7通常拿来存放函数的参数,更多的参数使用堆栈来传递

    • x0通常拿来存放函数的返回值

      int add(int a, int b);
      int sub(int a, int b);
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      .text ; 代码段
      .global _add, _sub
      _add:
      ; 相加
      add x0, x0, x1
      ret
          
      ; sub 函数实现
      _sub:
      ; 相减
      sub x0, x0, x1
      ret
      
  • 程序计数器

    • pc (Program Counter)
    • 记录CPU当前记录的是哪一条指令
    • 存储着当前CPU正在执行的指令的地址
    • 类似于8086汇编的ip寄存器
  • 堆栈指针

    • sp (Stack Pointer)
    • fp (Frame Pointer),也就是x29
  • 链接寄存器

    • lr (Link Register),也就是x30
    • 存储着函数的返回地址的下一条指令地址
  • 程序状态寄存器

    • cpsr (Current Program Status Register)

      CPSR01

      CPSR02

    • spsr (Saved Program Status Register),异常状态下使用

指令

  • mov指令

    • mov指令的格式为:
    • mov {条件} {s} 目的寄存器, 源操作数
    • mov 指令壳完成从另一个寄存、被移位的寄存器或将一个立即数加载到目的寄存器。其中 s 选项决定指令的操作是否影响CPSR中条件标志位的值,当前没有S时指令不更新CPSR中条件标志位的值。
    1
    2
    3
    4
    5
    6
    7
    
    .text ; 代码段
    .global _test
    _test:
    ; mov 指令
    mov x0, #0x8
    mov x1, x0
    ret
    
  • ret指令

    • 函数返回
    • lr (x30)寄存器的值赋值给pc寄存器
  • add指令

    • add指令的格式为:
      • add {条件} {s} 目的寄存器, 操作数1, 操作数2
    • add指令用于把两个操作数相加,并将结构存放到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    .text ; 代码段
    .global _test
      
    _test:
    ; add 指令
    mov x0, #0x1
    mov x1, #0x2
    add x2, x0, x1
    ret
    
  • sub指令

    • sub指令的格式为:
    • sub {条件} {s} 目的寄存器, 操作数1, 操作数2
    • sub指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或者无符号数的减法运算。
    1
    2
    3
    4
    5
    6
    7
    8
    
    .text ; 代码段
    .global _test
      
    _test:
    mov x0, #0x5
    mov x1, #0x2
    sub x2, x0, x1
    ret
    
  • cmp指令

    • cmp指令格式为:
    • cmp {条件} 操作数1, 操作数2
    • cmp指令用于把一个寄存器的内容和另一个寄存的内容或者立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存才结果,只更改条件标志位。标志位表示的是操作数1与操作数2的关系(大、小、相等),例如,当操作数1大于操作数2,则伺候的有GT后缀的指令将可以执行。

    • 将2个寄存器相减
    • 相减的结果会影响cpsr寄存器的标志位
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    .text ; 代码段
    .global _test
      
    _test:
    ; cmp 指令
    mov x0, #0x2
    mov x1, #0x1
    cmp x0, x1
    ret
    ; x0: #0x2 x1: #0x1  cpsr: 0010 0000 ...
    ; x0: #0x1 x1: #0x1  cpsr: 0110 0000 ...
    ; x0: #0x1 x1: #0x2  cpsr: 1010 0000 ...
    
  • b指令

    • b指令的格式为:

    • b {条件} 目标地址

    • b指令是最简单的跳转指令。一旦遇到一个b指令,arm处理器将立即跳转到给定的目标地址,从哪里继续执行。注意存储在跳转指令中的实际是相对当前pc值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对地址)。它是24位有符号数,左移两位后有符号位扩展为32为,表示的有效偏移为26位(前后32MB的地址空间)。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      .text ; 代码段
      .global _test
          
      _test:
      ; b 跳转指令
      b mycode
      mov x0, #0x2
      mycode: ; 跳转到这执行
      mov x1, #0x6
      ret
      
    • 可带条件跳转,一般跟cmp配合使用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      
      .text ; 代码段
      .global _test
          
      _test:
      ; b 指令带条件
      mov x0, #0x1
      mov x1, #0x1
      cmp x0, x1
      b.eq mycode ; cmp比较后相等跳mycode
      mov x0, #0x7
      ret
      mycode:
      mov x1, #0x8
      ret
      
  • bl指令

    • bl指令的格式为:
    • bl {条件} 目标地址
    • bl指令 是另一个带返回的跳转指令,但跳转之前,会在寄存器R14中保存PC当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。

    • 执行的操作
      • 将下一条指令的地址存储到lr (x30)寄存器中
      • 跳转到标记处开始执行代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    .text ; 代码段
    .global _test
      
    ; 内部私有函数
    mycode:
    mov x0, #0x2
    mov x1, #0x3
    add x2, x0, x1
    ret
      
    _test:
    ; bl 带返回跳转指令
    ; 如果是b指令跳转的话,执行到ret后不会跳b的下一条指令
    bl mycode 
    mov x3, #0x4
    mov x4, #0x5
    ret
    
  • 条件域

    • 当处理器工作在ARM状态时,几乎所有的指令均根据CPSR中条件码的状态和指令的条件域有条件的执行。当指令的执行条件满足是,指令被执行,否则指令被忽略。
    • 每一条ARM指令包含4位的条件码,唯一指令的最高4位[31:28]。天剑码共有16种,每种条件可用两个字符表示,这两个字符可以添加在指令助记符的后面和指令同时使用。例如,跳转指令可以加上后缀EQ变为beq或者b.eq表示”相等则跳转”,即当CPSR中的Z标志置位时发生跳转。

    • EQ: equal,相等
    • NE: not equal,不相等
    • GT: great than,大于
    • GE: great equal,大于等于
    • LT: less than,小于
    • LE: less equal,小于等于
    条件码助记符后缀标志含义
    0000EQZ置位相等
    0001NEZ清零不相等
    0010CSC置位无符号数大于或等于
    0011CCC清零无符号数小于
    0100MIN置位负数
    0101PLN清零正数或零
    0110VSV置位溢出
    0111VCV清零未溢出
    1000HIC置位Z清零无符号数大于
    1001LSC清零Z置位无符号数小于或等于
    1010GEN等于V带符号数大于或等于
    1011LTN不等于V带符号数小于
    1100GTZ清零且(N等于V)带符号大于
    1101LEZ置位或(N不等于V)带符号数小于或等于
    1110AL忽略无条件执行
  • 内存操作

    • load,从内存中读取数据

      • ldr指令格式为:
      • ldr {条件} 目的寄存器, <存储器地址>
      • ldr指令用于从存储器中将一个32位的子数据传送到目的寄存器中。该指令通常用于从存储器总读取32的子数据到通用寄存器,然后对数据新型处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当做目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样。

      • ldr (正数)ldur (负数)
      • ldp (p是pair的简称),从内存中读取数据放到一对寄存器中。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      ; x2的地址存放的是int a = 10
      ldp w1, w2, [x2, #0x10]
      ; 意思x2地址 + 0x10,将前4个字节先赋给w1,后4个字节赋给w2
      ; 示例
      ; 将存储器地址为x1的字数据读入寄存器x0
      ldr x0, [x1] 	
      ; 将存储器地址为x1 + x2的字数据读入到寄存器x0
      ldr x0, [x1, x2] 
      ; 将存储器地址为x1 + 8的字数据读入寄存器x0
      ldr x0, [x1, #8]
      ; 将存储器地址为x1 + x2的字数据读入寄存器x0,并将新地址x1 <-x1+x2
      ldr x0, [x1, x2]!
      ; 将存储器地址为x1 + 8的字数据读入寄存器x0,并将新地址x1 <-x1+8
      ldr x0, [x1, #8]!
      ; 将存储器地址为x1的字数据读入寄存器x0,并将新地址x1+x2写入x1
      ldr x0, [x1], x2
      ; 将存储器地址为x1 + x2 * 4的字数据读入寄存器x0,并将新地址x1 + x2*4写入x1
      ldr x0, [x1, x2, lsl#2]!
      ; 将存储器地址为x1的字数据读入寄存器x0,并将新地址x1+x2*4写入x1
      ldr x0, [x1], x2, lsl#2
      
    • store,往内存中写入数据

      • str指令的格式为:
      • str {条件} 源寄存器, <存储器地址>
      • str指令用于从源寄存器中将一个32为的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令ldr

      • strstur
      • stp
      • 零寄存器,里面存储的值是0
        • wzr (32bit, Word Zero Register),4个字节
        • xzr (64bit),8个字节
      1
      2
      3
      4
      5
      
      ; 示例
      ; 将x0中的子数据写入以x1为地址的寄存器中,并将新地址x1 + 8写入x1
      str x0, [x1], #8
      ; 将x0中的字数据写入以x1 + 8为地址的寄存器中
      str x0, [x1, #8]
      

寻址

  • 立即寻址

    • 立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。
    1
    2
    3
    
    add x0, x0, #1
    add x0, x0, #0x3f
    ; 在以上两条指令中,第二个源操作数即为立即数,要求以“#”为前缀,对于以十六进制表示的立即数,还要求在“#”后加上“0x”或“&”
    
  • 寄存器寻址

    • 寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类未处理器经常采用的一种方式,也就是一种知性效率较高的寻址方式。
    1
    
    add x0, x1, x1  ; x0 <- x1 + x2
    
    • 该指令的执行效果是将寄存器x1和x2的内容相加,其结构存放在寄存器x0中
  • 寄存器间接寻址

    • 寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。
    1
    2
    3
    
    add x0, x1, x2  ; x0 <- x1 + x2
    ldr x0, [x1]		; x0 <- [x1], 取决于x0有多少个字节
    str x0, [x1]  	; x1 <- [x1]
    
    • 第一条指令中,以寄存器x2的值作为操作数的地址,在存储器中取得一个操作数后与x1相加,结果存在寄存器x0中。
    • 第二条指令将以x1的值为地址的存储器中的数据传送到x0中。
    • 第三条指令将x0的值传送到以x1的值为地址的存储器中。
  • 基址变址寻址

    • 基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基址附近的地址单元。
    1
    2
    3
    4
    
    ldr x0, [x1, #4]			; x0 <- [x1 + 4]
    ldr x0, [x1, #4]! 		; x0 <- [x1 + 4], x1 <- x1 + 4
    ldr x0, [x1], #4			; x0 <- [x1], x1 <- x1 + 4
    ldr x0, [x1, x2]			; x0 <- [x1 + x2]
    
    • 第一条指令中,将寄存器x1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器x0中。
    • 第二条指令中,将寄存器x1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器x0中,然后x1的内容自增4个字节。
    • 第三条指令中,以寄存器x1的内容作为操作数的有效地址,从而取得操作数存入寄存器x0中,然后x1的内容自增4个字节。
    • 第四个指令中,将寄存器x1的内容加上寄存器x2的内容形成操作数的有效地址,从而取得操作数存入寄存器x0中。
  • 多寄存器寻址

    • 采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。中寻址方式可以用一条指令完成传送最多16个通用寄存器的值。
    1
    2
    3
    4
    5
    
    ldmia x0, {x1, x2, x3, x4}
    ; x1 <- [x0]
    ; x2 <- [x0 + 4]
    ; x3 <- [x0 + 8]
    ; x4 <- [x0 + 12]
    
    • 该指令的后缀IA表示在每次执行完嘉爱/存储操作后,x0按字长度增加,因此,指令可将连续存储单元的值传送到x1 ~ x4
  • 相对寻址

    • 与基址变址寻址方式类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加后得到操作数的有效地址。
  • 堆栈寻址

    • 堆栈是一种数据结构,按先进后出(First In Last Out, FILO)的方式工作,使用一个称作堆栈指针的专用寄存器知识当前的操作位置,堆栈指针总是指向栈顶。

函数的堆栈

  • 函数的类型
    • 叶子函数
      • 什么是叶子函数? 就是函数内部不再调用其他函数就是叫叶子函数
    • 非叶子函数

生成汇编文件

1
xcrun --sdk iphoneos clang -S -arch arm64 main.c -o main.s
本文由作者按照 CC BY 4.0 进行授权

10-AT&T汇编

12-iOS签名机制