我在 工作ARM 皮质 M4 ( STM32F4xxxx ),我试图了解二进制文件( *.elf
和 *.bin
)是如何在内存中构建和闪存的,特别是关于内存位置。具体来说,我不明白LMA
从实际的二进制文件偏移量“翻译”。让我用一个例子来解释:
我有一个 *.elf
文件,其(相关)部分如下:(从 objdump -h
获得)
my_file.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 000001c4 08010000 08010000 00020000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .bootloader 00004000 08000000 08000000 00010000 2**0
CONTENTS, ALLOC, LOAD, DATA
根据该文件,VMA 和 LMA 是
0x8000000
和 0x8010000
,什么都很好,因为它们是在链接器脚本文件中以这种方式定义的。此外,根据该报告,这些部分的偏移量为 0x10000
和 0x20000
分别。接下来,我执行以下命令转储与 .bootloader
对应的内存:xxd -s 0x10000 -l 16 my_file.elf
00010000: b007 c0de b007 c0de b007 c0de b007 c0de ................
现在,创建要闪存到内存中的二进制文件:
arm-none-eabi-objcopy -O binary --gap-fill 0xFF -S my_file.elf my_file.bin
根据上面提供的信息,据我了解,生成的二进制文件应该有
.bootloader
部分位于 0x8000000
.我知道这不是它的实际工作方式,因为文件会变得非常大,所以 bootloader
放在文件的开头,所以地址0x0
(检查两个内存块是否相同,即使它们位于不同的地址):xxd -s 0x00000 -l 16 my_file.bin
00000000: b007 c0de b007 c0de b007 c0de b007 c0de ................
据我了解,当提到的二进制文件闪存到内存中时,
bootloader
将在地址 0x0
, 考虑到有问题的 MCU 跳转到地址 0x4
(从 0x0
获取 SP 之后)当它开始工作时,正如我在这里检查过的那样(第 26 页):https://www.st.com/content/ccc/resource/technical/document/application_note/76/f9/c8/10/8a/33/4b/f0/DM00115714.pdf/files/DM00115714.pdf/jcr:content/translations/en.DM00115714.pdf最后,我的问题是:
请问
bootloader
实际放在0x0
?如果是这样,在链接器文件中定义内存扇区的目的是什么?这是因为
0x0
属于flash memory,当MCU启动时,flash全部复制到RAM
地址 0x8000000
?如果是这样,bootloader
从闪存执行,其余代码来自 RAM
?考虑到上述问题,如果我什么都不懂,
LMA
之间的关系/区别是什么?和 File offset
?
最佳答案
不,引导加载程序将位于 08000000,如 elf 文件中所定义。
图像将在该地址刻录在闪存中并直接从那里执行(不会复制到其他地方左右)。
有一些未记录的行为,即在生成二进制图像时跳过实际数据之前的未初始化区域。正如 BFDlib 源状态中的评论( https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=bfd/binary.c;h=37f5f9f7363e7349612cdfc8bc579369bbabbc0c;hb=HEAD#l238 )
/* The lowest section LMA sets the virtual address of the start
of the file. We use this to set the file position of all the
sections. */
.elf 中的最低部分 (.bootloader) LMA 为 08000000,因此二进制文件将从该地址开始。
在确定图像中的地址时,您应该考虑此地址并将其添加到文件偏移量中。
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 000001c4 08010000 08010000 00020000 2**0
/* ^^^^^^^^ */
/* this section will be at offset 10000 in image */
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .bootloader 00004000 08000000 08000000 00010000 2**0
/* ^^^^^^^^ */
/* this is the lowest LMA in your case it will be used */
/* as a start of an image, and this section will be placed */
/* directly at start of the image */
CONTENTS, ALLOC, LOAD, DATA
Memory layout: Bin. image layout:
000000000 \ skipped
... ________________ /
080000000 .bootloader 0
... ________________
080004000 <gap> 4000
... ________________
080010000 .text 10000
... ________________
0800101C4 101C4
该地址在 ldscript 中定义,因此二进制图像应从固定位置开始。但是,在处理 ldscrips 和二进制图像时,您应该注意这种行为。
总结一下构建和刷机过程:
更新:STM32F4xxx 启动过程。
从地址 0 开始的地址区域对于那些 MCU 来说是特殊的。它可以配置为映射其他区域,如闪存、SRAM 或系统 ROM。它们由引脚
BOOTSELx
选择.从 CPU 端看,第二个闪存副本(SRAM 或系统 ROM)似乎出现在地址 0 处。
CPU 启动时,首先从地址 0 读取初始 SP,从地址 4 读取初始 PC。实际上,执行的是从闪存读取。
如果代码链接到从实际闪存位置运行,那么初始 PC 将指向那里。在这种情况下,执行从实际闪存地址开始。
----- Mapped area (mimics contents as flash) ---
0: (02001000) ;
4: (0800ABCD) ----. ; CPU reads PC here
.... | ; (it points to flash)
----- FLASH ----- |
8000000: 20001000 | ; initial stack pointer
8000004: 0800ABCD --. | ; address of _start in flash
.... | |
800ABCD: <_start:> movw ... <-'<-' ; Code execution starts here
(注意:这不适用于 hex 图像(如 intel hex 或 s-record),因为此类格式明确定义了加载地址,并按原样使用)。
关于memory - 试图了解 ARM 二进制镜像中的加载内存地址 (LMA) 和二进制文件偏移量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54578360/