51 votos

C/C++ con GCC: estáticamente Agregar recurso archivos ejecutables/biblioteca

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

Por ejemplo me gustaría añadir archivos de imagen que nunca cambian (y si lo hacen, yo tendría que reemplazar el archivo de todos modos) y no quiero que ellos se encuentran alrededor en el sistema de archivos.

Si esto es posible (y creo que es debido a Visual C++ para Windows puede hacer esto, también), ¿cómo puedo cargar los archivos que se almacenan en el propio binario? ¿El ejecutable de análisis en sí mismo, buscar el archivo y extraer los datos de ella?

Tal vez hay una opción para GCC no he visto todavía. El uso de motores de búsqueda realmente no escupir la materia de derecho.

Necesito este trabajo para las bibliotecas compartidas y normal ELF-ejecutables.

Cualquier ayuda se aprecia

62voto

ndim Puntos 11557

He utilizado objcopy (GNU binutils) para vincular los datos binarios de un archivo foo-datos.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 foo-data.o de archivo de objeto que se puede vincular a tu ejecutable. El C de la interfaz se ve algo 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");

así que usted puede 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);
memcpy(foo_copy, foo_data, foo_size);

Si el objetivo de la arquitectura tiene restricciones especiales en cuanto a dónde constantes y variables que se almacenan los datos, o si desea almacenar los datos en la .text segmento para ajustarlo en el mismo tipo de memoria, como el código del programa, se puede jugar con el objcopy algunos parámetros más.

33voto

Simon Puntos 431

Puede incrustar archivos binarios ejecutables usando ldenlazador. Por ejemplo, si usted tiene el archivo foo.bar a continuación, puede incrustar en el archivo ejecutable de la adición de los siguientes comandos para ld

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

Si usted está invocando ld thru gcc , entonces usted necesitará agregar -Wl

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

Aquí --format=binary dice que el enlazador que el siguiente archivo es binario y --format=default vuelve al predeterminado el formato de entrada (esto es útil si usted va a especificar otros archivos de entrada después de foo.bar).

Entonces usted puede tener acceso al contenido de su archivo de código:

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

También hay símbolo denominado" "_binary_foo_bar_size". Creo que es de tipo uintptr_t pero yo no ver.

29voto

Hazok Puntos 825

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

Recientemente tuve la necesidad de insertar un archivo en un archivo ejecutable. Ya estoy trabajando en la línea de comandos con gcc, et al., y no con una elegante herramienta RAD que hace que todo suceda por arte de magia no era inmediatamente obvio para mí cómo hacer que esto suceda. Un poco de búsqueda en la red encontré un hack a la esencia de gato en la final del ejecutable y, a continuación, descifrar en la que se basa en un montón de información que no me interesa. Parecía que debe haber una mejor manera...

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

Supongamos que tenemos un nombre de archivo data.txt que queremos incrustar en nuestro ejecutable:

# cat data.txt
Hello world

Para convertir esto en un archivo de objeto que se puede vincular con nuestro programa sólo tiene que utilizar objcopy para producir una ".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 es en el "binario", formato, que nuestro archivo de salida debe estar en la "elf32-i386" (formato de archivos de objeto en el x86). El --binary-arquitectura opción le dice a objcopy que el archivo de salida está destinado a "ejecutar" en un x86. Esto es necesario para que ld se acepta el archivo para la vinculación con otros archivos para el x86. Uno podría pensar que especificar el formato de salida como "elf32-i386" implicaría esto, pero no lo hace.

Ahora que tenemos un objeto de archivo solo tenemos que incluir al ejecutar el enlazador:

# gcc main.c data.o

Cuando ejecutamos el resultado que obtenga el oró para la salida:

# ./a.out
Hello world

Por supuesto, yo no he dicho toda la historia sin embargo, ni muestra los principales.c. Cuando objcopy significa lo anterior conversión añade "vinculador" de símbolos con el objeto convertido archivo:

_binary_data_txt_start
_binary_data_txt_end

Después de la vinculación, estos símbolos especificar el inicio y el final del archivo incrustado. Los nombres de los símbolos se forman anteponiendo binario y anexando _start o _end para el nombre de archivo. Si el nombre de archivo contiene caracteres que no sería válida en nombre de un símbolo que se convierten en caracteres de subrayado (por ejemplo, data.txt se convierte en data_txt). Si usted recibe los nombres sin resolver al vincular el uso de estos símbolos, hacer un hexdump-C en el archivo de objeto y mira al final de la descarga para los nombres que objcopy eligió.

El código para utilizar el archivo incrustado ahora debería ser 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++);
}

Uno de los importantes y sutiles cosa a tener en cuenta es que los símbolos se agrega al archivo de objeto no son "variables". No contienen datos, en su lugar, su dirección es su valor. Declaro a ellos como tipo char, porque es conveniente para este ejemplo: los datos adjuntos es el carácter de los datos. No obstante, puede declararlos, como int si los datos es una matriz de enteros, o como struct foo_bar_t si los datos fueran una matriz de foo bares. Si los datos incrustados no es uniforme, entonces char es probablemente la más conveniente: tomar su dirección y echó el puntero al tipo adecuado a medida que atraviesan los datos.

29voto

Flexo Puntos 39273

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 el resto del código de entonces, se puede utilizar fmemopen para obtener un "regular" FILE * objeto, o, alternativamente, std::stringstream hacer una iostream. std::stringstream no es grande para esto, sin embargo, y usted puede, por supuesto, sólo tiene que utilizar un puntero en cualquier lugar se puede utilizar un iterador.

Si usted está usando esto con automake no olvide ajustar BUILT_SOURCES adecuadamente.

Lo bueno de hacerlo de esta manera es:

  1. Usted obtener el texto, por lo que puede ser en el control de versiones y parches de sensatez
  2. Es portátil y bien definido en cada plataforma

23voto

John Ripley Puntos 2922

Si usted quiere control sobre el nombre del símbolo exacta y la colocación de recursos, puede utilizar (o guión) el ensamblador de GNU (no es realmente parte de gcc) para importar archivos enteros binarios. Pruebe esto:

Asamblea (x 86/brazo):

    .section .rodata
    .global thing
    .type   thing, @object
    .align  4
thing:
    .incbin "meh.bin"
    .global thing_size
    .type   thing_size, @object
    .align  4
thing_size:
    .int    thing_size - thing

C:

#include <stdio.h>
extern char thing[];
extern unsigned thing_size;
int main() {
  printf("%p %u\n", thing, thing_size);
  return 0;
}

Lo que utilizas, es mejor hacer un script para generar todos los recursos y tienen nombres símbolo bonito/uniforme para todo.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X