111 votes

C / C ++ avec GCC: Ajout statique de fichiers de ressources à un exécutable / une bibliothèque

Quelqu'un a une idée de comment faire pour compiler en statique toute ressource fichier directement dans l'exécutable ou la bibliothèque partagée fichier à l'aide de GCC?

Par exemple j'aimerais ajouter des fichiers d'images qui ne changent jamais (et si ils le font, je dois remplacer le fichier de toute façon) et ne voulez pas qu'ils traînent dans le système de fichiers.

Si c'est possible (et je pense que c'est parce que Visual C++ pour Windows peut le faire aussi), comment puis-je charger les fichiers qui sont stockés dans le propre binaire? L'exécutable analyser lui-même, de trouver le fichier et extraire les données?

Peut-être il ya une option pour GCC, je n'ai pas encore vu. En utilisant les moteurs de recherche n'a pas vraiment cracher le droit des trucs.

J'aurais besoin de ce travail pour les bibliothèques partagées et normal ELF-exécutables.

Toute aide est appréciée

91voto

ndim Points 11557

J'ai utilisé objcopy (GNU binutils) pour relier les données binaires à partir d'un fichier foo-données.bin dans la section de données de l'exécutable:

objcopy -B i386 -I binary -O elf32-i386 foo-data.bin foo-data.o

Cela vous donne un foo-data.o fichier d'objet que vous pouvez lien dans votre exécutable. Le C de l'interface ressemble à quelque chose comme

/** created from binary via objcopy */
extern uint8_t foo_data[]      asm("_binary_foo_data_bin_start");
extern uint8_t foo_data_size[] asm("_binary_foo_data_bin_size");
extern uint8_t foo_data_end[]  asm("_binary_foo_data_bin_end");

ainsi, vous pouvez faire des trucs comme

for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) {
    transmit_single_byte(*byte);
}

ou

size_t foo_size = (size_t)((void *)foo_data_size);
void *foo_copy = malloc(foo_size);
memcpy(foo_copy, foo_data, foo_size);

Si votre architecture cible a des contraintes particulières quant à l'endroit où constant et variable les données sont stockées, ou vous voulez stocker des données dans l' .text segment pour le faire rentrer dans le même type de mémoire que le code de votre programme, vous pouvez jouer avec l' objcopy des paramètres un peu plus.

63voto

Flexo Points 39273

Avec imagemagick:

convert file.png data.h

Donne quelque chose comme:

/*
  data.h (PNM).
*/
static unsigned char
  MagickImage[] =
  {
    0x50, 0x36, 0x0A, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 
    0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4D, 0x50, 0x0A, 0x32, 0x37, 
    0x37, 0x20, 0x31, 0x36, 0x32, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 

....

Pour la compatibilité avec d'autres codes que vous pouvez ensuite utiliser fmemopen pour obtenir un "régulier" FILE * de l'objet, ou, alternativement, std::stringstream faire un iostream. std::stringstream n'est pas idéal pour ce bien et vous pouvez bien sûr utiliser un pointeur n'importe où, vous pouvez utiliser un itérateur.

Si vous êtes en utilisant ce avec automake n'oubliez pas de définir BUILT_SOURCES de façon appropriée.

La bonne chose à propos de cette façon de faire est:

  1. Vous obtenez un texte, de sorte qu'il peut être dans le contrôle de version et des correctifs sensiblement
  2. Il est portable et bien défini sur chaque plate-forme

55voto

Simon Points 431

Vous pouvez incorporer des fichiers binaires dans exécutable à l'aide de ldde l'éditeur de liens. Par exemple, si vous avez fichier foo.bar , alors vous pouvez l'intégrer dans exécutable ajouter les commandes suivantes pour ld

--format=binary foo.bar --format=default

Si vous appelez ld thru gcc , alors vous aurez besoin d'ajouter -Wl

-Wl,--format=binary -Wl,foo.bar -Wl,--format=default

Ici, --format=binary indique à l'éditeur de liens que le fichier suivant est binaire et --format=default passe par défaut du format d'entrée (ceci est utile si vous spécifiez d'autres fichiers d'entrée après l' foo.bar).

Ensuite, vous pouvez accéder au contenu de votre fichier de code:

extern uint8_t data[]     asm("_binary_foo_bar_start");
extern uint8_t data_end[] asm("_binary_foo_bar_end");

Il est aussi symbole nommé "_binary_foo_bar_size". Je pense qu'il est de type uintptr_t mais je n'ai pas vérifier.

42voto

Nordic Mainframe Points 13717

Vous pouvez mettre toutes vos ressources dans un fichier ZIP et ajouter à la fin du fichier exécutable:

g++ foo.c -o foo0
zip -r resources.zip resources/
cat foo0 resources.zip >foo

Cela fonctionne, parce que a) la Plupart des exécutables formats d'image ne se soucient pas si il y a des données supplémentaires derrière l'image et b) zip stocke le fichier de signature à la fin du fichier zip. Cela signifie, votre exécutable est régulièrement fichier zip après cela, (sauf pour votre avance exécutable, qui zip poignée), qui peut être ouvert et lu avec libzip.

