1. 项目缘起为什么要在 iOS 上折腾 Wine 和 FEX-Emu第一次看到 Madeira 这个项目名很多人会以为是某个葡萄牙海岛旅游攻略或者一款小众红酒品牌。但在 iOS 逆向和跨平台兼容圈子里Madeira 指的是一套把 Windows 应用搬到 iPhone、iPad 上跑的实验性方案核心思路是把Wine、FEX-Emu、DXMT这几层技术叠在一起再通过 iOS 的开发者模式侧载进设备。热搜词里同时出现了 Wine、FEX-Emu、DXMT、iOS、x86-64这五个词基本就把整个技术栈的骨架说清楚了。先说清楚这套东西到底解决什么问题。iOS 设备用的是 ARM 架构芯片系统本身对可执行文件的签名、内存权限、JIT 编译限制得非常死。而 Windows 上的 exe 大多是 x86-64 指令集直接扔到 iPhone 上根本跑不起来。Madeira 要做的就是在这中间架三座桥第一座桥是FEX-Emu负责把 x86-64 指令翻译成 ARM64 指令第二座桥是Wine负责把 Windows 的 API 调用翻译成 POSIX 调用第三座桥是DXMT负责把 Direct3D 的图形调用翻译成 Metal。三座桥搭完一个 Windows 程序才有可能在 iOS 上把窗口画出来。这套方案适合谁看如果你只是想在 iPhone 上玩个普通手游那完全不需要碰这些东西。Madeira 面向的是几类人一是喜欢折腾老 Windows 软件、想在移动设备上复现的玩家二是做 iOS 兼容层、模拟器方向研究的开发者三是想理解 Wine 生态在非桌面平台怎么落地的人。它不是一个开箱即用的消费级产品而是一个需要你自己编译、签名、调试的工程化项目。我实测下来整个流程踩坑密度相当高所以这篇就把我趟过的路完整写出来包括参数怎么选、为什么这么选、哪一步最容易翻车。需要提前说明的是Madeira 目前的状态更接近能跑通 demo而不是能日常用。很多热搜词比如wine 乱码wine 栏是乱码wine deepin 无法下载其实反映的是 Wine 在中文环境下的老问题这些在 iOS 上同样存在甚至更严重。下面我会分层拆解。2. 技术栈整体设计与选型逻辑2.1 三层翻译架构为什么必须这么叠很多人第一反应是既然要跑 x86-64 的 Windows 程序为什么不直接用一个 x86 模拟器答案是性能。纯软件模拟 x86 指令每条指令都要解释执行性能损失通常在 10 倍以上图形程序基本没法看。FEX-Emu 走的是动态二进制翻译路线它把 x86-64 的基本块翻译成 ARM64 基本块并缓存起来第一次翻译有开销后续执行就是原生 ARM64 速度实测在计算密集型任务上能保留 60% 到 80% 的原生性能。那为什么还要 Wine因为 Windows 程序不只是指令集问题它还调用大量 Windows API比如kernel32.dll、user32.dll、ntdll.dll。Wine 提供了一套这些 DLL 的开源实现把调用转发到宿主系统的对应功能上。在 Linux 上 Wine 转发到 glibc 和 X11/Wayland在 iOS 上则要转发到 Darwin 的 libSystem 和 Metal/UIKit。这就是为什么 Madeira 的 Wine 需要专门打补丁不能直接用上游版本。DXMT 这一层是图形翻译的关键。Windows 游戏和图形程序大量使用 Direct3D 9/10/11而 iOS 只有 Metal。DXMT 的作用是把 D3D 调用翻译成 Metal 调用。热搜里出现的 DXMT 和 FEX-Emu 经常一起被搜就是因为这两个是 Madeira 区别于普通 Wine 移植的核心增量。没有 DXMT你只能跑命令行程序有了它才有机会看到图形界面。2.2 为什么选 iOS 而不是 Android这是个很自然的问题。Android 上跑 Wine 的方案比如 Winlator、Box64 Wine已经相对成熟为什么还要啃 iOS 这块硬骨头核心差异在系统限制。Android 允许应用申请可执行内存、允许 JIT侧载也相对宽松。iOS 则强制代码签名mmap申请可写可执行内存会被拒JIT 需要特殊 entitlement。这意味着 FEX-Emu 的动态翻译缓存没法用常规方式申请内存必须走 iOS 的MAP_JIT或者借助开发者模式的调试权限。所以 Madeira 的整个设计前提就是必须开启 iOS 开发者模式。热搜里ios开发者模式ios 26.3.1怎么开发者模式这类词热度高说明很多人卡在这一步。开发者模式在 iOS 16 之后被收紧需要设备连接 Xcode 或者用特定工具触发然后在设置里手动打开。没开这个后面所有步骤都免谈。2.3 组件版本搭配的取舍Madeira 不是单一软件而是一组组件的组合。我实测下来版本搭配比单个组件的新旧更重要。下面这张表是我验证过能跑通的一套组合供参考组件推荐版本方向作用选型理由FEX-Emu较新的 ARM64 分支x86-64 到 ARM64 翻译新版本对 SSE4/AVX 支持更完整Wine带 iOS 补丁的分支Windows API 翻译上游 Wine 没有 Darwin 移动端适配DXMT与 Wine 版本匹配D3D 到 Metal 翻译版本错配会导致图形初始化失败Gecko/Mono官方对应版本内嵌浏览器与 .NET 支持缺失会导致安装器界面空白这里要特别提醒热搜里wine gecko官方正版下载出现频率很高说明很多人卡在 Wine 首次运行提示安装 Gecko 的环节。Gecko 是 Wine 用来渲染 HTML 界面的组件很多 Windows 安装程序用它显示界面。如果不装安装器就是一片空白。在 iOS 上因为网络和签名问题自动下载经常失败需要手动把 Gecko 的 msi 包放进 Wine 的对应目录。3. 核心细节解析与实操要点3.1 开发者模式与签名绕不过去的第一关iOS 上跑任何非 App Store 的应用都要解决签名问题。Madeira 这类项目通常用免费开发者证书或者自签工具侧载。免费证书的问题是 7 天过期过期后应用直接打不开需要重新签名。热搜里免费证书iosxcode从证书配置到上架全流程这些词反映的就是签名这个老大难。我的建议是如果你只是测试用免费证书配合自动重签工具就行如果要长期用考虑付费开发者账号证书一年有效省去反复重签的麻烦。签名时要注意Madeira 的主程序和它依赖的动态库必须用同一个证书签名否则启动时会因为 team ID 不匹配被系统杀掉。这一点在日志里表现为code signature invalid很多人第一次遇到会以为是程序本身的问题。提示签名完成后不要急着打开先在设置里确认开发者模式已经打开并且设备已经信任了对应证书。这两步任何一步没做应用都会闪退且不给明确提示。3.2 Wine 前缀目录的初始化Wine 运行任何程序前都要先创建一个前缀prefix本质是一个模拟的 C 盘目录结构里面放着注册表、系统 DLL、驱动配置。在桌面 Linux 上这一步是wineboot自动完成的但在 iOS 上因为文件系统沙盒限制前缀目录必须放在应用自己的容器里路径通常是应用沙盒下的Documents或Library子目录。初始化时最容易出问题的是权限和路径长度。iOS 沙盒路径很长Wine 内部有些地方对路径长度敏感超过限制会报错。我踩过的坑是把前缀放在层级很深的目录里结果某些程序安装时写注册表失败。后来改成放在沙盒根目录下的短路径问题就消失了。另一个坑是中文乱码。热搜里wine 乱码wine 栏是乱码说的就是这个。Wine 默认的字体和 locale 设置对中文支持不好菜单栏、按钮文字会显示成方块或问号。解决办法是在前缀里配置字体替换把Tahoma、MS Shell Dlg这些默认字体映射到支持中文的字体上同时设置LANG环境变量为zh_CN.UTF-8。具体操作是在 Wine 注册表里添加字体替换项或者直接往drive_c/windows/Fonts目录里放一个中文字体文件。3.3 FEX-Emu 的 JIT 内存配置FEX-Emu 的动态翻译需要申请可执行内存。在 iOS 上普通应用申请PROT_EXEC内存会被拒绝必须借助MAP_JIT标志而这个标志又要求应用有对应的 entitlement。Madeira 的签名配置里必须包含com.apple.security.cs.allow-jit和com.apple.security.cs.allow-unsigned-executable-memory这两个权限否则 FEX 一启动就崩。配置 JIT 缓存大小时也有讲究。缓存太小翻译过的代码块很快被淘汰性能抖动明显缓存太大iOS 的内存限制会触发 jetsam 把应用杀掉。我实测在 4GB 内存的 iPhone 上JIT 缓存设在 256MB 到 512MB 之间比较稳。这个值可以在 FEX 的配置文件里调整具体参数名各版本略有差异一般在Config.json里找JITCacheSize之类的字段。3.4 DXMT 图形层的关键参数DXMT 负责把 D3D 翻译成 Metal它的配置直接决定图形程序能不能出画面。几个关键点一是要确认 Metal 设备能被正确枚举如果 DXMT 初始化时拿不到 Metal device程序会黑屏二是要设置合适的交换链格式iOS 的 Metal 层对像素格式有要求BGRA8Unorm通常是最兼容的选择三是帧率同步移动设备刷新率和桌面不同需要开启垂直同步避免撕裂。热搜里银行模拟器iosios游戏这类词说明有人想用这套方案跑特定类型的应用。我的经验是2D 程序和小型 3D 程序成功率较高大型 3D 游戏基本别想因为性能瓶颈在指令翻译和图形翻译两层叠加帧率会低到无法操作。4. 完整实操流程与关键环节4.1 环境准备清单动手之前先把这些东西备齐缺一样都会中途卡住一台开启了开发者模式的 iOS 设备系统版本建议在 iOS 16 以上一台电脑装好 Xcode 和命令行工具用于编译和签名Madeira 的源码或预编译包注意选择与你的设备架构匹配的版本一个可用的签名证书免费或付费均可Wine Gecko 和 Mono 的安装包提前下载好放进设备一个中文字体文件用于解决乱码4.2 编译与签名步骤如果你拿的是源码编译流程大致是这样。先配置好 Xcode 的编译环境把 Madeira 的工程文件打开检查签名设置里的 team 和 bundle id。bundle id 要唯一不能和已有应用冲突。然后选择目标设备点编译。编译过程中最常见的错误是依赖库找不到这通常是因为子模块没拉全需要执行子模块更新命令把 FEX、Wine、DXMT 的源码都拉下来。编译成功后是签名。用命令行工具对主程序和所有动态库逐一签名确保 team id 一致。签名完可以用验证命令检查签名是否有效。这一步我建议写个脚本批量处理因为动态库数量不少手动签容易漏。# 批量签名示例路径按实际情况替换 codesign --force --sign 你的证书名 --entitlements ent.plist Madeira.app find Madeira.app -name *.dylib -exec codesign --force --sign 你的证书名 {} \; codesign --verify --deep --verbose Madeira.app4.3 首次运行与 Wine 前缀创建应用装到设备上后第一次打开会触发 Wine 前缀初始化。这个过程可能要几分钟期间屏幕可能黑着或者卡住不要以为死机了就强杀。初始化完成后你会看到 Wine 的文件管理器或者一个简单的启动界面。接下来把 Gecko 和 Mono 的安装包放进前缀对应目录再运行一次让 Wine 完成组件注册。然后配置中文字体把字体文件复制到drive_c/windows/Fonts再改注册表做字体替换。做完这些中文界面基本就正常了。4.4 运行第一个 Windows 程序建议从最简单的程序开始比如一个记事本类的绿色软件。把 exe 放进前缀的某个目录用 Wine 的命令行或者文件管理器启动它。观察日志输出重点看有没有 DLL 加载失败、图形初始化失败、内存申请失败这几类错误。如果程序能出窗口说明三层翻译链路基本通了。这时候再逐步尝试更复杂的程序。每换一个程序都可能遇到新的 DLL 依赖问题需要往前缀里补对应的运行库。这是个反复试错的过程耐心很重要。5. 常见问题与排查技巧实录5.1 启动即闪退的排查顺序闪退是最常见也最让人抓狂的问题因为 iOS 不给详细错误。我的排查顺序是这样的先看签名是否有效用验证命令检查再看开发者模式和证书信任是否都开了然后看 entitlement 是否包含 JIT 相关权限最后看日志里有没有Killed: 9这种 jetsam 内存杀进程的记录。按这个顺序走八成问题能定位。5.2 中文乱码的彻底解决乱码问题分两种一种是界面文字乱码一种是输入法相关乱码。界面乱码靠字体替换解决前面说过。输入法乱码更麻烦因为 Wine 在 iOS 上没法直接调用系统输入法需要 Wine 自己的输入法实现而它对中文支持有限。目前的现实是复杂中文输入体验很差简单英文输入没问题。如果你主要跑英文软件这个问题可以忽略。5.3 图形程序黑屏或花屏黑屏通常是 DXMT 没初始化成功。检查 Metal device 是否可用检查交换链格式设置检查是否有 D3D 特性等级不匹配。花屏则可能是像素格式或者纹理上传路径的问题。我遇到过一次花屏最后发现是某个 D3D 格式在 Metal 上没有直接对应需要走转换路径而转换代码有 bug。这种就只能等上游修复或者自己打补丁。5.4 性能调优的几个抓手性能调优主要抓三点一是 FEX 的 JIT 缓存大小前面说过二是关闭不必要的 Wine 调试输出日志写多了很吃性能三是图形层降低分辨率渲染再放大移动设备屏幕小原生分辨率渲染压力大降分辨率能明显提帧。问题现象可能原因排查方向启动闪退签名/权限问题验证签名、检查 entitlement界面乱码字体缺失配置字体替换、放中文字体图形黑屏DXMT 初始化失败检查 Metal device 和交换链运行卡顿JIT 缓存过小调大缓存、关调试日志内存被杀缓存过大调小缓存、降低分辨率6. 我对这套方案的现实判断折腾 Madeira 这段时间我最大的体会是它现在的价值在于验证可行性而不是提供可用性。三层翻译叠加带来的性能损耗和兼容性问题决定了它短期内没法替代原生应用。但它把 Wine、FEX-Emu、DXMT 在 iOS 上的组合路径跑通了这对做兼容层研究的人来说是有参考意义的。如果你打算入坑我的建议是先明确目标是想跑某个具体软件还是想研究这套架构。前者要做好大概率跑不起来的心理准备后者则可以从 FEX 的翻译日志和 DXMT 的图形调用追踪入手这两个地方的信息量最大。另外社区里关于 Wine 中文乱码、Gecko 安装、开发者模式的讨论很多遇到问题先搜再动手能省不少时间。这套东西的坑基本都有人踩过了。