Subsections


4. Linking issues

When you only use Pascal code, and Pascal units, then you will not see much of the part that the linker plays in creating your executable. The linker is only called when you compile a program. When compiling units, the linker isn't invoked.

However, there are times that you want to link to C libraries, or to external object files that are generated using a C compiler (or even another pascal compiler). The Free Pascal compiler can generate calls to a C function, and can generate functions that can be called from C (exported functions). More on these calling conventions can be found in section Calling.

In general, there are 2 things you must do to use a function that resides in an external library or object file:

  1. You must make a pascal declaration of the function or procedure you want to use.
  2. You must tell the compiler where the function resides, i.e. in what object file or what library, so the compiler can link the necessary code in.
The same holds for variables. To access a variable that resides in an external object file, you must declare it, and tell the compiler where to find it. The following sections attempt to explain how to do this.


4.1 Using external functions or procedures

The first step in using external code blocks is declaring the function you want to use. Free Pascal supports Delphi syntax, i.e. you must use the external directive. The external directive replaces, in effect, the code block of the function.

The external directive doesn't specify a calling convention; it just tells the compiler that the code for a procedure or function resides in an external code block.

There exist four variants of the external directive :

  1. A simple external declaration:
    Procedure ProcName (Args : TPRocArgs); external;
    
    The external directive tells the compiler that the function resides in an external block of code. You can use this together with the {$L } or {$LinkLib } directives to link to a function or procedure in a library or external object file. Object files are looked for in the object search path (set by -Fo) and libraries are searched for in the linker path (set by -Fl).

  2. You can give the external directive a library name as an argument:
    Procedure ProcName (Args : TPRocArgs); external 'Name';
    
    This tells the compiler that the procedure resides in a library with name 'Name'. This method is equivalent to the following:
    Procedure ProcName (Args : TPRocArgs);external;
    {$LinkLib 'Name'}
    
  3. The external can also be used with two arguments:
    Procedure ProcName (Args : TPRocArgs); external 'Name'
                                           name 'OtherProcName';
    
    This has the same meaning as the previous declaration, only the compiler will use the name 'OtherProcName' when linking to the library. This can be used to give different names to procedures and functions in an external library.

    This method is equivalent to the following code:

    Procedure OtherProcName (Args : TProcArgs); external;
    {$LinkLib 'Name'}
    
    Procedure ProcName (Args : TPRocArgs);
    
    begin
      OtherProcName (Args);
    end;
    
  4. Lastly, onder WINDOWS and OS/2, there is a fourth possibility to specify an external function: In .DLL files, functions also have a unique number (their index). It is possible to refer to these fuctions using their index:
    Procedure ProcName (Args : TPRocArgs); external 'Name' Index SomeIndex;
    
    This tells the compiler that the procedure ProcName resides in a dynamic link library, with index SomeIndex.

    Remark: Note that this is ONLY available under WINDOWS and OS/2.

In earlier versions of the Free Pascal compiler, the following construct was also possible :

Procedure ProcName (Args : TPRocArgs); [ C ];
This method is equivalent to the following statement:
Procedure ProcName (Args : TPRocArgs); cdecl; external;
However, the [ C ] directive is no longer supported as of version 0.99.5 of Free Pascal, therefore you should use the external directive, with the cdecl directive, if needed.


4.2 Using external variables

Some libaries or code blocks have variables which they export. You can access these variables much in the same way as external functions. To access an external variable, you declare it as follows:

Var
  MyVar : MyType; external name 'varname';
The effect of this declaration is twofold:
  1. No space is allocated for this variable.
  2. The name of the variable used in the assembler code is varname. This is a case sensitive name, so you must be careful.
The variable will be accessible with it's declared name, i.e. MyVar in this case.

A second possibility is the declaration:

Var
  varname : MyType; cvar; external;
The effect of this declaration is twofold as in the previous case:
  1. The external modifier ensures that no space is allocated for this variable.
  2. The cvar modifier tells the compiler that the name of the variable used in the assembler code is exactly as specified in the declaration. This is a case sensitive name, so you must be careful.
In this case, you access the variable with it's C name, but case insensitive. The first possibility allows you to change the name of the external variable for internal use.

In order to be able to compile such statements, the compiler switch -Sv must be used.

As an example, let's look at the following C file (in extvar.c):

/*
Declare a variable, allocate storage
*/
int extvar = 12;
And the following program (in extdemo.pp):
Program ExtDemo;

{$L extvar.o}

Var { Case sensitive declaration !! }
    extvar : longint; cvar;external;
    I : longint; external name 'extvar';
begin
  { Extvar can be used case insensitive !! }
  Writeln ('Variable ''extvar'' has value : ',ExtVar);
  Writeln ('Variable ''I''      has value : ',i);
end.
Compiling the C file, and the pascal program:
gcc -c -o extvar.o extvar.c
ppc386 -Sv extdemo
Will produce a program extdemo which will print
Variable 'extvar' has value : 12
Variable 'I'      has value : 12
on your screen.


4.3 Linking to an object file

