在x86_64(在Linux上使用nasm进行组装)中,我的直觉告诉我rax
在(1)和(2)之后将具有相同的内容。
push qword 11 ; (1) Works!
pop rax
;mov rax, qword 11 ; (2) Doesn't Work!
但是(1)工作正常,(2)使我的程序段错误。这是怎么回事?
答案 0 :(得分:3)
这些说明应该完全相同。
NASM显然不会让你通过像push qword 11
之类的东西来指定更长的编码。 (YASM)。如果您希望稍后使用不同的常量修补二进制文件,或者您的目标是进行代码对齐,则会出现问题。它使用的较小编码运行正好相同。如果它不安全,它就不会这样做。
; test-encoding.asm
...
mov eax, 11
mov rax, qword 11
push qword 11 ; yasm rejects this with "invalid size for operand 1"
; perhaps because there is no push imm64 encoding
pop rax
; push dword [rsi] ; neither NASM nor YASM can assemble this :/
push word [rsi] ; neither NASM nor YASM can assemble this :/
nasm -felf64 test-encoding.asm
objconv -fyasm test-encoding.o /dev/stdout
mov eax, 11 ; 0042 _ B8, 0000000B
mov eax, 11 ; 0047 _ B8, 0000000B
push 11 ; 004C _ 6A, 0B
pop rax ; 004E _ 58
objconv是Agner Fog非常好的反汇编程序。有关链接,请参阅x86 wiki。 objdump -Mintel -d
也有效,但对潜在问题没有有用的评论。
在64位模式下,push
的默认操作数大小为qword。所以push imm8
符号扩展常数编码只需要两个字节。
因此推/弹编码更短。如果您的其余代码有问题,可能会出现问题,只能显示不同的代码对齐或不同的填充?
英特尔的手册说,操作数大小可以被覆盖为32或16位,带有REX前缀(带REX.W=0
)或操作数大小前缀(66H
),但是NASM,YASM和所有GNU都拒绝汇编push dword [rsi]
或pushl (%rsi)
。使用db 0x40
在push qword [rsi]
之前手动插入REX.W = 0前缀会使反汇编程序阻塞它
所以我可以在64位模式下进行16位推送和64位推送组装/反汇编,但不是32位推送。 :/实际上在实际硬件上运行push rdx
,REX.W = 0(db 0x40
)或REX.W = 1(db 0x48
)给出相同的结果:64位推送完整寄存器,将rsp递减8。
这不是英特尔手册第一次出错,但我发现push dx
按预期汇编并运行(将rsp递减2)很奇怪。