
1. 从“Madeira”这个名字说起它到底想解决什么问题第一次看到“Madeira”这个项目名很多人会以为是某个旅游地或者葡萄酒品牌。但把标题和热搜词放在一起看——FEX-Emu、Wine、DXMT、iOS、x86-64——方向就非常清楚了这是一个围绕跨架构二进制翻译与 Windows 应用兼容层展开的项目目标是在非 x86 平台上跑起原本为 x86-64 Windows 编译的程序并且把触角伸到了移动端和桌面端两条线。我自己接触这类东西是从 Wine 开始的后来陆续折腾过 Box86/Box64、FEX-Emu、以及各种 DX 转译层。Madeira 给我的感觉是它不想只做“又一个兼容层”而是想把**指令集翻译FEX-Emu Windows API 兼容Wine 图形 API 转译DXMT/DXVK 一类**这三层打包成一个相对完整的方案让用户在 ARM 设备或者非 Windows 系统上尽量无感地运行 Windows 程序。它解决的问题很具体你手头有一台 ARM 架构的设备比如 Apple Silicon 的 Mac、或者某些 ARM Linux 开发板或者你用的是 Linux 桌面但想跑 Windows 软件传统做法要么性能损耗大要么配置极其繁琐。Madeira 试图把这条链路做短、做稳。适合谁来参考三类人一是喜欢在非 Windows 环境折腾 Windows 软件的技术爱好者二是做跨平台兼容性测试的开发者三是想了解二进制翻译和 API 转译原理的学生或工程师。哪怕你只是想搞明白“为什么 ARM 上能跑 x86 程序”这篇内容也能给你一条清晰的路径。2. 整体架构拆解三层翻译链路是怎么串起来的2.1 为什么是 FEX-Emu 而不是 QEMU跨架构运行 x86-64 程序最直接的想法是用 QEMU 做全系统模拟。但 QEMU 的问题是重——它模拟整个 CPU 和硬件环境性能损耗大而且和宿主系统的集成度低。FEX-Emu 走的是另一条路用户态指令翻译。它只翻译用户空间的 x86-64 指令到 ARM64不碰内核态因此可以直接利用宿主 Linux 的系统调用性能比全系统模拟高一个量级。Madeira 选 FEX-Emu 作为底层逻辑就在这里。FEX-Emu 的工作方式是程序启动时把 x86-64 的二进制代码块动态翻译成 ARM64 指令翻译结果会缓存起来下次执行同一段代码就不用重新翻译。这跟 JIT 编译的思路类似但针对的是指令集转换而非高级语言。注意FEX-Emu 对 x86-64 的支持相对完整但对某些老旧的 32 位 x86 程序支持有限。如果你要跑的是 32 位 Windows 软件可能需要额外配置或换用其他方案。2.2 Wine 在中间扮演什么角色FEX-Emu 解决了“指令能跑”的问题但 Windows 程序不光是指令它还调用大量 Windows API——文件操作、注册表、窗口管理、图形接口。这些 API 在 Linux 上不存在Wine 就是来填这个坑的。Wine 实现了一套 Windows API 的兼容层把 Windows 调用翻译成 POSIX 调用。Madeira 把 Wine 和 FEX-Emu 结合等于说指令层用 FEX-Emu 翻译API 层用 Wine 翻译。两层各管各的职责清晰。这里有个关键细节Wine 本身也有架构之分。在 ARM 上跑 Wine需要 Wine 的 ARM64 版本然后让它去加载 x86-64 的 Windows 程序中间由 FEX-Emu 做指令翻译。这个链路配置起来不简单Madeira 的价值就在于把这套东西预配置好了。2.3 DXMT 和图形转译的位置Windows 程序尤其是游戏大量依赖 DirectX。Linux 上跑 DirectX 程序常规做法是 DXVK把 D3D 转成 Vulkan或者 VKD3DD3D12 转 Vulkan。DXMT 是另一条路线它把 DirectX 转成 Metal——这明显是冲着 Apple 平台去的。Madeira 同时提到 DXMT说明它的目标平台很可能包括 macOS尤其是 Apple Silicon。在 Apple Silicon 上Vulkan 支持不如 Linux 原生Metal 才是第一公民。DXMT 让 DirectX 调用直接落到 Metal 上省去了 Vulkan 这一层理论上延迟更低、兼容性更好。三层链路串起来就是x86-64 Windows 程序 → FEX-Emu 翻译指令 → Wine 翻译 API → DXMT 翻译图形调用 → 宿主系统执行。每一层都有性能损耗但每一层也都在做必要的转换。Madeira 要做的就是让这三层协同工作而不是各自为政。3. 核心细节与实操要点从零搭起一条可用的链路3.1 环境准备与依赖安装假设你在 ARM64 Linux 环境比如某款 ARM 开发板或者虚拟机上操作。第一步是确认内核版本和架构uname -m # 应该输出 aarch64 uname -r # 建议 5.15 以上太老的内核对 FEX-Emu 支持不好然后安装基础依赖。FEX-Emu 需要一些开发库Wine 需要图形和音频相关的库sudo apt update sudo apt install -y build-essential cmake ninja-build git \ libsdl2-dev libvulkan-dev libgl1-mesa-dev \ libasound2-dev libpulse-dev libdbus-1-dev这些依赖里libvulkan-dev和libgl1-mesa-dev是给图形转译用的libasound2-dev和libpulse-dev是音频。别小看音频库很多 Windows 程序启动时如果找不到音频设备会直接崩溃。实操心得我试过在最小化安装的系统上直接编译 FEX-Emu结果卡在找不到libepoxy上。建议先把 Mesa 相关的开发包装全后面能省很多事。3.2 FEX-Emu 的编译与配置FEX-Emu 官方推荐用 CMake 构建。克隆代码后git clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DENABLE_ASSERTIONSOFF .. make -j$(nproc)编译完成后需要把 FEX 的根文件系统RootFS配置好。FEX 需要一个包含 x86-64 库的根文件系统来加载 Windows 程序依赖的库。官方提供了一套预编译的 RootFS也可以自己用 debootstrap 构建。配置环境变量是关键一步export FEX_ROOTFS/path/to/FEX_ROOTFS export FEX_APP_CONFIG/path/to/configFEX_ROOTFS指向 x86-64 的库文件目录FEX_APP_CONFIG是应用配置目录。这两个变量不设对后面 Wine 加载程序时会报找不到库。3.3 Wine 的交叉编译与集成在 ARM64 上跑 Wine 加载 x86-64 程序需要 Wine 本身是 ARM64 版本同时它能调用 FEX-Emu 来执行 x86-64 代码。Wine 有个特性叫“WoW64”Windows on Windows 64原本是让 64 位 Windows 跑 32 位程序这里可以类比理解ARM64 Wine 通过 FEX-Emu 跑 x86-64 程序。编译 Wine 时要注意开启 FEX 支持./configure --enable-archsarm64,x86_64 \ --with-fex/path/to/FEX \ --prefix/opt/wine-madeira make -j$(nproc) sudo make install--enable-archs指定支持的架构--with-fex指向 FEX 安装路径。编译过程比较长建议用-j拉满 CPU 核心。注意Wine 的版本选择很重要。太新的版本可能和 FEX-Emu 的接口不匹配太老的版本又缺少必要的功能。建议用 Wine 8.x 或 9.x 的稳定版配合 FEX-Emu 的最新 release。3.4 DXMT 的部署与图形配置DXMT 的部署相对独立。它本质上是一组 DLL替换掉 Windows 程序目录下的d3d11.dll、dxgi.dll等文件。在 Wine 环境下这些 DLL 需要放到 Wine 的system32目录或者程序的本地目录。# 假设 DXMT 编译产物在 build/bin 下 cp build/bin/*.dll /opt/wine-madeira/lib/wine/x86_64-windows/然后配置 Wine 的 DLL 覆盖规则让程序优先加载 DXMT 的 DLL 而不是 Wine 自带的WINEDLLOVERRIDESd3d11,dxgin,b wine program.exen,b的意思是先尝试原生native即 DXMT 的 DLL失败再回退到内置builtin即 Wine 自带的。这个顺序很重要反了的话 DXMT 就不生效了。图形后端方面DXMT 输出到 Metal所以宿主系统需要有 Metal 支持。在 macOS 上这是天然的在 Linux 上则需要通过其他方式桥接。这也是为什么 Madeira 在 Apple 平台上的完成度可能更高。4. 实操过程与核心环节实现跑通一个真实程序4.1 准备一个测试程序选一个不太复杂但有图形界面的 Windows 程序做测试。我一般用 Notepad 或者一个简单的 DirectX 示例程序。把程序放到一个目录下比如~/test-app/。先确认 FEX-Emu 能单独跑 x86-64 的 Linux 程序FEXLoader /path/to/x86_64-linux-binary如果这一步就报错说明 FEX 配置有问题先解决这个再往下走。常见错误是 RootFS 路径不对或者缺少 x86-64 的 libc。4.2 用 Wine 加载程序FEX 能跑之后用 Wine 加载 Windows 程序export WINEPREFIX~/madeira-prefix wineboot --init wine ~/test-app/program.exewineboot --init会初始化 Wine 前缀创建注册表和目录结构。第一次运行会弹出一堆安装 Mono 和 Gecko 的提示可以取消不影响基本功能。如果程序启动后界面乱码大概率是字体问题。Wine 默认字体对中文支持不好需要把 Windows 的字体文件比如simsun.ttc、msyh.ttf复制到 Wine 的字体目录cp /path/to/fonts/*.ttf ~/madeira-prefix/drive_c/windows/Fonts/然后在 Wine 注册表里设置字体替换wine regedit # 导航到 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes # 添加替换规则4.3 图形程序的额外配置如果程序用到 DirectX需要确认 DXMT 是否正确加载。可以在启动时加WINEDEBUGd3d看日志WINEDEBUGd3d wine program.exe 21 | grep -i dxmt\|d3d11日志里如果出现 DXMT 相关的初始化信息说明加载成功。如果还是走 Wine 自带的 D3D 实现检查 DLL 覆盖规则和 DLL 文件位置。性能调优方面FEX-Emu 有个FEX_TSOENABLED环境变量控制是否启用 x86 的内存序模拟。关掉它能提升性能但可能导致某些多线程程序出错export FEX_TSOENABLED0这个取舍要看具体程序。单线程程序关掉通常没问题多线程程序建议先开着稳定后再尝试关。4.4 参数计算与资源分配FEX-Emu 的翻译缓存大小可以调整。默认缓存可能不够大跑大型程序时频繁触发重新翻译export FEX_MAX_CACHE_SIZE512 # 单位 MB这个值不是越大越好。缓存太大占用内存太小又不够用。我的经验是小型工具 128MB 够用中型程序 256-512MB大型游戏 1GB 以上。可以先设 512MB观察FEX的日志里有没有缓存淘汰的记录有就往上加。CPU 核心分配也值得注意。FEX-Emu 是多线程翻译的但 Wine 和 DXMT 也有自己的线程。在核心数少的设备上限制 FEX 的翻译线程数反而能减少上下文切换export FEX_THREAD_COUNT45. 常见问题与排查技巧实录5.1 启动即崩溃先看日志再猜程序双击没反应或者秒退别急着重装。先开日志WINEDEBUGloaddll,process wine program.exe 21 | tee wine.logloaddll看 DLL 加载情况process看进程创建。常见原因有几个缺少 VC 运行库装vcrun2019、缺少 .NET装dotnet48、或者 FEX 翻译某条指令失败。FEX 翻译失败会在日志里打Unhandled instruction之类的信息。遇到这种要么换 FEX 版本要么看有没有补丁。5.2 界面乱码字体和编码双管齐下Wine 乱码是老问题了。除了复制字体还要检查 locale 设置export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8有些程序还需要在 Wine 里设置HKEY_CURRENT_USER\Control Panel\International下的Locale值为00000804中文简体。如果只是菜单栏乱码但内容正常多半是字体替换没配好。如果整个界面都是方块那是字体文件根本没加载。5.3 图形程序黑屏或花屏黑屏通常是图形后端没对接上。先确认 DXMT 的 DLL 版本和程序用的 DirectX 版本匹配。D3D11 程序用 DXMT 的d3d11.dllD3D12 程序需要d3d12.dll如果 DXMT 支持的话。花屏则可能是显存或纹理格式问题。试试关掉 FEX 的某些优化export FEX_TSOENABLED1 export FEX_MEMCPY_OPT0这些开关会降低性能但提升兼容性。定位到具体是哪个开关导致的问题后再针对性调整。5.4 常见问题速查表现象可能原因排查方向启动无反应FEX RootFS 路径错误检查FEX_ROOTFS环境变量提示缺少 DLLWine 前缀未初始化运行wineboot --init界面乱码字体缺失或 locale 不对复制字体 设置LANG图形黑屏DXMT 未加载检查WINEDLLOVERRIDES性能极低FEX 缓存太小增大FEX_MAX_CACHE_SIZE多线程崩溃TSO 模拟问题设FEX_TSOENABLED1音频无声PulseAudio 未连接检查libpulse和宿主音频服务避坑技巧每次改配置只改一个变量改完立刻测试。同时改多个变量出问题后根本不知道是哪个引起的。我在这上面浪费过好几个小时。6. 移动端与 iOS 相关的延伸思考热搜词里出现了 iOS、iOS 开发者模式、iOS 自动化这些说明 Madeira 的讨论范围不限于桌面。iOS 平台对这类兼容层的限制更严不能直接运行外部二进制不能随意加载动态库。所以 iOS 上的“运行 Windows 程序”更多是概念验证或者特定场景下的研究实际可用性远不如桌面端。但有些思路是相通的。比如 iOS 上的自动化工具需要模拟用户操作这和 Wine 模拟 Windows API 在思路上有相似之处——都是在一个受控环境里复现另一套行为。iOS 开发者模式则是为了调试和侧载和 Wine 的调试模式WINEDEBUG在目的上也有重叠。如果你在 iOS 上做类似探索重点会落在如何在不越狱的前提下加载外部代码、如何绕过沙盒限制做进程间通信、如何用 Metal 做图形转译。这些问题的答案和桌面端完全不同但底层原理——指令翻译、API 适配、图形转换——是一致的。7. 我个人在实际操作中的几点体会折腾 Madeira 这类项目最大的感受是配置比编译难排查比配置难。编译有文档配置靠试错排查靠日志和经验。我建议新手从最简单的程序开始比如一个纯 Win32 的记事本程序跑通了再上图形程序最后再碰游戏。另一个体会是版本匹配极其重要。FEX-Emu、Wine、DXMT 三个项目都在快速迭代版本不匹配导致的诡异问题占了我遇到问题的一半以上。我的做法是锁定一套已知能工作的版本组合除非有明确需求否则不轻易升级其中任何一个。最后分享一个小技巧把常用的环境变量写进一个env.sh每次开终端先 source 一下。这样不用每次手动 export也方便记录哪套配置是能用的。# env.sh export FEX_ROOTFS/opt/fex-rootfs export FEX_MAX_CACHE_SIZE512 export FEX_TSOENABLED1 export WINEPREFIX~/madeira-prefix export WINEDLLOVERRIDESd3d11,dxgin,b export LANGzh_CN.UTF-8这套东西后续还可以往容器化方向走——把整个环境打包成 Docker 镜像或者系统镜像换设备时直接部署省去重复配置的麻烦。不过那是另一个话题了。