问题描述
我想使用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相对文字的加载指令是ldr
和ldrsw
:
请参见ARMv8 Reference Manual中的表C3-16。