首页 文章

第三方C静态库:添加-ffunction-sections -fdata-sections

提问于
浏览
2

我有一个静态库(用arm-gcc编译),由第三方提供 . 我没有可能(让第三方)重新编译库 .

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

编译完成时使用:(ARM嵌入式处理器的GNU工具)4.8.4 20140526(发布)[ARM / embedded-4_8-branch revision 211358] .

有没有办法将每个数据和每个函数放入它们自己的单独部分,以便为这个库启用函数级链接,而无需重新编译代码?

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

  • 将库拆分为其目标文件 .

  • 对于每个目标文件:

  • 编写代码以将符号移动到自己的部分

  • 将新对象文件放回存档文件中

这可行,或者你有其他建议,理想情况下只使用arm-gcc提供的工具?

1 回答

  • 2

    我知道这已经过时了,但我也遇到了这个问题,并且认为我会提供我的发现 .

    TL;DR: It's possible, but incredibly difficult. You can't simply move symbols into their own sections. Relocations will bite you.

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

    这通过示例得到最好的说明 . 请使用以下非常简单的代码段:

    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
    

    差异很微妙,但很重要 . 标志启用代码执行两个单独的加载,而禁用的代码执行单个"load multiple."启用的代码执行此操作,因为它知道两个符号按特定顺序包含在同一部分中 . 使用启用的代码,情况并非如此 . 符号分为两个单独的部分,虽然它们很可能会保持其顺序和接近程度,但不能保证 . 更重要的是,如果未引用这两个部分,链接器可能会决定不使用一个部分,并将其删除 .

    另一个例子:

    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 标志编译的代码,需要解析机器指令以查找可能出现的这些以及任何其他重定位问题 . 这不是一件容易完成的任务 .

相关问题