Как встроить файл в исполняемый файл? - PullRequest
16 голосов
/ 22 июля 2011

У меня две проблемы, первая была решена.

Текущая проблема
Если я встраиваю файл, для загрузки которого требуется библиотека, например, изображение в формате 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 , ноЯ все еще получаю ту же ошибку.

Ответы [ 5 ]

7 голосов
/ 22 июля 2011

Я думаю, что для того, чтобы это работало с MinGW, вам нужно удалить начальное подчеркивание из имен в файле .c. Подробнее см. Встраивание двоичных двоичных объектов с использованием gcc mingw .

Проверьте, помогает ли использование следующих подсказок:

extern char binary_data_txt_start;
extern char binary_data_txt_end;

Если вам нужен один и тот же источник для работы со сборками Linux или MinGW, вам может понадобиться использовать препроцессор, чтобы правильное имя использовалось в разных средах.

7 голосов
/ 22 июля 2011

Если вы используете библиотеку, для чтения которой требуется FILE*, то вы можете использовать fmemopen(3) для создания псевдофайла из большого двоичного объекта памяти. Это позволит избежать создания временного файла на диске. К сожалению, это расширение GNU, поэтому я не знаю, доступно ли оно с MinGW (скорее всего, нет).

Однако большинство хорошо написанных библиотек (таких как libpng и *1000* библиотека JPEG IJG *) предоставляют процедуры для открытия файла из памяти, а не с диска. libpng, в частности, даже предлагает потоковый интерфейс, где вы можете постепенно декодировать файл PNG, прежде чем он будет полностью прочитан в память. Это полезно, если, скажем, вы транслируете чересстрочный PNG из сети и хотите отображать чересстрочные данные по мере их загрузки для лучшего взаимодействия с пользователем.

3 голосов
/ 07 августа 2011

В 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. ДАННЫЕ - это просто имя, вы можете выбрать другое имя. Другими встроенными ресурсами являются курсоры, диалоги, таблицы строк и т. Д.

1 голос
/ 04 января 2014

Я создал небольшую библиотеку под названием elfdataembed , которая предоставляет простой интерфейс для извлечения / ссылки на разделы, встроенные с использованием objcopy. Это позволяет передавать смещение / размер другому инструменту или ссылаться на него непосредственно из среды выполнения с помощью файловых дескрипторов. Надеюсь, это поможет кому-то в будущем.

Стоит отметить, что этот подход более эффективен, чем компиляция в символ, так как он позволяет внешним инструментам ссылаться на данные без необходимости извлечения, а также не требует загрузки всего двоичного файла в память для того, чтобы извлечь / ссылаться на него.

0 голосов
/ 22 июля 2011

Используйте nm data.o, чтобы увидеть, как он назвал символы.Это может быть что-то столь же простое, как различия в файловой системе, приводящие к тому, что производные от имени файла символы будут разными (например, имя файла с заглавной буквы).

Редактировать: Только что увидел ваш второй вопрос.Если вы используете потоки, вы можете создать канал и передать его в библиотеку (сначала используйте fdopen(), если он хочет FILE *).Если вы более конкретны в отношении API, с которым вам нужно поговорить, я могу добавить более конкретные советы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...