Having declared the external function or variable that resides in an object file, you can use it as if it was defined in your own program or unit. To produce an executable, you must still link the object file in. This can be done with the {$L file.o} directive.

This will cause the linker to link in the object file file.o. On LINUX systems, this filename is case sensitive. Under DOS, case isn't important. Note that file.o must be in the current directory if you don't specify a path. The linker will not search for file.o if it isn't found.

You cannot specify libraries in this way, it is for object files only.

Here we present an example. Consider that you have some assembly routine that calculates the nth Fibonacci number :

.text
        .align 4
.globl Fibonacci
        .type Fibonacci,@function
Fibonacci:
        pushl %ebp
        movl %esp,%ebp
        movl 8(%ebp),%edx
        xorl %ecx,%ecx
        xorl %eax,%eax
        movl $1,%ebx
        incl %edx
loop:
        decl %edx
        je endloop
        movl %ecx,%eax
        addl %ebx,%eax
        movl %ebx,%ecx
        movl %eax,%ebx
        jmp loop
endloop:
        movl %ebp,%esp
        popl %ebp
        ret
Then you can call this function with the following Pascal Program:
Program FibonacciDemo;

var i : longint;

Function Fibonacci (L : longint):longint;cdecl;external;

{$L fib.o}

begin
  For I:=1 to 40 do
    writeln ('Fib(',i,') : ',Fibonacci (i));
end.
With just two commands, this can be made into a program :
as -o fib.o fib.s
ppc386 fibo.pp
This example supposes that you have your assembler routine in fib.s, and your Pascal program in fibo.pp.


4.4 Linking to a library

To link your program to a library, the procedure depends on how you declared the external procedure.

In case you used the follwing syntax to declare your procedure:

Procedure ProcName (Args : TPRocArgs); external 'Name';
You don't need to take additional steps to link your file in, the compiler will do all that is needed for you. On WINDOWS NT it will link to Name.dll, on LINUX your program will be linked to library libname, which can be a static or dynamic library.

In case you used

Procedure ProcName (Args : TPRocArgs); external;
You still need to explicity link to the library. This can be done in 2 ways:
  1. You can tell the compiler in the source file what library to link to using the {$LinkLib 'Name'} directive:
    {$LinkLib 'gpm'}
    
    This will link to the gpm library. On LINUX systems, you needn't specify the extension or 'lib' prefix of the library. The compiler takes care of that. On DOS or WINDOWS systems, you need to specify the full name.
  2. You can also tell the compiler on the command-line to link in a library: The -k option can be used for that. For example
    ppc386 -k'-lgpm' myprog.pp
    
    Is equivalent to the above method, and tells the linker to link to the gpm library.

As an example; consider the following program :

program printlength;

{$linklib c} { Case sensitive }

{ Declaration for the standard C function strlen }
Function strlen (P : pchar) : longint; cdecl;external;

begin
  Writeln (strlen('Programming is easy !'));
end.
This program can be compiled with :
ppc386  prlen.pp
Supposing, of course, that the program source resides in prlen.pp.

To use functions in C that have a variable number of arguments, you must compile your unit or program in objfpc mode or Delphi mode, and use the Array of const argument, as in the following example:

program testaocc;

{$mode objfpc}

Const
  P : Pchar
    = 'example';
  F : Pchar
    = 'This %s uses printf to print numbers (%d) and strings.'#10;

procedure printf(fm: pchar;args: array of const);cdecl;external 'c';

begin
 printf(F,[P,123]);
end.
The output of this program looks like this:
This example uses printf to print numbers (123) and strings.


4.5 Making libraries

Free Pascal supports making shared or static libraries in a straightforward and easy manner. If you want to make libraries for other Free Pascal programmers, you just need to provide a command line switch. If you want C programmers to be able to use your code as well, you will need to adapt your code a little. This process is described first.

4.5.1 Exporting functions

When exporting functions from a library, there are 2 things you must take in account:

  1. Calling conventions.
  2. Naming scheme.
The calling conventions are controlled by the modifiers cdecl, popstack, pascal, stdcall. See section Calling for more information on the different kinds of calling scheme.

The naming conventions can be controlled by 3 modifiers:

cdecl:
A function that has a cdecl modifier, will be used with C calling conventions, that is, the caller clears the stack. Also the mangled name will be the name exactly as in the declaration. cdecl is part of the function declaration, and hence must be present both in the interface and implementation section of a unit.

export:
A function that has an export modifier, uses also the exact declaration name as its mangled name. Under WINDOWS NT and OS/2, this modifier signals a function that is exported from a DLL. The calling conventions used by a export procedure depend on the OS. This keyword can be used only in the implementation section.
Alias:
The alias modifier can be used to give a second assembler name to your function. This doesn't modify the calling conventions of the function.

If you want to make your procedures and functions available to C programmers, you can do this very easily. All you need to do is declare the functions and procedures that you want to make available as export, as follows:

Procedure ExportedProcedure; export;

