30

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
maxm
  • 5,161
  • 7
  • 30
  • 33
  • 6
    This looks like 32 bit code but you're trying to assemble for 64 bit (default on x86_64). Pass an appropriate flag to as to build as 32 bit, e.g. `as -arch i386 ...` – Paul R Mar 30 '11 at 11:14
  • 2
    I believe Paul has the right answer. You probably need --32 to as. – Peter Huene Mar 30 '11 at 11:15
  • 1
    I mention the textbook I'm using to learn x86 assembly (_Programming Ground Up_ seems to be a popular, free one)--is there a similarly popular textbook for x86/64bit (free's not that important) that you'd recommend for newbies? I realize this may be much too big a question to be asking here, but what's the difference between 32bit and 64bit as a first experience with assembly programming? – maxm Mar 30 '11 at 15:16
  • `int $0x80` / `eax=1` is `sys_exit(ebx)`. The 1 is the code for the exit system call; the return status goes in EBX in the 32-bit ABI. https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-i386-and-x86-6 – Peter Cordes Dec 01 '17 at 12:13
  • [legal push operand sizes in 64-bit mode are word and qword, but not dword](https://stackoverflow.com/questions/45127993/how-many-bytes-does-the-push-instruction-pushes-onto-the-stack-when-i-dont-spec). – Peter Cordes Dec 01 '17 at 12:30
  • The differences are simple once you know one or the other and understand what's going on, but when learning, stick to the mode that your book / tutorial / guide is for. **[Assembling 32-bit binaries on a 64-bit system](https://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain)**. Don't try to port 32-bit examples to 64-bit (or vice versa) *while* you're learning them for the first time. – Peter Cordes Dec 01 '17 at 12:36

5 Answers5

33

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:

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Thomas Pornin
  • 72,986
  • 14
  • 147
  • 189
  • thank 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 Mar 30 '11 at 15:05
  • 2
    where 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? – FigmentEngine Apr 11 '11 at 15:13
  • 4
    You may want to have a look at Intel's manuals: http://www.intel.com/products/processor/manuals/index.htm (that's complete and quite readable, but huge). – Thomas Pornin Apr 11 '11 at 15:23
  • thanks Thomas, there seems to be a starting article before this http://software.intel.com/en-us/articles/introduction-to-x64-assembly/?wapkw=%28assembly%29 – FigmentEngine Apr 11 '11 at 15:51
  • The 32-bit `int $0x80` ABI is available (but not recommended) in 64-bit mode , so the OP's program does successfully invoke `sys_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?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code) – Peter Cordes Dec 01 '17 at 12:17
15

I recently started following these examples too, I found the following worked:

  1. Add .code32 to the top of your assembly code
  2. Assemble with the --32 flag
  3. Link with the -m elf_i386 flag

You can see my example here

agam
  • 5,064
  • 6
  • 31
  • 37
  • In 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? http://stackoverflow.com/questions/25401047/how-to-make-it-work-in-x86-64-assembly – sinceq Aug 20 '14 at 09:52
  • **You don't need `.code32`, and in fact should avoid it so you get noisy failure at build time** (from instructions like `push %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](https://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain) – Peter Cordes Nov 27 '17 at 06:12
7

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.

Gunther Piez
  • 29,760
  • 6
  • 71
  • 103
  • 1
    Yeah, I think I just mentally blocked the suffix part bcs I had no idea what it meant. Thank you for clarifying – maxm Mar 30 '11 at 15:28
3

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
RazerSwift
  • 43
  • 3
-1

You may push any value without push command like that

decq %rsp
movb $0, (%rsp)

push 1 byte on stack and pop after

movb (%rsp), %al
incq %rsp

And any other size. As compilator don't generate any error and all will work good! To address may use rbp register instead of rsp. Now is allowed.

Anatoliy
  • 1
  • 1
  • You'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 set `eax=1` Your code would need `movzbl (%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 Nov 27 '17 at 06:06
  • And 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 Nov 27 '17 at 06:06