How 'push imm' encodes?

assembly, intel, nasm, x86

Solution

The manual is wrong. (this is not the only error, by the way)

In 64bit mode, there is no 32bit push. `push` is one of the few instructions that is promoted to 64bit without a REX.W prefix, and you can't demote it.

edit: actually, my version of the manual says the right thing:

Push sign-extended imm32. Stack pointer is decremented by the size of stack pointer.

So in 64bit mode, that translates to "push a qword, sign extended from the immediate".

Problem

The << Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference, N-Z >> says: ``` | 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 ``` Why the line 5 is illegal? I think it matches 'PUSH imm32'. And why the line 6 is legal? It does not match any of 'PUSH imm8/PUSH imm16/PUSH imm32'. Help me, please! ======Test====== ``` 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. ``` See the following example: ``` # 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' ```

Original source