我正在开发一个嵌入式程序,我有一个自定义链接描述文件。该程序有效,但我注意到链接器如何在内存中放置几个部分可能有些不妥。
以下是链接描述文件的相关部分:
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
答案 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 这样的东西,实际上并不需要一个部分,只需要一组符号声明即可。