Skip navigation

SEGGER Linker

The SEGGER Linker makes linking simpler, optimizes application size, and solves common linking problems that usually arise in embedded development.

  • Replacement for linkers of other toolchains
  • Ultra-fast linkage, even for large applications
  • Highly flexible placement algorithms automatically flow code and data around fixed areas
  • Minimize ROM usage with optional compression of RAM-based data and code

The SEGGER Linker is a fast linker that links applications for execution on Cortex-M microcontrollers. It is designed to be very flexible, yet simple to use, written from scratch without any legacy code or legacy thinking, targeting the requirements of embedded developers.

The linker combines object files and libraries to produce an executable image. This image is suitable for programming a Cortex-M microcontroller and includes symbol and debug information to test and examine the generated application.

The SEGGER Linker can be used with Embedded Studio or with any build system, such as make. It accepts Arm ELF object files generated by standard-conforming toolchains, such as GCC, Clang, or IAR.

  • Compatibility options for popular linkers
  • Modular linkage: only link in what is required, automatically
  • Detailed map file shows exactly how your application was linked and where sections are placed
  • Measure code size for symbols and modules
  • "Maximum packing" option packs code and data to reduce wasted space caused by alignment
  • Options to align code and data extract the best performance from caches and flash accelerators

Replace the Linker of Other Toolchains

The SEGGER Linker has mainly been designed to replace the GNU Linker, which has a lot of disadvantages for use with microcontroller applications.

The linker combines standard ELF object files and ELF object libraries to produce an executable image. It is therefore compatible with most other toolchains, too.

Ultra-fast Linkage

The SEGGER Linker provides fastest linkage speed, even for large applications. It can typically link up to 5 times faster.

While the SEGGER Linker has no problems to link anything in almost no time, link time of the GNU Linker significantly increases with application complexity and number of inputs.

Project GNU Linker SEGGER Linker Speedup
Complex Project (SSL Web Server) 2.720s 0.466s x5.8!CALCULATE_BAR_0_1!
Medium Project (embOS Blinky) 0.164s1.751s1 0.042s0.329s1 x3.9!CALCULATE_BAR_0_2!
Small Project (Simple main return) 0.088s1.717s1 0.026s0.322s1 x3.4!CALCULATE_BAR_0_2!

1: Linked with all object files from the complex project, which are discarded in other projects.

Optimize Your Application

The SEGGER Linker can optimize size and performance of your application, beyond what the compiler could have done.

  • Modular linkage only links in what is required, automatically.
  • Input fragments (functions and data) can be sorted in different ways to improve packing, leading to fewer gaps in memory and less memory usage.
  • Compression options minimize flash-copy space for initialized data and code in RAM.

Support for Flexible Memory Layouts

Small embedded systems - usually microcontrollers with built-in memories - are complex. Typically they have separate memory areas for flash and RAM and might use external memories, too.

RAM is usually also divided into distinct regions to enhance performance, so they can be accessed simultaneously by the CPU and peripherals.

Flash memory can contain "keep-out" areas, e.g. calibration data or bootloader APIs, that may not be overwritten by application code. Furthermore you might have to set certain values at fixed addresses, such as flash protection bytes or fixed-address jump tables.

The SEGGER Linker has been designed to work with complex memory layouts.

Analyze the Linker Output

The SEGGER Linker generates a comprehensive map file that is readable and understandable.

Map files come in handy, when you need to know more about how your application is linked. SEGGER Linker generated map files enable you to easily analyze the executable image and provide answers to following questions:

  • How much ROM And RAM does my application need?
  • How much code and data is pulled in because of one or multiple particular symbols?
  • How much space does a module need?
  • Where are my symbols placed?
  • Is my symbol where I want it to be?
  • Where are gaps of unused memory in the image?

Improved Runtime Initialization

The SEGGER Linker generates a runtime initialization tailored for your application.

It automatically handles initialization of read-write sections. With other linkers it is the user's responsibility to copy the initialization image from flash to RAM and to zero the "bss" section before entering main.

The SEGGER Linker generates the initialization code based on the actual input. It supports initialization of non-continuous data sections and optional compression of the initialization image. Only the initialization code that is required by the application is linked in.

SEGGER Linker Scripts

SEGGER Linker Scripts have a simple, yet powerful syntax which enables developers to tailor memory layout and section placement to their requirements.

GNU Linker Scripts are usually huge, complex, and limited in functionality. Whether you needed to place a symbol at a certain address, or you wanted to use multiple RAM sections, you would always have to find a workaround to do so.

The SEGGER Linker has been designed with such use cases, which are standard in embedded systems, in mind and provides easy-to-use mechanisms to solve them.

SEGGER Linker Scripts for common embedded systems can be written in about 30 to 50 lines of statements, where a comparable linker script for the GNU Linker easily require 5 to 10 times more content.

1. Define Your Memories

Each memory, ROM or RAM, is defined as a region.

In the simplest case a region has a start address and a size: define region FLASH = [from 0x00000000 size 256k];

