UMBC CMSC 313, Computer Organization & Assembly Language, Spring 2002, Section 0101

Linux System Calls from Assembly Language Programs


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: 11 Feb 2002 10:59:20 EST by Richard Chang
to Spring 2002 CMSC 313 Section Homepage