38voto

Hazok Points 825

À partir de http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:

J'ai récemment eu le besoin d'incorporer un fichier dans un fichier exécutable. Depuis que je suis en train de travailler sur la ligne de commande avec gcc, et al et non pas avec une fantaisie RAD outil qu'il fait, tout se fait comme par magie, il n'était pas immédiatement évident pour moi comment faire ça. Un peu de recherche sur le net trouvé un hack pour essentiellement de chat sur la fin de l'exécutable, puis déchiffrer où il était basé sur un tas d'information, je ne veux pas le savoir. Semble comme il devrait y avoir une meilleure façon...

Et bien, ce n'est objcopy à la rescousse. objcopy convertit les fichiers objets ou des fichiers exécutables à partir d'un format à un autre. L'un des formats qu'il comprenne, c'est "binaire", qui est essentiellement un fichier qui n'est pas dans l'un des autres formats qu'il comprend. Donc, vous avez probablement imaginé l'idée: convertir le fichier que nous voulons intégrer dans un fichier objet, alors il peut être tout simplement liée avec le reste de notre code.

Disons que nous avons un nom de fichier data.txt que nous souhaitons intégrer dans notre exécutable:

# cat data.txt
Hello world

Afin de le convertir en un fichier objet que nous pouvons faire un lien avec notre programme, nous utilisons tout simplement objcopy à produire un ".o" fichier:

# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o

Cela dit objcopy que notre fichier d'entrée est dans le "binaire", format, que notre fichier de sortie doit être dans le "elf32-i386" (format de fichiers objets sur les architectures x86). L' --binary-architecture option indique objcopy que le fichier de sortie est destinée à "exécuter" sur un système x86. Ceci est nécessaire afin que ld va accepter le fichier pour faire le lien avec d'autres fichiers pour le x86. On pourrait penser que le fait de spécifier le format de sortie comme "elf32-i386" impliquerait cela, mais il ne le fait pas.

Maintenant que nous avons un fichier de l'objet, nous avons seulement besoin de l'inclure lors de l'éditeur de liens:

# gcc main.c data.o

Quand nous courons le résultat nous obtenons le pria de sortie:

# ./a.out
Hello world

Bien sûr, je n'ai pas raconté toute l'histoire, ni de vous montrer principal.c. Lorsque objcopy n'est au-dessus de la conversion, il ajoute quelques "linker" les symboles de la conversion d'un fichier de l'objet:

_binary_data_txt_start
_binary_data_txt_end

Après la liaison, ces symboles pour indiquer le début et la fin du fichier incorporé. Le symbole noms sont formés en ajoutant le binaire et en ajoutant _start ou _end pour le nom de fichier. Si le nom de fichier contient des caractères qui ne sont pas valables dans un nom de symbole, ils sont convertis à des traits de soulignement (par exemple data.txt devient data_txt). Si vous obtenez les noms non résolus lors de la liaison à l'aide de ces symboles, faire un hexdump-C sur le fichier de l'objet et de regarder à la fin de la décharge pour les noms que objcopy choisi.

Le code à utiliser effectivement le fichier incorporé doit être maintenant assez é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++);
}

Un important et subtil chose à noter est que les symboles ajoutés au fichier de l'objet ne sont pas des "variables". Ils ne contiennent pas toutes les données, plutôt, leur adresse est leur valeur. Je leur déclare que le type char parce que c'est pratique pour cet exemple: les données incorporées, c'est le caractère de données. Cependant, vous pouvez les déclarer comme n'importe quoi, comme int si les données est un tableau d'entiers, ou comme struct foo_bar_t si les données étaient tout tableau de foo bars. Si les données incorporées n'est pas uniforme, le char est probablement le plus pratique: prendre son adresse et lancer le pointeur vers le type que vous traversez les données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X