GCC LD NOLOAD 链接器节生成可加载段

4

我正在开发一个 Arm 裸机应用,并使用 NOLOAD 标记了一些部分。根据在 Understanding linker script NOLOAD sections in embedded software 中的解释,我期望生成的 ELF 文件不会为这些部分生成可加载段(程序头),但实际上会生成。

这是正确的吗?为什么这些部分在 ELF 文件中被标记为可加载?

由于链接器仍将数据放置在 .bss 中,那么加载程序如何知道这些部分不应该被加载?或者我是否误解了 “load” 的意思,即 NOLOAD 只适用于初始化符号(通常会被放置在 .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这两个区段?

谢谢!


你能澄清一下问题或者你的问题是什么吗?我最近在处理链接脚本,但是我看不出这里有什么问题。 - GabrielT
“NOLOAD” 关键字告诉加载器不应该加载一个给定的段。我的期望是任何带有 “NOLOAD” 的段都不应该出现在 ELF 输出文件的程序头部,但实际上它们确实会出现。我想知道原因。 - Leonardo
你所提到的另一个堆栈溢出问题引用了NOLOAD(输出部分类型)的定义。该定义明确表示链接器将正常处理该部分,这将导致ELF在此部分方面没有任何变化(除了加载程序的添加属性)。是加载程序负责不加载这些部分。 - bbv
好的,但问题是:加载程序应该如何知道这些不应该被加载?当符号放置在.noinit部分时,链接器将其从.bss移动到.noload,我期望这里也有类似的操作? - Leonardo
也许你需要将该部分实际移动到一个专用的“_segment_”中,然后不加载它?如果我没记错,在ELF可执行文件中,段表是可选的... - dyp
1个回答

2
第二个段的文件大小为0,这意味着没有数据将从elf文件加载到该段中。
该段存在于表中的原因是,在非嵌入式系统上,程序加载器仍需要为该段中的部分请求内存,并使用相关属性标记相关页面(在本例中可读和可写)。
编辑:再次阅读问题后,我进行了更多实验,似乎所有的(NOLOAD)都只是将输出文件中的部分类型设置为SHT_NOBITS。elf规范称之为在文件中不占用空间但仍然是程序内存映像的部分。
如果目标是链接在程序加载之前已经存在于rom中的代码,则应该在链接器脚本中定义这些符号,而不是在任何部分外面。例如:already_present_code = 0x06000000; SECTIONS { .text : {*(.text*)} /* ... */}。这似乎有预期的效果。

是的,但是MemSiz不为零,这意味着即使在嵌入式系统上,该内存范围也必须填充为零。 - Leonardo
.bss通常会被填充为零,因为那里是初始化为零的全局变量所在的位置。我非常确定这是由C运行时而不是程序加载器完成的。(不幸的是,我现在没有任何东西来测试验证这一点,但是这篇文章显示GDB没有关于.bss的任何信息,我的启动代码有一个循环来初始化它)。 - Niautanor
(这就是__bss_start____bss_end__符号的作用)。其他部分不应该被初始化。您可以通过在链接器脚本中定义一个例如.noinit的符号,并编写一些代码来读取该位置的内存,将其递增,写回并打印它来验证。 - Niautanor
你没有理解我的问题,将 NOLOAD 添加到 .bss 上并没有对程序头表产生影响,因为 .bss 部分被标记为“已加载”。我想了解其中的原因。重新审视这个问题,似乎 NOLOAD 不会影响我标记的那些部分,因为它们没有代码。 - Leonardo
谢谢更新! - Leonardo
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接