Alguém tem uma ideia de como compilar estaticamente qualquer arquivo de recurso no executável ou no arquivo de biblioteca compartilhada usando o GCC?
Por exemplo, gostaria de adicionar arquivos de imagem que nunca mudam (e se mudarem, eu teria que substituir o arquivo de qualquer maneira) e não quero que eles fiquem no sistema de arquivos.
Se isso for possível (e acho que é porque o Visual C ++ para Windows também pode fazer isso), como posso carregar os arquivos que estão armazenados no próprio binário? O executável analisa a si mesmo, encontra o arquivo e extrai os dados dele?
Talvez haja uma opção para o GCC que ainda não vi. O uso de mecanismos de pesquisa realmente não revelou as coisas certas.
Eu precisaria disso para funcionar com bibliotecas compartilhadas e executáveis ELF normais.
Qualquer ajuda é apreciada
Respostas:
Com imagemagick :
Dá algo como:
Para compatibilidade com outro código, você pode usar
fmemopen
para obter umFILE *
objeto "normal" ou, alternativamente,std::stringstream
para fazer umiostream
.std::stringstream
não é ótimo para isso e é claro que você pode usar apenas um ponteiro em qualquer lugar em que possa usar um iterador.Se você estiver usando com o automake, não se esqueça de definir BUILT_SOURCES apropriadamente.
O bom de fazer dessa maneira é:
fonte
xxd -i infile.bin outfile.h
objcopy
para converter os dados binários diretamente em um arquivo de objeto; no entanto, isso raramente é uma preocupação.Atualização Passei a preferir o controle que a
.incbin
solução baseada em montagem de John Ripley oferece e agora uso uma variante disso.Usei objcopy (GNU binutils) para vincular os dados binários de um arquivo foo-data.bin à seção de dados do executável:
Isso fornece um
foo-data.o
arquivo de objeto que você pode vincular ao seu executável. A interface C parece algo comoentão você pode fazer coisas como
ou
Se sua arquitetura de destino tem restrições especiais quanto ao local onde os dados constantes e variáveis são armazenados, ou você deseja armazenar esses dados no
.text
segmento para fazê-los caber no mesmo tipo de memória que seu código de programa, você pode brincar umobjcopy
pouco mais com os parâmetros.fonte
ld
pois o formato de saída está implícito aqui, consulte stackoverflow.com/a/4158997/201725 .Você pode incorporar arquivos binários em executáveis usando o
ld
vinculador. Por exemplo, se você tiver um arquivofoo.bar
, poderá incorporá-lo ao executável adicionando os seguintes comandos aold
Se você estiver invocando
ld
através,gcc
então você precisará adicionar-Wl
Aqui,
--format=binary
informa ao vinculador que o arquivo a seguir é binário e--format=default
volta ao formato de entrada padrão (isso é útil se você especificar outros arquivos de entrada depoisfoo.bar
).Em seguida, você pode acessar o conteúdo do seu arquivo a partir do código:
Há também um símbolo denominado
"_binary_foo_bar_size"
. Acho que é do tipo,uintptr_t
mas não verifiquei.fonte
data_end
um array, não um ponteiro? (Ou isso é idiomático C?)data_end
for um ponteiro, o compilador pensará que há um ponteiro armazenado após o conteúdo do arquivo. Similarmente, se você mudar o tipo dedata
para um ponteiro, então você obterá um ponteiro consistindo dos primeiros bytes de um arquivo em vez de um ponteiro para seu início. Acho que sim.const pointer
. O compilador permite que você altere o valor de ponteiros não constantes, mas não permite que você altere o valor se for um array. Portanto, talvez seja menos digitado usar a sintaxe de array.Você pode colocar todos os seus recursos em um arquivo ZIP e anexá-lo ao final do arquivo executável :
Isso funciona porque a) os formatos de imagem mais executáveis não se importam se há dados extras por trás da imagem eb) o zip armazena a assinatura do arquivo no final do arquivo zip . Isso significa que seu executável é um arquivo zip regular depois disso (exceto para o executável inicial, que pode ser gerenciado pelo zip), que pode ser aberto e lido com libzip.
fonte
install_name_tool
. Além disso, o binário ainda funciona como executável.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:
Para converter isso em um arquivo objeto que podemos vincular ao nosso programa, usamos apenas objcopy para produzir um arquivo ".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:
Quando executamos o resultado, obtemos o resultado esperado:
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:
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:
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.
fonte
Se você deseja controlar o nome exato do símbolo e a localização dos recursos, pode usar (ou fazer um script) o assembler GNU (que não faz parte do gcc) para importar arquivos binários inteiros. Experimente isto:
Montagem (x86 / braço):
C:
O que quer que você use, provavelmente é melhor fazer um script para gerar todos os recursos e ter nomes de símbolo bonitos / uniformes para tudo.
Dependendo de seus dados e das especificações do sistema, você pode precisar usar diferentes valores de alinhamento (de preferência com
.balign
para portabilidade), ou tipos inteiros de um tamanho diferente parathing_size
, ou um tipo de elemento diferente para athing[]
matriz.fonte
Lendo todos os post aqui e na Internet cheguei à conclusão que não existe uma ferramenta de recursos, que é:
1) Fácil de usar no código.
2) Automatizado (para ser facilmente incluído no cmake / make).
3) Plataforma cruzada.
Decidi escrever a ferramenta sozinho. O código está disponível aqui. https://github.com/orex/cpp_rsc
É muito fácil usá-lo com o cmake.
Você deve adicionar esse código ao seu arquivo CMakeLists.txt.
O exemplo real, usando a abordagem, pode ser baixado aqui, https://bitbucket.org/orex/periodic_table
fonte