NASM returns an error like: "instruction not supported in 64-bit mode"
(Or with YASM, invalid size for operand 1
)
The subject instructions are pop ecx
and push ecx
.
What can I use instead of them or is there an other way to fix this issue?
The easiest and most common way to use the stack is with the dedicated "push" and "pop" instructions. "push" stores a constant or 64-bit register out onto the stack. The 64-bit registers are the ones like "rax" or "r8", not the 32-bit registers like "eax" or "r8d". "pop" retrieves the last value pushed from the stack.
pop has the same choices of size: 16, 32, or 64, except no 32-bit pop in 64-bit mode.
The pop instruction removes the 4-byte data element from the top of the hardware-supported stack into the specified operand (i.e. register or memory location). It first moves the 4 bytes located at memory location [SP] into the specified register or memory location, and then increments SP by 4. Syntax. pop <reg32>
push ecx and pop ecx are single-byte instructions, while add and sub are three byte each. The difference may not be huge, but all the saved bytes in all functions may add up to something substantial. ecx is considered to be spoiled by a function call, so it's safe to trash it with pop ecx after function calls.
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.
push ax
) and memory references (push word ptr[rax]
);push word 123
8-bit registers can't be pushed in any mode (except as part of a wider register), and 32-bit push/pop aren't available in 64-bit mode, even with a REX.W=0
prefix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With