Section courante

A propos

Section administrative du site

Problèmes de liaison

Lorsque vous n'utilisez que du code Pascal et des unités Pascal, vous ne verrez pas grand-chose du rôle joué par l'éditeur de liens dans la création de votre exécutable. L'éditeur de liens n'est appelé que lorsque vous compilez un programme. Lors de la compilation des unités, l'éditeur de liens n'est pas appelé.

Cependant, il peut arriver qu'une liaison à des bibliothèques C ou à des fichiers objets externes créés par d'autres compilateurs soit nécessaire. Le compilateur Free Pascal peut générer des appels à une fonction C, et peut générer des fonctions pouvant être appelées depuis C (fonctions exportées).

Utiliser du code externe et des variables

En général, il y a 3 choses que vous devez faire pour utiliser une fonction résidant dans une bibliothèque externe ou un fichier objet :

  1. Vous devez faire une déclaration en pascal de la fonction ou de la procédure que vous souhaitez utiliser.
  2. Vous devez déclarer la convention d'appel correcte à utiliser.
  3. Vous devez indiquer au compilateur où réside la fonction, c'est-à-dire dans quel fichier objet ou quelle bibliothèque, afin que le compilateur puisse y lier le code nécessaire.

Il en va de même pour les variables. Pour accéder à une variable résidant dans un fichier objet externe, vous devez la déclarer et indiquer au compilateur où la trouver. Les sections suivantes tentent d'expliquer comment procéder.

Déclarer des fonctions ou procédures externes

La première étape de l'utilisation de blocs de code externes consiste à déclarer la fonction que vous souhaitez utiliser. Le Free Pascal prend en charge la syntaxe Delphi, c'est-à-dire que vous devez utiliser la directive externe. La directive external remplace, en effet, le bloc de code de la fonction.

La directive external ne spécifie pas de convention d'appel ; il indique simplement au compilateur que le code d'une procédure ou d'une fonction réside dans un bloc de code externe. Un modificateur de convention d'appel doit être déclaré si les blocs de code externes n'ont pas les mêmes conventions d'appel que Free Pascal.

Il existe quatre variantes de la directive externe :

Remarque : Notez que ceci n'est disponible que sous Windows et OS/2.

Déclaration des variables externes

Certaines bibliothèques ou blocs de code ont des variables qu'ils exportent. Vous pouvez accéder à ces variables de la même manière que les fonctions externes. Pour accéder à une variable externe, vous la déclarez comme suit :

  1. Var
  2.  MyVar:MyType; External name 'varname';

L'effet de cette déclaration est double :

  1. Aucun espace n'est alloué à cette variable.
  2. Le nom de la variable utilisée dans le code assembleur est varname. Il s'agit d'un nom sensible à la casse, vous devez donc faire attention.

La variable sera accessible avec son nom déclaré, c'est-à-dire MyVar dans ce cas. Une seconde possibilité est la déclaration :

  1. Var
  2.  VarName:MyType; cvar; External;

L'effet de cette déclaration est double comme dans le cas précédent :

La première possibilité permet de changer le nom de la variable externe à usage interne.

A titre d'exemple, regardons le fichier C suivant (dans extvar.c) :

  1. /*
  2. Déclarer une variable, allouer à l'entreposage
  3. */
  4. int extvar = 12;

Et le programme suivant (dans extdemo.pp) :

  1. Program ExtDemo;
  2. {$L extvar.o}
  3. Var
  4.  { Déclaration sensible à la casse }
  5.  ExtVar:LongInt; cvar;External;
  6.  I:LongInt; External Name 'extvar';
  7. BEGIN
  8.  { Extvar peut être utilisé sans tenir compte de la casse }
  9.  Writeln('La variable ''extvar'' a pour valeur : ',ExtVar);
  10.  Writeln('La variable ''I'' a pour valeur : ',I);
  11. END.

Compilation du fichier C, et du programme Pascal :

gcc -c -o extvar.o extvar.c
ppc386 -Sv extdemo

Produira un programme extdemo affichant :

La variable ''extvar'' a pour valeur : 12
La variable ''I'' a pour valeur : 12

