CMSC313, Computer Organization & Assembly Language Programming, Spring 2013
 Linux System Calls 
Introduction
The Linux operating system protects the operating system and the hardware 
from user processes. This prevents buggy applications from crashing the 
operating system. However, this also means that user applications cannot 
have direct access to the hardware --- even for routine activities such as 
printing to the screen. To accomplish tasks like reading from the keyboard, 
printing to the screen, opening a file and writing to the file, a user 
process must ask the operating system for help. In high-level languages, 
this mechanism is transparent to the programmer (e.g., the printf 
function in C behaves the same way regardless of the target platform's 
operating system). In Linux assembly language progams, these tasks are 
accomplished through "system calls" to the kernel.
Linux System Calls
These system routines are called using software interrupt 80H (= 
12810). The mnemonic of this instruction is:
      int    80H
When the system call terminates, the user program continues execution at the 
machine instruction after the interrupt instruction (except the case when 
the system call is to "exit"). So, in many respects, a system call 
resembles a function call.
Almost 200 different system calls are available through this single int 80h 
instruction. The mechanism used to distinguish these calls is the value 
stored in the EAX register. For example, if EAX contains 4, the system call 
requested is the "write" function. If EAX contains 1, the system call 
requested is "exit". A complete list of the names of the system calls and 
the corresponding system call number is stored in the C header file 
"/usr/include/asm/unistd.h". Documentation for the functions are available 
in chapter 2 of the UNIX man pages. Arguments to be passed to the system 
calls are stored in registers EBX, ECX, EDX, ESI, EDI and EBP in that 
order. The value returned by the system call, if any, is stored in EAX.
The values of all other registers should be preserved according to the 
documentation. (In case of weird bugs, double check that the system call 
has not mangled the contents of the registers.)
An Example
Suppose that we want to print to the screen. We look through the system 
calls in the unistd.h header file and notice the following lines:
   #define __NR_exit         1
   #define __NR_fork         2
   #define __NR_read         3
   #define __NR_write        4
   #define __NR_open         5
   #define __NR_close        6
So, system call number 4 is some sort of write function. Then we
consult the UNIX man page for the write command:
   man 2 write
We figure out that the function prototype of the write function is:
   ssize_t write(int fd, const void *buf, size_t count);
It turns out that the argument "fd" is a file descriptor and that the file 
descriptor for stdout is 1. The argument "buf" is the address of the first 
byte to be printed and "count" indicates the number of bytes to print. 
Thus, to make a system call to "write" we must load EAX with 4, EBX with 1, 
ECX with the address of the string to be printed and EDX with the length 
of the string. So, the following code fragment will print out "Hello 
world\n":
        SECTION .data
msg:    db "Hello world", 10
...
        SECTION .text
...
        mov     eax, 4          ; syscall number for write
        mov     ebx, 1          ; file descriptor for stdout
        mov     ecx, msg        ; address of "hello world\n"
        mov     edx, 12         ; length of "hello world\n"
        int     80h             ; make the syscall
Last Modified:
22 Jul 2024 11:28:18 EDT
by
Richard Chang
 to Spring 2013 CMSC 313 Homepage
to Spring 2013 CMSC 313 Homepage