Не буду рассказывать про то, что такое файл ресурсов, для чего нужен, и как его подключить к проекту (кто не в курсе этих вопросов – дальше можно читать только для общего образования), а сразу опишу проблему. Предположим, у нас есть файл, скажем, resources.rc, в котором содержится все необходимое проекту добро и успешно используется под Windows с помощью нехитрого набора функций LoadResource, FindResource и т.п. Задача: собрать этот же проект под Linux, в процессе подключить ресурсы к исполняемому файлу и, естественно, прочитать их в своей программе.

При сборке проекта в Windows файл определения ресурсов (.rc) компилируется в двоичный вид (.res) и на этапе компоновки подключается к exe-файлу, фактически прописываясь после запуска где-то в оперативной памяти. Напомню, что определить местоположение искомого ресурса позволяет функция:

 
/* hModule – модуль, в котором ищем ресурсы (NULL – если в текущем)
   lpName – имя ресурса
   lpType – тип ресурса (меню, диалог, строка …) */
HRSRC WINAPI FindResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType) 

Технология отлажена, сомнению не подвергается. Уже на этом этапе в Linux возникает несколько вопросов. Во-первых, чем откомпилировать исходный файл ресурсов в двоичный формат? Во-вторых, как подключить его к формируемому исполняемому файлу проекта? И, в-третьих, как найти и прочитать в файле нужный ресурс?

Шаг 1. Компилируем файл ресурсов

Самый простой шаг. С данной задачей вполне справляется утилита wrc (Wine Resource Compiler), которая умеет компилировать файл ресурсов в 16- или 32-разрядный двоичный формат. Применение простое:

 > wrc resources.rc –o resources.res 

Шаг 2. Подключение файла ресурсов

У нас уже имеется скомпилированный файл ресурсов, представленный в двоичном виде, однако, как это ни странно, пока что он представляет собой всего лишь набор символов. В таком виде его даже можно будет включить в сборку, но найти его в памяти во время работы программы будет проблематично, и наши ресурсы так и останутся болтаться внутри бинарника космическим мусором. На помощь приходит утилита objcopy, которая умеет преобразовывать объектные (и исполняемые) файлы в разные "правильные" форматы, которые потом уже можно будет прилинковать. С ее помощью преобразуем наш двоичный файл ресурсов (resources.res) в объектный (resources.o). Включаем магию:

 > objcopy –I binary –O elf32-i386 –B i386 resources.res resources.o 

Говорим, что на входе у нас двоичный файл (-I binary), на выходе хотим получить файл в формате объектных файлов на 32-разрядной платформе x86 (-O elf32-i386), причем он должен выглядеть как "исполняемый" файл (-B i386). В таком виде его уже можно будет подключать к проекту:

 > gcc myprogram.c resources.o 

Я использую cmake, поэтому в мой CMakeLists.txt добавил несколько строчек:

if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
    set(RC resources.rc)
else()
    set(RC resources.o)
endif()

# Предполагается, что переменные ${TARGET} и ${SOURCES} уже где-то заданы
add_executable(${TARGET} ${SOURCES} ${RC}) 

На этом почти вся магия кончается. Остается невыясненным только один вопрос: как найти ресурсы в готовом executable файле? Компоновщик, выполнив свою работу, великодушно оставил в собранном файле несколько меток:

 
_binary_resources_res_end
_binary_resources_res_size
_binary_resources_res_start 

По этим меткам теперь можно прочитать содержимое файла ресурсов.

Шаг 3. Поиск и чтение ресурсов в программе

Для начала определим переменные, соответствующие вышеописанным меткам компоновщика:

extern char _binary_resources_res_start,
            _binary_resources_res_end; 

При определении использую тип char, поскольку в моем проекте в ресурсах хранятся только ссылки на текстовые файлы в формате xml. Дополнительно определю привычные типы, не известные Linux, исключительно для удобства работы

#ifdef WIN32
# include <Windows.h>
#else
typedef void* HMODULE;
typedef void* HGLOBAL;
typedef void* HRSRC;
#endif 

