Incrustar recursos en ejecutable usando GCC

Resuelto Kos asked hace 14 años • 6 respuestas

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?

Kos avatar Nov 12 '10 03:11 Kos
Aceptado

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/ bin2hpara 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.

Michael Burr avatar Nov 11 '2010 20:11 Michael Burr

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
Riot avatar Feb 06 '2014 14:02 Riot

Para esta tarea se puede utilizar la .incbindirectiva 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);   
}
hookenz avatar Mar 29 '2016 21:03 hookenz

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 #embeduno 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++.

irowe avatar Sep 01 '2022 13:09 irowe

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}
The Oathman avatar Mar 27 '2022 19:03 The Oathman