11.5.Â Creating Portable Code

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.

11.5.1.Â Dealing with Function Numbers

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:

%ifdef LINUX
%define SYS_execve 11
%else
%define SYS_execve 59
%endif

11.5.2.Â Dealing with Conventions

Both, the calling convention, and the return value (the
errno problem) can be resolved with macros:

11.5.3.Â Dealing with Other Portability Issues

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 code.

11.5.4.Â Using a Library

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:

sys.open:
sys.close:
[etc...]
int 80h
ret

Your Linux library will require more different functions.
But even here you can group system calls using the same
number of parameters:

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 code.

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 program.

11.5.5.Â Using an Include File

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 code.

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 descriptors: