11

在构建基于 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

然后我可以查看整个系统生成的 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 的初始化数据在哪里?

我可以使用 readelf 或 objdump 等任何 binutils 命令找到这些数据的位置吗?

/谢谢


这是在 stm32 (Cortex M3) mcu 上,使用的是 CodeBench 版本的 gcc。

4

1 回答 1

15

编译器将所有代码和一些只读数据放在该.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部分中。当然,汇编器语法在您的平台上可能会有所不同。

有关详细信息,请参阅此处的链接器手册

于 2012-02-15T12:44:58.840 回答