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:
+40
@@ -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 R12–R22 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
@@ -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.
Binary file not shown.
Reference in New Issue
Block a user