LD手册没有解释
KEEP
命令的作用。下面是来自第三方链接器脚本的片段,其中包含 KEEP
。 KEEP
命令在 ld
中起什么作用?
SECTIONS
{
.text :
{
. = ALIGN(4);
_text = .;
PROVIDE(stext = .);
KEEP(*(.isr_vector))
KEEP(*(.init))
*(.text .text.*)
*(.rodata .rodata.*)
*(.gnu.linkonce.t.*)
*(.glue_7)
*(.glue_7t)
*(.gcc_except_table)
*(.gnu.linkonce.r.*)
. = ALIGN(4);
_etext = .;
_sidata = _etext;
PROVIDE(etext = .);
_fini = . ;
*(.fini)
} >flash
即使没有引用符号,LD 也会将符号保留在该部分中。 (--gc-sections)。
通常用于二进制启动过程中具有某些特殊含义的部分,或多或少地标记依赖树的根。
(下面为 Sabuncu)
依赖树:
如果消除未使用的代码,则可以分析代码并标记所有可到达的部分(代码+全局变量+常量)。
因此,您选择一个部分,将其标记为“已使用”,然后查看它引用的其他(未使用)部分,然后将这些部分标记为“已使用”,并检查它们引用的内容等。
未标记“已使用”的部分是多余的,可以删除。
由于一个部分可以引用多个其他部分(例如,一个过程调用三个不同的其他部分),因此如果您绘制结果,您会得到一棵树。
根源:
上述原则给我们留下了一个问题:始终使用的“第一”部分是什么?可以这么说,树的第一个节点(根)?这就是“keep()”的作用,它告诉链接器哪些部分(如果可用)是首先要查看的部分。因此,这些总是链接在一起的。
通常这些是从程序加载器调用的部分,用于执行与动态链接(可以是可选的,并且取决于操作系统/文件格式)以及程序的入口点相关的任务。
附注(2024),查看 Randomblue 的脚本,它可能是为了嵌入某些内容,并演示了保留部分的另一个原因。 ISR(中断服务例程)可以由 CPU 通过某些跳转表或直接通过地址计算来调用,并且可能不会被目标代码引用。因此它总是 KEEP()d。
说明其用法的最小 Linux IA-32 示例
主.S
.section .text
.global _start
_start:
/* Dummy access so that after will be referenced and kept. */
mov after, %eax
/*mov keep, %eax*/
/* Exit system call. */
mov $1, %eax
/* Take the exit status 4 bytes after before. */
mov $4, %ebx
mov before(%ebx), %ebx
int $0x80
.section .before
before: .long 0
/* TODO why is the `"a"` required? */
.section .keep, "a"
keep: .long 1
.section .after
after: .long 2
链接.ld
ENTRY(_start)
SECTIONS
{
. = 0x400000;
.text :
{
*(.text)
*(.before)
KEEP(*(.keep));
*(.keep)
*(.after)
}
}
编译并运行:
as --32 -o main.o main.S
ld --gc-sections -m elf_i386 -o main.out -T link.ld main.o
./main.out
echo $?
输出:
1
如果我们注释掉
KEEP
行,则输出为:
2
如果我们:
mov keep, %eax
--gc-sections
输出返回到
1
。
在 Ubuntu 14.04、Binutils 2.25 上测试。
解释
没有提及符号
keep
,因此它包含部分 .keep
。
因此,如果启用了垃圾收集并且我们不使用
KEEP
来进行例外,则该部分将不会放入可执行文件中。
由于我们将 4 添加到
before
的地址,因此如果 keep
部分不存在,则退出状态将为 2
,它出现在下一个 .after
部分中。
TODO:如果我们从
"a"
中删除 .keep
,什么也不会发生,这使得它可以分配。我不明白为什么会这样:该部分将被放入 .text
段内,由于它的神奇名称,该段将是可分配的。
强制链接器保留某些特定部分
SECTIONS
{
....
....
*(.rodata .rodata.*)
KEEP(*(SORT(.scattered_array*)));
}