我正在寻找一种方法来轻松地将任何外部二进制数据嵌入到由 GCC 编译的 C/C++ 应用程序中。
我想做的一个很好的例子是处理着色器代码 - 我可以将其保留在像
const char* shader = "source here";
这样的源文件中,但这非常不切实际。
我希望编译器为我做这件事:在编译(链接阶段)时,读取文件“foo.bar”并将其内容链接到我的程序,以便我能够以二进制数据形式访问内容代码。
对于我想作为单个 .exe 文件分发的小型应用程序可能很有用。
GCC 支持这样的东西吗?
有几种可能性:
使用 ld 的功能将任何文件转换为对象(使用 gcc mingw 嵌入二进制 blob):
ld -r -b binary -o binary.o foo.bar # then link in binary.o
bin2c
/
bin2h
实用程序将任何文件转换为字节数组(在代码中嵌入图像,而不使用资源部分或外部图像)
ld -r -b binary
使用绑定到可执行文件中的数据:
#include <stdio.h>
// a file named foo.bar with some example text is 'imported' into
// an object file using the following command:
//
// ld -r -b binary -o foo.bar.o foo.bar
//
// That creates an bject file named "foo.bar.o" with the following
// symbols:
//
// _binary_foo_bar_start
// _binary_foo_bar_end
// _binary_foo_bar_size
//
// Note that the symbols are addresses (so for example, to get the
// size value, you have to get the address of the _binary_foo_bar_size
// symbol).
//
// In my example, foo.bar is a simple text file, and this program will
// dump the contents of that file which has been linked in by specifying
// foo.bar.o as an object file input to the linker when the progrma is built
extern char _binary_foo_bar_start[];
extern char _binary_foo_bar_end[];
int main(void)
{
printf( "address of start: %p\n", &_binary_foo_bar_start);
printf( "address of end: %p\n", &_binary_foo_bar_end);
for (char* p = _binary_foo_bar_start; p != _binary_foo_bar_end; ++p) {
putchar( *p);
}
return 0;
}
display (unsigned int)&_binary_foo_bar_size
显示文本资源的正确大小。但是将其分配给变量总是给出错误的值。我可以通过以下方式解决这个问题:
unsigned int iSize = (unsigned int)(&_binary_foo_bar_end - &_binary_foo_bar_start)
这是一种解决方法,但效果很好,而且不太难看。
xxd -i mybinary > myheader.h
GAS 指令可用于此任务。这是一个完全免费的许可库:
https://github.com/graphitemaster/incbin
回顾一下。 incbin方法是这样的。 您有一个使用 gcc -c thing.s 编译的 thing.s 汇编文件
.section .rodata
.global thing
.type thing, @object
.align 4
thing:
.incbin "meh.bin"
thing_end:
.global thing_size
.type thing_size, @object
.align 4
thing_size:
.int thing_end - thing
在您的 c 或 cpp 代码中,您可以通过以下方式引用它:
extern const char thing[];
extern const char* thing_end;
extern int thing_size;
然后将生成的 .o 与其余编译单元链接。
感谢 @John Ripley 的回答:C/C++ with GCC:静态地将资源文件添加到可执行文件/库
但是上面的方法没有incbin给你的那么方便。 要使用 incbin 完成上述操作,您不需要编写任何汇编程序。 只需执行以下操作即可:
#include "incbin.h"
INCBIN(thing, "meh.bin");
int main(int argc, char* argv[])
{
// Now use thing
printf("thing=%p\n", gThingData);
printf("thing len=%d\n", gThingSize);
}
#embed
,它无需使用外部工具即可准确实现您正在寻找的目标。请参阅 C23 标准的 6.10.3.1(此处是最新工作草案的链接)。这是关于这一新功能背后的一位委员会成员的
#embed
历史的好博文。
以下是标准草案中的一个片段,展示了其用途:
#include <stddef.h>
void have_you_any_wool(const unsigned char*, size_t);
int main (int, char*[]) {
static const unsigned char baa_baa[] = {
#embed "black_sheep.ico"
};
have_you_any_wool(baa_baa, sizeof(baa_baa));
return 0;
}
目前不存在 C++ 的等效指令。
如果我想将静态数据嵌入到可执行文件中,我会将其作为无符号字符数组打包到 .lib/.a 文件或头文件中。如果您正在寻找一种便携式方法。 我创建了一个命令行工具,实际上可以在here完成这两个任务。您所要做的就是列出文件,然后选择选项 -l64 来输出 64 位库文件以及包含指向每个数据的所有指针的标头。
您还可以探索更多选项。例如,这个选项:
>BinPack image.png -j -hx
将把 image.png 的数据输出到头文件中,因为十六进制和行将根据 -j 选项进行调整。
const unsigned char BP_icon[] = {
0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0x00,0x00,0x01,0xed,0x00,0x00,0x01,0xed,0x08,0x06,0x00,0x00,0x00,0x34,0xb4,0x26,
0xfb,0x00,0x00,0x02,0xf1,0x7a,0x54,0x58,0x74,0x52,0x61,0x77,0x20,0x70,0x72,0x6f,
0x66,0x69,0x6c,0x65,0x20,0x74,0x79,0x70,0x65,0x20,0x65,0x78,0x69,0x66,0x00,0x00,
0x78,0xda,0xed,0x96,0x5d,0x92,0xe3,0x2a,0x0c,0x85,0xdf,0x59,0xc5,0x2c,0x01,0x49,
0x08,0x89,0xe5,0x60,0x7e,0xaa,0xee,0x0e,0xee,0xf2,0xef,0x01,0x3b,0x9e,0x4e,0xba,
0xbb,0x6a,0xa6,0x66,0x5e,0x6e,0x55,0x4c,0x8c,0x88,0x0c,0x07,0xd0,0x27,0x93,0x84,
0xf1,0xef,0x3f,0x33,0xfc,0xc0,0x45,0xc5,0x52,0x48,0x6a,0x9e,0x4b,0xce,0x11,0x57,
0x2a,0xa9,0x70,0x45,0xc3,0xe3,0x79,0xd5,0x5d,0x53,0x4c,0xbb,0xde,0xd7,0xe8,0x57,
0x8b,0x9e,0xfd,0xe1,0x7e,0xc0,0xb0,0x02,0x2b,0xe7,0x03,0xcf,0xa7,0xa5,0x87,0xff,
0x1a,0xf0,0xb0,0x54,0xd1,0xd2,0x0f,0x42,0xde,0xae,0x07,0xc7,0xf3,0x83,0x92,0x4e,
0xcb,0xfe,0x22,0xc4,0xa7,0x91,0xb5,0xa2,0xd5,0xee,0x97,0x50,0xb9,0x84,0x84,0xcf,
0x07,0x74,0x09,0xd4,0x73,0x5b,0x31,0x17,0xb7,0x8f,0x5b,0x38,0xc6,0x69,0xaf}
我想分享一个完整的 C++ 程序示例,希望它对某人有所帮助。
生成文件
data_base := $(wildcard *.dat)
all: main
main: main.o $(data_base:.dat=.o)
$(CXX) -z noexecstack $(LDFLAGS) -o $@ $^ $(LDLIBS)
%.o: %.dat
ld -r -b binary -o $@ $<
clean:
rm -f *.o
main.cpp
#include <iostream>
#include <sstream>
extern char _binary_table_dat_start[];
extern char _binary_table_dat_end[];
int main (int argc, char* argv[]) {
std::cout << "Address of start" << &_binary_table_dat_start << std::endl;
std::cout << "Address of end" << &_binary_table_dat_end << std::endl;
std::istringstream ss;
ss.rdbuf()->pubsetbuf(_binary_table_dat_start, _binary_table_dat_end - _binary_table_dat_start - 1); // Exclude null terminator
std::size_t i = 0;
while (!ss.eof()) {
std::string s;
std::getline(ss, s);
std::cout << "Line" << ++i << ": " << s << std::endl;
}
std::cout << "Total number of lines " << i << std::endl;
return 0;
}
表.dat
random text....
您可以在头文件中执行此操作:
#ifndef SHADER_SRC_HPP
#define SHADER_SRC_HPP
const char* shader= "
//source
";
#endif
然后将其包括在内。
另一种方法是读取着色器文件。