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(或C++源代码).实际上,我在XPM和GIMP中看到了类似的东西,但它可以用于任何二进制数据.
要在构建链这样的工具是不是很难在VS,更简单的make和cmake也.
这样的工具可能如下所示:
#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)
$ ./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.
在解决和测试不同的事情之后,我回到了原来的方法(链接),它像魔术一样工作,以下是详细信息:
为了将数据包含在最终可执行文件的.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)