Engineer999
Engineer999

Reputation: 3955

Does the linker script always determine at what addresses code is placed

I've spent most of my time developing for microcontrollers using tools and IDEs such as Atmel Studio, and abstracted from as to what goes on behind the scenes exactly.

Let's say in this case we are executing the code directly from flash which can be the case in embedded systems.

When we develop an application and flash it to the microcontroller using provided tools from the chip vendor, is it the loader / flasher that determines at what physical address in memory will be flashed?

I understand that the linker script defines memory offsets as to where the different sections such as .data and .txt should be placed, so is it actually this that ultimately determines at what address in the mcu flash everything should go?

Lets's say now that I discover a part of flash that is not used at all and I wish to put something else there (another application even), do I modify the linker script, create a new one, or how would I write to this particular location? I haven't fully grasped this yet.

Upvotes: 3

Views: 1230

Answers (2)

Bart
Bart

Reputation: 1580

You asked if you should modify or create a new linker script. I would requirement that you modify the exciting linker script. I find them from time to time quite extensive. Why throw away what is already there while modifying it is only a few lines.

Some very common cases for micro controllers to change the linker script are:

  1. Adding a bootloader in the first flash sector while the actual program starts in a higher sector.
  2. Adding a sector dedicated to storing data generated by the application.
  3. Using a dedicated sector for configuration parameters.

Lets use the first as an example on how to write to firmware to different flash sectors. Important to know is that for this example the bootloader and the actual application are two separate projects each with their own compilation configuration or makefile. Each project also has its own linker script. Below part of both files is listed:

The bootloader:

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 16K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
  CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K
}

The application:

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x0800C000, LENGTH = (1M - 16K)
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
  CCMRAM (rw)     : ORIGIN = 0x10000000, LENGTH = 64K
}

Please not the differences in the definition of the FLASH. The bootloader flash start at the default. The size allocated for the bootloader is 16K. Important to note is that the total flash size of the example chip is 1M. This is seen in the size of the flash of the application project. But we do not allow the full 1M for the application as of course we need to reduce it by the size allocated for the bootloader. You can also see it does not start at the same address. It has an offset of 0xC000 which amounts for the 16k.

Before you can run and debug the application code you have to do two more things:

  1. Define the vector table offset of your code. This is required for the compiler and linker to know where the code is located in memory. This is where my knowledge of this part is lacking a bit so anyone who knows please elaborate.
  2. If you would like to debug your application you also have to tell your debugger where to start. Atollic does this via a linker script. From what I remember from a few years back Atmel Studio has an option in the programming window where you can set the offset.

One final note: when you do something link this it is best to stick to the flash sectors of the chip. Often you can only delete a whole sector. Placing your next part of the code at the start of the next sector makes live easier.

The given examples are based on a linker file generated by Atollic TrueSTUDIO for STM32 for a STM32F4xx chip. Compilation is done using ARM gcc toolchain.

Upvotes: 1

Richard at ImageCraft
Richard at ImageCraft

Reputation: 665

Your idea is generally correct. A linker script, which is nothing more than a file with its specialized language that conveys certain information regarding memory and how they should be used, determines the layout of your program in the memory. Some linkers would accept such memory specifications via command line options as well.

Regarding your specific question, yes, you can write another firmware that occupies different memory addresses (than the first one) by modifying the second one's linker script, in particular, the starting address of the program image. This of course does not say anything about how these two pieces of firmware will run or communicate. Those are separate issues. You will also need to know the MCU's flash erasable page boundary so you can place the 2nd image in a different erasable page boundary.

In some firmware downloader / programmer, you may also be able to specify a different starting offset than what's specified in the firmware image. This is separate from the linker script process. There are a few uses for this. For example, your firmware maybe built to run from SRAM instead of directly from flash. Therefore at firmware download, it will need to be moved to a flash location, and at runtime, some kind of mechanism will be used to copy the code from the flash to SRAM where the program actually will run. There are other scenarios as well.

Upvotes: 4

Related Questions