#!/bin/bash # test_time32_abi.sh — 反汇编断言:mipsel 二进制必须走 o32 time32 取时(4263), # 不得残留 time64 入口(4403)。这是宿主 fallback 单测无法覆盖的「真机取时路径」证据。 # # 用法(任意目录): # 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 # 可用 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。 set -u 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 字节缓冲、无宽度转换。 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_has_16b(from, to, j, l, frag, f, off, r, has_sra, seen, k, a) { delete seen has_sra = 0 for (j = from; j <= to && j <= NR; j++) { l = line[j] 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 (!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 } 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_has_16b(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_has_16b(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 done [ "$fail" -eq 0 ] && echo "ALL PASS" || echo "SOME TESTS FAILED" exit "$fail"