З http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :
Нещодавно у мене була потреба вбудувати файл у виконуваний файл. Оскільки я працюю в командному рядку з gcc та ін., А не з вигадливим інструментом RAD, який робить все магічним, мені не одразу було очевидно, як це зробити. Трохи пошуків у мережі знайшов хакер, який по суті перевів його на кінець виконуваного файлу, а потім розшифрував, де це базується на купу інформації, про яку я не хотів знати. Здавалося, повинен бути кращий спосіб ...
І є, це обйкопія на допомогу. objcopy перетворює об'єктні файли або виконувані файли з одного формату в інший. Одним із форматів, який він розуміє, є "двійковий", що в основному є будь-яким файлом, який не входить в інший формат, який він розуміє. Отже, ви, мабуть, передбачили ідею: перетворіть файл, який ми хочемо вбудувати, у об’єктний файл, тоді його можна просто пов’язати з рештою нашого коду.
Скажімо, у нас є ім’я файлу data.txt, яке ми хочемо вбудувати у наш виконуваний файл:
# cat data.txt
Hello world
Щоб перетворити це в файл об'єкта, який ми можемо зв'язати з нашою програмою, ми просто використовуємо objcopy для створення файлу ".o":
# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o
Це повідомляє objcopy, що наш вхідний файл має "двійковий" формат, що наш вихідний файл повинен бути у форматі "elf32-i386" (об'єктні файли на x86). Параметр --binary-architecture повідомляє objcopy, що вихідний файл призначений для "запуску" на x86. Це потрібно для того, щоб ld прийняв файл для зв'язування з іншими файлами для x86. Можна подумати, що вказівка вихідного формату як "elf32-i386" означатиме це, але це не так.
Тепер, коли у нас є файл об’єкта, нам потрібно включити його лише під час запуску компоновщика:
# gcc main.c data.o
Коли ми запускаємо результат, ми отримуємо молитву за вихід:
# ./a.out
Hello world
Звичайно, я ще не розповів усієї історії, і не показав вам main.c. Коли objcopy виконує вищезазначене перетворення, він додає деякі символи "зв’язки" до перетвореного об’єктного файлу:
_binary_data_txt_start
_binary_data_txt_end
Після зв’язування ці символи визначають початок і кінець вбудованого файлу. Назви символів утворюються шляхом додавання двійкових символів та додавання _start або _end до імені файлу. Якщо ім'я файлу містить будь-які символи, які були б неприпустимими в назві символу, вони перетворюються на підкреслення (наприклад, data.txt стає data_txt). Якщо при зв'язуванні за допомогою цих символів ви отримуєте невирішені імена, зробіть hexdump -C на об'єктному файлі та подивіться на кінець дампа, щоб вибрати імена, які вибрав objcopy.
Код для фактичного використання вбудованого файлу тепер повинен бути досить очевидним:
#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++);
}
Важливо і тонко помітити, що символи, додані до об’єктного файлу, не є “змінними”. Вони не містять жодних даних, швидше, їх адреса є їх значенням. Я оголошую їх як тип char, оскільки це зручно для цього прикладу: вбудовані дані - це символьні дані. Однак ви можете оголосити їх як що завгодно, як int, якщо дані є масивом цілих чисел, або як struct foo_bar_t, якщо дані являють собою будь-який масив foo-барів. Якщо вбудовані дані не є однорідними, то char, мабуть, найзручніший: візьміть його адресу та перекиньте покажчик на потрібний тип під час обходу даних.