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

load single byte from label to register in arm64 using GAS

我想使用 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 的内存地址加载一个字节,而不是抛出汇编程序错误。

none of these work, but why?

因为只有 PC-relative 文字的加载指令是 ldrldrsw:

参见 ARMv8 Reference Manual 中的 Table C3-16。

正如 Siguza 所说,CPU 根本没有任何此类说明。 LDRB 支持的唯一寻址模式是寄存器基址 + register-or-immediate 偏移量,如您在体系结构参考手册(任何汇编程序员必读)中所见。 assembler 非常正确地拒绝 assemble 一条不存在的指令。

所以你无法在一条指令中实现你想要的;你需要两个。但是,这两条指令可能比您选择的指令更有效。标准成语used by compilers好像是:

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

这里adrp会用CHAR地址的前52位加载x19;该指令编码 CHAR 所在页面相对于当前指令页面的偏移量(此偏移量由 linker 或加载程序计算并填充),因此它产生position-independent 代码。然后地址的低12位通过寻址方式中的偏移量相加(限制为12位);此偏移量在 link 时已知,因为 load-time 重定位仅以页为单位或更多页发生。请注意,我们可以使用与加载目的地相同的寄存器来计算地址;我们不需要第二个。

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

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