MIPS、ELF和部分链接

4
我有一个大型的软件项目,其构建过程非常复杂,流程如下:
  1. 编译各个源文件。
  2. 使用ld -r将每个模块的对象文件部分链接成另一个.o文件。
  3. 使用objcopy -G隐藏每个模块中的私有符号。
  4. 再次使用ld -r将模块对象部分链接在一起。
  5. 将模块链接在一起形成共享对象。
第三步是必需的,以允许模块专用的全局变量不被导出到项目的其他部分。
这在ARM和IA32上都可以正常工作。不幸的是,我现在需要让它在mips上运行(特别是针对Android的mipsel-linux-gnu)。而MIPS的共享对象ABI要比其他平台复杂得多,而且它无法正常工作。
问题在于第五步出现了错误,具体原因为:
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一起使用来修复地址的低半部分。但是代码看起来像是在尝试进行GOT间接寻址,这让我感到困惑。我不知道这是否与我早期的问题有关,或者是一个不同的问题,或者根本不是问题...
    更新:
    显然,MIPS根本不支持隐藏的全局符号!
    我们通过修改应该被隐藏的符号名称来解决了这个问题,这样就没有人能够知道它们的含义了。这将外部需求推得相当紧,但我向管理层解释说这是唯一的获得可出货产品的方法,他们就同意了。
    这完全是令人不快的(还需要进行一些非常恶心的makefile工作),所以如果有更好的解决方案,我会很乐意听取...
    2个回答

    1

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

    在MIPS(以及我熟悉的大多数汇编语言)中,你有基本的三个可见级别:局部、全局和弱。此外,你还有共享对象的comm。当然,GNU喜欢让事情变得更加复杂,并添加更多内容。gas提供了protected、hidden和internal(至少,很难跟上所有扩展)。有了所有这些,手动调整可见性的步骤似乎是不必要的。

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

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

    您真的有一个设置:1)子模块 2)子模块 ->模块 3-5)模块 ->共享库。简化它不会有害。您始终可以在2)或3-5)处插入C级接口,仅查找GCC将为您生成哪些架构的汇编代码,并以此作为将可见性分解为清晰接口的基础。

    希望我能为你提供一个定制的解决方案,但是如果没有你的完整项目参考,那几乎是不可能的。我可以向你保证,在MIPS位置(尤其是工具链)存在问题的同时,可见性选项(特别是如果你在使用gas、libbfd和gcc)是相同的。

    0

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


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接