GCC LD NOLOAD 链接器部分生成可加载段
Posted
技术标签:
【中文标题】GCC LD NOLOAD 链接器部分生成可加载段【英文标题】:GCC LD NOLOAD linker section generates loadable segment 【发布时间】:2021-04-27 02:07:54 【问题描述】:我正在开发一个 Arm 裸机应用程序,我已经用 NOLOAD
标记了一些部分。根据Understanding linker script NOLOAD sections in embedded software
中的解释,我希望生成的 ELF 文件没有这些部分有可加载的段(程序头),但确实有。
这是正确的吗?为什么这些部分在 ELF 文件中标记为可加载?
由于链接器仍在将数据放在.bss
中,加载程序应该如何知道不应该加载这些部分?还是我错过了“加载”的含义,因为NOLOAD
仅对 initialized 符号有意义(通常会放入 .data
)?
这是我的链接器脚本的一部分:
.bss (NOLOAD) :
. = ALIGN(4);
__bss_start__ = .;
*(.bss_begin .bss_begin.*)
*(.bss .bss.*)
*(COMMON)
*(.bss_end .bss_end.*)
. = ALIGN(4);
__bss_end__ = .;
>DRAM
.noinit (NOLOAD) :
. = ALIGN(4);
__noinit_start__ = .;
*(.noinit .noinit.*)
. = ALIGN(4) ;
__noinit_end__ = .;
> DRAM
/* Check if there is enough space to allocate the main stack */
._stack (NOLOAD) :
. = ALIGN(4);
. = . + __Main_Stack_Size ;
. = ALIGN(4);
>DRAM
这是输出的 ELF 文件:
arm-none-eabi-readelf.exe -l test.elf
Elf file type is EXEC (Executable file)
Entry point 0x601b9
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x00060000 0x00060000 0x06840 0x06840 RWE 0x10000
LOAD 0x020000 0x20010000 0x20010000 0x00000 0x06084 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .text .ARM.exidx.reset .data
01 .systemclock .bss ._stack
为什么会有.bss
和._stack
部分?
谢谢!!
【问题讨论】:
你能澄清问题吗/你的问题,我最近在处理链接器脚本,但在这里看不到问题NOLOAD
关键字告诉加载器不应该加载给定的部分。我的期望是任何带有NOLOAD
的部分都应该 NOT 出现在带有 LOAD 标志的 ELF 输出文件程序头中,但它们确实出现了。我想知道为什么。
您提到的另一个堆栈溢出问题引用了NOLOAD(输出节类型)的定义。此定义明确表示链接器将正常处理该部分,然后不会导致 ELF w.r.t 发生任何变化。本节(加载程序的添加属性除外)。加载器负责不加载这些部分。
好的,但接下来的问题是:加载程序应该如何知道不应该加载这些?当一个符号放在.noinit
部分时,链接器将它从.bss
移动到.noload
,我期待这里有类似的东西吗?
也许您必须将该部分实际移动到专用的 segment 中,然后才不会加载? IIRC 节表在 ELF 可执行文件中是可选的...
【参考方案1】:
第二个段的FileSiz为0,表示没有数据会从elf文件加载到这个段中。
该段存在于表中的原因是在非嵌入式系统上,程序加载器仍需要为该段中的段申请内存,并用相关属性(可读可写)标记相关页案例)。
编辑:再次阅读问题后,我做了更多实验,似乎(NOLOAD)
似乎所做的只是将输出文件中的部分类型设置为SHT_NOBITS
。 elf 规范称该部分在文件中不占用空间但仍是程序内存映像的一部分。
如果目标是在加载程序之前链接到 rom 中已经存在的代码,则这些符号应在链接描述文件的任何部分之外定义。例如。 already_present_code = 0x06000000; SECTIONS .text : *(.text*) /* ... */
。这似乎达到了预期的效果。
【讨论】:
是的,但是MemSiz
不为零,这意味着即使在嵌入式系统上,内存范围也必须用零填充。
.bss 通常会用零填充,因为这是用零初始化的全局变量所在的位置。我很确定这是由 C 运行时完成的,而不是由程序加载器完成的。 (不幸的是,我现在没有任何东西可以测试验证这一点,但this post 表明 GDB 没有说明 .bss 并且我的启动代码有一个初始化它的循环)。
(这就是 __bss_start__
和 __bss_end__
符号的用途)。其他部分不应初始化。您可以通过定义一个符号来验证这一点,例如在链接器脚本中的 .noinit 中,并编写一些代码来读取该位置的内存,将其递增,将其写回并打印。
您错过了我的问题的重点,将NOLOAD
添加到.bss
对程序头表没有影响,因为.bss
部分被标记为“已加载” .我想了解为什么。重温一下,NOLOAD
似乎不会影响我标记的部分,因为它们没有代码。
您是否真的验证了 gdb 在加载程序时会向NOLOAD
部分写入内容的说法?我相当肯定它没有,一旦我在大约 13 小时内再次访问硬件,我会自己检查一下。以上是关于GCC LD NOLOAD 链接器部分生成可加载段的主要内容,如果未能解决你的问题,请参考以下文章