We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hello everyone, I’m working on a project (for learning purposes) where I want to implement a loader for a position-independent executable (PIE) on a Cortex-M microcontroller. My goal is to load the binary at runtime into an arbitrary memory location and let it run as a PIE.
I’m using the Arm GNU toolchain, compiling the modules with -fPIE and linking with -pie. So far, all my builds have worked fine and seem to behave correctly as position-independent code when relocated by my loader. However, I would like to understand whether the PIE support in this toolchain is officially supported on Cortex-M targets, or whether I’ve just been lucky so far. Are there any known limitations or caveats in relying on -fPIE/-pie for embedded (bare-metal) Cortex-M use cases?
-fPIE
-pie
About the loader
I’m now trying to focus on what the loader should actually do, and whether my current approach is correct and sufficient.
At runtime, I load the raw binary image, which includes all sections marked with ALLOC (text, data, bss, etc.). I have external information that gives me the offsets, addresses, and sizes of these sections (manually extracted from the ELF, but this could be automated).
ALLOC
I have noticed:
The .rel.dyn section contains relocation entries, all of which have R_ARM_RELATIVE (0x17) as their r_info type. My understanding is that for these relocations, I just need to add the load address offset to the values at the specified offsets.
.rel.dyn
R_ARM_RELATIVE
r_info
The .got section contains pointers used by the code. There is also a .got.plt section, but I see no relocation entries referring to it in .rel.dyn. Do I need to manually initialize .got.plt? Or can I ignore it, since I’m not using dynamic linking?
.got
.got.plt
The .dynamic section is present as well. Since I’m not doing dynamic linking (and there’s no dynamic linker on Cortex-M), can this section be ignored? Or does it contain information useful for relocation (e.g., addresses, counts for .rel.dyn entries)?
.dynamic
What I think the loader must do
Here’s the basic sequence of what my loader currently does:
Copy/load the code and data sections to RAM.
Iterate over the .rel.dyn entries and apply the base address relocation (for R_ARM_RELATIVE).
Optionally initialize the stack pointer and jump to the entry point.
Is this complete? Am I missing anything fundamental, especially related to .got.plt or .dynamic sections?
Summary
Is PIE support (-fPIE/-pie) in Arm GNU toolchain (in particulare newlib-nano) for Cortex-M an officially supported feature? Or is its correctness a side effect?
Is the approach I described for the loader sufficient to correctly relocate and run a PIE binary on Cortex-M?
Any guidance or references on how to build robust PIE loaders on bare-metal Arm microcontrollers would be greatly appreciated!