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

标签 memory linker arm embedded microcontroller

我在 工作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 是 0x80000000x8010000 ,什么都很好,因为它们是在链接器脚本文件中以这种方式定义的。此外,根据该报告,这些部分的偏移量为 0x100000x20000分别。接下来,我执行以下命令转储与 .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 和二进制图像时,您应该注意这种行为。

总结一下构建和刷机过程:
  • 链接时,起始地址在 ldscript 中定义,并且 elf 中的第一部分位于那里。
  • 转换为二进制时,起始地址由 LMA 确定,二进制图像从该地址开始。
  • 闪烁图像时,将相同的地址作为参数赋予 flasher,因此图像放置在正确的位置(在 ldscript 中定义)。

  • 更新: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/

    相关文章:

    linux - 内存:堆栈和交换

    c++ - 通过更改 C++ 中的内存来损坏计算机的风险

    reactjs - 为什么 React 应用程序在 Heroku 上会占用大量内存?

    c++ - 仅当链接为 -static 时,才会从链接库调用包装函数

    gcc - ARM Cortex M4 SVC_Handler "UsageFault"

    java - 获取Android LRUCache当前使用的内存

    c++ - 将文件从另一个 .vcproj 链接/包含到现有 .vcproj

    c++ - 关于 undefined reference 的令人沮丧的 C++ 编译器错误

    linux - 如何修改流程中的指令? Linux 和 ARMv7

    assembly - 使用汇编语言除奇数