带有 GCC 的 C/C++:静态地将资源文件添加到可执行文件/库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4864866/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C/C++ with GCC: Statically add resource files to executable/library
提问by Atmocreations
Does anybody have an idea how to statically compile any resource file right into the executable or the shared library file using GCC?
有人知道如何使用 GCC 将任何资源文件静态编译为可执行文件或共享库文件吗?
For example I'd like add image files that never change (and if they do, I'd have to replace the file anyway) and wouldn't want them to lie around in the file system.
例如,我想添加永远不会改变的图像文件(如果他们这样做了,无论如何我都必须替换该文件)并且不希望它们位于文件系统中。
If this is possible (and I think it is because Visual C++ for Windows can do this, too), how can I load the files which are stored in the own binary? Does the executable parse itself, find the file and extract the data out of it?
如果这是可能的(我认为这是因为 Visual C++ for Windows 也可以这样做),我如何加载存储在自己的二进制文件中的文件?可执行文件是否解析自身,找到文件并从中提取数据?
Maybe there's an option for GCC I haven't seen yet. Using search engines didn't really spit out the right stuff.
也许我还没有看到 GCC 的选项。使用搜索引擎并没有真正吐出正确的东西。
I would need this to work for shared libraries and normal ELF-executables.
我需要它来为共享库和普通 ELF 可执行文件工作。
Any help is appreciated
任何帮助表示赞赏
采纳答案by Flexo
With imagemagick:
使用imagemagick:
convert file.png data.h
Gives something like:
给出类似的东西:
/*
data.h (PNM).
*/
static unsigned char
MagickImage[] =
{
0x50, 0x36, 0x0A, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20,
0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4D, 0x50, 0x0A, 0x32, 0x37,
0x37, 0x20, 0x31, 0x36, 0x32, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
....
For compatibility with other code you can then use either fmemopen
to get a "regular" FILE *
object, or alternatively std::stringstream
to make an iostream
. std::stringstream
is not great for this though and you can of course just use a pointer anywhere you can use an iterator.
为了与其他代码兼容,您可以使用fmemopen
获取“常规”FILE *
对象,或者std::stringstream
创建一个iostream
. std::stringstream
但这不是很好,您当然可以在任何可以使用迭代器的地方使用指针。
If you're using this with automake don't forget to set BUILT_SOURCESappropriately.
如果您在 automake 中使用它,请不要忘记适当地设置 BUILT_SOURCES。
The nice thing about doing it this way is:
这样做的好处是:
- You get text out, so it can be in version control and patches sensibly
- It is portable and well defined on every platform
- 你得到文本,所以它可以在版本控制和补丁中明智地
- 它在每个平台上都具有便携性和良好定义
回答by Nordic Mainframe
You can put all your resources into a ZIP file and append that to the end of the executable file:
您可以将所有资源放入一个 ZIP 文件并将其附加到可执行文件的末尾:
g++ foo.c -o foo0
zip -r resources.zip resources/
cat foo0 resources.zip >foo
This works, because a) Most executable image formats don't care if there's extra data behind the image and b) zip stores the file signature at the end of the zip file. This means, your executable is a regular zip file after this (except for your upfront executable, which zip can handle), which can be opened and read with libzip.
这是有效的,因为 a) 大多数可执行图像格式不关心图像后面是否有额外的数据 b) zip 将文件签名存储在 zip 文件的末尾。这意味着,您的可执行文件在此之后是一个常规的 zip 文件(除了您的前期可执行文件,zip 可以处理),可以使用 libzip 打开和读取。
回答by ndim
UpdateI have grown to prefer the control John Ripley's assembly .incbin
based solutionoffers and now use a variant on that.
更新我越来越喜欢John Ripley 的.incbin
基于程序集的解决方案提供的控制,现在使用它的一个变体。
I have used objcopy (GNU binutils) to link the binary data from a file foo-data.bin into the data section of the executable:
我使用 objcopy (GNU binutils) 将二进制数据从文件 foo-data.bin 链接到可执行文件的数据部分:
objcopy -B i386 -I binary -O elf32-i386 foo-data.bin foo-data.o
This gives you a foo-data.o
object file which you can link into your executable. The C interface looks something like
这为您提供了一个foo-data.o
目标文件,您可以将其链接到您的可执行文件中。C 接口看起来像
/** created from binary via objcopy */
extern uint8_t foo_data[] asm("_binary_foo_data_bin_start");
extern uint8_t foo_data_size[] asm("_binary_foo_data_bin_size");
extern uint8_t foo_data_end[] asm("_binary_foo_data_bin_end");
so you can do stuff like
所以你可以做类似的事情
for (uint8_t *byte=foo_data; byte<foo_data_end; ++byte) {
transmit_single_byte(*byte);
}
or
或者
size_t foo_size = (size_t)((void *)foo_data_size);
void *foo_copy = malloc(foo_size);
assert(foo_copy);
memcpy(foo_copy, foo_data, foo_size);
If your target architecture has special constraints as to where constant and variable data is stored, or you want to store that data in the .text
segment to make it fit into the same memory type as your program code, you can play with the objcopy
parameters some more.
如果您的目标架构对常量和变量数据的存储位置有特殊限制,或者您希望将该数据存储在.text
段中以使其适合与程序代码相同的内存类型,则可以objcopy
更多地使用参数。
回答by John Ripley
If you want control over the exact symbol name and placement of resources, you can use (or script) the GNU assembler (not really part of gcc) to import whole binary files. Try this:
如果您想控制资源的确切符号名称和位置,您可以使用(或脚本)GNU 汇编程序(实际上不是 gcc 的一部分)来导入整个二进制文件。尝试这个:
Assembly (x86/arm):
组装(x86/臂):
.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:
C:
#include <stdio.h>
extern char thing[];
extern unsigned thing_size;
int main() {
printf("%p %u\n", thing, thing_size);
return 0;
}
Whatever you use, it's probably best to make a script to generate all the resources, and have nice/uniform symbol names for everything.
无论您使用什么,最好编写一个脚本来生成所有资源,并为所有内容提供漂亮/统一的符号名称。
回答by Hazok
From http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:
来自http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967:
I recently had the need to embed a file in an executable. Since I'm working at the command line with gcc, et al and not with a fancy RAD tool that makes it all happen magically it wasn't immediately obvious to me how to make this happen. A bit of searching on the net found a hack to essentially cat it onto the end of the executable and then decipher where it was based on a bunch of information I didn't want to know about. Seemed like there ought to be a better way...
我最近需要在可执行文件中嵌入一个文件。由于我使用 gcc 等在命令行上工作,而不是使用让这一切神奇地发生的花哨的 RAD 工具,因此对我来说如何做到这一点并不是很明显。在网上进行了一些搜索,发现了一个黑客,基本上是将它放在可执行文件的末尾,然后根据一堆我不想知道的信息破译它的位置。似乎应该有更好的方法......
And there is, it's objcopy to the rescue. objcopy converts object files or executables from one format to another. One of the formats it understands is "binary", which is basicly any file that's not in one of the other formats that it understands. So you've probably envisioned the idea: convert the file that we want to embed into an object file, then it can simply be linked in with the rest of our code.
还有,它是 objcopy 来救援。objcopy 将目标文件或可执行文件从一种格式转换为另一种格式。它理解的一种格式是“二进制”,它基本上是任何不属于它理解的其他格式之一的文件。所以你可能已经想到了这个想法:将我们想要嵌入的文件转换为目标文件,然后它可以简单地与我们的其余代码链接。
Let's say we have a file name data.txt that we want to embed in our executable:
假设我们有一个文件名 data.txt,我们想要嵌入到我们的可执行文件中:
# cat data.txt
Hello world
To convert this into an object file that we can link with our program we just use objcopy to produce a ".o" file:
要将其转换为我们可以与我们的程序链接的目标文件,我们只需使用 objcopy 生成一个“.o”文件:
# objcopy --input binary \
--output elf32-i386 \
--binary-architecture i386 data.txt data.o
This tells objcopy that our input file is in the "binary" format, that our output file should be in the "elf32-i386" format (object files on the x86). The --binary-architecture option tells objcopy that the output file is meant to "run" on an x86. This is needed so that ld will accept the file for linking with other files for the x86. One would think that specifying the output format as "elf32-i386" would imply this, but it does not.
这告诉 objcopy 我们的输入文件是“二进制”格式,我们的输出文件应该是“elf32-i386”格式(x86 上的目标文件)。--binary-architecture 选项告诉 objcopy 输出文件打算在 x86 上“运行”。这是必需的,以便 ld 将接受用于与 x86 的其他文件链接的文件。有人会认为将输出格式指定为“elf32-i386”会暗示这一点,但事实并非如此。
Now that we have an object file we only need to include it when we run the linker:
现在我们有了一个目标文件,我们只需要在运行链接器时包含它:
# gcc main.c data.o
When we run the result we get the prayed for output:
当我们运行结果时,我们得到了祈祷的输出:
# ./a.out
Hello world
Of course, I haven't told the whole story yet, nor shown you main.c. When objcopy does the above conversion it adds some "linker" symbols to the converted object file:
当然,我还没有讲完整个故事,也没有给你看main.c。当 objcopy 进行上述转换时,它会向转换后的目标文件添加一些“链接器”符号:
_binary_data_txt_start
_binary_data_txt_end
After linking, these symbols specify the start and end of the embedded file. The symbol names are formed by prepending binaryand appending _start or _end to the file name. If the file name contains any characters that would be invalid in a symbol name they are converted to underscores (eg data.txt becomes data_txt). If you get unresolved names when linking using these symbols, do a hexdump -C on the object file and look at the end of the dump for the names that objcopy chose.
链接后,这些符号指定嵌入文件的开始和结束。符号名称是通过预先添加二进制文件并将 _start 或 _end 附加到文件名来形成的。如果文件名包含任何在符号名称中无效的字符,它们将被转换为下划线(例如 data.txt 变为 data_txt)。如果在使用这些符号进行链接时得到未解析的名称,请在目标文件上执行 hexdump -C 并查看转储的末尾是否有 objcopy 选择的名称。
The code to actually use the embedded file should now be reasonably obvious:
实际使用嵌入文件的代码现在应该相当明显:
#include <stdio.h>
extern char _binary_data_txt_start;
extern char _binary_data_txt_end;
main()
{
char* p = &_binary_data_txt_start;
while ( p != &_binary_data_txt_end ) putchar(*p++);
}
One important and subtle thing to note is that the symbols added to the object file aren't "variables". They don't contain any data, rather, their address is their value. I declare them as type char because it's convenient for this example: the embedded data is character data. However, you could declare them as anything, as int if the data is an array of integers, or as struct foo_bar_t if the data were any array of foo bars. If the embedded data is not uniform, then char is probably the most convenient: take its address and cast the pointer to the proper type as you traverse the data.
需要注意的一件重要而微妙的事情是添加到目标文件中的符号不是“变量”。它们不包含任何数据,相反,它们的地址就是它们的价值。我将它们声明为 char 类型,因为它对于这个例子很方便:嵌入的数据是字符数据。但是,您可以将它们声明为任何内容,如果数据是整数数组,则声明为 int,如果数据是任何 foo 条数组,则声明为 struct foo_bar_t。如果嵌入的数据不统一,那么 char 可能是最方便的:在遍历数据时获取其地址并将指针转换为正确的类型。
回答by Simon
You can embed binary files in executable using ld
linker.
For example, if you have file foo.bar
then you can embed it in executable adding the following commands to ld
您可以使用ld
链接器在可执行文件中嵌入二进制文件。例如,如果你有文件,foo.bar
那么你可以将它嵌入到可执行文件中,添加以下命令ld
--format=binary foo.bar --format=default
If you are invoking ld
thru gcc
then you will need to add -Wl
如果您正在调用ld
直通gcc
,那么你将需要添加-Wl
-Wl,--format=binary -Wl,foo.bar -Wl,--format=default
Here --format=binary
tells the linker that the following file is binary and --format=default
switches back to default input format (this is usefull if you will specify other input files after foo.bar
).
Here--format=binary
告诉链接器以下文件是二进制文件并--format=default
切换回默认输入格式(如果您在 之后指定其他输入文件,这将很有用foo.bar
)。
Then you can access content of your file from code:
然后您可以从代码访问文件的内容:
extern uint8_t data[] asm("_binary_foo_bar_start");
extern uint8_t data_end[] asm("_binary_foo_bar_end");
There is also symbol named "_binary_foo_bar_size"
. I think it is of type uintptr_t
but i didn't check it.
还有一个名为 的符号"_binary_foo_bar_size"
。我认为它是类型,uintptr_t
但我没有检查它。
回答by user2794512
Reading all post here and in Internet I have made a conclusion that there is no tool for resources, which is :
阅读此处和互联网上的所有帖子,我得出结论,没有资源工具,即:
1) Easy to use in code.
1)易于在代码中使用。
2) Automated (to be easy included in cmake/make).
2) 自动化(易于包含在 cmake/make 中)。
3) Cross-platform.
3)跨平台。
I have decided to write the tool by myself. The code is available here. https://github.com/orex/cpp_rsc
我决定自己写这个工具。该代码可在此处获得。 https://github.com/orex/cpp_rsc
To use it with cmake is very easy.
与 cmake 一起使用它非常容易。
You should add to your CMakeLists.txt file such code.
您应该将此类代码添加到 CMakeLists.txt 文件中。
file(DOWNLOAD https://raw.github.com/orex/cpp_rsc/master/cmake/modules/cpp_resource.cmake ${CMAKE_BINARY_DIR}/cmake/modules/cpp_resource.cmake)
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake/modules)
include(cpp_resource)
find_resource_compiler()
add_resource(pt_rsc) #Add target pt_rsc
link_resource_file(pt_rsc FILE <file_name1> VARIABLE <variable_name1> [TEXT]) #Adds resource files
link_resource_file(pt_rsc FILE <file_name2> VARIABLE <variable_name2> [TEXT])
...
#Get file to link and "resource.h" folder
#Unfortunately it is not possible with CMake add custom target in add_executable files list.
get_property(RSC_CPP_FILE TARGET pt_rsc PROPERTY _AR_SRC_FILE)
get_property(RSC_H_DIR TARGET pt_rsc PROPERTY _AR_H_DIR)
add_executable(<your_executable> <your_source_files> ${RSC_CPP_FILE})
The real example, using the approach can be downloaded here, https://bitbucket.org/orex/periodic_table
真实的例子,使用方法可以在这里下载, https://bitbucket.org/orex/periodic_table