使用 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 文字的加载指令是 ldr
和 ldrsw
:
参见 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。
如果您仍然觉得它太“冗长”,您可以随时将其包装在宏中。
我想使用 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 文字的加载指令是 ldr
和 ldrsw
:
参见 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。
如果您仍然觉得它太“冗长”,您可以随时将其包装在宏中。