1

From the intel instruction reference:

68 id PUSH imm32

It means pushing dword-sized immediates is valid in 64-bit program.

So I wrote the following program:

section .text
    global _start

_start:
    call _foo

    mov rax, 60
    syscall

_foo:
    push word 0xFFFF      ;ok
    pop ax                ;ok
    push dword 0xFFFFFFFF ; warning: signed dword value exceeds bounds
    pop rax
    call _bar
    ret

_bar:
    ret

Why did I get this warning? From the intel reference:

The operand size (16, 32, or 64 bits) determines the amount by which the stack pointer is decremented (2, 4 or 8). If the source operand is an immediate of size less than the operand size, a sign-extended value is pushed on the stack.

In my case the operand size is 32 bits. It's not less than operand size. So I expected everything was going to be okay. But, when executing the program after push dword 0xFFFFFFFF the 64-bit sign extension of it was pushed actually.

Also it's not clear why do we have push imm32 instruction in 64-bit mode. If we try to

pop eax ;instruction not supported in 64-bit mode

So even if we could push 32-bit into the stack, we cannot pop it into 32-bit register.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • 3
    Note "operand size" != "immediate value size". – Jonathon Reinhart Feb 09 '18 at 12:25
  • @JonathonReinhart Do you mean that part of the reference `Operand size. The D flag in the current code-segment descriptor determines the default operand size; it may be overridden by instruction prefixes`? So it means `push` operand size is defined in the CSD... ? But why is `push word 0xFFFF` is okay then. sizeof(word) < sizeof(dword) < operand_size. – St.Antario Feb 09 '18 at 12:27
  • 2
    In 64-bit code, you choices for `push`/`pop` operand size are 16 and 64. IDK why AMD chose not to make `REX.W=0 push` mean a 32-bit push. (i.e. allow the operand-size to be overridden from the default 64 to 32). – Peter Cordes Feb 09 '18 at 12:31
  • 2
    Also note you definitely do not want to misalign the stack pointer, trust me. Furthermore, popping into rax will have the same effect on eax, so just use that (it won't have same effect on the top 32 bits though). – Jester Feb 09 '18 at 12:32
  • 3
    The only 64-bit instruction that allows a 64-bit immediate is one encoding of `mov` (what AT&T calls `movabs`). Everything else uses sign-extended 8 or 32-bit immediates. See https://stackoverflow.com/questions/40315803/difference-between-movq-and-movabsq-in-x86-64. – Peter Cordes Feb 09 '18 at 12:33
  • @PeterCordes Because it rarely makes sense to do a dword push in long mode and if behaviour was as you request, every normal `push` would require a REX prefix. Of course, you could make e.g. `40 50` be a 32 bit push, but I guess the decoder would have been more difficult for that. – fuz Feb 09 '18 at 15:29
  • @fuz: yes, that's what I meant: 32-bit push encodeable only with an explicit REX.W=0. Obviously you want 64-bit to be the default operand-size. But no x86-64 instructions work like that: any that have a default operand-size of 64 (e.g. `call`) aren't overrideable back to 32. (`loop` defaults to RCX, but an *address-size* prefix overrides it back to ECX. Address-size normally does 64->32, but not for direct registers, so it's weird.) – Peter Cordes Feb 10 '18 at 01:39
  • 1
    The Intel instruction set reference I have says "64 bit mode: N.E.", or actually `FF /6 PUSH r/m32 M N.E. Valid Push r/m32`. See the `N.E.` there? that is the column for 64 bit mode. – Rudy Velthuis Feb 10 '18 at 11:25
  • Correction to my above comment: `push r32` is *not* encodeable in 64-bit mode. Testing results in [How many bytes does the push instruction push onto the stack when I don't specify the operand size?](//stackoverflow.com/q/45127993) because the wording of the documentation got me thinking REX.W=0 could work. – Peter Cordes Aug 22 '19 at 15:50

0 Answers0