☰
在非越狱 iPhone 上运行 x86-64 Windows 游戏:Madeira 的 FEX-Emu + Wine + DXMT 技术栈全解析
2026/9/30 2:05:38 网站建设 项目流程
  • 游戏开发
  • 图形学

【免费下载链接】Madeira

Run x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT

项目地址:https://gitcode.com/GitHub_Trending/mad/Madeira
点击查看免费下载

Madeira 是一个将 x86-64 Windows PC 游戏带到非越狱 iPhone 上的研究型开源项目:它把 Wine 为骨架,结合 docs/BUILDING.md、ARCHITECTURE_ANALYSIS.md、docs/CONTROLLERS.md 以及app/Madeira/下的 Swift/Objective-C 源码与build/下的构建脚本,完整梳理其架构原理、JIT 获取机制、构建步骤、运行配置与许可边界,让你既能理解"为什么能跑",也能按步骤动手复现。

Madeira 是什么:三层技术栈的合一

按照 README.md 的定位,Madeira 解决的核心问题是:在没有越狱、也没有 App Store 分发的 iPhone 上,运行原生的 x86-64 Windows PC 游戏。它没有自研模拟器,而是将三个成熟开源组件组合成一个整体:

组件角色在本项目中的形态
FEX-Emux86-64 → ARM64 动态二进制翻译(JIT)作为libarm64ecfex.dll编译,随包发布为 app/Madeira/arm64ec-windows/xtajit64.dll
WineWindows API → Darwin/POSIX API 翻译ARM64EC PE 模块 + unix 侧静态库(ntdll、wineserver、win32u 等),采用 xtajit 接口接入 FEX
DXMTD3D11 → Metal 图形翻译unix 侧libdxmt_combined.a+ PE 侧winemetal.dll/d3d11.dll

最值得注意的设计决策是进程模型:整个运行时跑在单个 Mach 进程内,wineserver不是独立的守护进程,而是宿主进程里的一个线程。原因在 ARCHITECTURE_ANALYSIS.md 中有明确分析——iOS 不允许fork(),Wine 传统上大量依赖 fork 创建进程,因此 Madeira 采用"应用代码走 FEX 仿真、Wine 的数百个 DLL 全部以 ARM64 原生速度运行"的混合模型。从源码结构看,这一模型通过 Wine 的xtajit接口(对应 Windows 自身的 xtajit.dll)实现:仿真的 DLL 必须导出BTCpuProcessInit(初始化仿真器)、BTCpuSimulate(主仿真循环,永不返回)、BTCpuGetBopCode(获取回调机制),FEX 提供的libwow64fex.dll/libarm64ecfex.dll正是 xtajit 的一种实现。这样只有游戏自身的 x86 代码经过仿真,Wine 的 API 翻译层几乎零仿真开销。

整体架构:从 .exe 到 Metal 的调用链

综合 ARCHITECTURE_ANALYSIS.md 推荐架构(Option A:Direct Port)与仓库实际文件布局,数据流如下:

Windows x86-64 游戏 (.exe) │ ▼ FEX-Emu —— x86-64 → ARM64 JIT 翻译(W^X 合规,双映射内存) │ ▼ Wine —— Windows API → Darwin/POSIX(原生 ARM64,经 xtajit 接入 FEX) │ ▼ DXMT —— D3D11 → Metal(直译,无 Vulkan 中间层) │ ▼ Metal —— Apple GPU(CAMetalLayer 呈现)

