AsAssembly · Lesson 7 of 10

System Calls

A syscall is the gate between user space and the kernel. Reading files, writing to the terminal, allocating memory, creating threads — all of it goes through syscalls. In x86-64 Linux, the interface is beautifully simple.

x86-64 ASM
; Linux x86-64 syscall convention:
; rax = syscall number
; rdi = argument 1
; rsi = argument 2
; rdx = argument 3
; r10 = argument 4
; r8  = argument 5
; r9  = argument 6
; Return value in rax (negative = error, -(errno))

; Key Linux syscall numbers:
; 0   read    (fd, buf, count)
; 1   write   (fd, buf, count)
; 2   open    (path, flags, mode)
; 3   close   (fd)
; 9   mmap    (addr, length, prot, flags, fd, offset)
; 11  munmap  (addr, length)
; 39  getpid  ()
; 57  fork    ()
; 59  execve  (path, argv, envp)
; 60  exit    (status)
; 231 exit_group (status)

; Read from stdin into buffer
section .bss
    buf resb 128    ; reserve 128 uninitialized bytes

section .text
global _start

_start:
    ; sys_read(fd=0, buf, count=128)
    mov rax, 0          ; read
    mov rdi, 0          ; stdin
    mov rsi, buf
    mov rdx, 128
    syscall
    ; rax = number of bytes actually read (or negative errno)

    ; Echo it back — write the bytes we just read
    mov rdx, rax        ; count = bytes read
    mov rax, 1          ; write
    mov rdi, 1          ; stdout
    mov rsi, buf
    syscall

    ; Exit cleanly
    mov rax, 60
    xor rdi, rdi
    syscall
x86-64 ASM
; Open, read, and print a file
section .data
    filename db "/etc/hostname", 0   ; null-terminated path

section .bss
    filebuf resb 256

section .text
global _start

_start:
    ; open(filename, O_RDONLY=0)
    mov rax, 2              ; sys_open
    lea rdi, [rel filename] ; path
    xor rsi, rsi            ; flags = O_RDONLY (0)
    xor rdx, rdx            ; mode = 0 (ignored for read-only)
    syscall
    ; rax = file descriptor (or negative error)
    mov r12, rax            ; save fd in r12 (callee-saved)

    ; read(fd, buf, 256)
    mov rax, 0              ; sys_read
    mov rdi, r12            ; fd
    lea rsi, [rel filebuf]  ; buffer
    mov rdx, 256            ; max bytes
    syscall
    mov r13, rax            ; save bytes read

    ; write(stdout, buf, bytes_read)
    mov rax, 1
    mov rdi, 1
    lea rsi, [rel filebuf]
    mov rdx, r13
    syscall

    ; close(fd)
    mov rax, 3
    mov rdi, r12
    syscall

    ; exit(0)
    mov rax, 60
    xor rdi, rdi
    syscall
✦ Tip
The syscall table is in `/usr/include/asm/unistd_64.h` or at `man 2 syscalls`. You can also trace syscalls of any running program with `strace ./program` — this is incredibly useful for understanding what your OS is doing and for debugging segfaults that happen inside libc.