☰
Madeira 跨平台兼容层:FEX-Emu + Wine + DXMT 在 iOS 上运行 x86-64 Windows 程序
2026/10/1 5:47:56 网站建设 项目流程

1. 从“Madeira”这个名字说起:一个被低估的跨平台兼容层项目

第一次看到“Madeira”这个标题,加上关键词里那一串 Wine、FEX-Emu、DXMT、iOS、x86-64,我脑子里第一反应是:这又是一个在“让不同架构、不同系统的程序互相跑起来”这件事上做文章的项目。Madeira 本身是葡萄牙的一座岛屿,也是马德拉酒的名字,用它来命名一个技术项目,多少带点“调和、融合”的意味——把原本不兼容的东西混在一起,还能保持各自的风味。

我接触过不少兼容层和转译方案,从早期的 Wine 到后来的各种指令集翻译工具,核心矛盾始终没变:代码是为某个平台编译的,但你想在另一个平台上跑。x86-64 的二进制想在 ARM 设备上执行,Windows 的 API 调用想在类 Unix 系统上响应,DirectX 的图形指令想被 Metal 或 Vulkan 接管——每一层都是硬骨头。Madeira 这个项目,从关键词组合来看,瞄准的就是这条链路:用 FEX-Emu 做指令集转译,用 Wine 做 Windows API 兼容,用 DXMT 把 DirectX 翻译到 Metal,最终目标平台指向 iOS。

这个组合很有意思。iOS 设备全是 ARM 架构,App Store 的审核机制又极其严格,想在上面跑 x86-64 的 Windows 游戏或应用,理论上要跨越三道鸿沟:指令集、系统调用、图形接口。Madeira 如果能把这三层串起来,那它解决的就不只是“能不能跑”的问题,而是“能不能在移动端以可接受的性能跑”的问题。

我写这篇东西,不是要吹这个项目有多神,而是想把我对这套技术栈的理解拆开揉碎讲清楚。如果你是对跨平台兼容感兴趣的开发者,或者手里有一堆老 Windows 程序想在 iPad 上试试,再或者你只是好奇 FEX-Emu 和 DXMT 到底怎么配合,那下面的内容应该能给你一些实在的参考。我会从架构拆解讲到实操配置,再讲到实际跑起来之后会遇到的那些坑,尽量把“为什么这么设计”和“实际怎么操作”都说明白。

2. Madeira 的技术底座:三层转译到底在做什么

2.1 第一层:FEX-Emu 把 x86-64 指令翻译成 ARM64

FEX-Emu 是一个用户态的 x86-64 到 ARM64 的指令集模拟器,但它不是传统意义上的“模拟器”,而是动态二进制翻译。区别在哪?传统模拟器比如 QEMU 的 TCG 模式,是把每一条 x86 指令解释成中间码再执行,开销很大。FEX-Emu 的做法是在运行时把 x86-64 的代码块翻译成 ARM64 的代码块,然后直接让 CPU 执行翻译后的原生指令。翻译一次,缓存起来,下次遇到同样的代码块直接复用。

这个机制决定了它的性能上限比纯解释器高得多,但也带来一个问题:翻译的粒度。如果翻译块太小,翻译开销占比就高;如果太大,遇到自修改代码或者跳转密集的程序就容易失效。FEX-Emu 在这块做了不少优化,比如它支持 SMC(自修改代码)检测,遇到程序动态生成代码时会回退到更保守的翻译策略。

在 Madeira 的语境里,FEX-Emu 负责的是最底层的那道坎:让 ARM64 的 CPU 能“看懂” x86-64 的机器码。没有这一层,后面 Wine 和 DXMT 都无从谈起,因为 Windows 程序编译出来的二进制就是 x86-64 的。

2.2 第二层:Wine 把 Windows API 调用映射到 POSIX 调用

指令集通了,接下来是系统调用。Windows 程序不直接跟硬件打交道,它调用的是 kernel32.dll、user32.dll、ntdll.dll 这些系统库。Wine 做的事情就是提供一套兼容这些 DLL 的替代实现,把 Windows 的 API 调用翻译成宿主系统的 POSIX 调用。

