我知道这是旧的,但我也遇到了这个问题,并想我会提供我的发现。
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 标志编译的代码等效的有效目标文件将需要解析机器指令以查找这些以及可能出现的任何其他重定位问题。这不是一件容易的事。