#!/bin/bash # 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 # 只跑构建门禁(11 个组件) # bash patch_v2/sdk/test_time32_abi.sh ... # 构建门禁 + 二进制检查 # bash patch_v2/sdk/test_time32_abi.sh --no-builds ... # 只跑二进制检查(旧用法) # 可用 OBJDUMP= 指定 objdump(默认 llvm-objdump)。 # # 已知自证样本: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}" usage() { echo "用法: $0 [--builds|--no-builds] [mipsel-binary ...]" >&2 echo " 无参数 / --builds 只跑构建门禁(11 个 M43 组件 + meow_watchdog)" >&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 meow_watchdog|patch_v2/meow_watchdog/build_watchdog.sh ' # 语义断言:要求 16 字节写回与「被调函数填的 8 字节 ts32 缓冲」存在数据依赖。 sem_awk=' function hex2num(h, i, n, c) { n = 0; h = tolower(h) for (i = 1; i <= length(h); i++) { c = index("0123456789abcdef", substr(h, i, 1)) - 1 if (c < 0) return -1 n = n * 16 + c } return n } 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 (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 } } } for (b in ok0) if ((b in ok4) && (b in ok8)) return 1 return 0 } { line[NR] = $0 } END { ng = 0; nj = 0 for (i = 1; i <= NR; i++) { if (!match(line[i], /^[ \t]*[0-9a-f]+:/)) continue h = substr(line[i], RSTART, RLENGTH); sub(/:$/, "", h); gsub(/[ \t]/, "", h) addr[i] = hex2num(h) if (line[i] ~ /addiu[ \t]+\$[0-9a-z]+,[ \t]*\$zero,[ \t]*0x10a7/) { ng++; gt[ng] = i } if (match(line[i], /jal[ \t]+0x[0-9a-f]+/)) { s = substr(line[i], RSTART, RLENGTH); sub(/.*0x/, "", s) nj++; jfrom[nj] = i; jto[nj] = hex2num(s) } } for (g = 1; g <= ng; g++) { i = gt[g] if (window_dep(i + 1, i + 70)) exit 0 } for (g = 1; g <= ng; g++) { a = addr[gt[g]] best = -1 for (j = 1; j <= nj; j++) { t = jto[j] if (t <= a && a - t <= 1024 && t > best) best = t } if (best < 0) continue for (j = 1; j <= nj; j++) { if (jto[j] != best) continue if (window_dep(jfrom[j] + 1, jfrom[j] + 60)) exit 0 } } exit 1 } ' 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"