De http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :
Recentemente, tive a necessidade de inserir um arquivo em um executável. Já que estou trabalhando na linha de comando com gcc, et al e não com uma ferramenta RAD sofisticada que faz tudo acontecer magicamente, não foi imediatamente óbvio para mim como fazer isso acontecer. Um pouco de pesquisa na rede encontrou um hack para basicamente catá-lo no final do executável e, em seguida, decifrar onde ele estava baseado em um monte de informações que eu não queria saber. Parecia que deveria haver uma maneira melhor ...
E há, é objcopy para o resgate. objcopy converte arquivos de objeto ou executáveis de um formato para outro. Um dos formatos que ele entende é "binário", que basicamente é qualquer arquivo que não esteja em um dos outros formatos que ele entende. Então, você provavelmente teve a ideia: converter o arquivo que queremos incorporar em um arquivo de objeto, então ele pode simplesmente ser vinculado ao resto do nosso código.
Digamos que temos um arquivo de nome data.txt que queremos incorporar em nosso executável:
# cat data.txt
Hello world
Para converter isso em um arquivo objeto que podemos vincular ao nosso programa, usamos apenas objcopy para produzir um arquivo ".o":
# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o
Isso diz ao objcopy que nosso arquivo de entrada está no formato "binário", que nosso arquivo de saída deve estar no formato "elf32-i386" (arquivos de objeto no x86). A opção --binary-architecture diz ao objcopy que o arquivo de saída deve "rodar" em um x86. Isso é necessário para que o ld aceite o arquivo para vinculação com outros arquivos do x86. Alguém poderia pensar que especificar o formato de saída como "elf32-i386" implicaria isso, mas não é.
Agora que temos um arquivo-objeto, só precisamos incluí-lo quando executamos o vinculador:
# gcc main.c data.o
Quando executamos o resultado, obtemos o resultado esperado:
# ./a.out
Hello world
Claro, eu não contei a história toda ainda, nem mostrei a você main.c. Quando objcopy faz a conversão acima, ele adiciona alguns símbolos de "vinculador" ao arquivo de objeto convertido:
_binary_data_txt_start
_binary_data_txt_end
Após a vinculação, esses símbolos especificam o início e o fim do arquivo incorporado. Os nomes dos símbolos são formados por binários precedentes e do _start ou _end ao nome do arquivo. Se o nome do arquivo contiver quaisquer caracteres que seriam inválidos em um nome de símbolo, eles serão convertidos em sublinhados (por exemplo, data.txt torna-se data_txt). Se você obtiver nomes não resolvidos ao vincular usando esses símbolos, faça um hexdump -C no arquivo de objeto e procure no final do dump os nomes escolhidos por objcopy.
O código para realmente usar o arquivo incorporado agora deve ser razoavelmente óbvio:
#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++);
}
Uma coisa importante e sutil a notar é que os símbolos adicionados ao arquivo de objeto não são "variáveis". Eles não contêm nenhum dado, em vez disso, seu endereço é seu valor. Eu os declaro como tipo char porque é conveniente para este exemplo: os dados incorporados são dados de caracteres. No entanto, você pode declará-los como qualquer coisa, como int se os dados forem um array de inteiros, ou como struct foo_bar_t se os dados forem qualquer array de foo bars. Se os dados embutidos não forem uniformes, então char é provavelmente o mais conveniente: pegue seu endereço e lance o ponteiro para o tipo apropriado conforme você percorre os dados.