La méthode "industrielle" permet d'automatiser la mise à jour de la table des symboles, mais nécessite tout de même quelques interventions humaines (comprendre que l'automatisation complète par script est faisable, mais risquée).
Par rapport au code source de Prog0
, il faut bien sûr que la fonction partagée soit déclarée (comme toutes les fonctions en C) mais il faut surtout qu'elle le soit en extern
. En effet, cela indique au compilateur qu'elle se situe dans un autre fichier objet, et que par conséquent il laisse ce symbole indéfini tant qu'on n'invoque pas l'éditeur de liens. Il faut donc empêcher EDK d'atteindre cette dernière phase de compilation. Pour cela, il suffit d'ajouter l'option -c
dans la boite de dialogue "Set Compiler Settings", à l'onglet "Advanced" dans le champ "Program Sources Compiler Options". Il faut donc faire la dernière phase de compilation manuellement avec l'éditeur de liens.
Le code source du programme Prog0
se présente alors de la manière suivante :
extern int add(int a, int b); int main(void) { int c = 0; c = add(13, 2); while(1); return 0; }
Ensuite, il faut mettre à jour le fichier ELF de Prog0
, pour cela j'utilise l'éditeur de liens ld. Il existe l'option --just-symbols
qui permet de mettre à jour la table des symboles d'un fichier ELF à partir d'un autre, sans y intégrer les fonctions. Malheureusement cette option en fait trop dans le sens où elle met aussi à jour les symboles déjà définis, c'est à dire main
, ainsi que toutes les fonctions portant le même nom dans les deux programmes...
La solution consiste à créer un fichier ELF ne comportant que les symboles de Prog1
dont on a besoin pour mettre à jour Prog0
, puis de l'utiliser pour mettre à jour ce dernier. La création d'un tel fichier ELF passe dans un premier temps par la création d'un fichier comportant le nom des symboles à conserver, j'utilise alors la commande readelf pour écrire un tel fichier :
bash# powerpc-eabi-readelf -s Prog1/executable.elf >fichier_symboles
ce qui donne un fichier de la forme suivante :
Symbol table '.symtab' contains 67 entries: Num: Value Size Type Bind Vis Ndx Name (...) 39: 01001500 0 OBJECT GLOBAL DEFAULT 9 _heap_start 40: 010004f8 0 OBJECT GLOBAL DEFAULT 9 ___bss_start (...) 42: 01000000 56 FUNC GLOBAL DEFAULT 1 add (...) 46: 01000080 132 NOTYPE GLOBAL DEFAULT 1 _start (...) 53: 01000038 72 FUNC GLOBAL DEFAULT 1 main (...) 57: 01001500 0 OBJECT GLOBAL DEFAULT 9 __stack (...)
Il ne reste plus qu'à garder les noms des symboles qui sont intéressants (un par ligne). Dans cette démonstration, le fichier ne fera au final plus que deux lignes :
add exit
Il faut garder le symbole exit
car ce dernier ne sera pas présent dans le fichier ELF de Prog0
. En effet, étant donné que l'on a recompilé avec l'option -c
, ce symbole n'a pas été inséré. Ce n'est absolument pas gênant d'utiliser la routine exit
de l'autre processus, puisqu'il s'agit seulement d'une boucle sans fin.
A présent, je peux créer le fameux fichier ELF ne comportant que les symboles de Prog1
dont on a besoin pour mettre à jour Prog0
. Cela s'effectue grâce à la commande suivante :
bash# powerpc-eabi-ld --just-symbols=Prog1/executable.elf \ --retain-symbols-file=fichier_symboles \ -L ./ppc405_0/lib -o temp.elf
Maintenant, il ne reste plus qu'à mettre à jour Prog0
avec la table des symboles de temp.elf. Il faut tout de même faire attention à ne pas oublier de définir le symbole _START_ADDR
qui est utile pour le CStart (fichier crt0.S
) :
bash# powerpc-eabi-ld --just-symbols=temp.elf -L ./ppc405_0/lib \ --defsym _START_ADDR=0x00000000 Prog0/executable.elf \ ppc405_0/lib/libxil.a -o Prog0/exec.elf
On peut maintenant copier le fichier ELF final à la place du fichier ELF d'origine. Si on vérifie la table des symboles, on constate qu'elle est correctement mise à jour :
bash# cp Prog0/exec.elf Prog0/executable.elf bash# powerpc-eabi-readelf -s Prog0/executable.elf |grep 03 36: 01000000 56 FUNC GLOBAL DEFAULT ABS add 55: 0100011c 168 FUNC GLOBAL DEFAULT ABS exit
Il ne reste plus qu'à charger les deux programmes en mémoire. On peut alors voir avec GBD que c=15
après l'appel à la fonction add
. Comme précédemment, on ne peut pas déboguer facilement cette fonction car le débogueur ne connaît pas son code source.
Il y a deux inconvénients majeurs à cette méthode:
- il faut entrer un grand nombre de commandes, ce qui la rend plutôt difficile à utiliser pour de petits projets. En revanche la mise en oeuvre est plus adaptée pour de grands projets.
- lors du débogage du processus
Prog0
, le débogueur de Xilinx ne sait pas que les sources de la fonction est disponible dans l'autre projet, ce qui veut dire que l'on ne peut pas déboguer facilement les fonctions partagées par l'autre projet.