4

我正在开发一个嵌入式程序,其中有一个自定义链接器脚本。该程序有效,但我注意到链接器在内存中放置几个​​部分的方式可能有问题。

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

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 如何生成其地图输出文件的错误吗?

另请参阅:了解 GNU 链接描述文件的位置计数器

4

1 回答 1

5

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

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

注意:所有内容都显示在地图文件中。它不会出现在生成的二进制文件中,也不会在 ELF 中包含数据。只有部分元信息将保存在 ELF 中。

编辑:地图文件中的加载地址就像一个用于加载的位置计数器。加载地址是ld设置放置东西的地方。ld如果它们的大小为零,则不是真的把东西放在那里。地图输出在语言上不是明确的;我可以看到它是如何令人困惑的,但ld在创建输出二进制文件时做得正确。BSS通常NOLOAD由目标文件中的 gcc 标记,因此在示例中只有堆栈部分需要NOLOAD. 对于类似stack的东西,一个部分并不是真正需要的,只需要一组符号声明就可以了。

于 2013-01-27T23:41:28.083 回答