试图了解 ARM 二进制映像中的加载内存地址 (LMA) 和二进制文件偏移量

Dan*_*Dan 5 memory embedded microcontroller linker arm

我正在ARM Cortex 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
Run Code Online (Sandbox Code Playgroud)

根据该文件,VMA 和 LMA 是0x80000000x8010000,这完全没问题,因为它们在链接器脚本文件中以这种方式定义。此外,根据该报告,这些部分的偏移量分别为0x100000x20000。接下来,我执行以下命令来转储对应的内存.bootloader

xxd -s 0x10000 -l 16 my_file.elf
00010000: b007 c0de b007 c0de b007 c0de b007 c0de  ................ 
Run Code Online (Sandbox Code Playgroud)

现在,创建要刷入内存的二进制文件:

arm-none-eabi-objcopy -O binary --gap-fill 0xFF -S my_file.elf my_file.bin 
Run Code Online (Sandbox Code Playgroud)

根据上面提供的信息,据我所知,生成的二进制文件应该有.bootloader位于0x8000000. 我知道这不是它的实际工作方式,因为文件会变得非常大,所以bootloader放在文件的开头,所以地址0x0(检查两个内存块是否相同,即使它们位于不同的地址):

xxd -s 0x00000 -l 16 my_file.bin
00000000: b007 c0de b007 c0de b007 c0de b007 c0de  ................
Run Code Online (Sandbox Code Playgroud)

据我了解,当提到的二进制文件被刷入内存时,bootloader将在地址0x0,考虑到有问题的 MCU 在它开始工作时跳转到地址0x4(从 获取 SP 后0x0),这完全没问题,正如我在这里查看的(第 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

考虑到上述问题,如果我还没有理解任何东西,theLMA和 the之间的关系/区别是File offset什么?

Vov*_*ium 3

不,引导加载程序将位于 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.  */
Run Code Online (Sandbox Code Playgroud)

.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

Run Code Online (Sandbox Code Playgroud)

该地址在 ldscript 中定义,因此二进制图像应从固定位置开始。但是,在处理 ldscrip 和二进制图像时,您应该注意这种行为。

总结一下构建和刷新过程:

  1. 链接时,起始地址在 ldscript 中定义,并且 elf 中的第一部分位于此处。
  2. 转换为二进制时,起始地址由 LMA 确定,二进制图像从该地址开始。
  3. 当刷新图像时,相同的地址作为参数提供给 flasher,因此图像被放置在正确的位置(在 ldscript 中定义)。

更新:STM32F4xxx启动过程。

从地址 0 开始的地址区域对于这些 MCU 来说是特殊的。它可以配置为映射其他区域,例如闪存、SRAM 或系统 ROM。它们是通过 pin 选择的BOOTSELx。从 CPU 端来看,闪存的第二个副本(SRAM 或系统 ROM)出现在地址 0 处。

CPU启动时,首先从地址0读取初始SP,从地址4读取初始PC。实际上,是从Flash存储器中读取。如果代码链接为从实际闪存位置运行,则初始 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
Run Code Online (Sandbox Code Playgroud)

(注意:这不适用于十六进制图像(如英特尔十六进制或 s-record),因为此类格式明确定义了加载地址,并且按原样使用)。

  • 有一种称为“映射”或“别名”的“魔法”。首先,2.4节描述了几种STM32F4芯片的启动过程。根据“BOOTSEL”引脚闪存,内部引导加载程序或 SRAM 可能会映射到从 0 开始的区域。映射的闪存就是您的情况。所以CPU实际上是从0x4开始的,它是从0x08000004映射的。该地址不包含要执行的代码,而是包含Reset Exception Vector,它是要执行的第一条指令的绝对地址。当您将图像链接到地址 08000000 时,它将指向闪存中的某个位置,CPU 就在那里开始执行。 (3认同)
  • 请注意,实际的重映射由 SYSCFG_MEMRMP 寄存器的 MEM_MODE 字段控制,这就是记录重映射模式的位置(在参考手册中)。内置引导加载程序根据引导引脚设置该寄存器。 (2认同)