C / C++ con GCC: Agregar estáticamente archivos de recursos al ejecutable / biblioteca


¿Alguien tiene una idea de cómo compilar estáticamente cualquier archivo de recursos directamente en el ejecutable o en el archivo de biblioteca compartida usando GCC?

Por ejemplo, me gustaría agregar archivos de imagen que nunca cambian (y si lo hacen, tendría que reemplazar el archivo de todos modos) y no quisiera que se queden en el sistema de archivos.

Si esto es posible (y creo que es porque Visual C++ para Windows también puede hacer esto), ¿cómo puedo cargar los archivos que se almacenan en el binario propio? Hace el ejecutable analizar sí mismo, encontrar el archivo y extraer los datos de él?

Tal vez hay una opción para GCC que aún no he visto. El uso de motores de búsqueda realmente no escupió las cosas correctas.

Necesitaría esto para funcionar con bibliotecas compartidas y ejecutables ELF normales.

Cualquier ayuda es apreciada

Author: Atmocreations, 2011-02-01

8 answers

Con imagemagick:

convert file.png data.h

Da algo como:

/*
  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, 

....

Para la compatibilidad con otro código, puede usar fmemopen para obtener un objeto "regular" FILE *, o alternativamente std::stringstream para hacer un iostream. std::stringstream no es genial para esto, sin embargo, y por supuesto, puede usar un puntero en cualquier lugar donde pueda usar un iterador.

Si estás usando esto con automake no olvides establecer BUILT_SOURCES apropiadamente.

Lo bueno de hacerlo así el camino es:

  1. Se obtiene el texto, por lo que puede ser en el control de versiones y parches sensatamente
  2. Es portátil y está bien definido en todas las plataformas
 44
Author: Flexo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-01 16:42:28

Actualización He crecido para preferir el control La solución basada en el ensamblado .incbin de John Ripley y ahora uso una variante en eso.

He usado objcopy (GNU binutils) para enlazar los datos binarios de un archivo foo-data.bin en la sección de datos del ejecutable:

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

Esto le da un archivo objeto foo-data.o que puede enlazar en su ejecutable. La interfaz C se ve algo así como

/** 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");

Para que puedas hacer cosas como

for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) {
    transmit_single_byte(*byte);
}

O

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 su arquitectura de destino tiene restricciones especiales en cuanto a dónde se almacenan los datos constantes y variables, o desea almacenar esos datos en el segmento .text para que quepan en el mismo tipo de memoria que su código de programa, puede jugar con los parámetros objcopy un poco más.

 80
Author: ndim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-13 19:21:03

Puede incrustar archivos binarios en ejecutables usando ld enlazador. Por ejemplo, si tiene archivo foo.bar, puede incrustarlo en el ejecutable agregando los siguientes comandos a ld

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

Si está invocando ld a través de gcc entonces tendrá que agregar -Wl

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

Aquí --format=binary le dice al enlazador que el siguiente archivo es binario y --format=default cambia de nuevo al formato de entrada predeterminado (esto es útil si especifica otros archivos de entrada después de foo.bar).

, Entonces usted puede acceder al contenido de su archivo desde el código:

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

También hay un símbolo llamado "_binary_foo_bar_size". Creo que es de tipo uintptr_t pero no lo comprobé.

 46
Author: Simon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-24 01:03:15

Usted puede poner todos sus recursos en un archivo ZIP y anexar al final del archivo ejecutable:

g++ foo.c -o foo0
zip -r resources.zip resources/
cat foo0 resources.zip >foo

Esto funciona, porque a) A la mayoría de los formatos de imagen ejecutables no les importa si hay datos adicionales detrás de la imagen y b) zip almacena la firma del archivo en el extremo del archivo zip. Esto significa que su ejecutable es un archivo zip normal después de esto (excepto su ejecutable inicial, que zip puede manejar), que se puede abrir y leer con libzip.

 38
Author: Nordic Mainframe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 11:44:11

De http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:

Recientemente tuve la necesidad de incrustar un archivo en un ejecutable. Dado que estoy trabajando en la línea de comandos con gcc, et al y no con una herramienta RAD de lujo que hace que todo suceda mágicamente, no era inmediatamente obvio para mí cómo hacer que esto suceda. Un poco de búsqueda en la red encontró un truco para esencialmente cat en el extremo del ejecutable y luego descifrar donde se basó en un montón de información que no quería saber. Parecía que debería haber una mejor manera...

Y hay, es objcopy al rescate. objcopy convierte archivos objeto o ejecutables de un formato a otro. Uno de los formatos que entiende es "binario", que es básicamente cualquier archivo que no está en uno de los otros formatos que entiende. Así que probablemente has imaginado la idea: convertir el archivo que queremos incrustar en un archivo objeto, entonces simplemente puede ser vinculado con el el resto de nuestro código.

Digamos que tenemos un nombre de archivo de datos.txt que queremos incrustar en nuestro ejecutable:

# cat data.txt
Hello world

Para convertir esto en un archivo objeto que podamos enlazar con nuestro programa solo usamos objcopy para producir un ".o " archivo:

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

Esto le dice a objcopy que nuestro archivo de entrada está en el formato "binario", que nuestro archivo de salida debe estar en el formato "elf32-i386" (archivos objeto en el x86). La opción binary binary-architecture le dice a objcopy que el archivo de salida está destinado a " ejecutar" en un x86. Esto es necesario para que ld acepte el archivo para enlazar con otros archivos para el x86. Uno pensaría que especificar el formato de salida como "elf32-i386" implicaría esto, pero no lo hace.

Ahora que tenemos un archivo objeto solo necesitamos incluirlo cuando ejecutamos el enlazador:

# gcc main.c data.o

Cuando ejecutamos el resultado obtenemos la salida orada por:

# ./a.out
Hello world

Por supuesto, todavía no he contado toda la historia, ni te he mostrado main.c. Cuando objcopy hace la conversión anterior añade algunos símbolos "enlazadores" al archivo objeto convertido:

_binary_data_txt_start
_binary_data_txt_end

Después del enlace, estos símbolos especifican el inicio y el final del archivo incrustado. Los nombres de los símbolos se forman anteponiendo binario y añadiendo _start o _end al nombre del archivo. Si el nombre del archivo contiene caracteres que no serían válidos en un nombre de símbolo, se convierten en guiones bajos (por ejemplo, datos.txt se convierte en data_txt). Si obtiene nombres sin resolver al enlazar usando estos símbolos, haga un hexdump-C en el objeto archive y busque al final del volcado los nombres que eligió objcopy.

El código para usar realmente el archivo incrustado ahora debería ser razonablemente obvio:

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

Una cosa importante y sutil a tener en cuenta es que los símbolos añadidos al archivo objeto no son "variables". No contienen ningún dato, más bien, su dirección es su valor. Los declaro como tipo char porque es conveniente para este ejemplo: los datos incrustados son datos de caracteres. Sin embargo, usted podría declararlos como cualquier cosa, como int si los datos son una matriz de enteros, o como estructura foo_bar_t si los datos eran cualquier matriz de barras foo. Si los datos incrustados no son uniformes, entonces char es probablemente el más conveniente: tome su dirección y lance el puntero al tipo adecuado a medida que recorre los datos.

 34
Author: Hazok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-29 08:40:52

Si desea controlar el nombre exacto del símbolo y la ubicación de los recursos, puede usar (o un script) el ensamblador GNU (que en realidad no forma parte de gcc) para importar archivos binarios completos. Prueba esto:

Montaje (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;
}

Sea lo que sea que uses, probablemente sea mejor hacer un script para generar todos los recursos, y tener nombres de símbolos agradables/uniformes para todo.

 32
Author: John Ripley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-14 18:47:50

Puedo añadir que el método de John Ripley es probablemente el mejor aquí por una gran razón - alineación. Si haces un objcopy estándar o " ld-r-b binary-o foo.o foo.txt " y luego mira el objeto resultante con objdump-x parece que la alineación para el bloque está establecida en 0. Si desea que la alineación sea correcta para datos binarios que no sean char, no puedo imaginar que esto sea algo bueno.

 6
Author: carveone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-11 16:38:14

Leyendo todos los post aquí y en Internet he llegado a la conclusión de que no hay ninguna herramienta para los recursos, que es :

1) Fácil de usar en código.

2) Automatizado (para ser fácil incluido en cmake / make).

3) Multiplataforma.

He decidido escribir la herramienta por mí mismo. El código está disponible aquí. https://github.com/orex/cpp_rsc

Usarlo con cmake es muy fácil.

Usted debe agregar a sus listas de CMakeLists.archivo txt tal codificar.

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})

El ejemplo real, usando el enfoque se puede descargar aquí, https://bitbucket.org/orex/periodic_table

 4
Author: user2794512,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-03 11:14:56