Understanding the Linker Script & Memory Map (Map File)

Understanding the Linker Script & Memory Map (Map File)

When working in bare-metal embedded development, we don’t have an operating system managing memory for us. Every instruction and variable must be placed at the correct physical memory address. This is where the linker script and map file become essential.

In this article, we will walk through a real linker script used for the Tiva TM4C123GH6PM microcontroller, understand what each section means, and then see how the map file helps us verify memory placement. Finally, we relate everything to a simple main.c application that toggles an LED.


1. The Linker Script: Defining How Code and Data Are Placed in Memory

The linker script tells the linker where to place different parts of the program in Flash and RAM.

Target and Entry Point

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(Reset_Handler)
        

This configures the output file format, target CPU architecture, and specifies that execution begins at Reset_Handler, which is part of the startup code.

Defining Physical Memory Regions

MEMORY {
    ROM (rx)  : ORIGIN = 0x00000000, LENGTH = 256K
    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
}
        

ROM = Flash memory (read + execute) RAM = volatile memory (read + write + execute)

This defines the real memory layout of the MCU.

Stack and Heap Allocation

STACK_SIZE = 2048;
HEAP_SIZE = 0;
        

We allocate 2 KB stack and disable heap, since malloc is not used.

Interrupt Vector Table in Flash

.isr_vector : {
    KEEP(*(.isr_vector))
    . = ALIGN(4);
} >ROM
        

The vector table must be at the beginning of Flash so the CPU can fetch reset and exception addresses.

Code and Constants in Flash

.text : {
    . = ALIGN(4);
    *(.text)
    *(.text*)
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
} >ROM

_etext = .;
        

.text holds program instructions. .rodata holds constant data. _etext marks where this section ends.

Stack Placement in RAM

.stack : {
    __stack_start__ = .;
    . = . + STACK_SIZE;
    __stack_end__ = .;
} >RAM
        

Reserves dedicated stack space.

Initialized Variables (.data) in RAM but Stored in Flash

.data : AT(_etext) {
    __data_start = .;
    *(.data)
    *(.data*)
    __data_end__ = .;
} >RAM
        

.data contains global variables with initial values. They are stored in Flash and copied to RAM at startup.

Uninitialized Variables (.bss) in RAM

.bss : {
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    __bss_end__ = .;
} >RAM
        

.bss contains all variables without initial values and is zero-initialized by the startup code.


2. Relating This to the Application (main.c)

const uint8_t a;
const uint8_t b;
        

These are const, so they go to .rodata in Flash.

uint8_t x = 'A';
uint32_t y = 1;
        

These have initial values, so they go to .data (copied to RAM).

uint8_t c;
uint16_t d;
        

No initial value → placed in .bss and zero-initialized.

The LED blink logic executes from .text, and GPIO registers are accessed through memory-mapped I/O.

This demonstrates how different variable types map directly to physical memory regions.


3. The MAP File: Your Verification Tool

After linking, the compiler generates a .map file that shows:

• Where each symbol and section is located in memory

• How much Flash and RAM is used

• Start and end addresses of each section

This file is critical for:

• Confirming .text, .data, .bss, heap, and stack fit within limits

• Ensuring vector table is correctly placed at address 0x00000000

• Debugging startup code issues

• Avoiding silent memory corruption or unexpected resets

Every firmware engineer should inspect the map file before flashing the MCU.


Key Takeaways

  1. The linker script defines how your firmware is laid out in memory.
  2. Flash holds code and constants. RAM holds runtime-modifiable data.
  3. .data is copied from Flash to RAM at startup; .bss is zero-initialized.
  4. The map file verifies memory placement and usage, and prevents runtime errors.
  5. Understanding these concepts is essential for reliable bare-metal firmware development.

Full Startup Implementation & Notes

https://github.com/abdurrahman-ar-9489/RTOS-From-Scratch/tree/main/Lessons/lesson06


Great share! If anyone wants quick access to curated experts, here’s a direct link: https://gopluto.ai/user-query/lesson-understanding-memory-ad29

Like
Reply

To view or add a comment, sign in

Others also viewed

Explore content categories