如何在Mac OS X上将二进制文件内容嵌入可执行文件中?

7
我的命令行程序的构建过程会生成一个二进制文件(超过500KB),目前必须通过argv中的路径引用该文件。我希望将此文件嵌入到可执行文件中。
在Linux上,似乎可以使用objcopy将二进制文件制作为对象文件:(参考链接)
objcopy --input binary --output elf32-i386 --binary-architecture i386 myfile.dat myfile.o

然而,OS X开发工具链不包括objcopy命令。除了安装binutils之外,还有哪些可能性?
我是通过Xcode构建我的项目,并使用自定义构建规则生成文件。
2个回答

9
在链接阶段,将参数 -sectcreate <segname> <sectname> <file> 传递给链接器。如果您通过调用编译器来驱动链接器,这是非常常见的,那么您需要将其作为 -Wl,-sectcreate,<segname>,<sectname>,<file> 进行传递。
您可以自己编写段和节名称。
在运行时,您可以使用getsectdata() 函数_dyld_get_image_vmaddr_slide() 来获取数据的指针。

getsectiondata是否有任何真相可以避免使用下划线函数的需求? - zneak
还有没有更好的方法获取 Mach header,而不是调用 _dyld_get_image_header? - zneak
你唯一能够依赖getsectiondata()的方式是它被记录在文档中,但似乎并没有。为什么你对“带下划线”的函数如此迷信?它们已经被记录并且完全安全可用。如果你想要可执行文件的Mach头,而不是任何动态库,并且你正在可执行文件中编写代码,你可以简单地引用&_mh_execute_header。请参阅/usr/include/mach-o/ldsyms.h。顺便说一句,你可以将索引0传递给dyld(3)函数(例如_dyld_get_image_vmaddr_slide())来引用主可执行文件。 - Ken Thomases
如果我们要深入研究文档中记录和未记录的内容,那么它并没有说明getsectdata不会调整ASLR滑动值,所以也许我应该担心苹果最终会修复它,导致我的代码出现问题。我相信我们都怀疑他们不会这样做,但我也怀疑getsectiondata不会消失,正是因为这个原因。无论如何,从任何角度来看,这都感觉像使用dlsym获取全局变量句柄一样hacky和奇怪(因为本质上就是这样),所以我可能会选择让它感觉最不糟糕的函数。 - zneak
最后一步是似乎没有方便的方法来自动链接输出文件,无论它们的名称如何。如果我重命名文件或添加新文件,我还需要去更改链接器标志。如果我找到了解决办法,我会接受答案,但如果出现其他情况,我可能会保持开放态度。 - zneak
getsectdata() 的 man 页面(我没有链接到它,因为它似乎不在线上)说:“Getsectdata 与 getsectdatafromheader 相同,只是其第一个参数为链接编辑器定义的符号 _mh_execute_header。” 同一 man 页面还说,您必须将 getsectdatafromheader() 的结果添加滑动值。虽然我猜它只适用于动态库,但可执行文件也会被 ASLR 滑动,除非它们没有构建为位置无关。 - Ken Thomases

3

这篇关于objcopy的问题所证明的那样,将二进制文件包含到可执行文件中的另一种方法是使用.incbin汇编指令。相对于objcopy,这种解决方案有两个主要优点:开发者可以控制符号名称(objcopy似乎有一个固定的命名规则),而且不需要用到objcopy

相比基于链接器的-sectcreate解决方案,此解决方案也有优势。它跨平台,并且访问数据要简单得多。

我正在使用这个Xcode构建规则脚本来生成要包含的文件和带有.incbin指令的汇编文件:

my_generation_tool -o $DERIVED_FILE_DIR/$INPUT_FILE_NAME.out $INPUT_FILE_PATH

export AS_PATH=$DERIVED_FILE_DIR/$INPUT_FILE_NAME.out.s

echo "\t.global _data_start_$INPUT_FILE_BASE" > $AS_PATH
echo "\t.global _data_end_$INPUT_FILE_BASE" >> $AS_PATH
echo "_data_start_ $INPUT_FILE_BASE:" >> $AS_PATH
echo "\t.incbin \"$INPUT_FILE_NAME.out\"" >> $AS_PATH
echo "_data_end_$INPUT_FILE_BASE:" >> $AS_PATH

然后,给定一个被这个规则处理的文件 "somefile.gen",汇编代码将如下所示:

    .global _data_start_somefile
    .global _data_end_somefile
_data_start_somefile:
    .incbin "somefile.gen.out"
_data_end_somefile:

数据可以使用data_start_somefiledata_end_somefile符号在C中访问(macOS链接器会在C名称前加上虚假的_,这就是为什么汇编文件中有它们的原因):
extern char data_start_somefile, data_end_somefile;

for (const char* c = &data_start_somefile; c != &data_end_somefile; ++c)
{
    // do something with character
}

另一篇帖子中的答案包含更多功能,其中一些人可能会发现有用(比如一个 length 符号)。


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