【问题标题】:How 'push imm' encodes?'push imm' 如何编码?
【发布时间】:2012-07-02 09:40:23
【问题描述】:

> 说:

| Opcode* | Instruction | Op/En | 64-Bit Mode | Compat/Leg Mode | Description |
|      6A | PUSH imm8   | C     | Valid       | Valid           | Push imm8.  |
|      68 | PUSH imm16  | C     | Valid       | Valid           | Push imm16. |
|      68 | PUSH imm32  | C     | Valid       | Valid           | Push imm32. |

# cat -n test.asm

 1  bits 64
 2
 3  push byte 12
 4  push word 12
 5  push dword 12
 6  push qword 12
 7

# nasm test.asm

 test.asm:5: error: instruction not supported in 64-bit mode
  1. 为什么第 5 行是非法的?我认为它与“PUSH imm32”匹配。

  2. 为什么第 6 行是合法的?它与“PUSH imm8/PUSH imm16/PUSH imm32”中的任何一个都不匹配。

请帮帮我!

======测试======

    I think that the Intel manual is right, the 'push imm' 
    instructions do have three encoding form:
    Form 1: 6a XX
    Form 2: 66 68 XX XX 
    Form 3: 68 XX XX XX XX

    What we are arguing is the implemental specific behavior of the NASM.

    In NASM 2.04rc1(the above example I given):
    1. The 'byte' in 'push byte imm' direct the NASM use the Form 1, 
       no matter how long the imm given in the instruction it is, the imm
       was trunked to a byte in final machine code. 
    2. The 'word' in 'push word imm' direct the NASM use the Form 2,
       no matter how long the imm given in the instruction it is, the imm 
       was trucked or zero-extended to a word in final machine code.
    3. The 'qword' in 'push dword imm' direct the NASM use the Form 3,
       no matter how long the imm given in the instruction it is, the imm 
       was trucked or zero-extended to a dword in final machine code.
       Note: the 'qword' in the instruction but 'dword' in final machine
       code, which made us confused.
    4. if none of 'byte', 'word', 'qword' is given, the NASM use the From 3.

请看下面的例子:

# cat -n push.asm
1  bits 64
2
3  push byte 0x21
4  push byte 0x4321
5
6  push word 0x4321
7  push word 0x654321
8
9  push qword 0x654321
10
11  push 0x21
12  push 0x4321
13  push 0x654321
14  push 0x87654321
15  push 0xa987654321
16
# nasm -v
NASM version 2.04rc1 compiled on Feb 21 2009
# nasm push.asm -o push.bin
push.asm:4: warning: signed byte value exceeds bounds
push.asm:7: warning: word data exceeds bounds
# ndisasm -b 32 push.bin // 'ndisasm -b 64 push.bin' not works right in this version of NASM.
00000000  6A21              push byte +0x21
00000002  6A21              push byte +0x21
00000004  66682143          push word 0x4321
00000008  66682143          push word 0x4321
0000000C  6821436500        push dword 0x654321
00000011  6821000000        push dword 0x21
00000016  6821430000        push dword 0x4321
0000001B  6821436500        push dword 0x654321
00000020  6821436587        push dword 0x87654321
00000025  6821436587        push dword 0x87654321

In newer NASM, the behavior changes:
(1) 'push 0x21' was encoded to '6A21' in NASM 2.10.01,
    while '6821000000' in NASM 2.04rc1; 
(2)'push dword 0x654321' in 64 bit mode was allowed 
    in NASM 2.10.01 and encoded to '6821436500'

