Next Chapter | Previous Chapter | Contents | Index
This chapter attempts to cover some of the common issues involved when writing 32-bit code, to run under Win32 or Unix, or to be linked with C code generated by a Unix-style C compiler such as DJGPP. It covers how to write assembly code to interface with 32-bit C routines, and how to write position-independent code for shared libraries.
Almost all 32-bit code, and in particular all code running under
A lot of the discussion in section 8.4, about interfacing to 16-bit C programs, still applies when working in 32 bits. The absence of memory models or segmentation worries simplifies things a lot.
Most 32-bit C compilers share the convention used by 16-bit compilers,
that the names of all global symbols (functions or data) they define are
formed by prefixing an underscore to the name as it appears in the C
program. However, not all of them do: the 
The older Linux 
See also section 2.1.27.
The C calling convention in 32-bit programs is as follows. In the following description, the words caller and callee are used to denote the function doing the calling and the function which gets called.
CALL ESP EBP EBP EBP EBP EBP [EBP] EBP [EBP+4] CALL [EBP+8] EBP printf ESP EBP AL AX EAX ST0 ESP EBP EBP RET RETN ESP POP There is an alternative calling convention used by Win32 programs for
Windows API calls, and also for functions called by the Windows
API such as window procedures: they follow what Microsoft calls the
Thus, you would define a function in C style in the following way:
global  _myfunc 
_myfunc: 
        push    ebp 
        mov     ebp,esp 
        sub     esp,0x40        ; 64 bytes of local stack space 
        mov     ebx,[ebp+8]     ; first parameter to function 
        ; some more code 
        leave                   ; mov esp,ebp / pop ebp 
        ret
At the other end of the process, to call a C function from your assembly code, you would do something like this:
extern  _printf 
        ; and then, further down... 
        push    dword [myint]   ; one of my integer variables 
        push    dword mystring  ; pointer into my data segment 
        call    _printf 
        add     esp,byte 8      ; `byte' saves space 
        ; then those data items... 
segment _DATA 
myint       dd   1234 
mystring    db   'This number -> %d <- should be 1234',10,0
This piece of code is the assembly equivalent of the C code
    int myint = 1234; 
    printf("This number -> %d <- should be 1234\n", myint);
To get at the contents of C variables, or to declare variables which C
can access, you need only declare the names as
          extern _i 
          mov eax,[_i]
And to declare your own integer variable which C programs can access as
          global _j 
_j        dd 0
To access a C array, you need to know the size of the components of the
array. For example, 
To access a C data structure, you need to know the offset from the base
of the structure to the field you are interested in. You can either do this
by converting the C structure definition into a NASM structure definition
(using 
To do either of these, you should read your C compiler's manual to find
out how it organizes data structures. NASM gives no special alignment to
structure members in its own 
struct { 
    char c; 
    int i; 
} foo;
might be eight bytes long rather than five, since the
c32.mac Included in the NASM archives, in the 
An example of an assembly function using the macro set is given here:
proc    _proc32 
%$i     arg 
%$j     arg 
        mov     eax,[ebp + %$i] 
        mov     ebx,[ebp + %$j] 
        add     eax,[ebx] 
endproc
This defines 
Note that the 
NetBSD, and its close cousins FreeBSD and OpenBSD, take a different
approach by hacking PIC support into the 
The operating system loads a PIC shared library by memory-mapping the library file at an arbitrarily chosen point in the address space of the running process. The contents of the library's code section must therefore not depend on where it is loaded in memory.
Therefore, you cannot get at your variables by writing code like this:
        mov     eax,[myvar]             ; WRONG
Instead, the linker provides an area of memory called the global
offset table, or GOT; the GOT is situated at a constant distance from
your library's code, so if you can find out where your library is loaded
(which is typically done using a 
The data section of a PIC shared library does not have these restrictions: since the data section is writable, it has to be copied into memory anyway rather than just paged in from the library file, so as long as it's being copied it can be relocated too. So you can put ordinary types of relocation in the data section without too much worry (but see section 9.2.4 for a caveat).
Each code module in your shared library should define the GOT as an external symbol:
extern _GLOBAL_OFFSET_TABLE_ ; in ELF extern __GLOBAL_OFFSET_TABLE_ ; in BSD a.out
At the beginning of any function in your shared library which plans to access your data or BSS sections, you must first calculate the address of the GOT. This is typically done by writing the function in this form:
func:   push    ebp 
        mov     ebp,esp 
        push    ebx 
        call    .get_GOT 
.get_GOT: 
        pop     ebx 
        add     ebx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc 
        ; the function body comes here 
        mov     ebx,[ebp-4] 
        mov     esp,ebp 
        pop     ebp 
        ret
(For BSD, again, the symbol
The first two lines of this function are simply the standard C prologue
to set up a stack frame, and the last three lines are standard C function
epilogue. The third line, and the fourth to last line, save and restore the
The interesting bit is the 
If you didn't follow that, don't worry: it's never necessary to obtain the address of the GOT by any other means, so you can put those three instructions into a macro and safely ignore them:
%macro  get_GOT 0 
        call    %%getgot 
  %%getgot: 
        pop     ebx 
        add     ebx,_GLOBAL_OFFSET_TABLE_+$$-%%getgot wrt ..gotpc 
%endmacro
Having got the GOT, you can then use it to obtain the addresses of your
data items. Most variables will reside in the sections you have declared;
they can be accessed using the 
        lea     eax,[ebx+myvar wrt ..gotoff]
The expression 
If you declare variables as 
Note that due to a peculiarity of the way BSD
If your library needs to get at an external variable (external to the
library, not just to one of the modules within it), you must use
the 
        mov     eax,[ebx+extvar wrt ..got]
This loads the address of 
Common variables must also be accessed in this way.
If you want to export symbols to the user of the library, you have to declare whether they are functions or data, and if they are data, you have to give the size of the data item. This is because the dynamic linker has to build procedure linkage table entries for any exported functions, and also moves exported data items away from the library's data section in which they were declared.
So to export a function to users of the library, you must use
global  func:function           ; declare it as a function 
func:   push    ebp 
        ; etc.
And to export a data item such as an array, you would have to code
global array:data array.end-array ; give the size too array: resd 128 .end:
Be careful: If you export a variable to the library user, by declaring
it as 
Equally, if you need to store the address of an exported global in one of your data sections, you can't do it by means of the standard sort of code:
dataptr: dd global_data_item ; WRONG
NASM will interpret this code as an ordinary relocation, in which
Instead of the above code, then, you must write
dataptr: dd global_data_item wrt ..sym
which makes use of the special 
Either method will work for functions: referring to one of your functions by means of
funcptr: dd my_function
will give the user the address of the code you wrote, whereas
funcptr: dd my_function wrt .sym
will give the address of the procedure linkage table for the function, which is where the calling program will believe the function lives. Either address is a valid way to call the function.
Calling procedures outside your shared library has to be done by means of a procedure linkage table, or PLT. The PLT is placed at a known offset from where the library is loaded, so the library code can make calls to the PLT in a position-independent way. Within the PLT there is code to jump to offsets contained in the GOT, so function calls to other shared libraries or to routines in the main program can be transparently passed off to their real destinations.
To call an external routine, you must use another special PIC relocation
type, 
Having written some code modules and assembled them to
ld -shared -o library.so module1.o module2.o # for ELF ld -Bshareable -o library.so module1.o module2.o # for BSD
For ELF, if your shared library is going to reside in system directories
such as 
ld -shared -soname library.so.1 -o library.so.1.2 *.o
You would then copy