几个关键落点可以直接在仓库中印证:

  • ARM64EC PE 模块:app/Madeira/arm64ec-windows/下存放着kernel32.dll、user32.dll、d3d11.dll、winemetal.dll、xtajit64.dll等 PE 文件,以及 Wine 的驱动wineios.drv、winemetal.dll等 iOS 移植产物。
  • 单进程呈现链路:app/Madeira/ContentView.swift中实现的MetalHostView是一个直接挂到UIWindow上的裸 UIView,承载唯一的CAMetalLayer单例(MetalHostView.shared)。注释记录了 2026-07-03 的实测问题:SwiftUI 托管的 layer 在 iOS 26/27 上会走快照/间接路径导致 Metal present 被静默丢弃(presentedTime==0),因此改为窗口级 host view,并沿用 MeloNX 的私有 API 技巧setDisplaySyncEnabled:false让 present 脱离显示同步调度,同时用UIScreen.main.maximumFramesPerSecond设置名义帧率(ml651)。DXMT 的 swapchain 只在启动时注册一次该 layer,之后只有其frame被重新挂载/调整大小。
  • JIT 内存管理:app/Madeira/JITAllocator.h声明了双映射 JIT 区域 API——jit_region_create()创建同一物理内存的两个视图(RW 视图用于写生成代码、RX 视图用于执行),jit_make_region_no_footprint()利用VM_LEDGER_FLAG_NO_FOOTPRINT让 JIT 池不计入 Jetsam 内存占用,以及面向 iOS 26 的 BRK 协议接口jit26_prepare_region()/jit26_detach()。
  • 游戏选择与输入:ContentView.swift同时承载触摸输入层(MetalBackedView,支持多点触控、硬件键盘桥接winios_post_key→WM_KEYDOWN/WM_CHAR)与游戏库浏览逻辑。

运行状态:哪些游戏可玩

README.md 的 Status 章节给出了如实但克制的现状(仓库明确声明这是研究项目而非产品):

  • Thumper 与 ULTRAKILL 可玩(playable)。
  • Marvel Cosmic Invasion 已进入 gameplay 阶段,但一次运行曾以无法解释的方式终止,且其操作尚不可靠。
  • 其他游戏能达到 gameplay,但帧率较低。

README 同时给出明确预期管理:"expect rough edges, per-title quirks and breaking changes"——不同游戏会有各自的怪癖,且项目处于快速演进中,破坏性变更随时可能发生。这决定了在使用前应当关注当前提交与构建说明,而不是把某个游戏的体验当作稳定承诺。

前置要求:越狱、JIT 与签名

运行 Madeira 需要满足三个条件(均来自 README.md 的 Requirements 章节):

  1. 一台非越狱 iPhone。开发机是 A15(iPhone 13 Pro)。注意"非越狱"是硬性前提——项目的卖点恰恰是不需要越狱也能拿到 JIT。
  2. JIT。iOS 上启用 JIT 必须让调试器附加进程(CS_DEBUGGED标志),项目使用 StikDebug(StikJIT 生态)来完成附加。这正是项目无法通过 App Store 分发的根本原因,只能**侧载(sideloading)**安装。
  3. 一个用于签名的 Apple ID。免费账号即可,但其描述文件(provisioning profile)7 天过期,因此应用需要每周重新构建并重装。好消息是应用的容器(container)在重装后保留,Wine prefix 与游戏存档不会丢失。

这条约束决定了日常使用节奏:每周重签一次,Prefix 和存档因容器保留而免于迁移。侧载工具链(如各类 sideloader)与签名流程不在本仓库范围内,README 只声明了"必须侧载"这一事实。

JIT 的获取与内存管理:iOS 26 BRK 协议

JIT 是整个项目的地基。没有 JIT,x86 仿真会退化为纯解释执行,性能下降 10–50 倍(ARCHITECTURE_ANALYSIS.md)。iOS 对可执行内存执行严格的W^X(写与执行互斥)策略,一个页面永远不能同时可写又可执行。Madeira 在仓库中的实现横跨两代机制:

权限声明

app/Madeira/Madeira.entitlements声明了三个关键 entitlement:

<key>com.apple.developer.kernel.increased-memory-limit</key> <true/> <key>com.apple.security.cs.allow-jit</key> <true/> <key>get-task-allow</key> <true/>
  • com.apple.security.cs.allow-jit:允许 JIT 编译;
  • get-task-allow:允许调试器附加(获取CS_DEBUGGED的前提);
  • com.apple.developer.kernel.increased-memory-limit:提高 Jetsam 物理内存阈值,避免大内存占用时被系统杀掉。

StikDebug 附加流程

app/Madeira/StikJITHelper.swift实现了完整的启用流程:

  1. 通过 URL schemestikjit://enable-jit?bundle-id=...&script-data=...唤起 StikDebug,把内嵌的 JIT 脚本(base64 形式,源码另有app/Madeira/madeira-jit.js可独立编辑,开发时可从 bundle 加载覆盖内置脚本)交给它;
  2. StikDebug 对目标进程执行vAttach;<pid>,内核置上CS_DEBUGGED;
  3. App 侧每 0.5 秒轮询jit_check_debugged(),确认后再进入内存分配阶段。

iOS 26 的 BRK 协议

iOS 26+ 引入 TXM(Trusted Execution Monitor)后,简单的"附加-分离"不再有效,调试器必须全程保持附加。仓库的实现改为BRK 协议:App 在需要可执行内存的地方触发BRK #0xf00d(内联 x16 携带命令分发),附着的调试器拦截后通过_M<size>,rx或逐页M命令把页面标记为可执行(JITAllocator.h中的jit26_prepare_region()/jit26_detach())。StikJITHelper.swift内嵌脚本处理了协议细节:区分软信号停止(EXC_SOFT_SIGNAL 原样转发、绝不计数重试、绝不分离)、跳过非 BRK 的原始 fault(作为 unix 信号回灌给 Wine 的 sigaction 处理器)、对EXC_RESOURCE(Jetsam 内存水位,ml359)只记录并继续,以及CMD_PREPARE_REGION/CMD_MAP_PAGE_ZERO/CMD_DETACH三种命令。脚本还实现了故障去重(同 pc 重复 8 次判定为不可投递而杀死 inferior,避免"挂着调试器但线程永久停摆"的假死)。

双映射 JIT 池

JITAllocator.h/JITAllocator.c提供 MeloNX 风格的双映射方案:同一物理内存通过mach_make_memory_entry_64+vm_map建立 RW 与 RX 两个虚拟视图,写入与执行互不干扰,规避pthread_jit_write_protect_np的逐线程切换开销。默认池大小128 MB(StikJITHelper.swift中allocatePool(poolSize: 128 * 1024 * 1024))。源码还记录了两个实战坑:

  • FEX dispatcher 的位置相关编码 bug(ml1036):JIT 池必须落在足够高的地址(经验值 ≥ 0x119000000),否则 dispatcher 的字面量池修复会静默失效、执行分支到零内存。StikJITHelper 通过预占约 96MB 低位地址空间(16MB 一块,进程生命周期内保持存活)把下一次 ANYWHERE 分配"顶"到高位;
  • ARM64EC 加载器依赖 RWX 于镜像页(ml748):jit_wx_probe()对 file-backed 映射上PROT_WRITE是否存活的 A/B 探测,用于区分"VM 比硬件严格"还是"加载器本身的可移植性缺陷"。

StikJITHelper还有 ml1040 的早期地址窗口占位(构造器在镜像加载时先占 0x140000000 窗口,避免运行期碎片化)。

从源码构建 Madeira

构建被拆成多条链路。README 只给了总览:"build/*/build.sh覆盖原生部分,应用用xcodebuild构建",并强调FEX、wine、research/dxmt是指向含 iOS 工作的 fork 的 submodule,上游克隆在这里无法构建。完整可复现步骤记录在 docs/BUILDING.md(2026-09-16 的可复现性记录,commit 8a8cabe):

git clone --recurse-submodules <this repo>

不在仓库中的输入

BUILDING.md 明确指出,仅靠克隆无法构建出可运行的应用,以下输入必须自行准备:

输入说明
toolchains/llvm-mingw-20260421-ucrt-macos-universal/122 MB 第三方 mingw 工具链(用于交叉编译 ARM64EC PE),固定 SHA-256bd85a397...cb3f7,解压到toolchains/下
toolchains/llvm-project/+llvm-ios-build/+llvm-host-build/为 iOS 构建的 LLVM(数小时),配置参数(iOS arm64、-DCMAKE_SYSTEM_NAME=iOS、-DLLVM_DEFAULT_TARGET_TRIPLE=arm64-apple-ios17.0等)已从 CMakeCache 读回记录
research/GPTK/Metal Shader Converter 4.0 beta 2.pkgApple 的 30MB 安装包(licence 受限),SHA-256 钉死在build/madeira-d3d12/deps.sh;仅重建转换器时才需要,库本体已跟踪
app/Madeira/x86_64-vcruntime/Microsoft Visual C++ 2015-2022 x64 运行时 DLL(concrt140、msvcp140*、vcruntime140* 等),须从vc_redist.x64.exe或合法 Windows 安装提取,不随仓库分发,详见 tools/fetch-vcruntime.md
StikDebug + 免费 Apple ID运行时要求(见 README)

原生构建链(按顺序执行)

  1. GNU 加密栈:build/gnutls-ios/build.sh从build/gnutls-ios/src的跟踪 tarball(含 SHA256SUMS)构建 GMP 6.3.0、Nettle 3.10.1、GnuTLS 3.8.9 →app/Madeira/lib{gmp,nettle,hogweed,gnutls}.a(这四个产物同样被跟踪)。
  2. FEX(submodule,分支ios-port-2607):
    • iOS 静态库:build/fex-ios/build.sh用 CMake(-DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 -DBUILD_TESTING=OFF -DBUILD_THUNKS=OFF -DENABLE_FEX_ALLOCATOR=OFF -DENABLE_CLANG_THUNKS=ON)构建FEXCore、FEXCore_Base及 External 下的 cephes/fmt/SoftFloat-3e/xxhash 归档;
    • ARM64EC 模块:build/fex-arm64ec/build.sh通过FEX/Data/CMake/toolchain_mingw.cmake交叉编译目标arm64ecfex,把Bin/libarm64ecfex.dll复制为app/Madeira/arm64ec-windows/xtajit64.dll(关键开关-DENABLE_FEX_ALLOCATOR=ON -DENABLE_JEMALLOC_GLIBC_ALLOC=ON -DENABLE_OFFLINE_RUNTIME=ON)。
  3. Wine(submodule,分支madeira-lgpl):
    • unix 侧:build/ntdll-unix/build.sh、build/wineserver/build.sh、build/win32u-unix/build.sh→app/Madeira/lib{ntdll_unix,wineserver,win32u_unix}.a;
    • PE 侧:build/wine-pe/build-ntdll.sh首次配置wine/build-arm64ec(--enable-archs=arm64ec --without-x --disable-tests),构建dlls/ntdll后strip 并填充到 SizeOfImage + 0x50000再拷入应用;其余 PE 模块make -C dlls/<name>后手动拷贝。
  4. DXMT(submodule,分支ios-port):
    • unix 侧:build/dxmt-ios/build.sh(依赖 LLVM iOS 构建)→app/Madeira/libdxmt_combined.a;
    • PE 侧:meson setup research/dxmt/build-arm64ec research/dxmt -Dbuildtype=release -Dwine_build_path=../../wine/build-arm64ec --cross-file=research/dxmt/build-arm64ec-win.txt,再ninja -C research/dxmt/build-arm64ec src/winemetal/winemetal.dll(及 d3d11.dll)→app/Madeira/arm64ec-windows/。
  5. 原生 D3D12 运行时:build/madeira-d3d12/build-pe.sh→d3d12.dll、madeira_d3d12.dll及测试程序(cube.exe、triangle.exe、texquad.exe、d3d12-cube-x64.exe等,均被跟踪);build/madeira-d3d12/fetch-converter.sh校验 Metal Shader Converter 库,build/stage-licenses.sh刷新随包许可证副本(Xcode 构建在许可证过期时会失败)。
  6. 应用本体:
xcodebuild -project app/Madeira.xcodeproj -scheme Madeira \ -destination 'generic/platform=iOS' -allowProvisioningUpdates build

之后把Payload/Madeira.app打包为 IPA 侧载。BUILDING.md 特别注明:只有 Debug 配置能跑游戏,Release 构建曾导致 guest 崩溃。这是与"正常 iOS 开发"直觉相反、但必须遵守的约束。

运行时配置:Documents/madeira.cfg

运行开关统一收敛到一个文件:Documents/madeira.cfg(实现于 app/Madeira/MadeiraConfig.swift,ml1095)。语法如下:

# 注释以 # 开头 key = value # 空白被裁剪;值延伸到行尾,可包含 '=';最后一行生效 env.NAME = value # 环境变量导出 dxmt = a=b;c=d # DXMT 选项,单行

要点:

  • 键名是旧的按文件命名去掉madeira-前缀与.txt后缀后的名字(swap-mb、vram-mb、pool、totalphys、wx等,完整清单见MadeiraConfig.legacyKeys);
  • 单一事实源:madeira.cfg存在时,旧的一值一文件布局(madeira-<key>.txt)被整体忽略;不存在时旧布局仍可用。首次启动migrateLegacy()会从旧文件一次性写出madeira.cfg(旧文件保留、仅被忽略,日志会列出可删除项);deleteLegacyFiles()幂等地清理旧文件;
  • 原生侧同源解析:C 侧通过build/madeira_cfg.h以相同规则读取同一文件;
  • 布尔值接受1/on/true/yes(MadeiraConfig.bool)。

一个可直接上手的例子——禁用 XInput 输入子系统(见 docs/CONTROLLERS.md):

env.MADEIRA_XINPUT = 0 # 整体关闭物理+触摸手柄生产器与控制器事件声明 env.MADEIRA_TOUCH_XINPUT = 0 # 仅关闭触摸手柄输入

[xinput] ml1920日志标记物理手柄启用与连接事件,[touch-xinput] ml1930标记触摸启用一次。

输入子系统:XInput 快照与触摸合并

docs/CONTROLLERS.md 说明手柄输入通过共享的XInput 快照进入 Windows 游戏:

  • 物理手柄:配对 GameController 设备,最多 4 个稳定的扩展手柄槽位,断开一个不会重排其他槽位;在串行队列上以250 Hz(4 ms,1 ms 调度余量)采样,无物理手柄或应用不活跃时停止定时器;手柄保持连接但上报中性输入。iOS 18 上用handlesGameControllerEvents/GCEventInteraction声明 gamepad 事件,防止焦点导航吞掉手柄输入(GamepadInput.swift的ClaimGamepadEvents)。
  • 触摸手柄:横屏触摸编辑器中的 A/B/X/Y、方向键、LB/RB、L3/R3、Menu/View/Guide、LT/RT、LS/RS 全部映射到 XInput;LT/RT 是全压扳机,LS/RS 是带径向钳制的模拟摇杆(完整有符号 XInput 范围),摇杆位移相对首次触摸位置。触摸输入合并进玩家 1(槽位 0)。
  • 仲裁规则:按钮取并集;扳机取两者较大值;摇杆上,物理摇杆处于标准死区外时优先,否则偏移的触摸摇杆优先,静止触摸不覆盖小的物理偏移(TouchGamepad.swift的GamepadSample.merge实现,死区阈值左摇杆 7849、右摇杆 8689)。
  • 生命周期:隐藏控件、进入编辑、竖屏、移除覆盖层或重映射都会释放输入;退后台释放按住但保留连接身份;UIKit 处理独立手指与取消,两个映射到同一按钮的控件松开其一、另一保持按住。
  • 回滚开关:上文env.MADEIRA_XINPUT = 0/env.MADEIRA_TOUCH_XINPUT = 0。