【问题讨论】:

    标签: assembly x86 nasm intel


    【解决方案1】:

    手册有误。 (顺便说一句,这不是唯一的错误)

    在 64 位模式下,没有 32 位推送。 push 是为数不多的不带 REX.W 前缀提升为 64 位的指令之一,您不能将其降级。

    编辑:实际上,我的手册版本说得对:

    推送符号扩展的 imm32。 堆栈指针是 减少的大小 堆栈指针。

    所以在 64 位模式下,这意味着“推送一个 qword,从立即数扩展符号”。

    【讨论】:

    • 根据ref.x86asm.net有一个支持m32的64位push指令(FF,操作码扩展6)。不确定这是否是手册中的错误。
    • @Polynomial push r/m32 在 64 位模式下无效。手册中的那一行是正确的,但push imm32 的那一行并不像@harold 所说的那样。
    • 这是英特尔文档中众多的错别字和错误之一。最好的办法是仔细检查 AMD 文档中的内容。
    • 感谢您的澄清 :)
    • 我困惑了几天。谢谢!
    【解决方案2】:

    编辑:下面的文字是错误的 w.r.t。立即。

    这是它的工作原理......

    无论操作数大小是多少,在 64 位模式下,您只能使用 push 将堆栈指针前移 2 或 8 个字节,并在如此分配的空间中写入 16 位或 64 位值。

    因此,您不能推送 8 位寄存器或 32 位寄存器,因为它们不会进行零或符号扩展。但您可以推送 16 位或 64 位寄存器。

    立即数有点不同。

    push imm8(操作码 6Ah 后跟 imm8)将首先将 8 位立即数操作数符号扩展为 64 位,然后压入生成的 8 字节值。

    push imm32(操作码 68h 后跟 imm32)将首先将 32 位立即数操作数符号扩展为 64 位,然后压入生成的 8 字节值。

    push imm16(操作数大小前缀 66h,操作码 6Ah 后跟 imm16)会将不带任何扩展名的 16 位立即操作数作为 2 字节值推送。

    当您说push qword 12 时,NASM 似乎有点宽容。但如果您使用不能表示为有符号 32 位整数的值,它将发出警告,例如push qword 0x80000000push qword 2147483648push qword -2147483649

    This is the relevant piece of documentation:
    ELSE IF SRC is immediate byte
      THEN TEMP ← SignExtend(SRC); (* extend to operand size *)
    ELSE IF SRC is immediate word (* operand size is 16 *)
      THEN TEMP ← SRC;
    ELSE IF SRC is immediate doubleword (* operand size is 32 or 64 *)
      THEN
        IF operand size = 32
          THEN TEMP ← SRC;
          ELSE TEMP ← SignExtend(SRC); (* extend to operand size of 64 *)
    FI;
    

    现在,我的 NASM 版本(2011 年 7 月 15 日编译的 2.09.10)实际上并未针对 push dword 12 发出警告或错误。如果您的确实如此,那是一个错误。

    看起来我在 NASM 中发现了一个错误。它将push word 12 组装成 66h、6Ah、0Ch。 IOW,它将push imm8 的操作码用于push imm16。这不可能。无论操作数大小前缀如何,字节操作数仍然是字节操作数。只有“字”操作数可以扩展或缩减为双字和四字操作数。一直都是这样,同一条指令的一对操作码,一个对字节操作数进行操作,另一个对(q/d)字操作数进行操作,操作数大小前缀会影响“字”的“字性”操作数。 64位模式下push word 12的正确编码应该是66h、68h、0Ch、0h。

    第 5 行是非法的,因为在 64 位模式下您不能压入 32 位操作数。您只能推送 64 位操作数(push byte 12 实际上是 push byte-sign-extended-to-qword 12)或 16 位操作数。看一个解释push操作的伪代码:

    IF in 64-bit mode (* stack-address size = 64 *)
      THEN
        IF operand size = 64
          THEN
            RSP ← RSP − 8;
            Memory[RSP] ← TEMP; (* Push quadword *)
          ELSE (* operand size = 16 *)
            RSP ← RSP − 2;
            Memory[RSP] ← TEMP; (* Push word *)
    FI;
    

    我无法说出确切的原因,但有两个:

    1. 这样实现很方便
    2. 设计者试图降低未对齐堆栈错误的可能性

    【讨论】:

    • push imm16 编码的有趣之处在于,真的看起来不太对
    • @harold 是的。操作码映射清楚地表明:68h 用于PUSH Iz,6Ah 用于PUSH Ib
    • 我尝试了更新的 NASM。它的行为与旧的不同。 link of the test result
    • @yangwen 2.04rc1 compiled on Feb 21 20092.09.10 compiled on Jul 15 2011 新吗?
    • @Alexey Frunze,我的意思是更新的是 2.10.01
    【解决方案3】:
        I think that the Intel manual is right, the 'push imm' 
        instructions do have three encoding form:
        Form 1: 6a XX
        Form 2: 66 68 XX XX 
        Form 3: 68 XX XX XX XX
    
        What we are arguing is the implemental specific behavior of the NASM.
    
        In NASM 2.04rc1(the above example I given):
        1. The 'byte' in 'push byte imm' direct the NASM use the Form 1, 
           no matter how long the imm given in the instruction it is, the imm
           was trunked to a byte in final machine code. 
        2. The 'word' in 'push word imm' direct the NASM use the Form 2,
           no matter how long the imm given in the instruction it is, the imm 
           was trucked or zero-extended to a word in final machine code.
        3. The 'qword' in 'push dword imm' direct the NASM use the Form 3,
           no matter how long the imm given in the instruction it is, the imm 
           was trucked or zero-extended to a dword in final machine code.
           Note: the 'qword' in the instruction but 'dword' in final machine
           code, which made us confused.
        4. if none of 'byte', 'word', 'qword' is given, the NASM use the From 3.
    

    请看下面的例子:

    # cat -n push.asm
    1  bits 64
    2
    3  push byte 0x21
    4  push byte 0x4321
    5
    6  push word 0x4321
    7  push word 0x654321
    8
    9  push qword 0x654321
    10
    11  push 0x21
    12  push 0x4321
    13  push 0x654321
    14  push 0x87654321
    15  push 0xa987654321
    16
    # nasm -v
    NASM version 2.04rc1 compiled on Feb 21 2009
    # nasm push.asm -o push.bin
    push.asm:4: warning: signed byte value exceeds bounds
    push.asm:7: warning: word data exceeds bounds
    # ndisasm -b 32 push.bin // 'ndisasm -b 64 push.bin' not works right in this version of NASM.
    00000000  6A21              push byte +0x21
    00000002  6A21              push byte +0x21
    00000004  66682143          push word 0x4321
    00000008  66682143          push word 0x4321
    0000000C  6821436500        push dword 0x654321
    00000011  6821000000        push dword 0x21
    00000016  6821430000        push dword 0x4321
    0000001B  6821436500        push dword 0x654321
    00000020  6821436587        push dword 0x87654321
    00000025  6821436587        push dword 0x87654321
    

    【讨论】:

    • 在较新的 NASM 中,行为发生了变化: (1) 'push 0x21' 在 NASM 2.10.01 中被编码为 '6A21',而在 NASM 2.04rc1 中被编码为 '6821000000'; (2) NASM 2.10.01 中允许 64 位模式下的“push dword 0x654321”并编码为“6821436500”
    • 这也是一种看待它的方式。我认为这里的问题是 NASM 和英特尔手册对同一事物使用了不同的术语,但它们的真正含义是相同的。将 68XXXXXXXX 称为“a push that take a dword”或“a push that push a qword”都是正确的,但都有些误导。
    猜你喜欢
    • 2013-03-09
    • 2019-06-28
    • 2011-08-23
    • 2023-04-10
    • 2015-09-04
    • 1970-01-01
    • 2012-12-02
    相关资源
    最近更新 更多