Subsections


7 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 linking to C libraries, or to external object files created by other compilers may be necessary. The Free Pascal compiler can generate calls to a C function, and can generate functions that can be called from C (exported functions).

1 Using external code and variables

In general, there are 3 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 declare the correct calling convention to use.
  3. 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.


1 Declaring external function 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. A calling convention modifier should be declared if the external code blocks does not have the same calling conventions as Free Pascal. For more information on the calling conventions section CallingConventions.

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. The name of the routine is case-sensitive and should match exactly the name of the routine in the object file.

    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.


2 Declaring 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.
The first possibility allows you to change the name of the external variable for internal use.

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.

3 Declaring the calling convention modifier

To make sure that all parameters are correctly passed to the external routines, you should declare it with the correct calling convention modifier. When linking with code blocks compiled with standard C compilers (such as GCC), the cdecl modifier should be used so as to indicate that the external routine uses C type calling conventions. For more information on the supported calling conventions, section CallingConventions.

As might expected, external variable declarations do not require any calling convention modifier.

4 Declaring the external object code


1 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 most systems, this filename is case sensitive. The object file is first searched in the current directory, and then the directories specified by the -Fo command line.

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 which uses the C calling convention 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.


2 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 following 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 it will link to name.dll, on LINUX and most UNIX'es 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 UNIX systems (such as LINUX), you must not specify the extension or 'lib' prefix of the library. The compiler takes care of that. On other systems (such as WINDOWS, 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.


2 Making libraries

Free Pascal supports making shared or static libraries in a straightforward and easy manner. If you want to make static libraries for other Free Pascal programmers, you just need to provide a command line switch. To make shared libraries, refer to the chapter libraries. 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.

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, safecall, stdcall and register. See section CallingConventions for more information on the different kinds of calling scheme.

The naming conventions can be controlled by 2 modifiers in the case of static libraries:

For more information on how these different modifiers change the name mangling of the routine section NameMangling.

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 these units too.

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;

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 (dynamic) library.

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

ppc386 -CD myunit
On UNIX systems, such as LINUX, this will leave you with a file libmyunit.so. On WINDOWS and OS/2, this will leave you with myunit.dll. An easier way to create shared libraries is to use the library keyword. For more information on creating shared libraries, chapter libraries.

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 most UNIX systems, 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 Unit searching strategy

When you compile a unit, the compiler will by default always look for unit files.

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 or ppc.cfg. The existence of one of these symbols can be used to decide which unit search path to set. For example, on LINUX:

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


3 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 smartlinked dynamical library. The whole shared library is loaded into memory anyway by the dynamic linker (or the operating system), so there would be no gain in size by making it smartlinked.



Free Pascal Compiler
2001-09-22