宿主验证可用(POSIX 主机 + C 编译器 + Swift):

python3 build/host-tests/check-gamepad.py python3 build/host-tests/check-touch-gamepad.py

第一个编译生产快照/查询代码,检查数据包、范围、槽位、非法查询、断连/重连与并发读写;第二个检查独立按住、布局/生命周期清空、模拟量范围、重复摇杆与物理/触摸仲裁。需要时用SWIFTC指定 Swift 路径。这些是逻辑测试,不覆盖 UIKit 手势与设备集成。

许可与合规边界

README.md 的 License 章节非常细致,是使用与二次分发前必读的内容:

  • 项目本体:GPL-3.0-or-later(见 LICENSE),衍生作品若分发必须保持开源。
  • 上游许可证与项目 fork 的差异:Wine、GnuTLS 为 LGPL-2.1-or-later,GMP、Nettle 为 LGPL-3.0-or-later,FEX-Emu、DXMT 为 MIT,rpmalloc 为 0BSD;原文在 LICENSES/。但本项目使用的 fork 并不与其上游同许可证,每个 fork 自带LICENSE-MADEIRA.md:
Fork条款
wine依据 LGPL-2.1 §3 重新授权为GPL-3.0-or-later
FEX、dxmt上游 MIT 保留;修改部分GPL-3.0-or-later
rpmalloc上游 0BSD 保留;Will Faust 的修改GPL-3.0-or-later
  • 该授权不溯及既往:fork 此前已公开,此前以宽松许可获取的内容仍按原许可可用。
  • 逐组件明细见 THIRD-PARTY-NOTICES.md。特别注意MSVC 运行时 DLL 不随仓库分发,须按 tools/fetch-vcruntime.md 自行提供。
  • docs/LICENSING.md 是 LGPL 再链接义务的记录:BUILDING.md 即为"scripts to control compilation and installation",但截至记录日期,端到端的干净机器重建+签名+安装尚未完整执行,因此再链接能力仍标记为 unverified。

上游贡献注意事项

README 还包含一条对贡献者的明确提醒:仓库中的 fork 包含大量AI 辅助生成的代码,而 FEX-Emu 的贡献政策禁止使用 AI 生成代码提交上游,因此不要把本 fork 的 AI 生成改动提交到上游。MIT 许可允许 fork 本身存在,政策约束的是向上游回馈的改动;在向任何上游提 PR 前,应先查阅该上游自己的贡献政策。

总结:一份可复现的研究路线图

Madeira 的价值不在于"开箱即用的游戏模拟器",而在于它为"非越狱 iOS 上跑 x86-64 Windows 游戏"这一此前近乎无解的命题,给出了一个完整、可构建、可复现的参考实现:单 Mach 进程 + 线程化 wineserver 绕开 fork 限制;xtajit 接口让 Wine 原生运行、仅游戏代码过 FEX 仿真;DXMT 直译 D3D11 到 Metal 避免 Vulkan 中间层;BRK 协议与双映射 JIT 池在 iOS 26 的 W^X 与 TXM 约束下拿到可执行内存。配合 docs/BUILDING.md 的逐步构建记录、app/Madeira/MadeiraConfig.swift 的统一配置文件和 docs/CONTROLLERS.md 的输入方案,任何具备 iOS 开发环境与 Apple ID 的开发者都能沿这条路线自行构建、侧载并逐游戏调试。

  • 游戏开发
  • 图形学

【免费下载链接】Madeira

Run x86-64 Windows PC games on jailed iOS via FEX-Emu + Wine + DXMT

项目地址:https://gitcode.com/GitHub_Trending/mad/Madeira
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询