我如何知道.data节需要从哪里获取init数据?(gcc链接器)

rdlzhqv9  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(174)

在构建基于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 out put中找不到任何应该放置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

然后,我可以查看整个系统的结果elf,在本例中为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版本。

3okqufwl

3okqufwl1#

编译器把所有的代码和一些只读数据放在.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部分。当然,汇编语法在您的平台上可能会有所不同。
有关详细信息,请参阅此处的链接器手册

相关问题