You are calling scanf
correctly, using the x86-64 System V calling convention. It leaves its return value in eax
. After successful conversion of one operand (%d
), it returns with eax
= 1.
... correct setup for scanf, including zeroing AL.
call scanf ; correct
int 80h ; insane: system call with eax = scanf return value
Then you run int 80h
, which makes a 32-bit legacy-ABI system call using eax=1
as the code to determine which system call. (see What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?).
eax=1
/ int 80h
is sys_exit
on Linux. (unistd_32.h
has __NR_exit
= 1). Use a debugger; that would have shown you which instruction was making your program exit.
Your title (before I corrected it) said you got a segmentation fault, but I tested on my x86-64 desktop and that's not the case. It exits cleanly using an int 80h
exit system call. (But in code that does segfault, use a debugger to find out which instruction.) strace
decodes int 0x80
system calls incorrectly in 64-bit processes, using the 64-bit syscall
call numbers from unistd_64.h
, not the 32-bit unistd_32.h
call numbers.
Your code was close to working: you use the int 0x80
32-bit ABI correctly for sys_write
, and only pass it 32-bit args. (The pointer args fit in 32 bits because static code/data is always placed in the low 2GiB of virtual address space in the default code model on x86-64. Exactly for this reason, so you can use compact instructions like mov edi, formatin
to put addresses in registers, or use them as immediates or rel32 signed displacements.)
OTOH I think you were doing that for the wrong reason. And as @prl points out, you forgot to maintain 16-byte stack alignment.
Also, mixing system calls with C stdio functions is usually a bad idea. Stdio uses internal buffers instead of always making a system call on every function call, so things can appear out of order, or a read
can be waiting for user input when there's already data in the stdio buffer for stdin
.
Your loop is broken in several ways, too. You seem to be trying to call printf
with the 32-bit calling convention (args on the stack).
Even in 32-bit code, this is broken, because printf
's return vale is in eax
. So your loop is infinite, because printf
returns the number of characters printed. That's at least two from the %d\n
format string, so dec rax
/ jnz
will always jump.
In the x86-64 SysV ABI, you need to zero al
before calling printf
(with xor eax,eax
), if you didn't pass any FP args in XMM registers. You also have to pass args in rdi
, rsi
, ..., like for scanf.
You also add rsp, 8
after pushing two 8-byte values, so the stack grows forever. (But you never return, so the eventual segfault will be on stack overflow, not on trying to return with rsp
not pointing to the return address.)
Decide whether you're making 32-bit or 64-bit code, and only copy/paste from examples for the mode and OS you're targeting. (Note that 64-bit code can and often does use mostly 32-bit registers, though.)
See also Assembling 32-bit binaries on a 64-bit system (GNU toolchain) (which does include a NASM section with a handy asm-link
script that assembles and links into a static binary). But since you're writing main
instead of _start
and are using libc functions, you should just link with gcc -m32
(if you decide to use 32-bit code instead of replacing the 32-bit parts of your program with 64-bit function-calling and system-call conventions).
See What are the calling conventions for UNIX & Linux system calls on i386 and x86-64.