web-dev-qa-db-fra.com

Incorporation de ressources dans un exécutable à l'aide de GCC

Je cherche un moyen d'incorporer facilement toutes les données binaires externes dans une application C/C++ compilée par GCC.

Un bon exemple de ce que j'aimerais faire est de gérer le code du shader - je peux simplement le conserver dans des fichiers source comme const char* shader = "source here"; mais c'est extrêmement peu pratique.

Je voudrais que le compilateur le fasse pour moi: lors de la compilation (étape de liaison), lire le fichier "foo.bar" et lier son contenu à mon programme, afin que je puisse accéder au contenu en tant que données binaires à partir du code.

Peut être utile pour les petites applications que j'aimerais distribuer sous forme de fichier .exe unique.

Est-ce que GCC prend en charge quelque chose comme ça?

51
Kos

Il y a plusieurs possibilités:


Mise à jour: Voici un exemple plus complet de la façon d'utiliser les données liées à l'exécutable en utilisant ld -r -b binary:

#include <stdio.h>

// a file named foo.bar with some example text is 'imported' into 
// an object file using the following command:
//
//      ld -r -b binary -o foo.bar.o foo.bar
//
// That creates an bject file named "foo.bar.o" with the following 
// symbols:
//
//      _binary_foo_bar_start
//      _binary_foo_bar_end
//      _binary_foo_bar_size
//
// Note that the symbols are addresses (so for example, to get the 
// size value, you have to get the address of the _binary_foo_bar_size
// symbol).
//
// In my example, foo.bar is a simple text file, and this program will
// dump the contents of that file which has been linked in by specifying
// foo.bar.o as an object file input to the linker when the progrma is built

extern char _binary_foo_bar_start[];
extern char _binary_foo_bar_end[];

int main(void)
{
    printf( "address of start: %p\n", &_binary_foo_bar_start);
    printf( "address of end: %p\n", &_binary_foo_bar_end);

    for (char* p = _binary_foo_bar_start; p != _binary_foo_bar_end; ++p) {
        putchar( *p);
    }

    return 0;
}

Mise à jour 2 - Obtenir la taille de la ressource: je n'ai pas pu lire correctement la taille _binary_foo_bar_size. Au moment de l'exécution, gdb me montre la bonne taille de la ressource texte en utilisant display (unsigned int)&_binary_foo_bar_size. Mais l'assigner à une variable a toujours donné une mauvaise valeur. Je pourrais résoudre ce problème de la manière suivante:

unsigned int iSize =  (unsigned int)(&_binary_foo_bar_end - &_binary_foo_bar_start)

C'est une solution de contournement, mais cela fonctionne bien et n'est pas trop laid.

43
Michael Burr

En plus des suggestions déjà mentionnées, sous linux, vous pouvez utiliser l'outil de vidage hexadécimal xxd, qui a une fonctionnalité pour générer un fichier d'en-tête C:

xxd -i mybinary > myheader.h
21
Riot

Le .incbin Directive GAS peut être utilisée pour cette tâche. Voici une bibliothèque sous licence totalement gratuite qui s'enroule autour d'elle:

https://github.com/graphitemaster/incbin

Récapituler. La méthode incbin est comme ça. Vous avez un fichier Assembly thing.s que vous compilez avec gcc -c thing.s

      .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

Dans votre code c ou cpp, vous pouvez le référencer avec:

extern const char thing[];
extern const char* thing_end;
extern int thing_size;

Vous liez donc le .o résultant au reste des unités de compilation. Le crédit est dû à @John Ripley avec sa réponse ici: C/C++ avec GCC: ajouter statiquement des fichiers de ressources à l'exécutable/bibliothèque

Mais ce qui précède n'est pas aussi pratique que ce qu'incbin peut vous donner. Pour accomplir ce qui précède avec incbin, vous n'avez pas besoin d'écrire d'assembleur. Juste ce qui suit fera l'affaire:

#include "incbin.h"

INCBIN(thing, "meh.bin");

int main(int argc, char* argv[])
{
    // Now use thing
    printf("thing=%p\n", gThingData);
    printf("thing len=%d\n", gThingSize);   
}
7
Matt

Vous pouvez le faire dans un fichier d'en-tête:

#ifndef SHADER_SRC_HPP
#define SHADER_SRC_HPP
const char* shader= "

//source

";
#endif

et il suffit de l'inclure.

Une autre façon est de lire le fichier shader.

0
BЈовић