C / C++ avec GCC: ajouter statiquement des fichiers de ressources à l'exécutable / bibliothèque

Quelqu'un a-t-il une idée pour compiler statiquement n'importe quel fichier de ressources directement dans l'exécutable ou le fichier de bibliothèque partagé en utilisant GCC?

par exemple, j'aimerais ajouter des fichiers image qui ne changent jamais (et s'ils changent, je devrais quand même remplacer le fichier) et je ne voudrais 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é en binaire? L'exécutable analyser lui-même, de trouver le fichier et extraire les données?

peut-être QU'il y a une option pour GCC que je n'ai pas encore vu. Utiliser les moteurs de recherche n'a pas vraiment donné les bons résultats.

j'en aurais besoin pour travailler pour les bibliothèques partagées et les exécutables ELF normaux.

toute aide est appréciée

81
demandé sur Atmocreations 2011-02-01 19:02:49

8 réponses

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 un autre code, vous pouvez alors utiliser fmemopen pour obtenir un objet" régulier FILE * , ou std::stringstream pour 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 utilisez cela avec automake n'oubliez pas de définir BUILT_SOURCES de manière appropriée.

la bonne chose à faire de cette façon est:

  1. vous obtenez le texte, de sorte qu'il peut être dans le contrôle de version et les correctifs sensé
  2. il est portable et bien défini sur chaque plate-forme
44
répondu Flexo 2011-02-01 16:42:28

Update j'ai grandi pour préférer le contrôle John Ripley's assembly .incbin solution basée offre et maintenant utiliser une variante sur ce.

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

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

cela vous donne un fichier d'objet foo-data.o que vous pouvez lier à votre exécutable. L'interface C on dirait 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");

donc vous pouvez faire des choses 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);
assert(foo_copy);
memcpy(foo_copy, foo_data, foo_size);

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

80
répondu ndim 2017-11-13 19:21:03

vous pouvez intégrer des fichiers binaires dans l'exécutable en utilisant ld linker. Par exemple, si vous avez le fichier foo.bar alors vous pouvez l'intégrer dans l'exécutable en ajoutant les commandes suivantes à ld

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

si vous invoquez ld thru gcc alors vous devrez ajouter -Wl

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

ici --format=binary indique au linker que le fichier suivant est binaire et --format=default revient à par défaut le format d'entrée (ceci est utile si vous spécifiez d'autres fichiers d'entrée après foo.bar ).

alors vous pouvez accéder au contenu de votre fichier à partir du code:

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

il y a aussi le symbole nommé "_binary_foo_bar_size" . Je pense que c'est du type uintptr_t mais je ne l'ai pas vérifié.

46
répondu Simon 2012-07-24 01:03:15

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 formats d'image exécutables ne se soucient pas s'il y a des données supplémentaires derrière l'image et b) zip stocke la signature du fichier à la fin du fichier zip . Cela signifie que votre exécutable est un fichier zip régulier après cela (sauf pour votre exécutable initial, que zip peut gérer).), qui peut être ouvert et lu avec libzip.

38
répondu Nordic Mainframe 2012-12-19 11:44:11

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 à la ligne de commande avec gcc, et al et pas avec un outil de Rad sophistiqué qui fait que tout se passe comme par magie, il n'était pas évident pour moi immédiatement comment faire pour que cela se produise. Un peu de recherche sur le net trouvé un hack pour essentiellement de chat sur la fin de l'exécutable et ensuite déchiffrer où c'était basé sur un tas d'informations que je ne voulais pas savoir. On dirait qu'il devrait y avoir un meilleur moyen...

et il y a, c'est objcopie à la rescousse. objcopy convertit des fichiers d'objets ou des exécutables 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.txt que nous voulons intégrer dans notre exécutable:

# cat data.txt
Hello world

pour convertir ceci en un fichier objet que nous pouvons lier avec notre programme, nous utilisons objcopy pour produire un".o" fichier:

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

cela indique à objcopy que notre fichier d'entrée est dans le format" binaire", que notre fichier de sortie devrait être dans le " elf32-i386" format (fichiers objet sur le x86). L'option --binary-architecture indique à objcopy que le fichier de sortie est destiné à "s'exécuter" sur un x86. Cela est nécessaire pour que ld accepte le fichier pour la liaison avec d'autres fichiers pour 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 le linker:

# gcc main.c data.o

