I'm working on a project on a cortex-m3 CPU with bare-metal environment. Due to software upgrade needs the executable image on the CPU can be in one of two addresses in the flash memory which presents a problem. The start address of the image is known only at load time and not during static-linkage. I'm not too sure but I think this doesn't really qualify as dynamic loading but I could really be mistaken here as I'm no expert on this. Is there a way to compile and link the image in a way it's base address can be given only at load time given there is no OS and no dynamic loader?
2 Answers
For bare-metal embedded systems like yours, this is accomplished by compiling/linking your code as relocatable code, also known as Position Independent Code.
How this is accomplished is influenced by the processor, and the actual approach is implemented by your development toolset, so you'll want to consult with the documentation for your cross-development tools. It is usually a set of compiler & linker options (incl. your linker command script, if you have one) that determine how things are laid out & what registers are used to access them.
Things become a little easier when you're working with a platform (processor & OS) that supports an MMU (not on the CM3, sorry) - then the code can be located anywhere in physical memory, but via the MMU, its logical address space can be different. Thus, at link time, addresses for code & data can be fixed, and then at load time, the logical address space is set up via the MMU, and the program is none the wiser.
You might find this other SO question ("Trying to load position independent code on cortex-m3") helpful as well.
-
Well, tried the instructions in the referenced link and my toolchain (I think) doesn't even support the PIC/PIE options, I think because of the way newlib was compiled. I am using the NXP LPCXpresso BTW on the LPC1758. – Boaz Jun 14 '11 at 07:44
-
If you dont need newlib or gcclib you can probably have more flexibility, if not, depending on your application size, your flash based boot code can simply copy a binary to ram and branch/run from there. You compile your apps for the fixed ram address, the boot/copy code is trivial, some assembler wrapped around the binary (which can be PIC because you are doing it by hand). It is a ldm/stm loop with a branch. – old_timer Jun 14 '11 at 18:52
-
This solution is indeed easy to implement and would defenitaly work, but I am afraid our application binary image is larger than the RAM size (which is 64KB while half of this is used for the AHB or something like that). – Boaz Jun 16 '11 at 16:10
-
Boaz I like your answer, but I was wondering how to manage references to OS-side from the App build environment. For instance, OS-side has certain variables that I would like to access from my APP-end. How can these 2 things be built separately and connect at run-time? – bazz Apr 08 '15 at 22:38
You need some way for the device to determine when it is reset which of two possible locations it should start executing from. But generally a bare-metal device has only a single starting location it runs from when it's reset (some controllers can select from two or maybe more entry points based on the logic level of some pins on the device).
We had a similar requirement, and worked out the following scheme:
- a small bootloader program is required - it's built and linked to be the program that gains control of the CPU at reset
- the main program image is actually built twice - once for each possible location. Note: the two possible loading locations are fixed and known by the bootloader.
- there's a small data structure at the start of the program image that contains several bits of information important to the bootloader. Among them are the entry point address of the program and a checksum of the program image
The bootloader examines the fixed, well-known locations to checksum the two possible images.
- if it finds no valid images, it simply loops (the watchdog will reset the device, but that doesn't really matter - it's a brick until a valid main program is loaded)
- if it finds only one valid image, that's the entry point that it jumps to.
- if it finds both images are valid it uses other information in the data structure to determine which one to give control to (version information, last known good, whatever your policy might be).
The key to this is that the bootloader must be simple and stupid. It's not easily upgradeable, so you want it to be stupid enough that it can't have bugs.
Now the device can be upgraded while it's running by flashing the image to the non-running location (the Cortex-M3 device we have allows this - if the LPC1758 doesn't allow this, then you have to have something that runs from RAM perform the flash update). Reset, and the bootloader picks up the newly flashed image.
The system requires a little up-front work to get the bootloader running and rock-solid, but once it's working updates are 100% reliable (if the new flash doesn't complete, the old image is the only one that checksums, so it'll run at next reset - no bricks). The main disadvantage - and it's a big one - is that you essentially lose half your flash address space for the main program since the flash has to be able to hold two compete images.

- 333,147
- 50
- 533
- 760
-
Hi Michael, this is actually what we are doing currently and I was hoping for a solution more like the one proposed in the first answer. Thanks alot for the informative answer though. – Boaz Jun 16 '11 at 16:04