gpt4 book ai didi

linker - MIPS、ELF 和部分链接

转载 作者:行者123 更新时间:2023-12-02 09:40:23 25 4
gpt4 key购买 nike

我有一个大型软件项目,具有复杂的构建过程,其工作原理如下:

  1. 编译各个源文件。
  2. 使用 ld -r 将每个模块的目标文件部分链接到另一个 .o。
  3. 使用 objcopy -G 隐藏每个模块中的私有(private)符号。
  4. 再次使用 ld -r 将模块对象部分链接在一起。
  5. 将模块链接到一个共享对象中。

需要执行第 3 步才能允许不导出到项目其余部分的模块私有(private)全局变量。

这一切都可以在 ARM 和 IA32 上正常工作。不幸的是,现在我必须让事情在 mips 上运行(特别是适用于 Android 的 mipsel-linux-gnu)。而且 MIPS 共享对象 ABI 比其他平台上的复杂得多,而且不起作用。

发生的情况是步骤 5 失败并出现以下错误:

CALL16 reloc at 0x1234 not against global symbol

这似乎是因为编译器生成 CALL16 重定位来调用另一个编译单元中的函数,但 CALL16 只允许您调用全局符号 --- 并且由于步骤 3,我们尝试调用的一些符号不再是全局性的。

此时我可以看到几个可能的选项:

  • 说服链接器将 CALL16 重定位解析为第 2 步中的正常编译单元内 PC 相关调用。
  • 同上,但在第 4 步或第 5 步。
  • 告诉编译器不要为编译单元间函数调用生成 CALL16 重定位。
  • 其他。

由于外部要求,禁用步骤 3 恐怕不是一个选项。

我真正非常喜欢做的是生成绝对代码,该代码在加载时修补到正确的地址;它更小,更快,并且简单得多,而且我们不需要在进程之间共享库。不幸的是,Android 的 dlopen() 似乎不支持此功能。

目前我已经超出了我的能力范围。大家有什么建议吗?

这是 gcc 4.4.5(来自 Emdebian),binutils 2.20.1。目标 BFD 是 elf32-tradlittlemips。主机操作系统是Linux,我正在针对Android进行交叉编译。

附录

从第 4 步中收到这样的警告。

$MODULE.o: Can't find matching LO16 reloc against `$SYMBOLNAME' for R_MIPS_GOT16 at 0x18 in section `.text.$SYMBOLNAME'

查看步骤 4 输入的反汇编,我可以看到编译器生成的代码如下所示:

50:   8f9e0000        lw      s8,0(gp)
50: R_MIPS_GOT16 $SYMBOLNAME
54: 8fd9001c lw t9,28(s8)
58: 0320f809 jalr t9
5c: 00a02021 move a0,a1

GOT16 不是固定到地址的高半部分吗,并且应该在低半部分后面跟一个 LO16 吗?但代码看起来像是在尝试进行 GOT 间接寻址。这让我很困惑。我不知道这是否与我之前的问题有关,或者是一个不同的问题,或者根本不是问题......

更新

显然 MIPS 根本不支持隐藏的全局符号!

我们通过修改应该隐藏的符号的名称来解决这个问题,这样就没有人能知道它们是什么。这极大地插入了外部需求,但我通过指出这是获得可交付产品的唯一方法来说服管理层。

这完全是可怕的(并且涉及一些令人厌恶的 makefile 工作),所以我宁愿想要一个更好的解决方案,如果有人有的话......

最佳答案

我不确定您遇到的具体 GOT 问题。 binutils 中的 GOT、LO16/HI16 内容存在很多错误和问题。我认为大多数问题已在您使用的版本中得到修复,除非您的目标是 MIPS16(您似乎没有这样做)。 LO16 实际上只需要在那里,除了 MIPS16 之外,您还可以从 GOT 中提取完整的 26 位偏移量,因为您有 32 位寄存器。 LO16 不是必需的,但某些 ABI/API 仍然正式要求它,但它最多只是一个警告(如果您正在使用它,您可以尝试在该阶段删除 -Werror)。老实说,我只了解该部分的基础知识,但对于您的其余情况,即使没有答案,我也有一些建议(鉴于您设置的复杂性,很难确定)。

在 MIPS(以及我熟悉的大多数程序集)中,您具有基本的三个级别的可见性:本地、全局和弱。此外,您还有共享对象的通信。当然,GNU 喜欢让事情变得更复杂并添加更多内容。 Gas 提供 protected 、隐藏的和内部的(至少,很难跟上所有扩展)。有了所有这些,您手动​​调整可见性的设置步骤似乎就没有必要了。

如果您可以删除变量的中间全局性,那么您就不需要将它们设为非全局性,这只能简化您以后遇到的任何 GOT 问题。

总体问题有点困惑。我不确定隐藏的全局符号是什么意思,这有点矛盾(当然可移植性和特定项目会带来疯狂的问题和限制)。您似乎希望在一个阶段使用交叉汇编单元符号,但在稍后阶段不需要。如果不使用 GNU 扩展(在我的书中最好避免这种情况),您可能希望将步骤 1-2 中的全局变量替换为 comm 和/或弱全局变量。您始终可以使用预处理器技巧来避免在该阶段出现多个子单元(丑陋,但这是此级别的可移植代码)。

您确实设置了 1) 子模块 2) 子模块 -> 模块 3-5) 模块 -> 共享库。简化一下不会有坏处。您始终可以在 2) 或 3-5) 处插入一个 C 级接口(interface),只是为了查找 GCC 将为您的架构生成什么程序集,并将其用作将可见性分解为干净接口(interface)的基础。

希望我能为您提供量身定制的解决方案,但如果没有完整的项目可供使用,这是几乎不可能的。我可以放心,虽然 MIPS 位置(特别是工具链)有问题,但可见性选项(特别是如果您使用 Gas、libbfd 和 gcc)是相同的。

关于linker - MIPS、ELF 和部分链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8095531/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com