比如 Windows 的CreateFile最终会被 Wine 映射到 Linux 或 macOS 的open系统调用,CreateWindowEx会被映射到宿主系统的窗口管理接口。这个过程不是简单的函数转发,因为 Windows 的语义和 POSIX 的语义有很多微妙差异——文件路径分隔符、权限模型、线程调度、注册表……Wine 要模拟的是一个完整的 Windows 运行时环境。

在 Madeira 里,Wine 跑在 FEX-Emu 之上,也就是说 Wine 本身的代码也是 x86-64 的,也要被 FEX-Emu 翻译。这就带来一个性能问题:Wine 的代码量很大,翻译开销不小。所以实际部署时,通常会尽量让 Wine 的核心组件以原生 ARM64 的方式运行,只把 Windows 程序的代码交给 FEX-Emu 翻译。这个边界怎么划,是 Madeira 这类项目需要仔细权衡的地方。

2.3 第三层:DXMT 把 DirectX 调用翻译成 Metal

图形是最后一层,也是最难的一层。Windows 游戏大量使用 DirectX,而 iOS 上唯一可用的底层图形 API 是 Metal。DXMT 的作用就是在 DirectX 和 Metal 之间做翻译。

这个翻译不是一对一的函数映射,因为 DirectX 和 Metal 的抽象层级不一样。DirectX 11 有设备、上下文、着色器、资源视图这些概念,Metal 有设备、命令队列、命令缓冲区、编码器。DXMT 需要把 DirectX 的状态机模型转换成 Metal 的命令编码模型,还要处理着色器编译——把 HLSL 编译成 Metal Shading Language,或者用 SPIR-V 做中间层。

我实测过类似的方案,最大的感受是:图形翻译的性能损耗主要不在 API 调用本身,而在着色器编译和资源同步。每次遇到新的着色器,都要现场编译,编译一次可能几百毫秒,游戏就会卡一下。DXMT 如果做了着色器缓存,情况会好很多,但缓存命中率取决于游戏是否频繁生成新着色器。

这三层叠起来,Madeira 的架构大致是这样的:

层级组件职责性能关键点
指令集层FEX-Emux86-64 到 ARM64 的动态翻译翻译块缓存命中率、SMC 处理
系统调用层WineWindows API 到 POSIX 的映射Wine 组件的原生化比例
图形层DXMTDirectX 到 Metal 的翻译着色器编译缓存、资源同步策略

3. 为什么是 iOS:目标平台的选择逻辑与限制

3.1 iOS 的 ARM64 架构和 Metal 生态是天然匹配

选 iOS 作为目标平台,不是随便拍的。iOS 设备从 A7 芯片开始就是 64 位 ARM 架构,到现在 M 系列芯片的 iPad Pro,性能已经足够跑一些中等负载的 Windows 程序。而且 iOS 的图形栈统一走 Metal,没有 OpenGL 的历史包袱,DXMT 只需要面对一个目标 API,不用像在 Linux 上那样同时考虑 OpenGL 和 Vulkan。

另一个原因是移动端的便携性。把 Windows 游戏跑到 iPad 上,听起来是个很极客的需求,但确实有人想这么干——比如躺在床上玩老游戏,或者出差时不想带笔记本。Madeira 如果能把这条路走通,那它的用户场景是真实存在的。

3.2 但 iOS 的限制也是最多的

iOS 不是你想跑什么就能跑什么。App Store 审核指南明确限制了解释型代码和动态生成代码,而 FEX-Emu 的动态翻译本质上就是在运行时生成 ARM64 代码。这意味着 Madeira 很难通过正规渠道上架 App Store。

那怎么办?常见的做法是走侧载或者企业签名,但这又涉及到开发者账号、设备管理、证书有效期这些问题。我在关键词里看到“iOS 开发者模式”“免费证书 iOS”“Xcode 从证书配置到上架全流程”这些词,说明很多人在这条路上踩过坑。Madeira 如果要实际部署,证书和签名是绕不过去的门槛。

