You have one major problem here, and two other major problems that might not be visible with this particular caller:
- You forgot to set
edx
to the string length. The system call you're using is
sys_write(int fd, const char *buf, size_t len);
, so the len
you're passing is whatever garbage your caller left in edx
.
If that doesn't make sys_write
return -EFAULT
without printing anything, then maybe you're printing a bunch of binary garbage before it detects a problem, if you ended up with a very large len
? Hmm, write
probably checks the whole range before writing anything, so maybe you ended up with a medium-sized value in edx
and just printed some binary garbage after your strings. Your strings should be there at the start of your output, though.
Use strace ./my_program
to find out, or GDB to look at edx
before the call. (strace
won't actually work right for int 0x80
from 64-bit code; it decodes as if you were using the native 64-bit syscall
ABI).
You clobber the caller's rbx
without saving/restoring it, violating the calling convention (the x86-64 System V ABI) where rbx
is a call-preserved register. See http://agner.org/optimize/ for a nice doc about calling conventions. This may have no effect; your main
probably doesn't compile to code that uses rbx
for anything, so the rbx
you're destroying is "owned" by the CRT start function that calls main
.
Strangely, you save/restore rbp
even though you don't use it at all.
Most importantly, you're using the 32-bit int 0x80
ABI from 64-bit code!! This is why I've been saying that write
takes its length
are in edx
, not rdx
, because the 32-bit int 0x80
ABI is exactly the same ABI that 32-bit user-space can use, where it only looks at the low 32 bits of registers.
Your code will break with pointers outside the low 32 bits of virtual address space. e.g. if you compiled with gcc -fPIE -pie
, even those string literals in the .rodata
section will be outside the low 32 bits, and sys_write
will probably return -EFAULT
without doing anything.
-pie
is the default for gcc on many modern Linux distros, so you're "lucky"(?) that your program printed anything at all instead of just having write
fail with -EFAULT
. You aren't checking error return values, and it doesn't segfault your program the way it would if you tried to read/write from a bad point in user-space, so it fails silently.
As @fuz points out, Why cant i sys_write from a register? shows some example code for using the 64-bit syscall
for sys_write
, but it hard-codes a length. You're going to need to strlen
your string, either with the libc function or with a loop. (Or a slow but compact repe scasb
).