Files
MeowOS-For-R3P2/patch_v2/sdk/test_time32_abi.sh
T
AskaEth 1ad2346d34 fix(m43): r2 评审 P2 — 构建级主判据 + 数据依赖语义 + bootmenu 注释
- 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 未碰该文件)。
2026-11-11 18:51:16 +08:00

225 lines
9.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 # 只跑构建门禁(10 个组件)
# bash patch_v2/sdk/test_time32_abi.sh <mipsel-bin> ... # 构建门禁 + 二进制检查
# bash patch_v2/sdk/test_time32_abi.sh --no-builds <bin> ... # 只跑二进制检查(旧用法)
# 可用 OBJDUMP=<path> 指定 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 只跑构建门禁(10 个 M43 组件)" >&2
echo " <binary> ... 构建门禁 + 二进制检查" >&2
echo " --no-builds <binary> 只跑二进制检查" >&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)
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 <target> 找真正产出该二进制的逻辑行。
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 <<EOF
$BUILD_TARGETS
EOF
fi
# ---- 辅助判据:二进制反汇编 ----
if [ "${#bins[@]}" -gt 0 ]; then
command -v "$OD" >/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"