sur votre écran.

Déclaration du modificateur de convention d'appel

Pour vous assurer que tous les paramètres sont correctement transmis aux routines externes, vous devez les déclarer avec le modificateur de convention d'appel correct. Lors de la liaison avec des blocs de code compilés avec des compilateurs C standard (tels que GCC), le modificateur cdecl doit être utilisé de manière à indiquer que la routine externe utilise les conventions d'appel de type C.

Comme on pouvait s'y attendre, les déclarations de variables externes ne nécessitent aucun modificateur de convention d'appel.

Déclaration du code objet externe

Lien vers un fichier objet : Après avoir déclaré la fonction ou la variable externe qui réside dans un fichier objet, vous pouvez l'utiliser comme si elle était définie dans votre propre programme ou unité. Pour produire un exécutable, vous devez toujours lier le fichier objet. Cela peut être fait avec la directive {$L file.o}.

Cela amènera l'éditeur de liens à créer un lien dans le fichier objet file.o. Sur la plupart des systèmes, ce nom de fichier est sensible à la casse. Le fichier objet est d'abord recherché dans le répertoire courant, puis dans les répertoires spécifiés par la ligne de commande -Fo.

Vous ne pouvez pas spécifier de bibliothèques de cette manière, cela concerne uniquement les fichiers objets. On présente ici un exemple. Considérez que vous disposez d'une routine d'assemblage qui utilise la convention d'appel C calculant le nième nombre de Fibonacci :

  1. .text
  2. .align 4
  3. .globl Fibonacci
  4. .type Fibonacci,@function
  5. Fibonacci:
  6. pushl %ebp
  7. movl %esp,%ebp
  8. movl 8(%ebp),%edx
  9. xorl %ecx,%ecx
  10. xorl %eax,%eax
  11. movl $1,%ebx
  12. incl %edx
  13. loop:
  14. decl %edx
  15. je endloop
  16. movl %ecx,%eax
  17. addl %ebx,%eax
  18. movl %ebx,%ecx
  19. movl %eax,%ebx
  20. jmp loop
  21. endloop:
  22. movl %ebp,%esp
  23. popl %ebp
  24. ret

Ensuite, vous pouvez appeler cette fonction avec le programme Pascal suivant :

  1. Program FibonacciDemo;
  2. Var 
  3.  I:LonGint;
  4.  
  5. Function Fibonacci(L:LongInt):LongInt;cdecl;External; {$L fib.o}
  6.  
  7. BEGIN
  8.  For I:=1 to 40 do WriteLn('Fib(',i,') : ',Fibonacci(i));
  9. END.

Avec seulement deux commandes, cela peut être transformé en programme :

as -o fib.o fib.s
ppc386 fibo.pp

Cet exemple suppose que vous avez votre routine assembleur dans fib.s et votre programme Pascal dans fibo.pp.

Lien vers une bibliothèque : Pour lier votre programme à une bibliothèque, la procédure dépend de la façon dont vous avez déclaré la procédure externe. Si vous avez utilisé la syntaxe suivante pour déclarer votre procédure :

  1. Procedure ProcName(Args:TPRocArgs);External 'Name';

Vous n'avez pas besoin de prendre des mesures supplémentaires pour lier votre fichier, le compilateur fera tout ce qui est nécessaire pour vous. Sous Windows, il sera lié à name.dll, sous LINUX et la plupart des Unix, votre programme sera lié à la bibliothèque libname, pouvant être une bibliothèque statique ou dynamique. Au cas où vous auriez utilisé :

  1. Procedure ProcName(Args:TPRocArgs); External;

Vous devez toujours créer un lien explicite vers la bibliothèque. Cela peut être fait de 2 manières :

  1. Vous pouvez indiquer au compilateur dans le fichier source à quelle bibliothèque créer un lien en utilisant la directive {$LinkLib 'Name'} :

    1. {$LinkLib 'gpm'}

    Cela créera un lien vers la bibliothèque gpm. Sur les systèmes UNIX (tels que LINUX), vous ne devez pas spécifier l'extension ou le préfixe «lib» de la bibliothèque. Le compilateur s'en charge. Sur d'autres systèmes (tels que Windows), vous devez spécifier le nom complet.

  2. Vous pouvez également indiquer au compilateur sur la ligne de commande de créer un lien vers une bibliothèque : l'option -k peut être utilisée pour cela. Par exemple :

  3. ppc386 -k'-lgpm' myprog.pp

    Est équivalent à la méthode ci-dessus et indique à l'éditeur de liens de créer un lien vers la bibliothèque gpm.

