GAS Intel 语法中如何比较寄存器与变量地址?
结论
在 GAS 的 Intel 语法中,可以尝试用 OFFSET FLAT:digitSpace 将符号地址作为立即数传给 cmp:
.intel_syntax noprefix
cmp rcx, OFFSET FLAT:digitSpace
不过,这种写法不适用于任意 64 位地址。x86-64 的 cmp r64, imm 指令只能编码 32 位立即数,比较时会将其符号扩展到 64 位。因此,符号地址必须能用这种形式表示,链接重定位也要符合相应限制。对于常见的 PIE 程序,或地址不受这些限制的代码,应先用 lea 将地址放入寄存器,再进行寄存器比较。
为什么直接写符号会比较变量内容
问题中的 NASM 代码是:
cmp rcx, digitSpace
在 GAS Intel 语法中,裸符号通常表示内存操作数。这样写会读取 digitSpace 所在内存中的值,再与 rcx 比较,而不会把符号地址作为比较值。GAS 提供 OFFSET FLAT: 来表示将符号地址作为立即数;GCC 文档也用这种写法展示 Intel 汇编中的符号地址形式。GAS 文档 · GCC 文档
通用的 64 位写法
在 x86-64 下,更稳妥的做法是用 lea 取地址,再比较两个寄存器:
.intel_syntax noprefix
lea rbx, [rip + digitSpace]
cmp rcx, rbx
lea 会计算 digitSpace 的有效地址并放入 rbx,不会读取变量内容。[rip + digitSpace] 是常见 64 位位置无关代码使用的 RIP 相对寻址形式。如果 rbx 已经存有该地址,也可以直接执行 cmp rcx, rbx。
使用 OFFSET FLAT 时的限制
cmp rcx, OFFSET FLAT:digitSpace
只有当符号地址能用 cmp 的符号扩展 32 位立即数表示,且汇编和链接所用的重定位方式允许时,这种写法才成立。否则可能出现链接重定位错误,或无法表示目标地址。OFFSET FLAT: 解决的是如何把符号作为立即数地址来写的问题,并不会让 cmp 获得可编码的完整 64 位立即数。
如果要比较 rcx 与任意正常的 64 位变量地址,用 lea 加寄存器比较是通用做法。确认代码布局和链接方式符合上述限制后,再考虑使用 OFFSET FLAT:。
备注:内容仅供参考。
