2010-11-11 18 views
40

Estoy buscando una forma de insertar fácilmente cualquier información binaria externa en una aplicación C/C++ compilada por GCC.Incrustar recursos en ejecutable usando GCC

Un buen ejemplo de lo que me gustaría hacer es manejar el código del sombreador; puedo guardarlo en archivos fuente como const char* shader = "source here";, pero eso es extremadamente poco práctico.

Me gustaría que el compilador lo hiciera por mí: en la compilación (fase de vinculación), lea el archivo "foo.bar" y vincule su contenido a mi programa, para que yo pueda acceder a los contenidos como datos binarios del código.

Podría ser útil para pequeñas aplicaciones que me gustaría distribuir como un solo archivo .exe.

¿GCC es compatible con algo como esto?

Respuesta

40

Hay un par de posibilidades: la capacidad


Actualización: Aquí hay un ejemplo más completo de cómo utilizar los datos ligados en el ejecutable usando 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; 
} 

Actualización 2 - Obtener el tamaño de recursos: No podía leer el _binary_foo_bar_size correctamente. En tiempo de ejecución, gdb me muestra el tamaño correcto del recurso de texto usando display (unsigned int)&_binary_foo_bar_size. Pero asignar esto a una variable siempre daba un valor incorrecto. Yo podría resolver este problema de la siguiente manera:

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

Es una solución, pero funciona bien y no es demasiado feo.

+0

Los sombreadores no son BLOB. Ellos son texto normal. –

+2

@VJo: luego trata el blob como texto. Es posible que deba hacer un poco de trabajo para asegurarse de que haya un ''\ 0'' al final del texto si lo necesita terminado así. Algunos experimentos pueden estar en orden. –

+0

Gracias, Michael; se parece a lo que necesitaba, pero recibo el error 'objdump: foo.o: No se reconoce el formato de archivo', y otro similar al intentar vincular ese objeto con mi fuente. ¿Algún consejo? Estoy en Windows, usando tdm-mingw 4.5.1 y mi ld -v produce 'GNU ld (GNU Binutils) 2.20.51.20100319'. Puedo recurrir a su segunda sugerencia, por lo que es solo mi curiosidad a partir de ahora. :) – Kos

1

Usted puede hacer esto en un archivo de cabecera:

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

//source 

"; 
#endif 

y justo que incluyen.

Otra forma es leer el archivo de sombreado.

+1

Creo que Kos quiere ser capaz de mantener la fuente de sombreado sin tener que preocuparse de escapar caracteres especiales (entre otros posibles problemas). –

+0

@Michael Obviamente nunca usaste ni un solo sombreador. –

+0

@VJo: no - nunca usaste un sombreador. Me estaba acercando a la pregunta como incorporar datos arbitrarios que residen en archivos externos en el programa. Definitivamente puedo aceptar que esta podría ser una solución mucho mejor para los sombreadores en particular. –

16

Además de las sugerencias ya se mencionó, en Linux, puede utilizar la xxd herramienta de volcado hexadecimal, que tiene una función para generar un archivo de cabecera C:

xxd -i mybinary > myheader.h 
+2

Creo que esta solución es la mejor. También es compatible con multiplataforma y compilador cruzado. –

+0

Esto es cierto, pero tiene un inconveniente: los archivos de encabezado resultantes son ** mucho ** más grandes que el archivo binario original. Esto no tiene ningún impacto en el resultado final compilado, pero puede no ser deseable como parte del proceso de compilación. – Riot

+0

este problema se puede resolver usando ** encabezado precompilado **. –

3
No

exactamente una nueva forma, pero sin duda muy conveniente. Me encontré con esta biblioteca con licencia totalmente gratuita basada en el método de ensamblador incbin no mencionado entre las respuestas aquí.

https://github.com/graphitemaster/incbin

Para recapitular. El método incbin es así. Tienes un archivo de ensamblaje de cosa que compilas con 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 

En su código C o CPP se puede hacer referencia a ella con:

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

Así entonces vincular el .o resultante con el resto de las unidades de compilación. Crédito donde se debe a @John Ripley con su respuesta aquí: C/C++ with GCC: Statically add resource files to executable/library

Pero lo anterior no es tan conveniente como lo que incbin puede proporcionarle. Para lograr lo anterior con incbin no necesita escribir ningún ensamblador. Solo lo siguiente va a hacer:

#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); 
} 
Cuestiones relacionadas