I seek to implement a position independent firmware to achieve a dual slot fota strategy:
I followed the steps described by the UM2609 guide for stm32CubeIDE chapter 2.8 which covers this exact requirement https://www.st.com/resource/en/user_manual/um2609-stm32cubeide-user-guide-stmicroelectronics.pdf .
At this point:
At this point I think I am maybe missing something regarding global variables relocation, or am I doing something else wrong?
I hope any of you can shed light on this mistery as online information regarding this subject is very scarce!
++++ UPDATE ++++
I have discovered that by disabling the -fpie option the code runs even if I load it in a different address than where it is supposed to be placed (by linker script) and does not crash the way it did with -fpie was enabled,
Things work like a charm, but I do not understand:
Why does my compiled code seem to be position independent with default when neither -fpic, -fpie or such are enabled?
Do you have the program loaded into both areas? Specially if the same image is loaded into both areas, code running in the 2nd area may "call" functions in the first area.
For example, code is compiled/linked to start at 0x0800 0000. Function MyInit() is at 0x0800 1000. Code is loaded at 0x0800 0000 (1st area) and 0x0801 0000 (2nd area). Yes, I know this doesn't allow for the bootloader that is probably really at 0x0800 0000. So the bootloader starts running the program in the 2nd area. It gets to the MyInit() call and calls the (fixed) address of MyInit() at 0x0800 1000, NOT what would be the position-independent address of 0x0801 1000.
Hi, no, the flash is formatted beforehand and I only upload the "application-fw" binary "manually"using stm32CubeProgrammer, then I upload and debug the bootloader using stm32cubeide (and I also load the application-fw debug information with it but without uploading the application of course).
The main firmware and the bootloader share the stm32 libraries initializations but then they call different functions and print different texts on the terminal.
In more detail:
Probably not the issue, but did you modify the startup code to copy "rodata' from Flash into RAM? I don't THINK the stock startup will do that.
The assembly code you showed looks a bit odd, specially the "add r3, pc" line. Does that source code generate the same assembly without the -fPIE flag? Though even subtracting out the PC value it STILL doesn't give the correct address.
Besides why fpie was shifting the load addresses in that manner, I would like also to know how come the code without fpie is still somewhat position independent as I see that functions are called and jumps are performed always relatively to the program counter!
Another Update: regarding my first firmware, the one with -fpie, I now notice that the disassembly loads the global offset table exactly 0x10000 away from where it is actually located. 0x10000 is coincideentally the offset at which I load the firmware with respect to the original linker placement....
So I wonder, it looks like the program expects the GOT to be placed relatively to the PC with a fixed offset, but if the firmware gets uploaded in a different place than the linker's expected location, the GOT location calculated in that way becomes invalid.
What is the point of having position independent code if the global offset table is loaded in a position-dependent manner?Am I missing something?
So, to answer my last message, the key is to use -fpic in conjunction with -msingle-pic-base (remove -fpie), this option will make the compiler reference the GOT from a fixed register (R9 by default).
To allow this option to be effective the bootloader must load the correct ram address of the GOT (already relocated to ram and patched with the offset where necessary) into R9 right before launching the application firmware.
(Another way would be to populate R9 in the startup code of the firmware itself)
I am still experiencing some problems tho, i will update the post as soon as I discover something more
Been a while since I've spent a lot of time on ELF objects at this level. LINUX would allow for relocation, etc.
In KEIL the ARM ABI used R9 as a means of communicating data addressing, where the code / data live at unrelated addresses, and there might be multiple instances / threads, ie code exists once, data exists in multiple different and unrelated contexts. In STM32 usage I've seen this with several of the .STLDR (External Loaders) when built with Keil using address independent options, and perhaps not adequately addressed by STM32 Cube Programmer or ST-LINK Utilities in furnishing the execution environment.
So, the problem now seems to be that static variables are somehow not listed by the .got.
When trying to access one of these static zero-initialized variables (so living int he .bss section) , the program goes into hardfault because it loads it from the wrong address.
This is the content of the .got section and since .bss starts at 0x20000f20 and ends at 0x200015d8 you can see several entries belong to the .bss section, but others do not appear, like this one below
Which is this static array
When the program tries to access this variable it does so at 0x1fff15d8 whic is exactly 0x10000 away from where it actually is ( and 0x10000 is the offset by which I upload the code to test the position independence).
This is because said variable is loaded by adding an offset to the program counter PC, if it were to be loaded via GOT this would not happen!
So why are local static variables not addressed via GOT while global variables are? is there a way to ensure GOT coverage for all static variables?