11.1. On position independent firmware images

Compiling firmware images as a position independent code is hard as stated by various sources, eg. https://mcuoneclipse.com/2021/06/05/position-independent-code-with-gcc-for-arm-cortex-m/ or https://www.eevblog.com/forum/microcontrollers/bare-metal-arm-gcc-position-independent-code-possible/. Some even claim they are the only on the Earth to have solved the issue - but many things still don’t work - https://techblog.paalijarvi.fi/2022/01/16/portable-position-independent-code-pic-bootloader-and-firmware-for-arm-cortex-m0-and-cortex-m4/

As per the eevblog thread, the following flags are required:

1CFLAGS += -fPIC -mno-pic-data-is-text-relative -msingle-pic-base -mpic-register=r9
2LFLAGS += -fPIC

R9 value has to be initialised on startup:

1asm("ldr r9, =_data");

Oh but we are using FreeRTOS which trashes R9 content when a new thread is created. It needs a modification:

 1diff --git a/lib/freertos/portable/GCC/ARM_CM4F/port.c b/lib/freertos/portable/GCC/ARM_CM4F/port.c
 2index d5cbef4..85dec97 100644
 3--- a/lib/freertos/portable/GCC/ARM_CM4F/port.c
 4+++ b/lib/freertos/portable/GCC/ARM_CM4F/port.c
 5@@ -208,7 +208,13 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px
 6    pxTopOfStack--;
 7    *pxTopOfStack = portINITIAL_EXC_RETURN;
 8
 9-       pxTopOfStack -= 8;      /* R11, R10, R9, R8, R7, R6, R5 and R4. */
10+       pxTopOfStack -= 2; /* R11, R10. */
11+
12+       extern uint32_t _data;
13+       pxTopOfStack--; /* R9 */
14+       *pxTopOfStack = (StackType_t)&_data;
15+
16+       pxTopOfStack -= 5;      /* R8, R7, R6, R5 and R4. */
17
18    return pxTopOfStack;
19 }

Linker script modification (using libopencm3):

 1diff --git a/lib/cortex-m-generic.ld b/lib/cortex-m-generic.ld
 2index f7b1da01..819d6bd9 100644
 3--- a/lib/cortex-m-generic.ld
 4+++ b/lib/cortex-m-generic.ld
 5@@ -100,6 +100,8 @@ SECTIONS
 6        *(.data*)       /* Read-write initialized data */
 7        *(.ramtext*)    /* "text" functions to run in ram */
 8        . = ALIGN(4);
 9+               _got = .;
10+               *(.got)
11        _edata = .;
12    } >ram AT >rom
13    _data_loadaddr = LOADADDR(.data);

Now it starts to behave until it goes to a Newlib function compiled without PIC support.

TODO: Newlib has to be recompiled with PIC support