A titre d'exemple, considérons le programme suivant :

  1. Program PrintLength;
  2.  
  3. {$linklib c} { Sensible aux majuscules et minuscules }
  4.  
  5. { Déclaration pour la fonction C standard strlen }
  6. Function StrLen(P:PChar):LongInt; cdecl;External;
  7.  
  8. BEGIN
  9.  Writeln(StrLen('La programmation est simple !'));
  10. END.

Ce programme peut être compilé avec :

ppc386 prlen.pp

En supposant, bien sûr, que la source du programme réside dans prlen.pp.

Pour utiliser des fonctions en C qui ont un nombre variable de paramètres, vous devez compiler votre unité ou programme en mode objfpc ou en mode Delphi, et utiliser le paramètre Array of Const, comme dans l'exemple suivant :

  1. Program Testaocc;
  2.  
  3. {$mode objfpc}
  4.  
  5. Const
  6.  P:Pchar='Exemple';
  7.  F:PChar= 'Ce %s utilise printf pour imprimer des nombres (%d) et des chaînes de caractères.'#10;
  8.  
  9. Procedure Printf(fm:PChar;Args:Array of const);cdecl;external 'c';
  10.  
  11. BEGIN
  12.  printf(F,[P,123]);
  13. END.

Le résultat de ce programme ressemble à ceci :

Cet exemple utilise printf pour imprimer des nombres (123) et des chaînes de caractères.

Alternativement, le programme peut être construit comme suit :

  1. Program TestAocc;
  2. Const
  3.  P:PChar='Exemple';
  4.  F:Pchar= 'Ce %s utilise printf pour imprimer des nombres (%d) et des chaînes de caractères.'#10; 
  5.  
  6. Procedure Printf(fm:PChar);cdecl;Varargs;External 'c';
  7.  
  8. BEGIN
  9.  printf(F,P,123);
  10. END.

Le modificateur varargs signale au compilateur que la fonction autorise un nombre variable de paramètres (la notation points de suspension en C).

Créer des bibliothèques

Le Free Pascal prend en charge la création de bibliothèques partagées ou statiques de manière simple et directe. Si vous souhaitez créer des bibliothèques statiques pour d'autres programmeurs Free Pascal, il vous suffit de fournir un commutateur de ligne de commande. Si vous souhaitez que les programmeurs C puissent également utiliser votre code, vous devrez adapter un peu votre code. Ce processus est décrit en premier.

Fonctions d'exportation

Lors de l'exportation de fonctions depuis une bibliothèque, vous devez prendre en compte 2 éléments :

  1. Appel aux conventions
  2. Schéma de dénomination

Les conventions d'appel sont contrôlées par les modificateurs cdecl, popstack, pascal, safecall, stdcall et register.

Les conventions de dénomination peuvent être contrôlées par 2 modificateurs dans le cas de bibliothèques statiques :

Remarque : Si dans votre unité, vous utilisez des fonctions qui se trouvent dans d'autres unités, ou des fonctions système, alors le programme C devra également lier les fichiers objets de ces unités.

Exporter des variables

Tout comme vous pouvez exporter des fonctions, vous pouvez également exporter des variables. Lors de l'exportation de variables, il convient de considérer uniquement les noms des variables. Pour déclarer une variable devant être utilisée par un programme C, on la déclare avec le modificateur cvar :

  1. Var MyVar:MyTpe; cvar;

