Programmation de bibliothèques partagées
Le Free Pascal prend en charge la création de bibliothèques partagées sur plusieurs systèmes d'exploitation. Le tableau suivant indique quels systèmes d'exploitation prennent en charge la création de bibliothèques partagées :
Systèmes d'exploitation | Extension de la bibliothèque | Préfixe de bibliothèque |
---|---|---|
Linux | .so | lib |
Windows | .dll | Aucun |
BeOS | .so | lib |
FreeBSD | .so | lib |
NetBSD | .so | lib |
La colonne de préfixe de bibliothèque indique comment les noms des bibliothèques sont résolus et créés. Par exemple, sous Linux, le nom de la bibliothèque aura toujours le préfixe lib lors de sa création. Donc si vous créez une bibliothèque appelée mylib, sous Linux, cela donnera le libmylib.so. De plus, lors de l'importation de routines depuis des bibliothèques partagées, il n'est pas nécessaire de donner le préfixe de la bibliothèque ou l'extension du nom de fichier.
Dans les sections suivantes, nous expliquons comment créer une bibliothèque et comment utiliser ces bibliothèques dans des programmes.
Création d'une bibliothèque
La création de bibliothèques est prise en charge dans n'importe quel mode du compilateur Free Pascal, mais il se peut que les paramètres ou les valeurs de retour diffèrent si la bibliothèque est compilée dans 2 modes différents. Par exemple. si votre fonction attend un paramètre Integer, alors la bibliothèque attendra différentes tailles entières si vous la compilez en mode Delphi ou en mode TP.
Une bibliothèque peut être créée comme un programme, seulement elle utilise le mot-clef library et elle possède une section d'exportation. La liste suivante présente une bibliothèque simple (progex/subs.pp) :
- Library Subs;
-
- Function SubStr(CString:PChar;FromPos,ToPos:LongInt):PChar; cdecl;
- 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;
- If _Length>ToPos Then CString[ToPos+1]:=#0;
- End;
- End;
-
- Exports
- SubStr;
-
- END.
La fonction SubStr n'a pas besoin d'être déclarée dans le fichier bibliothèque lui-même. Il peut également être déclaré dans la section interface d'une unité utilisée par la bibliothèque.
La compilation de cette source entraînera la création d'une bibliothèque appelée libsubs.dylib sur MacOS, libsubs.so sur d'autres systèmes Unix, ou subs.dll sur Windows ou OS/2. Le compilateur prendra en charge toute liaison supplémentaire nécessaire pour créer une bibliothèque partagée.
La bibliothèque exporte une fonction : SubStr. L'affaire est importante. La casse telle qu'elle apparaît dans la clause Exports est utilisée pour exporter la fonction.
Si vous souhaitez que votre bibliothèque soit appelée à partir de programmes compilés avec d'autres compilateurs, il est important de spécifier la convention d'appel correcte pour les fonctions exportées. Étant donné que les programmes générés par d'autres compilateurs ne connaissent pas les conventions d'appel de Free Pascal, vos fonctions seraient appelées de manière incorrecte, ce qui entraînerait une pile corrompue.
Sous Windows, la plupart des bibliothèques utilisent la convention stdcall, il peut donc être préférable d'utiliser celle-là si votre bibliothèque doit être utilisée sur les systèmes Windows. Sur la plupart des systèmes Unix, la convention d'appel C est utilisée, donc le modificateur cdecl doit être utilisé dans ce cas.
Utiliser une bibliothèque dans un programme Pascal
Pour utiliser une fonction résidant dans une bibliothèque, il suffit de déclarer la fonction telle qu'elle existe dans la bibliothèque comme fonction externe, avec les paramètres et le type de retour corrects. La convention d'appel utilisée par la fonction doit également être déclarée correctement. Le compilateur liera ensuite la bibliothèque comme spécifié dans l'instruction externe à votre programme.
Par exemple, pour utiliser la bibliothèque telle que définie ci-dessus depuis un programme Pascal, vous pouvez utiliser le programme Pascal suivant, nommé progex/psubs.pp, (sauf sur les MacOS) :
Comme le montre l'exemple, vous devez déclarer la fonction comme externe, suivie du nom de la bibliothèque. Sur Mac, vous devez également ajouter la directive {$linklib }, comme dans l'exemple suivant (progex/psubsm.pp) :
Ici aussi, il est nécessaire de spécifier la convention d'appel correcte (elle doit toujours correspondre à la convention utilisée par la fonction dans la bibliothèque), et d'utiliser la casse correcte pour votre déclaration. Notez également que l'importation de la bibliothèque n'a pas spécifié l'extension du nom de fichier et que le préfixe lib n'a pas été ajouté.
Ce programme peut être compilé sans aucun commutateur de commande supplémentaire et devrait fonctionner comme ça, à condition que la bibliothèque soit placée là où le système peut la trouver. Par exemple, sous Linux, il s'agit de /usr/lib ou de tout répertoire répertorié dans le fichier /etc/ld.so.conf. Sous Windows, il peut s'agir du répertoire du programme, du répertoire système Windows ou de tout répertoire mentionné dans le PATH.
Utiliser la bibliothèque de cette manière relie la bibliothèque à votre programme au moment de la compilation. Cela signifie que :
- La bibliothèque doit être présente sur le système sur lequel le programme est compilé.
- La bibliothèque doit être présente sur le système sur lequel le programme est exécuté.
- Les deux bibliothèques doivent être exactement identiques.
Ou il se peut simplement que vous ne connaissiez pas le nom de la fonction à appeler, vous connaissez simplement les arguments qu'elle attend.
Il est donc également possible de charger la bibliothèque au moment de l'exécution, d'entreposer l'adresse de la fonction dans une variable procédurale et d'utiliser cette variable procédurale pour accéder à la fonction dans la bibliothèque. Le dynlibs fournit une API multiplateforme pour charger une bibliothèque et obtenir les points d'entrée des fonctions dans la bibliothèque.
L'exemple suivant, nommé progex/plsubs.pp, illustre cette technique :
- Program TestSubs;
-
- Uses DynLibs;
-
- Type
- TSubStrFunc=Function(Const CString:PChar; FromPos,ToPos:LongInt):PChar; cdecl;
-
- Const
- BaseLibName='libsubs.';
- {$IFDEF WINDOWS}
- libext='dll';
- {$ELSE}
- {$IFDEF MACOS}
- libext='dylib';
- {$ELSE}
- libext='so';
- {$ENDIF}
- {$ENDIF}
-
- Var
- S:PChar ;
- FromPos,ToPos:Integer;
- Lib:TLibHandle;
- SubStr:TSubStrFunc;
-
- BEGIN
- S:='Test';
- FromPos:=2;
- ToPos:=3;
- Lib:=LoadLibrary('libsubs.so');
- Pointer(Substr):=GetProcAddress(lib,'SubStr');
- WriteLn(SubStr(s,FromPos,ToPos));
- UnloadLibrary(Lib);
- END.
Comme dans le cas des liaisons à la compilation, l'élément crucial de ce code source est la déclaration du type TSubStrFunc. Il doit correspondre à la déclaration de la fonction que vous essayez d'utiliser.
Le fait de ne pas spécifier une définition correcte entraînera une pile défectueuse ou, pire encore, pourrait provoquer le crash de votre programme en raison d'une violation d'accès.
Utiliser une bibliothèque Pascal à partir d'un programme C
Remarque : Les exemples de cette section supposent un système Linux ; Des commandes similaires à celles ci-dessous existent cependant pour d'autres systèmes d'exploitation.
Vous pouvez également appeler une bibliothèque générée par Free Pascal depuis un programme C (celui-ci est nommé progex/ctest.c) :
Pour compiler cet exemple, la commande suivante peut être utilisée :
gcc -o ctest ctest.c -lsubs |
à condition que le code soit dans ctest.c.
La bibliothèque peut également être chargée dynamiquement à partir de C, comme le montre l'exemple suivant (progex/ctest2.c) :
- #include <dlfcn.h>
- #include <string.h>
- #include <stdio.h>
-
- int main() {
- void * lib;
- char *s;
- int FromPos,ToPos;
- char *(*SubStr) (const char *, int, int);
- lib=dlopen("./libsubs.so",RTLD_LAZY);
- SubStr = dlsym(lib,"SUBSTR");
- s=strdup( " Test " );
- FromPos=2;
- ToPos=3;
- printf("Résultat de SubStr : '%s '\n",(*SubStr) (s,FromPos,ToPos));
- dlclose(lib);
- return 0;
- }
Cela peut être compilé à l'aide de la commande suivante :
gcc -o ctest2 ctest2.c -ldl |
Le -ldl indique à gcc que le programme a besoin de la bibliothèque libdl.so pour charger les bibliothèques dynamiques.
Quelques problèmes Windows
Par défaut, Free Pascal (en fait, l'éditeur de liens utilisé par Free Pascal) crée des bibliothèques n'étant pas déplaçables. Cela signifie qu'ils doivent être chargés à une adresse fixe en mémoire : cette adresse est appelée adresse ImageBase. Si deux bibliothèques générées par Free Pascal sont chargées par un programme, il y aura un conflit, car la première bibliothèque occupe déjà l'emplacement mémoire où la deuxième bibliothèque doit être chargée.
Il existe 2 commutateurs dans Free Pascal contrôlant la génération des bibliothèques partagées sous Windows :
- WR Génère une bibliothèque déplaçable. Cette bibliothèque peut être déplacée vers un autre emplacement en mémoire si l'adresse ImageBase souhaitée est déjà utilisée.
- WB Spécifiez l'adresse ImageBase de la bibliothèque générée. L'ImageBase standard utilisé par Free Pascal est 0x10000000. Ce commutateur permet de modifier cela en spécifiant une autre adresse, par exemple -WB11000000.
La première option est préférable, car un programme peut charger de nombreuses bibliothèques présentes sur le système et celles-ci peuvent déjà utiliser l'adresse ImageBase. La deuxième option est plus rapide, car aucun déplacement n'est nécessaire si l'adresse ImageBase n'est pas encore utilisée.