使用 GCC 在可执行文件中嵌入资源
Posted
技术标签:
【中文标题】使用 GCC 在可执行文件中嵌入资源【英文标题】:Embedding resources in executable using GCC 【发布时间】:2011-05-08 17:13:18 【问题描述】:我正在寻找一种将任何外部二进制数据轻松嵌入到由 GCC 编译的 C/C++ 应用程序的方法。
我想做的一个很好的例子是处理着色器代码 - 我可以将它保存在像 const char* shader = "source here";
这样的源文件中,但这是非常不切实际的。
我希望编译器为我执行此操作:在编译(链接阶段)时,读取文件“foo.bar”并将其内容链接到我的程序,以便我能够以二进制形式访问内容代码中的数据。
对于我想作为单个 .exe 文件分发的小型应用程序可能很有用。
GCC 是否支持这样的东西?
【问题讨论】:
C/C++ with GCC: Statically add resource files to executable/library的可能重复 【参考方案1】:有几种可能性:
使用 ld 的功能将任何文件转换为对象 (Embedding binary blobs using gcc mingw):
ld -r -b binary -o binary.o foo.bar # then link in binary.o
使用 bin2c
/bin2h
实用程序将任何文件转换为字节数组 (Embed image in code, without using resource section or external images)
更新:这是一个更完整的示例,说明如何使用 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;
更新 2 - 获取资源大小:我无法正确读取 _binary_foo_bar_size。在运行时,gdb 使用display (unsigned int)&_binary_foo_bar_size
向我显示文本资源的正确大小。但是将其分配给变量总是会给出错误的值。我可以通过以下方式解决这个问题:
unsigned int iSize = (unsigned int)(&_binary_foo_bar_end - &_binary_foo_bar_start)
这是一种解决方法,但效果很好,而且不太难看。
【讨论】:
@VJo:然后将 blob 视为文本。如果您需要像这样终止它,您可能需要做一些工作以确保文本末尾有一个'\0'
。可能需要进行一些试验。
@VJo: text is 二进制。 计算机上的一切都是二进制的。
@MSalters 回复:“文本是二进制的”。是的,但是,...在文本中,EOL 在不同系统上的处理方式可能不同。明确地将其称为二进制可防止此类问题。
@atlaste:您描述的是可写(“数据”)和可执行(“代码”)之间的区别。只读数据不需要任何方法。
你能告诉ld
为数据生成哪个符号名称吗?【参考方案2】:
除了前面提到的建议,在linux下可以使用hex dump工具xxd,它有一个生成C头文件的功能:
xxd -i mybinary > myheader.h
【讨论】:
我认为这个解决方案是最好的。它也是跨平台和跨编译器的支持。 这是真的,但它确实有一个缺点 - 生成的头文件比原始二进制文件大很多。这对最终编译结果没有影响,但作为构建过程的一部分可能是不可取的。 这个问题可以通过precompiled header来解决。【参考方案3】:.incbin
GAS directive 可用于此任务。这是一个完全免费的许可库:
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: Statically add resource files to executable/library
但是上面的不如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);
【讨论】:
我喜欢这种方法,因为它允许控制符号名称。【参考方案4】:您可以在头文件中执行此操作:
#ifndef SHADER_SRC_HPP
#define SHADER_SRC_HPP
const char* shader= "
//source
";
#endif
只包括那个。
其他方法是读取着色器文件。
【讨论】:
我认为 Kos 希望能够维护着色器源,而不必担心转义特殊字符(以及其他可能的问题)。 @VJo:不——从未使用过着色器。我正在接近这个问题,因为将驻留在外部文件中的任意数据嵌入到程序中。我当然可以接受,这对于着色器来说可能是一个更好的解决方案。 定义(而不是声明)全局变量的文件不应该是头文件,而是源模块。而且您的类型效率极低。改为const char shader[] = "source";
。
另外,我相信 C++ 不允许您以其他方式使用多行字符串文字,而不是在每行中单独打开和关闭 ""
引号或在每行末尾添加反斜杠线。更不用说在开发过程中将着色器作为独立文件提供的其他好处(至少是语法着色?)。
从 C++11 开始,您可以使用“原始字符串文字”,它看起来像 R"*( ... multiline text ... )*"
。您可以使用其他分隔符代替 *.以上是关于使用 GCC 在可执行文件中嵌入资源的主要内容,如果未能解决你的问题,请参考以下文章
Struts2无法在可执行的战争中初始化Dispatcher - 嵌入式Tomcat