Skip to content

ARM64 Instructions notes

jiaxw32 edited this page May 12, 2023 · 2 revisions

汇编指令

csel

Q: 解释下面汇编指令

csel x8, x9, x1, lt

这条指令是 ARM64 汇编语言中的指令,其作用是在两个寄存器之间选择一个,根据另一个寄存器的值来选择。

  • csel:指令助记符,意为“条件选择”(Conditional SELect)。
  • x8:目标寄存器,用于存储选择的值。
  • x9:第一个源寄存器,用于在条件成立时提供一种选择。
  • x1:第二个源寄存器,用于在条件不成立时提供另一种选择。
  • lt:条件,表示如果条件寄存器中的数小于,则选择第一个源寄存器的值,否则选择第二个源寄存器的值。

这条指令的含义是:如果条件寄存器中的数小于,则将 x9 的值存储到 x8 中,否则将 x1 的值存储到 x8 中。所以,它选择 x9 或 x1 中的一个值存储到 x8 中,这个选择取决于条件寄存器中的值。

tbnz

Q: 解释下面汇编指令

tbnz w8, #0x1f, #0x1009a9e54

这条指令其作用是测试一个位是否为零,如果不为零,则跳转到指定的地址。这里对指令中涉及的一些术语进行解释:

  • tbnz:指令助记符,意为“测试位并跳转”(Test Bit aNd Jump)。
  • w8:表示要测试的寄存器,这里是第8个通用寄存器 w8。
  • #0x1f:表示要测试的位的位置,即第31位,这个值用十六进制表示。
  • #0x1009a9e54:表示跳转的地址,这个值也用十六进制表示。

这条指令的含义是:测试 w8 寄存器的第31位是否为零。如果该位不为零,则跳转到地址 0x1009a9e54(即跳转到一个指定的代码段或函数),否则不跳转继续执行后续指令。如果跳转,则执行从该地址开始的指令。

举个例子,如果 w8 的值为 0x80000000,那么它的第31位是 1,不为零,这时就会跳转到地址 0x1009a9e54,否则,如果 w8 的值为 0x00000000,那么它的第31位是 0,这时就不会跳转,继续执行后续指令。

cbnz w9, loc_10211b040

Q: 解释下面汇编指令

cbnz w9, loc_10211b040

该含义是:如果寄存器w9的值不为0,则跳转到地址loc_10211b040处执行代码,否则继续执行后面的指令。具体来说,这里涉及到以下几个术语的解释:

• cbnz:条件分支指令,作用是判断寄存器值是否为0,如果不为0则跳转。 • w9:表示寄存器的名称,这里是通用寄存器w9,其值作为条件分支的依据。 • loc_10211b040:是跳转目的地地址,这里使用的是标号(label)的形式,而不是直接使用地址值。这个标号是程序员自己起的名字,可以代表代码段、函数、循环等。在实际汇编中,编译器会将其转换为对应的地址值。

因此,以上指令的作用是:如果w9寄存器的值不为0,则跳转到loc_10211b040处执行代码,否则继续执行后面的指令。具体执行的后续指令会根据程序代码的具体情况而定。

ldxr

Q: 请解释下面汇编代码

 ldxr w8, [x0]

ldxr w8, [x0] 汇编指令的作用是原子地将 x0 指向的内存地址上的值读取到寄存器 w8 中。其中,ldxr 是 ARMv8 架构中的一种原子读取指令,可以保证在读取内存值的同时,其它线程无法修改该内存地址的值,从而实现了原子读取操作。这种操作常用于一些需要在多个线程间共享变量的场景下,能够有效避免由于多个线程对同一内存地址进行读写操作而导致的程序错误。

ldxr 与 stxr 示例

Q: 请解释下面汇编代码

ldxr       w8, [x0]
add        w8, w8, #0x1
stxr       w9, w8, [x0]
cbnz       w9, loc_10211b040
ret

这段 ARM64 汇编代码的作用是实现一个原子加一的操作,它的伪代码如下:

Load w8, [x0]      ; w8 = [x0]
Add w8, w8, #0x1   ; w8 = w8 + 1
Store w9, w8, [x0] ; [x0] = w8, 如果写入成功w9=0,否则w9=1
CompareAndBranchNotZero w9, loc_10211b040 ; 如果w9 != 0, 则跳转到loc_10211b040
Return ; 返回

这段代码的实现使用了 ARMv8 指令集提供的原子读-改-写指令,并且使用了 Check-And-Set 模式实现原语操作,以保证该操作是原子的。具体来讲:

  1. 执行 ldxr w8, [x0] 指令来原子性地读取寄存器 x0 指向的内存地址上的值,结果保存到寄存器 w8 中。
  2. 执行 add w8, w8, #0x1 指令来将寄存器 w8 中的值加 1。
  3. 执行 stxr w9, w8, [x0] 指令原子性地将寄存器 w8 中的值写回到寄存器 x0 指向的内存地址上,写回的操作结果保存在寄存器 w9 中。如果写回操作成功,则 w9 的值为 0,否则为 1。
  4. 执行 cbnz w9, loc_10211b040 指令,如果 w9 不为 0,则跳转到标签 loc_10211b040,否则继续执行下一条指令。
  5. 执行 ret 指令,返回到调用该函数的地址。

简单来说,这段代码的作用就是将寄存器 x0 指向的内存地址上的值原子性地加 1。如果写回操作成功,函数返回;否则,函数跳转到一个错误处理代码块。

ccmp

请解释下面汇编指令

ccmp x21, x8, #0, eq

ccmp x21, x8, #0, eq 是 ARMv8 架构中的一条条件比较指令,其作用是比较 x21 和 x8 的值,如果相等则将条件码寄存器(condition code register)的 Z 标志位置为 1,否则将其置为 0。其中,#0 表示比较的值为零,eq 表示只有在相等的情况下才会设置 Z 标志位。

