如何在ARM64汇编中用Clang的as计算标签差值消除硬编码长度
解决ARM64/MacOS汇编中消除硬编码字符串长度的问题
针对你的需求,有三种可行的方法来让汇编器自动计算字符串长度,替代硬编码的mov X2, #13:
方法1:使用.equ预定义长度常量
先在字符串末尾添加结束标签,用.equ计算并存储长度,再在指令中使用这个常量:
.global _main .align 2 _main: mov X0, #1 adr X1, helloworld mov X2, #str_length // 使用预定义的长度常量 mov X16, #4 svc 0 mov X0, #0 mov X16, #1 svc 0 helloworld: .ascii "Hello World!\n" helloend: // 字符串结束标签 .equ str_length, helloend - helloworld // 汇编时计算长度
这种方式会让汇编器预先算出helloend - helloworld的数值(这里是13),然后将mov X2, #str_length替换为mov X2, #13,完全符合AArch64指令的立即数要求。
方法2:使用ldr加载汇编时计算的常量
如果字符串长度超过AArch64mov指令支持的立即数范围(比如长度很大),可以用ldr配合伪指令加载常量:
.global _main .align 2 _main: mov X0, #1 adr X1, helloworld ldr X2, =(helloend - helloworld) // 加载汇编时计算的长度 mov X16, #4 svc 0 mov X0, #0 mov X16, #1 svc 0 helloworld: .ascii "Hello World!\n" helloend:
汇编器会把helloend - helloworld的结果存入程序的文字池(literal pool),然后通过PC相对寻址将值加载到X2寄存器,适合处理大数值。
方法3:运行时计算标签差值
如果需要在运行时动态计算长度,可以先将两个标签的地址加载到寄存器,再做减法:
.global _main .align 2 _main: mov X0, #1 adr X1, helloworld // 加载字符串起始地址到X1 adr X3, helloend // 加载字符串结束地址到X3 sub X2, X3, X1 // 运行时计算长度存入X2 mov X16, #4 svc 0 mov X0, #0 mov X16, #1 svc 0 helloworld: .ascii "Hello World!\n" helloend:
这种方式不需要汇编器预先计算长度,而是在程序运行时通过地址相减得到结果。
为什么你的原写法报错?
AArch64的mov指令要求#后面跟的是符合特定编码规则的立即数(可以通过移位、旋转得到的小数值)。虽然helloend - helloworld是汇编时可计算的常量,但直接写在mov X2, #后面时,GNU as(Clang自带的汇编器兼容该实现)无法在AArch64目标下正确处理这种表达式的fixup,导致出现unknown AArch64 fixup kind错误。通过.equ预定义常量、ldr =加载,或者运行时寄存器相减的方式,可以规避这个问题。
内容的提问来源于stack exchange,提问作者Mark Reed
相关产品推荐
相关产品推荐

