Z http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :
Niedawno miałem potrzebę osadzenia pliku w pliku wykonywalnym. Ponieważ pracuję w wierszu poleceń z gcc, et al, a nie z fantazyjnym narzędziem RAD, które sprawia, że wszystko dzieje się w magiczny sposób, nie od razu było dla mnie oczywiste, jak to zrobić. Trochę przeszukiwania sieci znalazło hack, który zasadniczo zakotował go na końcu pliku wykonywalnego, a następnie rozszyfrował, gdzie był oparty na zbiorze informacji, o których nie chciałem wiedzieć. Wydawało się, że powinien być lepszy sposób ...
I jest, to objcopy na ratunek. objcopy konwertuje pliki obiektowe lub pliki wykonywalne z jednego formatu na inny. Jeden z formatów, które rozumie, to „binarny”, czyli zasadniczo każdy plik, który nie jest w żadnym z innych formatów, które rozumie. Więc prawdopodobnie wyobraziłeś sobie pomysł: przekonwertuj plik, który chcemy osadzić w pliku obiektowym, a następnie można go po prostu połączyć z resztą naszego kodu.
Powiedzmy, że mamy nazwę pliku data.txt, którą chcemy osadzić w naszym pliku wykonywalnym:
# cat data.txt
Hello world
Aby przekonwertować to na plik obiektowy, który możemy połączyć z naszym programem, używamy po prostu objcopy do utworzenia pliku „.o”:
# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o
To mówi objcopy, że nasz plik wejściowy jest w formacie "binarnym", a nasz plik wyjściowy powinien być w formacie "elf32-i386" (pliki obiektowe na x86). Opcja --binary-architecture mówi objcopy, że plik wyjściowy ma "działać" na x86. Jest to potrzebne, aby ld zaakceptował plik do łączenia z innymi plikami dla x86. Można by pomyśleć, że określenie formatu wyjściowego jako „elf32-i386” oznaczałoby to, ale tak nie jest.
Teraz, gdy mamy plik obiektowy, musimy go dołączyć tylko wtedy, gdy uruchamiamy konsolidator:
# gcc main.c data.o
Kiedy uruchomimy wynik, otrzymamy modlony o wyjście:
# ./a.out
Hello world
Oczywiście nie opowiedziałem jeszcze całej historii, ani nie pokazałem ci main.c. Kiedy objcopy wykonuje powyższą konwersję, dodaje kilka symboli „konsolidatora” do przekonwertowanego pliku obiektowego:
_binary_data_txt_start
_binary_data_txt_end
Po połączeniu symbole te określają początek i koniec osadzonego pliku. Nazwy symboli są tworzone przez poprzedzające je binarne i dodanie _start lub _end do nazwy pliku. Jeśli nazwa pliku zawiera jakiekolwiek znaki, które byłyby nieprawidłowe w nazwie symbolu, są one konwertowane na podkreślenia (np. Data.txt staje się data_txt). Jeśli otrzymujesz nierozwiązane nazwy podczas łączenia przy użyciu tych symboli, wykonaj zrzut heksowy -C na pliku obiektowym i spójrz na koniec zrzutu, aby znaleźć nazwy wybrane przez objcopy.
Kod do faktycznego korzystania z osadzonego pliku powinien być teraz dość oczywisty:
#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++);
}
Ważną i subtelną rzeczą, na którą należy zwrócić uwagę, jest to, że symbole dodane do pliku obiektowego nie są „zmiennymi”. Nie zawierają żadnych danych, raczej ich adres jest ich wartością. Deklaruję je jako typ char, ponieważ jest to wygodne w tym przykładzie: osadzone dane to dane znakowe. Jednak możesz zadeklarować je jako dowolne, jako int, jeśli dane są tablicą liczb całkowitych, lub jako struct foo_bar_t, jeśli dane byłyby dowolną tablicą słupków foo. Jeśli osadzone dane nie są jednolite, prawdopodobnie najwygodniejszy jest znak char: weź jego adres i rzuć wskaźnik na odpowiedni typ podczas przeglądania danych.