С http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:
Недавно у меня возникла необходимость встроить файл в исполняемый файл.Так как я работаю в командной строке с gcc, et al, а не с причудливым инструментом 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: взять его адрес и привести указатель к нужному типу при прохождении данных.