From 1ad2346d34caf852a3ba009640c9fe9e0631e8a8 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 18:38:05 +0800 Subject: [PATCH] =?UTF-8?q?fix(m43):=20r2=20=E8=AF=84=E5=AE=A1=20P2=20?= =?UTF-8?q?=E2=80=94=20=E6=9E=84=E5=BB=BA=E7=BA=A7=E4=B8=BB=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=20+=20=E6=95=B0=E6=8D=AE=E4=BE=9D=E8=B5=96=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=20+=20bootmenu=20=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test_time32_abi.sh 增加构建级主判据:解析各组件的 build_*.sh,按 -o <产物> 定位实际编译行(自动拼接续行),断言同时出现 meow_time32.c 与 -DMEOW_TIME32_LIBC_OVERRIDES。缺任一即 FAIL(100% 抓「漏加」)。 - 二进制 16B 断言从「空间窗口」改为「数据依赖」:0/4/8 写回必须来自 被调函数填的 ts32 缓冲(lw off() → sw 0x0,lw off+4() → sw 0x8, 含 sra 符号扩展),修掉无关结构体写回造成的 false positive。 已知健康件 launcherd(非内联转换)仍 PASS;复现的评审反例 bad6b 现 FAIL。 - bootmenu 注释把 initramfs 误述改为 squashfs /usr/bin(M23 未碰该文件)。 --- patch_v2/bootmenu/build_bootmenu.sh | 3 +- patch_v2/sdk/test_time32_abi.sh | 219 ++++++++++++++++++++-------- 2 files changed, 162 insertions(+), 60 deletions(-) diff --git a/patch_v2/bootmenu/build_bootmenu.sh b/patch_v2/bootmenu/build_bootmenu.sh index 34131a7..a966302 100755 --- a/patch_v2/bootmenu/build_bootmenu.sh +++ b/patch_v2/bootmenu/build_bootmenu.sh @@ -7,7 +7,8 @@ cd "$(dirname "$0")" CC="$HOME/Project/R3ProII/.tools/zig-mipsel-cc" MEOW_TOUCH_DIR="../meow_launcherd" # M43: bootmenu 也不链 meow_ui.a,单独链 time32 修取时/睡眠 ABI。只改语义正确的取时来源, -# boot_timeout/按键长按/守候计时行为不变(修复后才是真实毫秒;initramfs 内同样生效)。 +# boot_timeout/按键长按/守候计时行为不变(修复后才是真实毫秒;squashfs /usr/bin,改坏影响 +# 开机选单/自动启动,但不是 initramfs 级不可恢复)。 "$CC" -Os -static -I "$MEOW_TOUCH_DIR" -DMEOW_TIME32_LIBC_OVERRIDES \ meow_bootmenu.c "$MEOW_TOUCH_DIR/meow_touch.c" ../sdk/meow_time32.c -o meow_bootmenu ls -la meow_bootmenu diff --git a/patch_v2/sdk/test_time32_abi.sh b/patch_v2/sdk/test_time32_abi.sh index 64ba5b8..7499881 100755 --- a/patch_v2/sdk/test_time32_abi.sh +++ b/patch_v2/sdk/test_time32_abi.sh @@ -1,32 +1,60 @@ #!/bin/bash -# test_time32_abi.sh — 反汇编断言:mipsel 二进制必须走 o32 time32 取时(4263), -# 不得残留 time64 入口(4403)。这是宿主 fallback 单测无法覆盖的「真机取时路径」证据。 +# test_time32_abi.sh — time32 接入门禁(构建级主判据 + 二进制语义辅助判据)。 +# +# 主判据(构建级,直接抓「漏加」这个真实故障模式): +# 逐个组件解析 build_*.sh 里真正产出该二进制的编译逻辑行(按 -o <产物> 定位, +# 自动拼接反斜杠续行),断言命令行同时包含: +# meow_time32.c +# -DMEOW_TIME32_LIBC_OVERRIDES +# 只查构建命令,不受二进制里无关函数/结构体的干扰。 +# +# 辅助判据(二进制级): +# 1) 4263(SYS_clock_gettime time32)出现 ≥1 次;4403(SYS_clock_gettime64)0 次; +# 2) 4263 之后必须出现 16 字节 time64 布局写回,且写回数据必须来自被调函数填的 +# 8 字节 ts32 缓冲(数据依赖,防无关结构体写回造成假阳性): +# lw $r, off($sp) → sw $r, 0x0($base) +# sra ...,0x1f → sw ..., 0x4($base) +# lw $r2, off+4($sp) → sw $r2, 0x8($base) +# 同一 base 且 base 不是 $sp。内联形态在 4263 后 70 条内找;非内联形态 +# (如 launcherd 链接 meow_ui_time.c)在「4263 所在函数的调用者」jal 后 60 条内找。 +# 缺陷版(4263 的 a1 直接指向调用方 8 字节缓冲、无宽度转换)两条都不成立。 # # 用法(任意目录): -# bash patch_v2/sdk/test_time32_abi.sh patch_v2/meow_watchdog/meow_watchdog -# bash patch_v2/sdk/test_time32_abi.sh patch_v2/meow_watchdog/meow_watchdog patch_v2/meow_launcherd/meow_launcherd +# bash patch_v2/sdk/test_time32_abi.sh # 只跑构建门禁(10 个组件) +# bash patch_v2/sdk/test_time32_abi.sh ... # 构建门禁 + 二进制检查 +# bash patch_v2/sdk/test_time32_abi.sh --no-builds ... # 只跑二进制检查(旧用法) # 可用 OBJDUMP= 指定 objdump(默认 llvm-objdump)。 # -# 期望:每个二进制同时满足三条—— -# 1) 4263(SYS_clock_gettime time32)出现 ≥1 次; -# 2) 4403(SYS_clock_gettime64)出现 0 次; -# 3) 4263 之后必须把结果按 16 字节 time64 布局写回:tv_sec 写 0x0/0x4(含 sra ...0x1f -# 符号扩展)、tv_nsec 写 0x8。 -# 第 3 条是必需的:zig 0.17 的「缺陷版」与 meow_time32 的「修复版」都呈 4263-only, -# 只看 1/2 区分不了。缺陷版把 4263 的 a1 直接指向调用方 8 字节缓冲、无宽度转换;修复版 -# 才写 0x0/0x4/0x8。已知样本:amsd 出货件(缺陷)FAIL、修复后 amsd PASS、当前 zig 不加 -# meow_time32.c 重编的 sysui FAIL。 +# 已知自证样本:amsd 出货件(4263-only + 直写调用方)FAIL;修复后 amsd PASS; +# 当前 zig 不加 meow_time32.c 重编的 sysui FAIL;launcherd(非内联转换形态)PASS。 set -u +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" OD="${OBJDUMP:-llvm-objdump}" -command -v "$OD" >/dev/null 2>&1 || { echo "缺少 objdump: $OD(可用 OBJDUMP=... 覆盖)" >&2; exit 2; } -[ "$#" -gt 0 ] || { echo "用法: $0 [...]" >&2; exit 2; } -# 语义断言(两条路径,覆盖内联与非内联两种编译形态): -# A. 4263 指令所在窗口内出现同一基址寄存器的 0x0/0x4/0x8 存储 + sra 符号扩展(内联形态); -# B. 若 A 不成立,定位 4263 所在函数(最近的 jal 目标),检查其直接调用者在 jal 之后是否 -# 做同样的 0x0/0x4/0x8 写回(meow_ui_time.c 那种「调用 time32 取时再转换」的形态)。 -# 缺陷版两条都不成立:4263 的 a1 直达调用方 8 字节缓冲、无宽度转换。 +usage() { + echo "用法: $0 [--builds|--no-builds] [mipsel-binary ...]" >&2 + echo " 无参数 / --builds 只跑构建门禁(10 个 M43 组件)" >&2 + echo " ... 构建门禁 + 二进制检查" >&2 + echo " --no-builds 只跑二进制检查" >&2 +} + +# 组件 → 构建脚本(产物名用于在脚本里定位 -o <产物>)。 +# meow_audio 是 audiod 构建脚本的 cli 目标;audiod 守护本身是例外(不链 time32)。 +BUILD_TARGETS=' +meow_sysui|patch_v2/meow_sysui/build_sysui.sh +meow_svcd|patch_v2/meow_svcd/build_svcd.sh +meow_bootmenu|patch_v2/bootmenu/build_bootmenu.sh +meow_amsd|patch_v2/meow_amsd/build_amsd.sh +meow_netmgrd|patch_v2/meow_netmgrd/build_netmgrd.sh +meow_crash|patch_v2/meow_crash/build_crash.sh +meow_svc_perf|patch_v2/meow_svc_perf/build_svc_perf.sh +meow_powermgr|patch_v2/meow_powermgr/build_powermgr.sh +meowpanel|patch_v2/meowpanel/build_meowpanel.sh +meow_audio|patch_v2/meow_audiod/build_audiod.sh +' + +# 语义断言:要求 16 字节写回与「被调函数填的 8 字节 ts32 缓冲」存在数据依赖。 sem_awk=' function hex2num(h, i, n, c) { n = 0; h = tolower(h) @@ -37,27 +65,38 @@ function hex2num(h, i, n, c) { } return n } -function window_has_16b(from, to, j, l, frag, f, off, r, has_sra, seen, k, a) { - delete seen +function window_dep(from, to, j, l, s, parts, reg, rest, off, base, has_sra, loadoff, sec_off, ok0, ok4, ok8, b) { + delete loadoff; delete sec_off; delete ok0; delete ok4; delete ok8 has_sra = 0 for (j = from; j <= to && j <= NR; j++) { l = line[j] + if (match(l, /(^|[ \t])lw[ \t]+\$[0-9a-z]+,[ \t]*0x[0-9a-f]+\(\$sp\)/)) { + s = substr(l, RSTART, RLENGTH) + sub(/^.*lw[ \t]+\$/, "", s) + split(s, parts, /,[ \t]*0x/) + reg = parts[1]; rest = parts[2]; off = rest; sub(/\(.*/, "", off) + loadoff[reg] = hex2num(off) + } if (l ~ /sra[ \t]+\$[0-9a-z]+,[ \t]*\$[0-9a-z]+,[ \t]*0x1f/) has_sra = 1 - if (l ~ /(^|[ \t])sw[ \t]+\$[0-9a-z]+,[ \t]*0x[048]\(\$[0-9a-z]+\)/) { - frag = l - if (match(frag, /0x[048]\(\$[0-9a-z]+\)/)) { - f = substr(frag, RSTART, RLENGTH) - off = substr(f, 3, 1) - r = f; sub(/.*\(\$/, "", r); sub(/\).*/, "", r) - seen[off SUBSEP r] = 1 + if (match(l, /(^|[ \t])sw[ \t]+\$[0-9a-z]+,[ \t]*0x[048]\(\$[0-9a-z]+\)/)) { + s = substr(l, RSTART, RLENGTH) + sub(/^.*sw[ \t]+\$/, "", s) + split(s, parts, /,[ \t]*0x/) + reg = parts[1]; rest = parts[2] + off = rest; sub(/\(.*/, "", off) + base = rest; sub(/^.*\(\$/, "", base); sub(/\).*/, "", base) + if (base == "sp") continue + if (off == "0" && (reg in loadoff)) { + sec_off[base] = loadoff[reg]; ok0[base] = 1 + } else if (off == "4" && (base in ok0) && has_sra) { + ok4[base] = 1 + } else if (off == "8" && (base in sec_off) && (reg in loadoff) \ + && loadoff[reg] == sec_off[base] + 4) { + ok8[base] = 1 } } } - if (!has_sra) return 0 - for (k in seen) { - split(k, a, SUBSEP) - if (a[1] == "0" && (("4" SUBSEP a[2]) in seen) && (("8" SUBSEP a[2]) in seen)) return 1 - } + for (b in ok0) if ((b in ok4) && (b in ok8)) return 1 return 0 } { line[NR] = $0 } @@ -75,7 +114,7 @@ END { } for (g = 1; g <= ng; g++) { i = gt[g] - if (window_has_16b(i + 1, i + 70)) exit 0 + if (window_dep(i + 1, i + 70)) exit 0 } for (g = 1; g <= ng; g++) { a = addr[gt[g]] @@ -87,37 +126,99 @@ END { if (best < 0) continue for (j = 1; j <= nj; j++) { if (jto[j] != best) continue - if (window_has_16b(jfrom[j] + 1, jfrom[j] + 60)) exit 0 + if (window_dep(jfrom[j] + 1, jfrom[j] + 60)) exit 0 } } exit 1 } ' -fail=0 -for bin in "$@"; do - [ -f "$bin" ] || { echo "FAIL 文件不存在: $bin"; fail=1; continue; } - dis="$("$OD" -d --triple=mipsel "$bin" 2>/dev/null)" || { - echo "FAIL objdump 失败: $bin"; fail=1; continue; - } - # 只匹配「把系统调用号装进 a0」的指令,不能裸 grep 0x1133:分支/跳转目标的 - # 十六进制偏移(如 <.text+0x11338>)也含该子串,会把已修好的二进制误判为残留 4403。 - n_time32=$(printf '%s\n' "$dis" | grep -cE 'addiu[[:space:]]+\$[0-9a-z]+,[[:space:]]*\$zero,[[:space:]]*0x10a7' || true) - n_time64=$(printf '%s\n' "$dis" | grep -cE 'addiu[[:space:]]+\$[0-9a-z]+,[[:space:]]*\$zero,[[:space:]]*0x1133' || true) - sem=FAIL - if printf '%s\n' "$dis" | awk "$sem_awk"; then sem=OK; fi - printf '%-52s 4263=%-2s 4403=%-2s 16B-layout=%s\n' \ - "$bin" "$n_time32" "$n_time64" "$sem" - if [ "$n_time32" -lt 1 ]; then - echo "FAIL $bin: 未找到 4263(o32 time32)取时路径"; fail=1 - fi - if [ "$n_time64" -ne 0 ]; then - echo "FAIL $bin: 发现 4403(time64)路径(应有 0 处)"; fail=1 - fi - if [ "$sem" != "OK" ]; then - echo "FAIL $bin: 4263 后无 16 字节 time64 写回(0x0/0x4/0x8)——疑似直接写调用方 8 字节/无宽度转换"; fail=1 - fi +builds=1 +bins=() +for a in "$@"; do + case "$a" in + --builds) builds=1 ;; + --no-builds) builds=0 ;; + -h|--help) usage; exit 0 ;; + -*) echo "未知参数: $a" >&2; usage; exit 2 ;; + *) bins+=("$a") ;; + esac done +if [ "${#bins[@]}" -eq 0 ] && [ "$builds" -eq 0 ]; then + usage; exit 2 +fi + +fail=0 + +# ---- 主判据:构建脚本必须链 meow_time32.c 且启用 LIBC_OVERRIDES ---- +check_build() { + target="$1"; script="$2" + if [ ! -f "$ROOT/$script" ]; then + echo "FAIL build $target: 构建脚本不存在: $script"; fail=1; return + fi + # 拼接反斜杠续行,按 -o 找真正产出该二进制的逻辑行。 + line=$(awk -v bin="$target" ' + { + buf = (buf == "" ? $0 : buf " " $0) + if (buf ~ /\\[ \t]*$/) { sub(/\\[ \t]*$/, "", buf); next } + if (buf ~ ("(^|[ \t])-o[ \t]+" bin "([ \t]|$)")) print buf + buf = "" + } + END { + if (buf != "" && buf ~ ("(^|[ \t])-o[ \t]+" bin "([ \t]|$)")) print buf + } + ' "$ROOT/$script") + if [ -z "$line" ]; then + echo "FAIL build $target: $script 中未找到 -o $target 的编译命令"; fail=1; return + fi + has_src=no; has_macro=no + case "$line" in *meow_time32.c*) has_src=yes ;; esac + case "$line" in *-DMEOW_TIME32_LIBC_OVERRIDES*) has_macro=yes ;; esac + printf 'build %-14s src=%-3s macro=%-3s (%s)\n' "$target" "$has_src" "$has_macro" "$script" + if [ "$has_src" != yes ] || [ "$has_macro" != yes ]; then + echo "FAIL build $target: 编译命令缺少 meow_time32.c 或 -DMEOW_TIME32_LIBC_OVERRIDES" + fail=1 + fi +} + +if [ "$builds" -eq 1 ]; then + echo "== 构建门禁(meow_time32.c + -DMEOW_TIME32_LIBC_OVERRIDES)==" + while IFS='|' read -r target script; do + [ -z "$target" ] && continue + check_build "$target" "$script" + done </dev/null 2>&1 || { echo "缺少 objdump: $OD(可用 OBJDUMP=... 覆盖)" >&2; exit 2; } + echo "== 二进制检查(4263/4403 + 16B 数据依赖写回)==" + for bin in "${bins[@]}"; do + [ -f "$bin" ] || { echo "FAIL 文件不存在: $bin"; fail=1; continue; } + dis="$("$OD" -d --triple=mipsel "$bin" 2>/dev/null)" || { + echo "FAIL objdump 失败: $bin"; fail=1; continue; + } + # 只匹配「把系统调用号装进寄存器」的指令:裸 grep 0x1133 会把分支偏移 + # (如 <.text+0x11338>)误判成残留 4403。 + n_time32=$(printf '%s\n' "$dis" | grep -cE 'addiu[[:space:]]+\$[0-9a-z]+,[[:space:]]*\$zero,[[:space:]]*0x10a7' || true) + n_time64=$(printf '%s\n' "$dis" | grep -cE 'addiu[[:space:]]+\$[0-9a-z]+,[[:space:]]*\$zero,[[:space:]]*0x1133' || true) + sem=FAIL + if printf '%s\n' "$dis" | awk "$sem_awk"; then sem=OK; fi + printf '%-52s 4263=%-2s 4403=%-2s 16B-layout=%s\n' \ + "$bin" "$n_time32" "$n_time64" "$sem" + if [ "$n_time32" -lt 1 ]; then + echo "FAIL $bin: 未找到 4263(o32 time32)取时路径"; fail=1 + fi + if [ "$n_time64" -ne 0 ]; then + echo "FAIL $bin: 发现 4403(time64)路径(应有 0 处)"; fail=1 + fi + if [ "$sem" != "OK" ]; then + echo "FAIL $bin: 4263 后无「ts32 缓冲→16 字节写回」数据依赖(疑似直接写调用方 8 字节/无宽度转换或无关联结构体写回)"; fail=1 + fi + done +fi [ "$fail" -eq 0 ] && echo "ALL PASS" || echo "SOME TESTS FAILED" exit "$fail"