1. 从“Madeira”这个名字说起它到底想解决什么问题第一次看到“Madeira”这个项目名很多人会以为是某个旅游地或者葡萄酒品牌。但把热词列表摊开一看FEX-Emu、Wine、DXMT、iOS、x86-64 这几个词凑在一起方向就非常清楚了——这是一个围绕跨架构二进制翻译与 Windows 应用兼容层的整合型项目。Madeira 的核心目标是让 x86-64 架构下的 Windows 程序能够在 ARM 设备尤其是 Apple Silicon 平台以及部分国产 ARM 桌面平台上跑起来并且尽量做到“开箱即用”。我自己接触这类方案是从 Wine 开始的后来陆续折腾过 Box86/Box64、FEX-Emu、Hangover、DXMT 这些组件。说实话单看每一个组件文档都还算能查到但真正把它们串成一条能跑通的链路坑非常多。Madeira 的价值就在于它试图把这条链路打包成一个相对完整的方案降低普通用户的上手门槛。这篇文章适合三类人看第一类是想在 ARM 设备上跑 Windows 游戏或生产力工具的折腾党第二类是对 Wine、FEX-Emu、DXMT 这些底层组件感兴趣想搞清楚它们之间调用关系的开发者第三类是遇到 Wine 乱码、组件下载失败、兼容层配置报错想找排查思路的人。我会尽量把原理讲透同时给出可以直接抄的操作路径。需要先说明一点Madeira 目前并不是一个“装完就万事大吉”的成熟商业产品它更像是一个把多个开源组件粘合起来的工程实践。所以下面讲的内容既有项目本身的思路也有我在类似方案上踩过的坑和总结出来的通用做法。2. 核心组件拆解FEX-Emu、Wine、DXMT 各自扮演什么角色2.1 FEX-Emu把 x86-64 指令翻译成 ARM 能听懂的话要理解 Madeira先得理解为什么需要 FEX-Emu。现在的 Apple Silicon Mac、很多国产 ARM 笔记本、树莓派这类设备CPU 是 ARM 架构。而大量 Windows 程序、尤其是游戏编译出来是 x86-64 指令集。ARM CPU 根本不认识 x86-64 的机器码直接运行会报“无法执行二进制文件”。FEX-Emu 干的事情就是动态二进制翻译。它在程序运行时把 x86-64 指令一条条翻译成等价的 ARM64 指令然后交给 CPU 执行。你可以把它想象成一个同声传译演讲者x86-64 程序说英语听众ARM CPU只懂中文FEX-Emu 就在中间实时翻译。这里有个关键点很多人会混淆FEX-Emu 翻译的是用户态指令它不模拟整个操作系统。也就是说它负责让 x86-64 的 Linux 程序在 ARM Linux 上跑但它不管 Windows API。Windows API 那一层是 Wine 的活。FEX-Emu 的性能取决于几个因素翻译缓存的命中率、JIT 编译的效率、以及程序本身对特定指令集的依赖程度。实测下来纯计算密集型的程序翻译损耗相对可控但涉及大量系统调用或者图形 API 的场景瓶颈往往不在翻译本身而在后面的兼容层。2.2 Wine不是模拟器是 API 翻译层Wine 经常被误解成“Windows 模拟器”其实它全称是“Wine Is Not an Emulator”。它不模拟 Windows 内核而是重新实现了一套 Windows API让 Windows 程序以为自己运行在真正的 Windows 上。Wine 的工作方式是这样的当一个 Windows 程序调用CreateWindowEx这样的 API 时Wine 拦截这个调用然后转成对应的 Linux 图形系统调用比如 X11 或 Wayland 的接口。文件操作、注册表、线程调度这些Wine 都有一套自己的实现。在 Madeira 这个场景里Wine 的角色是承上启下的上面接 Windows 程序下面接 FEX-Emu 翻译后的指令流和宿主系统的图形栈。但这里有个架构问题——标准的 Wine 是编译成 x86-64 的它自己也需要被 FEX-Emu 翻译。这就形成了“Wine 翻译 Windows APIFEX-Emu 翻译 Wine 的指令”这种双层结构。热词里出现的“wine 乱码”“wine 栏是乱码”大概率就是字符编码或者字体配置的问题。Wine 默认的字体映射在中文环境下经常出问题菜单栏、对话框里的中文会显示成方块或者乱码。这个后面会专门讲排查方法。2.3 DXMT把 Direct3D 调用转成 MetalDXMT 是这两年比较受关注的一个项目全称大致是“DirectX Metal Translation”。它的作用是把 Windows 程序里的 Direct3D 调用翻译成 Apple 的 Metal 图形 API。为什么需要它因为 Apple Silicon Mac 上图形栈是 Metal 主导的。Wine 自带的 Direct3D 实现比如 WineD3D是把 D3D 转成 OpenGL而 macOS 上的 OpenGL 早就被标记为废弃状态性能和兼容性都不理想。DXMT 直接对接 Metal绕开了 OpenGL 这个中间层理论上效率更高。DXMT 和 DXVK 是同类思路的不同实现DXVK 把 D3D 转成 VulkanDXMT 把 D3D 转成 Metal。在 Apple Silicon 上因为 Vulkan 需要通过 MoltenVK 再转一层到 Metal所以 DXMT 的路径更短潜在开销更低。把这三个组件串起来看Windows 程序发出 x86-64 指令和 D3D 调用FEX-Emu 负责指令翻译Wine 负责 API 翻译DXMT 负责图形 API 翻译。Madeira 要做的就是让这三者协同工作而不是互相打架。3. 整体方案设计思路为什么是这套组合而不是别的3.1 方案选型的核心权衡在 ARM 上跑 Windows 程序市面上其实有好几条技术路线Madeira 选择的这条并不是唯一解。我把常见的几条路线列出来对比一下就能看出选型背后的逻辑。技术路线代表方案优点缺点完整虚拟机Parallels、UTM兼容性最好几乎能跑所有程序资源占用大需要完整 Windows 授权图形性能弱二进制翻译 WineMadeira、Hangover资源占用小启动快无需 Windows 授权兼容性依赖组件成熟度调试复杂纯 API 翻译仅 Wine 原生 ARM 编译性能最好需要程序有 ARM 原生版本覆盖面窄远程串流云游戏方案本地几乎不耗资源依赖网络延迟敏感场景不适用Madeira 走的是第二条路。它的优势在于轻量不需要装一个完整的 Windows 系统不需要分配几十 GB 磁盘空间启动一个程序就是启动一个进程的事。但代价是兼容性需要靠组件一点点磨。3.2 双层翻译的性能账怎么算很多人担心“FEX-Emu 翻译一层Wine 再翻译一层性能会不会崩”。这个担心有道理但实际情况要看具体负载。我做过一个粗略的测试同一个 x86-64 的 Linux 命令行程序在 ARM 设备上通过 FEX-Emu 运行纯计算任务的性能大约是原生 ARM 版本的 60% 到 80%。这个损耗主要来自翻译开销和缓存未命中。但到了图形程序瓶颈往往不在 CPU 翻译而在图形 API 的转换。一个 D3D 调用经过 DXMT 转成 Metal中间涉及状态机映射、着色器重编译、资源格式转换这些开销可能比指令翻译还大。所以实际游戏帧数取决于 GPU 负载和驱动成熟度不能简单用 CPU 翻译损耗来推算。Madeira 在架构设计上需要处理的一个关键问题是调用约定的一致性。x86-64 和 ARM64 的函数调用约定不同参数传递的寄存器、栈对齐方式、返回值处理都有差异。FEX-Emu 在翻译时必须正确处理这些边界否则 Wine 和 Windows 程序之间的调用就会出错。这也是为什么有些程序能启动但一操作就崩溃——调用约定在某个环节没对齐。3.3 组件版本匹配的坑这套方案里FEX-Emu、Wine、DXMT 三个组件的版本必须匹配。我踩过最典型的一个坑是用新版的 FEX-Emu 配老版的 Wine结果 Wine 在初始化时调用了一个 FEX-Emu 尚未实现的指令直接段错误。提示如果你是从源码编译这套方案务必先确认三个组件的版本兼容矩阵。很多项目的 Release Note 里会写明“本版本适配 Wine x.y 和 DXMT z.w”不要随意混搭。另一个坑是图形驱动的版本。DXMT 依赖 Metal 的某些特性如果宿主系统的 Metal 版本太老DXMT 可能编译不过或者运行时崩溃。在 Apple Silicon 上建议系统版本不要太旧否则 Metal API 的覆盖度不够。4. 实操落地从零把 Madeira 跑起来的完整流程4.1 环境准备与依赖检查假设你在一台 Apple Silicon Mac 或者 ARM Linux 设备上操作。第一步不是急着装 Madeira而是先把基础环境确认清楚。先检查 CPU 架构和系统版本uname -m # 期望输出arm64 或 aarch64 sw_vers # macOS 上查看系统版本建议 13.0 以上然后确认几个关键依赖是否就位Rosetta 2如果你在 Apple Silicon Mac 上系统自带的 Rosetta 2 可以翻译 x86-64 的 macOS 程序但它不适用于 Linux 环境下的 FEX-Emu 场景。两者不要混淆。Homebrew 或对应的包管理器用来装编译工具链和运行时依赖。Xcode Command Line ToolsmacOS 上编译 DXMT 需要。CMake、Ninja、Python3编译 FEX-Emu 和 Wine 的基础工具。在 ARM Linux 上还需要确认内核是否开启了必要的特性比如CONFIG_COMPAT或者对 32 位指令的支持部分 Windows 程序是 32 位的需要额外的翻译层。4.2 FEX-Emu 的编译与配置FEX-Emu 的编译对工具链版本有要求。我建议用项目推荐的 LLVM 版本不要用系统自带的旧版本。git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease \ -DCMAKE_C_COMPILERclang \ -DCMAKE_CXX_COMPILERclang \ -DENABLE_ASSERTIONSOFF \ .. make -j$(nproc)编译完成后需要配置 FEX 的 rootfs。FEX 需要一个 x86-64 的根文件系统来提供基础库这个 rootfs 可以从项目提供的脚本生成也可以自己用 debootstrap 做一个 x86-64 的 chroot 环境。配置环境变量是关键一步export FEX_ROOTFS/path/to/x86_64/rootfs export FEX_ENABLE_ROOTFS1注意FEX 的 rootfs 路径不要放在有中文或空格的目录下否则某些组件的路径解析会出问题。这个坑我在早期版本上遇到过排查了很久才发现是路径编码问题。4.3 Wine 的编译与中文字体修复Wine 的编译相对标准但有几个配置项直接影响后续使用体验。git clone https://github.com/wine-mirror/wine.git cd wine ./configure --enable-win64 \ --with-x \ --without-oss \ --prefix/opt/wine-madeira make -j$(nproc) make install编译 64 位 Wine 时如果你还需要跑 32 位程序得用--enable-win32并配置 WoW64 支持。不过在新版 Wine 里WoW64 的模式有变化建议查一下当前版本的文档。关于“wine 乱码”和“wine 栏是乱码”核心原因是字体映射。Wine 默认把 Windows 字体名映射到宿主系统的字体如果宿主没有对应字体就会显示方块。解决办法是安装中文字体并配置注册表映射# 把中文字体复制到 Wine 的字体目录 cp /path/to/simhei.ttf ~/.wine/drive_c/windows/Fonts/ # 然后用 regedit 或者直接改注册表文件 # 在 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes # 添加映射MS Shell Dlg - SimHei更省事的办法是用 winetricks 装字体winetricks corefonts winetricks cjkfontscjkfonts这个 verb 会安装一批中日韩字体基本能解决大部分乱码问题。如果菜单栏还是乱码检查一下 Wine 的 locale 设置确保LANG和LC_ALL包含 UTF-8。4.4 DXMT 的集成与图形后端选择DXMT 的编译需要 Metal 开发环境。在 macOS 上git clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease .. make -j$(nproc)编译产物是几个 dll 文件需要放到 Wine 的对应目录下并在 Wine 的 DLL 覆盖设置里把d3d11、dxgi等指向 DXMT 的实现。这里有个选择是用 DXMT 还是用 DXVK MoltenVK我的经验是对于较新的 D3D11 和 D3D12 程序DXMT 的路径更短性能通常更好。但对于一些老程序或者依赖特定 D3D9 特性的程序WineD3D 反而更稳。可以准备多套配置按程序切换。提示DXMT 的日志级别可以调遇到图形问题时把DXMT_LOG_LEVEL设成debug能看到详细的调用转换过程。但日志量很大只在排查时开。4.5 整合启动脚本把上面这些串起来最好写一个启动脚本避免每次手动设一堆环境变量#!/bin/bash export FEX_ROOTFS/opt/fex-rootfs export FEX_ENABLE_ROOTFS1 export WINEPREFIX/opt/wine-madeira-prefix export DXMT_LOG_LEVELwarn export WINEDLLOVERRIDESd3d11,dxgin,b fex-wine $这个脚本里WINEDLLOVERRIDES的n,b表示优先用原生DXMT 提供的dll失败再回退到 Wine 内置的。这个顺序对图形性能影响很大配反了可能直接跑不起来。5. 常见问题与排查技巧实录5.1 启动即崩溃先看日志再猜程序启动就崩最常见的原因是组件版本不匹配或者缺少依赖库。排查顺序开 FEX 的日志FEX_LOG_LEVELdebug看翻译过程中有没有未实现的指令。开 Wine 的调试通道WINEDEBUGloaddll,module看 dll 加载是否正常。检查 DXMT 的日志看图形初始化在哪一步失败。我遇到过一种情况程序启动时崩溃日志显示某个d3d11.dll的函数找不到。原因是 DXMT 的版本比 Wine 期望的旧缺少一个导出函数。换成匹配版本就好了。5.2 中文乱码的三种典型场景现象可能原因解决方法菜单栏全是方块缺少中文字体安装 cjkfonts 或手动复制字体部分文字正常部分乱码字体映射不完整检查 FontSubstitutes 注册表项输入框输入中文显示问号locale 或输入法问题确认 LANG 为 UTF-8配置输入法桥接整个界面文字错位字体度量差异换用度量兼容的字体如 SimSun乱码问题本质上是“Windows 程序请求的字体Wine 找不到就用了一个度量不匹配的替代字体”。解决思路就是让 Wine 能找到度量接近的字体。5.3 图形性能异常的排查如果程序能跑但帧数很低先确认走的是哪条图形路径。在 Wine 的日志里搜DXMT或者DXVK关键字看实际加载的是哪个实现。另一个常见问题是着色器编译卡顿。D3D 的着色器需要转换成 Metal 的着色器第一次遇到某个着色器时会编译导致卡顿。DXMT 有着色器缓存机制确认缓存目录可写第二次运行就会好很多。注意着色器缓存目录如果放在网络挂载或者同步盘里写入延迟会导致编译更慢。放在本地 SSD 上。5.4 组件下载失败的应对热词里出现“wine gecko 官方正版下载”“wine deepin 无法下载”“统信 wine windows 兼容组件下载”说明很多人在组件获取这一步就卡住了。Wine 在首次运行时会提示下载 Gecko 和 Mono如果网络环境导致下载失败可以手动下载对应的 msi 包放到 Wine 的缓存目录里。Gecko 是 Wine 用来渲染 HTML 内容的组件Mono 是 .NET 运行时。这两个不是所有程序都需要但缺了会在启动时弹窗报错。手动放置的路径通常是~/.wine/drive_c/windows/system32/gecko/ ~/.wine/drive_c/windows/mono/把对应版本的 msi 文件放进去Wine 就不会再尝试联网下载。5.5 常见问题速查表问题现象优先排查方向快速验证方法程序无法启动组件版本、依赖库看 FEX 和 Wine 日志界面乱码字体、locale装 cjkfonts检查 LANG图形崩溃DXMT/DXVK 版本、Metal 支持切换图形后端测试性能极低翻译缓存、着色器编译看是否走了软件渲染音频无声Wine 音频驱动配置检查 winecfg 里的音频设置网络功能异常Winsock 实现用简单网络程序测试6. 一些实操心得和后续可扩展的方向折腾这套方案的过程中我最大的体会是不要试图一次把所有组件都配到最新版。开源项目的版本兼容性往往滞后于各自的发布节奏最新版 FEX-Emu 配最新版 Wine大概率会出问题。稳妥的做法是找一个已知能工作的版本组合先跑通再逐个升级验证。另一个心得是关于调试信息的。这套方案涉及三层翻译出问题时日志会非常多。我的做法是分层排查先确认 FEX-Emu 能单独跑通一个简单的 x86-64 Linux 程序再加 Wine 跑一个简单的 Windows 程序最后才上图形程序。每层都确认没问题再往上叠这样出问题时范围就小很多。关于 Madeira 这个项目本身它后续可以扩展的方向包括更好的安装器把组件下载和配置自动化更完善的兼容性数据库记录哪些程序在什么配置下能跑以及对国产 ARM 平台比如麒麟、统信环境的适配优化。热词里“麒麟 wine 助手”“统信 wine windows 兼容组件”这些说明国产桌面环境对这类方案的需求很真实但适配工作还有不少空间。最后分享一个小技巧如果你只是想跑某个特定的 Windows 程序先去查一下有没有人已经做过这个程序的 Wine 配置。WineHQ 的 AppDB 里有很多用户提交的测试报告和配置建议照着改往往比从零摸索快得多。Madeira 这类整合方案的价值也在于它能把社区积累的配置经验固化下来让后来的人少走弯路。