Для применения FindResource не хватает только hModule. Под Windows в эту переменную будет записан хендл текущего модуля, в Linux – адрес начала блока ресурсов в нашем файле

#ifdef WIN32
    HMODULE hModule = GetModuleHandle();
#else
    HMODULE hModule = &_binary_resources_res_start;
#endif 

Все готово для поиска нужного ресурса, кроме того, что в Linux отсутствует соответствующая функция, которую придется изготовить самостоятельно. Поскольку все ресурсы, которые были подключены к нашему проекту, сохранены в формате файла res, неплохо было бы для начала узнать его формат. Читаем в MSDN, что в основе формата лежит структура RESOURCEHEADER

typedef struct {
    DWORD DataSize;
    DWORD HeaderSize;
    WORD  TypeOfTYPE;
    WORD  TYPE;
    WORD  TypeOfNAME;
    WORD  NAME;
    DWORD DataVersion;
    WORD  MemoryFlags;
    WORD  LanguageId;
    DWORD Version;
    DWORD Characteristics;
} RESOURCEHEADER; 

Под Linux я также переопределил типы

typedef uint16_t WORD;
typedef uint32_t DWORD; 

и добавил константу, обозначающую тип ресурса: в моем случае это простые данные

 #define RT_RCDATA "10" 

Итак, в файле ресурсов после структуры RESOURCEHEADER следуют данные, размер которых составляет DataSize байт. Этой информации почти достаточно для того, чтобы распотрошить его полностью. Последняя засада, которая поджидает на пути к цели: каждый ресурс, включающий структуру и сами данные, выровнен по двойному слову. То есть, в случае если его размер в байтах не кратен четырем, в хвост к нему дописывается недостающее количество нулевых символов. У меня получилась такая функция для поиска ресурса типа RC_DATA по заданному имени

#ifndef WIN32
HRSRC FindResource(HMODULE hModule, const char* name, const char* type)
{
    RESOURCEHEADER *res = (RESOURCEHEADER*)hModule;
    char *resAddr = (char*)hModule;
    DWORD bytes = 0;
    WORD  offset = 0;
    HRSRC  hResource = NULL;

    while ( resAddr + offset < &_binary_Resources_res_end )
    {
        res = (RESOURCEHEADER*)(resAddr + offset);
        char resName[6] = "";
        if (res->TypeOfNAME == 0xffff)
        {
            snprintf(resName, 6, "%d", res->NAME);
        }
        else
            throw ModuleResourceException("invalid module resource name");

        if (strncmp(resName, name, strlen(name)) == 0)
        {
            hResource = res;
            break;
        }

        DWORD resSize = sizeof(RESOURCEHEADER) + res->DataSize + offset;
        resAddr += resSize;
        bytes += resSize;
        offset = 4 - bytes % 4;
        if (offset == 4)
            offset = 0;
    }
    return hResource;
}
#endif 

Теперь функция FindResource вернет нам адрес начала искомого ресурса, который будем использовать далее

HRSRC hResource = FindResource(hModule, name, RT_RCDATA);

#ifdef WIN32
    HGLOBAL memory = LoadResource(hModule, hResource);
    size_t  memorySize = SizeOfResource(hModule, hResource);
#else
    HGLOBAL memory = (char*)hResource + sizeof(RESOURCEHEADER);
    size_t  memorySize = ((RESOURCEHEADER*)hResource)->DataSize;
#endif 

И более ничто не мешает прочитать содержимое памяти в свою переменную

std::string myResource;

#ifdef WIN32
    memcpy(&myResource[0], (const char*)LockResource(memory), memorySize);
    FreeResource(memory);
#else
    memcpy(&myResource[0], (const char*)memory, memorySize);
#endif 

Resource::~Resource()

Безусловно, рассмотренный пример использования в Linux привычных для Windows файлов определения ресурсов довольно примитивен, поскольку "заточен" только под один тип данных (RT_RCDATA). Однако вполне представляет собой основу детской пирамидки с уже одетым на нее небольшим колечком, поверх которого можно набросать еще много всего.