GCC 工具链生成并嵌入资源文件
简介
众所周知,资源文件对程序的运行具有不可或缺的作用。然而,想要实现跨平台的资源文件利用,我们就不得不摒弃 Win32 资源文件。本文介绍了利用 (MinGW) GCC 工具链生成并嵌入资源文件的一种方法。
Step 1:利用 ld 生成资源对象文件
在这一步,我们将利用 链接器(ld) 生成我们所需要的资源对象文件(.o)。
准备资源文件
资源文件可以是任意的格式,包括但不限于图片,文本;以下用两个文本文档为例:
文件:a.txt
dawdawwdwkjagcbsfgbcfgfjbkajkaadgad
文件:b.txt
Hello,txt1!
生成资源对象文件
在包含该资源文件的文件夹路径下打开 CMD,执行指令:
ld -r -b binary a.txt b.txt -o res.o
提示
指令模板为:ld -r -b binary {文件1} {文件2} -o {输出文件路径}
;通配符可用。
这样,我们就得到了资源对象文件res.o
。
检查资源对象文件符号(可选)
为了确保编译的成功,我们可以先行检查资源文件暴露的符号,得出符号命名规律。
在原先的 CMD 中执行指令
objdump -x res.o
我们便得到了一大段输出。找到输出的最后,有类似的表格:
SYMBOL TABLE:
[ 0](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000023 _binary_a_txt_size
[ 1](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x000000000000000b _binary_b_txt_size
[ 2](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x000000000000002e _binary_b_txt_end
[ 3](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000023 _binary_b_txt_start
[ 4](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000023 _binary_a_txt_end
[ 5](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000000000000000 _binary_a_txt_start
这表示,我们的文本文档嵌入了资源对象文件之中,并生成了几个符号。可以总结得出:
::: theorem 符号的命名规律
_binary_文件名_start
_binary_文件名_end
_binary_文件名_size
注意:文件名中.
转为_
。
Step 2:编写测试代码
符号的引入
我们的最终目标是将资源文件嵌入可执行文件中并在程序中使用。为了实现这一目标,我们需要在源码文件中引入符号,例如:
extern const char _binary_a_txt_start[], _binary_a_txt_end[];
extern const char _binary_b_txt_start[], _binary_b_txt_end[];
使用extern
关键字,并复制符号名称,我们就可以将符号引入源码了。
特别注意
符号是一个地址,应定义为 const char[] 类型。
符号的使用
例如:
int main(){
size_t a_len = _binary_a_txt_end - _binary_a_txt_start;
size_t b_len = _binary_b_txt_end - _binary_b_txt_start;
printf("a.txt len=%u,content= %.*s\n", a_len, a_len, _binary_a_txt_start );
printf("b.txt len=%u,content= %.*s\n", b_len, b_len, _binary_b_txt_start );
return 0;
}
保存为test.cpp,然后编译:
g++ res.o test.cpp -o test.exe
这样,我们就得到了可执行文件。执行的结果应如下所示:
a.txt len=35,content= dawdawwdwkjagcbsfgbcfgfjbkajkaadgad
b.txt len=11,content= Hello,txt1!
Step 3:封装起来,便于使用
这里利用 GCC 的宏定义展开特性,编写示例封装代码:
文件:customResource.hpp
#pragma once
#include <cstdint>
#define RESDEF(name) extern const char _binary_##name##_start[], _binary_##name##_end[]
#define RES(name) _binary_##name##_start, _binary_##name##_end
namespace Utils {
class Resource {
public:
Resource(const char* begin, const char* end)
: _begin(begin)
, _end(end)
{
}
~Resource() { }
const char* begin() { return this->_begin; }
const char* end() { return this->_end; }
size_t length() { return this->_end - this->_begin; }
const uint8_t* data() { return (const uint8_t*)this->_begin; }
private:
const char* _begin = nullptr;
const char* _end = nullptr;
};
}
文件:main.cpp
#include <cstdio>
#include "customResource.hpp"
using namespace Utils;
RESDEF(a_txt);
RESDEF(b_txt);
Resource a(RES(a_txt));
Resource b(RES(b_txt));
int main()
{
printf("a.txt len=%u,content= %.*s\n", a.length(), a.length() , a.begin());
printf("b.txt len=%u,content= %.*s\n", b.length(), b.length() , b.begin());
return 0;
}
编译运行即可。
使用提示
先使用RESDEF(文件名)
来引入符号,然后使用Utils::Resource(RES(文件名))
来实例化对象
后记
本文参考了下列博文:
并经 Windows 11 + MinGW-w64 11.2.0 Seh 测试通过再行编写而成。在此对原博文博主们表示感谢!