条件码寄存器一般用于存储条件比较指令的结果,它包含了一些标志位,包括 Z(零位)、N(负位)、C(进位或借位)和 V(溢出)等,用于指示前面指令执行的结果中是否满足特定条件。在条件分支、循环等控制语句中,程序会根据条件码寄存器的标志位来判断是否跳转到特定的代码逻辑中。

因此,ccmp x21, x8, #0, eq 这条指令的作用是比较 x21 和 x8 的值,如果相等则将条件码寄存器的 Z 标志位置为 1,否则将其置为 0。这通常用在条件分支语句中,用于判断是否执行某段特定的代码逻辑。

cset

请解释下面指令

cset       w0, eq

cset w0, eq 是一个 ARMv8 指令,用于将条件码寄存器中的某个标志位的值复制到指定的寄存器中。本指令将条件码寄存器的“等于”(eq)标志位的值复制到 w0 寄存器中。

条件码寄存器包含一些标志位,包括零位 Z、负位 N、进位或借位 C 和溢出 V 等。这些标志位是由执行前一个指令所得到的结果决定的。当执行的比较指令满足条件时,产生的结果可能会将条件码寄存器中某个标志位置为1。cset 指令可以将这些标志位的值直接复制到另一个寄存器中。

例如,如果执行之前存在条件比较指令 cmp x0, x1,那么在比较过程中,条件码寄存器的 eq 标志位会被设置为 1,如果我们执行 cset w0, eq 指令,则会将 w0 寄存器的值设置为 1。

这个指令通常用于在程序中实现比较的结果,它可以使程序员不必手动检查条件码寄存器的标志位,从而更加简单和方便。

b.hs

Q: 请解释下面指令

cmp x23, x21
b.hs #0x1095b2850

  • cmp x23, x21:这是一个条件比较指令,比较 x23 和 x21 的值。它会设置条件码寄存器中的各种标志位,如比较结果为零则将 Z 标志位置为 1、比较结果为负则将 N 标志位置为 1、比较过程中出现了进位或借位则将 C 标志位置为1,以及比较的操作数出现了溢出则将 V 标志位置为 1 等等。这里仅仅进行了一次比较,比较的结果会对后面的 b.hs 指令产生影响。
  • b.hs #0x1095b2850:这是一个条件分支指令,意思是如果条件码寄存器中的 C 和 V 标志位都被清零,就跳转到某个地址(这里是 0x1095b2850,即一个指令地址)。.hs 后缀表示条件代码条件为 “higher or same”(大于等于)。因为在 ARMv8 中无符号数的比较可以看作是有符号数比较的一个特殊情况,因此 “higher or same”表示无符号比较的结果为 “greater than”(大于)或 “equal to”(等于)。也就是说,如果 x23 大于等于 x21,那么 b.hs 指令就会跳转到指定的地址。

因此,整个指令的意思是:比较 x23 和 x21 的值,如果 x23 大于等于 x21,就跳转到某个指令地址。

ldrsw

Q: 请解释下面指令

ldrsw x23, [x8, #0xa04]

这条指令的作用是将内存地址 x8 加上偏移量 0xA04 所得到的地址上的有符号 32 位整数(signed 32-bit integer)加载到寄存器 x23 中。

具体解释如下:

• ldrsw:Load Register Signed Word, ARM64 架构中 word 长度为 32 位,表示加载有符号的 32 位整数 • x23:表示将加载的值存储到寄存器 x23 中 • [x8, #0xa04]:表示从内存地址 x8 加上偏移量 0xA04,即从地址为 x8 + 0xA04 的内存位置处获取要加载的值。

在整个指令执行完毕后,寄存器 x23 中将保存从指定内存地址上加载的 32 位有符号整数。

寄存器

x29 寄存器的作用

在ARM64中,x29寄存器是帧指针寄存器(Frame Pointer Register),也称为FP寄存器。它用于指向当前函数栈帧的底部,用于帧栈帧的维护和函数调用的管理。

在函数执行的过程中,函数参数、局部变量以及返回地址等数据都是存储在函数的栈帧中的。x29寄存器存储的值就是当前函数栈帧的底部地址,也就是所谓的帧指针。通过x29寄存器指向的地址,可以找到当前函数栈帧内各个局部变量和函数参数的位置。

在函数调用时,编译器会将当前函数的返回地址、调用者函数的FP寄存器、函数参数等信息存储到当前函数的栈帧中,然后将当前函数的FP寄存器指向栈帧的底部,这样就可以维护函数调用的堆栈信息。当函数返回时,FP寄存器会被还原到调用函数的FP寄存器的值,返回地址也会从栈中取出,控制会返回到调用函数。

总的来说,x29寄存器在ARM64架构中扮演了非常重要的角色,它的作用是维护函数调用的堆栈信息,使得函数调用与返回的过程变得可控。

x20 寄存器

在ARM64汇编中,x20寄存器是通用寄存器之一,通常用作全局指针寄存器(Global Pointer Register,缩写为GP寄存器)。GP寄存器在C语言中有很多作用,通常情况下,它被用来保存一个指向全局变量的指针,后续代码可以通过间接寻址方式来访问全局变量。

在ARM64的函数调用中,x20寄存器还可以作为一个传参寄存器,用于向C函数中传递第五个参数,如果有必要的话。x20寄存器还可以被用作临时寄存器,用于在代码运行时暂时存储一些中间结果。

总之,在ARM64汇编中,x20寄存器的具体作用取决于代码的实现方式和具体上下文环境,可能是全局指针寄存器、传参寄存器或临时寄存器等。但是它的主要的作用还是用于存储全局变量地址,以及作为传参寄存器。

Clone this wiki locally