MIPS,ELF和部分链接

时间:2011-11-11 14:42:23

标签: linker shared-libraries mips elf binutils

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

  1. 编译各个源文件。
  2. 使用ld -r将每个模块的目标文件部分链接到另一个.o。
  3. 使用objcopy -G隐藏每个模块中的私人符号。
  4. 使用ld -r再次将模块对象部分链接在一起。
  5. 将模块链接到一个共享对象。
  6. 需要步骤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吗?但代码看起来像是在试图进行GOT间接。这让我很困惑。我不知道这是否与我之前的问题有关,或者是一个不同的问题,或者根本不是问题......

    更新

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

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

    这完全是令人毛骨悚然的(并且涉及一些非常恶心的makefile工作),所以我宁愿选择一个更好的解决方案,如果有人有...

2 个答案:

答案 0 :(得分: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和/或weakglobals。您总是可以使用使用预处理器技巧来避免在舞台上有多个子单元(丑陋,但这是该级别的可移植代码)。

你真的有1)子模块的设置2)子模块 - >模块3-5)模块 - >共享库。简化那不能伤害。您可以随时插入2)或3-5)C级接口,以便找到GCC将为您的架构生成的组件,并将其用作打破清晰界面可视性的基础。

希望我能给你一个量身定制的解决方案,但如果没有你的完整项目,这是非常不可能的。我可以放心,虽然MIPS位置(特别是工具链)存在问题,但可见性选项(特别是如果您使用gas,libbfd和gcc)是相同的。

答案 1 :(得分:0)

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