公开 SDK 落后主仓的一批 bugfix(主仓是固件构建源,这些修复已在设备上跑过)。 本次只做「主仓 → 公开仓」单向同步,图像栈方向由主仓侧另一单处理。 时钟 ABI(M16 第二弹,原文件自认「残余未修」): - meow_ui_time.c 147→345 行:futex 相对超时接管(__timedwait/_cp)、 timerfd_create/settime/gettime 的 itimerspec 64↔32 双向转换、 __clock_settime64 越界返 ENOTSUP,另有 4 个纯换算函数 - meow_ui_internal.h:struct meow_time32 / meow_itimerspec32 契约移入内部头 - CLOCK_ABI.md:补接管机制、验证配方与 objdump 证据 - 新增 test_meow_time64.c IME bug3(成对才有意义): - meow_ui_ime.c:IME_TAP_COOLDOWN_MS 200 冷却窗 + 打开时二次 drain - meow_ui_input.c:meow_input_touch_reset() 并在 drain 内调用(复位触摸状态机) - test_ime_fix.c:新增用例④ stray-tap 列表行高(M15,修用户可见 bug): - meow_ui_widget.c:listv_item_h() 由主题字体行高派生,不再写死 56 (两行列表 62 > 56 会让首行标题被裁剪守卫整条跳过) - include/meow_ui_widgets.h:新增 meow_listview_item_h() - 新增 test_meow_ui_listview_rowh.c 其余:meow_select.c / meow_ui_dialog.c 统一 MD3 形状 token; include/meow_ipc.h 补 MSG_KEY_PAGEUP/PAGEDOWN 枚举; meow_ui_sys.c / meow_cachegov.c 取时统一走 meow_ui_time_ms()。 测试辅助函数标定:tuku/test/test_settings_lock_host.c 的注入前等待 150ms→350ms。 该等待按修复前 IME 标定,新冷却窗下首键 dt_open≈114ms 会被当迟到抬起吞掉, 只录到 "68" 两位。仅改等待时长,断言原样保留。 验证:tuku 四套脚本全绿(run_tests 644 PASS / zip 106 / cbz 296 / webtoon PASS); 12 个源文件与主仓逐字节一致;sdk/build_sdk.sh 34 成员; tuku/build_tuku.sh 产出静态 MIPS32r2 ELF。
319 lines
18 KiB
Markdown
319 lines
18 KiB
Markdown
# SDK 时钟 ABI 说明(meow_ui_time_ms 真机故障的根因与修复)
|
||
|
||
面向对象:维护 SDK / 工具链 / 重链 App 的人。**结论先行**:这不是 SDK 代码写错,
|
||
是 zig 0.17 自带 musl 在 mipsel 上的 time64 ABI 自相矛盾;修复落在 SDK 链上
|
||
(`patch_v2/sdk/meow_ui_time.c` + 链接期强定义),改后需重编 `meow_ui.a` 并重链四 App。
|
||
|
||
## 1. 现象(真机)
|
||
|
||
M10 抓到的日志首列(`meow_ui_time_ms()`,64 位)可拆成:
|
||
|
||
```
|
||
6607363222717682072 → lo=1135000 hi=1538396632
|
||
8754846870717682072 → lo=1135000 hi=2038396632
|
||
18138485921460348192 → lo=1140000 hi=4223195352
|
||
```
|
||
|
||
- 低 32 位 = 开机秒数 × 1000,**恒为 1000 的倍数 → 毫秒位丢失**;
|
||
- 高 32 位每次调用随机、无单调性。
|
||
|
||
同进程另一份取时(旧容器编的 core)正常 → 问题只在「zig 0.17 编的那部分」。
|
||
|
||
## 2. 根因链(三段证据)
|
||
|
||
### 2.1 zig 头文件:`time_t` 是 64 位,`struct timespec` 16 字节
|
||
|
||
`<zig lib>/libc/include/mips-linux-musl/bits/alltypes.h`:
|
||
|
||
```c
|
||
#define _REDIR_TIME64 1
|
||
#define __USE_TIME_BITS64 1
|
||
typedef _Int64 time_t; /* 无条件 64 位 */
|
||
struct timespec { time_t tv_sec; ...; long tv_nsec; ... }; /* tv_sec@0(8B)、tv_nsec@8 */
|
||
```
|
||
|
||
`include/generic-musl/time.h` 因此在 `_REDIR_TIME64` 下把 `clock_gettime` 重定向到
|
||
`__clock_gettime64`。用该链编译的 C 代码一律按「16 字节、tv_sec 8 字节」取数
|
||
(`patch_v2/sdk/meow_ui.a(meow_ui_gfx.o)` 的旧 `meow_ui_time_ms` 反汇编:`lw 0x10/0x14`
|
||
取 tv_sec、`lw 0x18` 取 tv_nsec,正是这个布局)。
|
||
|
||
### 2.2 zig 的 clang 给 mipsel 预定义了 `__ILP32__`
|
||
|
||
```
|
||
$ echo | zig cc --target=mipsel-linux-musleabi -dM -E - | grep __ILP32__
|
||
#define __ILP32__ 1
|
||
```
|
||
|
||
(arm-linux-musleabi 同样为 1;x86-linux-musl / aarch64 为 0。容器里的 gcc 对 mips o32
|
||
**不定义** `__ILP32__`。)
|
||
|
||
### 2.3 musl 的 time64 回退分支被这句 `!__ILP32__` 整体编译掉
|
||
|
||
`<zig lib>/libc/musl/src/time/clock_gettime.c`:
|
||
|
||
```c
|
||
#if defined(SYS_clock_gettime64) && !defined(__ILP32__)
|
||
... __syscall(SYS_clock_gettime64, clk, ts); /* 4403 */
|
||
... 失败(ENOSYS) 再 __syscall(SYS_clock_gettime, clk, ts32); /* 4263 + 宽度转换 */
|
||
#else
|
||
r = __syscall(SYS_clock_gettime, clk, ts); /* 直接把 8 字节 time32 写进调用者缓冲 */
|
||
#endif
|
||
```
|
||
|
||
这句 `!__ILP32__` 本意只排除 x32(`__ILP32__` 是 x32 的标记),但 clang 把它当成
|
||
「32 位数据模型」的通名。于是 zig 的 mipsel libc 走进 `#else`:
|
||
`__clock_gettime64` 实际执行 **SYS_clock_gettime(4263)**,只写 8 字节
|
||
`{ int32 tv_sec; int32 tv_nsec; }`。
|
||
|
||
**证据(反汇编,当前 zig 0.17 静态链接的探针)**:
|
||
|
||
```
|
||
0002576c <__clock_gettime64>:
|
||
257c8: addiu $2,$zero,0x10a7 ; 4263 = SYS_clock_gettime(time32)
|
||
257cc: syscall ; $5 直接是调用者的 16 字节 struct 指针,无 4403 尝试、无转换
|
||
```
|
||
|
||
同一符号在**旧容器产物** `patch_v2/ncm_app/ncm`(已入库,gcc 构建)里是:
|
||
|
||
```
|
||
115748: addiu $2,$zero,0x1133 ; 4403 = SYS_clock_gettime64
|
||
11574c: syscall
|
||
...
|
||
115774: addiu $2,$zero,0x10a7 ; ENOSYS 后回退 4263,写入本地 ts32
|
||
...
|
||
; 再把 ts32 升成 64 位写回调用者(tv_sec=tv_sec、tv_nsec=tv_nsec)
|
||
```
|
||
|
||
两者对比即根因:**头文件 ABI(64 位 time_t)与 libc 实现 ABI(32 位 time32)不一致**。
|
||
|
||
### 2.4 数值解释(为什么「低 32 = 秒×1000、高 32 随机」)
|
||
|
||
libc 把 `{sec, nsec}` 当两个 int32 写进 16 字节缓冲的前 8 字节;SDK 按 64 位读:
|
||
|
||
```
|
||
tv_sec = sec | (nsec << 32) // 低字正确、高字=纳秒
|
||
tv_nsec = 缓冲偏移 8 的未初始化值 // 毫秒项≈0
|
||
result = tv_sec*1000 + tv_nsec/1e6
|
||
= sec*1000 + (nsec*1000 mod 2^32)<<32 + 垃圾/1e6
|
||
```
|
||
|
||
- 低 32 位只剩 `sec*1000`(nsec 项左移 32 位不落到低字)→ 恒为 1000 的倍数;
|
||
- 高 32 位 = `nsec*1000 mod 2^32` → 每次随机、不单调;
|
||
- `tv_nsec` 来自越界读 → 通常是小值/0(真机上恰好 0,所以没有额外尾数)。
|
||
|
||
## 3. 修复(`patch_v2/sdk/meow_ui_time.c`)
|
||
|
||
1. **取数走内核 o32 ABI**:`meow_ui_time_ms()` 用
|
||
`syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &{int32 sec; int32 nsec;})`,
|
||
再 `meow_time_ms_from_parts(sec, nsec)` 升成 64 位毫秒。完全不依赖 libc 头声明的
|
||
`struct timespec` 宽度 —— 即使将来 zig 修好(或换回容器链),内核 time32 入口始终存在。
|
||
2. **链接期修 libc 入口**:同一 TU 强定义 `__clock_gettime` / `__clock_gettime64`
|
||
(musl 的 `time()`、`gettimeofday()`、`__timedwait_cp()`(pthread 定时等待)内部都调
|
||
`__clock_gettime`)。应用链接顺序是 `... meow_ui.a ... -lc`,归档先解析符号,libc 里那份
|
||
坏实现(及其 VDSO 前导)不会被拉入 —— 用 `nm` 验证链接产物里没有 `cgt_init/__vdsosym`,
|
||
且 `__time64`/`__timedwait_cp` 的 `jal` 指向本 TU 的实现。
|
||
3. **睡眠入口一并修正(同源缺陷)**:`__nanosleep_time64` / `__clock_nanosleep(_time64)`
|
||
先按 time32 转换再发 `SYS_nanosleep`/`SYS_clock_nanosleep`,`rem` 再扩回 64 位。
|
||
修复前 zig libc 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||
SDK 里 `usleep(16000)`(帧节拍、输入轮询、IME 冷却)全部变成忙等空转。
|
||
4. **去掉四处重复的 `now_ms()`**:`meow_ui_gfx.c` / `meow_ui_sys.c` / `meow_ui_input.c` /
|
||
`meow_cachegov.c` 统一调 `meow_ui_time_ms()`(cachegov 保留宿主测试注入桩)。
|
||
5. 构建脚本 `build_sdk.sh` 的 OBJS 增加 `meow_ui_time`;宿主单测 `test_meow_time.c`
|
||
固定换算边界与「单调且前进」;探针 `tools/clock_abi_probe.c` 用于宿主/真机一眼验收。
|
||
6. **M16 续修同源入口**(同一 TU,`#if MEOW_TIME32_KERNEL_ABI` 限定 mips o32):
|
||
`__timedwait_cp`/`__timedwait`(futex 相对超时)、`__timerfd_settime64`/
|
||
`__timerfd_gettime64`/`timerfd_create`、`__clock_settime64`。换算逻辑抽成平台无关的
|
||
`meow_time32_{rel,abs}_from_parts()` / itimerspec 转换(`meow_ui_internal.h`),
|
||
宿主 `test_meow_time64.c` 直接覆盖。清单、逐项证据与未修项配方见 §6/§7。
|
||
|
||
非 mips 目标(宿主单测、aarch64 宿主)走普通 `clock_gettime`,行为不变。
|
||
|
||
## 4. 验证
|
||
|
||
宿主:
|
||
|
||
```
|
||
cd patch_v2/sdk
|
||
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o /tmp/meow_ui_time.o
|
||
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c /tmp/meow_ui_time.o -o /tmp/test_meow_time
|
||
/tmp/test_meow_time # == PASS ==(换算边界 + 单调前进)
|
||
```
|
||
|
||
交叉:
|
||
|
||
```
|
||
bash patch_v2/sdk/build_sdk_host.sh --verify # 成员清单/架构自检 + 0 warning
|
||
llvm-objdump -d meow_ui.a(meow_ui_time.o) # meow_ui_time_ms: syscall 4263, lw 0x18 取 nsec、sra 扩展 tv_sec
|
||
```
|
||
|
||
链接期接管(用真实归档链一个探针,检查符号来源):
|
||
|
||
```
|
||
# 不应出现 libc 的 VDSO 前导(cgt_init/__vdsosym)→ libc 的取时实现未被拉入
|
||
strings -a clock_abi_probe_sdk | grep -c __vdso # 期望 0
|
||
llvm-nm clock_abi_probe_sdk | grep -E 'clock_gettime|nanosleep'
|
||
# __time64 / __timedwait_cp 的 jal 应指向本 TU 的 __clock_gettime;睡眠走 SYS_nanosleep(4166)/clock_nanosleep(4265)
|
||
```
|
||
|
||
探针 `tools/clock_abi_probe.c`(宿主 `gcc` 或 mipsel 交叉;加 `-DMEOW_PROBE_WITH_SDK`
|
||
并链 `meow_ui.a` 可同时验收 `meow_ui_time_ms`):把 `struct timespec` 预填 0xAA 后取时,
|
||
若偏移 8..11 仍是 0xAA 即说明 libc 只写了 8 字节(ABI 不一致),退出码 2。
|
||
|
||
真机(塔台批准后):日志首列应连续、出现非整千差;`meow_ui_time_ms()` 两次取样差 = 真实毫秒。
|
||
本次设备已离线,真机确认**待办**。
|
||
|
||
## 5. 影响面
|
||
|
||
- **SDK 内**:`meow_ui_time_ms()` 及其上层 —— 动画 tween(`meow_ui_anim.c`)、滚动/fling
|
||
(`meow_ui_scroll.c`/`meow_select.c`)、IME 冷却(`meow_ui_ime.c`)、状态栏动画
|
||
(`meow_ui_gfx.c`)、通知 TTL(`meow_ui_notify.c`)、输入层 tick/启动冷却
|
||
(`meow_ui_input.c`)、cachegov 回退窗口(`meow_cachegov.c`)、日志首列(`meow_ui_log.c`)。
|
||
- **App 侧**:`on_tick` 入参 `now`、`meow_anim_*`、以及任何自行调用 `meow_ui_time_ms()` 的
|
||
App(hello/reader/files/ncm…)。
|
||
- **产物**:`meow_ui.a` 变更 → 需**重编并重链四 App**(ncm/reader/files/hello);
|
||
`time()`/`gettimeofday()` 的修正也在同一份归档里生效(链接期覆盖)。
|
||
- **同步等待(M16)**:`pthread_cond_timedwait` / `sem_timedwait` / `pthread_join` 超时 /
|
||
`aio_suspend` / 非 PI 的 `pthread_mutex_timedlock`/rwlock timed 的**相对超时按真实剩余
|
||
时间**生效(不再「亚秒即超时」)。SDK 侧直接受益者是 `meow_ui_audio.c` 的 5 处条件等待
|
||
(:312/:351/:417/:431/:499)。
|
||
`timerfd_*`、`clock_settime` 一并修正(当前无调用方)。
|
||
- 真机写入红线未触碰;本单只改 SDK 源与文档/测试。
|
||
|
||
## 6. 同源入口全量清点(M16)
|
||
|
||
M16 把 zig 0.17 musl 源里所有 `#if defined(SYS_*_time64) && !defined(__ILP32__)` 守卫
|
||
逐条编译、反汇编核对(`zig cc --target=mipsel-linux-musleabi`),结论如下。
|
||
「回退分支」= `__ILP32__` 被定义时实际编译进去的代码。
|
||
|
||
### 6.1 受损(回退分支把 time64 结构直传 time32 系统调用)
|
||
|
||
| musl 源(守卫行) | 入口 | 回退分支实际行为 | 状态 |
|
||
| --- | --- | --- | --- |
|
||
| `thread/__timedwait.c:14` | futex 相对超时 → `pthread_cond_timedwait` / `sem_timedwait` / `pthread_join` 超时 / `aio_suspend` / 非 PI `pthread_mutex_timedlock` / rwlock timed | `__syscall_cp(SYS_futex=4238, …, to)`,`to` 是 16B time64 结构;内核只读前 8B:`tv_nsec` 落在 `tv_sec` 高半字(恒 0)→ 亚秒等待变 0(立即 ETIMEDOUT)、整秒等待丢纳秒 | **M16 已接管** |
|
||
| `linux/timerfd.c:14` | `timerfd_settime` | 32B itimerspec 直传 `SYS_timerfd_settime32=4323`;内核按 16B 读 → `value.tv_sec` 读到调用者的 `interval.tv_nsec`,`value.tv_nsec` 读到填充字节 | **M16 已接管** |
|
||
| `linux/timerfd.c:42` | `timerfd_gettime` | 4322 只写 16B,调用者按 32B 解 → 字段串位 | **M16 已接管** |
|
||
| `time/clock_settime.c:9` | `clock_settime` | 16B 直传 `SYS_clock_settime32=4262` → nsec 恒 0 | **M16 已接管** |
|
||
| `signal/sigtimedwait.c:10` | `sigtimedwait` | 16B 直传 `SYS_rt_sigtimedwait=4197` | 未修(SDK/App 未用,§7.3 配方) |
|
||
| `ipc/semtimedop.c:19` | `semtimedop` | 16B 直传 `SYS_ipc=4117`(`IPCOP_semtimedop`) | 未修(未用) |
|
||
| `time/timer_settime.c:13` | `timer_settime` | 32B itimerspec 直传 4258 | 未修(未用) |
|
||
| `time/timer_gettime.c:11` | `timer_gettime` | 4259 只写 16B | 未修(未用) |
|
||
| `stat/utimensat.c:15` | `utimensat`(`futimens` 同源) | `times[2]`(32B)直传 4316 | 未修(仅 ncm 宿主 bench/测试出现) |
|
||
| `network/recvmmsg.c:21` | `recvmmsg` | 16B timeout 直传 4335 | 未修(未用) |
|
||
| `mq/mq_timedsend.c:10` | `mq_timedsend` | 16B 直传 4273 | 未修(未用) |
|
||
| `thread/pthread_mutex_timedlock.c:8` | PI 互斥量的 timedlock 快路径 | 16B 直传 4238(`FUTEX_LOCK_PI`) | 未修(非 PI 走已修的 `__timedwait`;SDK/App 不用 PI 互斥量) |
|
||
|
||
唯一在用的受害者是 futex:`meow_ui_audio.c`(5 处,:312/:351/:417/:431/:499)、
|
||
`meow_audiod`、ncm 播放线程都用
|
||
`pthread_cond_timedwait`,修复前「剩 0.4s」会被内核当成 `{0,0}` 立即超时 → 音频环形缓冲
|
||
的等待退化成忙轮询。
|
||
|
||
### 6.2 不受影响(回退分支自己做了宽度转换)——纠正 M11 §6 的猜测
|
||
|
||
| musl 源 | 证据 |
|
||
| --- | --- |
|
||
| `select/pselect.c:15` | 回退分支 `syscall_cp(SYS_pselect6=4301, …, (long[]){s, ns}, …)`:o32 的 `long`=4B → 正好 8B time32 |
|
||
| `select/ppoll.c:14` | 同上(4302)。反汇编 `__ppoll_time64`:`lw 0x0/0x8` 读 16B 结构 → `sw` 成栈上两字 → `addiu $4,$zero,0x10ce`(4302) |
|
||
| `select/select.c:28` | mips 无 `SYS_select`,走 `(long[]){s,ns}` + 4301;同 pselect |
|
||
| `linux/clock_adjtime.c:40` | 回退分支保留 `if (sizeof(time_t) > sizeof(long))`,构造 long 版 `struct ktimex` |
|
||
| `misc/getrusage.c:9` | 同上宽度分支(`dest = &ru_maxrss - 4*sizeof(long)` + 回填) |
|
||
| `linux/wait4.c:11` | 同上宽度分支 |
|
||
|
||
M11 §6 把 pselect/ppoll 列为「未验证」是保守估计;本次逐条反汇编确认它们**本来就是对的**,
|
||
不需要接管(也就没有可修的低风险项)。
|
||
|
||
## 7. 接管机制与未修项配方(M16)
|
||
|
||
### 7.1 符号名不是 API 名:`_REDIR_TIME64` 把调用点改了名
|
||
|
||
zig 的 `alltypes.h` 无条件 `_REDIR_TIME64=1`,头文件里每个受影响 API 都有一行
|
||
`__REDIR(x, __x_time64)`(`generic-musl/*.h`)。应用里写 `timerfd_settime(...)`,编译出来
|
||
引用的是 `__timerfd_settime64`。所以接管必须定义**重定向后的符号**:
|
||
|
||
| API | 应用引用的符号 | libc 该 `.o` 还定义(必须一并接管,否则重复定义) |
|
||
| --- | --- | --- |
|
||
| `timerfd_settime/gettime` | `__timerfd_settime64` / `__timerfd_gettime64` | `timerfd_create` |
|
||
| `clock_settime` | `__clock_settime64` | (无) |
|
||
| 内部 `__timedwait_cp` | `__timedwait_cp` | `__timedwait`、`__eintr_valid_flag`(弱) |
|
||
|
||
只定义 `__timedwait_cp` 是**不行**的:libc 的 `__timedwait.o` 会因 `__timedwait` 未解析被
|
||
拉入,与本文的重定义撞车。实测:
|
||
|
||
```
|
||
ld.lld: error: duplicate symbol: __timedwait_cp
|
||
>>> defined at ovr2.c
|
||
>>> defined at __timedwait.c:34 (.../musl/src/thread/__timedwait.c:34)
|
||
```
|
||
|
||
### 7.2 为什么链接期接管有效
|
||
|
||
- 「hidden」不阻止静态链接接管:lld 按归档顺序解析,`meow_ui_time.o` 先于 `libc.a` 参与,
|
||
libc 内部对 `__timedwait_cp` 的引用就绑到本文(实测 `jal 0x…<__timedwait_cp>`)。
|
||
- 前提是 `meow_ui_time.o` 被拉入:SDK/App 都引用 `meow_ui_time_ms()`,且 `clock_gettime`
|
||
被重定向到 `__clock_gettime64`(本文也定义),所以它总会被拉入。**若将来把本 TU 的
|
||
接管符号拆到没人引用的新 TU,接管会静默失效。**
|
||
- `__eintr_valid_flag` 给弱定义,libc `signal/sigaction.c` 的强定义优先(同时链 sigaction
|
||
实测不冲突)。
|
||
- 取消点语义不重写:`__timedwait_cp` 仍调 libc 的 `__syscall_cp`,`ECANCELED`/`EINTR`
|
||
处理逐行照抄 musl。
|
||
- 结构宽度前提:本 TU 与被接管入口的调用者必须用同一套 zig 头(`time_t` 8B、
|
||
`struct timespec` 16B)。当前四 App 与 launcher 都用 `zig-mipsel-cc` 编译
|
||
(`$SYSROOT/lib` 只有 `libz.a`,libc 来自 zig 内置 musl),前提成立;若将来换链,
|
||
必须整链替换、不能混用。
|
||
|
||
### 7.3 未修项的配方(同一模式,约 10 行/项)
|
||
|
||
1. 在 `meow_ui_time.c` 的 mips 分支里定义 `__<api>_time64`(或该 API 的重定向名);
|
||
2. 入参展成 `meow_time64_spec` / `meow_time64_itimerspec`,调
|
||
`meow_time32_rel_from_parts()`(超时类,饱和)或 `meow_time32_abs_from_parts()`
|
||
(绝对时刻类,越界回 `ENOTSUP`);
|
||
3. 发 `SYS_<api>_time32`(或 `SYS_<api>` 的 time32 号),输出再反向展开;
|
||
4. 同一 `.o` 里 libc 定义的所有符号一起接管(见 7.1 表),否则重复定义;
|
||
5. 宿主单测补边界,交叉反汇编确认结构宽度与 syscall 号。
|
||
|
||
不建议顺手全改:这些 API 当前零调用(全仓 `grep` 只有 ncm 的宿主 bench/测试用 `utimensat`),
|
||
每多接管一个就多一份「转换写错 → 静默写坏文件时间/信号等待」的面;优先级应按「谁先被 App
|
||
用」来定。根治仍是上游:换容器旧链(gcc 不定义 `__ILP32__`)编 SDK,或修补 zig 内置 musl
|
||
的 `!__ILP32__` 守卫(本意只排除 x32)。
|
||
|
||
## 8. M16 验证
|
||
|
||
宿主(aarch64 Termux,gcc):
|
||
|
||
```
|
||
cd patch_v2/sdk
|
||
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o /tmp/m.o # 0 warning
|
||
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time64.c /tmp/m.o -o /tmp/t && /tmp/t
|
||
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c /tmp/m.o -o /tmp/t0 && /tmp/t0
|
||
```
|
||
|
||
- `test_meow_time64`(新,23 项):相对/绝对换算边界(INT32_MAX±1、INT64_MAX、负值)、
|
||
itimerspec 四字段不串位与越界原子性,以及「同一块 16B 内存按内核 o32 只读 8B」的
|
||
before/after 模拟(0.4s → `{0,0}` 立即超时;2.3s → 2s 整)。
|
||
- `test_meow_time`(M11 回归):换算边界 + 单调前进,仍 PASS。
|
||
|
||
交叉:
|
||
|
||
```
|
||
bash patch_v2/sdk/build_sdk_host.sh --verify # 76 成员、全 MIPS32r2、0 warning
|
||
```
|
||
|
||
反汇编(本单新增/改写的入口):
|
||
|
||
| 符号 | syscall 号 | 缓冲宽度证据 |
|
||
| --- | --- | --- |
|
||
| `__timedwait_cp` | `0x108e` = 4238 `SYS_futex` | 超时在栈上 8B(`addiu $18,$sp,0x20` + `sw $18,0x10($sp)`),不再直传 16B 结构 |
|
||
| `__timerfd_settime64` | `0x10e3` = 4323 `timerfd_settime32` | 从调用者 32B 结构读 `0x0/0x4/0x8/0x10/0x14/0x18`,打包成栈上 16B |
|
||
| `__timerfd_gettime64` | `0x10e2` = 4322 | 内核只写栈上 16B,再按字段展开到调用者 32B |
|
||
| `__clock_settime64` | `0x10a6` = 4262 `clock_settime32` | 8B `{int32 sec, int32 nsec}` |
|
||
|
||
链接期(真实 `meow_ui.a` + 探针,顺序同 App:`probe.o meow_ui.a … -lc`):
|
||
|
||
```
|
||
llvm-objdump -d probe_sdk | sed -n '/<__pthread_cond_timedwait_time64>:/,/^$/p' | grep jal.__timedwait
|
||
# cd930: jal 0x419e8 <__timedwait_cp> ← 绑到 meow_ui.a 里的实现
|
||
llvm-nm probe_sdk | grep -c __timedwait_cp # 1(libc 那份未被拉入)
|
||
```
|
||
|
||
真机验证仍待设备(本单未碰真机):`pthread_cond_timedwait` 传 100ms 超时的等待应真的等
|
||
约 100ms 再超时,而不是立即返回。
|