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

qf9go6mv  于 2022-11-13  发布在  其他
关注(0)|答案(6)|浏览(215)

我正在寻找一种方法,可以在GCC编译的C/C++应用程序中轻松地嵌入任何外部二进制数据。
我想做的一个很好的例子是处理着色器代码-我可以只把它保存在像const char* shader = "source here";这样的源文件中,但这是非常不切实际的。
我希望编译器能帮我完成:在编译(链接阶段)时,读取文件“foo.bar“,并将其内容链接到我的程序,这样我就可以从代码中以二进制数据的形式访问内容。
对于我想作为单个.exe文件分发的小应用程序可能很有用。
GCC是否支持类似的功能?

dsf9zpds

dsf9zpds1#

有几种可能性:

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;
}

更新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)

这是一个变通办法,但它工作得很好,也不是太难看。

4jb9z9bj

4jb9z9bj2#

除了前面提到的建议,在linux下,您可以使用十六进制转储工具xxd,它有一个生成C头文件的特性:

xxd -i mybinary > myheader.h
wooyq4lh

wooyq4lh3#

.incbin GAS directive可以用来完成这个任务。下面是一个完全免费的许可库:
https://github.com/graphitemaster/incbin
incbin方法是这样的:你有一个thing.s汇编文件,用gcc -c 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);   
}
holgip5t

holgip5t4#

对于C23,现在有预处理器指令#embed,它不需要使用外部工具就能达到你想要的效果。参见6.10.3.1的C23标准(这里有一个最新working draft的链接)。这里是good blog post关于#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++的等效指令。

mxg2im7a

mxg2im7a5#

如果我想把静态数据嵌入到一个可执行文件中,我会把它打包成一个.lib/.a文件或者一个头文件,作为一个无符号字符数组。如果你正在寻找一种可移植的方法,我已经创建了一个命令行工具,它实际上可以同时执行here。你所要做的就是列出文件,并选择选项-l 64以输出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}
e7arh2l6

e7arh2l66#

您可以在头文件中执行此操作:

#ifndef SHADER_SRC_HPP
#define SHADER_SRC_HPP
const char* shader= "

//source

";
#endif

把它也包括进去。
另一种方法是读取着色器文件。

相关问题