我一直在阅读由GCC生成的不同ARM汇编代码,并发现了一些规范中没有提到的内容。
movw r0, #39784
movt r0, 1
显然,第一个操作是将值39784移入r0的低16位,但movt操作符的'1'操作数是奇数,因为它没有哈希标记。我原本认为即时值需要哈希标记,这是有条件的吗?还是我漏掉了什么神奇的东西?
我一直在阅读由GCC生成的不同ARM汇编代码,并发现了一些规范中没有提到的内容。
movw r0, #39784
movt r0, 1
显然,第一个操作是将值39784移入r0的低16位,但movt操作符的'1'操作数是奇数,因为它没有哈希标记。我原本认为即时值需要哈希标记,这是有条件的吗?还是我漏掉了什么神奇的东西?
对于ARMv7,GNU gas
的行为取决于.syntax
支持两种略有不同的ARM和THUMB指令语法。默认使用旧式样式,其中ARM和THUMB指令有自己的单独语法。可以通过.syntax指令选择新的统一语法,它具有以下主要特点:
- 立即数操作数不需要#前缀。
而https://sourceware.org/binutils/docs-2.26/as/ARM_002dChars.html#ARM_002dChars则说明:
要表示立即数操作数,可以使用“#”或“$”。
对于ARMv8,#
始终是可选的
https://sourceware.org/binutils/docs-2.26/as/AArch64_002dChars.html#AArch64_002dChars文档:
“#”可选择用于指示立即操作数。
测试
Ubuntu 16.04,Binutils 2.26.1。
v7.S:
/* These fail */
mov r0, 1
mov r0, 0x1
/* These work */
mov r0, #1
mov r0, #0x1
mov r0, $1
mov r0, $0x1
.syntax unified
mov r0, 1
mov r0, #1
mov r0, 0x1
mov r0, #0x1
mov r0, $1
mov r0, $0x1
v8.S:
mov x0, 1
mov x0, #1
mov x0, 0x1
mov x0, #0x1
组装:
arm-linux-gnueabi-as v7.S
aarch64-linux-gnu-as v8.S
结果:v8 成功,v7 在没有 # 的 divided
行失败:
v7.S:1: Error: immediate expression requires a # prefix -- `mov r0,1'
v7.S:2: Error: immediate expression requires a # prefix -- `mov r0,0x1'
待办事项
嗯,但是对于某些v7指令,#
实际上是可选的,例如movw
和movt
没有错误:
movw r0, 1
movt r0, 0x1
但是存在以下错误:
movw r0, $1
movt r0, $0x1
ARM参考手册
ARMv8-fb手册本身就包含有汇编/反汇编的建议/要求,位于C1.2“结构A64汇编语言”中:
A64汇编语言不需要“#”字符来引入常量立即操作数,但汇编器必须允许使用带或不带“#”字符引入的立即值。Arm建议A64反汇编器在立即操作数前输出“#”。
个人建议
在您的v7代码中使用.syntax unified
,并且在v7或v8中不要对任何文字使用#
。
统一语法更新更好,那些#
和$
标记只是更多的代码噪音。
Linux内核也支持我的观点: https://github.com/torvalds/linux/blob/v4.19/arch/arm/include/asm/unified.h#L23
ldr =#
只在 v7 上有效,因此最好对于 v7 和 v8 都只使用 =
而不带 #
。 - Ciro Santilli OurBigBook.com
gcc
和gas
是亲密的。我不会将编译器输出作为编写汇编程序的指南。但是查看编译器正在执行的操作很有用。 - artless noise