I'm working on an embedded project using an ARM Cortex-M4 processor (specifically, an STM32F4
microcontroller) and I've encountered a peculiar issue related to memory sections defined in my linker script. I'm using the arm-none-eabi-gcc
toolchain for compilation.
I have a binary file that I want to include in my firmware. I've created a custom section (.rawdata
) in my assembly file to include this binary file, and I've modified my linker script
to place this section in the Flash
memory. However, I'm facing an issue with the integrity of the data in the .rawdata section during runtime.
When I place the .rawdata
section within the .data
section or in the .text
section in my linker script
, everything works as expected: the data is correctly allocated in memory, and the actual data is stored correctly. I can access and print the data during runtime without any issues.
However, when I try to create a standalone .rawdata
section (outside of .data
) or place it within the .text
section, the data seems to be allocated, but the content is not what I expect. During runtime, when I print the content of the .rawdata
section, I get incorrect values (not matching the original .bin
content).
Interestingly, when I inspect the .elf
file using arm-none-eabi-objdump
, the .rawdata
section appears to contain the correct data. This discrepancy only occurs during runtime.
Linker Script Snippet:
ld
.rawdata :
{
. = ALIGN(4);
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} >FLASH
Why does placing the .rawdata
section inside the .data
section work correctly, but creating it as a standalone section leads to incorrect runtime data?
Is there a specific consideration or configuration I'm missing for custom sections in the linker script for ARM Cortex-M4 processors?
How can I ensure that the .rawdata
section's data remains intact during runtime when it's defined outside the .data section?
Any insights or suggestions on how to resolve this issue would be greatly appreciated. Thank you in advance for your help!
if I check using
arm-none-eabi-objdump -h -j .rawdata blink.elf
arm-none-eabi-objdump -s -j .rawdata blink.elf
blink.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
1 .rawdata 0000003c 080001c8 080001c8 000071dc 2**0
CONTENTS, READONLY
blink.elf: file format elf32-littlearm
Contents of section .rawdata:
80001c8 cdcccc3d cdcc4c3e 9a99993e cdcccc3e ...=..L>...>...>
80001d8 0000003f 9a99193f 3333333f cdcc4c3f ...?...?333?..L?
80001e8 6666663f 0000803f cdcc8c3f 9a99993f fff?...?...?...?
80001f8 6666a63f 3333b33f 0000c03f ff.?33.?...?
which is the correct data that should be loaded.
But if during runtime I check printing the content inside that memory area with USART I get:
data size: 60 bytes
Start address: 0x80001c8
End address: 0x8000204
00000000
00000000
3f501eb4
bfe008ee
3d600962
416277e1
c6ffe8d3
3c201ffe
40c1e8d1
41428902
c6004342
4161df76
c2000560
4161a8f3
c2000560
in which the address is the same as above but the content in memory is different.
Instead,
if I place the .rawdata
inside .text
or .data
sections I get the same memory contents as it should.
Now just to add, I have found that using: arm-none-eabi-objcopy -O binary blink.elf so.bin
the .rawdata
data disappears, I have checked using hexdump
and the actual data were just in the .elf file but not in the generated .bin.
Also, I have noticed some conflicts in the offset of the .hex
file:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08000000 001000 0001c8 00 WA 0 0 4
[ 2] .rawdata PROGBITS 080001c8 0071dc 00003c 00 0 0 1
[ 3] .text PROGBITS 080001d0 0011d0 0053cc 00 AX 0 0 16
[ 4] .data PROGBITS 20000000 007000 0001dc 00 WA 0 0 4
[ 5] .bss NOBITS 200001dc 0071dc 00017c 00 WA 0 0 4
you can see that both .rawdata
and .bss
are in the same offset in the hex file, but .bss is marked as TYPE NOBITS, so it should not take any space for data in the .hex file, I don't know if this can also cause issues
To solve this problem and create a custom section, in the assembly file, add the "aw" flags to the .rawdata
section:
.section .rawdata,"aw"
.incbin "my.bin"
From the binutils docs:
The "aw" flags specify that the section should be allocated (a) and writable (w).
This ensures that the section is properly allocated in memory and can be accessed and modified during runtime.
If no flags are specified, the default flags depend upon the section name. If the section name is not recognized, the default will be for the section to have none of the above flags: it will not be allocated in memory, nor writable, nor executable. The section will contain data.
So according to the docs, the section won't be copied from the .elf
to the .bin
using arm-none-eabi-objcopy -O binary blink.elf so.bin
, nor flashed
to the MCU used tools like openocd
, but is going to be discarded since it has no section header flags in it:
this is what we get in the .elf without specifying any flags
arm-none-eabi-readelf -a blink.elf
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08000000 001000 0001c8 00 WA 0 0 4
[ 2] .rawdata PROGBITS 080001c8 0071dc 00003c 00 0 0 1
[ 3] .text PROGBITS 080001d0 0011d0 0053cc 00 AX 0 0 16
[ 4] .data PROGBITS 20000000 007000 0001dc 00 WA 0 0 4
[ 5] .bss NOBITS 200001dc 0071dc 00017c 00 WA 0 0 4
Instead if we add "wa" flags we get:
arm-none-eabi-readelf -a blink.elf
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08000000 001000 0001c8 00 WA 0 0 4
[ 2] .rawdata PROGBITS 080001c8 0011c8 00003c 00 WA 0 0 4
[ 3] .text PROGBITS 08000210 001210 0053cc 00 AX 0 0 16
[ 4] .data PROGBITS 20000000 007000 0001dc 00 WA 0 0 4
[ 5] .bss NOBITS 200001dc 0071dc 00017c 00 WA 0 0 4
You can see that flags are added, and section offset are ordered as specified in the linker script
.
From this, you can see that without specifying any flags in the declaration of the section the linker will put in the same offset of the .elf
file the .bss
section and the .rawdata
, this is not happening with flags specified.
In the end, .rawdata
section will look like this and will correctly load data in the MCU at _mstart
:
.rawdata : ALIGN(4)
{
_mstart = .;
KEEP(*(.rawdata))
. = ALIGN(4);
_mend = .;
} >FLASH