C / C++ с GCC: статическое добавление файлов ресурсов в исполняемый файл / библиотеку
есть ли у кого-нибудь идея, как статически скомпилировать любой файл ресурсов прямо в исполняемый файл или файл общей библиотеки с помощью GCC?
например, я хотел бы добавить файлы изображений, которые никогда не меняются (и если они это сделают, мне все равно придется заменить файл) и не хотел бы, чтобы они лежали в файловой системе.
Если это возможно (и я думаю, что это потому, что Visual C++ для Windows тоже может это сделать), как я могу загрузить файлы, которые хранятся в собственном двоичном файле? Выполняет ли исполняемый файл сам синтаксический анализ, находит файл и извлекает из него данные?
может быть, есть вариант для GCC, который я еще не видел. Использование поисковых систем на самом деле не выплевывало правильные вещи.
Мне это нужно для работы с общими библиотеками и обычными ELF-исполняемыми файлами.
любая помощь приветствуется
8 ответов:
С imagemagick:
convert file.png data.h
дает что-то типа:
/* data.h (PNM). */ static unsigned char MagickImage[] = { 0x50, 0x36, 0x0A, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4D, 0x50, 0x0A, 0x32, 0x37, 0x37, 0x20, 0x31, 0x36, 0x32, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ....
для совместимости с другим кодом, то вы можете использовать либо
fmemopen
чтобы получить "обычный"
обновление Я вырос, чтобы предпочесть контроль собрание Джона Рипли
.incbin
на основании решения предложения и теперь использовать вариант на что.я использовал objcopy (GNU binutils) для связывания двоичных данных из файла foo-data.Бин в раздел данных исполняемого файла:
objcopy -B i386 -I binary -O elf32-i386 foo-data.bin foo-data.o
это дает вам
foo-data.o
объектный файл, который можно связать с исполняемым файлом. Интерфейс C выглядит примерно так/** created from binary via objcopy */ extern uint8_t foo_data[] asm("_binary_foo_data_bin_start"); extern uint8_t foo_data_size[] asm("_binary_foo_data_bin_size"); extern uint8_t foo_data_end[] asm("_binary_foo_data_bin_end");
так что вы можете делать такие вещи, как
for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) { transmit_single_byte(*byte); }
или
size_t foo_size = (size_t)((void *)foo_data_size); void *foo_copy = malloc(foo_size); assert(foo_copy); memcpy(foo_copy, foo_data, foo_size);
если ваша целевая архитектура имеет специальные ограничения относительно того, где хранятся постоянные и переменные данные, или вы хотите сохранить эти данные в
.text
сегмент, чтобы он вписывался в тот же тип памяти, что и ваш программный код, вы можете играть сobjcopy
параметры немного больше.
вы можете вставить двоичные файлы в исполняемый файл с помощью
ld
линкер. Например, если у вас есть файлfoo.bar
затем вы можете встроить его в исполняемый файл, добавив следующие команды вld
--format=binary foo.bar --format=default
если вы призываете
ld
доgcc
тогда вам нужно будет добавить-Wl
-Wl,--format=binary -Wl,foo.bar -Wl,--format=default
здесь
--format=binary
указывает компоновщику, что следующий файл является двоичным и--format=default
переключается на формат ввода по умолчанию (это полезно, если вы укажете другой вход файлы послеfoo.bar
).затем вы можете получить доступ к содержимому файла из кода:
extern uint8_t data[] asm("_binary_foo_bar_start"); extern uint8_t data_end[] asm("_binary_foo_bar_end");
есть также символ с именем
"_binary_foo_bar_size"
. Я думаю, что это типаuintptr_t
но я не проверял его.
вы можете поместить все свои ресурсы в ZIP-файл и добавьте это в конец исполняемого файла:
g++ foo.c -o foo0 zip -r resources.zip resources/ cat foo0 resources.zip >foo
это работает, потому что а) большинство исполняемых форматов изображений не волнует, есть ли дополнительные данные за изображением и Б) zip хранит подпись файла в конец файла zip. Это означает, что ваш исполняемый файл является обычным zip-файлом после этого (за исключением вашего предварительного исполняемого файла, который может обрабатывать zip), который можно открыть и прочитать с помощью libzip.
от http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:
недавно у меня возникла необходимость встраивать файл в исполняемый файл. Поскольку я работаю в командной строке с gcc и т. д., а не с причудливым инструментом RAD, который делает все это волшебным, мне не сразу стало ясно, как это сделать. Немного поиска в сети нашел хак, чтобы по существу cat его на конец исполняемого файла, а затем расшифровать, где это было основано на куче информации, о которой я не хотел знать. Казалось, что должен быть лучший способ...
и есть, это objcopy на помощь. objcopy преобразует объектные файлы и исполняемые файлы из одного формата в другой. Один из форматов, который он понимает, - это "двоичный", который в основном представляет собой любой файл, который не находится в одном из других форматов, которые он понимает. Поэтому вы, вероятно, предусмотрели идею: преобразуйте файл, который мы хотим встроить в объектный файл, тогда он может просто быть связаны с остальной частью нашего кода.
допустим, у нас есть имя файла данных.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
конечно, я не рассказал всю историю тем не менее, ни показал вам главное.c. когда objcopy выполняет приведенное выше преобразование, он добавляет некоторые символы "компоновщика" в преобразованный объектный файл:
_binary_data_txt_start _binary_data_txt_end
после связывания эти символы указывают начало и конец встроенного файла. Имена символов формируются путем добавления binary и добавить _start и _end к имени файла. Если имя файла содержит какие-либо символы, которые были бы недопустимы в имени символа, они преобразуются в подчеркивания (например, данные.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 bars. Если внедренные данные неоднородны, то char, вероятно, наиболее удобен: возьмите его адрес и приведите указатель к нужному типу по мере прохождения данных.
Если вы хотите контролировать точное имя символа и размещение ресурсов, вы можете использовать (или сценарий) ассемблер GNU (на самом деле не часть gcc) для импорта целых двоичных файлов. Попробуйте это:
сборка (x86 / arm):
.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
C:
#include <stdio.h> extern char thing[]; extern unsigned thing_size; int main() { printf("%p %u\n", thing, thing_size); return 0; }
что бы вы ни использовали, вероятно, лучше всего сделать скрипт для генерации всех ресурсов и иметь хорошие/единообразные имена символов для всего.
могу ли я добавить, что метод Джона Рипли, вероятно, лучший здесь по одной огромной причине - выравнивание. Если вы делаете стандартную objcopy или " LD-R-B binary-o foo.о Фу.txt", а затем посмотрите на полученный объект с objdump-x похоже, что выравнивание для блока установлено в 0. Если вы хотите, чтобы выравнивание было правильным для двоичных данных, отличных от char, я не могу себе представить, что это хорошо.
читая все сообщения здесь и в Интернете я сделал вывод, что нет никакого инструмента для ресурсов, который является :
1) простота использования в коде.
2) автоматизированный (чтобы быть легко включены в cmake/make).
3) крест-платформы.
Я решил написать инструмент сам. Код доступен здесь. https://github.com/orex/cpp_rsc
использовать его с cmake очень легко.
вы должны добавить в свой CMakeLists.txt-файл с таким кодом.
file(DOWNLOAD https://raw.github.com/orex/cpp_rsc/master/cmake/modules/cpp_resource.cmake ${CMAKE_BINARY_DIR}/cmake/modules/cpp_resource.cmake) set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake/modules) include(cpp_resource) find_resource_compiler() add_resource(pt_rsc) #Add target pt_rsc link_resource_file(pt_rsc FILE <file_name1> VARIABLE <variable_name1> [TEXT]) #Adds resource files link_resource_file(pt_rsc FILE <file_name2> VARIABLE <variable_name2> [TEXT]) ... #Get file to link and "resource.h" folder #Unfortunately it is not possible with CMake add custom target in add_executable files list. get_property(RSC_CPP_FILE TARGET pt_rsc PROPERTY _AR_SRC_FILE) get_property(RSC_H_DIR TARGET pt_rsc PROPERTY _AR_H_DIR) add_executable(<your_executable> <your_source_files> ${RSC_CPP_FILE})
реальный пример, используя подход можно скачать здесь, https://bitbucket.org/orex/periodic_table