With the SEGGER Linker you can also define combined regions using +, -, &, and | operators.
Region unions combine two or more ranges into one region: define region RAM = [from 0x1fff0000 size 64k] + [0x20000000 size 192k];
Region subtraction allows you to punch holes in a range: define region ROM = [from 0x00000000 size 256k] - [from 0x400 size 16]
Region intersection enables you to get the overlap of two ranges: define region RAM = [from 0x20000000 size 256k] & [from 0x1fff8000 size 96k]

2. Define Your Blocks

Blocks can be used to keep certain sections and symbols together. There are some default blocks to be defined for use of C++ (constructors and destructors) and thread-local storage (TLS), and you can define the blocks for your system: define block config { section .config, section .config.* };

You can also define blocks to reserve space that is used by the application without placing actual symbols into it. For example stack: define block stack with size = __STACKSIZE__, alignment = 8, readwrite access { };

3. To Initialize or not to Initialize?

The SEGGER Linker automatically generates initialization code for data in RAM, which, on startup, can be zero initialized or initialized by copying the by initialization values from flash.

Read-write sections that have the NOBITS attribute are zero initialized. Usually that is the .bss section where all static variables which are not explicitly initialized go into. This is done automatically.

Read write sections that have the PROGBITS attribute, i.e. the .data section for all variables that are initialized, are linked with initialize by copy with packing=none. You can optionally change the packing of "initialize by copy" sections to compress the initialization image and save flash.

You can also define additional sections to be copied from flash to RAM on startup. This is for example necessary for RAM functions: initialize by copy { section .fast, section .ramcode };

You might also want to not initialize certain variables on startup. For example preserve battery-backed data across a power cycle: do not initialize { section .backup, section .backup.* };

The SEGGER Linker will now generate the initialization image and code for data to be copied and will not touch data to be not initialized. There is no additional need to modify the runtime init or startup code.

4. Place Sections into Memory

With the SEGGER Linker you have full control over which symbol, section, or block goes into which memory region. You can also let the linker do what it is best at and let it place everything for you automatically.

Simply place readonly data and code into flash and variables into RAM: place in FLASH { readonly, read exec }; place in RAM { readwrite, zeroinit };

You will also want your vector table to be first in your flash and your stack to be at the end of RAM: place at start of FLASH { section .vectors }; place at end of RAM { block heap, block stack };

When you have more than one RAM, placement with the GNU Linker could be tricky. You would have to know what to place into RAM1 and what to place into RAM2. With the SEGGER Linker you do not have to worry about that: place in RAM1 then RAM2 { readwrite, zeroinit };

If you need to place a symbol or section at a fixed address, you can do that, too: place at address 0x400 { section .flashconfig }; or place at address 0x400 { symbol aFlashConfig };

5. Put Everything Together

The following is an example linker script that can create your whole application. For more advanced options and reference, have a look at the SEGGER Linker User Guide.

//
// Physical memory layout:
//   Flash:  2MB @ 0x00000000
//     Sector  0 -  3: 16kB
//     Sector  4 -  5: 32 kB
//     Sector  6 - 23: 128 kB
//   RAM1:  64kB @ 0x1fff0000
//   RAM2: 192kB @ 0x20000000
//   CCM:   64kB @ 0x10000000
//
// Firmware memory layout:
//   Flash Sector 0-3 reserved for bootloader
//   Flash Sector 4 used for non-volatile configuration
//   Remaining Flash free for application
//
//   RAM1 used for RAM functions
//   RAM2+RAM1 used for data
//
define memory with size = 4G;

define region BOOTLOADER = [from 0x00000000 size 64k];
define region CONFIG     = [from 64k        size 32k];
define region APP        = [from 96k        size 2M - 96k];

define region RAM1       = [from 0x1fff0000 size 64k];
define region RAM2       = [from 0x20000000 size 196k];
//
// Standard blocks
//
define block ctors  { section .ctors, section .ctors.*, block with alphabetical order { init_array } };
define block dtors  { section .dtors, section .dtors.*, block with reverse alphabetical order { fini_array } };
define block exidx  { section .ARM.exidx.* };
define block tbss   { section .tbss,  section .tbss.*  };
define block tdata  { section .tdata, section .tdata.* };
define block tls    { block tbss, block tdata };
//
// Fixed size blocks for stack and heap
//
define block heap  with size = __HEAPSIZE__,           alignment = 8, readwrite access { };
define block stack with size = __STACKSIZE__,          alignment = 8, readwrite access { };
//
// Initialization method of sections
//
do not initialize   { section .non_init };
initialize by copy  { section .fast };
//
// User-defined blocks
//
define block config { section .config, section .config.* };
//
// Specific placement of sections
//
place in CONFIG         { block config };
place in RAM1           { section .fast };
place at start of APP   { section .vectors };
//
// General RAM placement
//
place in RAM2 then RAM1
                        { readwrite,
                          zeroinit,
                          block tls,
                          section .non_init
                        };
place at end of RAM2
                        {
                          block stack,
                          block heap
                        };
//
// General Flash placement
//
place in APP with maximum packing
                        { readexec,
                          readonly,
                          block ctors,
                          block dtors
                        };
place in APP            { block exidx };