De http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :
J'ai récemment eu le besoin d'intégrer un fichier dans un exécutable. Puisque je travaille en ligne de commande avec gcc, et al et non avec un outil RAD sophistiqué qui fait que tout se passe comme par magie, il ne m'a pas été immédiatement évident de savoir comment y arriver. Un peu de recherche sur le net a trouvé un hack pour le mettre essentiellement à la fin de l'exécutable, puis déchiffrer où il était basé sur un tas d'informations que je ne voulais pas connaître. Il semblait qu'il devrait y avoir un meilleur moyen ...
Et il y a, c'est objcopy à la rescousse. objcopy convertit les fichiers objets ou exécutables d'un format à un autre. L'un des formats qu'il comprend est "binaire", qui est essentiellement tout fichier qui n'est pas dans l'un des autres formats qu'il comprend. Vous avez donc probablement envisagé l'idée: convertir le fichier que nous voulons incorporer en un fichier objet, puis il peut simplement être lié au reste de notre code.
Disons que nous avons un nom de fichier data.txt que nous voulons intégrer dans notre exécutable:
# cat data.txt
Hello world
Pour le convertir en un fichier objet que nous pouvons lier avec notre programme, nous utilisons simplement objcopy pour produire un fichier ".o":
# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o
Cela indique à objcopy que notre fichier d'entrée est au format "binaire", que notre fichier de sortie doit être au format "elf32-i386" (fichiers objets sur le x86). L'option --binary-architecture indique à objcopy que le fichier de sortie est destiné à "s'exécuter" sur un x86. Ceci est nécessaire pour que ld accepte le fichier pour la liaison avec d'autres fichiers pour le x86. On pourrait penser que spécifier le format de sortie comme "elf32-i386" impliquerait cela, mais ce n'est pas le cas.
Maintenant que nous avons un fichier objet, nous n'avons besoin de l'inclure que lorsque nous exécutons l'éditeur de liens:
# gcc main.c data.o
Lorsque nous exécutons le résultat, nous obtenons la sortie priée:
# ./a.out
Hello world
Bien sûr, je n'ai pas encore raconté toute l'histoire, ni montré main.c. Lorsque objcopy effectue la conversion ci-dessus, il ajoute des symboles "linker" au fichier objet converti:
_binary_data_txt_start
_binary_data_txt_end
Après la liaison, ces symboles spécifient le début et la fin du fichier incorporé. Les noms de symboles sont formés en ajoutant binaire au début et en ajoutant _start ou _end au nom de fichier. Si le nom de fichier contient des caractères qui ne seraient pas valides dans un nom de symbole, ils sont convertis en traits de soulignement (par exemple, data.txt devient data_txt). Si vous obtenez des noms non résolus lors de la liaison à l'aide de ces symboles, effectuez un hexdump -C sur le fichier objet et recherchez à la fin du vidage les noms choisis par objcopy.
Le code pour utiliser réellement le fichier incorporé devrait maintenant être raisonnablement évident:
#include <stdio.h>
extern char _binary_data_txt_start;
extern char _binary_data_txt_end;
main()
{
char* p = &_binary_data_txt_start;
while ( p != &_binary_data_txt_end ) putchar(*p++);
}
Une chose importante et subtile à noter est que les symboles ajoutés au fichier objet ne sont pas des «variables». Ils ne contiennent aucune donnée, mais leur adresse est leur valeur. Je les déclare en tant que type char car c'est pratique pour cet exemple: les données incorporées sont des données de caractères. Cependant, vous pouvez les déclarer comme n'importe quoi, comme int si les données sont un tableau d'entiers, ou comme struct foo_bar_t si les données étaient n'importe quel tableau de foo bars. Si les données incorporées ne sont pas uniformes, alors char est probablement le plus pratique: prenez son adresse et transtypez le pointeur vers le type approprié lorsque vous parcourez les données.