English Persian
Chapter 11. x86 Assembly Language Programming
Two very different assemblers are available for FreeBSD. One is man:as[1], which uses the traditional UNIX(R) assembly language syntax. It comes with the system.
The other is /usr/ports/devel/nasm. It uses the Intel syntax. Its main advantage is that it can assemble code for many operating systems. It needs to be installed separately, but is completely free.
This chapter uses nasm syntax because most assembly language programmers coming to FreeBSD from other operating systems will find it easier to understand. And, because, quite frankly, that is what I am used to.
The standard man:ld[1] linker comes with FreeBSD. It works with the code assembled with either assembler.
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(R) convention used by MS-DOS(R). Why? Because the UNIX(R) convention allows any program written in any language to access the kernel.
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(R) 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:
The `5` that we have placed in `EAX` identifies the kernel function, in this case `open`.
Linux is a UNIX(R) like system. However, its kernel uses the same system-call convention of passing parameters in registers MS-DOS(R) does. As with the UNIX(R) 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`:
This convention has a great disadvantage over the UNIX(R) 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.
% brandelf -t Linux filename
If you are coding specifically for FreeBSD, you should always use the UNIX(R) 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.
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.
The [.filename]#syscalls# File
The numbers are listed in [.filename]#syscalls#. `locate syscalls` finds this file in several different formats, all produced automatically from [.filename]#syscalls.master#.
You can find the master file for the default UNIX(R) calling convention in [.filename]#/usr/src/sys/kern/syscalls.master#. If you need to use the other convention implemented in the Linux emulation mode, read [.filename]#/usr/src/sys/i386/linux/syscalls.master#.