使用GCC在可执行文件中嵌入资源

Kos*_*Kos 49 c c++ resources gcc embedded-resource

我正在寻找一种方法来轻松地将任何外部二进制数据嵌入到由GCC编译的C/C++应用程序中.

我想做的一个很好的例子就是处理着色器代码 - 我可以把它保存在源文件中,const char* shader = "source here";但这是非常不切实际的.

我希望编译器为我做:在编译(链接阶段)时,读取文件"foo.bar"并将其内容链接到我的程序,以便我能够从内容中访问内容作为二进制数据码.

对于我想作为单个.exe文件分发的小型应用程序可能很有用.

GCC是否支持这样的事情?

Mic*_*urr 49

有几种可能性:


更新:这是一个更完整的示例,说明如何使用绑定到可执行文件中的数据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;
}
Run Code Online (Sandbox Code Playgroud)

更新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)
Run Code Online (Sandbox Code Playgroud)

这是一种解决方法,但它运作良好并且不太难看.

  • @VJo:text _is_ binary._Everything_在计算机上是二进制的. (5认同)
  • @VJo:然后将blob视为文本.你可能需要做一些工作,以确保在文本末尾有一个'\ 0',如果你需要它那样终止.一些实验可能是有序的. (3认同)
  • @atlaste:您所描述的是可写(“数据”)和可执行(“代码”)之间的区别。只读数据不需要这两种方法。 (3认同)
  • @MSalters 回复:“文本是二进制的”。是的,但是,...在文本中,EOL 在不同的系统上可能会有不同的处理方式。明确地将其称为二进制可以防止此类缺陷。 (2认同)
  • 你能告诉“ld”为数据生成哪个符号名称吗? (2认同)

Rio*_*iot 24

除了已经提到的建议,在linux下你可以使用十六进制转储工具xxd,它具有生成C头文件的功能:

xxd -i mybinary > myheader.h
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个解决方案是最好的.它也是跨平台和交叉编译器支持. (4认同)
  • 的确如此,但是它确实有一个缺点-生成的头文件比原始二进制文件大得多。这对最终的编译结果没有影响,但是在构建过程中可能是不可取的。 (2认同)
  • 这个问题可以通过使用**预编译头**来解决。 (2认同)

iro*_*owe 18

对于 C23,现在存在预处理器指令#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;
}
Run Code Online (Sandbox Code Playgroud)

目前不存在 C++ 的等效指令。

  • 该片段是从标准中逐字摘录的。请参阅此 pdf 的第 169 页:https://open-std.org/JTC1/SC22/WG14/www/docs/n3088.pdf 我们当然可以让委员会有幽默感吗? (2认同)

Mat*_*att 10

.incbinGAS指令可用于这项任务.这是一个完全免费的许可库,它包裹着它:

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
Run Code Online (Sandbox Code Playgroud)

在您的c或cpp代码中,您可以引用它:

extern const char thing[];
extern const char* thing_end;
extern int thing_size;
Run Code Online (Sandbox Code Playgroud)

那么你将结果.o与其余的编译单元链接起来.感谢@John Ripley及其答案:C/C++与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);   
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这种方法,因为它允许控制符号名称。 (3认同)