ARM64 指令用法学习整理

1. CBZ

当我们谈论ARM64指令集时,CBZ(Compare and Branch on Zero)是一种条件分支指令。它用于在寄存器上进行比较,并且如果该寄存器的值为零,则跳转到指定的标签或地址。

CBZ指令的基本语法如下:

CBZ <寄存器>,<标签>
其中,<寄存器>是一个通用寄存器(例如X0、X1等),而<标签>是跳转的目标标签或地址。

CBZ指令执行的操作如下:

比较寄存器中的值是否为零。
如果寄存器的值为零,则根据指定的标签或地址进行无条件跳转,程序继续执行跳转后的那条指令。
如果寄存器的值不为零,则程序继续顺序执行下一条指令。
下面是一个示例代码段,演示了如何使用CBZ指令:

CBZ X0, Label     ; 如果X0的值为零,跳转到Label标签处
...               ; 如果X0的值不为零,继续执行下一条指令

Label:
… ; 如果X0的值为零,从此处开始执行
在上述示例中,如果寄存器X0的值为零,则会跳转到标签Label处执行相应的代码;否则,程序将继续执行下一条指令。

值得注意的是,CBZ指令用于判断寄存器值是否为零,它不会修改寄存器的值。因此,在使用CBZ指令前,需要确保寄存器中的值已经被正确赋值。

2. adrp

ADRP指令的基本语法如下:

ADRP <寄存器>, <标签>
其中,<寄存器>是一个通用寄存器(例如X0、X1等),而<标签>是一个数据或代码段的标签。

ADRP指令执行的操作如下:

计算<标签>相对于当前指令的地址偏移量。
将地址偏移量的高32位部分(29至0位)左移12位,并存储到指定的<寄存器>中。
低12位将被清零。这是因为ARM64指令集的访存操作要求地址必须在4字节对齐上。
以下是一个示例代码段,演示了如何使用ADRP指令:

