
1. 项目缘起为什么要在 Linux 上折腾 Windows 应用兼容层第一次听到 Madeira 这个名字很多人会以为是那个葡萄牙的旅游海岛但在我们这群整天泡在 Linux 桌面环境里的折腾党眼中它指向的是另一件事——在非 Windows 系统上跑 Windows 程序的兼容方案。具体来说这个项目标题背后牵扯到的是一整套技术栈FEX-Emu 负责指令集翻译Wine 负责 API 转换DXMT 负责图形层转译最终目标是在 ARM 架构的 Linux 设备上流畅运行 x86-64 的 Windows 游戏和生产力工具。你可能会问为什么非得这么折腾直接用 Windows 不就完了道理很简单我手头有一台搭载 ARM 芯片的轻薄本续航十几个小时发热几乎可以忽略但偏偏有些老旧的行业软件、某些只在 Windows 上发行的独立游戏还有几个必须用 IE 内核的网银控件就是没有 Linux 原生版本。这时候一套靠谱的兼容层就是刚需。而 Madeira 这个项目本质上就是把这几个组件串起来形成一个可用的、可维护的、性能过得去的运行环境。这套方案适合谁如果你是一个 Linux 桌面用户手头有 ARM 设备偶尔需要跑一些 Windows 程序又不想装双系统或者开虚拟机那这套东西值得你花时间研究。如果你只是想在 x86 的 Linux 上跑 Windows 软件那其实不需要 FEX-Emu 这一层直接 Wine 或者 Proton 就够了Madeira 的核心价值在于跨架构场景。另外如果你对 iOS 开发、Xcode 打包、证书配置这些话题感兴趣后面我也会顺带聊到一些相关的工具链问题因为在实际操作中这些环节经常和兼容层环境产生交集。我写这篇东西的出发点很简单网上关于 FEX-Emu 和 Wine 组合的中文资料太碎了要么是几年前的旧帖子要么是只讲概念不讲实操真正踩过的坑没人说。我前后折腾了大概三周从完全跑不起来到能稳定运行几个常用程序中间经历了无数次崩溃、乱码、黑屏、闪退最后总算摸出了一套相对可靠的流程。下面我把整个思路、操作步骤、参数配置和避坑经验全部摊开来讲你照着做至少能少走一半弯路。2. 整体架构拆解FEX-Emu、Wine、DXMT 各自扮演什么角色2.1 三层翻译机制的分工与协作要理解 Madeira 这套方案你得先搞清楚这三个组件各自干什么活。我用一个生活化的类比来解释假设你是一个只懂中文的人要和一个只懂葡萄牙语的人做生意中间需要翻译。FEX-Emu 就是那个把葡萄牙语x86-64 指令转成中文ARM64 指令的同声传译它负责最底层的 CPU 指令翻译。Wine 则是业务顾问它不翻译语言但它知道 Windows 程序的办事流程——比如怎么调用系统 API、怎么读写注册表、怎么加载 DLL——然后把这些流程映射到 Linux 对应的机制上。DXMT 则是图形方面的专家专门处理 Direct3D 到 Metal 的转换让 Windows 游戏的画面能在 Linux 的图形栈上正常渲染。这三者的协作顺序是这样的当你启动一个 Windows 程序时Wine 首先接管进程创建加载 PE 格式的可执行文件然后程序里的 x86-64 指令被 FEX-Emu 动态翻译成 ARM64 指令交给 CPU 执行。如果程序调用了 Direct3D 接口Wine 会把调用转给 DXMTDXMT 再翻译成 Metal 或 Vulkan 调用最终由 GPU 渲染出画面。整个链路里任何一环出问题你看到的可能就是黑屏、闪退或者满屏乱码。为什么选择 FEX-Emu 而不是 QEMU 用户态模拟这是很多人会问的问题。QEMU 的用户态模拟虽然通用性强但性能损耗太大尤其是涉及大量系统调用和图形操作时帧率直接掉到个位数。FEX-Emu 的设计目标是够用就好的指令翻译它只翻译实际执行到的代码块并且做了大量缓存优化对于大多数 Windows 程序来说性能损耗可以控制在 20% 到 40% 之间部分场景下甚至更低。当然这个数字取决于程序本身的指令密集程度像视频编码这种纯计算任务损耗会更高一些。2.2 为什么 Wine 版本选择如此关键Wine 的版本选择直接决定了你的兼容层能不能跑起来。我试过从 Wine 6.x 到 Wine 9.x 的多个版本最后发现对于 ARM FEX-Emu 的组合Wine 8.x 系列的稳定性明显好于 9.x 早期版本。原因在于 Wine 9.x 引入了一些新的 WoW64 模式改动这些改动在 x86 原生环境下没问题但在跨架构翻译场景下某些系统调用的处理路径会变得异常复杂导致程序启动时卡死或者报出莫名其妙的错误。具体来说我推荐使用 Wine 8.0.2 或者 Wine 8.21 这两个版本作为基础。如果你用的是 Deepin 或者统信 UOS系统自带的 Wine 版本可能比较老需要手动替换。麒麟系统的用户可能会遇到麒麟 wine 助手这个工具它本质上是一个 Wine 的图形化封装方便你安装和配置 Windows 程序但底层还是调用系统的 Wine 运行时。如果你发现麒麟 wine 助手下载后无法正常使用大概率是因为它依赖的 Wine 版本和 FEX-Emu 不兼容这时候你需要手动指定 Wine 的路径。还有一个常见问题是 Wine 乱码。这个问题的根源通常有两个一是缺少中文字体二是 locale 设置不正确。Wine 默认使用自带的字体库但这些字体对中文的支持很差你需要把系统的中文字体链接到 Wine 的字体目录或者在 Wine 配置里指定字体替换规则。locale 方面确保LANG和LC_ALL设置为zh_CN.UTF-8否则某些程序的中文界面会显示成方块或者问号。2.3 DXMT 的定位与适用边界DXMT 是一个相对较新的项目它的全称是 DirectX Metal Translation目标是把 Direct3D 11 和部分 Direct3D 12 的调用翻译成 Metal 调用。在 ARM Linux 设备上Metal 并不是原生可用的所以 DXMT 实际上是通过 MoltenVK 或者类似的中间层先把 D3D 转成 Vulkan再转成 Metal。这听起来很绕但实际效果比直接用 WineD3D 要好不少尤其是在苹果 M 系列芯片的 Linux 虚拟机上DXMT 的帧率表现明显优于传统的 WineD3D 方案。不过 DXMT 并不是万能的。它目前对 Direct3D 9 的支持比较有限很多老游戏还是得靠 WineD3D 来跑。另外DXMT 对 Vulkan 驱动的版本有要求如果你的系统 Vulkan 驱动太老DXMT 会直接报错退出。我在实际操作中遇到过一次 DXMT 初始化失败的问题排查了半天才发现是 Mesa 版本太低升级到 Mesa 23.0 以上就解决了。所以如果你打算用 DXMT先确认你的图形栈是否满足最低要求。3. 环境搭建实操从零开始配置 Madeira 运行环境3.1 系统准备与依赖安装我用的基础系统是 Ubuntu 22.04 ARM64 版本其他发行版的操作逻辑类似只是包管理命令不同。首先确保你的系统已经更新到最新状态然后安装必要的编译工具和依赖库。这一步看起来简单但很多人卡在依赖缺失上尤其是 32 位库的支持因为很多 Windows 程序是 32 位的Wine 需要对应的 32 位运行库。sudo dpkg --add-architecture armhf sudo apt update sudo apt install -y build-essential cmake git python3 python3-pip sudo apt install -y libc6:armhf libstdc6:armhf libgcc-s1:armhf sudo apt install -y libvulkan1 libvulkan-dev vulkan-tools sudo apt install -y libgl1-mesa-dri libglx-mesa0 mesa-vulkan-drivers sudo apt install -y fontconfig fonts-wqy-zenhei fonts-wqy-microhei这里有几个关键点需要注意。第一dpkg --add-architecture armhf是为了让系统能安装 32 位 ARM 库虽然 FEX-Emu 主要处理 x86-64 指令但 Wine 本身在 ARM 上运行时部分组件仍然是 32 位的。第二字体包一定要装否则后面 Wine 里的中文全是乱码你连菜单都看不懂。第三Vulkan 驱动和 Mesa 版本要尽量新DXMT 对这两个依赖比较敏感。安装完依赖后重启系统让所有更改生效。然后验证一下 Vulkan 是否正常工作vulkaninfo | grep GPU id如果能看到你的 GPU 信息说明 Vulkan 栈没问题。如果报错先解决 Vulkan 问题再往下走否则后面 DXMT 肯定跑不起来。3.2 FEX-Emu 的编译与配置FEX-Emu 的安装有两种方式直接下载预编译的二进制包或者从源码编译。我建议第一次折腾的话用预编译包省时间。但如果你需要最新的特性或者修复那就得自己编译。预编译包的下载地址在 FEX-Emu 的官方发布页面上选择FEX-Emu-版本号-aarch64.tar.gz这个包。wget https://github.com/FEX-Emu/FEX/releases/download/FEX-2307/FEX-2307-aarch64.tar.gz tar -xzf FEX-2307-aarch64.tar.gz cd FEX-2307 sudo ./install.sh安装完成后你需要配置 FEX-Emu 的根文件系统。FEX-Emu 需要一个包含 x86-64 库和基本工具的根文件系统镜像这个镜像可以从项目提供的脚本生成也可以直接下载现成的。我推荐用项目自带的RootFS工具生成这样能确保库版本匹配。git clone https://github.com/FEX-Emu/RootFS.git cd RootFS ./generate_rootfs.sh这个过程会下载一个 Ubuntu 20.04 x86-64 的基础镜像然后解压到指定目录。生成完成后你需要设置环境变量让 FEX-Emu 知道根文件系统的位置export FEX_ROOTFS/path/to/your/rootfs export FEX_APP_CONFIG/path/to/your/fex-config.jsonFEX-Emu 的配置文件里可以调整很多参数比如是否启用 JIT 缓存、缓存大小、是否开启多线程翻译等。对于大多数场景默认配置就够用了但如果你发现某些程序启动特别慢可以尝试增大 JIT 缓存{ Config: { RootFS: /path/to/rootfs, JITCacheSize: 268435456, Multiblock: true, SMCChecks: mtrack } }JITCacheSize的单位是字节我这里设的是 256MB对于大多数程序足够了。Multiblock开启后会一次翻译多个基本块能提升性能但会增加内存占用。SMCChecks是自修改代码检测策略mtrack模式在兼容性和性能之间比较平衡。3.3 Wine 的编译与 FEX-Emu 集成Wine 的编译是整个流程里最耗时的环节在 ARM 设备上编译 Wine 可能需要一两个小时。如果你不想等可以找现成的 ARM64 Wine 包但要注意版本兼容性。我选择的是从源码编译 Wine 8.0.2因为这样可以精确控制编译选项确保和 FEX-Emu 的集成没有问题。wget https://dl.winehq.org/wine/source/8.0/wine-8.0.2.tar.xz tar -xf wine-8.0.2.tar.xz cd wine-8.0.2 mkdir build cd build ../configure --enable-win64 --with-vulkan --without-x make -j$(nproc) sudo make install这里--enable-win64表示只编译 64 位版本因为 FEX-Emu 主要处理 64 位 x86-64 指令。--with-vulkan开启 Vulkan 支持后面 DXMT 需要用到。--without-x是因为我们不需要 X11 的原生支持Wine 会通过 Wayland 或者 XWayland 来显示窗口。编译完成后你需要把 Wine 的二进制文件和 FEX-Emu 关联起来。具体做法是创建一个包装脚本让 Wine 启动时自动调用 FEX-Emu 来翻译 x86-64 指令#!/bin/bash export FEX_ROOTFS/path/to/rootfs export WINEPREFIX/path/to/your/wineprefix exec /usr/local/bin/FEXInterpreter /usr/local/bin/wine64 $把这个脚本保存为wine-fex放到/usr/local/bin目录下然后赋予执行权限。之后你运行 Windows 程序时用wine-fex代替wine就行了。3.4 DXMT 的安装与图形层配置DXMT 的安装相对简单因为它主要以 Wine 的 DLL 形式提供。你需要从 DXMT 的发布页面下载对应的.dll文件然后放到 Wine 的system32和syswow64目录下。具体来说d3d11.dll、dxgi.dll、d3d10core.dll这几个文件是核心必须替换。cd $WINEPREFIX/drive_c/windows/system32 wget https://github.com/3Shain/dxmt/releases/download/v0.1.0/d3d11.dll wget https://github.com/3Shain/dxmt/releases/download/v0.1.0/dxgi.dll wget https://github.com/3Shain/dxmt/releases/download/v0.1.0/d3d10core.dll替换完成后你需要在 Wine 的注册表里设置 DLL 覆盖让 Wine 优先加载 DXMT 的 DLL 而不是自带的 WineD3Dwine reg add HKCU\Software\Wine\DllOverrides /v d3d11 /t REG_SZ /d native /f wine reg add HKCU\Software\Wine\DllOverrides /v dxgi /t REG_SZ /d native /f wine reg add HKCU\Software\Wine\DllOverrides /v d3d10core /t REG_SZ /d native /f这里native表示优先使用原生 DLL也就是我们替换进去的 DXMT 版本。如果你发现某些程序用 DXMT 跑不起来可以改回builtin让 Wine 使用自带的 WineD3D。4. 常见问题排查与性能调优实录4.1 Wine 乱码问题的三种解法Wine 乱码是我遇到的最频繁的问题表现形式有好几种菜单文字变成方块、对话框里的中文显示为问号、某些程序的界面直接空白。经过反复排查我总结出三种解法按优先级排列。第一种是字体链接。Wine 默认的字体目录是$WINEPREFIX/drive_c/windows/Fonts你需要把系统的中文字体复制或者链接过去cd $WINEPREFIX/drive_c/windows/Fonts ln -s /usr/share/fonts/truetype/wqy/wqy-zenhei.ttc ./wqy-zenhei.ttc ln -s /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ./wqy-microhei.ttc然后在 Wine 的注册表里设置字体替换wine reg add HKCU\Software\Wine\Fonts\Replacements /v MS Shell Dlg /t REG_SZ /d WenQuanYi Zen Hei /f wine reg add HKCU\Software\Wine\Fonts\Replacements /v MS Shell Dlg 2 /t REG_SZ /d WenQuanYi Zen Hei /f wine reg add HKCU\Software\Wine\Fonts\Replacements /v SimSun /t REG_SZ /d WenQuanYi Zen Hei /f第二种是 locale 设置。确保你的系统 locale 是zh_CN.UTF-8并且在启动 Wine 时显式指定export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8第三种是安装wine-gecko和wine-mono。有些程序的界面依赖 Gecko 渲染引擎如果缺少这个组件界面会显示异常。你可以从 Wine 的官方仓库下载wine-gecko的安装包然后手动安装到 Wine 的mshtml目录下。注意要下载和你的 Wine 版本匹配的 gecko 包版本不匹配会导致安装失败。4.2 程序启动崩溃与日志分析程序启动崩溃的原因很多最常见的是缺少 DLL、指令翻译错误、或者图形初始化失败。排查这类问题的第一步是开启 Wine 的调试日志WINEDEBUGloaddll,seh wine-fex your_program.exe 21 | tee wine.logloaddll会记录所有 DLL 的加载过程seh会记录异常处理信息。如果程序在加载某个 DLL 时崩溃日志里会显示最后加载的是哪个 DLL你可以据此判断是缺少依赖还是 DLL 版本不对。如果日志里出现大量FEXCore相关的错误那说明是指令翻译出了问题。这时候你可以尝试调整 FEX-Emu 的配置比如关闭Multiblock或者切换SMCChecks模式。有些程序使用了自修改代码mtrack模式可能无法正确处理换成full模式会降低性能但提高兼容性。还有一种崩溃是图形初始化失败导致的表现是程序启动后立即退出日志里能看到DXGI或者D3D11相关的错误。这时候你需要检查 DXMT 的 DLL 是否正确替换以及 Vulkan 驱动是否正常工作。可以先用vulkaninfo确认 Vulkan 可用然后再检查 DXMT 的日志输出。4.3 性能调优从能跑到跑得顺让程序跑起来只是第一步跑得顺畅才是目标。我在性能调优上花了不少时间总结下来有几个关键点。首先是 JIT 缓存的预热。FEX-Emu 第一次运行某个程序时需要动态翻译大量指令这个过程比较慢。但翻译结果会被缓存下来第二次启动同一程序时就会快很多。你可以通过多次启动程序来预热缓存或者直接使用 FEX-Emu 的--profile模式生成缓存文件。其次是图形层的选择。DXMT 在大多数场景下比 WineD3D 快但并非绝对。对于 Direct3D 9 的老游戏WineD3D 配合csmt模式可能更流畅。你可以通过设置环境变量来切换export WINEDLLOVERRIDESd3d9b # 使用 WineD3D export WINE_CSMT1 # 开启命令流多线程第三是 CPU 调度策略。在 ARM 设备上CPU 核心有大有小把 Wine 进程绑定到大核上能显著提升性能taskset -c 4-7 wine-fex your_program.exe这里的4-7是大核的 CPU 编号具体编号取决于你的设备。你可以用lscpu查看核心分布然后根据实际情况调整。4.4 常见问题速查表问题现象可能原因解决方法程序启动后立即闪退缺少 32 位运行库安装libc6:armhf等 32 位库界面中文显示为方块缺少中文字体链接字体并设置注册表替换图形界面黑屏DXMT 初始化失败检查 Vulkan 驱动和 DXMT DLL程序运行极慢JIT 缓存未预热多次启动程序预热缓存音频无声PulseAudio 未配置安装libpulse并设置PULSE_SERVER窗口无法调整大小Wayland 兼容问题切换到 XWayland 或设置WINEDLLOVERRIDES程序报错缺少 DLL依赖库未安装用winetricks安装对应运行库键盘输入无响应输入法冲突关闭系统输入法或设置XMODIFIERS这个表格里的问题都是我实际遇到过的解决方法也经过验证。但要注意不同的硬件和系统组合可能会有差异如果某个方法不管用可以尝试其他替代方案。5. 跨平台开发工具链的交叉参考5.1 iOS 开发环境与兼容层的交集虽然 Madeira 项目主要关注 Linux 上的 Windows 兼容层但在实际操作中我经常需要同时处理 iOS 开发相关的任务。比如我需要在 Linux 上管理 iOS 应用的证书和描述文件或者用自动化脚本处理 Xcode 打包流程。这些任务和 Wine 环境看似无关但实际上有一些交叉点。举个例子Xcode 打包 iOS 应用时如果突然变得很慢原因可能是证书链验证超时或者钥匙串访问冲突。在 Linux 上你可以用openssl命令手动验证证书链排查是哪个环节出了问题。另外iOS 开发者模式的开启流程在不同系统版本上略有差异iOS 26.3.1 的开发者模式入口在设置 - 隐私与安全性里面需要连接 Xcode 或者使用第三方工具才能激活。还有一个常见需求是 iOS 应用上架。从证书配置到最终提交 App Store Connect整个流程涉及多个步骤生成证书签名请求、创建 App ID、配置描述文件、归档应用、上传二进制文件。每一步都有坑比如证书类型选错、描述文件过期、二进制文件包含不支持的架构等。我建议用fastlane或者xcodebuild命令行工具来自动化这些步骤减少手动操作的出错概率。5.2 移动端 WebView 与兼容性调试在移动端开发中WebView 的兼容性问题经常让人头疼。比如抖音的 iOS WebView 不能自动播放视频这是因为 iOS 的 WebKit 对自动播放有严格限制必须由用户手势触发。解决方法是在视频元素上添加playsinline和muted属性然后在用户点击事件里调用play()方法。另一个常见问题是 iOS 浏览器唤起安装 App。有些网页会通过自定义 URL Scheme 或者 Universal Links 来唤起 App但如果 App 没有安装浏览器会报错。更好的做法是提供一个降级方案比如跳转到 App Store 下载页或者显示一个提示页面引导用户手动下载。这里要注意任何下载链接都必须来自官方渠道不要使用来路不明的第三方分发平台。如果你需要在 iOS 上调试网络请求可以用Fiddler或者Charles作为代理工具。iOS 连接 Fiddler 的步骤是在 Wi-Fi 设置里配置 HTTP 代理指向运行 Fiddler 的电脑 IP 和端口然后在 iOS 上安装并信任 Fiddler 的根证书。这个过程在 iOS 10 以后需要在设置 - 通用 - 关于本机 - 证书信任设置里手动开启信任开关否则 HTTPS 请求会失败。5.3 自动化脚本与效率工具无论是管理 Wine 环境还是处理 iOS 开发任务自动化脚本都能大幅提升效率。我常用的几个工具包括winetricks用于安装 Windows 运行库、fastlane用于 iOS 打包和发布、xcrun用于管理 Xcode 工具链。这些工具的共同点是都可以通过命令行调用方便集成到 CI/CD 流程中。以winetricks为例安装常用运行库只需要一行命令winetricks -q vcrun2019 dotnet48 corefonts-q表示静默安装不弹出图形界面。vcrun2019是 Visual C 2019 运行库很多现代 Windows 程序依赖它。dotnet48是 .NET Framework 4.8一些老的管理软件需要。corefonts是微软核心字体安装后能改善界面显示效果。对于 iOS 开发fastlane的gym和deliver命令可以自动化打包和上传流程fastlane gym --scheme MyApp --export_method app-store fastlane deliver --ipa MyApp.ipa --skip_screenshots这些命令看起来简单但背后的配置项很多比如证书管理、描述文件匹配、版本号递增等。我建议先用fastlane init生成配置文件然后根据项目需求逐步调整。6. 实际运行效果与个人经验总结经过几周的折腾我的 ARM Linux 设备现在能稳定运行几个常用的 Windows 程序一个老版本的行业管理软件、两个独立游戏、还有一个用于处理特定格式文件的工具。启动时间从最初的几十秒缩短到现在的几秒钟图形界面基本流畅音频正常中文显示没有问题。当然不是所有程序都能完美运行有些依赖特定硬件驱动的软件仍然无法使用有些游戏的帧率只有原生 Windows 的一半左右。我个人在实际操作中的体会是这套方案的门槛主要不在技术本身而在于耐心和排查问题的能力。你可能会遇到各种奇怪的报错有些能在日志里找到线索有些只能靠反复尝试。我的建议是每次只改一个变量改完立即测试确认有效再继续下一步。另外保持系统干净很重要不要在同一个 Wine 容器里装太多程序否则依赖冲突会让你痛不欲生。最后再分享一个小技巧如果你发现某个程序在 FEX-Emu 下运行不稳定可以尝试用box64作为替代方案。box64是另一个 x86-64 到 ARM64 的翻译层虽然性能略逊于 FEX-Emu但在某些场景下兼容性更好。两个方案可以共存通过不同的启动脚本切换这样你就有了一个备选方案不至于在一棵树上吊死。