
1. 从“Madeira”这个名字说起一个被低估的跨平台兼容层项目第一次看到“Madeira”这个项目名大多数人会联想到葡萄牙那座以葡萄酒闻名的岛屿或者是一块口感扎实的蛋糕。但在跨平台兼容和系统仿真这个圈子里Madeira 代表的是另一种东西——一个围绕 Wine 生态、面向 x86-64 指令翻译与 iOS 端运行 Windows 应用场景的探索性项目。它没有铺天盖地的宣传也没有官方文档站更多时候是以零散的提交记录、issue 讨论和社区口口相传的形式存在。你如果只是搜“Madeira”大概率会被旅游攻略淹没但如果你把关键词换成“Wine”“FEX-Emu”“DXMT”“x86-64”“iOS”就能慢慢拼出这个项目的轮廓。先把结论放在前面Madeira 并不是一个从零写起的模拟器它更像是一套“胶水层”和“配置编排方案”把几个已经相当成熟的组件——Wine、FEX-Emu、DXMT、以及 iOS 侧的运行环境——串成一条可用的链路。它的核心价值在于让原本为 Windows/x86-64 编译的应用程序能够在 ARM 架构的移动设备上跑起来并且尽量保留图形加速能力。这件事听起来简单做起来处处是坑指令翻译的性能损耗、图形 API 的转译、文件系统的路径映射、输入设备的适配每一项都能让一个周末的折腾变成一周的调试。这篇文章适合三类人看。第一类是对 Wine 有一定了解、想把它搬到移动端或 ARM 设备上的开发者第二类是在做 iOS 自动化、iOS 设备模拟、或者研究 iOS 原生插件与 WebView 行为的工程师想搞清楚“为什么有些 Windows 程序能在 iOS 上跑”这件事的底层逻辑第三类是对 FEX-Emu、DXMT 这些名词有耳闻但没实际动过手的技术爱好者。我会尽量把每个环节的“为什么”讲清楚而不是只丢一堆命令让你照抄。毕竟这类项目最大的问题就是命令抄完了报错看不懂然后卡死。需要提前说明的是Madeira 本身并不是一个官方命名的成熟产品社区里对它的称呼也比较松散。有人把它当作一套构建脚本有人把它理解为一个 Wine 的前端封装还有人把它和“麒麟 Wine 助手”“统信 Wine Windows 兼容组件”这类国产系统上的兼容方案放在一起讨论。这些讨论背后其实指向同一个需求在非 x86 平台上运行 Windows 应用并且希望这个过程尽可能无感。Madeira 就是在这个需求缝隙里长出来的东西。2. Madeira 的组件拼图Wine、FEX-Emu、DXMT 各自扮演什么角色2.1 Wine 负责“假装自己是 Windows”但它不翻译指令Wine 的全称是“Wine Is Not an Emulator”这句话本身就是它的定位声明。它做的事情是实现 Windows API 的兼容层把 Windows 程序调用的 kernel32、user32、gdi32 这些动态库调用映射到宿主系统的 POSIX 接口上。你可以把它理解成一个“同声传译”Windows 程序说“我要创建一个窗口”Wine 把这句话翻译成宿主系统能听懂的“创建一个窗口”的调用。但 Wine 有一个前提宿主 CPU 的指令集必须和程序编译时的目标指令集一致。也就是说如果 Windows 程序是 x86-64 编译的而你的设备是 ARM64Wine 本身帮不了你。它不会把 x86-64 的机器码翻译成 ARM64 的机器码。这就是为什么在 M 系列芯片的 Mac 上跑 Windows 游戏光有 Wine 不够还需要 Rosetta 2 或者 CrossOver 内置的翻译层。在 Linux ARM 设备上这个翻译任务通常交给 Box86/Box64而在 Madeira 的语境里交给的是 FEX-Emu。2.2 FEX-Emu 是 x86-64 到 ARM64 的“实时翻译官”FEX-Emu 是一个用户态的 x86-64 模拟器专门为 ARM64 宿主设计。它的工作方式是动态二进制翻译程序运行到哪条 x86-64 指令FEX 就把它翻译成对应的 ARM64 指令翻译结果会被缓存起来下次遇到同样的代码块就直接复用。这种“边跑边翻译”的策略比全量静态翻译灵活得多也比纯解释执行快得多。FEX-Emu 有几个关键特性值得注意。第一它支持 x86-64 的完整指令集包括 SSE、AVX 等 SIMD 扩展这对图形程序和游戏来说很重要。第二它有一个“thunking”机制允许 x86-64 代码直接调用 ARM64 的原生库避免所有调用都走翻译层。第三它的性能高度依赖于代码的“热路径”是否被充分缓存第一次运行某个程序时会有明显的卡顿后续运行会好很多。在 Madeira 的链路里FEX-Emu 承担的是最底层的指令翻译工作。Wine 的 Windows API 实现跑在 FEX 之上Windows 程序的机器码也跑在 FEX 之上。你可以把 FEX 想象成一个“CPU 模拟器”但它模拟的不是硬件而是指令集语义。2.3 DXMT 把 Direct3D 调用转成 Metal图形是跨平台运行 Windows 程序时最容易翻车的地方。Windows 程序通常调用 Direct3D 9/10/11/12 来渲染画面而 iOS 和 macOS 用的是 MetalLinux 用的是 Vulkan 或 OpenGL。DXMT 的作用就是在 Direct3D 和 Metal 之间架一座桥。DXMT 的全称是“DirectX Metal Translation”它的思路和 DXVKDirect3D 到 Vulkan类似但目标 API 是 Metal。它把 D3D11 的绘制调用、着色器、资源绑定翻译成 Metal 的对应概念。这个翻译过程不是一对一的因为 D3D 和 Metal 的抽象层级不同DXMT 需要做大量的状态跟踪和资源重定向。实测下来DXMT 对 D3D11 的支持已经相当可用D3D12 的支持还在完善中D3D9 则通常交给 Wine 内置的 WineD3D 或者别的转译层。在 Madeira 里DXMT 是可选的但如果你要跑的是带图形界面的 Windows 程序没有它基本只能看到黑屏或者软件渲染的幻灯片。有了 DXMT才能利用上设备的 GPU 加速。2.4 三者的协作关系把这三个组件串起来看Windows 程序x86-64 机器码 D3D 调用→ FEX-Emu 翻译机器码 → Wine 提供 Windows API → DXMT 翻译图形调用 → 宿主系统iOS/ARM64执行。Madeira 要做的就是让这条链路在 iOS 环境下能跑通并且尽量稳定。这里有一个常见的误解很多人以为 Wine 自己就能跑 x86 程序。实际上Wine 只负责 API 翻译指令翻译是另一回事。在 x86 宿主上Wine 可以直接跑 x86 程序因为指令集一致在 ARM 宿主上必须有一个指令翻译层。Madeira 把 FEX-Emu 拉进来就是为了补上这一环。3. 在 iOS 上跑 Windows 程序到底难在哪3.1 iOS 的沙箱限制是第一道墙iOS 的应用沙箱机制决定了一个 App 不能随意创建可执行文件、不能 fork 子进程、不能加载未签名的动态库。而 Wine 和 FEX-Emu 的工作方式恰恰需要动态生成代码、加载自定义的动态库、在运行时修改内存权限。这些操作在 iOS 上默认都是被禁止的。所以任何在 iOS 上跑 Wine 的方案都必须先解决“如何在一个受限环境里获得足够的执行权限”这个问题。常见的做法包括利用 JIT即时编译权限、通过特定的开发者模式绕过部分限制、或者把翻译层做成静态编译的库而不是运行时生成代码。Madeira 在这方面的具体实现社区里没有完整的公开文档但从它依赖 FEX-Emu 这一点来看它大概率是在 JIT 权限和静态翻译之间做了某种折中。注意iOS 的开发者模式、JIT 权限、以及应用签名机制在不同系统版本上的行为差异很大。iOS 26.3.1 这类较新版本对 JIT 的限制比早期版本严格得多很多在老版本上能跑通的方案在新版本上会直接崩溃。如果你打算动手先确认你的设备系统版本和开发者模式状态。3.2 图形栈的适配是第二道墙即使指令翻译跑通了图形渲染也是个大问题。iOS 的 Metal 和 Windows 的 Direct3D 在资源管理、同步机制、着色器模型上都有差异。DXMT 虽然做了翻译但它本身也需要宿主系统提供足够的 Metal 特性支持。在 iOS 上Metal 的某些高级特性比如某些纹理格式、计算着色器可能不可用或者需要特定的设备支持。另外iOS 的显示管理是高度封闭的。一个 App 不能随意创建全屏窗口、不能直接操作显示缓冲区。Wine 程序通常期望自己控制窗口和渲染表面这在 iOS 上需要额外的适配层。Madeira 如果要在 iOS 上跑图形程序必须处理这个“窗口系统翻译”的问题。3.3 输入和文件系统的映射是第三道墙Windows 程序期望的输入是键盘和鼠标文件系统是 C:\ 盘符和注册表。iOS 的输入是触摸屏和虚拟键盘文件系统是沙箱内的目录结构。Wine 本身提供了一部分映射机制比如把宿主文件系统挂载为 Z:\ 盘把触摸事件模拟成鼠标事件。但在 iOS 上这些映射需要额外的胶水代码。文件系统方面iOS 的沙箱让“安装”一个 Windows 程序变得复杂。你不能像在桌面系统上那样直接运行一个 .exe 安装程序因为安装程序需要写入 Program Files、注册表等位置。Wine 的 prefix前缀目录机制可以解决一部分问题但 prefix 本身需要放在可写目录里而且 iOS 对可写目录的访问也有权限限制。3.4 性能是第四道墙也是最现实的一道FEX-Emu 的动态翻译有性能开销DXMT 的图形翻译也有性能开销iOS 设备的散热和功耗限制又比桌面设备严格得多。实测中一个在桌面 x86 上流畅运行的 2D 程序在 ARM 设备上通过 FEX Wine DXMT 跑起来帧率可能只有原来的三分之一到一半。如果是 3D 程序差距更大。这不是 Madeira 独有的问题而是所有“x86 程序跑在 ARM 上”方案的共同瓶颈。缓解手段包括启用 FEX 的代码缓存、调整 Wine 的线程模型、降低图形设置、使用更高效的翻译策略。但这些手段的效果因程序而异没有万能药。4. 动手之前的准备工作环境、工具和心态4.1 确认你的设备是否具备条件不是所有 iOS 设备都适合折腾这类项目。你需要确认几件事设备是否支持开发者模式、系统版本是否在可接受范围内、是否有足够的存储空间Wine prefix 和翻译缓存可能占用几个 GB、是否有稳定的电源供应翻译过程很耗电。从社区反馈来看较新的 iOS 版本对 JIT 和动态代码生成的限制更严这意味着在老版本系统上能跑通的方案在新系统上可能需要额外的绕过手段。如果你手头有多个设备建议先用一个“不那么重要”的设备做实验避免把主力机搞成砖。4.2 工具链的准备在 iOS 上构建和部署这类项目通常需要一台 Mac 作为开发机安装 Xcode 和相关的命令行工具。你需要熟悉 Xcode 的证书配置、设备管理、以及如何把编译好的二进制推送到设备上。如果你之前做过 iOS 开发这部分是轻车熟路如果没做过建议先补一下 Xcode 从证书配置到上架的基本流程至少要知道怎么给一个 App 签名并安装到真机上。另外你可能需要一些辅助工具来查看设备日志、调试崩溃、分析性能。iOS 的 Console.app 和 Xcode 的 Instruments 是常用的选择。对于 Wine 和 FEX 的日志通常需要开启详细输出然后通过日志来定位问题。4.3 心态准备这不是一个“一键安装”的项目Madeira 这类项目的最大特点就是“没有银弹”。你可能会遇到编译错误、链接错误、运行时崩溃、图形花屏、输入无响应等各种问题。每个问题的排查都需要耐心和对底层机制的理解。如果你期望的是下载一个安装包、点几下就能跑 Windows 程序那这个方向可能不适合你。但如果你愿意花时间理解 Wine 的 prefix 机制、FEX 的翻译缓存、DXMT 的图形管线那么这个过程本身就是一个很好的学习机会。很多在桌面 Linux 上跑 Wine 时不会遇到的问题在 iOS 上会被放大逼着你去理解每一个环节。5. 从零搭建 Madeira 链路的实操思路5.1 先跑通 FEX-Emu 的独立测试在把 Wine 拉进来之前建议先单独验证 FEX-Emu 能否在你的设备上正常工作。找一个简单的 x86-64 命令行程序比如一个打印 hello world 的静态编译二进制用 FEX 跑起来看看是否能输出正确结果。这一步的目的是确认指令翻译层本身是通的排除掉 Wine 和图形栈的干扰。如果这一步就失败了后面的步骤不用继续。常见的问题包括FEX 的 rootfs 没有正确配置、动态链接器路径不对、JIT 权限被系统拒绝。查看 FEX 的日志输出通常会给出具体的错误原因。5.2 配置 Wine 的 prefix 和基本环境FEX 跑通之后下一步是配置 Wine。Wine 需要一个 prefix 目录来存放虚拟的 C 盘、注册表、以及安装的程序。在 iOS 上这个目录必须放在 App 沙箱内可写的位置。你可以用wineboot来初始化 prefix用winecfg来调整配置。关键配置项包括Windows 版本通常设为 Windows 10 或 11、图形驱动选择 DXMT 或 WineD3D、音频驱动如果不需要音频可以禁用、以及 DLL 覆盖某些程序需要特定的 DLL 替换。这些配置在桌面 Wine 上很常见但在 iOS 上你需要通过命令行或者配置文件来修改因为图形化的 winecfg 可能跑不起来。5.3 接入 DXMT 并验证图形输出Wine 的基本环境跑通后可以尝试运行一个简单的图形程序比如 Windows 自带的记事本或者一个简单的 Win32 窗口程序。如果能看到窗口说明 Wine 的窗口系统映射是通的。然后接入 DXMT尝试运行一个使用 D3D11 的程序观察是否能正常渲染。DXMT 的配置通常涉及设置环境变量比如指定 Metal 设备、启用或禁用某些特性。如果遇到花屏或崩溃可以尝试切换 DXMT 的版本或者调整 Wine 的图形设置。社区里有一些针对特定程序的配置分享可以作为参考。5.4 处理输入和文件系统的映射图形跑通后输入和文件系统的问题会逐渐暴露出来。触摸屏的点击需要被映射成鼠标事件虚拟键盘的输入需要被映射成键盘事件。Wine 本身提供了一些输入映射机制但在 iOS 上可能需要额外的适配。文件系统方面你需要把 Windows 程序放到 prefix 的 drive_c 目录下或者通过 Wine 的 Z:\ 盘访问宿主文件系统。这一步的难点在于iOS 的触摸事件和 Windows 的鼠标事件在语义上有差异。比如长按、滑动、多点触控这些操作在 Windows 程序里可能没有对应的概念。你需要决定哪些操作映射成鼠标左键、哪些映射成右键、哪些忽略。6. 那些只有踩过才知道的坑6.1 Wine 乱码问题字体和编码的双重陷阱“Wine 乱码”是社区里出现频率极高的问题。在 iOS 上这个问题可能比桌面 Linux 更严重因为 iOS 的字体管理和 Linux 不同。Wine 程序显示乱码通常有两个原因一是缺少合适的字体二是编码映射不对。字体方面Wine 需要一套 Windows 兼容的字体来渲染界面。如果 prefix 里没有安装这些字体Wine 会回退到宿主系统的字体而宿主字体的字符集可能不包含程序需要的字符。解决办法是把 Windows 的核心字体比如 SimSun、Microsoft YaHei复制到 prefix 的字体目录或者通过 winetricks 安装字体包。编码方面某些程序使用 GBK 或 Shift-JIS 编码而 Wine 默认可能按 UTF-8 处理。这需要在 Wine 的配置里调整 locale 设置或者使用LANG环境变量来指定正确的编码。6.2 FEX-Emu 的缓存失效和性能抖动FEX-Emu 的翻译缓存是性能的关键。如果缓存没有正确持久化每次启动程序都要重新翻译速度会慢到无法接受。在 iOS 上缓存目录必须放在可写位置并且要确保 App 有权限读写。另外某些系统更新或 App 重装会导致缓存失效需要重新生成。性能抖动是另一个常见问题。同一个程序有时候跑得流畅有时候卡顿。这可能是因为 FEX 的翻译线程和 Wine 的线程调度冲突或者是因为 iOS 的后台限制导致翻译线程被挂起。调整线程优先级、限制后台活动、或者使用性能模式可能有助于缓解。6.3 DXMT 的着色器编译卡顿DXMT 在第一次遇到某个着色器时需要把它从 D3D 的字节码翻译成 Metal 的着色器。这个翻译过程可能很慢导致程序在首次渲染某个效果时卡顿。如果程序有大量的着色器变体卡顿会非常明显。缓解办法包括预编译着色器缓存、降低着色器复杂度、或者接受首次运行的卡顿。某些程序允许你调整图形设置来减少着色器变体比如关闭某些特效。6.4 iOS 后台限制导致的进程被杀iOS 对后台进程的管理非常严格。如果一个 App 在后台运行系统可能在几秒到几分钟内把它挂起或杀掉。对于 Wine 和 FEX 这种需要持续运行的程序这意味着你很难在后台保持一个 Windows 程序的运行状态。如果你需要长时间运行某个程序必须保持 App 在前台并且关闭自动锁屏。另外某些 iOS 版本对 CPU 密集型任务有额外的限制可能会在设备发热时降频或杀进程。7. 这套方案能跑什么不能跑什么7.1 适合的场景从社区反馈和实测来看Madeira 链路比较适合跑以下几类程序轻量级的 Win32 工具软件比如文本编辑器、计算器、小游戏、基于 D3D11 的 2D 游戏、以及一些不依赖复杂系统服务的命令行工具。这些程序的共同特点是对性能要求不高、图形调用简单、不依赖特定的 Windows 组件。另外如果你只是想在 iOS 设备上体验一下 Wine 的工作方式或者做一些兼容性测试这套方案也是可用的。它的价值不在于“替代 Windows”而在于“验证可行性”。7.2 不适合的场景大型 3D 游戏、依赖 .NET 或 DirectX 12 的现代应用、需要内核级驱动的程序、以及依赖特定硬件外设的软件基本跑不起来。这不是 Madeira 的问题而是整个“x86 模拟 API 翻译”路线的共同局限。另外任何需要稳定长时间运行的生产力工具也不建议用这套方案。翻译层的性能开销和 iOS 的后台限制会让体验变得不可靠。7.3 和国产系统 Wine 方案的对比社区里经常把 Madeira 和“麒麟 Wine 助手”“统信 Wine Windows 兼容组件”放在一起讨论。这些国产方案通常是在 Linux 桌面环境下把 Wine 和 Box86/Box64 打包成易用的安装包面向的是普通用户。Madeira 更偏向技术探索面向的是愿意自己编译和调试的开发者。两者的目标用户不同但底层的技术栈有重叠。如果你在 Linux 桌面上已经用过 Wine那么理解 Madeira 的链路会容易很多。区别主要在于 iOS 的沙箱限制和 ARM 架构的翻译层。8. 一些实用的调试技巧和工具8.1 日志是你的第一手资料Wine 和 FEX 都支持详细的日志输出。通过设置环境变量比如WINEDEBUGall和 FEX 的日志级别你可以看到每一个 API 调用、每一次指令翻译、每一个错误。日志可能很冗长但它是定位问题的关键。在 iOS 上你可以通过 Xcode 的 Devices and Simulators 窗口查看设备日志或者使用 Console.app 过滤特定进程的输出。如果你在开发环境中运行也可以把日志重定向到文件方便后续分析。8.2 用最小复现来隔离问题遇到崩溃或异常时不要一上来就调试整个程序。先找一个最小的复现用例一个最简单的窗口程序、一个只调用一个 API 的测试程序。确认最小用例能跑通后再逐步增加复杂度。这样可以快速定位是哪个环节出了问题。8.3 性能分析先看瓶颈在哪如果程序能跑但很慢先用性能分析工具确定瓶颈。是 FEX 的翻译开销大还是 DXMT 的图形翻译慢还是 Wine 的 API 调用有阻塞Xcode 的 Instruments 可以帮你看到 CPU 和 GPU 的使用情况。如果 CPU 占用高但 GPU 空闲瓶颈可能在翻译层如果 GPU 占用高瓶颈可能在图形翻译。8.4 社区资源不要闭门造车Wine、FEX-Emu、DXMT 都有自己的社区和 issue 跟踪系统。遇到问题时先搜索是否有人遇到过类似情况。很多坑已经被别人踩过解决方案可能就在某个 issue 的评论里。另外这些项目的文档虽然不完整但关键配置和已知问题通常会在 README 或 wiki 里提到。9. 我个人在实际操作中的几点体会折腾 Madeira 这类项目最大的收获不是“跑通了某个程序”而是对跨平台兼容的整个链路有了更具体的理解。以前用 Wine 的时候只知道它“能跑 Windows 程序”但不知道背后有多少层翻译和映射。自己动手搭一遍之后才明白为什么有些程序能跑、有些不能为什么性能差异那么大。另一个体会是iOS 的封闭性既是限制也是保护。它让很多在桌面系统上理所当然的操作变得困难但也迫使你去寻找更优雅的解决方案。比如JIT 权限的限制逼着你去研究静态翻译和 AOT 编译沙箱的限制逼着你去设计更合理的文件系统映射。这些约束反而让方案更清晰。最后如果你只是想“用”Windows 程序而不是“研究”怎么跑 Windows 程序那这套方案可能不是最优选择。但如果你对底层机制感兴趣愿意花时间调试那么 Madeira 链路是一个很好的学习平台。它涉及的每一个组件——Wine、FEX-Emu、DXMT——都值得单独深入研究。