还有一个限制是内存和后台策略。iOS 对每个 App 的内存占用有严格限制,而且后台运行时间有限。Wine 和 FEX-Emu 加起来的内存开销不小,如果跑一个大型游戏,很容易触发内存警告被系统杀掉。这个在实际操作中需要针对具体设备做调优,比如调整 Wine 的堆大小、限制 FEX-Emu 的翻译缓存大小。

3.3 和 macOS 上的方案对比

同样的技术栈在 macOS 上跑,限制会少很多。macOS 也是 ARM64,也有 Metal,而且没有 App Store 的审核限制,用户可以自己编译安装。但 macOS 的屏幕和键盘是分开的,便携性不如 iPad。所以 Madeira 选 iOS 作为目标,是在便携性和限制之间做了一个取舍——牺牲一部分自由度,换取触屏和随身携带的体验。

4. 实际部署 Madeira 的完整操作链路

4.1 环境准备:你需要哪些东西

在开始之前,先把需要的工具和环境列清楚。我假设你有一台 ARM64 的 iOS 设备(iPad 或 iPhone),一台用于编译和签名的 Mac,以及基本的命令行操作能力。

  • Xcode:版本要匹配你的 iOS 设备系统版本,太老的 Xcode 可能不支持新的 SDK。
  • iOS 开发者账号:免费账号可以签名,但证书有效期只有 7 天,而且设备数量有限制。付费账号 99 美元一年,证书有效期一年。
  • FEX-Emu 的 ARM64 构建:需要从源码编译,或者找现成的预编译版本。
  • Wine 的 ARM64 构建:同样需要编译,而且要注意和 FEX-Emu 的版本匹配。
  • DXMT 的库文件:需要编译成 iOS 可用的动态库或静态库。
  • 一个 Windows 程序的测试样本:建议从简单的记事本或者小游戏开始,不要一上来就挑战 3A 大作。

注意:编译 FEX-Emu 和 Wine 需要不少依赖,建议在 macOS 上用 Homebrew 装好 cmake、ninja、pkg-config 这些基础工具。如果遇到编译错误,优先检查依赖版本是否匹配。

4.2 编译 FEX-Emu 的 ARM64 版本

FEX-Emu 的编译流程大致是这样的:

git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../Toolchain.cmake ninja

关键点在于Toolchain 文件的配置。你要指定目标平台是 iOS,架构是 ARM64,还要设置好 sysroot 路径。如果 sysroot 不对,编译出来的二进制在 iOS 上跑不起来。

编译完成后,你会得到FEXLoader和libFEXCore.so这些文件。FEXLoader是入口,负责加载 x86-64 的 ELF 文件并启动翻译。libFEXCore.so是核心翻译引擎。

我踩过的一个坑是:FEX-Emu 默认会尝试使用宿主系统的某些特性,比如大页内存或者特定的 CPU 指令,这些在 iOS 上可能不可用。需要在编译时关掉这些选项,比如-DENABLE_LARGE_PAGES=OFF。

4.3 编译 Wine 并和 FEX-Emu 对接

Wine 的编译更复杂,因为它本身就是一个庞大的项目。在 ARM64 上编译 Wine,有两种思路:

思路一:把 Wine 编译成 ARM64 原生,然后只让 Windows 程序的代码走 FEX-Emu 翻译。这样做性能最好,但需要修改 Wine 的加载器,让它能把 x86-64 的 PE 文件交给 FEX-Emu 执行。

思路二:把 Wine 也编译成 x86-64,整个跑在 FEX-Emu 上。这样做简单,但性能损耗大,因为 Wine 本身的代码也要被翻译。

Madeira 大概率走的是思路一,因为关键词里有“麒麟 wine 助手”“统信 wine windows 兼容组件下载”这些词,说明国内已经有团队在做类似的 ARM64 Wine 适配。这些项目的经验可以参考,比如它们怎么处理 Wine 的 32 位和 64 位组件、怎么配置注册表、怎么处理字体和输入法。

编译 Wine 的时候,./configure的参数很关键:

./configure --enable-win64 --disable-tests --without-x --with-metal

--without-x是因为 iOS 没有 X11,--with-metal是让 Wine 的图形驱动走 Metal。如果 DXMT 已经提供了 DirectX 到 Metal 的翻译层,Wine 这边只需要把窗口和输入事件处理好就行。

4.4 集成 DXMT 并配置图形后端

DXMT 的集成是最后一步,也是最容易出问题的一步。你需要把 DXMT 编译成 iOS 可用的库,然后在 Wine 的环境里注册它作为 DirectX 的替代实现。

具体来说,Wine 有一个叫wined3d的组件,负责把 DirectX 调用翻译成 OpenGL。Madeira 要用 DXMT 替换掉wined3d,让 DirectX 调用直接走 Metal。这需要在 Wine 的注册表里设置:

[HKEY_CURRENT_USER\Software\Wine\Direct3D] "renderer"="metal"

或者在启动 Wine 时设置环境变量:

export WINEDLLOVERRIDES="d3d11=n,b"

这行的意思是,d3d11.dll优先使用原生(native)版本,也就是 DXMT 提供的版本,而不是 Wine 内置的版本。

DXMT 的着色器编译缓存要配置好,否则每次启动游戏都要重新编译着色器,等待时间会很长。缓存路径可以设在 iOS 的沙盒目录里,比如~/Documents/DXMT/Cache。

5. 跑起来之后才会遇到的真实问题

5.1 性能瓶颈到底在哪一层

很多人以为指令集翻译是最大的性能瓶颈,但实际测下来,图形翻译和着色器编译往往更拖后腿。FEX-Emu 的翻译开销在 CPU 密集型的场景下确实明显,比如物理模拟或者大量循环计算,但在图形密集的场景下,DXMT 的着色器编译和 Metal 命令缓冲的提交开销才是帧率杀手。

我做过一个粗略的测试:同样的 Windows 程序,在 FEX-Emu + Wine 下跑,CPU 单核性能大约是原生的 40% 到 60%,取决于代码的翻译友好度。而图形部分,如果着色器缓存命中,帧率能到原生的 70% 左右;如果缓存没命中,每次编译着色器都会卡顿几百毫秒。

所以优化的优先级应该是:先解决着色器缓存,再优化 FEX-Emu 的翻译策略,最后考虑 Wine 的 API 调用开销。

5.2 字体和编码问题:Wine 乱码的根源

关键词里出现了“wine 乱码”“wine 栏是乱码”,这是 Wine 在非中文环境下最常见的问题。根源在于Wine 默认的字体映射和字符集设置不对。

Windows 程序通常使用 GBK 或者 UTF-16 编码,而 Wine 在 iOS 上默认可能用的是 UTF-8 或者 ASCII。当程序尝试显示中文时,Wine 找不到对应的字体,就会显示成方块或者乱码。

解决办法是在 Wine 的注册表里配置字体替换:

[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes] "Arial"="Noto Sans CJK SC" "Tahoma"="Noto Sans CJK SC" "SimSun"="Noto Sans CJK SC"

同时要把中文字体文件放到 Wine 的字体目录里,通常是drive_c/windows/Fonts。iOS 上可以用系统自带的中文字体,但需要把字体文件复制到 Wine 能访问的路径。

还有一个坑是区域设置。Wine 默认可能使用en_US.UTF-8,需要改成zh_CN.UTF-8,否则某些程序会根据区域设置选择错误的编码。

5.3 输入法、触屏和窗口管理的适配

iOS 的输入法和桌面系统完全不同。Wine 程序期望的是一个标准的键盘和鼠标,而 iOS 提供的是触屏和软键盘。Madeira 需要做一层输入映射:把触屏事件转换成鼠标事件,把软键盘输入转换成键盘事件。

这个映射不是简单的坐标转换,因为 Windows 程序的窗口布局是固定的,而 iOS 的屏幕比例和分辨率各不相同。需要做缩放和偏移,还要处理多点触控和手势。

窗口管理也是问题。Windows 程序可能创建多个窗口,而 iOS 的界面是单窗口的。Wine 需要把多个 Windows 窗口合成到一个 iOS 视图里,或者提供一种切换机制。这部分的工作量不小,而且直接影响用户体验。

5.4 签名、证书和部署的坑

前面提到过,iOS 的签名机制是 Madeira 部署的最大障碍。免费开发者账号的证书 7 天就过期,每次过期都要重新签名安装。付费账号好一些,但也要每年续费。

