Search code examples
gcclinkergnu-assemblerlinker-scriptsbinutils

Data Integrity Issue with Custom .rawdata Section in Linker Script


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


Solution

  • 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