1

If I compile the following code in nasm:

[bits 16]
push word 0x0101

nasm gives me the following output:

68 01 01

This instruction works fine when running in 16-bit mode.

Now, if I change to:

[bits 32]
push word 0x0101

nasm gives me the following output:

66 68 01 01

This does not work in 32-bit protected mode.

I know that pushing imm8 and imm32 works fine in protected-mode and I suspect that push imm16 would work fine if nasm outputted the same bytes as it is outputting in the 16-bits case.

Could somebody explain what I am missing? Thanks!


@harold After some investigation I found out that the push is actually working, but only 2 bytes are being pushed to the stack instead of 4. The program was breaking because I was incorrectly expecting 4 bytes when cleaning the stack. Thanks for that. – felipeek 1 min ago

Now, my question is: in 32-bit mode, push byte does push 4 bytes to the stack, using 3 bytes as padding (this is translated into x86's PUSH imm8 instruction). Similarly, push dword also pushes 4 bytes to the stack. Why push word would push only 2 bytes? It seems like a big inconsistency to me. Am I missing something?

felipeek
  • 1,193
  • 2
  • 10
  • 31
  • "does not work" - in what way? It does something unusual (actually pushing a word, not widening the word to a dword), but it should do something – harold Aug 30 '20 at 00:13
  • @harold 66 is not a valid PUSH opcode, so my understanding is that the processor is not even interpreting this instruction as a PUSH instruction. The final result is that the rest of the program is transformed into random instructions being executed – felipeek Aug 30 '20 at 00:15
  • 2
    66 is the operand size override prefix, it's used in 16bit mode to access 32bit operations, and in 32bit mode to access 16bit operations – harold Aug 30 '20 at 00:17
  • @harold thanks, that makes more sense. I will gather more info – felipeek Aug 30 '20 at 00:19
  • @harold After some investigation I found out that the push is actually working, but only 2 bytes are being pushed to the stack instead of 4. The program was breaking because I was incorrectly expecting 4 bytes when cleaning the stack. Thanks for that. – felipeek Aug 30 '20 at 00:36
  • Now, my question is: in 32-bit mode, `push byte` does push 4 bytes to the stack, using 3 bytes as padding (this is translated into x86's PUSH imm8 instruction). Similarly, `push dword` also pushes 4 bytes to the stack. Why `push word` would push only 2 bytes? It seems like a big inconsistency to me. Am I missing something? – felipeek Aug 30 '20 at 00:36
  • 1
    In 32-bit protected mode if you `push imm8` on the stack then that gets encoded as `0x6a, imm8`. No instruction prefix is put on that. That instruction is 2 bytes but the byte is sign extended by the processor to 32-bits wide and the stack has 4 subtracted from it. In 32-bit protected-mode the default encoding for `push imm32` is `0x6b, imm32` but 0x6b is also the instruction for `push imm16` (0x6b, imm16). – Michael Petch Aug 30 '20 at 00:53
  • 3
    In order to encode `push word imm16` in 32-bit protected mode the assembler has to put a operand override on it (prefix 0x66). This has the unfortunate side effect of of putting a 16-bit word on the stack and subtracting 2 from ESP (not 4!). This can cause you problems if your code was expecting 4 bytes to be pushed. – Michael Petch Aug 30 '20 at 00:55
  • @MichaelPetch thanks, I see the origin of the problem now: the only way to differentiate between `push imm16` and `push imm32` is via this operand override prefix... it is still weird to me this design which pushing 8 bits or 32 bits behaves different from pushing 16 bits, but it is what it is I guess – felipeek Aug 30 '20 at 01:02
  • 2
    Related: [How many bytes does the push instruction push onto the stack when I don't specify the operand size?](https://stackoverflow.com/q/45127993) - there are only limited encodings of `push`, and none for `push dword sign_extended_imm16`. The syntax for that would maybe be `push dword strict word`; `push word` is simply how you specify the operand size, not the immediate encoding. `push word 1` will still use the signe extended imm8 opcode. – Peter Cordes Aug 30 '20 at 02:19

0 Answers0