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 链接器部分生成可加载段的主要内容,如果未能解决你的问题,请参考以下文章

编译汇编链接加载

007.链接器命令脚本LD文件教程

是否在编译时和运行时都调用了ld?

GCC 编译使用动态链接库 LD

gcc 参数

GCC链接器:在指定部分移动符号