FreeBSD Developers' Handbook
Prev | Chapter 11 x86 Assembly Language Programming | Next

# 11.3 System Calls

## 11.3.1 Default Calling Convention

By default, the FreeBSD kernel uses the C calling convention. Further, although the kernel is accessed using `int 80h`, it is assumed the program will call a function that issues `int 80h`, rather than issuing `int 80h` directly.

This convention is very convenient, and quite superior to the Microsoft® convention used by MS-DOS®. Why? Because the UNIX® convention allows any program written in any language to access the kernel.

An assembly language program can do that as well. For example, we could open a file:

    kernel:
    	int	80h	; Call kernel
    	ret

    open:
    	push	dword mode
    	push	dword flags
    	push	dword path
    	mov	eax, 5
    	call	kernel
    	add	esp, byte 12
    	ret

This is a very clean and portable way of coding. If you need to port the code to a UNIX system which uses a different interrupt, or a different way of passing parameters, all you need to change is the kernel procedure.

But assembly language programmers like to shave off cycles. The above example requires a `call/ret` combination. We can eliminate it by `push`ing an extra dword:

    open:
    	push	dword mode
    	push	dword flags
    	push	dword path
    	mov	eax, 5
    	push	eax		; Or any other dword
    	int	80h
    	add	esp, byte 16

The `5` that we have placed in `EAX` identifies the kernel function, in this case `open`.

## 11.3.2 Alternate Calling Convention

FreeBSD is an extremely flexible system. It offers other ways of calling the kernel. For it to work, however, the system must have Linux emulation installed.

Linux is a UNIX like system. However, its kernel uses the same system-call convention of passing parameters in registers MS-DOS does. As with the UNIX convention, the function number is placed in `EAX`. The parameters, however, are not passed on the stack but in `EBX, ECX, EDX, ESI, EDI, EBP`:

    open:
    	mov	eax, 5
    	mov	ebx, path
    	mov	ecx, flags
    	mov	edx, mode
    	int	80h

This convention has a great disadvantage over the UNIX way, at least as far as assembly language programming is concerned: Every time you make a kernel call you must `push` the registers, then `pop` them later. This makes your code bulkier and slower. Nevertheless, FreeBSD gives you a choice.

If you do choose the Linux convention, you must let the system know about it. After your program is assembled and linked, you need to brand the executable:

    % brandelf -t Linux filename

## 11.3.3 Which Convention Should You Use?

If you are coding specifically for FreeBSD, you should always use the UNIX convention: It is faster, you can store global variables in registers, you do not have to brand the executable, and you do not impose the installation of the Linux emulation package on the target system.

If you want to create portable code that can also run on Linux, you will probably still want to give the FreeBSD users as efficient a code as possible. I will show you how you can accomplish that after I have explained the basics.

## 11.3.4 Call Numbers

To tell the kernel which system service you are calling, place its number in `EAX`. Of course, you need to know what the number is.

### 11.3.4.1 The `syscalls` File

The numbers are listed in `syscalls`. `locate syscalls` finds this file in several different formats, all produced automatically from `syscalls.master`.

You can find the master file for the default UNIX calling convention in `/usr/src/sys/kern/syscalls.master`. If you need to use the other convention implemented in the Linux emulation mode, read `/usr/src/sys/i386/linux/syscalls.master`.

> Note: Not only do FreeBSD and Linux use different calling conventions, they sometimes use different numbers for the same functions.

`syscalls.master` describes how the call is to be made:

    0	STD	NOHIDE	{ int nosys(void); } syscall nosys_args int
    1	STD	NOHIDE	{ void exit(int rval); } exit rexit_args void
    2	STD	POSIX	{ int fork(void); }
    3	STD	POSIX	{ ssize_t read(int fd, void *buf, size_t nbyte); }
    4	STD	POSIX	{ ssize_t write(int fd, const void *buf, size_t nbyte); }
    5	STD	POSIX	{ int open(char *path, int flags, int mode); }
    6	STD	POSIX	{ int close(int fd); }
    etc...

It is the leftmost column that tells us the number to place in `EAX`.

The rightmost column tells us what parameters to `push`. They are `push`ed from right to left.

For example, to `open` a file, we need to `push` the `mode` first, then `flags`, then the address at which the `path` is stored.

Prev | Home | Next
The Tools | Up | Return Values