Portability is generally not one of the strengths of assembly language. Yet, writing
assembly language programs for different platforms is possible, especially with nasm. I have written assembly language libraries that can be
assembled for such different operating systems as Windows® and FreeBSD.
It is all the more possible when you want your code to run on two platforms which,
while different, are based on similar architectures.
For example, FreeBSD is UNIX®, Linux is UNIX like. I only mentioned three differences between them (from
an assembly language programmer's perspective): The calling convention, the function
numbers, and the way of returning values.
In many cases the function numbers are the same. However, even when they are not, the
problem is easy to deal with: Instead of using numbers in your code, use constants which
you have declared differently depending on the target architecture:
%define SYS_execve 11
%define SYS_execve 59
Both, the calling convention, and the return value (the errno problem) can be resolved with macros:
%macro system 0
mov ebx, [esp+32]
mov ecx, [esp+36]
mov edx, [esp+40]
mov esi, [esp+44]
mov ebp, [esp+48]
or eax, eax
%macro system 0
The above solutions can handle most cases of writing code portable between FreeBSD and
Linux. Nevertheless, with some kernel services the differences are deeper.
In that case, you need to write two different handlers for those particular system
calls, and use conditional assembly. Luckily, most of your code does something other than
calling the kernel, so usually you will only need a few such conditional sections in your
You can avoid portability issues in your main code altogether by writing a library of
system calls. Create a separate library for FreeBSD, a different one for Linux, and yet
other libraries for more operating systems.
In your library, write a separate function (or procedure, if you prefer the
traditional assembly language terminology) for each system call. Use the C calling
convention of passing parameters. But still use EAX to pass
the call number in. In that case, your FreeBSD library can be very simple, as many
seemingly different functions can be just labels to the same code:
Your Linux library will require more different functions. But even here you can group
system calls using the same number of parameters:
[etc... one-parameter functions]
mov ebx, [esp+12]
or eax, eax
The library approach may seem inconvenient at first because it requires you to produce
a separate file your code depends on. But it has many advantages: For one, you only need
to write it once and can use it for all your programs. You can even let other assembly
language programmers use it, or perhaps use one written by someone else. But perhaps the
greatest advantage of the library is that your code can be ported to other systems, even
by other programmers, by simply writing a new library without any changes to your
If you do not like the idea of having a library, you can at least place all your
system calls in a separate assembly language file and link it with your main program.
Here, again, all porters have to do is create a new object file to link with your main
If you are releasing your software as (or with) source code, you can use macros and
place them in a separate file, which you include in your code.
Porters of your software will simply write a new include file. No library or external
object file is necessary, yet your code is portable without any need to edit the
Note: This is the approach we will use throughout this chapter. We will name
our include file system.inc, and add to it whenever we deal
with a new system call.
We can start our system.inc by declaring the standard file
%define stdin 0
%define stdout 1
%define stderr 2
Next, we create a symbolic name for each system call:
%define SYS_nosys 0
%define SYS_exit 1
%define SYS_fork 2
%define SYS_read 3
%define SYS_write 4
We add a short, non-global procedure with a long name, so we do not accidentally reuse
the name in our code:
We create a macro which takes one argument, the syscall number:
%macro system 1
mov eax, %1
Finally, we create macros for each syscall. These macros take no arguments.
%macro sys.exit 0
%macro sys.fork 0
%macro sys.read 0
%macro sys.write 0
Go ahead, enter it into your editor and save it as system.inc. We will add more to it as we discuss more syscalls.