真机 meow_ui_time_ms() 返回「低 32=秒×1000、高 32 随机」的根因不是 SDK 写法,
而是 zig 0.17 自带 musl 在 mipsel 上的 time64 ABI 自相矛盾:
- zig 头(mips-linux-musl/bits/alltypes.h)无条件 64 位 time_t、struct timespec 16 字节;
- zig 的 clang 给 32 位 mips 预定义 __ILP32__,musl 的 time64 回退分支
`#if defined(SYS_x_time64) && !defined(__ILP32__)`(本意只排除 x32)被编译掉;
- 于是 libc 的 __clock_gettime64 实为 o32 time32 实现(SYS_clock_gettime 4263),
只写 8 字节 {int32 sec; int32 nsec},与头的 16 字节布局错配。
修复(patch_v2/sdk/meow_ui_time.c):
- meow_ui_time_ms() 直接按内核 o32 ABI 取 8 字节 time32,再升 64 位毫秒;
- 同 TU 强定义 __clock_gettime/__clock_gettime64(归档先于 -lc,链接期接管,
time()/gettimeofday()/pthread 定时等待的内部取时随之修正);
- 同源缺陷一并修:__nanosleep_time64/__clock_nanosleep(_time64) 转换 time32 后重发
(修复前 usleep(16000) 等亚秒睡眠立即返回、变忙等空转);
- gfx/sys/input/cachegov 四处重复 now_ms 收敛到 meow_ui_time_ms;
- build_sdk.sh OBJS 增加 meow_ui_time;新增 test_meow_time 宿主单测、
tools/clock_abi_probe.c 探针、CLOCK_ABI.md 根因/验证/影响面文档;
- 26 个宿主测试的编译注释补 meow_ui_time.c(cachegov 测试补时钟源)。
验证:宿主单测 PASS;anim(32)/view_event 宿主用例全过;build_sdk_host.sh --verify
76 成员全 MIPS32r2、0 warning;探针链接后无 libc cgt_init/__vdsosym,
__time64/__timedwait_cp 的取时指向本 TU,睡眠走 4166/4265 且用本地 8 字节结构。
148 lines
5.9 KiB
C
148 lines
5.9 KiB
C
/* meow_ui_time.c — SDK 单调时钟(meow_ui_time_ms)+ mipsel o32 时间 ABI 兜底。
|
||
*
|
||
* 根因(M11;真机日志 + 反汇编证据链见 sdk/CLOCK_ABI.md):
|
||
* zig 0.17 的 clang 前端给 32 位 mips 目标预定义了 __ILP32__;zig 自带 musl 的
|
||
* time64 回退分支统一写作 `#if defined(SYS_x_time64) && !defined(__ILP32__)`
|
||
* (该 `!__ILP32__` 本意只排除 x32),于是 mipsel 的 libc 把「time64 入口」编译成了
|
||
* 老 32 位实现:__clock_gettime64 直接做 SYS_clock_gettime(4263),只写 8 字节
|
||
* `{ int32 tv_sec; int32 tv_nsec; }`。而 zig 头文件又无条件把 time_t 声明成 64 位
|
||
* (struct timespec 16 字节、tv_sec 8 字节),两边 ABI 不一致:
|
||
* tv_sec 低字 = 真 sec,高字 = 真 nsec → 64 位结果高 32 位随机、非单调;
|
||
* tv_nsec(结构偏移 8)根本没被写 → 毫秒位恒 0(真机实测全是 1000 的倍数)。
|
||
* 容器旧链(gcc)不定义 __ILP32__,time64 回退正确——对照 patch_v2/ncm_app/ncm:
|
||
* 同一函数里先 SYS_clock_gettime64(4403)、ENOSYS 后走 4263 并做宽度转换。
|
||
*
|
||
* 修复:不依赖 libc 头声明的 struct timespec 宽度,直接按**内核 o32 ABI**
|
||
* (8 字节 time32:int32 sec + int32 nsec)取时,再自行升成 64 位毫秒;
|
||
* 同时在本 TU 给出 __clock_gettime / __clock_gettime64 的强定义。应用链接顺序是
|
||
* `... meow_ui.a ... -lc`,归档先参与解析,因此链接期整体替换掉 libc 里那份
|
||
* ABI 不一致的实现:time()、gettimeofday()、pthread 定时等待的内部取时
|
||
* (musl 的 time()/__timedwait_cp 都调 __clock_gettime)随之修正。
|
||
* 上层时间位宽仍是 64 位,只有内核取数按 o32 time32;2038 前有效。
|
||
*
|
||
* 同一句 `!__ILP32__` 还打穿了睡眠:__nanosleep_time64/__clock_nanosleep 走 `#else`
|
||
* 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||
* `usleep(16000)` 立即返回(SDK 里帧节拍/轮询会变忙等空转)。这里一并按 time32
|
||
* 转换后重发,恢复亚秒睡眠。
|
||
*
|
||
* 残余(未修):futex 相对超时(pthread 定时等待的剩余时间按 8 字节读)、timerfd_settime、
|
||
* clock_settime/adjtime、pselect/ppoll 超时——同为该守卫的受害者,见 CLOCK_ABI.md §6。
|
||
*/
|
||
#include <errno.h>
|
||
#include <stdint.h>
|
||
#include <time.h>
|
||
|
||
#include "meow_ui.h"
|
||
|
||
#if defined(__mips__) && !defined(__mips64) && (__SIZEOF_LONG__ == 4)
|
||
#define MEOW_TIME32_KERNEL_ABI 1
|
||
#include <sys/syscall.h>
|
||
#include <unistd.h>
|
||
#else
|
||
#define MEOW_TIME32_KERNEL_ABI 0
|
||
#endif
|
||
|
||
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec)
|
||
{
|
||
return (uint64_t)sec * 1000u + (uint64_t)nsec / 1000000u;
|
||
}
|
||
|
||
uint64_t meow_ui_time_ms(void)
|
||
{
|
||
#if MEOW_TIME32_KERNEL_ABI
|
||
struct meow_time32 { int32_t tv_sec; int32_t tv_nsec; } t;
|
||
t.tv_sec = 0;
|
||
t.tv_nsec = 0;
|
||
if (syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &t) != 0) return 0;
|
||
return meow_time_ms_from_parts(t.tv_sec, t.tv_nsec);
|
||
#else
|
||
struct timespec ts;
|
||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0;
|
||
return meow_time_ms_from_parts((int64_t)ts.tv_sec, (int64_t)ts.tv_nsec);
|
||
#endif
|
||
}
|
||
|
||
#if MEOW_TIME32_KERNEL_ABI
|
||
/* 内核 o32 time32 布局(与 libc 头的 time_t 宽度无关)。 */
|
||
struct meow_time32 {
|
||
int32_t tv_sec;
|
||
int32_t tv_nsec;
|
||
};
|
||
|
||
static int32_t time32_clamp(int64_t v)
|
||
{
|
||
if (v > INT32_MAX) return INT32_MAX;
|
||
if (v < 0) return 0;
|
||
return (int32_t)v;
|
||
}
|
||
|
||
/* ---- libc 取时入口的强定义 ----
|
||
* 返回约定与 musl 一致:0 成功 / -1 失败(errno)。
|
||
* 说明:<time.h> 在 _REDIR_TIME64 下把标识符 clock_gettime 的引用改名为
|
||
* __clock_gettime64,故本 TU 不定义 clock_gettime 这个名字;musl 里它只是
|
||
* __clock_gettime 的 weak alias,同一份语义已由下面的强定义接管。 */
|
||
int __clock_gettime(clockid_t clk, struct timespec *ts);
|
||
int __clock_gettime64(clockid_t clk, struct timespec *ts);
|
||
|
||
int __clock_gettime(clockid_t clk, struct timespec *ts)
|
||
{
|
||
struct meow_time32 t;
|
||
if (!ts) { errno = EFAULT; return -1; }
|
||
t.tv_sec = 0;
|
||
t.tv_nsec = 0;
|
||
if (syscall(SYS_clock_gettime32, clk, &t) != 0) return -1;
|
||
ts->tv_sec = t.tv_sec;
|
||
ts->tv_nsec = t.tv_nsec;
|
||
return 0;
|
||
}
|
||
|
||
int __clock_gettime64(clockid_t clk, struct timespec *ts)
|
||
{
|
||
return __clock_gettime(clk, ts);
|
||
}
|
||
|
||
/* ---- 睡眠入口(同源缺陷:亚秒 usleep 变空转)----
|
||
* __clock_nanosleep 按 musl 内部约定返回 0 或正 errno;__nanosleep_time64 是
|
||
* nanosleep/usleep 的重定向目标,按公共约定返回 0/-1(errno)。 */
|
||
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||
int __nanosleep_time64(const struct timespec *req, struct timespec *rem);
|
||
|
||
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||
{
|
||
struct meow_time32 rq, rm;
|
||
long r;
|
||
|
||
if (clk == CLOCK_THREAD_CPUTIME_ID) return EINVAL;
|
||
if (!req) return EFAULT;
|
||
if (req->tv_nsec < 0 || req->tv_nsec >= 1000000000L) return EINVAL;
|
||
rq.tv_sec = time32_clamp(req->tv_sec);
|
||
rq.tv_nsec = (int32_t)req->tv_nsec;
|
||
rm.tv_sec = 0;
|
||
rm.tv_nsec = 0;
|
||
|
||
if (clk == CLOCK_REALTIME && !flags)
|
||
r = syscall(SYS_nanosleep, &rq, rem ? &rm : 0);
|
||
else
|
||
r = syscall(SYS_clock_nanosleep_time32, clk, flags, &rq, rem ? &rm : 0);
|
||
if (r != 0) return errno;
|
||
if (rem) {
|
||
rem->tv_sec = rm.tv_sec;
|
||
rem->tv_nsec = rm.tv_nsec;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||
{
|
||
return __clock_nanosleep(clk, flags, req, rem);
|
||
}
|
||
|
||
int __nanosleep_time64(const struct timespec *req, struct timespec *rem)
|
||
{
|
||
int r = __clock_nanosleep(CLOCK_REALTIME, 0, req, rem);
|
||
if (r) { errno = r; return -1; }
|
||
return 0;
|
||
}
|
||
#endif
|