在Linux可执行映像中嵌入资源的标准方法是什么?

27

通过Windows API(参见http://msdn.microsoft.com/en-us/library/ms648008(v=VS.85).aspx),将二进制资源嵌入PE映像(EXE,DLL)非常容易。

那么在Linux中是否有类似的标准API呢?

或者也许有一些非正式的资源嵌入方法吗?

目标是将一些静态的二进制和/或文本数据嵌入可执行文件中,例如图片、HTML等,以便程序二进制分发简单,只需复制一个文件即可(假设所有库依赖都没问题)。

更新:

按照bdk的建议,我尝试了使用gcc mingw嵌入二进制数据中描述的解决方案,并且它对我有效。尽管存在一些值得注意的问题:我的项目(在Code::Blocks中)由多个C++文件组成,将二进制数据添加到任何对应的目标文件中会导致它们变得无用,打破了构建 - objdump -x会显示在嵌入后大部分符号已经消失(我没找到如何修复它)。为了解决这个问题,我向项目中添加了一个空的虚拟.CPP文件,目的是提供一个对象文件进行尝试,并编写了以下自定义构建步骤,以便该文件可以很好地工作(示例使用Code::Blocks宏)。

$compiler $options $includes -c $file -o $object
ld -Ur -b binary -o $object <binary payload path>

@noloader 他在问关于Linux的问题,.exe 是用于Windows系统的。你应该更加注意 :) - sashoalm
@sashoalm - 这个链接的问题与Windows PE无关。我更改了链接问题的标题,以便将“.exe”更改为“可执行文件”。这应该能让人们明白了。 - jww
4个回答

37

制作一个汇编文件,命名为blob.S:

    .global blob
    .global blob_size
    .section .rodata
blob:
    .incbin "blob.bin"
1:
blob_size:
    .int 1b - blob

使用gcc -c blob.S -o blob.o进行编译,现在你可以在C程序中通过以下方式访问blob:

extern uint8_t blob[];
extern int blob_size;

使用bin2c转换工具通常效果良好,但如果Blob很大,则incbin方案速度更快且使用的内存更少(编译时间)


非常好的方法。谢谢你。你有没有想过这个方法在GCC支持的各种平台上的可移植性? - 0xC0000022L
1
它应该是可移植的,这是Linux内核用于向映像添加initramfs文件系统的方式。 - Simon Edlund
这应该是被接受的答案。我想要更明确的一件事是,一个名为“blob.bin”的文件应该存在,并且它的内容是嵌入的内容。 - Todd Freed
https://github.com/graphitemaster/incbin/ - kervin

8

objcopy --add-section 允许您将任意文件作为一个节添加到ELF可执行文件中。(objcopy man page)。但是这只是解决了一半的问题,因为我还没有找到一种方法可以从C程序内部访问此数据,除非使用ELF库加载和解析ELF二进制文件。

编辑 附加信息:

如果您有一个编译好的名为MyProgram的程序和一个名为MyResource.dat的资源文件,您想将它嵌入到MyProgram中,您可以像这样使用objcopy命令:

objcopy MyProgram --add-section MyResource=MyResource.dat

现在,如果您使用命令 objdump -x MyProgram 查看程序,则会看到一个名为 MyResource 的部分,其中包含 MyResource.dat 的内容。该文件现在嵌入在可执行文件中。
现在的问题是,如何从程序内部访问数据。我的直觉告诉我,加载器应该将文件放置在某个位置的内存中,并且您应该能够获取指向它的指针,但是我不确定如何简单地实现这一点。理想情况下,我希望能够 dlopen 我的可执行文件并 dlsym 该部分,但是这样做行不通,因为它是一个部分而不是符号。
我所知道的唯一替代方法是从程序内部访问该部分是使用 libelf 库或类似库,这有点像用大锤敲钉子。您可以在应用程序中使用它来将自身作为 ELF 资源加载并检索部分。文档很少,但这里有一个示例。

http://em386.blogspot.com/2007/03/quick-libelf-guide.html

我希望有人能提供更简单的方法来访问--add-section中的数据。
编辑2:在我的研究中,我遇到了这个问题: 使用gcc mingw嵌入二进制代码块,它适用于gcc和mingw,展示了一种使用ld而不是objcopy添加数据并能够将其作为符号访问的方法。看起来很有前途。

你能否不将该部分添加到.o文件中,然后创建一个重定位符号。这样应该允许从另一个C或C++文件进行链接。 - doron
这似乎非常接近我想要的,您能否详细说明一下?ELF库是什么?它是否保证在现代Linux系统上存在?也许您可以添加一些参考资料? - Andrey
@Andrey- 当然,我已经编辑了我的答案,告诉你我所知道的一切。 - bdk
1
你太棒了!非常感谢你不遗余力地改进你的答案并添加所有重要细节。你“编辑2”的解决方案对我很有帮助。 - Andrey
1
你可以在objcopy命令中添加选项--set-section-flags MyResource=alloc,load --change-section-address MyResource=<address>,以便让链接器将MyResource部分加载到固定地址。确定一个合理的地址可能有些棘手。 - Chris Dodd
1
http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 - hookenz

4
当然。可以尝试使用Bin2Hex转换器之类的工具。将二进制数据转换为C++字符数组,然后将其嵌入到代码中作为一个常量变量。

谢谢提供链接,我之前见过这个解决方案(它被用于在 Motif 中表示图标)。但我不喜欢它——它会将 perl 引入我的构建过程中,更糟糕的是,它会在代码中引入一些十六进制噩梦。尽管如此,它仍然是一个解决方法,所以点赞 +1。 - Andrey
@Andrey,坦白地说,如果你真的想要,甚至只使用bash,也可以轻松实现这样的脚本,而不需要perl。在这里查看此处。关于hexmare-忽略它,就像忽略二进制数据文件内容一样。只需将其放入单独的.h文件中,并将脚本添加到构建过程中,就完成了。基本上这就是MSVC与他们的资源所做的一样。 - littleadv
2
我使用 xxd -i 进行此操作。 - Andrew Domaszek

1

关于makeself怎么样?

它可以从一个目录中制作一个tar归档文件,其中包含所有程序和资源文件,并将其打包成可执行的shell文件。 当用户运行可执行文件时,它会提取文件并运行任意命令(可以是程序的主要可执行文件)。 缺点是每次用户启动可执行文件时,都需要先加载/提取文件,然后才能启动真正的程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接