Analyzing the U-Boot SPL Linker Script for ARM64 Architecture

The build system for U-Boot SPL determines the linker script used during the final linking stage. Based on the logic in scripts/Makefile.spl, if a board-specific script (e.g., board/sunxi/u-boot-spl.lds) is not present, the build system defaults to the architecture-specific script located at arch/arm/cpu/armv8/u-boot-spl.lds. The if_changed_dep function processes this source file to generate the final spl/u-boot-spl.lds, expanding macros and preparing the layout for the binary.

Memory Region Definitions

The script defines two primary memory regions used by the SPL phase:

#define CONFIG_SPL_TEXT_BASE        0x10060
#define CONFIG_SPL_MAX_SIZE         0x7fa0
#define CONFIG_SPL_BSS_START_ADDR   0x4ff80000
#define CONFIG_SPL_BSS_MAX_SIZE     0x00080000
  • SRAM: Used for execution. CONFIG_SPL_TEXT_BASE (0x10060) marks the start of the code region.
  • SDRAM: Used for the BSS segment. CONFIG_SPL_BSS_START_ADDR (0x4ff80000) marks where uninitialized data will reside at runtime.

The output format is specified as elf64-littleaarch64, with the entry point set to _start.

Code Section (.text) Layout

The executable code is mapped to the SRAM region. The layout ensures specific ordering:

  1. Alignment: The address counter . is aligned to 8 bytes immediately.
  2. Image Boudnaries: Zero-length arrays, __image_copy_start and __image_copy_end, are placed at the beginning and end of the copyable content. These symbols, defined in arch/arm/lib/section.c, do not consume space but mark the range for memory copy operations.
  3. Startup Code: The code from arch/arm/cpu/armv8/start.o is forced to the beginning of the text section to ensure execution starts there.
  4. General Code: All other object files contribute their text sections following the startup code.
/* arch/arm/lib/section.c */
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));

SRAM Data Sections

Following the text section, the SRAM region contains:

  • .rodata: Read-only data. The linker sorts these by alignment (descending) and then by name to minimize padding.
  • .data: Initialized global and static variables.
  • .u_boot_list: A section for linker-list structures (often used for command registration). The KEEP directive ensures these sections are retained even if unused. They are sorted by name.

The linker list mechanism relies on macros defined in include/linker_lists.h. For example, declaring a command involves placing a structure into a specific section:

/* Macro expansion creates symbols like _u_boot_list_2_cmd_2_help */
#define ll_entry_declare(_type, _name, _list)              \
    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)  \
            __attribute__((unused,                          \
            section(".u_boot_list_2_"#_list"_2_"#_name)))

BSS and SDRAM Placement

The BSS (Block Started by Symbol) segment holds uninitialized global variables. In the linker script, this section is assigned to the SDRAM region. Unlike the text and data sections, BSS does not occupy space in the binary file; it is zeroed out in memory during runtime initialization.

Because the BSS is located in external memory (SDRAM), the SPL must initialize the SDRAM controller before attempting to use any uninitialized global variables or before performing relocation to that memory region.

Discarding Sections

The /DISCARD/ directive at the end of the script instructs the linker to exclude specific sections (such as comment sections or debugging info) from the final output binary to reduce size.

Tags: U-Boot Embedded Systems bootloader Linker Script SPL

Posted on Fri, 09 Oct 2026 16:45:46 +0000 by SauloA