Incorporare risorse nell’eseguibile usando GCC

Sto cercando un modo per incorporare facilmente qualsiasi dato binario esterno in un’applicazione C / C ++ compilata da GCC.

Un buon esempio di cosa mi piacerebbe fare è gestire il codice shader: posso semplicemente tenerlo nei file sorgente come const char* shader = "source here"; ma questo è estremamente poco pratico.

Mi piacerebbe che il compilatore lo facesse per me: su compilation (linking stage), leggi il file “foo.bar” e collega il suo contenuto al mio programma, così che sarei in grado di accedere ai contenuti come dati binari dal codice.

Potrebbe essere utile per le piccole applicazioni che vorrei distribuire come un singolo file .exe.

GCC supporta qualcosa di simile?

Ci sono un paio di possibilità:

  • usa la capacità di ld di trasformare qualsiasi file in un object ( Incorporando blob binari usando gcc mingw ):

     ld -r -b binary -o binary.o foo.bar # then link in binary.o 
  • utilizzare bin2c / bin2h per trasformare qualsiasi file in una matrice di byte ( incorporare l’immagine nel codice, senza utilizzare la sezione risorse o immagini esterne )


Aggiornamento: ecco un esempio più completo di come utilizzare i dati associati all’eseguibile usando ld -r -b binary :

 #include  // a file named foo.bar with some example text is 'imported' into // an object file using the following command: // // ld -r -b binary -o foo.bar.o foo.bar // // That creates an bject file named "foo.bar.o" with the following // symbols: // // _binary_foo_bar_start // _binary_foo_bar_end // _binary_foo_bar_size // // Note that the symbols are addresses (so for example, to get the // size value, you have to get the address of the _binary_foo_bar_size // symbol). // // In my example, foo.bar is a simple text file, and this program will // dump the contents of that file which has been linked in by specifying // foo.bar.o as an object file input to the linker when the progrma is built extern char _binary_foo_bar_start[]; extern char _binary_foo_bar_end[]; int main(void) { printf( "address of start: %p\n", &_binary_foo_bar_start); printf( "address of end: %p\n", &_binary_foo_bar_end); for (char* p = _binary_foo_bar_start; p != _binary_foo_bar_end; ++p) { putchar( *p); } return 0; } 

Aggiornamento 2 – Ottenere la dimensione della risorsa: non ho potuto leggere correttamente _binary_foo_bar_size. In fase di runtime, gdb mi mostra la giusta dimensione della risorsa testuale usando display (unsigned int)&_binary_foo_bar_size . Ma assegnarlo a una variabile dava sempre un valore sbagliato. Potrei risolvere questo problema nel seguente modo:

 unsigned int iSize = (unsigned int)(&_binary_foo_bar_end - &_binary_foo_bar_start) 

È una soluzione, ma funziona bene e non è troppo brutto.

Oltre ai suggerimenti già menzionati, sotto linux puoi usare lo strumento hex dump xxd, che ha una funzione per generare un file header C:

 xxd -i mybinary > myheader.h 

Non esattamente un modo nuovo, ma sicuramente molto conveniente. Mi sono imbattuto in questa libreria con licenza totalmente gratuita basata sul metodo di assemblaggio incbin non menzionato tra le risposte qui.

https://github.com/graphitemaster/incbin

Per ricapitolare. Il metodo incbin è come questo. Avete un file di assembly thing.s che compilate con gcc -c thing.s

  .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 

Nel tuo codice c o cpp puoi fare riferimento a:

 extern const char thing[]; extern const char* thing_end; extern int thing_size; 

Quindi collega il risultato .o con il resto delle unità di compilazione. Credito in cui è dovuto @John Ripley con la sua risposta qui: C / C ++ con GCC: aggiungere staticamente file di risorse a eseguibili / libreria

Ma quanto sopra non è conveniente come quello che ti può dare Incbin. Per eseguire quanto sopra con incbin non è necessario scrivere alcun assemblatore. Solo il seguente farà:

 #include "incbin.h" INCBIN(thing, "meh.bin"); int main(int argc, char* argv[]) { // Now use thing printf("thing=%p\n", gThingData); printf("thing len=%d\n", gThingSize); } 

Puoi farlo in un file di intestazione:

 #ifndef SHADER_SRC_HPP #define SHADER_SRC_HPP const char* shader= " //source "; #endif 

e includetelo.

L’altro modo è leggere il file shader.