Assembly Language - System Calls

System calls are the only way for user programs to interact with the operating system kernel. Through system calls, assembly programs can read/write files, allocate memory, create processes, and more.


What is a system call?

User programs run in restricteduser modeand cannot directly access hardware or execute privileged operations.

When a program needs to perform kernel-level functions such as I/O operations or memory allocation, it must go throughsystem callsto request services from the operating system kernel.

The system call flow is: the user program initiates a system call → the CPU switches to kernel mode → the kernel executes the corresponding service → returns to user mode and continues execution.

Linux 32位系统调用流程

You can think of a system call as:The user program makes a phone call to the operating system, asking it to do something it doesn't have permission to do.


Linux system call mechanism

In Linux 32-bit systems, a system call is completed through the following steps:

  1. willSystem call numberPlaced into the EAX register
  2. Put parameters into registers such as EBX, ECX, EDX, ESI, EDI
  3. Executeint 0x80The instruction triggers a software interrupt
  4. The CPU switches to kernel mode, and the kernel executes the corresponding service based on the call number in EAX.
  5. The kernel puts the return value into EAX, switches back to user mode, and continues execution.

Overview of common system calls

System callCall number (EAX)FunctionParameters
sys_exit1Exit programEBX = return value (exit code)
sys_fork2Fork a child processNo parameters
sys_read3Read file/inputEBX=fd, ECX=buf, EDX=count
sys_write4Write file/outputEBX=fd, ECX=buf, EDX=count
sys_open5Open fileEBX=filename, ECX=flags, EDX=mode
sys_close6Close fileEBX=fd
sys_creat8Create fileEBX=filename, ECX=mode
sys_lseek19Move file pointerEBX=fd, ECX=offset, EDX=whence
sys_brk45Adjust data segment size (memory allocation)EBX = new address

The complete system call list can be found in/usr/include/asm/unistd_32.hView it there. Note that 32-bit and 64-bit system call numbers are different.


sys_write - output string

The most commonly used system call, used to print content to the screen:

Example

; File path: write_syscall.asm
; Use sys_write to output a string

section .data
    msg db 'Hello, example! Welcome to assembly.', 0xA
    len equ $ - msg

section .text
    global _start

_start:
    ; Call sys_write (4)
    mov eax, 4                  ; System call number 4 = sys_write
    mov ebx, 1                  ; File descriptor 1 = stdout (standard output)
    mov ecx, msg                ; Address of data to output
    mov edx, len                ; Data length (bytes)
    int 0x80                    ; Trigger system call

    ; On success, EAX returns the actual number of bytes written

    ; Exit program
    mov eax, 1
    mov ebx, 0
    int 0x80

Output:

$ nasm -f elf32 write_syscall.asm -o write_syscall.o
$ ld -m elf_i386 write_syscall.o -o write_syscall
$ ./write_syscall
Hello, example! Welcome to assembly.

sys_read - read user input

Read data from standard input (keyboard):

Example

; File path: read_syscall.asm
; Use sys_read to read user input and echo it

section .bss
    buffer resb 64              ; Reserve a 64-byte input buffer

section .data
    prompt db 'Please enter your name: '
    prompt_len equ $ - prompt
    output_msg db 'Hello, '
    output_msg_len equ $ - output_msg

section .text
    global _start

_start:
    ; Output prompt message
    mov eax, 4
    mov ebx, 1
    mov ecx, prompt
    mov edx, prompt_len
    int 0x80

    ; Read user input
    mov eax, 3                  ; System call number 3 = sys_read
    mov ebx, 0                  ; File descriptor 0 = stdin (standard input)
    mov ecx, buffer             ; Input buffer address
    mov edx, 64                 ; Read at most 64 bytes
    int 0x80

    ; EAX returns the actual number of bytes read (including the trailing newline)
    mov esi, eax                ; Save the actual input length to esi

    ; Output "Hello, "
    mov eax, 4
    mov ebx, 1
    mov ecx, output_msg
    mov edx, output_msg_len
    int 0x80

    ; Output the user's input content
    mov eax, 4
    mov ebx, 1
    mov ecx, buffer
    mov edx, esi                ; Use the actual number of bytes entered
    int 0x80

    ; Exit the program
    mov eax, 1
    mov ebx, 0
    int 0x80

Running result:

$ nasm -f elf32 read_syscall.asm -o read_syscall.o
$ ld -m elf_i386 read_syscall.o -o read_syscall
$ ./read_syscall
Please enter your name: example
Hello, example

sys_exit - exit program

Every program must call sys_exit to exit normally; otherwise, the CPU will continue executing the following garbage data, causing a segmentation fault.

Example

; Exit with different exit codes

    mov eax, 1                  ; System call number 1 = sys_exit
    mov ebx, 0                  ; Exit code 0 = normal exit (a non-zero value is also allowed)
    int 0x80

The exit code can be checked in the shell via$?View:

$ ./program
$ echo $?
0

Generic system call template

When writing system calls, you can follow the following general template:

Example

; General template for system calls
; Applicable to Linux 32-bit systems

; Step 1: Put the system call number into EAX
mov eax,System call number

; Step 2: Put the arguments in order
mov ebx,No.1argument(s)
mov ecx,No.2argument(s)
mov edx,No.3argument(s)
mov esi,No.4argument(s)
mov edi,No.5argument(s)

; Step 3: Trigger the system call
int 0x80

; Step 4: Check the return value (in EAX)
; A negative value usually indicates an error
cmp eax, 0
jl error_handler              ; If EAX < 0, jump to error handling

int 0x80After triggering, EAX holds the return value. Most system calls return a non-negative value on success, and a negative error code on failure (e.g., -1 indicates EPERM).

Other extensions