FEX的dispatcher位置相关编码bug:JIT池低于0x119000000为何致命
【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira
Madeira 是一个在越狱 iOS 设备上通过 FEX-Emu + Wine + DXMT 运行 x86-64 Windows PC 游戏的开源项目。这篇文章带你弄清它日志里反复出现的 "0x119000000" 到底意味着什么:FEX 的 dispatcher(调度入口)存在一个位置相关编码 bug,当 JIT 池的起始地址低于 0x119000000 时,游戏会在第一个编译块运行之前就跳进零内存区域而崩溃——这是新手排查"启动即闪退"时最需要理解的一条底层约束。
dispatcher是什么:JIT池里的"总调度台"
💡 先建立直觉,再谈 bug。
- JIT 池:FEX 把 x86-64 代码块翻译成 ARM64 机器码,写到一块同时可读可执行(RX)的大内存区,这就是 JIT 池(通常 500MB 以上)。
- dispatcher:池内一个固定偏移(+0x7FFC130)处的"调度入口"。guest 程序每切换一次函数、每完成一次系统调用,都要先跳回它中转,再分发到真正的编译块。可以把它理解成总调度台。
- 关键点:dispatcher 的地址 = JIT 池基址 + 固定偏移,它自身没有固定地址,完全取决于池落在哪。而 bug 恰恰出在"它落在哪"上。
为什么低于 0x119000000 是致命的
FEX 在生成 dispatcher 时,会把自身地址的顶部字节隐含地编进若干字面量(literal pool)修复中。实测发现:
| JIT 池基址 | dispatcher 地址顶部字节 | 结果 |
|---|---|---|
| 0x114000000 ~ 0x117xxxxxx | 0x11 | 字面量修复静默损坏,mode A 失败 |
| ≥ 0x119000000(如 0x119000000 → dispatcher 0x120FC130) | 0x12 | 一切正常(mode B) |
也就是说 0x119000000 这个"魔法数字"的由来是:池基址 + 0x7FFC130 之后,dispatcher 地址的顶部字节恰好进入 0x12 区间。低于阈值时,编码出来的跳转目标变成零内存,执行流在 block 0 还没跑起来之前就"静默"分支到地址 0——没有任何报错,只是整个会话直接死掉。这正是注释里写的 "mode A"(低地址失败路径);高于阈值时走 "mode B",由 Wine 侧运行时修补兜底,逻辑见 build/ntdll-unix/signal_arm64_ios.c 中init_syscall_frame的 PC 重定向。
📌 一句话记住:池放低了,dispatcher 的第一次分发就会跳进 0x0,且不会告诉你为什么。
Madeira如何把地址"垫高":三道防线
bug 出在 FEX 内部,修复思路只能在给池选地址这一层做文章。app/Madeira/StikJITHelper.swift 实现了三道防线:
- 预占低地址空间(pin 循环):在真正申请池之前,先反复
vm_allocate(每次 16MB,VM_FLAGS_ANYWHERE),把系统的"分配前沿"一路推到 0x119000000 之上(最多 32 块),这样调试器随后申请的 RX 池只能落在高位。注意它不是固定垫 96MB,而是动态垫到前沿越过阈值为止——固定数量在 ASLR 低滑移时会垫不够(真实事故:6 块只到 0x118790000,差 8.4MB,启动即快速失败)。 - 落地后逐条校验,不合格就"重抽":池地址必须同时满足三条(L404-L413):
- ≥ 0x119000000(低于则记
mode A low); - 不在 guest 64GB 窗口 [0x7000000000, 0x8000000000) 内;
- 不覆盖 0x140000000 的可执行窗口。 不合格就释放并重试(最多 3 次),仍失败则报
BAD POOL并延迟退出让你重启。
- ≥ 0x119000000(低于则记
- 空洞普查(hole census):申请前先扫描 [0x119000000, 0x7000000000) 的真实空闲空洞,放不下就主动缩小池,宁要能启动的小池,也不要落进 guest 窗口的大池(后者是另一种致命摆放)。
遇到闪退时,用户应该看什么
如果你玩着遇到"启动即黑屏/只有壁纸/秒退",按顺序检查:
- 日志里搜
BAD POOL placement——出现(mode A low)说明池落低了,退出重开一次(ASLR 每次给的地址不同,多数情况重开即好); - 搜
JIT-pool pin chunk——这些行记录了垫高过程是否顺利; - 确认 JIT 是通过 Madeira 应用内的 "Enable JIT" 按钮开启的:只有 StikDebug 带着 Madeira 的脚本挂上,池申请才有应答,否则会提示 "StikDebug is not attached" 而不是崩溃。
相关资料导航
- 核心实现与完整注释:app/Madeira/StikJITHelper.swift
- 池基址门槛在故障分派侧的体现:build/ntdll-unix/signal_arm64_ios.c
- mode B 的运行时修补点:build/ntdll-unix/signal_arm64_ios.c
- 项目总览:README.md
【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考