x86 Assembly pushl/popl don't work with "Error: suffix or operands invalid"
Solution 1
In 64-bit mode you cannot push and pop 32-bit values; you need pushq
and popq
.
Also, you will not get a proper exit this way. On 32-bit x86, you would need to set %eax
to 1 to select the exit()
system call, and set %ebx
to the exit code you actually wish. On 64-bit x86 (that's what you are using), conventions are different: the system call number for exit()
is 60, not 1; the first system call parameter goes in %rdi
, not %rbx
; the system-call invocation opcode is not int $0x80
but the special, x86-64-only opcode syscall
.
Which leads to:
.section .data
.section .text
.globl _start
_start:
pushq $60
popq %rax
pushq $1
popq %rdi
syscall
(each push
/pop
sequence can be replaced with a simple mov
(like mov $60, %eax
) of course; I suppose that you are trying to explicitly test push
and pop
, optimize for code-size, or avoid 0
bytes in the machine code (for an exploit payload))
Related:
- What are the calling conventions for UNIX & Linux system calls on i386 and x86-64
- What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
Solution 2
I recently started following these examples too, I found the following worked:
- Add
.code32
to the top of your assembly code - Assemble with the
--32
flag - Link with the
-m elf_i386
flag
You can see my example here
Solution 3
You need to replace the push/pop sequence with
pushq $1 # push the value 1 onto the stack
popq %rax # pop 1 off the stack and into the %eax register
Note the error message is "suffix or operand invalid", you only checked the second part of the logical OR
in the error message, maybe because you weren't exactly sure what the suffix means: it's the "l".
Edit: Please see Thomas answer for an explanation why your code won't work anyway even if it assembles.
Solution 4
I ran into this error working through the same book. I created the following shell script (att.sh):
#!/bin/sh
as --32 $1.s -o $1
ld -melf_i386 $1.o -o $1
./$1
echo $?
Which I then made executable and ran (assuming an input file myfile.s):
chmod +x ./att.sh
./att.sh myfile
maxm
Updated on December 02, 2020Comments
-
maxm over 3 years
I'm a newbie to assembly programming, working through Programming Ground Up on an Ubuntu x86_64 desktop with GNU assembler v2.20.1.
I've been able to assemble/link execute my code, up until I get to using pushl/popl instructions for manipulating the stack. The following code fails to assemble:
.section .data # empty .section .text .globl _start _start: pushl $1 # push the value 1 onto the stack popl %eax # pop 1 off the stack and into the %eax register int $0x80 # exit the program with exit code '1'
Using "as test.s -o test.o", these errors appear on the terminal and test.o is not created:
test.s: Assembler messages: test.s:9: Error: suffix or operands invalid for 'push' test.s:10: Error: suffix or operands invalid for 'popl'
I've checked the documentation, and the operands I'm using for pushl and popl are valid. This isn't exactly a debugging question--so what's wrong with my code? Or is it my assembler?
-
maxm about 13 yearsthank you, you guys both rock :) and yes, I was just using push/pop to demonstrate the problem, not reinventing mov. Though I was ignorant of the fact that x32 vs. x64 architecture was an issue.
-
maxm about 13 yearsYeah, I think I just mentally blocked the suffix part bcs I had no idea what it meant. Thank you for clarifying
-
FigmentEngine about 13 yearswhere would a good reference on the differences between 32/64 be found? i.e. what resource can I use in conjunction with this book to program on 64bit?
-
Thomas Pornin about 13 yearsYou may want to have a look at Intel's manuals: intel.com/products/processor/manuals/index.htm (that's complete and quite readable, but huge).
-
FigmentEngine about 13 yearsthanks Thomas, there seems to be a starting article before this software.intel.com/en-us/articles/introduction-to-x64-assembly/…
-
sinceq over 9 yearsIn 32-bit instruction, link with the -m elf_i386 flag,the program can run correctly. But how to make it work in 64-bit instruction? stackoverflow.com/questions/25401047/…
-
Peter Cordes over 6 yearsYou're totally missing the point. The OP has 32-bit code, and accidentally built it as 64-bit. They want to do a 32-bit push to match the
pop %eax
as a 3-byte way to seteax=1
Your code would needmovzbl (%rsp), %eax
to do that. Also, you usually don't want to misalign%rsp
like that, because a signal handler could run at any time. And no, you can't always use%rbp
; making a frame pointer is optional. -
Peter Cordes over 6 yearsAnd anyway, the OP didn't want to avoid
push
/pop
.push $1
works fine in 64-bit mode (as a 64-bit push.) -
Peter Cordes over 6 yearsYou don't need
.code32
, and in fact should avoid it so you get noisy failure at build time (from instructions likepush %eax
) if you forget the other 2 steps and build as 64-bit. All.code32
does is let you put 32-bit machine code inside a 64-bit executable, where it will decode "wrong" at run-time. (Or to switch back to 32-bit after.code16
or.code32
). If that's not what you want, don't use it! See also building 32-bit executables with 64-bit GNU tools -
Peter Cordes over 6 yearsThe 32-bit
int $0x80
ABI is available (but not recommended) in 64-bit mode , so the OP's program does successfully invokesys_exit(0)
instead of crashing. (Linux initializes all the regs to 0 before entering user-space, so EBX will be 0 unless the binary is dynamically linked, in which case it will contain whatever ld.so left.) See What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?