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)或者 VKD3D(D3D12 转 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_TYPE=Release -DENABLE_ASSERTIONS=OFF .. 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-archs=arm64,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 自带的:
WINEDLLOVERRIDES="d3d11,dxgi=n,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 是否正确加载。可以在启动时加WINEDEBUG=+d3d看日志:
WINEDEBUG=+d3d wine program.exe 2>&1 | grep -i "dxmt\|d3d11"日志里如果出现 DXMT 相关的初始化信息,说明加载成功。如果还是走 Wine 自带的 D3D 实现,检查 DLL 覆盖规则和 DLL 文件位置。
性能调优方面,FEX-Emu 有个FEX_TSOENABLED环境变量,控制是否启用 x86 的内存序模拟。关掉它能提升性能,但可能导致某些多线程程序出错:
export FEX_TSOENABLED=0这个取舍要看具体程序。单线程程序关掉通常没问题,多线程程序建议先开着,稳定后再尝试关。
4.4 参数计算与资源分配
FEX-Emu 的翻译缓存大小可以调整。默认缓存可能不够大,跑大型程序时频繁触发重新翻译:
export FEX_MAX_CACHE_SIZE=512 # 单位 MB这个值不是越大越好。缓存太大占用内存,太小又不够用。我的经验是:小型工具 128MB 够用,中型程序 256-512MB,大型游戏 1GB 以上。可以先设 512MB,观察FEX的日志里有没有缓存淘汰的记录,有就往上加。
CPU 核心分配也值得注意。FEX-Emu 是多线程翻译的,但 Wine 和 DXMT 也有自己的线程。在核心数少的设备上,限制 FEX 的翻译线程数反而能减少上下文切换:
export FEX_THREAD_COUNT=45. 常见问题与排查技巧实录
5.1 启动即崩溃:先看日志再猜
程序双击没反应或者秒退,别急着重装。先开日志:
WINEDEBUG=+loaddll,+process wine program.exe 2>&1 | tee wine.log+loaddll看 DLL 加载情况,+process看进程创建。常见原因有几个:缺少 VC++ 运行库(装vcrun2019)、缺少 .NET(装dotnet48)、或者 FEX 翻译某条指令失败。
FEX 翻译失败会在日志里打Unhandled instruction之类的信息。遇到这种,要么换 FEX 版本,要么看有没有补丁。
5.2 界面乱码:字体和编码双管齐下
Wine 乱码是老问题了。除了复制字体,还要检查 locale 设置:
export LANG=zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8有些程序还需要在 Wine 里设置HKEY_CURRENT_USER\Control Panel\International下的Locale值为00000804(中文简体)。
如果只是菜单栏乱码但内容正常,多半是字体替换没配好。如果整个界面都是方块,那是字体文件根本没加载。
5.3 图形程序黑屏或花屏
黑屏通常是图形后端没对接上。先确认 DXMT 的 DLL 版本和程序用的 DirectX 版本匹配。D3D11 程序用 DXMT 的d3d11.dll,D3D12 程序需要d3d12.dll(如果 DXMT 支持的话)。
花屏则可能是显存或纹理格式问题。试试关掉 FEX 的某些优化:
export FEX_TSOENABLED=1 export FEX_MEMCPY_OPT=0这些开关会降低性能但提升兼容性。定位到具体是哪个开关导致的问题后,再针对性调整。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 启动无反应 | FEX RootFS 路径错误 | 检查FEX_ROOTFS环境变量 |
| 提示缺少 DLL | Wine 前缀未初始化 | 运行wineboot --init |
| 界面乱码 | 字体缺失或 locale 不对 | 复制字体 + 设置LANG |
| 图形黑屏 | DXMT 未加载 | 检查WINEDLLOVERRIDES |
| 性能极低 | FEX 缓存太小 | 增大FEX_MAX_CACHE_SIZE |
| 多线程崩溃 | TSO 模拟问题 | 设FEX_TSOENABLED=1 |
| 音频无声 | 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_SIZE=512 export FEX_TSOENABLED=1 export WINEPREFIX=~/madeira-prefix export WINEDLLOVERRIDES="d3d11,dxgi=n,b" export LANG=zh_CN.UTF-8这套东西后续还可以往容器化方向走——把整个环境打包成 Docker 镜像或者系统镜像,换设备时直接部署,省去重复配置的麻烦。不过那是另一个话题了。