可以移动 Mach-O 可执行文件的 __LINKEDIT 段吗

Can the __LINKEDIT segment of a Mach-O executable be moved

在 Mach-O 可执行文件中,我试图增加 __LINKEDIT 段之前的 __LLVM 段的大小(使用自制工具)。我正在考虑两种策略:(a) 将 __LLVM 段移动到 __LINKEDIT 段之后,生成一个不是 ld 将创建的文件(现在间隙和节地址乱序), (b) 移动 __LINKEDIT 段以允许调整其前面的 __LLVM 段的大小。我需要结果被下游处理接受,例如生成 .ipa 文件或发送到 App Store。

这个问题是关于我的假设和这些方法的可行性。具体来说,可能导致他们失败的潜在陷阱是什么?

我实现的第一种方法 (a) 被 segedit 的 -extract 选项理解,但它的 -replace 选项抱怨段乱序。我将一个新段附加到文件并更新相应加载命令中的地址和长度值以引用这个新段数据(在文件和目标内存中)。这可能没问题,只要其他下游处理将接受结果(仍有待检查;例如,任何本地签名都可能无效)。

第二种方法 (b) 看起来更简洁,只要没有引用 __LINKEDIT 段,我猜它包含链接信息(符号表等等,而不是代码)。我还没有尝试过这个,尽管 segedit 对结果会满意似乎已成定局,这可能表明其他处理也可能更快乐。是否有可能因为简单地移动此段而导致任何引用无效?我猜我将不得不更新更多的加载命令(它们似乎引用到 __LINKEDIT 段),我没有检查过,但这应该相当简单。

编辑:将我对 "section" 的混淆使用替换为 "segment"(在回答中提到)。

添加:上下文是我无法控制生成原始可执行文件的地方。我需要 post-process 它,本质上是执行一个 'segedit -replace' 过程,其中段中的 a 部分将被替换为大于 space 之前分配给该段的部分.

运行-ON 澄清问题:从答案看来,移动 __LINKEDIT 段会破坏它。这可以通过仅调整加载命令(例如 LC_DYLD_INFO_ONLY、LC_LOAD_DYLINKER、LC_LOAD_DYLIB)而不是任何段中的数据来解决吗?我还不熟悉这些加载命令,想知道是否要继续。

基本上,段和节描述了物理文件如何映射到虚拟内存。 正如我在之前的答案迭代中提到的,分段顺序有限制:

  1. __TEXT 部分必须从可执行物理文件偏移量 0
  2. 开始
  3. __LINKEDIT 部分必须 而不是 从物理文件偏移量 0
  4. 开始
  5. __LINKEDIT 的文件偏移量 + 文件大小应等于物理可执行文件大小(这意味着 __LINKEDIT 是最后一个段)。否则代码签名将不起作用。

__DYLD_INFO_ONLY 包含 dyld 加载绑定操作码的文件偏移量:

  • 变基
  • 加载时绑定
  • 弱绑定
  • 延迟绑定
  • 导出

对于每种类型,__DYLD_INFO_ONLY 中都有文件偏移量和大小条目,描述了与 __LINKEDIT 匹配的文件中的数据(在 "regular" ld 链接的可执行文件中)。 __DYLD_INFO_ONLY 不直接使用来自 __LINKEDIT 的任何段和节信息,文件偏移量和大小就足够了。

编辑也如@kirelagin 回答中所述
"Apparently, the new version of dyld from 10.12 Sierra performs a check that previous versions did not perform: it makes sure that the LC_SYMTAB symbols table is entirely within the __LINKEDIT segment."

我假设既然您想扩大前面 __LLVM 段的大小,您还需要文件本身中的一些额外数据。通常由 __LINKEDIT 描述的数据(即不是段和部分本身,而是实际数据)不会使用 100% 的 space 因此可以修改它以开始 "later" 并占用少 space。

乔纳森·莱文 (Jonathan Levin) 开发的名为 jtool 的工具或许可以为您完成。

我知道这是一个老问题,但我在解决另一个问题时解决了这个问题

  1. define the slide amount, this must be page-aligned, so I choose 0x4000.
    1. add the slide amount to the relevant load commands, this includes but is not limited to:
      • __LINKEDIT segment (duh)
      • dyld_info_command
      • symtab_command
      • dysymtab_command
      • linkedit_data_commands
    2. physically move the __LINKEDIT in the file.