Cela indiquera au compilateur que le nom assembleur de la variable (tel qu'utilisé par les programmes C) doit être exactement tel que spécifié dans la déclaration, c'est-à-dire sensible à la casse.

Il n'est pas permis de déclarer plusieurs variables comme cvar dans une seule instruction, c'est-à-dire que le code suivant produira une erreur :

  1. Var Z1,Z2:LongInt;cvar;

Compilation de bibliothèques

Pour créer des bibliothèques partagées, il faut utiliser le mot clef library dans le fichier de compilation principal (le fichier projet).

Les fichiers objets .o que le compilateur écrit lorsqu'il compile une unité sont des fichiers objets normaux car ils sont produits par un compilateur C. Ils peuvent être combinés à l'aide des outils ar et ranlib dans une bibliothèque statique. Cependant, pour diverses raisons, c'est une mauvaise idée.

Remédier à ces problèmes (et à d'autres) nécessite une connaissance approfondie du fonctionnement interne du compilateur et de RTL, et c'est donc une mauvaise idée de tenter d'utiliser des bibliothèques statiques avec Free Pascal.

Stratégie de recherche d'unités

Lorsque vous compilez une unité, le compilateur recherchera toujours par défaut les fichiers d'unité. Pour pouvoir différencier les unités compilées en tant que bibliothèques statiques ou dynamiques, il existe 2 commutateurs :

Commutateur Description
-XD Cela définira le symbole FPC_LINK_DYNAMIC
-XS Cela définira le symbole FPC_LINK_STATIC

La définition d'un symbole annulera automatiquement la définition de l'autre.

Ces deux commutateurs peuvent être utilisés conjointement avec le fichier de configuration fpc.cfg. L'existence de l'un de ces symboles peut être utilisée pour décider quel chemin de recherche d'unité définir. Par exemple, sous Linux :

# Définir les chemins des unités
#IFDEF FPC_LINK_STATIC
-Up/usr/lib/fpc/linuxunits/staticunits
#ENDIF
#IFDEF FPC_LINK_DYNAMIC
-Up/usr/lib/fpc/linuxunits/sharedunits
#ENDIF

Avec un tel fichier de configuration, le compilateur cherchera ses unités dans différents répertoires, selon que -XD ou -XS est utilisé.

Utiliser les liens intelligents

Vous pouvez compiler vos unités à l'aide de liens intelligents. Lorsque vous utilisez le SMARTLINK, le compilateur crée une série de blocs de code aussi petits que possible, c'est-à-dire qu'un bloc de code ne contiendra que le code d'une procédure ou d'une fonction.

Lorsque vous compilez un programme utilisant une unité liée de manière intelligente, le compilateur ne liera que le code dont vous avez réellement besoin et laissera de côté tout autre code. Cela se traduira par un binaire plus petit, qui sera chargé en mémoire plus rapidement, accélérant ainsi l'exécution.

Pour activer le SMARTLINK, on peut donner l'option SMARTLINK sur la ligne de commande : -CX, ou on peut mettre la directive {$SMARTLINK ON} dans le fichier d'unité :

  1. Unit Testunit;
  2.  
  3. {$SMARTLINK ON}
  4.  
  5. INTERFACE
  6.  { ... }
  7.  
  8. END.

Le SMARTLINK ralentira le processus de compilation, en particulier pour les grandes unités.

Lorsqu'une unité foo.pp est connectée, le nom du fichier de code est remplacé par libfoo.a.

Techniquement parlant, le compilateur crée de petits fichiers assembleur pour chaque procédure et fonction de l'unité, ainsi que pour toutes les variables globales définies (qu'elles soient ou non dans la section interface). Il assemble ensuite tous ces petits fichiers et utilise ar pour collecter les fichiers objets résultants dans une seule archive.

Le SMARTLINK et la création de bibliothèques partagées (ou dynamiques) s'excluent mutuellement, c'est-à-dire que si vous activez le SMARTLINK, la création de bibliothèques partagées est désactivée. La création de bibliothèques statiques est toujours possible. La raison en est que créer une bibliothèque dynamique intelligente n'a pas beaucoup de sens. L'ensemble de la bibliothèque partagée est de toute façon chargé en mémoire par l'éditeur de liens dynamique (ou le système d'exploitation), il n'y aurait donc aucun gain de taille en la rendant intelligente.



Dernière mise à jour : Mercredi, le 28 juin 2023