quand on exécute le résultat nous obtenons la sortie priée pour:

# ./a.out
Hello world

bien sûr, je n'ai pas raconté toute l'histoire, ni de vous montrer principal.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 le lien, ces symboles indiquent le début et la fin du fichier intégré. Les noms de symboles sont formés par la pré-édition binaire et en ajoutant _start ou _end au nom du fichier. Si le fichier nom contient tous les caractères qui seraient invalides dans un nom de symbole ils sont convertis en underscores (par exemple Données.txt devient data_txt). Si vous obtenez des noms non résolus en utilisant ces symboles, faites un hexdump-C sur le fichier objet et regardez à la fin du dump pour les noms que objcopy a choisis.

le code pour utiliser réellement le fichier incorporé doit 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 le les symboles ajoutés au fichier objet ne sont pas des "variables". Ils ne contiennent pas de données, leur adresse est leur valeur. Je les déclare comme type char parce que c'est pratique pour cet exemple: les données intégrées sont des données de caractères. 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 intégrées ne sont pas uniformes, alors char est probablement le plus pratique: prendre son adresse et lancer le pointeur pour le type approprié que vous parcourez les données.

34
répondu Hazok 2013-01-29 08:40:52

si vous voulez contrôler le nom exact du symbole et l'emplacement des ressources, vous pouvez utiliser (ou script) le GNU assembleur (qui ne fait pas vraiment partie de gcc) pour importer des fichiers binaires entiers. Essayez ceci:

assemblage (x86 / arm):

    .section .rodata
    .global thing
    .type   thing, @object
    .align  4
thing:
    .incbin "meh.bin"
thing_end:
    .global thing_size
    .type   thing_size, @object
    .align  4
thing_size:
    .int    thing_end - thing

C:

#include <stdio.h>
extern char thing[];
extern unsigned thing_size;
int main() {
  printf("%p %u\n", thing, thing_size);
  return 0;
}

peu importe ce que vous utilisez, il est probablement préférable de faire un script pour générer toutes les ressources, et avoir des noms de symboles nice/uniforme pour tout.

32
répondu John Ripley 2016-02-14 18:47:50

puis - je ajouter que la méthode de John Ripley est probablement la meilleure ici pour une raison énorme-alignement. Si vous faites une objcopie standard ou " LD-r-B binaire-o foo.o foo.txt" et puis regardez l'objet résultant avec objdump -x, il ressemble à l'alignement du bloc est définie sur 0. Si vous voulez que l'alignement soit correct pour les données binaires autres que char, Je ne peux pas imaginer que ce soit une bonne chose.

6
répondu carveone 2012-01-11 16:38:14

en lisant tout le post ici et dans Internet j'ai fait une conclusion qu'il n'y a pas d'outil pour les ressources, qui est:

1) Facile à utiliser dans le code.

2) Automatisé (pour être facilement inclus dans cmake/faire).

3) plate-forme transversale.

j'ai décidé d'écrire l'outil par moi-même. Le code est disponible ici. https://github.com/orex/cpp_rsc

pour l'utiliser avec cmake c'est très facile.

vous devez ajouter à vos listes de Cmakel.txt fichier tel code.

file(DOWNLOAD https://raw.github.com/orex/cpp_rsc/master/cmake/modules/cpp_resource.cmake ${CMAKE_BINARY_DIR}/cmake/modules/cpp_resource.cmake) 

set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake/modules)

include(cpp_resource)

find_resource_compiler()
add_resource(pt_rsc) #Add target pt_rsc
link_resource_file(pt_rsc FILE <file_name1> VARIABLE <variable_name1> [TEXT]) #Adds resource files
link_resource_file(pt_rsc FILE <file_name2> VARIABLE <variable_name2> [TEXT])

...

#Get file to link and "resource.h" folder
#Unfortunately it is not possible with CMake add custom target in add_executable files list.
get_property(RSC_CPP_FILE TARGET pt_rsc PROPERTY _AR_SRC_FILE)
get_property(RSC_H_DIR TARGET pt_rsc PROPERTY _AR_H_DIR)

add_executable(<your_executable> <your_source_files> ${RSC_CPP_FILE})

le vrai exemple, en utilisant l'approche peut être téléchargé ici, https://bitbucket.org/orex/periodic_table

4
répondu user2794512 2015-08-03 11:14:56