Inferensys

Glossary

Linker Script

A linker script is a configuration file that directs the linker on how to combine object files and libraries, specifying the memory layout, section placement, and symbol addresses for the final executable or binary image.
Stylish WeWork-like workspace with hot desks and document wall, professional searching through enterprise knowledge base on a mounted ultrawide display, warm industrial pendants overhead.
COMPILATION

What is a Linker Script?

A linker script is a critical configuration file used in the final stage of compiling software for specialized hardware like NPUs.

A linker script is a configuration file written in the linker's command language that directs the linker on precisely how to combine multiple object files and libraries into a single executable or binary image. It explicitly defines the target hardware's memory layout, specifying the addresses and sizes of memory regions (like SRAM, DRAM, or NPU-local memory) and dictating where each section of code and data (.text, .data, .bss) must be placed. This control is essential for embedded systems and hardware accelerators where memory is constrained and specific addresses are mapped to physical hardware registers or fast on-chip memory.

For NPU acceleration, a linker script ensures that performance-critical kernels and weight tensors are placed in optimal memory regions to minimize latency and maximize bandwidth. It manages symbol resolution, assigning final addresses to functions and variables, and handles relocation adjustments. By defining entry points and stack/heap locations, it creates a runnable binary tailored to the exact memory map of the target System-on-Chip (SoC). This low-level control is a foundational step in vendor SDK workflows for generating efficient, deployable binaries for specialized accelerators.

VENDOR SDK AND INTRINSIC MAPPING

Key Components of a Linker Script

A linker script is a configuration file that provides the linker with a precise blueprint for constructing the final executable or binary image from compiled object files and libraries. It is critical for NPU programming to define the exact memory layout, section placement, and symbol addresses required by the target accelerator hardware.

01

Memory Layout Definition

The core directive of a linker script is to define the memory map of the target system. This specifies the available memory regions, their start addresses, lengths, and access permissions (e.g., read, write, execute). For NPUs, this often involves mapping code to tightly coupled instruction memory (TCM) and data to high-bandwidth on-chip SRAM.

  • MEMORY Command: Declares physical memory blocks (e.g., FLASH, SRAM, NPU_IMEM).
  • Example: MEMORY { NPU_CMEM (rwx) : ORIGIN = 0x80000000, LENGTH = 256K }
02

Section Placement Directives

The SECTIONS command controls how input sections from object files (.text, .data, .bss, .rodata) are mapped into output sections and placed within the defined memory regions. This is essential for ensuring NPU kernels and their associated weights are located in optimal memory for performance.

  • Output Sections: Define where specific types of code/data go (e.g., .npu.text).
  • Input Section Patterns: Use wildcards like *(.text.vendor_kernel*) to collect specific functions.
  • Location Counter (.): The special variable that tracks the current output address.
03

Symbol Assignment and Resolution

Linker scripts can create, define, and assign values to symbols, which are addresses or absolute values accessible to the program. This is used to expose the start and end addresses of memory regions or sections to the runtime or bootloader.

  • Assignment Operator (=): _npustack_top = ORIGIN(NPU_CMEM) + LENGTH(NPU_CMEM);
  • PROVIDE: Ensures a symbol is only created if not already defined elsewhere, preventing conflicts.
  • Symbols as Entry Points: The ENTRY(symbol) command specifies the first instruction to execute.
04

Alignment and Region Constraints

NPU memory subsystems often have strict alignment requirements for data types and DMA transfers. Linker scripts enforce these using alignment directives and region fill/overflow checks.

  • ALIGN(expression): Forces the location counter to the next specified boundary (e.g., ALIGN(128) for cache lines).
  • SUBALIGN: Controls alignment of input sections within an output section.
  • FILL: Specifies a fill pattern for unused areas within a section.
  • >region: The placement operator ensures a section is placed within a specific memory region.
05

KEEP and Garbage Collection

To prevent the linker's garbage collection (--gc-sections) from removing critical but unreferenced code—such as NPU kernel functions or interrupt vectors that are called via hardware, not software—the KEEP command is used.

  • Syntax: KEEP(*(.npu.vectors)) ensures the interrupt vector table is preserved.
  • Application: Essential for vendor runtime initialization code and hardware-specific sections that are not explicitly called from the main application but are required for the NPU to function.
06

Overlays and Advanced Constructs

For complex systems, linker scripts support advanced constructs like overlays (where the same memory region is used for different code sections at different times) and conditional expressions. They can also include other script files and use built-in functions for calculations.

  • OVERLAY: Manages memory overlays for systems with constrained RAM.
  • INCLUDE: Incorporates another linker script file.
  • Built-in Functions: Use SIZEOF, ADDR, LOADADDR to query section properties programmatically within the script.
GLOSSARY

How Linker Scripts Work in NPU Acceleration

A linker script is a critical configuration file in the compilation toolchain that directs the final stages of binary creation for Neural Processing Unit (NPU) targets.

A linker script is a configuration file written in the linker's command language that explicitly directs the linker on how to combine sections from input object files and libraries into the final executable or binary image. For NPU acceleration, it precisely defines the memory map, specifying absolute addresses for code (.text), data (.data, .bss), and other sections within the NPU's distinct memory hierarchies (e.g., tightly coupled memory, global DRAM). This control is essential because NPUs often have non-uniform, specialized memory architectures where placement directly impacts performance and correctness.

