Search code examples
memorylinkerarmembeddedmicrocontroller

Trying to understand the load memory address (LMA) and the binary file offset in an ARM binary image


I'm working in an ARM Cortex M4 (STM32F4xxxx) and I'm trying to understand how exactly the binaries (*.elf and *.bin) are built and flashed in memory, specially with regards to the memory locations. Specifically, what I don't understand is how the LMA gets 'translated' from the actual binary file offset. Let me explain with an example:

I have an *.elf file whose (relevant) sections are the following ones:(obtained from 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

According to that file, the VMA and LMA are 0x8000000 and 0x8010000, what is perfectly fine since they are defined that way in the linker script file. In addition, according to that report, the offsets of those sections are 0x10000 and 0x20000 respectively. Next, I execute the following command for dumping the memory corresponding to the .bootloader:

xxd -s 0x10000 -l 16 my_file.elf
00010000: b007 c0de b007 c0de b007 c0de b007 c0de  ................ 

Now, create the binary file to be flashed into memory:

arm-none-eabi-objcopy -O binary --gap-fill 0xFF -S my_file.elf my_file.bin 

According to the information provided above, and as far as I understand, the generated binary file should have the .bootloader section located at 0x8000000. I understand that this is not how it actually works, inasmuch as the file would get extremely big, so the bootloader is placed at the beginning of the file, so the address 0x0 (check that both memory chunks are identical, even though the are at different addresses):

xxd -s 0x00000 -l 16 my_file.bin
00000000: b007 c0de b007 c0de b007 c0de b007 c0de  ................

As far as I understand, when the mentioned binary file is flashed into memory, the bootloader will be at address 0x0, what is perfectly fine taking into account that the MCU in question jumps to the address 0x4 (after getting the SP from 0x0) when it starts working, as I have checked here (page 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

Finally, my questions are:

Will the bootloader actually be placed at 0x0? If so, what's the purpose of defining the memory sectors in the linker file?

Is this because 0x0 belongs to flash memory, and when the MCU starts, all the flash is copied into RAM at address 0x8000000? If so, will the bootloader be executed from flash memory and all the rest of the code from RAM?

Taking into account the above questions, if I have not understood anything, what's the relation/difference between the LMA and the File offset?


Solution

  • No, bootloader will be at 08000000, as defined in elf file.

    Image will be burned in flash at that address and executed directly from there (not copied somewhere else or so).

    There's somewhat undocumented behaviour, that unitialized area before actual data is skipped when producing binary image. As comment in BFDlib source states (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.  */
    

    Lowest section (.bootloader) LMA is 08000000 in your .elf, so binary file will start at this address.
    You should take this address into account and add it to file offset when determining address in the image.

    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
    
    

    That address defined in ldscript, so binary image should start at fixed location. However you should be aware of this behaviour when dealing with ldscrips and binary images.

    To summarize building and flashing process:

    1. When linking, start address is defined in ldscript, and and first section in elf located there.
    2. When converting to binary, start address is determined from LMA and binary image starts from that address.
    3. When flashing image, same address given to flasher as a parameter, so image is placed at the right place (defined in ldscript).

    Update: STM32F4xxx booting process.

    Address region starting at address 0 is special to those MCUs. It can be configured to map other regions, which are flash, SRAM, or system ROM. They're selected by pins BOOTSELx. From CPU side it looks like second copy of flash (SRAM or system ROM) appears at address 0.

    When CPU starts, it first reads initial SP from adress 0 and initial PC from address 4. Actually, reads from the flash memory are performed. If the code is linked to run from actual flash location, then initial PC will point there. In this case execution starts at actual flash address.

    ----- 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
    

    (Note: this does not apply to hex images (like intel hex or s-record) as such formats define loading address explicitly and it is used as is there).