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:
You must make a pascal declaration of the function or procedure you want to use.
You must declare the correct calling convention to use.
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.
Declaring external functions or procedures
The first step in using external code blocks is declaring the function you want to use. Free Pascalsupports 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 calling conventions section Code issues.
There exist four variants of the external directive:
A simple external declaration:
Procedure ProcName (Args : TPRocArgs); external;
The
externaldirective 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).You can give the
externaldirective 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'}The
externalcan 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;Lastly, under Windowsand OS/2, there is a fourth possibility to specify an external function: In
.DLLfiles, functions also have a unique number (their index). It is possible to refer to these functions using their index:Procedure ProcName (Args : TPRocArgs); external 'Name' Index SomeIndex;This tells the compiler that the procedure
ProcNameresides in a dynamic link library, with indexSomeIndex.Remark: Note that this is only available under Windowsand OS/2.
Declaring external variables
Some libraries 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:
No space is allocated for this variable.
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 its 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:
The
externalmodifier ensures that no space is allocated for this variable.The
cvarmodifier tells the compiler that the name of the variable used in the assembler code is exactly as specified in the declaration, but is subject to C name mangling (it can e.g. be prefixed with a _ on some platforms): this means the name 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 insensitively }
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.
Declaring the calling convention modifier
To make sure that all parameters are correctly passed to the external routines, you should declare them 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, see section Code issues.
As might be expected, external variable declarations do not require any calling convention modifier.
Declaring the external object code
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 were 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.
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 Windowsit will link to name.dll, on Linuxand 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 explicitly link to the library. This can be done in 2 ways:
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
gpmlibrary. On Unixsystems (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.You can also tell the compiler on the command line to link in a library: The
-koption can be used for that. For exampleppc386 -k'-lgpm' myprog.pp
Is equivalent to the above method, and tells the linker to link to the
gpmlibrary.
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.
As an alternative, the program can be constructed as follows:
program testaocc;
Const
P : Pchar
= 'example';
F : Pchar
= 'This %s uses printf to print numbers (%d) and strings.'#10;
procedure printf(fm: pchar);cdecl;varargs;external 'c';
begin
printf(F,P,123);
end.
The varargs modifier signals the compiler that the function allows a variable number of arguments (the ellipsis notation in C).
An experiment, built from development snapshots. Not the official Free Pascal or Lazarus documentation.