The script manages symbol resolution and relocation, binding symbolic references to final addresses. It can also define symbols for the runtime to query, such as the start and end of a scratchpad memory region. By dictating section ordering and alignment, linker scripts enable hardware-aware optimizations, such as placing critical kernels or weight buffers in faster, lower-latency memory banks. This level of control is a foundational step in vendor SDK workflows for generating binaries that fully leverage the NPU's physical architecture, moving beyond generic compilation to hardware-specific deployment.

VENDOR SDK AND INTRINSIC MAPPING

Common Use Cases for Linker Scripts

Linker scripts are essential for controlling the low-level layout of software, especially when targeting specialized hardware like NPUs. These cards detail their primary applications in embedded and accelerator programming.

01

Memory Map Definition for NPU Firmware

The core function of a linker script is to define the precise memory layout of the final executable. For NPU firmware, this involves:

  • Mapping sections like .text (code), .data (initialized variables), and .bss (uninitialized data) to specific address ranges in the NPU's internal SRAM or tightly coupled memory.
  • Placing vendor runtime libraries and hardware intrinsic implementations in performance-critical memory banks.
  • Reserving memory-mapped I/O regions for registers that control the accelerator, ensuring code does not accidentally overwrite them.
  • Example: Directing all vector computation kernels (.text.vec) into a low-latency scratchpad memory for maximum throughput.
02

Section Placement for Hardware Intrinsics

Linker scripts enforce the correct placement of code that uses vendor-specific intrinsics or inline assembly.

  • Vendor SDKs often provide pre-compiled object files containing optimized routines. The linker script must ensure these are placed in memory regions that align with the NPU's instruction cache architecture.
  • It can group all functions using a particular tensor core ISA extension into a contiguous .section.npu.tensor to improve cache locality.
  • This prevents the linker from scattering low-level hardware-accessing code throughout the binary, which could degrade performance or violate alignment constraints.
03

Managing Multiple Memory Banks

Modern NPUs feature heterogeneous memory hierarchies (e.g., global DDR, shared L2 cache, local SRAM). Linker scripts manage data placement across these banks.

  • Performance-critical data (e.g., weights for the active layer) can be assigned to fast SRAM using a >sram directive in the section definition.
  • Less-frequently accessed data (e.g., model metadata) can be placed in slower, but larger, external DRAM (>ddr).
  • This manual partitioning is crucial for meeting real-time latency requirements in edge AI deployments, as it minimizes costly data movement between memory levels.
04

Creating Custom Sections for Profiling

Developers use linker scripts to create custom ELF sections for instrumentation and performance profiling.

  • A section like .probe_data can be defined to hold counters for kernel execution cycles. The script places it in a memory region accessible to both the NPU and a host CPU for analysis.
  • Auto-tuning frameworks may generate multiple kernel variants. The linker script can place each variant (.kernel.variant1, .kernel.variant2) in separate address ranges, allowing the runtime to benchmark and select the fastest.
  • This enables evaluation-driven development by providing a structured way to embed telemetry directly into the binary layout.
05

Handling Static vs. Dynamic Linking

Linker scripts control how static libraries (.a files) and dynamic libraries (.so files) from a vendor SDK are incorporated.

  • For static linking, the script pulls required object files from the vendor's libnpu.a archive and places them in the NPU's memory map. Unused functions are discarded to save space.
  • For dynamic linking on systems with an OS, the script can define the INTERP segment to point to a vendor runtime dynamic loader and set the DYNAMIC segment for symbol resolution.
  • This is key for deployment optimization, allowing a single driver binary to support multiple NPU generations through runtime library selection.
06

Defining Entry Points and Boot Sequences

In bare-metal or RTOS environments for NPUs, the linker script defines the system's entry point and bootstrapping code layout.

  • It sets the initial Program Counter (PC) address by assigning the vector table or _start symbol to a specific ROM address.
  • It organizes the boot sequence: ROM code copies .data from flash to RAM and zeroes out .bss before jumping to main.
  • For firmware updates, it can define a bootloader section in protected memory and an application section in upgradable memory, managing the symbols and jump tables between them. This supports secure over-the-air updates in embedded AI systems.
LINKER SCRIPT

Frequently Asked Questions

A linker script is a critical configuration file that directs the final stages of software compilation for hardware accelerators like NPUs. It defines the memory map, section placement, and symbol resolution for the executable binary.

A linker script is a configuration file written in the linker's command language that provides explicit instructions to the linker (e.g., ld) on how to combine multiple object files and libraries into a single executable or shared library. It works by defining the memory layout of the target system, specifying where different sections of code and data (like .text, .data, .bss) should be placed in memory, and resolving symbolic references between object files. For NPU programming, it is essential for mapping kernels and tensor buffers to specific accelerator memory regions (e.g., NPU SRAM, global DDR).

Key directives include:

  • MEMORY: Declares the memory regions (address, size, attributes) of the target hardware.
  • SECTIONS: Defines the output sections and rules for placing input sections from object files into them.
  • Symbol assignments (e.g., _stack_top = .;) to define critical addresses.
Prasad Kumkar

About the author

Prasad Kumkar

CEO & MD, Inference Systems

Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.

His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.