From 89f992bda316b2d9ccb295442000c0d860e80ab2 Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Sun, 30 Aug 2026 15:46:49 +0200 Subject: [PATCH] Round 23: root cause = linker fill before _start skews PIE fixup; TEXT_BASE 8-aligned, script asserts _start==__image_copy_start --- RESEARCH.md | 40 ++++++++++++++++++++++++++++++++++++++ build-uboot-payload.sh | 42 +++++++++++++++++----------------------- krane-uboot-payload.bin | Bin 598016 -> 598016 bytes krane-uboot.bin | Bin 454028 -> 454024 bytes 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/RESEARCH.md b/RESEARCH.md index 66528d2..3eb8abe 100644 --- a/RESEARCH.md +++ b/RESEARCH.md @@ -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. diff --git a/build-uboot-payload.sh b/build-uboot-payload.sh index 090e245..46e6e6c 100755 --- a/build-uboot-payload.sh +++ b/build-uboot-payload.sh @@ -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('> 2) | 0x14000000) # code0: b +0x40 struct.pack_into('I2M_+NJ^e5 zTK>Sw$&Br@H+`L^dxX>e(3y=tH?mY->`(81+c5j+>xaoZ--%vM+W0qd`iAo3E83jA zomb1X1~M$0wMyMKX^tLC$jgFPD_#q|ubfx>U*~^WhK|a{S5H>U$rVb~|2}D@G&L^z zvCgS0t{E4!8bVgeul}&}!-Ds5x?5kI`QX1uE5daB^^ePs|H*iIY4Xtt(Q#)T7U)|W zSh}>#*{m5z03f>=g_9eBxbGNg}n&}z)%t`w0&f7sh z*$+NetKPw#?ag>=+9cH^wYKw5_QcMsKguRD-@E>H=*{V$*B@mSv6^7VOvvE0AkLG#aeztURDqS|wA*3WWNl?CBU z(Kq-#Y{Lw{y{(u%Yk%XLix%QPmSQ`%O5+vf=~y2{IZP-I}3$jQL?+JTuN zB$4%h1c?3c|9_Bp`+oz*?f(s!45fkAHBC46VbTRLwkP{A-DG2YG~G6sX*PG_|Nj#h z5=$wRiLeqW3*(6y_Osq^z g+q1;kwr7d6pSsSX!TVpg-QqnvFeuI}`@mrU07lDEasU7T delta 820 zcmZoTpwe(aWrGfDy_V5km(1IOq91n)yVTF0Jm+iG-xHii9FoqRKlZ&_eh;H(ugBwx zlYbUAEx5X$VY2S>w@Z$_UchE5^>1;Tdbh&DA3ZxlE$of>Sr2O-{`506v%UX)xaHbo z6Spz@I?R0Be>r9K7xC~(e7pTz7=E5~kNoZ(av;^_=zP{4hv)kxhqlbAufJRterLwz z*?A3I3o731@%SlXnm^Oy^Rjd6_fOx~a!$zNe%fQ5tMAxzo^F#VQnIP|x~9!|vC}5U zn`gHPR7SF#n0ZuCuOsQ9RbgMV{!*hW{pt^|uAAO;-*r~QryBti=KDM}y(;D=xNpMB zm3v<^4<=IrA5xMjo_5<8D45H zH~z}Gsh-)~t(S|{`H?k7Iid-4s8 zQhXRT9enoPYO$Qip?L<|QyS8?{1mr6vgum<*LO;XnfF>5p7LfaaOqy3FX!07a?yBZ zW|G7Af~I*B6$64DP5mwyua6MPoP2cIr|lmfZb`0UQP&f<+-%5x{>NkocGfA3?3`T; z3=9?w><@XS3NVT@UYUN;f>n~Afq@w)%mT!DSyS}ew+1j;Gm7;9xk>^I3?e`p4tlmP zWMt%EWMtpIiizkV v7?~;PDj1qt8JSrbnzyHkvu#fkXFqkFWp(NQzIK!M?9)x&bDUWAfx`d*<-%ME diff --git a/krane-uboot.bin b/krane-uboot.bin index 5cf96b1991ceb5da5e8385be7154933c6370b0ee..fc2c2daba0e87c475bb7f74f541e6b861815a895 100644 GIT binary patch delta 198 zcmeCVE!}ZjT2O$2L4*MbbZivlU}R+8EXDYYkBbW^DX+kMl|f)LpMa~p%m+mVhKZaE zjISM-8A1|S|3`q>5C8uMi8tr#Z_n3fG?Zp$U}&1Y*o#pY#Mpk>i}5BK2QdOM6A&|R_YY!eHsUccGqNyFG&eUgN;TYmuZ88@1TADC-IUCt pl1c>)$I^5KV*>>v10yp9T?Io^D