如何在程序中包含数据对象文件(图像等)并访问符号?

cha*_*hma 7 c c++ assembly linker undefined-reference

我已经将几个资源文件转换为.obj文件objcopy,并将它们与我的程序源代码链接起来.我可以使用以下代码很好地访问程序中目标文件内的符号,但只能使用GCC/G ++(Cygwin):

extern uint8_t data[]   asm("_binary_Resources_0_png_start");
extern uint8_t size[]   asm("_binary_Resources_0_png_size");
extern uint8_t end[]    asm("_binary_Resources_0_png_end");
Run Code Online (Sandbox Code Playgroud)

该代码在Visual Studio中不起作用,可能是因为VS拥有自己的__asm命令.我希望.data通过链接它们将我的程序资源(图像,着色器等)包含在我的最终可执行文件部分中.

但是如何在VC++中访问目标文件中定义的符号?我尝试了extern uint8_t _binary_Resources_0_png_start[]extern "C" uint8_t _binary_Resources_0_png_start[]没有汇编命令,但我得到了未解决的符号链接错误.

小智 5

objcopy正如你所看到的那样,诀窍并不是一种全功能的嵌入资源的方式,根本不可移植.

Microsoft拥有自己的资源机制,因此如果您专门针对Windows,则可以使用Windows资源文件和RCDATA资源.

如果你想要一些完全可移植的东西,你唯一的选择是将文件格式化为C源代码,例如

const uint8_t my_binary[] = { 0x00, 0x01, ... }
Run Code Online (Sandbox Code Playgroud)

您可以直接为此编写自己的转换工具.

  • 一旦你拥有源代码生成器,你就可以动态生成C源代码,因此永远不需要将大的丑陋文件添加到源代码控制中.如果他们只是本地和短暂的,那真的没关系. (2认同)

Sch*_*eff 5

这可能是一种完全不同的方法,但它提供了一个相当简单但便携的解决方案:

我们使用一个小工具来加载二进制文件并将其输出为C(或C++源代码).实际上,我在XPM和GIMP中看到了类似的东西,但它可以用于任何二进制数据.

要在构建链这样的工具是不是很难在VS,更简单的makecmake也.

这样的工具可能如下所示:

#include <fstream>
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char **argv)
{
  if (argc < 2) {
    cerr << "Usage: " << argv[0] << " FILE [FILE...]" << endl;
    return -1;
  }
  for (size_t i = 1; i < argc; ++i) {
    fstream fIn(argv[i], ios::in | ios::binary);
    if (!fIn.good()) {
      cerr << "ERROR: Cannot open '" << argv[i] << "'!" << endl;
      continue;
    }
    // make name
    string name = argv[i];
    name = name.substr(0, name.find('.'));
    /// @todo more sophisticated name mangling?
    // print preface
    cout << "struct { const char *data; size_t size; } " << name << " = {" << endl
      << "  \"";
    // print data
    const char hex[] = "0123456789abcdef";
    unsigned char byte;
    enum { BytesPerLine = 16 };
    size_t n = 0;
    for (unsigned char byte; fIn.get((char&)byte); ++n) {
      if (n && !(n % BytesPerLine)) cout << "\"\n  \"";
      cout << "\\x" << hex[byte / 16] << hex[byte % 16];
    }
    // print size
    cout << "\",\n"
      "  " << n << "\n"
      "};" << endl;
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译和测试:

$ g++ -std=c++11 -o binToC binToC.cc

$ ./binToC
Usage: ./binToC FILE [FILE...]
Run Code Online (Sandbox Code Playgroud)

更多测试 fluffy_cat.png fluff_cat.png:

$ ./binToC fluffy_cat.png > fluffy_cat.inc

$ cat >fluffy_cat_test.cc <<'EOF'
> #include <fstream>
> 
> using namespace std;
> 
> #include "fluffy_cat.inc"
> 
> int main()
> {
>   ofstream fOut("fluffy_cat_test.png", ios::out | ios::binary);
>   fOut.write(fluffy_cat.data, fluffy_cat.size);
>   fOut.close();
>   return 0;
> }
> EOF

$ g++ -std=c++11 -o fluffy_cat_test fluffy_cat_test.cc

$ ./fluffy_cat_test

$ diff fluffy_cat.png fluffy_cat_test.png

$
Run Code Online (Sandbox Code Playgroud)

作为diff节目 - C源完全再现原始.

顺便说一句.我在回答SO时使用了相同的技术(类似的形式):在特定时间在qglwidget上绘制一个rect.


cha*_*hma 1

在解决和测试不同的事情之后,我回到了原来的方法(链接),它像魔术一样工作,以下是详细信息:

为了将数据包含在最终可执行文件的.data部分中,您需要首先将该数据文件(可以是任意二进制文件(任何东西!))转换为可链接文件格式,也称为目标文件。

该工具objcopy包含在 Windows 中并可通过或GNU Binutils访问,它获取文件并生成目标文件。objcopy 在生成目标文件之前需要了解两件事:输出文件格式和输出体系结构。为了确定这两件事,我使用该工具检查有效的可链接目标文件:CygwinMinGWobjdump

objdump -f main.o
Run Code Online (Sandbox Code Playgroud)

这给了我以下信息:

main.o:     file format pe-x86-64
architecture: i386:x86-64, flags 0x00000039:
HAS_RELOC, HAS_DEBUG, HAS_SYMS, HAS_LOCALS
start address 0x0000000000000000
Run Code Online (Sandbox Code Playgroud)

有了这些知识,现在我可以创建目标文件:

objcopy -I binary -O pe-x86-64 -B i386 data_file.data data_file_data.o
Run Code Online (Sandbox Code Playgroud)

为了处理大量文件,批处理文件可以派上用场。

然后,我只需将生成的目标文件与我的程序源链接在一起,并通过符号取消引用 objcopy 生成的指针,可以轻松地查询其名称:

objdump -t data_file_data.o
Run Code Online (Sandbox Code Playgroud)

结果是:

data_file_data.o:     file format pe-x86-64

SYMBOL TABLE:
[  0](sec  1)(fl 0x00)(ty  0)(scl  2) (nx 0) 0x0000000000000000 _binary_data_file_data_start
[  1](sec  1)(fl 0x00)(ty  0)(scl  2) (nx 0) 0x0000000000000006 _binary_data_file_data_end
[  2](sec -1)(fl 0x00)(ty  0)(scl  2) (nx 0) 0x0000000000000006 _binary_data_file_data_size
Run Code Online (Sandbox Code Playgroud)

实际上,以下代码适用于GCC/G++

extern uint8_t data[]   asm("_binary_data_file_data_start");
extern uint8_t end[]    asm("_binary_data_file_data_end");
Run Code Online (Sandbox Code Playgroud)

以及以下内容MSVC++

extern "C" uint8_t _binary_data_file_data_start[]; // Same name as symbol
extern "C" uint8_t _binary_data_file_data_end[];   // Same name as symbol
Run Code Online (Sandbox Code Playgroud)

每个文件的大小计算如下:

_binary_data_file_data_end - _binary_data_file_data_start
Run Code Online (Sandbox Code Playgroud)

例如,您可以将数据写回到文件中:

FILE* file;

file = fopen("data_file_reproduced.data", "wb");
fwrite(_binary_data_file_data_start,                               //Pointer to data
       1,                                                          //Write block size
       _binary_data_file_data_end - _binary_data_file_data_start,  //Data size
       file);

fclose(file);
Run Code Online (Sandbox Code Playgroud)