【发布时间】:2020-07-12 17:35:37
【问题描述】:
我会说我在 C++ 方面表现不错,但是我对链接器脚本很陌生,我不太确定自己做错了什么。 首先,这是我的链接器脚本:
ENTRY(ISR_Reset)
MEMORY {
FLASH (rx) : ORIGIN = 0x80000, LENGTH = 128K
RAM (rwx) : ORIGIN = 0x20000000 LENGTH = 36K
}
SECTIONS {
.text : {
*(.vector_table)
*(.text.startup)
*(.text)
*(.rodata)
} > FLASH
__data_flash_source__ = ALIGN(4);
.data : AT(__data_flash_source__) {
__data_section_start__ = .;
*(.data)
*(.init_array)
__data_section_end__ = .;
__ram_end__ = ORIGIN(RAM) + LENGTH(RAM);
} > RAM
.bss : {
*(.bss)
} > RAM
}
我将数据从 FLASH 复制到 __data_section_start__ 和 __data_section_end__ 之间的 RAM,然后将 RAM 的其余部分清零。
我正在使用以下命令来编译我的代码:
arm-none-eabi-g++.exe .\src\start.s .\src\main.cpp -o .\bin\main -nostdlib -ffreestanding -fno-exceptions -nostartfiles -T .\src\sam3u2e.ld -mthumb -pedantic -Wall -std=c++17 -ggdb -Ofast
如果我删除 -Ofast 标志,并且不使用任何优化,我会收到以下链接器错误:
section .text._ZN4_PMCC2Ev LMA [0008019c,000801bf] overlaps section .data LMA [0008019c,0008019f]
但是,如果我确实包含它,它编译得很好,但是当我尝试像这样初始化结构时:
struct Example {
unsigned int const ID;
unsigned int volatile & some_register;
Example(unsigned int const id, unsigned int const register_address) : ID(id),
some_register(*((unsigned int volatile *)register_address)) {}
} instance(0x12345678, 0xDEADBEEF);
我注意到在反汇编中 instance.some_register 的地址存储在 FLASH 中,就在 main() 函数之后,但即使像这样写入它似乎也没有被任何代码引用:
instance.some_register = 1234;
然而,instance.ID 是从 RAM 加载的,但地址比.data 的大小更高,这意味着它永远不会从 FLASH 复制到 RAM。但是,如果我将.bss 的位置从> RAM 更改为> FLASH,它会尝试从FLASH 加载instance.ID,我不明白,因为无论如何ID 都应该在.rodata 中。
此外,我注意到,即使我在代码中根本没有引用该结构,而是将其定义为地址仍位于 FLASH 中,我想知道是否可以告诉链接器不包含代码对于未在任何地方引用的初始化结构?
编辑:链接器部分:
您可以看到 .data 部分(被复制到 RAM 中)的大小只有 4 个字节。
但是程序集尝试从 RAM 加载 instance.ID 和 instance.some_register 的地址 --- 这高于 0x20000004,这让我感到困惑,因为这些值无论如何都应该是只读的,因此存储在闪存中?
编辑 2:我可能不太清楚我想要代码做什么。我会再次尝试解释。我想定义一组不同的结构,每个结构都有不同的变量。每个变量引用一个设备寄存器(通过将变量的指针设置为相应寄存器的地址)。因此,当用户写入这些寄存器时,例如:peripheral.some_register = 123;,该值将写入实际的物理寄存器。
在已编译(优化)的代码中,我真的不希望这些结构存在,因此 CPU 不需要处理初始化数据,而是将其视为指向不同外设寄存器的指针集合。由于它是只读数据,它也可以位于 FLASH 中,因此不需要使用任何 RAM。
我猜这意味着我不能使用构造函数,因为这意味着 CPU 需要初始化它们,即使数据再次是只读的。 用户也不需要创建自己的结构实例,以防万一。
我最初想使用构造函数,因为有些外围设备每个都有超过 50 个寄存器,每个外围设备具有相同的偏移量,但基地址不同,所以我希望通过不需要复制粘贴来节省自己的一些工作每个寄存器的地址并为其添加一个数字。
【问题讨论】:
-
C++ 需要比 c 更多的支持。你有静态构造函数。 Ini/fini 和 ctor/dtor 可能是很好的搜索词。你可能也喜欢“c++filt”。
-
为什么不用 start 和 end/size 包装 bss 并干净地初始化 bss 而不是盲目地通过 ram 运行?或者至少先为零,然后再初始化 .data。
-
请显示二进制文件相关部分的反汇编。
-
@old_timer 我遍历存储在 FLASH 中的 .data 以将其加载到 RAM 中,并从最后一个地址到 ram 的末尾填充零。为什么不“干净”?你想看哪种拆解?加载到 RAM 或编译的 C++ 代码?
-
@artlessnoise 我将这些部分添加到链接器文件中,现在即使没有
-Ofast也可以编译,并且我在输出文件中看到了构造函数和析构函数,但是程序从不调用它们。为了澄清,我只是在 .text 部分之后为 .init、.fini、.ctor 和 .dtor 添加了.init : { *(.init) } > FLASH。此外,是否可以将寄存器地址存储在 .rodata 中,并在用户写入寄存器时让程序集引用它?而不必处理构造函数/析构函数?
标签: c++ optimization linker arm embedded