С http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 :
Недавно мне понадобилось встроить файл в исполняемый файл. Поскольку я работаю в командной строке с помощью gcc и др., А не с причудливым инструментом RAD, который заставляет все это происходить волшебным образом, мне не сразу было очевидно, как это сделать. Немного поискав в сети, был обнаружен хак, который по существу помещал его в конец исполняемого файла, а затем расшифровывал, где он был основан на кучке информации, о которой я не хотел знать. Казалось, что должен быть способ получше ...
И вот, objcopy спешит на помощь. 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). Если вы получаете неразрешенные имена при связывании с использованием этих символов, выполните шестнадцатеричный дамп -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: возьмите его адрес и приведите указатель к нужному типу при перемещении по данным.