Syscalls in 64-bit linux
The tables from man syscall
provide a good overview here:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax
x86_64 syscall rax rax See below
arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86_64 rdi rsi rdx r10 r8 r9 -
I have omitted the lines that are not relevant here. In 32-bit mode, the parameters were transferred in ebx
, ecx
, etc and the syscall number is in eax
. In 64-bit mode it is a little different: All registers are now 64-bit wide and therefore have a different name. The syscall number is still in eax
, which now becomes rax
. But the parameters are now passed in rdi
, rsi
, etc. In addition, the instruction syscall
is used here instead of int 0x80
to trigger a syscall.
The order of the parameters can also be read in the man pages, here man 2 ioctl
and man 2 read
:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
So here the value of int fd
is in rdi
, the second parameter in rsi
etc.
How to get rid of waiting for a newline
Firstly create a termios
structure in memory (in .bss
section):
termios:
c_iflag resd 1 ; input mode flags
c_oflag resd 1 ; output mode flags
c_cflag resd 1 ; control mode flags
c_lflag resd 1 ; local mode flags
c_line resb 1 ; line discipline
c_cc resb 19 ; control characters
Then get the current terminal settings and disable canonical mode:
; Get current settings
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5401 ; request: TCGETS
mov rdx, termios ; request data
syscall
; Modify flags
and byte [c_lflag], 0FDh ; Clear ICANON to disable canonical mode
; Write termios structure back
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5402 ; request: TCSETS
mov rdx, termios ; request data
syscall
Now you can use sys_read
to read in the keystroke:
mov eax, 0 ; syscall number: SYS_read
mov edi, 0 ; int fd: STDIN_FILENO
mov rsi, buf ; void* buf
mov rdx, len ; size_t count
syscall
Afterwards check the return value in rax
: It contains the number of characters read.
(Or a -errno
code on error, e.g. if you closed stdin by running ./a.out <&-
in bash. Use strace
to print a decoded trace of the system calls your program makes, so you don't need to actually write error handling in toy experiments.)
References: