GNU链接器映射文件提供意外的加载地址

时间:2013-01-22 08:01:28

标签: linker arm gnu

我正在开发一个嵌入式程序,我有一个自定义链接描述文件。该程序有效,但我注意到链接器如何在内存中放置几个​​部分可能有些不妥。

以下是链接描述文件的相关部分:

MEMORY {
    ROM (rx)    : ORIGIN = 0x00100000, LENGTH = 16k
    RAM (rwx)   : ORIGIN = 0x00200000, LENGTH = 4k
}

SECTIONS {
    /* Other sections go here. */
    .data : {
...
    } >RAM AT>ROM

    .bss : {
...
    } >RAM

    .stack : {
...
    } >RAM
...
}

以下是MAP文件的相关部分:

.data           0x00200040        0x0 load address 0x001003d4
                0x001003d4                __data_load = LOADADDR (.data)
                0x00200040                __data_start = .
 *(.data)
 *(.data*)
                0x00200040                . = ALIGN (0x4)
                0x00200040                _edata = .

.igot.plt       0x00200040        0x0 load address 0x001003d4
 .igot.plt      0x00000000        0x0 ./debug/sam7s_startup.o

.bss            0x00200040        0x0 load address 0x001003d4
                0x00200040                __bss_start__ = .
 *(.bss)
 *(.bss*)
 *(COMMON)
                0x00200040                . = ALIGN (0x4)
                0x00200040                _ebss = .
                0x00200040                __bss_end__ = .
                0x00200040                PROVIDE (end, _ebss)
                0x00200040                PROVIDE (_end, _ebss)
                0x00200040                PROVIDE (__end__, _ebss)

.stack          0x00200040      0x200 load address 0x001003d4
                0x00200040                __stack_start__ = .

因此,从地图文件中我看起来像.bss和.stack部分正在ROM中获取加载地址。我认为这是因为这两行:

.bss 0x00200040 0x0 load address 0x001003d4
.stack 0x00200040 0x200 load address 0x001003d4

这不好,因为它们在ROM中占用空间毫无意义。 .bss部分虽然现在为空,但将包含未初始化的全局变量,这些变量将在代码中设置为零。堆栈也只是将在代码中初始化的RAM的一部分。所以这些部分都不需要占用ROM中的空间。

所以我的问题是,阻止.bss和.stack加载到ROM中的正确方法是什么?我是否必须将.bss和.stack部分的结尾从>RAM更改为>RAM AT>RAM?这似乎有点多余。

在测试了一些我发现以下内容之后:

(1)使用(NOLOAD)属性(例如,将.stack :替换为.stack (NOLOAD) :)仍然会导致地图文件显示.stack和.bss部分的ROM加载地址。

(2)如上所述,指定RAM AT>RAM确实会阻止地图输出显示.stack和.bss部分的ROM加载地址。

(3)当地图文件显示.bss和.stack部分的加载地址时,看起来它们实际上并没有占用ROM中的空间。 .stack部分,虽然长度为0x200字节,但实际上并没有占用ROM中的那个空间,即使我为它指定了填充值并在链接器脚本中放置了一个部分。链接描述文件中跟随它的部分不会以不同的堆栈大小移动。

所以也许地图文件输出并不意味着我的意思,而.stack和.bss部分实际上并没有在ROM中给出加载地址。在尝试了一些事情后,它肯定会出现这种情况。知道为什么地图输出看起来好像部分被赋予ROM加载地址仍然是有趣的,特别是当使用(NOLOAD)时。这可能只是LD如何生成其地图输出文件的错误吗?

另请参阅:Understanding the Location Counter of GNU Linker Scripts

1 个答案:

答案 0 :(得分:5)

您正在寻找NOLOAD。见Gnu LD output section type。我现在读了你的整篇文章,我看到你假设NOLOAD。使用NOLOAD,定义了所有地址。如果您在“C”代码中使用它们,它们将从该地址加载。您必须提供一些启动代码,通常在汇编程序中清除 BSS 区域。通常,您不希望初始化堆栈

NOLOAD部分类似于编译/链接时间malloc()。你得到了使用的内存,只是不要指望任何东西。对于 BSS ,您可以在链接描述文件中定义__bss_start____bss_end__,并编写一个简短的初始化例程,以使用这些变量/地址清除此内存。

注意:一切都显示在地图文件中。它不会显示在生成的二进制文件中,也不会在ELF中显示数据。只是部分元信息将保存在ELF中。

编辑:地图文件中的加载地址就像一个用于加载的位置计数器。加载地址是将ld设置为放置内容的位置。如果它们的尺寸为零,那么ld并没有真正把它放在那里。地图输出在语言上不是不明确的;我可以看到它是如何混淆,但ld在创建输出二进制文件时正确地做了一些事情。 BSS 通常在目标文件中通过gcc标记为NOLOAD,因此在示例中,只有堆栈部分需要NOLOAD。对于像 stack 这样的东西,实际上并不需要一个部分,只需要一组符号声明即可。