ADRP X0, Label     ; 将Label标签的地址的高32位存储到X0寄存器中
...                ; 执行其它指令
LDR X1, [X0, #Offset]   ; 加载Label标签地址加上偏移量Offset处的数据到X1寄存器中
...

Label:

在上述示例中,ADRP指令将Label标签的地址的高32位存储到X0寄存器中。然后,通过LDR指令从X0寄存器加上偏移量Offset处加载数据到X1寄存器中。

ADRP指令的主要目的是加载全局数据或代码段的地址高位,以便进行跳转、访问全局变量或执行函数等操作。通过ADRP指令和后续的LDR指令,可以实现更灵活的地址计算和访存操作。

在Linux内核启动代码primary_entry中,使用adrp指令获取Linux内核在内存中的起始页地址,页大小为4KB,由于内核启动的时候MMU还未打开,此时获取的Linux内核在内存中的起始页地址为物理地址。
adrp通过当前PC地址的偏移地址计算目标地址,和实际的物理无关,因此属于位置无关码。

[arch/arm64/kernel/head.S]
SYM_CODE_START(primary_entry)
    ......
	adrp	x23, __PHYS_OFFSET
	and	x23, x23, MIN_KIMG_ALIGN - 1  // KASLR offset, defaults to 0
    ......
SYM_CODE_END(primary_entry)

[arch/arm64/kernel/head.S]
#define __PHYS_OFFSET	KERNEL_START  // 内核的物理地址
[arch/arm64/include/asm/memory.h]
// 内核的起始地址和结束地址在vmlinux.lds链接脚本中定义
#define KERNEL_START    _text         // 内核代码段的起始地址,也即内核的起始地址
#define KERNEL_END		_end          // 内核的结束地址

[arch/arm64/include/asm/memory.h]
/*
 * Alignment of kernel segments (e.g. .text, .data).
 *
 *  4 KB granule:  16 level 3 entries, with contiguous bit
 * 16 KB granule:   4 level 3 entries, without contiguous bit
 * 64 KB granule:   1 level 3 entry
 */
#define SEGMENT_ALIGN		SZ_64K

[include/linux/sizes.h]
#define SZ_64K				0x00010000

adrp指令根据PC的偏移地址计算目标页地址。
首先adrp将一个21位有符号立即数左移12位,得到一个33位的有符号数(最高位为符号位),接着将PC地址的低12位清零,这样就得到了当前PC地址所在页的地址,然后将当前PC地址所在页的地址加上33位的有符号数,就得到了目标页地址,最后将目标页地址写入通用寄存器。
此处页大小为4KB,只是为了得到更大的地址范围,和虚拟内存的页大小没有关系。
通过adrp指令,可以获取当前PC地址±4GB范围内的地址。
通常的使用场景是先通过adrp获取一个基地址,然后再通过基地址的偏移地址获取具体变量的地址。

3. adr_l

adr_l是Linux内核定义的一个宏,用于获取基于PC相对偏移+/- 4 GB内的符号地址。在内核上下文中,使用adrp和add指令获取符号地址,而在内核模块上下文中,使用mov指令获取符号地址。

	[arch/arm64/include/asm/assembler.h]
	.macro	adr_l, dst, sym
#ifndef MODULE  /* 内核上下文中 */
	adrp	\dst, \sym  /* 获取符号所在页的基地址 */
	/* :lo12:\sym - 获取符号sym的低12位地址。
	   符号所在页的基地址加上低12位地址就得到符号的完整地址 */
	add	\dst, \dst, :lo12:\sym
#else  /* 内核模块上下中 */
	/* 将符号的bit[64:48]地址加载到dst寄存器中,同时做
	   overflow check,其他位清零 */
	movz	\dst, #:abs_g3:\sym
	/* 将符号的bit[47:32]地址加载到dst寄存器中,不做
	   overflow check,其他位保持不变 */
	movk	\dst, #:abs_g2_nc:\sym
	/* 将符号的bit[31:16]地址加载到dst寄存器中,不做
	   overflow check,其他位保持不变 */
	movk	\dst, #:abs_g1_nc:\sym
	/* 将符号的bit[15:0]地址加载到dst寄存器中,不做
	   overflow check,其他位保持不变 */
	movk	\dst, #:abs_g0_nc:\sym
#endif
	.endm

4. adr

adr指令根据PC的偏移地址计算目标地址。偏移地址是一个21位的有符号数,加上当前的PC地址得到目标地址。adr可以获取当前PC地址±1MB范围内的地址。下面是adr指令的编码格式。立即数占用21位。
以上是adr族三条指令用法参看了大神 业余程序员plus的分析,欲知详情,请戳原文链接:ARMv8汇编指令-adrp、adr、adr_l

5. blr

blr是ARM64指令集中的一条指令,用于返回到调用函数的地址并跳转到该地址执行。

blr指令的基本语法如下:

blr <寄存器>
其中,<寄存器>是一个通用寄存器(例如X0、X1等),它包含了调用函数的地址。

当执行到blr指令时,它会将寄存器中保存的地址作为返回地址,并跳转到该地址继续执行代码。这实现了函数调用的返回操作。通常,在函数调用完成后,使用blr指令返回到调用者的地址,以便程序继续执行下一条指令。

需要注意的是,blr指令只能在AArch64的执行状态下使用,不可用于AArch32代码。另外,返回地址由寄存器提供,因此寄存器中保存的地址应正确地指向调用函数的位置,否则可能导致未定义的行为或异常。

请注意,在实际的代码中,通常会有一系列先前的操作来设置返回地址,并且blr指令的使用方法可能因编程语言和上下文而有所不同。

BL 和 BLR 执行结果是将 PC 寄存器值的下一个值(也就是PC+8)放到链接寄存器 LR中, 然后将目的子程序的地址放到 PC 中。 BLR的结果 与 BL类似,但是新的PC的值是从特定的寄存器(如x2)取得。。例如:blr x2
【ARM 常见汇编指令学习 1 – 跳转指令 BL 与 BLR 区别】
举一反三:B指令只是单纯的跳转到目标地址执行(单程式/一去不复返式跳转)
BL 和 BLR 指令 跳转到目标地址执行完后,还会回到链接寄存器LR中保存的地址。

6.

7.

4.

4.

4.

麻烦帮忙详细讲解下arm64指令

以上部分内容来自ChatGPT

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: msub指令ARM64体系结构中的一条指令,用于进行带符号整数的乘法和减法操作。 在ARM64体系结构中,msub指令的格式为msub Rd, Rn, Ra, Rm,其中Rd表示目标寄存器,Rn、Ra和Rm表示源寄存器。 msub指令的作用是将源寄存器Rm中的值与Rn中的值进行乘法运算,然后将结果与Ra中的值进行减法运算,最后将结果存储到目标寄存器Rd中。 这条指令在一些特定的运算场景下非常有用。例如,当我们需要计算一个多项式的值时,可以使用msub指令来进行高效的乘法和减法操作。此外,在一些计算密集型的算法中,使用msub指令可以提高计算性能。 需要注意的是,msub指令是带符号整数的运算指令,它将使用源寄存器中的值的符号进行运算。因此,在使用msub指令时要确保寄存器的值符合运算要求,否则可能会得到不正确的结果。 总而言之,msub指令ARM64体系结构中用于进行带符号整数乘法和减法操作的一条指令,可以在某些特定的运算场景下提高计算性能。 ### 回答2: msub 指令是在ARM64架构中的一条乘加指令。在ARM处理器中,乘加指令用于执行两个操作数的乘法运算,然后将乘法结果与第三个操作数相加。 msub指令的完整形式为 msub Rd, Rn, Rm, Ra,其中Rd表示目标寄存器,Rn表示第一个操作数,Rm表示第二个操作数,而Ra表示第三个操作数。 该指令的执行顺序为:先执行Rn与Rm的乘法运算,然后得到的乘法结果与Ra进行相加,最后的结果将存储在目标寄存器Rd中。 msub指令在一些特定的应用中非常有用,比如数字信号处理和一些科学计算中的数值计算。通过将乘法和加法操作的结果直接汇总在一条指令中执行,可以提高程序的运行效率,减少指令的执行次数,从而优化程序性能。 总之,msub指令ARM64架构中的一条乘加指令,用于执行两个操作数的乘法运算,然后将乘法结果与第三个操作数相加,最后将结果存储在目标寄存器中。它在一些特定的应用中具有重要的作用,可以提高程序的运行效率和性能。 ### 回答3: msub是ARM64指令集中的一条指令,它用于执行64位整数的乘法和减法操作。msub指令的格式为msub Rd, Rn, Rm, Ra,其中Rd是目标寄存器,Rn、Rm是源寄存器,而Ra是要减去的操作数。 msub指令的作用是计算乘法操作的结果并与原有的数值相减,然后将结果存储到目标寄存器Rd中。这实际上等价于执行(Rn * Rm) - Ra的计算。 ARM64架构的msub指令在处理需要同时进行乘法和减法操作的情况下非常高效,因为它可以在一个指令周期内完成这两个计算步骤。 使用msub指令可以减少指令的数量,提高程序的执行效率。尤其是在需要频繁进行乘法和减法操作的算法和应用中,msub指令可以大幅度提高运算效率。 总而言之,msub指令ARM64指令集中用于执行64位整数乘法和减法操作的一条指令。它能够通过一次指令完成乘法和减法计算,并将结果存储到指定的寄存器中,从而提高程序的执行效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值