UMBC CMSC 313, Computer Organization & Assembly Language,
Fall 2004, 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:
22 Jul 2024 11:29:44 EDT
by
Richard Chang
to Fall 2004 CMSC 313 Section Homepage