Как вставить файл в исполняемый файл?
У меня две проблемы: первая решена.
Текущая проблема
Если я вставляю файл, который требует загрузки библиотеки, например, jpeg-изображение или mp3-музыку, мне нужно будет использовать этот файл в качестве входа в библиотеку. Тем не менее, каждая библиотека отличается и использует способ получения файла как входной, вход может быть именем файла или указателем FILE * (из интерфейса файла libc).
Я хотел бы знать, как получить доступ к встроенному файлу с именем. Это будет неэффективно, если я создам временный файл, есть ли другой способ? Можно ли сопоставить имя файла в памяти? Мои платформы - это Windows и Linux.
Если show_file (const char * name) - это функция из библиотеки, мне понадобится строка для открытия файла.
Я видел эти вопросы:
Как получить файловый дескриптор буфера в памяти?
Получение имени файла из дескриптора файла в C
и следующий код - мое решение. Это хорошее решение? Это неэффективно?
# include <stdio.h>
# include <unistd.h>
extern char _binary_data_txt_start;
extern const void* _binary_data_txt_size;
const size_t len = (size_t)&_binary_data_txt_size;
void show_file(const char* name){
FILE* file = fopen(name, "r");
if (file == NULL){
printf("Error (show_file): %s\n", name);
return;
}
while (true){
char ch = fgetc(file);
if (feof(file) )
break;
putchar( ch );
}
printf("\n");
fclose(file);
}
int main(){
int fpipe[2];
pipe(fpipe);
if( !fork() ){
for( int buffsize = len, done = 0; buffsize>done; ){
done += write( fpipe[1], &_binary_data_txt_start + done, buffsize-done );
}
_exit(0);
}
close(fpipe[1]);
char name[200];
sprintf(name, "/proc/self/fd/%d", fpipe[0] );
show_file(name);
close(fpipe[0]);
}
Другая проблема (решена)
Я попытался вставить файл в Linux, с GCC, и он сработал. Тем не менее, я пытался сделать то же самое в Windows, с Mingw, и он не компилировался.
Код:
# include <stdio.h>
extern char _binary_data_txt_start;
extern char _binary_data_txt_end;
int main(){
for (char* my_file = &_binary_data_txt_start; my_file <= &_binary_data_txt_end; my_file++)
putchar(*my_file);
printf("\n");
}
Команды компиляции:
objcopy --input-target binary --output-target elf32-i386 --binary-architecture i386 data.txt data.o
g++ main.cpp data.o -o test.exe
В Windows я получаю следующую ошибку компилятора:
undefined reference to `_binary_data_txt_start'
undefined reference to `_binary_data_txt_end'
Я попытался заменить elf32-i386 на i386-pc-mingw32, но я все равно получаю ту же ошибку.
Ответы
Ответ 1
Я думаю, что для этого для работы с MinGW вам нужно удалить ведущее подчеркивание из имен в .c файле. См. Вложение бинарных капель с использованием gcc mingw для некоторых деталей.
Посмотрите, помогает ли следующая помощь:
extern char binary_data_txt_start;
extern char binary_data_txt_end;
Если вам нужен тот же источник для работы с сборками Linux или MinGW, вам может потребоваться использовать препроцессор, чтобы иметь правильное имя, используемое в разных средах.
Ответ 2
Если вы используете библиотеку, требующую FILE*
для чтения данных, вы можете использовать fmemopen(3)
для создания псевдофайла из памяти blob. Это позволит избежать создания временного файла на диске. К сожалению, это расширение GNU, поэтому я не знаю, доступно ли это с помощью MinGW (скорее всего, нет).
Однако большинство хорошо написанных библиотек (например, libpng и Библиотека JPEG IJG) предоставляют процедуры для открытия файла из памяти в отличие от диска. libpng, в частности, даже предлагает потоковый интерфейс, где вы можете инкрементно декодировать PNG файл, прежде чем он будет полностью прочитан в памяти. Это полезно, если, скажем, вы передаете чересстрочный PNG из сети, и вы хотите отображать чересстрочные данные при загрузке для лучшего пользовательского интерфейса.
Ответ 3
В Windows вы можете встраивать собственный ресурс в исполняемый файл. Вам понадобится файл .RC
и компилятор ресурсов. С помощью Visual Studio IDE вы можете сделать это без хлопот.
В вашем коде вы должны использовать функции FindResource
, LoadResource
и LockResource
для загрузки содержимого в память во время выполнения. Пример кода, который читает ресурс как длинную строку:
void GetResourceAsString(int nResourceID, CStringA &strResourceString)
{
HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(nResourceID), L"DATA");
HGLOBAL hResHandle = LoadResource(NULL, hResource);
const char* lpData = static_cast<char*> ( LockResource(hResHandle) );
strResourceString.SetString(lpData, SizeofResource(NULL, hResource));
FreeResource(hResource);
}
Где nResourceID
- это идентификатор ресурса в пользовательском типе ресурсов DATA
. DATA - это просто имя, вы можете выбрать другое имя. Другие встроенные ресурсы - это курсоры, диалоги, строковые таблицы и т.д.
Ответ 4
Я создал небольшую библиотеку elfdataembed, которая предоставляет простой интерфейс для извлечения/ссылки разделов, встроенных с помощью objcopy
. Это позволяет передавать смещение/размер другому инструменту или ссылаться на него непосредственно из среды выполнения с использованием дескрипторов файлов. Надеюсь, это поможет кому-то в будущем.
Стоит отметить, что этот подход более эффективен, чем компиляция для символа, поскольку он позволяет внешним инструментам ссылаться на данные без необходимости их извлечения, а также не требует, чтобы весь бинарный файл был загружен в память, чтобы извлекать/ссылаться на него.
Ответ 5
Используйте nm data.o
, чтобы увидеть, как он назван символами. Это может быть что-то столь же простое, как различия в файловой системе, в результате чего символы, полученные из имени файла, будут разными (например, имя файла заглавное).
Изменить: просто увидел ваш второй вопрос. Если вы используете потоки, вы можете сделать канал и передать его в библиотеку (сначала используя fdopen()
, если он хочет FILE *
). Если вы более конкретно относитесь к API, с которым вам нужно поговорить, я могу добавить более конкретные советы.
Ответ 6
Я никогда не пробовал этого, поэтому не уверен, что это сработает, но вы могли бы использовать именованный канал:
Окна
http://msdn.microsoft.com/en-us/library/aa365150(v=vs.85).aspx