Je vais donner un exemple tout fait que je commenterais petit à petit. Il s’agit d’un programme qui affichera « Hello World ! (32)» (Comme c’est original…), mais avec quelques subtilités.
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 | #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libtcc.h>
char my_program[] =
"int fonction(int n) "
"{"
" printf(\"Hello World! (%d)\\n\",n);"
" return 0; "
"}";
int main(int argc, char **argv)
{
TCCState *s;
int (*entry)(int);
void *mem;
int size;
s = tcc_new();
if (!s) {
fprintf(stderr, "Impossible de creer un contexte TCC\n");
exit(1);
}
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
if (tcc_compile_string(s, my_program) != 0) {
printf("Erreur de compilation !\n");
return 1;
}
size = tcc_relocate(s, NULL);
if (size == -1)
return 1;
mem = malloc(size);
tcc_relocate(s, mem);
entry = tcc_get_symbol(s, "fonction");
tcc_delete(s);
entry(32);
free(mem);
return 0;
}
|
Commentaires:
Code : C | #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libtcc.h>
|
Très classique, à ceci près que l'on inclut
libtcc.h
Code : C | char my_program[] =
"int fonction(int n)"
"{"
" printf(\"Hello World! (%d)\\n\",n);"
" return 0;"
"}";
|
Dans ce tableau de char est contenue une fonction qui sera compilée et exécutée. Bien sûr, vous n'êtes pas obligé de faire ainsi, vous pouvez récupérer directement l'entrée utilisateur, ou un champ texte de votre application graphique. L'important est de l'avoir disponible sous la forme d'un tableau de char.
Notez cependant ici que j’ai dû échapper (placer un antislash devant) les double quote (\" ) de façon à éviter de fermer malencontreusement la chaine de caractère. De la même manière, j'ai échappé le \n en \\n pour qu'il ne soit pas interprété en un retour à la ligne lors de la compilation.
Code : C | TCCState *s;
/// … ///
s = tcc_new();
if (!s)
{
fprintf(stderr, "Impossible de creer un context TCC\n");
exit(1);
}
|
Création d'un contexte TCC, ce qui se fait par création d'un pointeur, puis acquisition du contexte via un appel à
tcc_new Ce contexte identifie notre compilation. Ainsi, en faisant plusieurs contextes, on peut compiler plusieurs choses en même temps. Sachez aussi que quasiment toutes les fonctions de libtcc prennent pour premier argument un contexte TCC.
Un contexte TCC est une sorte de variable, dont on ne connait pas le contenu et qui stocke toutes les informations nécessaires à une compilation avec libtcc.
Code : C | tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
Cette ligne est importante. Elle indique à libtcc sous quelle forme nous allons récupérer la sortie de la compilation. Dans notre cas, nous allons l'exécuter, il est donc nécessaire de stocker le code exécutable en mémoire (TCC_OUTPUT_MEMORY). Remarquez que l'on passe comme premier argument le contexte TCC créé précédemment.
Code : C | if (tcc_compile_string(s, my_program) != 0)
{
printf("Erreur de compilation !\n");
return 1;
}
|
On compile le programme avec l'instruction
tcc_compile_string(s, my_program)
, puis, si la fonction renvoie autre chose que 0, c'est qu'il y a eu une erreur de compilation. Dommage

. Les arguments sont : le contexte TCC et le tableau de char contenant le programme.
Code : C | size = tcc_relocate(s, NULL);
if (size == -1)
return 1;
|
Cette instruction est plus compliquée. tcc_relocate sert à copier notre résultat de compilation depuis le contexte (virtuellement opaque à nos yeux) vers un endroit dans la mémoire que l’on contrôle ; malheureusement, nous ne savons pas combien ce code exécutable prend comme place, nous allons donc le copier une fois à vide (on l'envoie vers NULL, qui n’est pas valable), mais on obtient en retour le nombre d'octet copié, c'est-à-dire la taille du code compilé.
Code : C | mem = malloc(size);
tcc_relocate(s, mem);
|
Cette étape est très logiquement l'allocation de la mémoire disponible (déterminée à l'étape précédente et via
malloc), et la copie réelle du code exécutable vers cette adresse (via
tcc_relocate).
Code : C | entry = tcc_get_symbol(s, "fonction");
|
Comme vous avez pu le constater, dans notre code compilé n'était pas présent de fonction main. Et pour cause, elle n'est pas nécessaire ! Nous allons obtenir l'adresse de notre fonction via
tcc_get_symbol. Comme vous l'avez deviné, il prend comme argument un contexte TCC et un nom de symbole (un symbole est une variable, une fonction,...). Dans notre cas, nous voulons l'adresse de la fonction
fonction. Notez tout de même que nous aurions pu appeler cette fonction comme nous le voulions, par exemple
leGateauEstUnMensoge,
trucbidule, ou même
main (ce n'est pas interdit).
Qu'est-ce que entry ? Ce truc mystérieux que vous avez vu en début de main() est un pointeur sur fonction, en l'occurrence un pointeur sur une fonction qui prend en argument un int et renvois un int. Pour vous faire mieux comprendre: voici un pointeur sur une fonction qui prend en argument 2 int et un char, et renvois un int: int (*pointeur)(int, int, char);
. Les arguments des pointeurs doivent être les mêmes que la fonction ciblée ! Dans notre exemple, ils le sont, nous pouvons donc l’utiliser
Code : C
Ceci est la suppression du contexte TCC. Cela libère de la mémoire.
Code : C
Et le moment de vérité : exécution de la fonction ! Votre programma va afficher "Hello World ! (32)". Si vous aviez appelé la fonction avec 23 comme argument (
entry(23) ;
), elle aurait affiché "Hello world ! (23)". Les arguments passés au pointeur sont passés à la fonction.
Code : C
Libération de la mémoire et fin du programme.
Exécution du programme
Lorsque vous exécuterez ce programme, vous obtiendrez la sortie suivante :
Code : Console
Décortiquons le comportement du programme, en entrant dans les détails :
- Lancement du programme
- Acquisition d'un contexte TCC (les opérations réalisées ici nous sont inconnues)
- Compilation du programme contenu dans le tableau my_program
- Récupération de l'adresse de la fonction fonction(int n) dans le pointeur entry
- Exécution de cette fonction via le pointeur de fonction entry