使用寄存器推送/弹出与Mov

时间:2015-12-22 08:37:48

标签: assembly x86 nasm

在x86_64(在Linux上使用nasm进行组装)中,我的直觉告诉我rax在(1)和(2)之后将具有相同的内容。

push qword 11               ; (1) Works!
pop rax

;mov rax, qword 11          ; (2) Doesn't Work!

但是(1)工作正常,(2)使我的程序段错误。这是怎么回事?

1 个答案:

答案 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非常好的反汇编程序。有关链接,请参阅 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 0x40push 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)很奇怪。