ARM64汇编 ============ 寄存器 ---------- 寄存器是CPU的一个组成部分,里面存放着指令、数据和地址等供CPU计算使用,速度比内存快。寄存器分为通用寄存器和专用寄存器 arm64寄存器如下 .. image:: res/register.png - 通用寄存器 1) arm64提供了31个64位通用寄存器,x0-x30 2) 可以通过w0-w30来访问这31个64位寄存器的低32位,写入时会将高32位清零 - 浮点寄存器 v0-v31,一共有32个浮点寄存器,每个寄存器的大小是128位,分别可以用不同的方式来访问不同的位数 1) Bn: 8位 2) Hn: 16位 3) Sn: 32位 4) Dn: 64位 5) Qn:128位 - 特殊寄存器 1) 程序计数器:即PC,保存着当前CPU执行指令的地址不能用作算术指令的源或目的地以及用作加载或存储指令给 2) 堆栈指针: sp, 即x31, 指向堆栈的顶部,sp不能被大多数指令引用,但一些算术指令如ADD指令,可以读写当前的堆栈指针来调整函数中的堆栈指针,每个异常级别都有一个专用的SP寄存器 fp: 即x29, 帧指针,指向当前frame的栈底,也就是高地址 3) 链接寄存器:lr, 即x30,存储着函数的返回地址 4) 程序状态寄存器: cpsr, 这个寄存器是按位起作用的,每一位都有专门的作用。 spsr, 当发生异常时,cpsr会存入spsr直到异常恢复在复制回cpsr .. image:: res/CPSR01.png .. image:: res/CPSR02.png 在arm64中不再单一使用cpsr寄存器来保存当前状态,而是用PSTATE来保存处理器的状态 PSTATE包括以下的系统寄存器 **NZCV寄存器:保存条件标志** 可读可写的寄存器 .. image:: res/nzcv.png **DAIF寄存器:DAIF状态** 4种事件的mask .. image:: res/naif.png **CurrentEL:当前的exception level** arm64有4个exception level,当前的EL,保存在这个寄存器中,这个寄存器是只读寄存器 .. image:: res/currentEL.png **SPSel: 当前栈的选择** 在EL3, EL2, EL1是可以选择SP是使用SP_ELx还是SP_EL0,通过这个寄存器设置 .. image:: res/spsel.png **PAN: privileged access never** .. image:: res/pan.png **UAO: user access override** .. image:: res/uao.png 以上的寄存器可以通过 ``mrs`` 或 ``msr`` 指令进行访问 - 函数返回值问题 整型返回值被保存在x0和x1中,而浮点值则保存在v0-v3中,同时使用多个寄存器可以返回一个小型结构体类型的返回值。如果返回值为比较大的结构体,那么寄存器可能就变得不够用了,此时就需要调用者做出一些配合, 调用者会在一开始为该结构体分配一块内存,然后将其地址写到x8寄存器中,在设置返回值的时候,直接往该地址中写入数据即可 - 函数参数问题 一般来说,函数参数(整型参数、地址)不超过8个的话,会存放在x0-x7里面,如果浮点型数据或者超过8个,会在调用函数之前将这些参数入栈,函数调用完之后出栈 模式与异常等级 ----------------- 模式 ^^^^ - 用户模式(USR): ARM处理器正常程序执行状态 - 快速中断模式(FIQ): 高速数据传输或通道处理 - 外部中断模式(IRQ): 通用的中断处理 - 管理模式(supervisor): 操作系统使用的保护模式 - 数据访问终止模式(abort): 当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护 - 系统模式(system): 运行具有特权的操作系统任务 - 为定义指令终止模式(UND): 当未定义的指令执行时进入该模式 - hyp: 用于虚拟化扩展 - monitor: 用于security扩展 异常等级 ^^^^^^^^^^^ - EL0: 非特权用户模式 - EL1: 操作系统内核 - EL2: Hypervisor,虚拟扩展 - EL3: secure monitor,安全扩展,实现EL0和EL1的secture和Non-secure之间的切换,可以起到物理屏障安全隔离作用 模式与异常等级之间的关系 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - user模式: 只能在EL0执行 - monitor模式:只能在secure的EL3执行 - hyp模式:只能在non-secure的EL2执行,虚拟机 - system, supervisor, abort, undefined, IRQ, FIQ模式:依赖于secure模式 .. image:: res/ExpetionLevel1.png 寻址方式 --------- 立即寻址 ^^^^^^^^^ 立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数,这个操作数被称为立即数,对应的方式也称为立即寻址 :: ADD W0, W1, #0x05 寄存器寻址 ^^^^^^^^^^^ 寄存器寻址也就是利用寄存器中的数值作为操作数,这是一种常见的方式,也是效率比较高的寻址方式 :: ADD W0, W1, W2 寄存器间接寻址 ^^^^^^^^^^^^^^^^ 寄存器间接寻址就是以寄存器中值作为操作数的地址,而操作数本身存放在存储器中 :: ADD W0, W1, [W2] LDR W3, [W0] 基址变址寻址 ^^^^^^^^^^^^^ 基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元, 常见的有以下几种形式 :: LDR W0, [W1, #4] ;将寄存器w1中内容加上4作为地址,然后将该地址处的数据存入寄存器w0中 LDR W0, [W1, #4]! ;将寄存器w1中内容加上4作为地址,然后将该地址处的数据存入寄存器w0中,然后w1中的内容自增4 LDR W0, [W1], #4 ;将寄存器w1中的内容作为地址,然后将该地址处的数据存入w0中,并将w1中内容自增4 LDR W0, [W1, W2] ;将w1和w2中的内容相加作为地址,然后将该地址处的数据存入w0 基本操作 ---------- 分配和初始化全局变量 ^^^^^^^^^^^^^^^^^^^^^^ - 定义一个未初始化的static静态局部变量和全局变量 :: .byte expressions .byte2 expressions .hword expressions .short expressions .4byte expressions .word expressions .long expressions .8byte expressions .quad expressions .ascii "string" ;带'\n' .szciz "string" ;不带'\n' .string "string" .float flonums .single flonums .double flonums 对齐aligning ^^^^^^^^^^^^^^ :: .align abs-expr, abs-expr, abs-expr ;第一个abs-expr:对齐的size ;第二个abs-expr:填充 ;第三个abs-expr: 可选,对齐应该跳过的最大字节数 .balign[lw] abs-expr, abs-expr, abs-expr .skip size, fill .space size, fill ;分配一大块内存并将其全部初始化到相同的值,可以使用这两个指令 .equ symbol, expression .set symbol, expression ;宏定义,等价于define .equiv symbol, expression ;宏定义,如果已经定义过了则产生error .global symbol ;声明,声明后,所有文件都可以使用 .comm symbol, length 函数 ^^^^^^ :: .size name, expression .type name, type_description 条件 ^^^^ :: .if expression .ifdef symbol .inndef symbol .else .end include文件 ^^^^^^^^^^^^ :: .include "file" 宏 ^^^^ :: .macro macname .manro macname macargs ... .endm .exitm 如果宏使用参数,那么在宏体中使用该参数时添加前缀 "\" 指令 ----- 常用指令 .. image:: res/normal.png 大多数指令都是可以带条件的,条件码如下 .. image:: res/ope.png - mov指令格式为 :: mov.{条件}{s} 目的寄存器,原操作数 mov指令可以完成从另一个寄存器、移位寄存器或将一个立即数加载到目的寄存器,其中s选项决定指令的操作是否影响cpsr中的条件标志位的值,当没有s时指令不更新cpsr中条件标志位的值 :: .text .global _test _test: mov w1, 8 mov w2, w1 ret - mvn指令格式为 :: mvn.{条件}{s} 目的寄存器,源操作数 mvn指令可完成从另一个寄存器被移位的寄存器或将一个立即数加载到目的寄存器,与mov指令不同之处在于传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中,其中s选项决定 指令的操作是否影响cpsr中条件标志位的值,当没有s时指令不更新cpsr中条件标志位的值 :: .text .global _test _test: mov w1, 0 mvn w2, w1 ret - add指令的格式为 :: add.{条件}{s} 目的寄存器, 操作数1, 操作数2 add指令用于把两个操作数相加,并把结果存放到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或是一个立即数 :: .text .global _test _test: mov w1, 2 mov w2, 4 add w0, w1, w2 ret - adc指令的格式为 :: adc.{条件}{s} 目的寄存器, 操作数1, 操作数2 adc指令用于把两个操作数相加,再加上cpsr中的C条件标志位的值,并将结果存放到目的寄存器中。它使用一个进位标志位,这样就可以做比64位大的数的加法,注意不要忘记 设置s后缀来更改进位标志,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数 - sub指令的格式为 :: sub.{条件}{s} 目的寄存器, 操作数1,操作数2 sub指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或 无符号数的减法运算 :: .text .global _test _test: mov w1, 4 mov w2, 2 sub w0, w1, w2 ret - sbc指令的格式为 :: sbc.{条件}{s} 目的寄存器,操作数1, 操作数2 sbc指令用于把操作数1减去操作数2,再减去cpsr中的C条件标志位的反码,并将结果存放到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。 该指令使用进位标志来标识借位,这样就可以做大于32位的减法,注意不要忘记设置s后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算 - mul指令格式为 :: mul.{条件}{s} 目的寄存器,操作数1, 操作数2 mul指令完成操作数1和操作数2的乘法,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位,其中操作数1和操作数2均为64位的有符号或无符号数 :: .text .global _test _test: mov w1, 4 mov w2, 2 mul w0, w1, w2 ret - and指令格式为 :: and.{条件}{s} 目的寄存器,操作数1, 操作数2 and指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器或一个立即数。该指令常用于屏蔽操作数1的某些位 :: .text .global _test _test: mov w1, 4 and w0, w1, 3 ;保持w1的0位和1位,其余位清零 ret - orr指令格式为 :: orr.{条件}{s} 目的寄存器, 操作数1, 操作数2 orr指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位 :: .text .global _test _test: mov w1, 4 orr w0, w1, 3 ;设置w1的0, 1位,其余位保持不变 ret - eor指令格式为 :: eor.{条件}{s} 目的寄存器,操作数1,操作数2 eor指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中,操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位 :: .text .global _test _test: mov w1, 4 mov w0, w1, 3 ;反转w1的0,1位,其余位保持不变 - tst指令格式为 :: tst.{条件} 操作数1, 操作数2 tst指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位与运算,并根据运算结果更新cpsr中条件标志位的值,操作数1是要测试的数据,而操作数2是一个位掩码。该指令一般用来检测是否设置了特定的位 :: .text .global _test _test: mov w1, 0 tst w1, 1 - str指令格式为 :: str.{条件} 源寄存器, <存储器地址> str指令用于从源寄存器中将一个64位或32位(看使用rn还是wn)的字节数据传送到存储器中. str的示例与ldr一起说明 - strb strb指令格式为 :: strb.{条件} 源寄存器,<存储器地址> strb: (store register byte) 将寄存器中的值写入到内存中(只存储一个字节) :: strb w8, [sp, #7] ;将寄存器w8中的低1个字节保存到栈内存[sp + 7] 处 - adr主要用于形成pc的相对地址,把相对地址Load到寄存器中 :: adr ,