I'm writing a customized linker script for a bare-metal ARM application. The application is stored in a flash memory, at the moment I have a bootup code copying the whole application in SDRAM, and continuing execution in SDRAM for speed purpose. I want to modify this script to run the whole code from flash directly, but I have problems understanding certain elements.
In the linker script below, the .ram_data section has an execution address in RAM and a load address in ROM (both sections in SDRAM). From what I understand the execution address is used by the linker to do the symbol resolution of all elements which are not PC relative, but what about the load address? In the context of a bare-metal ARM there is no such thing as a program loader, and there is no way the linker can have any influence on where I write the program in flash, so what is it actually used for?
In the same way, how can the linker have any influence on the SDRAM being (rx) or (rw) at run-time? Does it actually change anything when I modify those options in the memory regions?
MEMORY
{
RAM(rw) : ORIGIN = 0x00001000, LENGTH = 12M-4K
ROM(rx) : ORIGIN = 0x007f1000, LENGTH = 60K
}
SECTIONS
{
.startup :
{ ... } > VECTOR
.rom_text :
{ ... } > ROM
.ram_data :
{
_data_start = .;
*(.data*)
_data_end = .;
} > RAM AT>ROM
.ram_bss :
{ ... } > RAM
}
Given your example linker script, these two questions are related.
From what I understand the execution address is used by the linker to do the symbol resolution of all elements which are not PC relative, but what about the load address?
In the same way, how can the linker have any influence on the SDRAM being (rx) or (rw) at run-time? Does it actually change anything when I modify those options in the memory regions?
The LOAD address is useful as everything must be programmed to FLASH. This is why .ram_data should have AT>ROM. It is telling the linker that the data will LOAD from the ROM/Flash. You have to make some assembler boot code that will copy it from flash to SDRAM in this case.
The 2nd question can be answered by putting >ROM for the .ram_data section. If you do this, the linker will complain that a write-able section is being placed in read-only memory. It is good to mark the MEMORY sections with read/write information as it can help to make sure you have the sections in the correct places. Ie, it is a cross check on the information you give the linker.
A mistake where >RAM AT>ROM is instead just >ROM makes the concepts/questions similar.
From what I understand the execution address is used by the linker to do the symbol resolution of all elements which are not PC relative, but what about the load address? In the context of a bare-metal ARM there is no such thing as a program loader, and there is no way the linker can have any influence on where I write the program in flash, so what is it actually used for?
That information is stored in the ELF executable, and is used by tools like objcopy to determine how the binary file (e.g, .bin or .hex) is laid out. Ultimately, it ends up telling your programmer where to put the program.
In the same way, how can the linker have any influence on the SDRAM being (rx) or (rw) at run-time? Does it actually change anything when I modify those options in the memory regions?
In this particular use case, I believe those flags are informational only. They're primarily used for dynamic-loaded programs.
Related
I started a new project using an STM32H7, currently using IAR EWARM V8, used the STM32CUBEMX to generate the configuration code, and get an initial project going.
I worked through a couple of the CUBEMX eval projects to get some hardware verified and working, and am able to step through code fine.
But there is something odd going on, in particular with variables if you assign them as local vars within a function, somehow IAR is placing them into the 'System Reserved' memory range...
ie within 0x1FF20000 - 0x1FFFFFFF
For example... the project example 'FMC_NOR' that STM provides, is test code for testing our a NOR flash, etc..
they created these two small arrays as globals vars just at the top of the main.c file.
(buffer_size is 0x1000)
uint16_t aTxBuffer[BUFFER_SIZE] = {0};
uint16_t aRxBuffer[BUFFER_SIZE] = {0};
When in the global space, they are allocated in the DTCM region (0x2000:0000)
When moved as local vars, they then become allocated into the 'reserved space'...
What happens is, when IAR encounters any arrays like this, the processor faults with an 'imprecise data access' hardware fault.
This same error occurs with code to initialize the JPEG module, as it attempts to load the arrays of Huffman tables, etc...
When using TrueStudio this problem does not occur... CubeMX auto-generates the linker files for whichever compiler you are using.
I didn't specifically see anything in the linker files pointing to the reserved memory address.
So not sure what could be going on? I'm new to using this processor, so I'm just starting to understand it's memory mapping.
Thanks for any help or suggestions, I'd like to get IAR figured out, as so far I like it a bit over TrueStudio.
I solved my own question... so no longer need help on this...
This is in the 'stm32h743xx_flash.icf' generated by STM CUBEMX for the STM32H7...
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x400;
define symbol __ICFEDIT_size_heap__ = 0x200;
/**** End of ICF editor section. ###ICF###*/
Bumped the 'size_cstack' up to 2k (0x800) and everything is fine...
Compile time. If you know at compile time where the process will reside
in memory, then absolute code can be generated. For example, if you know
that a user process will reside starting at location R, then the generated
compiler code will start at that location and extend up from there. If, at
some later time, the starting location changes, then it will be necessary
to recompile this code. The MS-DOS .COM-format programs are bound at
compile time.
What can be the reason of the starting location to change? Can it be
because of context switching/swapping ?
Does absolute code means binary code?
Load time. If it is not known at compile time where the process will reside
in memory, then the compiler must generate relocatable code. In this case,
final binding is delayed until load time. If the starting address changes, we
need only reload the user code to incorporate this changed value.
How is relocatable code different from absolute code? Does it contain info about base,limit and relocation register?
How is reloading more efficient then recompiling as they mentioned only reload means no recompiling only reload?
Execution time. If the process can be moved during its execution from
one memory segment to another, then binding must be delayed until run
time. .
Why it may be needed to move a process during it's execution?
The compile-time and load-time address-binding methods generate
identical logical and physical addresses. However, the execution-time address-binding scheme results in differing logical and physical addresses.
How compile and load-time methods generate identical logical and physical addresses?
To begin with, I would find a better source for your information. What you have is very poor.
What can be the reason of the starting location to change? Can it be because of context switching/swapping ?
You change the code or need the code to be loaded at a different location in memory.
Does absolute code means binary code?
No. They are independent concepts.
How is relocatable code different from absolute code? Does it contain info about base,limit and relocation register?
Relocatable code uses relative addresses, generally relative to the program counter.
(Base limit and relocation registers would be a system specific ocncept).
How is reloading more efficient then recompiling as they mentioned only reload means no recompiling only reload?
Let's say two different programs use the same dynamic library. They made need to have loaded at different locations in memory. It's not an efficiency issue.
Why it may be needed to move a process during it's execution?
This is what was done in ye olde days before virtual memory. To my knowledge no one does this any more.
How compile and load-time methods generate identical logical and physical addresses?
I don't know what the &^54 they are talking about. That statement makes no sense.
Dynamic libraries (.dll .so) are relocatable, because they might appear at different adresses in different applications, but in order to save memory, the operating system only has one copy in physical memory (virtual memory is great), and each application has read only access.
Same happens for applications that are relocatable. For security, it is also wize that the addresses are random - some remote attacks are slighty harder
Basically, when my system is running, I would like the user to ftp some new code to the SD card, and dynamically load the new function and create a task to run in the system. This is normal for Linux. For example, I can compile a SO, and dynamically load into the memory.
How to do it in uC/OS II or III?
This is not a service uC/OS-II or uC/OS-III can provide by itself.
You would need a program loader that is able to read an ELF file, copy its relevant sections (ex .text, .rodata, etc.) in memory according to load addresses specified and allocate memory for uninitialized memory sections. You would then be able to create a new uC/OS task and pass it the function pointer that corresponds to the ELF entry point.
Most embedded systems don't have a Memory Management Unit (MMU) and thus you would need to pay special care to the linking process to make sure any of those sections don't overlap with any code that would already running on your target. Depending on your toolchain, that would most likely involve carefully crafting your linker script.
Another option that would avoid the problem of potential overlapping of the memory space would be to use a toolchain that can produce position independent code and load the ELF in the heap of your main application or in any other allocated and available memory space that is allocated by your main application.
Yet another option would be to produce relocatable code and use or build a program linker that is able to process relocations at runtime, when you want to load the uploaded code.
I have a Cortex-M3 chip and on it I am running a bootloader that uses eCos. The bootloader, after checking for firmware updates etc, jumps to another location (BASE_ADDRESS_OF_APP + 0x19) on the ROM where the actual application resides (also compiled with eCos).
Now instead of running the normal firmware, I want to run my CppUTests compiled for the Cortex-M3 target. So I am able to compile and link my tests for the target platform, using the ecos glibc, but not the actual operating system. But when I load it on to my board using JTAG, it doesn't run.
After some investigation using arm-eabi-objdump, I found out that the reset vector of the CppUTest firmare is at an offset of 0x490 as opposed to an offset of 0x18 for the normal firmware. My suspicion is that this is the reason why the tests are never executed. Is this correct?
How is it possible that the two firmwares have different starting addresses when I am linking them with the same linker script?
How can I make sure that the starting point of the test program is the same as the starting point of the application?
It depends on how your linker script is written, if your entry point address is not set to a static location in the linker script, then there could be the chance that other data/code is put in the object file before your entry point, effectively moving the location of your entry point and indeed causing problems.
I typically solve this by creating a new section with only 1 symbol in it, and a jump/branch instruction as follows:
.section entryPointSection
b myCodeEntryPoint
Then in your linker script put the entryPointSection at the hard coded address that your bootloader will jump to.
The myCodeEntryPoint label can be the name of a C function (or assembly label if necessary) that is in the normal .text section and can be linked anywhere within reach of the jmp. It will become your entry point, but you don't really care where it is because the linker should find it and link it properly.
Consider posting your linker script if you have further questions.
I have basic doubt regarding executable stored in ROM.
As I know the executable with text and RO attributes is stored in ROM. Question is as ROM is for Read Only Memory, what happens if there is situation where the code needs to write into memory?
I am not able to conjure up any example to cite here (probably I am ignorant of such situation or I am missing out basic stuff ;) but any light on this topic can greatly help me to understand! :)
Last off -
1. Is there any such situation?
2. In such a case is copying the code from ROM to RAM is the answer?
Answer with some example can greatly help..
Many thanks in advance!
/MS
Read-only memory is read only because of hardware restrictions. The program might be in an EEPROM, flash memory protected from writes, a CD-ROM, or anything where the hardware physically disallows writing. If software writes to ROM, the hardware is incapable of changing the stored data, so nothing happens.
So if a software program in ROM wants to write to memory, it writes to RAM. That's the only option. If a program is running from ROM and wants to change itself, it can't because it can't write to ROM. But yes, the program can run from RAM.
In fact, running from ROM is rare except in the smallest embedded systems. Operating systems copy executable code from ROM to RAM before running it. Sometimes code is compressed in ROM and must be decompressed into RAM before running. If RAM is full, the operating system uses paging to manage it. The reason running from ROM is so rare is because ROM is slower than RAM and sometimes code needs to be changed by the loader before running.
Note that if you have code that modifies itself, you really have to know your system. Many systems use data-execution prevention (DEP). Executable code goes in read+execute areas of RAM. Data goes in read+write areas. So on these systems, code can never change itself in RAM.
Normally only program code, constants and initialisation data are stored in ROM. A separate memory area in RAM is used for stack, heap, etc.
There are few legitimate reasons why you would want to modify the code section at runtime. The compiler itself will not generate code that requires that.
Your linker will have an option to generate a MAP file. This will tell you where all memory objects are located.
The linker chooses where to locate based on a linker script (which you can customise to organise memory as you require). Typically on a FLASH based microcontroller code and constant data will be placed in ROM. Also placed in ROM are the initialisation data for non-zero initialised static data, this is copied to RAM before main() is called. Zero initialised static data is simply cleared to zero before main().
It is possible to arrange for the linker to locate some or all of the code in ROM and have the run-time start-up code copy it to RAM in the same way as the non-zero static data, but the code must either be relocatable or be located to RAM in the first instance, you cannot usually just copy code intended to run from ROM to RAM and expect it to run since it may have absolute address references in it (unless perhaps your target has an MMU and can remap the address space). Locating in RAM on micro-controllers is normally done to increase execution speed since RAM is typically faster than FLASH when high clock speeds are used, producing fewer or zero wait states. It may also be used when code is loaded at runtime from a filesystem rather than stored in ROM. Even when loaded into RAM, if the processor has an MMU it is likely that the code section in RAM section will be marked read-only.
Harvard architecture microcontrollers
Many small microcontrollers (Microchip PIC, Atmel AVR, Intel 8051, Cypress PSoC, etc.) have a Harvard architecture.
They can only execute code from the program memory (flash or ROM).
It's possible to copy any byte from program memory to RAM.
However, (2) copying executable instructions from ROM to to RAM is not the answer -- with these small microcontrollers, the program counter always refers to some address in the program memory. It's not possible to execute code in RAM.
Copying data from ROM to RAM is pretty common.
When power is first applied, a typical firmware application zeros all the RAM and then copies the initial values of non-const global and static variables out of ROM into RAM just before main() starts.
Whenever the application needs to push a fixed string out the serial port, it reads that string out of ROM.
With early versions of these microcontrollers, an external "device programmer" connected to the microcontroller is the only way change the program.
In normal operation, the device was nowhere near a "device programmer".
If the software running on the microcontroller needed to write to program memory ROM -- sorry, too bad --
it was impossible.
Many embedded systems had non-volatile EEPROM that the code could write to -- but this was only for storing data values. The microcontroller could only execute code in the program ROM, not the EEPROM or RAM.
People did may wonderful things with these microcontrollers, including BASIC interpreters and bytecode Forth interpreters.
So apparently (1) code never needs to write to program memory.
With a few recent "self-programming" microcontrollers (from Atmel, Microchip, Cypress, etc.),
there's special hardware on the chip that allows software running on the microcontroller to erase and re-program blocks of its own program memory flash.
Some few applications use this "self-programming" feature to read and write data to "extra" flash blocks -- data that is never executed, so it doesn't count as self-modifying code -- but this isn't doing anything you couldn't do with a bigger EEPROM.
So far I have only seen two kinds of software running on Harvard-architecture microcontrollers that write new executable software to its own program Flash: bootloaders and Forth compilers.
When the Arduino bootloader (bootstrap loader) runs and detects that a new application firmware image is available, it downloads the new application firmware (into RAM), and writes it to Flash.
The next time you turn on the system it's now running shiny new version 16.98 application firmware rather than clunky old version 16.97 application firmware.
(The Flash blocks containing the bootloader itself, of course, are left unchanged).
This would be impossible without the "self-programming" feature of writing to program memory.
Some Forth implementations run on a small microcontroller, compiling new executable code and using the "self-programming" feature to store it in program Flash -- a process somewhat analogous to the JVM's "just-in-time" compiling.
(All other languages seem to require a compiler far too large and complicated to run on a small microcontroller, and therefore have a edit-compile-download-run cycle that takes much more wall clock time).