What are system calls in assembly language?

System calls enable users to request a service from the operating system (OS). To execute a system call, the execution of the process is halted, and the execution of the system call starts in kernel mode. This switch from user mode to kernel mode may incur a short delay.

System calls are useful when a process requires some functionality that is administered or offered only by the operating system. Such functionalities include but are not limited to:

  • Input and output (I/O) operations

  • Creation or deletion of files

  • Creation and administration of processes

  • Networking

Semantics

Each system call is assigned a unique system call number. In UNIX, this information is stored in the unistd_32.h header file that we can locate by running the following command on the terminal:

locate unistd_32.h

The locate command is part of the mlocate package. If not already installed on your computer, it can be installed by running the following command on the terminal:

sudo apt install mlocate

The following information is present in the unistd_32.h header file.

educative@rukhshan:/usr/src/linux-headers-5.8.0-55-generic/arch/x86/include/generated/uapi/asm$ cat unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H 1

#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
...

The system call number of each system call is written next to it. For example, the system call number of the write system call is 4, and for the exit system call, it is 1.

Syntax

To execute a system call, the system call number is saved in the general-purpose register (GPR) eax. The arguments of the corresponding system call are stored in the GPRs ebx, ecx, and edx. Information in these registers is stored in sequential order, which means that the first argument is saved in ebx, the second in ecx, etc.

The return value of any system call is stored in the same register which was used to store its system call number, i.e., the eax GPR.

To see what arguments a particular system call will take, we can run the following command on the terminal:

man 2 name_of_command

For example, man 2 write outputs a description of the write system call.

WRITE(2)     Linux Programmer's Manual     WRITE(2)

NAME
       write - write to a file descriptor

SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);

DESCRIPTION
       write() writes up to count bytes from the buffer starting at buf to the
      the file referred to by the file descriptor fd.
...

Example

The following snippet of code demonstrates a simple assembly language program, which uses the write system call to print a string to the console.

segment .data:
message db "Welcome to Edpresso!" , 0xA
message_length equ $-message
segment .bss:
segment .text:
global _start
_start:
mov eax, 0x4 ;4 is the unique system call number of the write system call
mov ebx, 1 ;1 is the file descriptor for the stdout stream
mov ecx, message ;message is the string that needs to be printed on the console
mov edx, message_length ;length of message
int 0x80 ;interrupt to run the kernel
mov eax, 0x1 ;1 is the unique system call number of the exit system call
mov ebx, 0 ;argument to exit system call
int 0x80 ;interrupt to run the kernel and exit gracefully

In the .data segment, we have defined a string and assigned message as its identifier. The last character of the string is \n, which is represented by 0XA in hex. We use the db directive to allocate space for this string. Additionally, we define another variable and name it message_length, which stores the length of message.

In the .text section of our code, we set the value of the general purpose register (GPR) eax to 0x4, which is the system call number of the write system call. The write system call is used to write to a stream, and its syntax is as follows:

 ssize_t write(int fd, const void *buf, size_t count);

The write system calls returns the number of bytes written to a stream. The first argument to the write system call is the file descriptor of the stream we intend to write to. In our case, this is the stdout stream. The file descriptor of stdout is 1, and we store it in the GPR ebx. The second parameter is a pointer to the buffer that we wish to copy data from. This is the variable message and we store it in the GPR ecx. The third and final argument is the buffer size that we wish to write to, so we store message_length in the GPR edx.

Subsequently, the kernel is called using the int X80 instruction, which denotes a kernel interrupt. The kernel reads values from each of the registers filled above and writes the buffer to the stdout stream using the write system call.

To exit gracefully, we call the exit system call, which has 1 as its unique system call number.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved