U-Boot SPL Size Limits: Budget the Linked Image, BSS and Early Runtime Memory

Adding a small driver makes SPL fail its size check, or the image
still links but becomes unreliable before DRAM initialization. These are
related symptoms, yet they do not share one universal size limit. The
file written to storage, the linker-visible memory sections and the
early runtime allocations answer different capacity questions.

This article uses upstream U-Boot v2025.01. The generic ARM SPL
linker script is an example of concrete checks, not a claim that every
board uses that script or layout. A Buildroot release must inspect the
selected SPL configuration and linker script. No firmware build or
memory-usage measurement was performed here.

Preserve the right build
evidence

The generic xPL
documentation
explains that early loader objects are built
separately and identifies SPL’s ELF, binary and map outputs. It also
describes stage-specific configuration selection. A feature enabled in
main U-Boot therefore does not by itself establish what was compiled
into SPL.

Keep the generated configuration, linker map, ELF and final packaged
boot artifact for both the baseline and changed build. Record the
toolchain identity and build options. Comparing two binary lengths
without that context can mix the effect of a driver with changes in
optimization, logging, padding or packaging.

Start by asking which check failed. Capture the exact diagnostic and
the symbol or configured limit behind it. “SPL too large” is not precise
enough when one check excludes BSS and another includes a broader
footprint. The correct remedy depends on the resource the limit
represents.

Conceptual SPL memory-budget diagram distinguishing linked image sections, BSS and early runtime allocations.

Distinguish the configured
limits

The SPL
Kconfig definitions
describe SPL_MAX_SIZE as an image limit
excluding BSS. They separately provide a BSS-only limit or an overall
footprint limit that includes BSS. SPL_SIZE_LIMIT also has options to
reserve space for global data, an early allocation pool and a configured
stack allowance.

Read the actual generated values and dependencies instead of assuming
every option is active. A zero or unavailable limit can mean that a
particular check does not constrain the build. Conversely, increasing a
configured limit is not evidence that the hardware gained memory or that
the ROM can accept a larger image.

Create a resource table that connects each active check to its
physical reason: ROM transfer capacity, a reserved SRAM region, a
section boundary or runtime coexistence. If the reason cannot be
identified, resolve that uncertainty before changing the number. A
historical constant may be conservative, but it may also encode a
requirement missing from the current team’s notes.

Inspect what the
linker actually asserts

The tagged
generic ARM SPL linker script
contains distinct assertions for the
image-copy extent, BSS size and the span through the BSS end. It also
shows section placement and overlay behavior. This is why a simple sum
of file bytes and BSS is not a universal substitute for the actual
linker map.

For the selected board, locate the script that the build used and
inspect its symbols and assertions. Mark each section’s address range
and lifetime. If BSS resides in a different region or overlaps storage
used earlier in startup, document when that reuse becomes valid. Do not
draw every section as simultaneously occupying a single contiguous block
unless the actual layout supports that model.

The map should explain growth. Identify which objects or sections
changed after enabling the feature, including indirectly selected
support code. A small driver can bring in formatting, bus support or
library dependencies. Removing a random unrelated function may recover
bytes while leaving the feature’s real dependency cost unexplained.

Budget what
does not appear as ordinary file growth

An unchanged binary size does not prove unchanged runtime demand. A
new path may allocate memory, use deeper call chains or require
additional data structures before the larger memory system is available.
Review those lifetimes against the early SRAM budget.

The xPL documentation describes compiler-generated stack-usage
information and call-flow inspection as aids to estimating stack demand.
Treat such analysis as evidence with limits. Indirect calls,
configuration-dependent paths and exceptional error handling require
attention; a partial call graph should not be presented as an exact
worst-case measurement.

Where the platform supports runtime stack diagnostics, qualify them
under controlled conditions and record what they actually cover. The
release still needs an allowance for the relevant startup and failure
paths. A single successful normal boot is not a comprehensive stack
bound, and this article provides no measured headroom figure.

Make the feature change
reviewable

A proposed comparison keeps board configuration and toolchain fixed
while adding the feature. Record changes in image extent, BSS, early
allocation allowance and estimated or measured stack demand. Review
packaged-image padding separately so that a fixed-length output file
does not hide growth inside its reserved area.

Exercise the feature’s intended startup path and its recoverable
error cases on suitable hardware. A device-absent case can take
different branches from the successful path. If the feature is not
required before main U-Boot, consider whether it belongs in SPL at all,
but verify the boot dependency before removing it.

The acceptance decision should state which physical budgets remain
satisfied and which evidence supports each one. Link success is one
check, not a complete SRAM safety argument. By separating linked bytes,
BSS, temporary allocations and stack, the team can explain both a
rejected build and a runtime regression without solving either by
blindly raising a limit.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *