Skip to content

Instantly share code, notes, and snippets.

@MIvanchev
Last active April 4, 2023 13:39
Show Gist options
  • Star 65 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save MIvanchev/14de59fa2552d315ac74c30cf1c0b01e to your computer and use it in GitHub Desktop.
Save MIvanchev/14de59fa2552d315ac74c30cf1c0b01e to your computer and use it in GitHub Desktop.
Ever wondered what it takes to run Windows software on ARM? Then this article might be for you!

Running Windows software on the Raspberry Pi (and other ARM devices) with Wine

Contents

Introduction

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.

Notepad++ running on the Raspberry Pi

Configuring a 3G/1G VM split (32-bit distros only)

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.

Installing Wine

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.

Building Wine

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.

Running Windows software

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.

Build using Winelib

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.

Build using a MinGW toolchain supporting ARM

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.

Build using Visual Studio

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.

Piecing it all together

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!

Running Wine on ARM64

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.

We're done

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.

Credits

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
Copy link

@oxotnika, Hi I'm not much around the PC these days. The way I did it after cross-compiling the kernel was to copy the files to the Raspberry Pi using SSH in a local network. So the answer to your question is: Raspbian has to be already installed on the SD card and then you copy the new files to the responding locations. Without SSH it's going to be harder, because you'll have to mount the card inside the VM.
I copy to SD card:
http://joxi.ru/RmzYNpWfYl610r

@oxotnika
Copy link

Everything I collected a kernel but in article some moments are not so written or I that I do not understand that. It was necessary to look for answers in Google. CONFIG_VMSPLIT_3G=y
at me now)))

@TamaTamaGoGo
Copy link

I couldn’t successfully run
make
and
make install
It says no rule of running make.
I already done download git://source.winehq.org/git/wine.git, and cd wine, .configure!
How should I do?

@MIvanchev
Copy link
Author

@TamaTamaGoGo: what output does ./configure produce?

@binarymaster
Copy link

binarymaster commented Sep 1, 2020

win-10-pro-raspberry-pi-3-b

@alexptbg Now Raspberry Pi 4B 8GB too:

image

Copy link

ghost commented Sep 1, 2020

Yeah, software render forever and ever. People don't get than WoA is dead.. box86 can run just a few wine x86 games for now, but would be a much better implementation than WoA on soft render. And will be available for any ARM sbc, not just the rpi4 with his crappy gpu.

@binarymaster
Copy link

Yeah, software render forever and ever.

https://twitter.com/driver1998/status/1302128535859994625

Copy link

ghost commented Sep 5, 2020

Yeah, software render forever and ever.

https://twitter.com/driver1998/status/1302128535859994625

That's software rendering anyway. Also, on the pi3, not the pi4.

@binarymaster
Copy link

That's software rendering anyway

Well, FPS rates in the test results impressed me, see it in https://discord.gg/deveco (in #windows-on-arm channel, direct link to message)

Copy link

ghost commented Sep 5, 2020

Doing glxgears on software rendering it's not a complex task buddy... 160 fps ob glxgears!! Wow, that's fast..no, it's not. I achieved way more than 1000 on rpi3 back in the day when I used to do tinkering on mesa with rpi3

@vinutshetty
Copy link

Hi, Configuration of 3G/1G split is true for 8GB Pi4 as well?

Copy link

ghost commented Oct 12, 2020

its already 3g/1g on rpi4 kernel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment