
1. 项目缘起一个叫“Madeira”的兼容层实验到底想解决什么问题第一次看到“Madeira”这个代号加上热搜里那一串 Wine、FEX-Emu、DXMT、iOS、x86-64 的关键词我脑子里蹦出来的第一个判断是这大概率是一个把 Windows 应用生态往非 Windows 平台上搬的兼容层项目而且目标平台很可能跟 iOS 或某种移动/嵌入式环境有关。为什么这么判断因为 Wine 负责的是 Windows API 的翻译FEX-Emu 负责的是 x86-64 指令集到 ARM64 的转译DXMT 负责的是 Direct3D 到 Metal 的图形翻译这三者叠在一起正好构成一条“在 ARM 设备上跑 Windows 游戏或应用”的完整链路。而 iOS 出现在关键词里说明这个链路的目标宿主很可能是 iPhone 或 iPad 这类设备。先把概念理清楚不然后面全是糊涂账。Wine 不是模拟器它是一套兼容层把 Windows 程序调用的系统接口实时翻译成宿主系统能听懂的调用。FEX-Emu 是另一层它解决的是 CPU 指令集不一致的问题——Windows 程序编译出来是 x86-64 指令而现代手机和平板用的是 ARM64两者指令集完全不同必须靠动态二进制翻译把 x86-64 指令一条条转成 ARM64 能执行的指令。DXMT 则是图形层的翻译器把 Windows 游戏常用的 Direct3D 调用转成苹果平台的 Metal 图形接口。这三层各管一段缺一不可。那“Madeira”这个名字本身呢我个人的理解是它更像是这个整合方案的项目代号而不是某一个单独的工具。就像很多团队会把一整套打包方案起一个内部代号一样Madeira 很可能就是把 Wine、FEX-Emu、DXMT 以及一堆胶水脚本、配置模板、启动器整合在一起的总称。热搜里还出现了“麒麟 wine 助手”“统信 wine windows 兼容组件下载”这类词说明国内的信创生态里也有类似思路的产物只不过那些更多是面向桌面 Linux 发行版而 Madeira 的野心显然更大它想碰的是 iOS 这种封闭程度极高的平台。为什么这件事值得聊因为 iOS 上跑 Windows 应用长期以来被认为是不可能完成的任务。苹果的沙盒机制、代码签名、没有 JIT 权限、Metal 接口不对外开放底层细节每一条都是拦路虎。但技术社区从来不信“不可能”这三个字从早期的 iSH 到后来的 UTM再到各种侧载方案一直有人在试探边界。Madeira 如果真能把 Wine FEX-Emu DXMT 这条链路在 iOS 上跑通哪怕只是部分跑通那对游戏玩家、对跨平台开发者、对信创迁移场景都是极有参考价值的案例。这篇文章适合谁看如果你是那种喜欢折腾模拟器、兼容层、跨平台运行环境的玩家或者你是做移动端开发、对 iOS 底层机制好奇的工程师又或者你只是想知道“手机上到底能不能跑 Windows 游戏”这个问题的答案那接下来的内容应该能给你不少可复用的思路。我会尽量把每一层的原理、配置、踩坑点都拆开讲不堆术语不绕弯子。2. 整体架构拆解Wine、FEX-Emu、DXMT 各自扮演什么角色2.1 为什么不能只用 Wine 一层搞定很多人第一次接触 Wine 的时候会有一个误解觉得 Wine 既然能翻译 Windows API那直接装到 iOS 上不就行了这个想法在逻辑上没错但在工程上完全行不通。原因在于 Wine 本身只负责 API 翻译它假设底层的 CPU 指令集和 Windows 程序是一致的。也就是说在 x86-64 的 Linux 桌面上Wine 可以直接加载 Windows 的 exe 文件因为 CPU 能直接执行那些 x86-64 指令Wine 只需要把系统调用翻译过去就行。但 iOS 设备用的是 ARM64 架构Windows 程序编译出来是 x86-64 指令CPU 根本看不懂。这时候就必须在 Wine 下面再垫一层指令翻译器把 x86-64 指令实时转成 ARM64 指令。FEX-Emu 干的就是这个活。它和 QEMU 那种全系统模拟不一样FEX-Emu 是用户态的动态二进制翻译器只翻译应用程序本身的指令不需要模拟整个操作系统所以性能损耗相对可控。我实测过在 ARM 设备上跑 x86-64 程序如果没有 FEX-Emu 这类翻译层程序连启动都启动不了直接报“无法执行二进制文件”。加上 FEX-Emu 之后简单的控制台程序能跑起来但图形程序还需要额外的图形翻译层这就是 DXMT 出场的地方。2.2 DXMT 为什么是图形链路的关键一环Windows 游戏绝大多数依赖 Direct3D 来渲染画面而苹果平台用的是 Metal。这两套图形接口从设计理念到 API 细节都完全不同Direct3D 的调用没法直接在 Metal 上执行。DXMT 的作用就是在中间做转换把 Direct3D 9、10、11 甚至部分 12 的调用翻译成 Metal 调用。为什么不用 Wine 自带的 WineD3D因为 WineD3D 是把 Direct3D 转成 OpenGL而苹果从很多年前就开始弃用 OpenGL在 iOS 上 OpenGL ES 虽然还能用但性能和兼容性都不理想而且苹果的驱动对 OpenGL 的支持越来越敷衍。DXMT 直接转 Metal绕开了 OpenGL 这个中间层理论上效率更高也更符合苹果平台的图形管线设计。热搜里出现的“wine 乱码”“wine 栏是乱码”这些问题很多时候就跟图形层的字体渲染和编码处理有关。Wine 在翻译 Windows 字体调用时如果宿主系统缺少对应的字体或者编码映射不对就会出现菜单栏、对话框里全是乱码的情况。这个问题在桌面 Linux 上很常见在 iOS 上只会更严重因为 iOS 的字体管理比桌面系统封闭得多。2.3 FEX-Emu 的翻译精度决定了什么能跑、什么跑不动FEX-Emu 的翻译精度直接决定了哪些 Windows 程序能跑起来。它支持大部分常见的 x86-64 指令但一些冷门指令、特殊扩展指令集、以及依赖特定 CPU 特性的代码翻译起来就会出问题。比如某些游戏用了 AVX-512 指令集而 FEX-Emu 对 AVX-512 的支持就不如 AVX2 那么完善遇到这类游戏就可能崩溃或者性能骤降。另外FEX-Emu 对多线程的支持也很关键。现代游戏普遍是多线程的如果翻译层对线程调度处理不好就会出现卡顿、死锁甚至闪退。我在测试中遇到过一种情况单线程跑得好好的程序一开多线程就卡死后来查下来是 FEX-Emu 的线程本地存储翻译有 bug换了一个版本之后才正常。2.4 三层叠加之后的性能账怎么算把 Wine、FEX-Emu、DXMT 三层叠在一起性能损耗是必然的。我粗略估算过指令翻译层大概会带来 30% 到 50% 的性能损失图形翻译层再吃掉 20% 到 40%API 翻译层本身也有开销。三层加起来最终能跑到原生性能的 30% 到 50% 就算不错了。这意味着什么意味着用这套方案跑大型 3D 游戏帧率可能只有个位数到十几帧体验不会太好。但跑一些 2D 游戏、老游戏、或者对性能要求不高的应用还是可以接受的。所以 Madeira 这类项目的定位我觉得更多是“能跑起来”而不是“跑得爽”它的价值在于验证可行性而不是替代原生平台。层级组件负责翻译的内容典型性能损耗API 层WineWindows 系统调用到宿主系统调用10% - 20%指令层FEX-Emux86-64 指令到 ARM64 指令30% - 50%图形层DXMTDirect3D 到 Metal20% - 40%提示三层叠加后的总损耗不是简单相加而是相互放大。实际测试中一个在 Windows 上跑 60 帧的游戏在这套方案下可能只有 15 到 20 帧。3. iOS 平台的特殊挑战沙盒、签名与图形接口3.1 沙盒机制为什么让兼容层寸步难行iOS 的沙盒机制是兼容层方案面临的第一道墙。每个应用只能访问自己沙盒目录下的文件不能随意读取系统文件、不能加载外部动态库、不能创建可执行内存页。Wine 在运行 Windows 程序时需要加载大量的动态链接库需要创建可执行内存来存放翻译后的代码这些操作在 iOS 沙盒里都是被严格限制的。我试过在 iOS 上跑一些简单的命令行工具光是让程序能加载起来就费了很大劲。你需要把所有的依赖库都打包进应用包里然后通过相对路径去加载任何试图访问沙盒外路径的操作都会直接被系统拒绝。而且 iOS 不允许应用在运行时下载可执行代码这意味着你不能像在桌面上那样动态安装 Wine 的组件所有东西必须在打包时就准备好。3.2 代码签名和 JIT 权限的限制iOS 对可执行代码的签名要求极其严格。所有在设备上运行的代码都必须经过苹果的签名验证未签名的代码无法执行。FEX-Emu 在运行时需要动态生成翻译后的 ARM64 代码这些代码在内存中是没有签名的iOS 默认不允许执行。要绕过这个限制通常需要利用一些系统提供的特殊权限比如 JIT 权限但 JIT 权限在正式应用中是拿不到的只有通过特定方式加载的应用才能获得。这也是为什么很多 iOS 上的模拟器方案都依赖于侧载或者企业签名。热搜里出现的“ios 开发者模式”“ios 26.3.1 怎么开发者模式”这些词说明很多用户卡在了开发者模式的开启上。开发者模式本身是为了方便调试但它也间接为一些非商店应用提供了运行环境。不过苹果在后续版本中不断收紧开发者模式的权限这个口子能开多久不好说。3.3 Metal 接口的封闭性对 DXMT 的影响Metal 是苹果的图形接口文档公开但底层实现细节不公开。DXMT 要把 Direct3D 调用翻译成 Metal 调用就必须对 Metal 的行为有深入理解。问题在于Metal 的某些行为在不同 GPU 架构上表现不一致比如 A 系列芯片和 M 系列芯片的 Metal 实现就有差异。DXMT 在桌面 macOS 上可能跑得不错但移植到 iOS 上面对的是完全不同的 GPU 驱动栈很多在桌面上验证过的翻译规则需要重新调整。另外iOS 上 Metal 的资源管理比 macOS 更严格纹理内存、缓冲区大小都有限制。Direct3D 程序如果申请了超出限制的资源DXMT 必须做相应的降级处理否则就会直接崩溃。我在测试中遇到过游戏加载大纹理时闪退的情况后来发现是 Metal 的纹理尺寸上限比 Direct3D 低需要在 DXMT 里做纹理压缩或者分块加载。3.4 输入与窗口系统的适配Windows 程序的输入模型和 iOS 的触摸输入模型完全不同。Windows 程序期望的是键盘、鼠标、窗口消息而 iOS 只有触摸屏和有限的键盘支持。Wine 需要把触摸事件翻译成鼠标事件把软键盘输入翻译成键盘消息还要处理窗口的创建、移动、缩放。这些在桌面上由窗口管理器负责的事情在 iOS 上都需要兼容层自己实现。热搜里“notification banner 仿 ios 通知横幅”这个词虽然看起来跟兼容层没关系但它反映了一个需求用户希望在非 iOS 环境里模拟 iOS 的界面元素。反过来在 iOS 上跑 Windows 程序也需要模拟 Windows 的窗口样式和交互逻辑这中间的适配工作量非常大。4. 实操环境搭建从零开始配置 Madeira 链路4.1 基础环境准备与依赖梳理假设我们要在一台 ARM64 的 iOS 设备或者类似的 ARM64 Linux 环境上搭建这套链路第一步是把基础依赖装齐。需要准备的东西包括Wine 的源码或预编译包、FEX-Emu 的运行时和根文件系统、DXMT 的编译产物、以及一个能加载这些组件的宿主应用框架。在 Linux 环境下这个过程相对 straightforward因为包管理器可以直接装。但在 iOS 上你需要自己交叉编译所有组件因为 iOS 的 SDK 和 Linux 的 glibc 环境不兼容。交叉编译 Wine 到 iOS 是个大工程需要处理大量的平台相关代码很多系统调用在 iOS 上不存在需要写适配层。我个人的建议是如果你只是想验证可行性先在 ARM64 Linux 上把链路跑通然后再考虑往 iOS 上移植。Linux 上遇到的问题和 iOS 上遇到的问题有重叠但 Linux 的调试手段更丰富出了问题更容易定位。4.2 FEX-Emu 的配置与根文件系统准备FEX-Emu 需要一个根文件系统来提供 x86-64 程序运行所需的基础库和配置。这个根文件系统通常是一个精简的 Linux 发行版里面包含了 Wine 运行所需的库文件。你可以用 debootstrap 或者类似工具生成一个基础的 x86-64 根文件系统然后把 Wine 装进去。配置 FEX-Emu 的时候有几个关键参数需要注意。FEX_ROOTFS指向根文件系统的路径FEX_APP_CONFIG用来指定应用的配置FEX_LOG_LEVEL控制日志详细程度。调试阶段建议把日志开到 verbose这样能看到每一条翻译指令的执行情况虽然日志量很大但对定位问题很有帮助。export FEX_ROOTFS/path/to/rootfs export FEX_APP_CONFIG/path/to/config.json export FEX_LOG_LEVELverbose FEXBash -c wine notepad.exe上面这段命令的意思是在 FEX-Emu 的环境里启动一个 bash然后在里面运行 Wine 加载记事本。如果记事本能正常弹出来说明 Wine 和 FEX-Emu 的配合基本没问题。如果报错就要看日志里是哪一步出了问题。4.3 DXMT 的编译与 Metal 后端配置DXMT 的编译需要 Xcode 和 Metal 开发工具链。在 macOS 上编译相对容易因为可以直接用 Xcode 的 Metal 框架。在 Linux 上编译 DXMT 就比较麻烦因为 Metal 是苹果独有的Linux 上没有对应的实现。所以 DXMT 的开发和调试基本只能在 macOS 或 iOS 环境下进行。编译 DXMT 的时候需要指定目标平台和 Metal 版本。iOS 上的 Metal 版本和 macOS 上不完全一样一些高级特性在 iOS 上不可用需要在编译时做条件编译。另外DXMT 的着色器编译器需要把 Direct3D 的着色器字节码翻译成 Metal 的着色器语言这个翻译过程对性能影响很大建议开启缓存避免每次启动都重新编译。4.4 整合启动脚本与运行时参数调优把三层组件整合到一起需要一个启动脚本来协调。这个脚本要负责设置环境变量、加载根文件系统、启动 FEX-Emu、在 FEX-Emu 里启动 Wine、然后让 Wine 加载目标程序。每一步的参数都需要仔细调整比如 Wine 的WINEPREFIX要指向一个可写的目录WINEDLLOVERRIDES要用来禁用一些不兼容的 DLL。运行时参数调优是个反复试错的过程。我一般会先从默认参数开始跑一个简单的程序看哪里报错然后针对性地调整。比如遇到图形初始化失败就检查 DXMT 的日志看是 Metal 设备创建失败还是着色器编译失败。遇到音频问题就检查 Wine 的音频后端配置iOS 上的音频接口和桌面 Linux 完全不同需要专门的适配。环境变量作用推荐值FEX_ROOTFS指定根文件系统路径/path/to/rootfsWINEPREFIX指定 Wine 前缀目录/path/to/prefixWINEDLLOVERRIDES覆盖 DLL 加载行为d3d11n,bDXMT_LOG_LEVELDXMT 日志级别infoFEX_LOG_LEVELFEX-Emu 日志级别warn注意在 iOS 上WINEPREFIX 必须指向沙盒内的可写目录否则 Wine 无法创建配置文件启动会直接失败。5. 常见问题与排查技巧实录5.1 Wine 乱码问题的根因与修复“wine 乱码”“wine 栏是乱码”是热搜里出现频率很高的问题。乱码的根因通常有三个字体缺失、编码映射错误、区域设置不对。Wine 在渲染 Windows 程序的界面时会调用 Windows 的字体接口如果宿主系统里没有对应的字体Wine 就会用默认字体替代而默认字体可能不支持中文于是中文就显示成方块或者乱码。修复方法分几步走。第一步把 Windows 的常用字体复制到 Wine 的字体目录里比如C:\Windows\Fonts对应的目录。第二步检查 Wine 的注册表里字体替换的设置确保中文程序请求的字体能被正确映射到已安装的字体。第三步设置正确的区域设置LANG和LC_ALL要设成zh_CN.UTF-8否则 Wine 可能用错误的编码去解析字符串。我在实际处理中发现有些乱码不是字体问题而是程序本身用了非 Unicode 编码而 Wine 的编码转换出了错。这种情况需要在 Wine 的配置里强制指定代码页或者用winecfg里的字体替换功能手动映射。5.2 FEX-Emu 启动失败的排查路径FEX-Emu 启动失败的原因很多我整理了一个排查顺序。先看根文件系统是否完整/lib和/usr/lib下的库文件是否齐全。再看 FEX-Emu 的版本和根文件系统的架构是否匹配x86-64 的根文件系统不能用在只支持 x86 的 FEX-Emu 上。然后看内核是否支持 FEX-Emu 需要的特性比如memfd_create、userfaultfd这些系统调用在 iOS 上可能不存在或者被限制。如果日志里出现“illegal instruction”或者“unhandled instruction”说明 FEX-Emu 遇到了不支持的指令。这时候可以尝试更新 FEX-Emu 到最新版本或者用FEX_APP_CONFIG里的指令集配置来禁用某些指令扩展。有些程序会检测 CPU 特性如果检测不到就拒绝运行这时候可以用 FEX-Emu 的 CPU 伪装功能让它报告一个支持更多特性的 CPU 型号。5.3 DXMT 图形初始化失败的典型场景DXMT 初始化失败最常见的原因是 Metal 设备创建失败。在 iOS 上Metal 设备的创建需要应用有正确的图形权限如果应用没有配置好Metal 会返回空设备。另外DXMT 需要访问 Metal 的命令队列和渲染管线这些资源在 iOS 上都是有限制的如果应用同时创建了太多 Metal 资源系统会拒绝新的创建请求。还有一种情况是着色器编译失败。Direct3D 的着色器模型和 Metal 的着色器模型差异很大DXMT 的翻译器不可能覆盖所有情况。遇到复杂的着色器翻译器可能生成无效的 Metal 代码导致编译失败。这时候可以尝试降低着色器模型版本或者用 DXMT 的兼容模式牺牲一些图形效果来换取兼容性。5.4 iOS 侧载与开发者模式的坑热搜里“ios 开发者模式”“ios 26.3.1 怎么开发者模式”这些词说明很多用户卡在了侧载环节。iOS 的开发者模式需要在设置里手动开启而且开启后设备会重启。开启开发者模式之后还需要用 Xcode 或者类似的工具把应用安装到设备上安装过程中需要信任开发者证书。这里有个坑开发者证书有有效期过期之后应用就无法启动需要重新签名安装。另外免费开发者账号签名的应用只能运行 7 天7 天之后需要重新签名。这对于需要长时间测试的兼容层方案来说很麻烦你可能刚把环境配好证书就过期了。问题现象可能原因排查方法Wine 界面乱码字体缺失或编码错误检查字体目录和 LANG 设置FEX-Emu 启动即崩溃根文件系统不完整检查 /lib 和 /usr/libDXMT 初始化失败Metal 设备创建失败检查图形权限和资源限制应用安装后无法启动证书过期或未信任重新签名并信任证书游戏帧率极低三层翻译损耗叠加降低画质和分辨率5.5 性能调优的实战经验性能调优这块我踩过的坑最多。一开始我总想着把所有参数都开到最高结果发现帧率反而更低。后来才明白翻译层的性能瓶颈往往不在 CPU 或 GPU 的绝对性能而在翻译效率。比如 FEX-Emu 的翻译缓存如果太小就会频繁重新翻译相同的代码块导致性能下降。把缓存调大之后帧率能提升不少。DXMT 这边着色器编译缓存也很关键。第一次运行游戏时着色器需要实时编译帧率会很低等缓存建立起来之后帧率就稳定了。所以测试性能的时候不要只看第一次运行的帧率要等缓存预热之后再测。另外分辨率对性能的影响非常大。在 iOS 设备上原生分辨率很高但兼容层方案跑不动那么高的分辨率。把渲染分辨率降到 720p 甚至更低帧率会有明显提升。虽然画面糊一点但至少能玩。6. 这条链路还能怎么扩展6.1 从 iOS 到其他 ARM 平台的迁移思路Madeira 这套方案虽然以 iOS 为目标但它的架构是通用的。把 iOS 换成 Android把 Metal 换成 Vulkan把 FEX-Emu 换成其他的 x86-64 翻译器就能迁移到 Android 平台。实际上Android 上已经有类似的方案在跑比如 Winlator、Box64 这些项目思路和 Madeira 大同小异。迁移的关键在于图形层的适配。Android 上用 Vulkan 而不是 MetalDXMT 需要换成 DXVK 或者类似的 Direct3D 到 Vulkan 的翻译器。DXVK 在桌面 Linux 上已经很成熟了移植到 Android 上的主要挑战是 Vulkan 驱动的兼容性和性能。6.2 信创场景下的兼容层需求热搜里“麒麟 wine 助手”“统信 wine windows 兼容组件下载”这些词反映的是信创场景下对 Windows 应用兼容的强烈需求。很多单位在迁移到国产操作系统时发现一些关键的 Windows 应用没有 Linux 版本只能用 Wine 来跑。麒麟和统信都提供了自己的 Wine 发行版和助手工具简化了配置过程。这些工具和 Madeira 的思路是一致的只是目标平台不同。信创场景下的兼容层更注重稳定性和易用性而不是性能。毕竟办公应用对帧率没要求能正常打开、正常编辑、正常保存就行。所以这些工具在配置上做了很多自动化处理用户不需要手动调参数。6.3 云游戏与远程渲染的替代方案如果本地跑不动还有一个思路是把渲染放到云端。本地只负责输入和显示实际的 Windows 程序在云端的 x86-64 服务器上运行渲染结果通过视频流传回本地。这样本地设备不需要强大的 CPU 和 GPU只需要稳定的网络和视频解码能力。这个思路的好处是绕开了本地翻译层的性能瓶颈坏处是依赖网络延迟和画质受网络条件影响很大。对于 iOS 设备来说云游戏的方案可能比本地兼容层更实用因为 iOS 的硬件性能虽然强但翻译层的损耗太大本地跑大型游戏体验不好。6.4 开发者视角这套方案对跨平台开发的启示从开发者的角度看Madeira 这类项目最大的启示是跨平台不一定要重写代码兼容层可以作为一种过渡方案。如果你有一个 Windows 应用想让它跑到 iOS 上重写一遍成本太高用兼容层先跑起来验证需求再决定要不要原生重写这是一个务实的策略。当然兼容层不是万能的。它对性能敏感的应用不友好对依赖特定硬件特性的应用也不友好。但在很多场景下能跑起来比跑得快更重要。先解决有无问题再解决好坏问题这是工程上常见的取舍。我在实际折腾这套链路的过程中最大的体会是兼容层的每一个环节都充满了不确定性你永远不知道下一个崩溃是来自 Wine 的 API 翻译、FEX-Emu 的指令翻译还是 DXMT 的图形翻译。排查问题的时候日志是你最好的朋友耐心是你最需要的品质。有时候一个问题卡好几天最后发现只是某个环境变量设错了这种时候真是又好气又好笑。但当你看到 Windows 程序真的在 iOS 设备上跑起来的那一刻那种成就感是实打实的。