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. Como estoy trabajando en la línea de comandos con gcc, et al, y no con una elegante herramienta RAD que hace que todo suceda mágicamente, no me fue inmediatamente obvio cómo hacer que esto sucediera. Un poco de búsqueda en la red encontró un truco que esencialmente lo capturó al final del ejecutable y luego descifró dónde estaba basado en un montón de información que no quería conocer. Parecía que debería haber una mejor manera ...
Y lo hay, es obvio 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. Entonces, probablemente haya imaginado la idea: convertir el archivo que queremos incrustar en un archivo de objeto, luego simplemente se puede vincular con el resto de nuestro código.
Digamos 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 podamos vincular con nuestro programa, simplemente usamos objcopy para producir un archivo ".o":
# 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 formato "binario", que nuestro archivo de salida debe estar en formato "elf32-i386" (archivos de objeto en el x86). La opción --binary-architecture le dice a objcopy que el archivo de salida está destinado a "ejecutarse" en un x86. Esto es necesario para que ld acepte el archivo para vincularlo con otros archivos para x86. Uno pensaría que especificar el formato de salida como "elf32-i386" implicaría esto, pero no es así.
Ahora que tenemos un archivo de objeto, solo necesitamos incluirlo cuando ejecutamos el enlazador:
# gcc main.c data.o
Cuando ejecutamos el resultado, obtenemos el resultado pedido:
# ./a.out
Hello world
Por supuesto, aún no he contado toda la historia, ni les he mostrado el main.c. Cuando objcopy realiza la conversión anterior, agrega algunos símbolos de "vinculador" al archivo de objeto convertido:
_binary_data_txt_start
_binary_data_txt_end
Después de vincular, 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, data.txt se convierte en data_txt). Si obtiene nombres sin resolver al vincular usando estos símbolos, haga un hexdump -C en el archivo de objeto 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 agregados al archivo de 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, puede declararlos como cualquier cosa, como int si los datos son una matriz de enteros, o como struct foo_bar_t si los datos fueran cualquier matriz de barras foo. Si los datos incrustados no son uniformes, entonces char es probablemente lo más conveniente: tome su dirección y envíe el puntero al tipo adecuado a medida que recorre los datos.