04 | 具体指令使用细节
#
4. 具体指令使用细节#
4.1 mov、movz、movn、movk#
4.1.1 movmov 指令的格式:mov {条件}{S} 目的寄存器,源操作数 mov 指令可以完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中 S 选项决定指令的操作是否影响 CPSR 状态寄存器中的条件标志位的值,当没有 S 时指令不更新 CPSR 中条件标志位的值。 示例:
mov x1, x2; 将寄存器 x2 的值传送到寄存器 x1mov PC, Xn; 将寄存器 Xn 的值传送到 PC,常用于子程序返回mov Xn, Xm, LSL#3; 将寄存器 Xm 的值左移 3 位后传送到 Xn
#
4.1.2 movz用于移动立即值。movz 将16位的立即值移至寄存器,并且该立即值之外的所有其他位均设置位 0。立即值可以移到左侧的 0、16、32、48. 格式如下:MOVZ Xd, #imm{, LSL #shift}
- shift: 立即值,向左移动的值。
- 默认是 0
- 32 位通用寄存器可以是 0、16
- 64 位通用寄存器可以是0、16、32、48。
- Xd:64 位通用寄存器
- imm:16 位无符号立即数,范围在 0 ~ 65535 之间
示例:
instruction value of x0movz x0, #0x1234 x0 = 0x1234movz x0, #0x1234, lsl #16 x0 = 0x12340000
instruction value of x0movz x0, #0x1234 x0 = 0x1234movz x0, #0x1234, lsl #32 x0 = 0x123400000000
#
4.1.3 movkmovk 移动立即数,但保持该寄存器的其他位不变(k 保持)。例如,假设你需要移动此值 0x7fb7fb1f88 到寄存器 X0.首先,你将使用 movz 指令移动前 16 位(0 - 15 位),因此将寄存器的其他位设置为 0。然后,使用 movk 指令移动后 16 位(16 - 31 位),将之前移动的值仍保留在寄存器中,并对其他位执行相同的操作。 示例:
instruction value of x0mov x0, xzr x0 = 0x0000000000000000movk x0, #0x0123, lsl #48 x0 = 0x0123000000000000movk x0, #0x4567, lsl #32 x0 = 0x0123456700000000movk x0, #0x89ab, lsl #16 x0 = 0x0123456789ab0000movk x0, #0xcdef x0 = 0x0123456789abcdef instruction value of x0mov x0, #0x1f88 x0 =0x1f88movk x0, #0xb7fb, lsl #16 x0 =0xb7fb1f88movk x0, #0x7f, lsl #32 x0 =0x7fb7fb1f88
#
4.1.4 movn用于赋值立即数的位掩码,此处 n 表示取反。例如想要将 0xffffffff0000ffff 赋值给 x0,只需要使用 movn 将向0xffff 移动到左边 16 位,得到值 0x00000000ffff0000,取反后值为0xffffffff0000ffff这。
instruction value of x0MOVN x0, 0xFFFF, lsl 16 | x0 = 0xffffffff0000ffff
#
4.2 mvnmvn 与 mov 差不多,唯一的区别是,它赋值的时候,先按位取反,在赋值。 mvn 指令的格式:mov {条件}{S} 目的寄存器,源操作数 示例:
mvn x1, 0 mvn x2, x1 -1
mvn x1, 0xff; x1 = 0x ffff ff00mvn x1, 0x00000007; x1 = 0x ffff fff8mvn x1, #4; x1 = -5, 4 转换成二进制是 00000100, 取反是 1111 1011 取原码 1000 0100 取补码 1000 0101 因为第一位是符号位,1 代表负数,0101 = 5, 所以结果是-5
mvn指令的主要完成以下功能:
- 向寄存器传送一个负数
- 生成位掩码
- 求一个数的反码
#
4.3 addadd 的指令格式:add {条件}{S} 目的寄存器,操作数 1,操作数 2 add 指令用于把 2 个数相加,并将结果放到目的寄存器中。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。 示例:
add Xn, X1, X2; Xn = X1 + X2add Xn, X1, #256; Xn = X1 + 256add Xn, X2, X3,LSL#1; Xn = X2 + (R3 << 1)
#
4.3.1 adcadc 的指令格式:adc {条件}{S} 目的寄存器,操作数 1,操作数 2 adc指令用于把 2 个操作数相加,再加上 CPSR 中 C 条件标志位的值,并将结果放到目的寄存器中。它使用一个进位标记为,这样就可以做到比 64 位大的数的加法。注意不忘记设置 S 后缀来进行进位标志。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。
#
4.4 subsub 的指令格式:sub {条件}{S} 目的寄存器,操作数 1,操作数 2 sub 指令用于把操作数 1 减去操作数 2,并将结果放到目的寄存器中。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。该指令可用于有符号或无符号的减法运算。
sub Xn, X1, X2; Xn = X1 - X2sub Xn, X1, #256; Xn = X1 - 256sub Xn, X2, X3,LSL#1; Xn = X2 -(R3 << 1)
#
4.4.1sbcsbc指令的格式为:sbc {条件}{s} 目的寄存器,操作数1,操作数2 sbc指令用于把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置s后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
#
4.5 mulmul指令的格式为:mul {条件}{s} 目的寄存器,操作数1,操作数2 mul指令完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为64位的有符号或无符号数。
mul x0, x1, x2 ; 将寄存器 x1 和 x2 的值相乘后结果保存到寄存器 x0 中
#
4.6 andand指令的格式为:and {条件}{s} 目的寄存器,操作数1,操作数2 and 指令用于两个操作数进行逻辑与运算,并把结果放到目的寄存器中。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。
and x0, x0, #0xf ; 将寄存器 x0 的值和常量 0xf 按位与后保存到寄存器 x0 中 and Xn, X1, #256; Xn = X1 & 256and Xn, X2, X3,LSL#1; Xn = X2 & (R3 << 1)
#
4.7 orrorr 指令的格式为:orr {条件}{s} 目的寄存器,操作数1,操作数2 orr 指令用于两个操作数进行逻辑或运算,并把结果放到目的寄存器中。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。
and x0, x0, #0xf ; 将寄存器 x0 的值和常量 0xf 按位或后保存到寄存器 x0 中 and Xn, X1, #256; Xn = X1 | 256and Xn, X2, X3,LSL#1; Xn = X2 | (R3 << 1)
#
4.8 eoreor 指令的格式为:eor {条件}{s} 目的寄存器,操作数1,操作数2 eor 指令用于两个操作数进行逻辑异或运算,并把结果放到目的寄存器中。操作数 1 应该是一个寄存器,操作数 2 可以是一个寄存器、被位移的寄存器、立即数。
and x0, x0, #0xf ; 将寄存器 x0 的值和常量 0xf 异或后保存到寄存器 x0 中 and Xn, X1, #256; Xn = X1 ^ 256and Xn, X2, X3,LSL#1; Xn = X2 ^ (R3 << 1)
#
4.9 tsttst 指令的格式为:tst {条件} 操作数1,操作数2 tst 指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。
mov w1, 0tst w1, 1
// 调试结果(lldb) register read/t cpsr cpsr = 0b01100000000000000000000000000000 ;执行tst命令之前(lldb) register read/t cpsr cpsr = 0b01000000000000000000000000000000 ;执行tst命令之后
#
4.10 ldrldr 指令的格式为:ldr {条件} 目的寄存器,<存储器地址> ldr 指令用于从存储器中将一个 32 位的字符数据传送到目的寄存器中。该指令通常用于从存储器中读取 32 位的字符数据到通用寄存器,然后对数据进行处理。当程序计数器 PC 作为目的寄存器时,指令从存储器中读取的字符数据被当前目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样。 示例:
ldr x0, [x1]; 将存储器地址为 x1 的字符数据读取存入到 x0 中。ldr x0, [x1, x2]; 将存储器地址为 x1+x2 的字符数据读取存入到 x0 中。ldr x0, [x1, #8]; 将存储器地址为 x1+8 的字符数据读取存入到 x0 中。ldr x0, [x1, x2]!; 将存储器地址为 x1+x2 的字符数据读取存入到 x0 中。 并将新地址 x1+x2 写入 x1 ldr x0, [x1, #8]!; 将存储器地址为 x1+8 的字符数据读取存入到 x0 中。 并将新地址 x1+8 写入 x1 ldr x0, [x1], x2; 将存储器地址为 x1 的字符数据读取存入到 x0 中。 并将新地址 x1+x2 写入 x1 ldr x0, [x1, x2, LSL#2]!; 将存储器地址为 x1 + x2 * 4 的字符数据读取存入到 x0 中。 并将新地址 x1 + x2 * 4 写入 x1 ldr x0, [x1], x2, LSL#2; 将存储器地址为 x1 的字符数据读取存入到 x0 中。 并将新地址 x1 + x2 * 4 写入 x1
#
4.10.1 ldrsb大体上与 ldr 相同,只不过 ldrsb 只读取一个字节,ldr 读取 32 个字节。
ldrsb w8, [sp, #7] ; 将栈内存 [sp + 7] 出的 低 1 字节的值读取到寄存器 w8 中
#
4.11 strstr 指令的格式为:str {条件} 源寄存器,<存储器地址> str 指令用于从源寄存器中将一个 32 位的字符数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令 LDR。 示例:
str x1, [x2], #8; 将 x1 中的字符数据写入 x2 位地址的存储器中,并将新地址 x2 + 8 写入 x2str x1,[x2, #8]; 将 x1 中的字符数据写入到 x2 + 8 为地址的存储器中
#
4.11.1 strb大体上与 str 相同,这里只存入一个字节
strb w8, [sp, #7] ; 将寄存器 w8 中的低 1 字节的值保存到栈内存 [sp + 7] 处
#
4.12 stpstb 入栈指令,str 的变种,可以同时操作 2 个寄存器。
stp x29, x30, [sp, #0x10] ; 将 x29, x30 的值存入 sp 偏移 16 个字节的位置
#
4.13 ldpldp 出栈指令,ldr 的变种,可以同时操作 2 个寄存器
ldp x29, x30, [sp, #0x10] ; 将 sp 偏移 16 个字节的值取出来,存入寄存器 x29 和寄存器 x30
#
4.14 cset比较指令,满足条件,则并置 1,否则 0。例如:
cmp w8, #2 ; 将寄存器 w8 的值和常量 2 进行比较cset w8, gt ; 如果是大于(grater than),则将寄存器 w8 的值设置为 1,否则设置为 0
#
4.15 adrpADRP + ADD表示取值,常用于取常量和全局变量的值,但是这里取的是地址值。 ADRP + ADD表示取值,常用于取常量和全局变量的值,但是这里取的是地址值。 ADRP + ADD表示取值,常用于取常量和全局变量的值,但是这里取的是地址值。 用来定位数据段中的数据用, 因为 aslr 会导致代码及数据的地址随机化, 用 adrp 来根据 pc 做辅助定位
#
4.16 跳转指令#
4.16.1 b跳转到某地址(无返回值),不会改变寄存器 lr(x30) 的值;一般是当前方法内的跳转,如 while 循环,if else 等。例如:
b LBB0_1 ; 直接跳转到标签 ‘LLB0_1’ 处开始执行
#
4.16.2 bl跳转到某地址(有返回),先将下一条指令地址(即函数返回地址)保存到寄存器 lr(x30) 中,再进行跳转;一般用于不同方法直接的调用。例如:
bl 0x100cfa754 ;
先将下一指令地址(‘0x100cfa754’ 函数调用后的返回地址)保存到寄存器 ‘lr’ 中,然后再调用 ‘0x100cfa754’ 函数
#
4.16.3 blr跳转到某寄存器(的值)指向的地址(有返回),先将下一条指令地址(即函数返回地址)保存到寄存器 lr(x30) 中,再进行跳转;例如:
blr x20 ; 先将下一指令地址(‘x20’指向的函数调用后的返回地址)保存到寄存器 ‘lr’ 中,然后再调用 ‘x20’ 指向的函数
#
4.16.4 br跳转到某寄存器(的值)指向的地址(无返回),不会改变寄存器 lr(x30) 的值。
#
4.16.5 brk跳转指令特殊的一种,很少见
#
4.17 ret子程序(函数调用)返回指令,返回地址已默认保存到寄存器 lr(x30)中。
#
4.18 csel示例:
public static int less(int a,int b){ if(a<=b){ return a; }else{ return b; }}
cmp x10, #7 // tag == 7?csel x12, x11, x10, eq
对于这种根据不同情况选择不同值的语句,ARM在设计时专门有一条强大的csel指令,专门做这个事情。 先用cmp指令比较两个值,然后设置条件:
- ge 是大于
- le 是小于
- eq 等于
格式如下:
csel xd, xn, xm, <comd>
<comd>
对应上面的 ge、le、eq。请参考参考条件码
csel 指令的意思是,如果条件满足,xd = xn,否则 xd = xm。例如:
cmp x10, #7 CSEL X0, X0, X1, ge
如果x10 大于 7,x0 = x0,否则 x0 = x1.
#
4.19 cset格式:CSET <xd> , <cond>
cset 条件设置指令
<comd>
代表条件指令 ge、le、eq等。请参考参考条件码
如果满足条件,xd = 1,否则 xd = 0。
示例
cmp x10, #7 CSET W0, EQ ; if (cond == true) W0 = 1, else W0 = 0
如果x10 == 7,则 w0 = 1,否则 w0 = 0