I'm currently writing a program on STM32 that uses a simple bootloader and two sub-applications. The sub-applications are located in flash memory and the bootloader loads one of them (both code and data) into the RAM memory and then starts executing it. The copying is done simply by iterating over the addresses in flash and copying data from these addresses to RAM.
Recently I've encountered some strange bug. I was trying to read some data from a globally defined array. I was getting back wrong values, e.g. when I tried to read array[0]
I was getting back the value of array[1]
.
I did some debugging and disassembly of the .elf and .hex files and I think I've found the cause of this bug. It turned out that there is an empty space between the .ARM
section of the .elf file of this project.
Surprisingly, this empty space is not present in the .hex file (which I use for flashing the STM32 board).
This is what I am talking about:
2000ee70: 469e mov lr, r3
2000ee72: 4770 bx lr
Disassembly of section .ARM:
2000ee74 <__exidx_start>:
2000ee74: 7fff267c svcvc 0x00ff267c
2000ee78: 00000001 andeq r0, r0, r1
Disassembly of section .data:
2000ee80 <evnames.5255>:
2000ee80: 00000000 andeq r0, r0, r0
2000ee84: 08030104 stmdaeq r3, {r2, r8}
802de70: 4770469e
802de74: 7fff267c svcvc 0x00ff267c
802de78: 00000001 andeq r0, r0, r1
802de7c: 00000000 andeq r0, r0, r0
802de80: 08030104 stmdaeq r3, {r2, r8}
Obviously, the addresses are different because the addresses in the .elf file are VMA and the addresses in the .hex file are LMA.
What I've noticed here is that after the .ARM
section, the next memory address should be 2000ee7C
but from unknown reasons, the .data
section begins at 2000ee80
. So there is one unexplained, empty word between them. But this empty word is not present in the .hex file. The 00000001
is immediately followed by 00000000
.
So basically, I think the disassembly of the .hex file should output the following result:
802de70: 4770469e
802de74: 7fff267c svcvc 0x00ff267c
802de78: 00000001 andeq r0, r0, r1
802de7c: <something here>
802de80: 00000000 andeq r0, r0, r0
802de84: 08030104 stmdaeq r3, {r2, r8}
Because of this empty memory space, which disappears in the .hex file, when my bootloader loads the data to RAM, the array[0]
which LMA is 2000eec8
ends up being at the address 2000eec4
.
This is the troubling snippet of the linker script that I use:
/* ARM specific sections, they also go to FLASH and are copied to RAM */
.ARM.extab : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAMAPP AT> FLASH_APP
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > RAMAPP AT> FLASH_APP
/* Initialized data sections - variables etc. */
.data :
{
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
} >RAMAPP AT> FLASH_APP /* Data section is placed in FLASH_APP but its Virtual Memory Address is in RAM_APP */
I use the following command to link the .o files into one .elf file:
arm-none-eabi-gcc -Wl,--gc-sections -mthumb -mthumb-interwork -mcpu=cortex-m4 --specs=nosys.specs -L[path_to_library_files] -T[path_to_ld_file] [long_list_of_object_files] -o [output_elf_file]
I use the following command to convert the .elf file into the .hex file:
arm-none-eabi-objcopy -O ihex [elf_file] [output_hex_file]
I've tried to experiment with the parameters I give to the arm-none-eabi-objcopy
and arm-none-eabi-gcc
to get rid of this padding, for example --file-alignment
or --gap-fill
but to no success so far.
Does anyone have an idea about where does this empty space come from and how to get rid of it (or include it in the .hex file)?
EDIT: According to the suggestions from the first two comments, I've tried:
* using the newest versions of arm-none-eabi-gcc
and arm-none-eabi-objcopy
in my toolchain,
* ALIGN(4)
of the .ARM
section,
* not copying the .ARM
section to RAM.
Unfortunately, none of these solutions fixed this problem.
What I've noticed recently is that sometimes the data is aligned correctly in the .elf
file (the first address of the .data
section is the address that immediately follows the end of the .ARM
section). It depends on the address where the .ARM
section happens to end. I can manipulate that by adding some extra function invocations in the code (resulting in the larger .text
area), for example:
2000ee84: 469e mov lr, r3
2000ee86: 4770 bx lr
Disassembly of section .ARM:
2000ee88 <__exidx_start>:
2000ee88: 7fff2668 svcvc 0x00ff2668
2000ee8c: 00000001 andeq r0, r0, r1
Disassembly of section .data:
2000ee90 <evnames.5255>:
2000ee90: 00000000 andeq r0, r0, r0
2000ee94: 2001111c andcs r1, r1, ip, lsl r1
This way, I've estabilished that:
* when .ARM
section ends at address 0x*******0
the .data
section incorrectly starts at 0x*******8
(should be 0x*******4
)
* when .ARM
section ends at address 0x*******8
the .data
section incorrectly starts at 0x*******0
(should be 0x*******C
)
* when .ARM
section ends at address 0x*******4
the .data
section CORRECTLY starts at 0x*******8
* when .ARM
section ends at address 0x*******C
the .data
section CORRECTLY starts at 0x*******0
Okay, It turned out I had to add ALIGN_WITH_INPUT
to the .data section in the linker script. Like this:
.data : ALIGN_WITH_INPUT
{
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
} >RAMAPP AT> FLASH_APP
This parameter causes the LMAs of the section to be aligned in the same way the VMAs are.
Now the alignment in the .hex file is identical to the one in the .elf file.
Shout out to reddit user FreddieChopin who helped me to resolve this issue.