Incrustar recursos en ejecutable usando GCC
Estoy buscando una manera de incrustar fácilmente cualquier dato binario externo en una aplicación C/C++ compilada por GCC.
Un buen ejemplo de lo que me gustaría hacer es manejar el código de sombreado; puedo mantenerlo en archivos fuente, const char* shader = "source here";
pero eso es extremadamente poco práctico.
Me gustaría que el compilador lo hiciera por mí: al compilar (etapa de vinculación), lea el archivo "foo.bar" y vincule su contenido a mi programa, para poder acceder al contenido como datos binarios desde el código.
Podría resultar útil para aplicaciones pequeñas que me gustaría distribuir como un único archivo .exe.
¿GCC apoya algo como esto?
Hay un par de posibilidades:
use la capacidad de ld para convertir cualquier archivo en un objeto ( incrustando blobs binarios usando gcc mingw ):
ld -r -b binary -o binary.o foo.bar # then link in binary.o
use una utilidad
bin2c
/bin2h
para convertir cualquier archivo en una matriz de bytes ( incrustar imagen en el código, sin usar la sección de recursos o imágenes externas )
Actualización: aquí hay un ejemplo más completo de cómo usar datos vinculados al 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: obtención del tamaño del recurso: no pude 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. 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 alternativa, pero funciona bien y no es demasiado fea.
Además de las sugerencias ya mencionadas, en Linux puedes usar la herramienta de volcado hexadecimal xxd, que tiene una función para generar un archivo de encabezado C:
xxd -i mybinary > myheader.h
Para esta tarea se puede utilizar la .incbin
directiva GAS . Aquí hay una biblioteca con licencia totalmente gratuita que lo incluye:
https://github.com/graphitemaster/incbin
Recordar. El método incbin es así. Tienes un archivo ensamblador de thing.s 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 puede hacer referencia a él con:
extern const char thing[];
extern const char* thing_end;
extern int thing_size;
Luego vinculas el .o resultante con el resto de las unidades de compilación. El crédito correspondiente es para @John Ripley con su respuesta aquí: C/C++ con GCC: agregue estáticamente archivos de recursos al ejecutable/biblioteca
Pero el método anterior no es tan conveniente como el que puede ofrecerle incbin. Para lograr lo anterior con incbin no es necesario escribir ningún ensamblador. Sólo lo siguiente servirá:
#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);
}
Para C23, ahora existe la directiva de preprocesador #embed
, que logra exactamente lo que busca sin utilizar herramientas externas. Consulte 6.10.3.1 del estándar C23 (aquí hay un enlace al borrador de trabajo más reciente ). Aquí hay una buena publicación de blog sobre la historia de #embed
uno de los miembros del comité detrás de esta nueva característica.
Aquí hay un fragmento del borrador del estándar que demuestra su uso:
#include <stddef.h>
void have_you_any_wool(const unsigned char*, size_t);
int main (int, char*[]) {
static const unsigned char baa_baa[] = {
#embed "black_sheep.ico"
};
have_you_any_wool(baa_baa, sizeof(baa_baa));
return 0;
}
En este momento no existe una directiva equivalente para C++.
Si quiero incrustar datos estáticos en un ejecutable, lo empaquetaría en un archivo .lib/.a o en un archivo de encabezado como una matriz de caracteres sin firmar. Eso es si buscas un enfoque portátil. He creado una herramienta de línea de comandos que hace ambas cosas aquí . Todo lo que tiene que hacer es enumerar los archivos y seleccionar la opción -l64 para generar un archivo de biblioteca de 64 bits junto con un encabezado que incluye todos los punteros a cada dato.
También puedes explorar más opciones. Por ejemplo, esta opción:
>BinPack image.png -j -hx
generará los datos de image.png en un archivo de encabezado, como hexadecimal y las líneas se justificarán según la opción -j.
const unsigned char BP_icon[] = {
0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0x00,0x00,0x01,0xed,0x00,0x00,0x01,0xed,0x08,0x06,0x00,0x00,0x00,0x34,0xb4,0x26,
0xfb,0x00,0x00,0x02,0xf1,0x7a,0x54,0x58,0x74,0x52,0x61,0x77,0x20,0x70,0x72,0x6f,
0x66,0x69,0x6c,0x65,0x20,0x74,0x79,0x70,0x65,0x20,0x65,0x78,0x69,0x66,0x00,0x00,
0x78,0xda,0xed,0x96,0x5d,0x92,0xe3,0x2a,0x0c,0x85,0xdf,0x59,0xc5,0x2c,0x01,0x49,
0x08,0x89,0xe5,0x60,0x7e,0xaa,0xee,0x0e,0xee,0xf2,0xef,0x01,0x3b,0x9e,0x4e,0xba,
0xbb,0x6a,0xa6,0x66,0x5e,0x6e,0x55,0x4c,0x8c,0x88,0x0c,0x07,0xd0,0x27,0x93,0x84,
0xf1,0xef,0x3f,0x33,0xfc,0xc0,0x45,0xc5,0x52,0x48,0x6a,0x9e,0x4b,0xce,0x11,0x57,
0x2a,0xa9,0x70,0x45,0xc3,0xe3,0x79,0xd5,0x5d,0x53,0x4c,0xbb,0xde,0xd7,0xe8,0x57,
0x8b,0x9e,0xfd,0xe1,0x7e,0xc0,0xb0,0x02,0x2b,0xe7,0x03,0xcf,0xa7,0xa5,0x87,0xff,
0x1a,0xf0,0xb0,0x54,0xd1,0xd2,0x0f,0x42,0xde,0xae,0x07,0xc7,0xf3,0x83,0x92,0x4e,
0xcb,0xfe,0x22,0xc4,0xa7,0x91,0xb5,0xa2,0xd5,0xee,0x97,0x50,0xb9,0x84,0x84,0xcf,
0x07,0x74,0x09,0xd4,0x73,0x5b,0x31,0x17,0xb7,0x8f,0x5b,0x38,0xc6,0x69,0xaf}