4

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

  1. 编译单个源文件。
  2. 使用 .将每个模块的部分目标文件链接到另一个 .o 中ld -r
  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 步不是一个选项。

我真正非常想做的是生成绝对代码,这些代码在加载时被修补到正确的地址;它更小、更快、简单,而且我们不需要在进程之间共享库。不幸的是,Androiddlopen() 似乎不支持这一点。

目前我已经超出了我的深度。有人有什么建议吗?

这是 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工作),所以我宁愿想要一个更好的解决方案,如果有人有一个......

4

2 回答 2

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 时)是相同的。

于 2012-04-23T21:15:25.187 回答
0

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

于 2013-09-13T06:53:17.690 回答