【问题标题】:Third party C static library: Add -ffunction-sections -fdata-sections第三方 C 静态库:添加 -ffunction-sections -fdata-sections
【发布时间】:2015-07-15 09:14:00
【问题描述】:

我的情况是有一个 c 静态库(用 arm-gcc 编译),它是由第三方提供的。我不可能(让第三方)重新编译库。

在调查库内容时,我发现 gcc 选项 -ffunction-sections-fdata-sections 没有用于编译库。但这对于减少项目的二进制大小非常有帮助。

编译通过:(用于 ARM 嵌入式处理器的 GNU 工具)4.8.4 20140526(发布)[ARM/embedded-4_8-branch 修订版 211358]。

有没有什么方法可以将每个数据和每个函数放入各自单独的部​​分,以启用该库的函数级链接,而无需重新编译代码?

我想到了这种可能的方法:

  • 将库拆分为其目标文件。
  • 对于每个目标文件:
    • 编写代码将符号移动到自己的部分中
  • 将新的对象文件重新组合到存档文件中

这可行吗,或者您有其他建议,最好只使用 arm-gcc 提供的工具?

【问题讨论】:

    标签: c gcc arm static-libraries


    【解决方案1】:

    我知道这是旧的,但我也遇到了这个问题,并想我会提供我的发现。

    TL;DR:这是可能的,但非常困难。您不能简单地将符号移动到它们自己的部分中。搬迁会咬你一口。

    当编译器生成机器代码时,如果提供或不提供-ffunction-sections-fdata-sections 标志,它将生成稍有不同的指令。这是由于编译器能够做出关于符号所在位置的假设。这些假设会根据提供的标志而变化。

    这是最好的例子。采取以下非常简单的代码sn -p:

    int a, b;
    
    int getAPlusB()
    {
        return a + b;
    }
    

    以下是arm-none-eabi-objdump -xdr test.o的结果:

    arm-none-eabi-gcc -c -Os -mthumb -mcpu=cortexm3 -mlittle-endian -o test.o test.c:

    SYMBOL TABLE:
    00000000 g     F .text  0000000c getAPlusB
    00000004 g     O .bss   00000004 b
    00000000 g     O .bss   00000004 a
    
    Disassembly of section .text:
    
    00000024 <getAPlusB>:
      24:   4b01        ldr r3, [pc, #4]    ; (2c <getAPlusB+0x8>)
      26:   cb09        ldmia   r3, {r0, r3}
      28:   4418        add r0, r3
      2a:   4770        bx  lr
      2c:   00000000    .word   0x00000000
                2c: R_ARM_ABS32 .bss
    

    arm-none-eabi-gcc -c -Os -ffunction-sections -fdata-sections \ -mthumb -mcpu=cortexm3 -mlittle-endian -o test.o test.c:

    SYMBOL TABLE:
    00000000 g     F .text.getAPlusB    00000014 getAPlusB
    00000000 g     O .bss.b 00000004 b
    00000000 g     O .bss.a 00000004 a
    
    Disassembly of section .text.getAPlusB:
    
    00000000 <getAPlusB>:
       0:   4b02        ldr r3, [pc, #8]    ; (c <getAPlusB+0xc>)
       2:   6818        ldr r0, [r3, #0]
       4:   4b02        ldr r3, [pc, #8]    ; (10 <getAPlusB+0x10>)
       6:   681b        ldr r3, [r3, #0]
       8:   4418        add r0, r3
       a:   4770        bx  lr
        ...
                c: R_ARM_ABS32  .bss.a
                10: R_ARM_ABS32 .bss.b
    

    区别很微妙,但很重要。标志启用代码执行两个单独的加载,而禁用代码执行单个“加载多个”。启用的代码这样做是因为它知道两个符号都包含在同一个部分中,以特定的顺序。使用启用的代码,情况并非如此。这些符号位于两个单独的部分,虽然它们可能会保持它们的顺序和接近性,但不能保证。更重要的是,如果两个部分都没有被引用,链接器可能会决定一个部分没有被使用,并删除它。

    另一个例子:

    int a, b;
    
    int getB()
    {
      return b;
    }
    

    以及生成的代码。首先没有标志:

    SYMBOL TABLE:
    00000000 g     F .text  0000000c getB
    00000004 g     O .bss   00000004 b
    00000000 g     O .bss   00000004 a
    
    Disassembly of section .text:
    
    00000018 <getB>:
      18:   4b01        ldr r3, [pc, #4]    ; (20 <getB+0x8>)
      1a:   6858        ldr r0, [r3, #4]
      1c:   4770        bx  lr
      1e:   bf00        nop
      20:   00000000    .word   0x00000000
                20: R_ARM_ABS32 .bss
    

    还有旗帜:

    SYMBOL TABLE:
    00000000 g     F .text.getB 00000014 getB
    00000000 g     O .bss.b 00000004 b
    00000000 g     O .bss.a 00000004 a
    
    Disassembly of section .text.getB:
    
    00000000 <getB>:
       0:   4b01        ldr r3, [pc, #4]    ; (8 <getB+0x8>)
       2:   6818        ldr r0, [r3, #0]
       4:   4770        bx  lr
       6:   bf00        nop
       8:   00000000    .word   0x00000000
                8: R_ARM_ABS32  .bss.b
    

    在这种情况下,差异更加微妙。启用的代码使用偏移量 0 加载,而禁用的代码使用 4。由于禁用的代码引用了节的开头,因此它需要偏移到 b 的位置。然而,启用的代码引用了仅包含 b 的部分,因此不需要偏移量。如果我们将其拆分并仅更改重定位,则新代码将包含对a 所在部分的引用,而不是b。同样,这可能会导致链接器垃圾收集错误的部分。

    这些只是我在查看此问题时遇到的两种情况,可能还有更多。

    生成功能上与使用-ffunction-sections-fdata-sections 标志编译的代码等效的有效目标文件将需要解析机器指令以查找这些以及可能出现的任何其他重定位问题。这不是一件容易的事。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-15
      • 2019-10-10
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多