在构建基于 gcc 的裸机 mcu 项目时,您需要在启动期间处理 .data 和 .bss 部分的初始化。
.bss 部分非常简单,因为我只是将整个部分填充为 0。
但是 .data 部分中的变量需要在 rom/flash 中有它们的初始化数据并在启动时复制。
我怎么知道在哪里可以找到带有初始化值的数据?
让我们举个例子。
假设我在 main.c 中创建了两个全局变量
unsigned int my_global_variable_one = 1;
unsigned int my_global_variable_two = 2;
然后我可以在目标文件上使用 objdump 来查看它们将位于哪个部分,
但是我在 objdump 输出中找不到任何应该放置 init 数据的地方。
$ arm-none-eabi-objdump --syms main.o | grep my_global_variable
00000000 g O .data 00000004 my_global_variable_one
00000004 g O .data 00000004 my_global_variable_two
然后我可以查看整个系统的结果 Sprite ,在本例中为 main.elf。
$ arm-none-eabi-nm -n main.elf | grep my_global_variable
20000000 D my_global_variable_one
20000004 D my_global_variable_two
我在哪里可以找到它们的位置以便我可以复制数据?
我需要在我的链接器脚本中加入什么?
它应该是 .text 或 .rodata 之类的,但我怎么知道?
如何检查 my_global_variable_one 的初始化数据在哪里?
我可以使用任何 binutils 命令(如 readelf 或 objdump)找到这些数据的位置吗?
/谢谢
这是在 stm32 (Cortex M3) mcu 上,并且使用了 gcc 的 CodeBench 版本。
最佳答案
编译器将所有代码和一些只读数据放在 .text
中。部分。可能还有.rodata
部分。你可以让你的链接器脚本把它放在一个像这样的 ROM 地址中:
. = <rom-base-address>;
.rodata : { *(.rodata) }
<other read-only sections go here>
.text : { *(.text) }
编译器将所有可写数据的初始值放入
.data
部分,以及所有在 .bss
中没有初始值的符号. .bss
很简单,您只需将其放在 RAM 中即可。 .data
想在运行时在 RAM 中,但在加载时在 ROM 中,链接描述文件允许您使用 AT
来做到这一点。关键词:. = <ram-base-address>;
.bss : { *(.bss) }
.data : AT ( ADDR (.text) + SIZEOF (.text) )
{ *(.data) }
这意味着数据部分现在具有不同的 LMA 和 VMA(加载内存地址/虚拟内存地址)。您的程序将期望在 VMA(运行时地址)找到数据,但数据实际上存在于 LMA(您可能必须教您的 flasher 这样做),它就在
.text
之后。部分。如果您需要弄清楚复制到哪里和从哪里复制,那么您可以在链接描述文件中创建指针。因此,像这样修改上面的例子:
.data : AT ( ADDR (.text) + SIZEOF (.text) )
{ _data_lma = LOADADDR(.data); _data_vma = .;
*(.data);
_data_size = SIZEOF (.data);}
然后你可以做
memcpy (_data_vma, _data_lma, _data_size)
在您的启动代码中(尽管您可能需要在汇编程序中手动编写代码?)这些符号将作为在链接时解析的常量全局变量出现在您的程序中。因此,在您的汇编代码中,您可能有以下内容:_data_lma_k:
.long _data_lma
然后数字将被硬编码到
.text
部分。当然,汇编器语法在您的平台上可能会有所不同。有关更多信息,请参阅链接器手册 here
关于gcc - 我怎么知道 .data 部分需要从哪里获取 init 数据? (gcc 链接器),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9288822/