Subsections


11 Programming shared libraries

1 Introduction

Free Pascal supports the creation of shared libraries on several operating systems. The following table (table (DLLSupport) ) indicates which operating systems support the creation of shared libraries.


Table: Shared library support
Operating systems Library extension Library prefix
linux .so lib
windows .dll <none>
BeOS .so lib
FreeBSD .so lib
NetBSD .so lib

The library prefix column indicates how the names of the libraries are resolved and created. For example, under LINUX, the library name will alwauys have lib prefix when it is created. So if you create a library called mylib, under LINUX, this will result in the libmylib.so. Furthermore, when importing routines from shared libraries, it is not necessary to give the library prefix or the filename extension.

In the following sections we discuss how to create a library, and how to use these libraries in programs.

2 Creating a library

Creation of libraries is supported in any mode of the Free Pascal compiler, but it may be that the arguments or return values differ if the library is compiled in 2 different modes. E.g. if your function expects an Integer argument, then the library will expect different integer sizes if you compile it in Delphi mode or in TP mode.

A library can be created just as a program, only it uses the library keyword, and it has an exports section. The following listing demonstrates a simple library:

Example
{
  Example library
}
library subs;

function SubStr(CString: PChar;FromPos,ToPos: Longint): PChar; 
   cdecl; export;

var
  Length: Integer;

begin
  Length := StrLen(CString);
  SubStr := CString + Length;
  if (FromPos > 0) and (ToPos >= FromPos) then
  begin
    if Length >= FromPos then
      SubStr := CString + FromPos - 1;
    if Length > ToPos then
    CString[ToPos] := #0;
  end;
end;

exports
  SubStr;

end.

The function SubStr does not have to be declared in the library file itself. It can also be declared in the interface section of a unit that is used by the library.

Compilation of this source will result in the creation of a library called libsubs.so on UNIX systems, or subs.dll on WINDOWS or OS/2. The compiler will take care of any additional linking that is required to create a shared library.

The library exports one function: SubStr. The case is important. The case as it appears in the exports clause is used to export the function.

If you want your library to be called from programs compiled with other compilers, it is important to specify the correct calling convention for the exported functions. Since the generated programs by other compilers do not know about the Free Pascal calling conventions, your functions would be called incorrectly, resulting in a corrupted stack.

On WINDOWS, most libraries use the stdcall convention, so it may be better to use that one if your library is to be used on WINDOWS systems. On most UNIX systems, the C calling convention is used, therefore the cdecl modifier should be used in that case.

3 Using a library in a pascal program

In order to use a function that resides in a library, it is sufficient to declare the function as it exists in the library as an external function, with correct arguments and return type. The calling convention used by the function should be declared correctly as well. The compiler will then link the library as specified in the external statement to your program[*].

For example, to use the library as defined above from a pascal program, you can use the following pascal program:

Example
program testsubs;

function SubStr(const CString: PChar; FromPos, ToPos: longint): PChar;
  cdecl; external 'subs';

var
  s: PChar;
  FromPos, ToPos: Integer;
begin
  s := 'Test';
  FromPos := 2;
  ToPos := 3;
  WriteLn(SubStr(s, FromPos, ToPos));
end.
As is shown in the example, you must declare the function as external. Here also, it is necessary to specify the correct calling convention (it should always match the convention as used by the function in the library), and to use the correct casing for your declaration. Also notice, that the library importing did not specify the filename extension, nor was the lib prefix added.

This program can be compiled without any additional command-switches, and should run just like that, provided the library is placed where the system can find it. For example, on LINUX, this is /usr/lib or any directory listed in the /etc/ld.so.conf file. On WINDOWS, this can be the program directory, the WINDOWS system directory, or any directoy mentioned in the PATH.

Using the library in this way links the library to your program at compile time. This means that

  1. The library must be present on the system where the program is compiled.
  2. The library must be present on the system where the program is executed.
  3. Both libraries must be exactly the same.
Or it may simply be that you don't know the name of the function to be called, you just know the arguments it expects.

It is therefore also possible to load the library at run-time, store the function address in a procedural variable, and use this procedural variable to access the function in the library.

The following example demonstrates this technique:

Example
program testsubs;

Type
  TSubStrFunc = 
    function(const CString:PChar;FromPos,ToPos: longint):PChar;cdecl;

Function dlopen(name: pchar;mode: longint):pointer;cdecl;external 'dl';
Function dlsym(lib: pointer; name: pchar):pointer;cdecl;external 'dl';
Function dlclose(lib: pointer):longint;cdecl;external 'dl';

var
  s: PChar;
  FromPos, ToPos: Integer;
  lib : pointer;
  SubStr : TSubStrFunc;
  
begin
  s := 'Test';
  FromPos := 2;
  ToPos := 3;
  lib:=dlopen('libsubs.so',1);
  Pointer(Substr):=dlsym(lib,'SubStr');
  WriteLn(SubStr(s, FromPos, ToPos));
  dlclose(lib);
end.
As in the case of compile-time linking, the crucial thing in this listing is the declaration of the TSubStrFunc type. It should match the declaration of the function you're trying to use. Failure to specify a correct definition will result in a faulty stack or, worse still, may cause your program to crash with an access violation.

4 Using a pascal library from a C program

Remark: The examples in this section assume a LINUX system; similar commands as the ones below exist for other operating systems, though.

You can also call a Free Pascal generated library from a C program: ctest To compile this example, the following command can be used:

gcc -o ctest ctest.c -lsubs
provided the code is in ctest.c.

The library can also be loaded dynamically from C, as shown in the following example: ctest2 This can be compiled using the following command:

gcc -o ctest2 ctest2.c -ldl
language=delphi The -ldl tells gcc that the program needs the libdl.so library to load dynamical libraries.



Free Pascal Compiler
2001-09-22