Solution to LOAD segment with RWX permissions warning with CMSIS

November 11, 2023

I have not seen this is described somewhere, so here is a post about it. It is naturally not specific to CMSIS but I experienced it in this way.

It seems ld has been updated some time ago, and now it emits warning: <filename> has a LOAD segment with RWX permissions when obviously a LOAD segment has RWX permissions. A segment should ideally either have RX (the code segment) or RW (the data segment). RWX would mean a modifiable code, which is a risk.

(ELF uses the term program headers for segments)

The problem is, CMSIS linker script gcc_arm.ld has two sections, called .copy.table and .zero.table. These are used for initializing data and bss segments (actually only data because bss is initialized by C runtime but both has the same concept). These sections does not contain anything other than static data created by the linker, like below:

.copy.table :
{
    . = ALIGN(4);
    __copy_table_start__ = .;
    LONG (__etext)
    LONG (__data_start__)
    LONG ((__data_end__ - __data_start__) / 4)
    __copy_table_end__ = .;
} > FLASH

.zero.table :
{
    . = ALIGN(4);
    __zero_table_start__ = .;
    __zero_table_end__ = .;
} > FLASH

These sections are put into the text segment by the linker, which makes sense because they are used before data segment is initialized. However, not sure exactly why, when linker puts them into the text segment, it is also marked as writable. So the text segment becomes RWX, hence the warning is emitted.

Below is readelf output:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x08000000 0x08000000 0x03b7c 0x03b7c RWE 0x1000
  LOAD           0x005000 0x20000000 0x08003b7c 0x000c8 0x000c8 RW  0x1000
  LOAD           0x0000c8 0x200000c8 0x200000c8 0x00000 0x00174 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .copy.table 
   01     .data 
   02     .bss 

(the .zero.table section is discarded because it is not used)

It seems like warning does not cause a problem at least with ST’s programming tool (STM32CubeProg), however it is still not ideal.

Because it is obvious that the contents of .copy.table and .zero.table are read-only data, the segment does not need to be writable for sure. The solution I found is to define the segments and assign the output sections to the segments manually. When the segments are defined manually, flags can be set explicitly.

Adding the following segment (program header) descriptions to the linker script:

PHDRS 
{
  text PT_LOAD FLAGS(5);
  data PT_LOAD FLAGS(6);
  bss PT_LOAD FLAGS(6);
}

says to create these segments with these particular flags; RX (5) and RW (6).

Then, an output section can be assigned to a particular segment like this:

.text :
{
    ...
} >FLASH :text

When the segments are defined manually, all output sections have to be assigned to the segments manually. After this is done, the segments becomes:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x08000000 0x08000000 0x03b7c 0x03b7c R E 0x1000
  LOAD           0x005000 0x20000000 0x08003b7c 0x000c8 0x000c8 RW  0x1000
  LOAD           0x0000c8 0x200000c8 0x200000c8 0x00000 0x00174 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .copy.table 
   01     .data 
   02     .bss 

The segments are now as they should be, and the warning is not emitted.