Remark: You can only declare a function as exported in the Implementation section of a unit. This function may not appear in the interface part of a unit. This is logical, since a Pascal routine cannot call an exported function, anyway.

However, the generated object file will not contain the name of the function as you declared it. The Free Pascal compiler ''mangles'' the name you give your function. It makes the name all-uppercase, and adds the types of all parameters to it. There are cases when you want to provide a mangled name without changing the calling convention. In such cases, you can use the Alias modifier.

The Alias modifier allows you to specify another name (a nickname) for your function or procedure.

The prototype for an aliased function or procedure is as follows :

Procedure AliasedProc; [ Alias : 'AliasName'];
The procedure AliasedProc will also be known as AliasName. Take care, the name you specify is case sensitive (as C is).

Remark: If you use in your unit functions that are in other units, or system functions, then the C program will need to link in the object files from the units too.

4.5.2 Exporting variables

Similarly as when you export functions, you can export variables. When exportig variables, one should only consider the names of the variables. To declare a variable that should be used by a C program, one declares it with the cvar modifier:
Var MyVar : MyTpe; cvar;
This will tell the compiler that the assembler name of the variable (the one which is used by C programs) should be exactly as specified in the declaration, i.e., case sensitive.

It is not allowed to declare multiple variables as cvar in one statement, i.e. the following code will produce an error:

var Z1,Z2 : longint;cvar;

4.5.3 Compiling libraries

Once you have your (adapted) code, with exported and other functions, you can compile your unit, and tell the compiler to make it into a library. The compiler will simply compile your unit, and perform the necessary steps to transform it into a static or shared (dynamical) library.

You can do this as follows, for a dynamical library:

ppc386 -CD myunit
On LINUX this will leave you with a file libmyunit.so. On WINDOWS and OS/2, this will leave you with myunit.dll.

If you want a static library, you can do

ppc386 -CS myunit
This will leave you with libmyunit.a and a file myunit.ppu. The myunit.ppu is the unit file needed by the Free Pascal compiler.

The resulting files are then libraries. To make static libraries, you need the ranlib or ar program on your system. It is standard on any LINUX system, and is provided with the GCC compiler under DOS. For the dos distribution, a copy of ar is included in the file gnuutils.zip.

BEWARE: This command doesn't include anything but the current unit in the library. Other units are left out, so if you use code from other units, you must deploy them together with your library.

4.5.4 Moving units into a library

You can put multiple units into a library with the ppumove command, as follows:

ppumove -e ppl -o name unit1 unit2 unit3
This will move 3 units in 1 library (called libname.so on linux, name.dll on WINDOWS) and it will create 3 files unit1.ppl, unit2.ppl and unit3.ppl, which are unit files, but which tell the compiler to look in library name when linking your executable.

The ppumove program has options to create statical or dynamical libraries. It is provided with the compiler.

4.5.5 Unit searching strategy

When you compile a program or unit, the compiler will by default always look for .ppl files. If it doesn't find one, it will look for a .ppu file.

To be able to differentiate between units that have been compiled as static or dynamic libraries, there are 2 switches:

-XD:
This will define the symbol FPC_LINK_DYNAMIC
-XS:
This will define the symbol FPC_LINK_STATIC
Definition of one symbol will automatically undefine the other.

These two switches can be used in conjunction with the configuration file ppc386.cfg. The existence of one of these symbols can be used to decide which unit search path to set. For example:

# Set unit paths

#IFDEF FPC_LINK_STATIC
-Up/usr/lib/fpc/linuxunits/staticunits
#ENDIF
#IFDEF FPC_LINK_DYNAMIC
-Up/usr/lib/fpc/linuxunits/sharedunits
#ENDIF
With such a configuration file, the compiler will look for it's units in different directories, depending on whether -XD or -XS is used.


4.6 Using smart linking

You can compile your units using smart linking. When you use smartlinking, the compiler creates a series of code blocks that are as small as possible, i.e. a code block will contain only the code for one procedure or function.

When you compile a program that uses a smart-linked unit, the compiler will only link in the code that you actually need, and will leave out all other code. This will result in a smaller binary, which is loaded in memory faster, thus speeding up execution.

To enable smartlinking, one can give the smartlink option on the command line : -Cx, or one can put the {$SMARTLINK ON} directive in the unit file:

Unit Testunit

{SMARTLINK ON}
Interface
...
Smartlinking will slow down the compilation process, especially for large units.

When a unit foo.pp is smartlinked, the name of the codefile is changed to libfoo.a.

Technically speaking, the compiler makes small assembler files for each procedure and function in the unit, as well as for all global defined variables (whether they're in the interface section or not). It then assembles all these small files, and uses ar to collect the resulting object files in one archive.

Smartlinking and the creation of shared (or dynamic) libraries are mutually exclusive, that is, if you turn on smartlinking, then the creation of shared libraries is turned of. The creation of static libraries is still possible. The reason for this is that it has little sense in making a smarlinked dynamical library. The whole shared library is loaded into memory anyway by the dynamic linker (or WINDOWS NT), so there would be no gain in size by making it smartlinked.



root
2000-12-20