mingw-w64 from Linuxulated Debian Buster, building Wine/PE

Background

The MinGW project is a GCC port building Windows EXEs and DLLs, in their native PE binary format. The original mingw32 only supports Win32, and available in our ports as the dated mingw32-gcc package limited to GCC version 4.8.1. A fork called mingw-w64, additionally supporting Win64 and with updated Windows API header files, is generally the better MinGW these days.

Wine has used MinGW for a long time to cross-compile unit tests, and more recently to compile some of its own DLLs and EXEs (which I've called Wine/PE here), for better compatibility with some Windows applications that expect to see PE files when they look in directories like "C:\Windows\System32", and sometimes even load these with their own code. This is currently optional but preferred, and more compatible with Windows applications. It requires mingw-w64 and (until now) could not be used on FreeBSD where mingw-w64 is unavailable. Instead, we build the traditional ELF-based Wine binaries.

As a FreeBSD user and Wine contributor, it is highly annoying having to maintain an entire Linux VM and have it eating RAM, just to build unit tests. Several other options were considered/attempted, before the current one that was a major success.

Windows version of mingw-w64 running in Wine

This works, and works well, I've even compiled the Windows version of wxWidgets with it (https://bugs.winehq.org/show_bug.cgi?id=38924#c9). The problem is, it runs in wineconsole, not a *nix shell, and you can only build software whose build system expects the Windows-based mingw.

Software that expects a mingw cross-compiler integrated into the *nix shell (Wine, LibreOffice, etc.) is much more problematic. I tried to make shell scripts like i686-w64-mingw-gcc, which just call "wine ~/.wine/drive_c/mingw-w64/bin/i686-w64-mingw-gcc $*", and this got through Wine's ./configure, but came apart at the seams for the more advanced build tasks with lots of low-level command line options. I suspect either the GCC version is too old, or the Windows-based mingw-w64 expects Windows-style paths. It was also painfully slow: 2-3 seconds startup time for each call to i686-mingw-w64-gcc, and even with a maximally parallelized build, CPU utilization was crippled to only 20-30%. That's also why I gave up.

llvm-mingw

An interesting and promising project. Yet another mingw variant, this time using the LLVM compilers instead of GCC. However Wine is known to have issues when built with Clang, which is why our Wine in ports builds with GCC.

Porting
mingw-w64 to FreeBSD

Already tried that. It's really difficult and would take me far too long to figure out the mysterious GCC errors.

Linuxulated Debian Buster's mingw-w64

Debian Buster has quite a recent mingw-w64 version (8.3), full support for i386, and can be installed quite easily. As an added bonus, this is a commonly used mingw-w64 setup, so it is likely to work well.

(My setup is quite advanced, with an i386 FreeBSD chroot as per https://wiki.freebsd.org/i386-Wine in /compat/i386, which then has a nested i386 Linux in /compat/i386/compat/linux. For simplicity, and since not all readers will be installing mingw-w64 to use in a 32 bit chroot, I'll give instructions as if the installation was into /compat/linux. Rebase to another location as needed.)

Make some directories for the Debian Buster installation:

mkdir /compat
mkdir /compat/linux
mkdir /compat/linux/dev
mkdir /compat/linux/sys
mkdir /compat/linux/proc


Add mountpoints to /etc/fstab:

devfs /compat/linux/dev devfs rw 0 0
fdescfs /compat/linux/dev/fd fdescfs rw,linrdlnk 0 1
linsys /compat/linux/sys linsysfs rw 0 0
linproc /compat/linux/proc linprocfs rw 0 0


Mount them:

sudo mount /compat/linux/dev
sudo mount /compat/linux/dev/fd
sudo mount /compat/linux/proc
sudo mount /compat/linux/sys


Install debootstrap and use it to install the Debian Buster base system as follows:

pkg install debootstrap
debootstrap --foreign --arch=i386 buster /compat/linux http://ftp.debian.org/debian


Everything useful lives in Debian's package repository, and we need apt to install from it. However I haven't seen a tool as demanding and fussy as apt for a long time...

Firstly, no Linux binaries will run because they think the Linux kernel is too old. Fool them:
sysctl compat.linux.osrelease=4.19.0

Then apt was complaining about not enough cache memory. Luckily it also reported the setting to change, which I increased to 100 MiB by making a file /compat/linux/etc/apt/apt.conf.d/caching with this in it:
APT::Cache-Start 104857600;

apt then expects an "_apt" user, so add it to /compat/linux/etc/passwd:

_apt:x:64:64::/nonexistent:/bin/false


and apt wants that user to own some directories, so make it (using the numeric id of the _apt user, as it's not visible to FreeBSD's /etc/passwd):

chown 64 /compat/linux/var/lib/apt/lists/partial
chown 64 /compat/linux/var/cache/apt/archives/partial


and apt still broke here for me with a scary "Splitting of the file..." error, so you may also need to do this:

rm -rf /compat/linux/var/lib/apt/lists/*


apt still won't work because it sees FreeBSD's tar which breaks because it lacks the "--warning=no-timestamp" option, and fiddling with the PATH environment variable didn't help me. I had to chroot into the Linux environment to hide FreeBSD's tar to be able to use apt. At this stage, apt should finally work, and install mingw-w64:

chroot /compat/linux /bin/bash
apt update
apt install gcc-mingw-w64-i686


The mingw-w64 binaries are in /compat/linux/usr/bin, but some have unusual names, and are intended to be accessed through symlinks from /compat/linux/etc/alternatives. Unfortunately those symlinks don't work from outside the chroot, as their link targets are missing the /compat/linux prefix. You'll need to delete and re-create them. A relative symlink path might be best, as that would work from both inside and outside the chroot.

Leave the chroot. Add these locations to the end of your PATH:

export PATH=$PATH:/compat/linux/bin:/compat/linux/usr/bin:/compat/linux/etc/alternatives


And you can now build Windows binaries, eg.:

i686-w64-mingw-gcc hi.c -o hi.exe


Wine/PE

There are no special options. If Wine's ./configure detects mingw-w64, and "--without-mingw" wasn't passed, it will make a PE build. The detection is accomplished using the names of those tools linked from /compat/linux/etc/alternatives, so make sure they're valid and that directory in your PATH.

While building I sadly discovered some kind of bug, where parallel Wine builds (gmake -j4) break due to sporadic segfaults in mingw-w64 tools:
pid 44932 (i686-w64-mingw32-gc), jid 0, uid 1003: exited on signal 11 (core dumped)
pid 36895 (i686-w64-mingw32-as), jid 0, uid 1003: exited on signal 11 (core dumped)

However non-parallel builds are perfectly fine. The whole of Wine compiled successfully, installed, and ran several complex applications. It's probably the first ever Wine/PE build for FreeBSD. Binaries generated by this mingw-w64 setup also run successfully on Windows. 🥳

The segfaults could be caused by some kind of race condition in Linuxulator, which doesn't manifest when only 1 concurrent Linux process is running?

Anyway, I hope people find this useful, and don't struggle as much as I did. A big thank you to Linuxulator developers, without whose amazing contributions none of this would have been possible.
 
Are you using 13 CURRENT? I used to try creating a Buster jail on 11.4-p1 and failed. All of the binary, from ls, to cat, all failed with segmentation fault. But a Stretch jail worked fine for me, though.

BTW, I think if you want mingw-w64 to cross compile to Windows, you should actually create a port for it rather than relying on the Linux version. I know it's possible because NetBSD has it on their pkgsrc, too:

 
Are you using 13 CURRENT? I used to try creating a Buster jail on 11.4-p1 and failed. All of the binary, from ls, to cat, all failed with segmentation fault. But a Stretch jail worked fine for me, though.

12.1
Maybe 32 bit Linux also works better?

BTW, I think if you want mingw-w64 to cross compile to Windows, you should actually create a port for it rather than relying on the Linux version. I know it's possible because NetBSD has it on their pkgsrc, too:


That's great news. If or when I get around to making a port, that would be the best place to start.
 
Back
Top