When -858993444 Tests Run: A Tale of Linker Lists and Magic Numbers

Have you ever seen output like this from your test suite?
Running -858993444 bloblist tests
That’s not a buffer overflow or memory corruption. It’s a wierd interaction between linker alignment, compiler optimisations, and pointer arithmetic. Let me tell you how we tracked it down.
The Mystery
U-Boot uses ‘linker lists’ extensively – a pattern where the linker collects scattered data structures into contiguous arrays. Drivers, commands, and unit tests all use this mechanism. Each list has start (_1) and end (_3) markers, with entries (_2_*) in between.
To count entries, we use pointer subtraction:
#define ll_entry_count(_type, _list) \
(ll_entry_end(_type, _list) - ll_entry_start(_type, _list))
Simple, right? The compiler divides the byte span by sizeof(struct) to get the count. Except sometimes it returns garbage.
The Clue: 0xCCCCCCCC
That -858993444 value caught my eye. In hex, it’s 0xCCCCCCCC – a suspiciously regular pattern. This isn’t random memory; it’s the result of a calculation.
GCC optimizes division by constants using multiplicative inverses. Instead of expensive division, it multiplies by a “magic number” and shifts. For dividing by 40 (a typical struct size), GCC generates something like:
movl %eax, %edx imulq $-858993459, %rdx ; magic number for /40 shrq $34, %rdx
This optimization is mathematically correct – but only when the dividend is an exact multiple of the divisor. When it’s not, you get rubbish.
The Root Cause
U-Boot’s CONFIG_LINKER_LIST_ALIGN (32 bytes on sandbox) aligns each list’s start. But here’s the subtle bug: the end marker was also being aligned:
#define ll_entry_end(_type, _list) \
({ \
static char end[0] __aligned(32) \ // <-- Problem!
__attribute__((used)) \
__section(".u_boot_list_"#_list"_3"); \
(_type *)&end; \
})
When the next list in memory has a higher alignment requirement, the linker inserts padding before our end marker. Our list might have 15 entries of 40 bytes each (600 bytes), but the span from start to end becomes 608 bytes due to padding.
608 / 40 = 15.2
Feed 608 into GCC’s magic-number division for 40, and out comes 0xCCCCCCCC.
The Fix
Change the end marker alignment to 1:
static char end[0] __aligned(1) // No padding before end marker
Now the end marker sits immediately after the last entry, the span is an exact multiple, and pointer arithmetic works correctly.
Detection
We enhanced our check_linker_lists.py script to catch this:
- Gap analysis: Compare gaps between consecutive symbols. Inconsistent gaps mean padding was inserted within the list.
- Size comparison: Using
nm -Sto get symbol sizes, we check ifgap > size. If so, padding exists. - Span verification: Check if
(end - start)is a multiple of struct size. If not, pointer subtraction will fail.
./scripts/check_linker_lists.py u-boot -v
Lessons Learned
- Magic number division is fragile: It only works for exact multiples. Any padding breaks it silently.
- Zero-size markers inherit alignment from neighbours: The linker places them at aligned boundaries based on what follows.
- Pointer arithmetic assumes contiguous arrays: This assumption is violated when padding sneaks in.
- Garbage values often have patterns:
0xCCCCCCCCisn’t random – it’s a clue pointing to failed arithmetic.
The fix was one line. Finding it took considerably longer, but that’s firmware development for you.
This bug was found while working on U-Boot’s unit test infrastructure. The fix is in the patch “linker_lists: Fix end-marker alignment to prevent padding”.