- Introduction
- Configuring a 3G/1G VM split (32-bit distros only)
- Installing Wine
- Running Windows software
- Piecing it all together
- Running Wine on ARM64
- We're done
- Credits
I can't imagine working on a system on which Notepad++ and a bunch of other useful Windows tools aren't available. So when I started playing around with a Raspberry Pi to see if I can use it as a spec'd down desktop PC I knew I have to get Notepad++ to run or else forget about ARM entirely. Of course, emulation springs to mind immediately, but the poor Pi's already resource constrained enough... no, emulation isn't an option and this directly leads us to Wine. Yes, yes, meanwhile even the dead are aware that you could run Skype and TeamViewer with Eltech's ExaGear Desktop, but it's also driven by an x86 emulator under the hood and—as a bonus—is closed source. Actually, Windows 10 itself runs on the Pi, but I really dislike that OS despite otherwise being a Windows fanboy.
The concept of running Wine on ARM devices isn't new. StarCraft (as well as Diablo 1 & 2) is playable on ARM through Wine thanks to the insanely hard work of the Lithuanian hacker notaz. But while working on my project I couldn't find anything but scattered bits of information and sometimes there was even nothing at all. So this guide will walk you trough the steps required to execute Windows software with Wine on ARM devices running *nix. I specifically focus on a Raspberry Pi 3B+ running Raspbian and here's a screenshot of Notepad++ running there.
Unfortunately, we're confronted with compiling a custom kernel right at the beginning of our journey. Sweet marmalade... On 32-bit distros like Raspbian, Wine requires a 3G/1G VM split between user and kernel mode code. This is kinda baffling, because I always thought Windows uses a 2G/2G split unless you configure something else in boot.ini
, but I'm likely missing some context to see the deeper picture. For one reason or another, Raspbian's default kernel uses a 2G/2G split so Wine will refuse to start. Oh well, complaining doesn't help, let's just build the kernel. Btw, to find out the VM split of your system you could try
sudo modprobe configs
zcat /proc/config.gz | grep CONFIG_VMSPLIT
Should this work and you can spot CONFIG_VMSPLIT_2G=y
in the output, the situation is grave and action needs to be taken. If the configs
module isn't available and you believe your distro's split is OK, you can proceed with the Wine setup and come back to this step if Wine complains of an incorrect split.
To compile the kernel follow this guide. You'll absolutely want to cross compile, because a) the Pi's more sluggish than Boomer legislation and b) you'll condemn the SD card to gruesome death. Time to resurrect a notebook and install Ubuntu, I guess. Before compiling, configure the 3G/1G split. You can do this either through menuconfig or just edit the relevant lines of .config
to
CONFIG_VMSPLIT_3G=y
# CONFIG_VMSPLIT_3G_OPT is not set
# CONFIG_VMSPLIT_2G is not set
# CONFIG_VMSPLIT_1G is not set
Once the build is complete, copy the kernel, overlays and modules and reboot. Backup your old kernel to prevent disaster. My system booted up without any issues and I sincerely hope this will also be the case for you.
You have the usual options of installing through your distro's package manager or building Wine yourself. Surprisingly enough, Raspbian's repository provides a package, but it dates back to ancient Egypt's first pharaoh dynasties. See for yourself
sudo apt-get install wine
winecfg
wine --version
If winecfg
exits with the following error
Warning: memory above 0x80000000 doesn't seem to be accessible.
Wine requires a 3G/1G user/kernel memory split to work properly.
go back to the previous step and compile a kernel with a 3G/1G VM split. Otherwise you'll see the winecfg
GUI and thus the proof that Wine can run on ARM. The output of running wine --version
is currently wine-1.8.7 (Raspbian 1.8.7-2)
which is a maintenance release from early 2017. I decided to build Wine myself, because there's been a huge number of changes to Wine since then. If you prefer sticking with this old version, proceed with building your Windows software for ARM or – in case you already have an ARM build – with running it.
Uninstall Wine through the PM in case you've previously installed it. I recommend to build Wine on a hard drive or USB stick because it isn't healthy for the SD card. Cross compilation is not really feasible, because Wine has many dependencies, although it's possible.
Get Wine's source from somewhere, preferrably from the main Git repository, checkout the latest release and configure the build. To build Wine 3.19, for example, execute
git clone git://source.winehq.org/git/wine.git
cd wine
git checkout tags/wine-3.19
./configure --prefix="$HOME/.local" CFLAGS="-O2 -mcpu=cortex-a53 -mfpu=neon-fp-armv8"
I prefer to install Wine to my .local
directory as not to clutter /usr/local
and wreck havoc while experimenting with multiple versions. You'll want to adjust -mcpu
and -mfpu
to your particular hardware, but make sure that this code is compiled. To check if this is the case, list all preprocessor defines for this particular CPU/FPU combination.
gcc -mcpu=cortex-a53 -mfpu=neon-fp-armv8 -dM -E - < /dev/null | grep "__ARM_ARCH_8A__\|__ARM_ARCH_7A__"
The configuration script will most likely fail initially, because you're missing dependencies. Instead of installing them through hard manual labour, let's try sudo apt-get build-dep wine
to install as many as possible automatically. If you get the error E: You must put some 'source' URIs in your sources.list
open the file /etc/apt/sources.list
, uncomment the line beginning with deb-src
, save, execute sudo apt-get update
and try again. If no line in /etc/apt/sources.list
begins with dep-src
you're absolutely... yeah.
Assuming we've installed as many dependencies as we could, repeat the configuration and build Wine
make -j<number of cores you can build with without transforming your device into plasma>
make install
Booting Raspbian without a GUI, I compiled Wine with -j3
without raising the temperature too much. Use heatsinks and remove the lid of your enclosure to give the device a chance to breathe fresh air. It's up to you whether to execute make install
; Wine also runs from the build directory, but the usability's greatly reduced. In case you decide to install, open ~/.profile
and add
PATH="$HOME/.local/bin:$PATH"
WARNING Make sure you have SSH access to the system through Ethernet before editing ~/.profile
, because you could easily get stuck in a login loop. It happened to me and it was especially bad, because my home partition was encrypted and I've had disabled all networking... THANKS, OBAMA. The next time you login, Wine will be in your PATH
along with all the handy tools that are part of it. Verify stuff's OK with wine --version
and try running regedit
or notepad
.
This is where your true nature as a hacker will reveal itself. Any Windows program you want to use on the Raspberry Pi will have to be compiled for ARM. In case you didn't know—but it was my understanding that everyone had heard—Windows supports ARM devices through, initially, Windows RT and, currently, Windows 10. Of course, you'll have a hard time finding your favorite program precompiled for ARM so you'll probably have to do it yourself.
I want to believe you're in possession of the source code, I really do, because otherwise things will turn violent. One possible way involves disassembling or even decompiling the program and then recompiling it for ARM. In practice this will be a huge effort. Just consider it took notaz, the previously mentioned hacker, months to statically recompile StarCraft. Another clever solution which involves some emulation was demonstrated by André Hentschel and Stefan Dösinger, a duo of contributors to Wine. Their project Hangover employs QEMU to dynamically translate the x86/x64 binary into currently ARM64. During the emulated execution, calls to the Windows API are intercepted and forwarded to a native Wine installation. Naturally, this improves the performance significantly. Hangover is able to run Notepad++ and even a couple of 3D games. Talk about relentless hacking...
But let's stick to optimism and assume you have the source. Here are your options.
Winelib is a set of tools included with Wine allowing you to compile Windows software against Wine's implementation of the Win32 API directly on the *nix system. In fact, if you pay attention during the compilation of Wine, you'll notice that a program called winegcc
is invoked to link the DLLs and programs such as winecfg
. There's also wineg++
which is a symlink to winegcc
, wrc
and even widl
. winegcc
is an elaborate wrapper around GCC, actually, and doesn't generate Windows but *nix executables which Wine runs just as fine, but you can't run winegcc
compiled software back on Windows. Overall, Winelib does a really convincing job at emulating MinGW. If you want to try Winelib on an existing project, you should know about Winemaker, a tool which helps you with some commonly encountered issue like fixing upper and lowercase discrepancies, converting line endings and putting together a Makefile.
Let's see a very quick example of Winelib in action. Here's a simple program
/*
* greeter.c
*
* A humble demonstrator.
*/
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int CALLBACK WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DWORD tid;
TCHAR msg[256];
tid = GetCurrentThreadId();
/* This is a workaround for Wine bug #45913:
*
* https://bugs.winehq.org/show_bug.cgi?id=45913
*
* Normally we'd only use 1 call to _sntprintf.
*/
#ifdef _UNICODE
_snwprintf(msg,
sizeof msg / sizeof msg[0],
L"Your Majesty's thread ID is: %lu",
unsigned long tid);
#else
_snprintf(msg,
sizeof msg / sizeof msg[0],
"Your Majesty's thread ID is: %lu",
(unsigned long) tid);
#endif
MessageBox(NULL, msg, _T("Hello!"), MB_OK | MB_ICONEXCLAMATION);
return 0;
}
Let's compile both a Unicode and a non-Unicode version
winegcc -mwindows -mno-cygwin -o greeter.exe greeter.c
winegcc -mwindows -mno-cygwin -o greeter-unicode.exe -DUNICODE -D_UNICODE greeter.c
Launch the programs by typing wine greeter.exe.so
followed by wine greeter-unicode.exe.so
; they should behave identically. Notice we ran the .exe.so
instead of .exe
. This is due to winegcc
generating ELF binaries. The .exe
files are shell scripts wrapping the Wine calls you've just made so you can simply execute ./greeter.exe
and ./greeter-unicode.exe
instead.
In my experience, Winelib is excellent at compiling C source, but it has some rough edges regarding C++. Here's an example. As you may know, Windows and *nix aren't on the same page with respect to wchar_t
. Windows stores UTF-16 code points in wchar_t
while on *nix it might be something else; Linux and Mac seem to prefer UTF-32. So to build Unicode applications with Winelib, one must use Wine's implementation of msvcrt. But the headers of Wine's msvcrt are incompatible with the C++ standard library provided by GCC or something along these lines. Anyway, I couldn't get Notepad++ to compile with Winelib, but that's OK, because it currently doesn't compile with MinGW either.
While being hopelessly stuck on building Notepad++ for ARM, I received a tip from the previously mentioned Wine contributor André Hentschel regarding this project. It was started by Martin Storsjö and lets you build an ARM supporting MinGW toolchain using LLVM. The project includes a Dockerfile to build an image containing the toolchain and there are even prebuilt images for the impatient. Sadly, so far I haven't had the opportunity to try it out, but it's the next thing on my list, because I couldn't really count the times I wished MinGW would support ARM. This is an awesome project and I can't wait to update you on my experience.
There doesn't seem to be much awareness of Visual Studio's complete support for both 32-bit and 64-bit ARM, but this is how I compiled Notepad++ for the Pi. It also made the most sense, because Notepad++ is built with Visual Studio. In fact everything I did to build with VS2015 was to copy the existing x86 configurations and change the platform to ARM
. OK, I lied, there were a couple of extra hacks such as including <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
in the ARM configuration or having to define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
when invoking the compiler manually to build SciLexer.dll
. Apparently, Microsoft thought it'll be a great idea if the ARM support has to be "unmasked" although it's technically there the whole time. If you can't link due to missing libraries, you could try installing the latest Windows SDK. I'm deeply indebted to Pete for this article which helped me overcome the problems I've encountered.
The modifications I made to the original repository are available on my GitHub and you can grab some binaries from the AppVeyor project; just pick the right branch. arm_build
doesn't feature Boost.Regex and is intended for testing purposes only while boost_appveyor
is fully functional and lacks only the signature checking of SciLexer.dll
which I had to remove due to a crash at startup. See this issue for more info. Someday perhaps the fork will be merged into the Notepad++ project.
You have setup Wine on the Raspberry Pi, your Windows program is compiled for ARM and you've copied it to the device, so nothing prevents you from typing wine <program>
, hitting return and voilà! Just kidding, you probably have more errors to fix, LOL. Or, you've transcended Murphy's law and your favorite tools are running without major issues. Congratulations!
So far we've been dealing exclusively with 32-bit ARM, i.e. AArch32. Wine also works on ARM64, i.e. AArch64, but this is uncharted territory for me. I haven't tried running Wine on ARM64 myself and I also have limited experience with running Wine on x64. So proceed reading the next lines with caution and please correct me if I'm spreading misinformation.
The optimal situation with 64-bit Wine is having a single build able to run both 32-bit and 64-bit Windows programs, because otherwise a 64-bit Wine barely makes sense. Wine supports WOW64 on ARM64 so this arrangment is definitely possible, but unfortunately getting it to work is quite tricky. Wine's Wiki provides a great starting point. In a nutshell, you'll have to compile both a 32-bit and a 64-bit version of Wine and mash them together. That's right folks, this involves 2 sets of dependencies. If you don't care for the 64-bit support and just want to build a 32-bit Wine on your 64-bit OS, you'll still be in trouble because you'll have to compile with 32-bit dependcies. Arch Linux for example provides the multilib repository exactly for this scenario.
Let's assume you've managed to build a WOW64 version or you're sticking with a 32-bit Wine. You should know that ARM support on ARM64 hardware is optional. It's quite rare for an ARM64 device to lack compatibility, but it happens. Also, the 32-bit compatibility kernel item CONFIG_COMPAT must be enabled.
I hope you now have a basic overview of what's involved to run Windows software on ARM devices without emulation. Please do not hesitate to contact me with extra questions, corrections, personal experience etc. We should empower ourselves through communication. I'll try to continuously update this article. Also, it would be very kind of you to report all issues you encounter with running Wine to the development team. Your feedback is highly valuable for the project.
I thank all Wine contributors for their huge effort. We all know the core project's unbelievably solid, but the ARM support is also quite mature. Some months ago I would've never expected to pull this off. What's even more mind-blowing is that Wine began implementing said support only months after Windows RT was introduced in late 2012. This is some very serious dedication!
I specifically like to thank André Hentschel for his patentience with all my underinformed questions and problems. His ARM-fu is vast and he thinks in solutions instead of problems.
Big thanks also to Sakaki, the maintainer of a 64-bit Gentoo distribution for the Raspberry Pi, for discussing with me the feasbility of running Wine on the platform. I initially considered her Gentoo distribution for my experiments, but I was afraid of having to compile numerous Wine dependencies manually in case they're unavailable through Portage. I might as well be punished for being so lazy since having 64-bit support would've been awesome.
And also thank you Microsoft for supporting ARM!
@oxotnika
The kernel compile (the line
make -j4 zImage modules dtbs
) failed so every other step after that doesn't do anything meaningful. I don't know why it failed, but compiling on the device itself is a bad idea.