使用GAS将标签中的单个字节加载到arm64中的寄存器中

问题描述

我想使用ldrb指令将单个字节从内存加载到寄存器中。但是,如果第二个操作数是一个标签,这似乎是不可能的。一个完整的最小可复制评论示例:

// GNU Assembly,aarch64 Linux

.data

.equ SYS_EXIT,93
.equ SUCCESS,0

CHAR:
    .byte 1

.text

.global _start

_start:
    // none of these work,but why?
    // ldrb w19,CHAR       // invalid addressing mode at operand 2
    // ldrb w19,=CHAR      // invalid addressing mode at operand 2
    // ldrb w19,[CHAR]     // 64-bit integer or SP register expected at operand 2
    // ldr w19,CHAR        // loads 4 bytes instead of 1 byte

    // I have to do this but it's verbose and clunky
    ldr x20,=CHAR
    ldrb w19,[x20]
    // is there any way to coalesce the above 2 instructions into 1?

    mov x8,SYS_EXIT
    mov x0,SUCCESS
    svc 0

理想情况下,我想写ldrb <reg>,<label>,例如ldrb w19,CHAR,并且让它像我期望的那样从CHAR的内存地址中加载一个字节,而不是引发汇编错误

解决方法

正如Siguza所说,CPU根本没有任何这样的指令。 LDRB支持的唯一寻址模式是寄存器基址+寄存器或立即数偏移量,正如您在体系结构参考手册中所见(任何汇编程序员都必须阅读此书)。汇编程序非常正确地拒绝汇编一条不存在的指令。

因此,您无法在一条指令中实现您想要的;你需要两个。但是,这两个指令可能比您选择的指令更有效。标准习惯用法used by compilers似乎是:

adrp x19,CHAR
ldrb w19,[x19,#:lo12:CHAR]

这里adrp将用x19地址的高52位加载CHAR;该指令对CHAR所在页面的偏移量进行编码,该偏移量相对于当前指令的页面(此偏移量由链接器或加载程序计算并填充),以便生成与位置无关的代码。然后,在寻址模式下通过偏移量将地址的低12位相加(限制为12位)。该偏移量在链接时是已知的,因为加载时重定位仅以页面或更多页面为单位发生。注意,我们可以使用与加载目的地相同的寄存器来进行地址计算。我们不需要第二个。

与您的方法相比,这避免了使用文字池的需要,从而节省了额外的内存负载和8个字节的空间。

如果您仍然觉得它太“冗长”,则可以始终将其包装在宏中。

,

这些都不起作用,但是为什么呢?

因为唯一采用PC相对文字的加载指令是ldrldrsw

screenshot of table C3-16

请参见ARMv8 Reference Manual中的表C3-16。