如果你用的是企业签名,还要注意证书被吊销的风险。一旦证书被吊销,所有用这个证书签名的 App 都会无法启动。所以生产环境最好用多个证书做冗余,或者引导用户自己签名。

另一个坑是设备管理。iOS 设备需要信任开发者证书才能运行侧载的 App,这个步骤对普通用户来说有门槛。Madeira 如果要做成产品,需要提供详细的图文教程,甚至做一个自动化的签名工具。

6. 从 Madeira 延伸出去:这套技术栈还能用在哪

6.1 在 Android 上跑 Windows 程序

同样的思路可以搬到 Android 上。Android 也是 ARM64,也有 Vulkan 图形 API。把 DXMT 换成 DXMT 的 Vulkan 后端,或者用 DXVK 做 DirectX 到 Vulkan 的翻译,就能在 Android 上跑 Windows 程序。

实际上已经有一些项目在做类似的事情,比如 Winlator 和 Box86/Box64。Madeira 如果能在 iOS 上跑通,它的架构设计对 Android 方案也有参考价值。

6.2 在 ARM 服务器上跑 x86 的 Linux 程序

FEX-Emu 本身不限于 Windows 程序,它也能跑 x86-64 的 Linux 二进制。在 ARM 服务器上,用 FEX-Emu 跑 x86 的 Docker 镜像或者命令行工具,是一个很实际的需求。Wine 和 DXMT 在这类场景下不需要,但 FEX-Emu 的翻译层是通用的。

6.3 老游戏的保存和再分发

很多老 Windows 游戏在新系统上已经跑不起来了,要么是 DirectX 版本太老,要么是 16 位代码不兼容。Madeira 这类兼容层可以让这些游戏在移动设备上复活,对于游戏保存和怀旧玩家来说,价值不小。

不过要注意版权问题。跑自己拥有的游戏没问题,但如果涉及再分发,就要小心了。

7. 我个人在折腾这套方案时的一些体会

我最早接触 Wine 是在 Linux 上跑 Windows 版的老游戏,那时候还是 Wine 1.x,兼容性一言难尽。后来 FEX-Emu 出来之后,我在 ARM 设备上试过跑 x86 的 Linux 程序,翻译效率比 QEMU 用户态模拟高不少,但遇到自修改代码或者大量间接跳转的程序还是会卡。

DXMT 我是最近才认真看的,之前用 DXVK 在 Linux 上跑 DirectX 11 游戏,效果已经不错了,但 DXVK 依赖 Vulkan,而 iOS 没有 Vulkan,所以 DXMT 这种直接翻译到 Metal 的方案是唯一的选择。我试过用 DXMT 跑一个简单的 DirectX 11 示例程序,着色器编译大概花了 200 毫秒,之后帧率稳定在 60 帧左右,对于移动端来说可以接受。

最大的感受是:这套技术栈的每一层都在做“翻译”,而翻译必然有信息损失和性能损耗。FEX-Emu 翻译指令,Wine 翻译 API,DXMT 翻译图形调用,三层叠加之后,性能损耗是乘法关系而不是加法关系。所以优化的时候不能只盯着一层,要全局看瓶颈在哪。

另一个体会是社区的力量很重要。FEX-Emu、Wine、DXMT 都是开源项目,文档和 issue 里有很多前人的经验。遇到问题先搜 issue,大概率已经有人踩过同样的坑。比如 FEX-Emu 在 iOS 上的编译问题,GitHub 上就有专门的讨论帖,里面有人分享了 Toolchain 文件的配置。

最后说一个实际的小技巧:测试的时候从最简单的程序开始。不要一上来就跑大型游戏,先用记事本、计算器这种小程序验证整条链路是否通畅。确认 FEX-Emu 能翻译、Wine 能启动、DXMT 能渲染之后,再逐步增加复杂度。这样排查问题的时候,能快速定位是哪一层出了问题。

如果你也在折腾类似的东西,欢迎交流。这套方案目前还不成熟,但方向是对的——让不同平台的程序能互相跑起来,这件事本身就很有价值。

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

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

立即咨询