加载 Mach-O 可执行文件需要什么?

What is required for a Mach-O executable to load?

我正在尝试 hand-write 一个 Mach-O 可执行文件。一共有三个加载命令:

每个命令都匹配 mach/loader.h 和相关 headers 中的结构。 otool -l 按预期列出信息并且不报告任何错误。从各方面来看,它是一个 well-formed object 文件 — 然而 OS X 10.10.5 终止了任务 (SIGKILL)。

Mach-O 可执行文件的哪些功能在 OS X 加载之前会被检查?这些信息位于何处?这些功能会改变 version-to-version 吗? (often-cited "OS X ABI Mach-O Reference" 显然不见了。)


这里是一个partially annotated hexdump的二进制文件。

otool 完整性检查(摘录):

$ otool -l machtest
machtest:
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
…
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 152
  segname __TEXT
…
Section
  sectname __text
   segname __TEXT
…
Load command 2
        cmd LC_UNIXTHREAD
…

不是 100% 确定,但你需要 LC_LOAD_DYLINKER 在你的可执行文件之前加载命令到 运行 dyld,我很确定 OSX如果该加载命令不可用,则不会自动映射到 /usr/lib/dyld

您需要 /usr/lib/libSystem.B.dylibLC_LOAD_DYLIB 加载命令吗?我不这么认为,但拥有两者都很好,而且花费不多。

从 10.10.5 Yosemite 开始,可执行文件必须至少 4096 字节长 ( PAGE_SIZE ),否则将立即被杀死。 @Siguza在XNU kernelexec_activate_image函数https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/kern/kern_exec.c#L1456

中找到的相关代码

没有dyld

假设您想要一个仅使用系统调用的 64 位 macOS 可执行文件,您需要:

  • Mach-O 64 位 Header
  • LC_SEGMENT_64 __PAGEZERO(非零大小,名称可以是任何东西)
  • LC_SEGMENT_64 __TEXT(名称可以是任何东西;必须是可读和可执行的;部分是可选的)
  • LC_UNIXTHREAD

这里是my example这个案例。

用dyld

虽然没有 dyld 你不能做很多事情,所以如果你想使用它,最小的设置是:

  • Mach-O 64 位 Header
  • LC_SEGMENT_64 __PAGEZERO(非零大小)
  • LC_SEGMENT_64 __TEXT(名称可以是任何东西;必须是可读和可执行的;部分是可选的)
  • LC_SEGMENT_64 __LINKEDIT(必须是可写的,因为 dyld 需要可写段,在 ld 链接二进制文件中,可写段通常是 ​​__DATA
  • LC_DYLD_INFO_ONLY(指定实际 dyld 加载命令在可执行文件中的物理位置,通常可以找到它们 __LINKEDIT 但对此没有限制)或者有趣的是 LC_SYMTAB 而不是,这将使实际的 dyld 在没有 LC_DYLD_INFO_ONLY.
  • 的情况下无法使用
  • LC_DYSYMTAB(可以为空)
  • LC_LOAD_DYLINKER
  • LC_MAINLC_UNIXTHREAD
  • LC_LOAD_DYLIB(至少要加载一个最终依赖于 libSystem 或 libSystem 本身才能使 LC_MAIN 工作的实际 dylib)

此外,自 MacOS Monterey 12.3 起:

  • LC_SYMTAB(如果使用 LC_DYSYMTAB,则现在需要)

LC_UNIXTHREADLC_MAIN

在现代可执行文件中(自 10.7 Mountain Lion 起),LC_UNIXTHREADLC_MAIN 取代,这需要 dyld — 但任何可执行文件仍然支持 LC_UNIXTHREAD 作为10.12 Sierra(它应该在未来的 MacOS 版本中,因为它被 dyld 可执行文件本身用来实际启动)。

要使 dyld 正常工作,额外的步骤取决于绑定类型:
bind at load 是最省力的方法,其中 LC_DYLD_INFO_ONLY 指向有效 dyld load commands 指向可写段就可以了。
lazy binding__TEXT 中还需要额外的平台特定代码,它在加载时使用绑定 dyld_stub_binder 来延迟加载 dyld 加载函数的地址。
还有其他类型的 dyld binding 我没有在这里介绍。

可以在此处找到更多详细信息:https://github.com/opensource-apple/dyld/blob/master/src/ImageLoaderMachO.cpp