I have updated my arm-none-eabi GCC and the associated tools and rebuilt an embedded project I develop.
$ arm-none-eabi-ld --version
GNU ld (GNU Binutils) 2.39
Suddenly, I'm getting the warning
/usr/lib/gcc/arm-none-eabi/12.1.0/../../../../arm-none-eabi/bin/ld: warning: my_elf_file.elf has a LOAD segment with RWX permissions
.
This warning seems to be newly introduced. I haven't changed the source / linkerscript lately. (EDIT: I checked an old ELF file created with a previous version. It didn't print the warning during linking but has the same issue). I develop for an STM32F407 microcontroller. The memory configuration in my linker script is the following:
MEMORY
{
FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
Looking into the linked ELF I see:
$ readelf -l my_elf_file.elf
Elf file type is EXEC (Executable file)
Entry point 0x800b1f1
There are 5 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08000000 0x08000000 0x2220c 0x2220c RWE 0x10000
LOAD 0x040000 0x10000000 0x0802220c 0x003e8 0x00d30 RW 0x10000
LOAD 0x050000 0x20000000 0x080225f4 0x00c1c 0x00c1c RW 0x10000
LOAD 0x000c20 0x20000c20 0x08023210 0x00000 0x01e70 RW 0x10000
LOAD 0x002a90 0x20002a90 0x08023210 0x00000 0x08580 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .vectors .text .ARM .flashcrc
01 .ccmdata .ccmbss
02 .data
03 .bss
04 .heap_stack
Indeed, the first segment is flagged as RWE. It contains the sections .vectors, .text, .ARM and .flashcrc.
The .vectors section and the .text section contain the Vector table and the program code. I added another section in my linkerscript called .flashcrc
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >FLASH =0xFF
I use this section in the source code to position a const struct in there, that contains CRC checksums. These checksums are calculated and patched into the ELF later by a spearate python script. The struct is easier to find in the ELF, if it resides in its own section.
Removing this section or simply relocating it to RAM like this:
.flashcrc : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >RAM AT >FLASH =0xFF
removed the "W" flag from the segment and the warning is gone.
I don't understand why the ELF file contains an "writable" flag for a section that is located in FLASH which is marked in the linkerscript as non writable. I tried using (xr!w)
in the MEMORY definition, but it didn't change anything.
How do I convince the linker that the segment is not writable to silence this warning? Why don't the flags given in the MEMORY part of the linker script have any impact?
Weirdly enough this doesn't happen with the .vectors section which contains a const array of function pointers. So this section is basically identical to the .flashcrc section.
EDIT2:
Today I found a little more time to play around. The struct in the .flashcrc
section is defined in code (globally) like this:
volatile const struct flash_crcs __attribute__((section(".flashcrc"))) crcs_in_flash = {
.start_magic = 0xA8BE53F9UL,
.crc_section_ccm_data = 0UL,
.crc_section_text = 0UL,
.crc_section_data = 0UL,
.crc_section_vectors = 0UL,
.end_magic = 0xFFA582FFUL,
};
The crc values are patched into the ELF after linking. I had to make the struct volatile. If it isn't volatile, the compiler optimizes away the access to the struct because the elements are all 0 from its perspective as it doesn't know that these are patched after linking.
It turns out, that the warning disappears, if the volatile
keyword is removed. For some reason the volatile keyword tricks the compiler/linker into thinking that this should be writable.
Is there another way to prevent the compiler optimizing away the access to this struct but not using volatile?
Prior to version 13.1.0, GCC would place const volatile
objects in a writeable section (named .data
by default) instead of a read-only section (.rodata
) like it does with all other const
objects. Here's the patch where it was changed.
The linker sets the attributes of the output .flashcrc
section automatically based on the input sections that make it up. You can override it like this:
.flashcrc (READONLY) : ALIGN(4)
{
KEEP(*(.flashcrc))
KEEP(*(.flashcrc.*))
. = ALIGN(4);
} >FLASH =0xFF
See the Output Section Attributes and Output Section Type sections of the ld documentation for more detail. If you need compatibility with older toolchains, note that the READONLY type was added to ld relatively recently.
Likewise, the flags on the segments are set based on the sections they contain, so you end up with RWX for the one that covers flash. The attributes you specified for the different regions within the MEMORY command don't impact this. Per the documentation, those attributes "specify whether to use a particular memory region for an input section which is not explicitly mapped in the linker script."
I would also like to point out that the flags in the elf file have no bearing on whether memory is actually writable or executable on typical embedded platforms. You got a warning that was in some ways a false positive. It complained about RWX memory that is RX in reality. It's possible to get a false negative as well. If you were to put some code in ITCM without reconfiguring the MPU after loading, that memory would be RWX but you wouldn't get that now-desirable warning since it's RX according to the elf file.