64-BIT mode do not support PUSH and POP instructions

Konko picture Konko · Apr 16, 2017 · Viewed 14.9k times · Source

NASM returns an error like: "instruction not supported in 64-bit mode" and I couldn't figure out what to do.

The subject instruction is pop ecx and push ecx instructions. What can I use instead of them or is there an other way to fix this issue?

Answer

Matteo Italia picture Matteo Italia · Apr 16, 2017

The general idea is that you normally push and pop full registers, i.e. 64-bit registers in 64-bit mode. push's default operand-size is 64-bit, and 32-bit operand-size is not available. Does each PUSH instruction push a multiple of 8 bytes on x64? (yes, unless you specifically use a 16-bit push, but 32-bit isn't available).

You cannot push a 32 bit register in 64 bit mode; instead, you can push and pop the whole 64 bit register that contains a 32-bit value you want, so that's push rax instead of push eax. The same holds for memory references - you can push qword ptr[rax], but not push dword ptr[rax].

But: even in 64 bit mode you can still push:

  • 8 or 32 bit immediates sign extended to 64; this is generally handled automatically by your assembler as an optimization (if you do push 1 it will encode it with the most compact encoding, which will be 6A01, i.e. with an imm8 operand). It's always a 64-bit push unless you explicitly specify push word 1, regardless of what width of immediate the assembler picks.

  • the fs and gs segment registers but not the cs, ds, es, ss registers (which aren't important in 64-bit mode, and can only be read with mov, not push, freeing up those push/pop opcode for potential future use).

    As an exception, segment registers are either zero-extended or pushed on the stack with a 16-bit move (i.e. the other 48 bit on the stack are left unmodified); this isn't really much of a problem, since pop fs and pop gs just discard these extra bits.

You can emulate a push imm64 with push low32 / mov dword [rsp+4], high32. Or with mov r64, imm64 / push r64; mov to register (not memory) is the only x86-64 instruction that can take a 64-bit immediate.


With 16-bit operand-size (a 66h prefix), you can do a 16-bit push which adjusts RSP by 2 instead of 8. But normally don't do this because it will misalign the stack until you do a 16-bit pop or otherwise correct it.

  • 16 bit registers (push ax) and memory references (push word ptr[rax]);
  • 8-bit sign-extended or 16 bit immediates.

8-bit registers can't be pushed in any mode (except as part of a wider register), and 32-bit isn't available in 64-bit mode, even with a REX.W=0 prefix.