Round 23: root cause = linker fill before _start skews PIE fixup; TEXT_BASE 8-aligned, script asserts _start==__image_copy_start

This commit is contained in:
vhaudiquet
2026-08-30 15:46:49 +02:00
parent 8efb7ffe43
commit 89f992bda3
4 changed files with 58 additions and 24 deletions
+40
View File
@@ -882,3 +882,43 @@ initcall sequence.
| green only | initcalls before early_init_f: fdtdec_setup (embedded DTB!), log, bootstage, bloblist, arch/mach_cpu_init, initf_dm (clock driver probes) |
| green + orange | after early_init_f, before dram_init_banksize |
| green + orange + blue | after dram_init_banksize, before relocation |
## Round 23 — [REVERSED R22 analysis] true root cause: 4-byte linker fill before _start skews the PIE fixup
R22 (`21c3f792…`): blue hold only — board_init_f's green band never
painted. Root cause found in the disassembly, not guessed:
- `start.o`'s input `.text` section is 8-byte aligned, but
CONFIG_TEXT_BASE=0x4C000FFC is only 4-aligned → ld inserts a 4-byte
FILL at the start of .text → `_start` lands at TEXT_BASE+4, while
`_TEXT_BASE` (the word start.S's PIE fixup loads as the link base)
and `__image_copy_start` remain at TEXT_BASE.
- start.S: `pie_fixup: adr x0,_start; ldr x1,_TEXT_BASE; subs x9,x0,x1`
→ x9 = true_delta + 4 → EVERY relative relocation skewed by 4 →
corrupted gd/function/fdt pointers → death before board_init_f.
(The `adrp + #:lo12:` pairs used for the rela bounds also assume
delta ≡ 0 mod 4K — with the skew, both mechanisms break.)
- This retroactively explains the R12R22 alignment confusion: the
"+4" was never a stub/vectors mystery — it was a linker fill that
appears iff CONFIG_TEXT_BASE is not 8-aligned.
- [REVERSED] the "vectors/stub bytes precede _start" note from R18/19;
the 0xFFC leading bytes in old u-boot.bin dumps were the fill plus
ELF file-offset artifacts, and the R22 "dies in pure asm" conclusion
was wrong in mechanism (it died IN the fixup's corrupted pointers).
### Fix (payload `18cf3e93…`, flashed, cmp+vbutil OK)
- CONFIG_TEXT_BASE=0x4C001000 (8-aligned) → no fill:
`_start == __image_copy_start == _TEXT_BASE == 0x4C001000`, file
offset 0 = `b reset` (verified in u-boot-nodtb.bin and in the packed
image at 0x1000).
- File placed at image offset 0x1000 → runtime _start = 0x40001000,
start.S 4K check passes; PIE delta = -0xC000000 (4K-aligned).
- Payload script now derives the file offset from __image_copy_start
(not ELF section offsets) and ASSERTS `_start == __image_copy_start`
so a fill regression fails at build time instead of on device.
### Expected
blue → 3 blinks → 5 s hold → green band (board_init_f) → orange/blue/
red bands → cyan/white (video probe) → banner.
+18 -24
View File
@@ -40,35 +40,30 @@ uboot = open(uboot_path, 'rb').read()
wrapper = open('uboot-wrapper.bin', 'rb').read()
assert len(wrapper) == wrap_len
# VMA -> file offset inside the u-boot image (single contiguous LOAD
# segment): derive the constant from the lowest-VMA ELF section.
for line in subprocess.check_output(
['aarch64-linux-gnu-objdump', '-h',
uboot_path.replace('.bin', '')],
text=True).splitlines():
f = line.split()
if len(f) == 7 and f[1].startswith('.'):
vma_to_file = int(f[5], 16) - int(f[3], 16)
break
# u-boot.bin starts at __image_copy_start (the lowest output VMA);
# _start's file offset is its delta from that base. The PIE fixup in
# start.S loads the link base from _TEXT_BASE and the run base from
# adr _start, so _start MUST equal __image_copy_start (a 4-byte
# linker fill sneaks in when CONFIG_TEXT_BASE is not 8-aligned —
# start.o's .text input section is 8-aligned — and then every
# relocated pointer is skewed by 4). Fail loudly instead.
copy_vma = None
start_vma = None
for line in open(sym_path):
parts = line.split()
if len(parts) >= 2 and parts[-1] == '_start':
if len(parts) >= 2 and parts[-1] == '__image_copy_start':
copy_vma = int(parts[0], 16)
elif len(parts) >= 2 and parts[-1] == '_start':
start_vma = int(parts[0], 16)
break
else:
raise SystemExit('_start not found in u-boot.sym')
# PIE fixup (adrp/add lo12) needs runtime _start == link _start (mod 4K),
# so link _start must itself be 4K-aligned; then place the file so the
# runtime _start lands on a 4K boundary too. _start is NOT at file
# offset 0: the image leads with 0xFFC bytes of vectors/stub data
# (link _start = 0x4C001000 = file offset 0x1000 for TEXT_BASE
# 0x4C000FFC).
if copy_vma is None or start_vma is None:
raise SystemExit('symbols not found in u-boot.sym')
assert start_vma == copy_vma, \
("_start 0x%x != __image_copy_start 0x%x: CONFIG_TEXT_BASE is not "
"8-aligned and a linker fill shifted _start" %
(start_vma, copy_vma))
assert start_vma % 0x1000 == 0, \
"link _start 0x%x not 4K-aligned" % start_vma
start_file_off = start_vma + vma_to_file
start_file_off = start_vma - copy_vma
wrap_off = 0x40
uboot_off = 0x1000
while (uboot_off + start_file_off) % 0x1000:
@@ -77,7 +72,6 @@ pad = uboot_off - wrap_off - wrap_len
assert pad >= 0
total = uboot_off + len(uboot)
assert (0x40000000 + uboot_off + start_file_off) % 0x1000 == 0
hdr = bytearray(64)
hdr[0:4] = struct.pack('<I', (0x40 >> 2) | 0x14000000) # code0: b +0x40
struct.pack_into('<Q', hdr, 0x10, total) # image_size
Binary file not shown.
BIN
View File
Binary file not shown.