【问题标题】:MIPS, ELF and partial linkingMIPS、ELF 和部分链接
【发布时间】:2011-12-27 02:06:56
【问题描述】:

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

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

第 3 步是允许未导出到项目其余部分的模块私有全局变量所必需的。

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

发生的情况是第 5 步失败并出现此错误:

CALL16 reloc at 0x1234 not against global symbol

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

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

  • 说服链接器在第 2 步将 CALL16 重定位解析为正常的编译单元内 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 的低半部分吗?但是 code 看起来像是在尝试进行 GOT 间接寻址。这让我很困惑。我不知道这是否与我之前的问题有关,或者是不同的问题,或者根本不是问题......

更新

显然 MIPS 只是 不支持隐藏的全局符号!

我们已经通过修改应该隐藏的符号名称来解决这个问题,这样没人知道它们是什么。这极大地推动了外部要求,但我通过指出这是获得可交付产品的唯一途径来出售管理人员。

这完全令人毛骨悚然(并且涉及到一些非常令人作呕的 makefile 工作要做),所以我宁愿想要一个更好的解决方案,如果有人有的话......

【问题讨论】:

    标签: linker shared-libraries mips elf binutils


    【解决方案1】:

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

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

    如果您可以删除变量的中间全局性,它应该删除您需要使它们成为非全局性的,这只能用于简化您以后遇到的任何 GOT 问题。

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

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

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

    【讨论】:

      【解决方案2】:

      你的 binutils 太旧了。 2.23 中的一些变更集可能会解决您的问题,例如“隐藏没有 PLT 或 GOT 引用的符号”。

      【讨论】:

        猜你喜欢
        • 2020-05-08
        • 1970-01-01
        • 1970-01-01
        • 2013-12-11
        • 1970-01-01
        • 2018-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多