如何将文件打包进可执行程序并在运行时访问?
二进制文件嵌入可执行程序的完整方案
这种将资源嵌入可执行文件的做法非常普遍,比如嵌入式程序、轻量桌面工具、游戏小资源包等都会采用,避免部署时的文件依赖问题。以下分平台给出完整的构建+运行时方案,均兼容C++11及更早版本:
一、GNU/Linux + ELF 环境(重点)
构建阶段:用objcopy生成可链接目标文件
借助GNU binutils工具链的objcopy,直接将二进制文件转换为ELF目标文件,可直接参与链接:
- 将
foo.bin转换为foo.o:
# 64位系统 objcopy --input binary --output elf64-x86-64 --binary-architecture i386:x86-64 foo.bin foo.o # 32位系统替换为 # objcopy --input binary --output elf32-i386 --binary-architecture i386 foo.bin foo.o
该命令会自动生成三个全局符号:
_binary_foo_bin_start:嵌入数据的起始地址_binary_foo_bin_end:嵌入数据的结束地址_binary_foo_bin_size:嵌入数据的字节大小
- 编译时将
foo.o与源码一起链接:
g++ main.cpp foo.o -o my_program
运行时访问数据
在C代码中声明外部符号(用extern "C"避免C名字修饰):
#include <cstddef> // C++03替换为unsigned char,C++11可用std::byte typedef unsigned byte; extern "C" { extern const byte _binary_foo_bin_start[]; extern const byte _binary_foo_bin_end[]; extern const size_t _binary_foo_bin_size; } int main() { const byte* data_ptr = _binary_foo_bin_start; size_t data_size = _binary_foo_bin_size; // 示例:遍历数据 for (size_t i = 0; i < data_size; ++i) { // 处理data_ptr[i] } return 0; }
可选:从可执行文件提取数据
用objcopy反向导出:
objcopy --only-section .data my_program extracted_foo.bin
二、Windows 环境
构建阶段:用资源脚本嵌入
Windows可执行文件原生支持资源段,通过资源脚本实现嵌入:
- 创建
foo.rc资源脚本:
FOO_BIN RCDATA "foo.bin"
其中FOO_BIN是自定义资源ID,RCDATA表示原始数据类型。
- 编译资源脚本为
.res文件:
rc /v foo.rc
(Visual Studio项目中直接添加.rc文件即可,IDE会自动处理编译)
- 链接时将
.res与源码结合:
cl main.cpp foo.res /Fe:my_program.exe
运行时访问数据
通过Windows API读取资源:
#include <windows.h> #include <cstddef> int main() { HMODULE hModule = GetModuleHandle(NULL); // 查找资源 HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(FOO_BIN), RT_RCDATA); if (!hRes) { /* 错误处理 */ } // 加载资源 HGLOBAL hResData = LoadResource(hModule, hRes); if (!hResData) { /* 错误处理 */ } // 锁定资源获取数据指针 const unsigned char* data_ptr = static_cast<const unsigned char*>(LockResource(hResData)); size_t data_size = SizeofResource(hModule, hRes); // 使用数据 return 0; }
可选:从可执行文件提取数据
用Resource Hacker工具打开exe,找到RCDATA类型的FOO_BIN资源,直接导出即可。
三、macOS 环境
构建阶段:用ld的-sectcreate选项
针对Mach-O格式,通过链接器选项直接创建自定义节嵌入数据:
- 编译源码为目标文件:
clang++ -c main.cpp -o main.o
- 链接时嵌入
foo.bin:
clang++ main.o -o my_program -sectcreate __DATA __foo_bin foo.bin
其中__DATA是段名,__foo_bin是节名,可自定义。
运行时访问数据
用getsectdata函数获取节数据:
#include <mach-o/getsect.h> #include <cstddef> int main() { size_t data_size; const unsigned char* data_ptr = static_cast<const unsigned char*>( getsectdata("__DATA", "__foo_bin", &data_size) ); if (!data_ptr) { /* 错误处理 */ } // 使用数据 return 0; }
可选:从可执行文件提取数据
先用otool查看节的偏移和大小:
otool -l my_program | grep -A 10 __foo_bin
再用dd提取(替换示例中的偏移和大小):
dd if=my_program of=extracted_foo.bin skip=$((0x1234)) count=$((0x5678)) bs=1
通用注意事项
- 嵌入的数据默认处于只读段,安全性更高;若需可写,需调整工具参数(不推荐)。
- 大文件嵌入会显著增加可执行文件体积,需权衡资源大小与部署便利性。
- 跨平台场景可通过CMake等构建系统封装上述步骤,自动适配不同平台的嵌入逻辑。
内容的提问来源于stack exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

