NASM Intel 64位模式:为什么32位常量0xffffffff会导致“警告:有符号双字值超出界限”

Ral*_*alf 4 assembly x86-64 nasm immediate-operand

在 NASM (2.14.02) 中,指令add rbx, 0xffffffff导致

警告:有符号双字值超出界限 [-w+number-overflow]

我知道 64 位模式下的算术逻辑运算仅接受 32 位常量,但 0xffffffff 仍然是 32 位宽。

为什么 NASM 发出警告,为什么它假设一个有符号常量?它是否将 32 位符号扩展-1为 64 位-1(0xffffffffffffffff)并因此出现溢出?0x7fffffff 工作时不会发出警告。

我能以某种方式说服 NASM 这不是一个有符号常量,而是一个无符号常量吗?

jco*_*der 6

0x00000000FFFFFFFF 无法编码为 32 位符号扩展立即数,并且当 x86 立即数比操作数大小窄时,它们始终是符号扩展的。


您的理论有些正确,指令将指定的值添加到寄存器中。无法对将 0xffffffff 添加到寄存器的指令进行编码,因为将立即数写入 64 位寄存器的唯一可用编码是或。只有一种特殊编码addadd r/m64, sign_extended_imm32add r/m64, sign_extended_imm8mov(带有寄存器目标)可以使用 64 位立即数。

明显的编码(填充0xffffffffimm32)实际上会将 0xffffffffffffffff 添加到 rbx 中,这不是您所要求的,并且与add rbx, -1

无法将值编码到 32 位字段中,从而导致将 0x00000000ffffffff 添加到 rbx。Asm 源使用值,而不是立即数的位模式。

你需要做这样的事情:-

mov rcx, 0xffffffff     ; RCX = 0xFFFFFFFF = 0x00000000FFFFFFFF
add rbx, rcx
Run Code Online (Sandbox Code Playgroud)

mov ecx, 0xffffffff也会以同样的方式设置RCX;NASM 将为您优化为高效的 5 字节指令(在写入 32 位寄存器时隐式零扩展为 64 位),而不是需要带有 64 位立即数的 10 字节指令;有关机器代码中不同形式的 MOV 的更多信息,请参阅另一个问答。

  • 并且 NASM 将有助于优化为“mov ecx, 0xffffffff”(5 字节),而不是“mov rcx, 0xffffffff”(10 字节,因为它必须使用 imm64 形式,而不是 7 字节的 sign_extended_imm32 形式)。对于其他汇编程序(例如 GAS 或 YASM),它们不适合您,您应该自己为适合 32 位零扩展但不符号扩展的值执行此操作。(GAS 将与 `as -Os` 一起使用) (4认同)
  • @Ralf:是的,确切地说,汇编器不是编译器,因此它受到机器代码中可编码内容的限制。(它不会将一条源指令转换为多条机器指令。至少 NASM 不会;为了方便起见,一些 RISC ISA 有一些“伪指令”。)x86-64 仅具有一种特殊形式的 64 位立即数的“mov”。否则,当您使用 64 位操作数大小时,imm8 或 imm32 会进行符号扩展。[写入 32 位寄存器时的隐式零扩展](/sf/ask/782399621/) 是一个单独的东西,在将常量放入寄存器时很有用。 (2认同)