Windows 32/64 cross GCC with C++11 threads support

Daniel Beer

5 Apr 2015

This document explains how to build, on a POSIX system, a cross-compiling GCC targeting both 32 and 64 bit Windows systems, with C++11 threads support. It uses MinGW-w64.

These instructions were tested on Debian 7.0 (Wheezy) on an amd64 system.

Prerequisites

You will need the following packages:

For the following instructions, we'll assume that you have these packages collected somewhere in a directory of their own, which will also be the root build directory.

You need to choose an installation prefix, and make sure that that prefix is in your path. We'll use the environment variable $MINGW to designate this installation prefix. Set it before starting, or substitute it by hand in the commands that follow. For example:

MINGW=/opt/mingw64

You'll also need to ensure that:

Building the compiler

Building binutils

The first step is to build and install binutils:

tar xfj binutils-2.25.tar.bz2
mkdir binutils-build
cd binutils-build
../binutils-2.25/configure \
    --target=x86_64-w64-mingw32 \
    --enable-targets=x86_64-w64-mingw32,i686-w64-mingw32 \
    --prefix=$MINGW \
    --with-sysroot=$MINGW
make && make install
cd ..

Double-check before proceeding that binutils is in your PATH:

which x86_64-w64-mingw32-ld

Before the next step, create the following directories and symbolic links in your installation prefix:

mkdir -p $MINGW/x86_64-w64-mingw32/lib32
ln -s x86_64-w64-mingw32 $MINGW/mingw
ln -s lib $MINGW/mingw/lib64

Installing MinGW-w64 headers

Unpack, configure, and install MinGW-w64 headers for the Windows API and C runtime:

tar xfj mingw-w64-v4.0.1.tar.bz2
mkdir mingw-headers-build
cd mingw-headers-build
../mingw-w64-v4.0.1/mingw-w64-headers/configure \
    --prefix=$MINGW/x86_64-w64-mingw32 \
    --host=x86_64-w64-mingw32 \
    --build=$(gcc -dumpmachine)
make install
cd ..

Building the compiler

Next we unpack, configure and build GCC -- but only the core compiler. We can't build libgcc and the C++ library yet:

tar xfj gcc-4.9.2.tar.bz2
mkdir gcc-build
cd gcc-build
../gcc-4.9.2/configure \
    --target=x86_64-w64-mingw32 \
    --enable-targets=all \
    --prefix=$MINGW \
    --with-sysroot=$MINGW \
    --enable-threads=posix
make all-gcc && make install-gcc
cd ..

Building the C runtime

We've already unpacked the MinGW-w64 package. Now we use it to build and install the C runtime:

mkdir mingw-crt-build
cd mingw-crt-build
../mingw-w64-v4.0.1/mingw-w64-crt/configure \
    --prefix=$MINGW/x86_64-w64-mingw32 \
    --with-sysroot=$MINGW \
    --enable-lib32 \
    --enable-lib64 \
    --host=x86_64-w64-mingw32 \
    --build=$(gcc -dumpmachine)
make && make install
cd ..

Building the winpthreads library

First, build and install a 64-bit version. We need to move the DLL after installation so that it's not clobbered by the 32-bit version later:

mkdir mingw-wpth-build64
cd mingw-wpth-build64
../mingw-w64-v4.0.1/mingw-w64-libraries/winpthreads/configure \
    --prefix=$MINGW/x86_64-w64-mingw32 \
    --host=x86_64-w64-mingw32 \
    --build=$(gcc -dumpmachine)
make

# At this point, the build will fail, due to the fact that we
# enabled POSIX threads in the compiler, but libpthread.a doesn't
# exist yet. Work around it and try again:
cp fakelib/libgcc.a fakelib/libpthread.a

make && make install

# Move the created DLL
mv $MINGW/x86_64-w64-mingw32/bin/libwinpthread-1.dll \
   $MINGW/x86_64-w64-mingw32/lib64/

cd ..

Now build the 32-bit version. This is almost the same, except for the configure arguments and the final destination for the DLL:

mkdir mingw-wpth-build32
cd mingw-wpth-build32
../mingw-w64-v4.0.1/mingw-w64-libraries/winpthreads/configure \
    --prefix=$MINGW/x86_64-w64-mingw32 \
    --host=x86_64-w64-mingw32 \
    --build=$(gcc -dumpmachine) \
    --libdir=$MINGW/x86_64-w64-mingw32/lib32 \
    CC='x86_64-w64-mingw32-gcc -m32' \
    CCAS='x86_64-w64-mingw32-gcc -m32' \
    DLLTOOL='x86_64-w64-mingw32-dlltool -m i386' \
    RC='x86_64-w64-mingw32-windres -F pe-i386'
make

cp fakelib/libgcc.a fakelib/libpthread.a

make && make install

mv $MINGW/x86_64-w64-mingw32/bin/libwinpthread-1.dll \
   $MINGW/x86_64-w64-mingw32/lib32/

cd ..

Building the compiler runtime

Now, we can finish the GCC build:

cd gcc-build
make && make install
cd ..

This step takes quite a while, but after that, it's all done -- you should have a working Windows cross-compiler.

Building libraries with the cross-compiler (optional)

To avoid problems arising from a lack of package multilib support, I'd suggest using $MINGW/local64 and $MINGW/local32 as installation prefixes for 64 and 32 bit libraries. In this section, I'll show how to compile two libraries for 32-bit Windows, in a way that minimizes distribution dependencies.

There's nothing special about the choice of these libraries in particular -- they're just two that I've happened to need so far.

Building libusb-1.0

Building libusb 1.0 is fairly straightforward, once configured correctly:

tar xfz libusb-1.0.19.tar.bz2
mkdir libusb-build32
cd libusb-build32
../libusb-1.0.19/configure \
    --prefix=$MINGW/local32 \
    --host=x86_64-w64-mingw32 \
    CC='x86_64-w64-mingw32-gcc -m32' \
    DLLTOOL=true \
    RC='x86_64-w64-mingw32-windres -F pe-i386'

We provide a dummy DLLTOOL option because gcc already builds a functional import library. If you let libusb try to build its own, it doesn't work (you'll see warnings from gcc about skipping an incompatible libusb-1.0.dll.a if you try to link against it).

One minor annoyance if you're trying to build a minimize dependencies is that libtool builds shared libraries with shared dependencies. To enforce static dependencies for a library and avoid the need to distribute libgcc with your binary, edit libusb/Makefile prior to the make command, and edit the line that specifies LDFLAGS:

LDFLAGS = -Wc,-static

You can't specify these when giving the configure command, because they cause autoconf tests to fail. Specifying -static on its own wouldn't cause the tests to fail, but libtool interprets this as an instruction not to build a shared library at all. Specifying -Wc tells libtool to pass the flag to the compiler without trying to interpret it.

Build and install the library with:

make && make install
cd ..

Note that you can use pkg-config to obtain the necessary compiler and linker flags for this (and many other) libraries when cross-compiling:

PKG_CONFIG_PATH=$MINGW/local32/lib/pkgconfig
pkg-config --cflags libusb-1.0
pkg-config --libs libusb-1.0

Building wxWidgets

wxWidgets for 32-bit is similarly straight-forward, although one minor fix-up is required after installation, as the libraries get installed with incorrect names:

tar xfj wxWidgets-3.0.2.tar.bz2
mkdir wxwidgets-build32
cd wxwidgets-build32
../wxWidgets-3.0.2/configure \
    --prefix=$MINGW/local32 \
    --host=x86_64-w64-mingw32 \
    --disable-shared \
    CXX='x86_64-w64-mingw32-g++ -m32' \
    CC='x86_64-w64-mingw32-gcc -m32' \
    WINDRES='x86_64-w64-mingw32-windres -F pe-i386'
make && make install

# The library names don't match what wx-config reports. Fix this:
for x in /opt/mingw64/local32/lib/libwx_*.a; do
    y=$(echo "$x" | sed 's/-x86_64-w64-mingw32.a$/.a/')
    mv "$x" "$y"
done

Above, I've built a static library. Remove the --disable-shared argument to configure if you want a shared library instead.

Notes on linking

If you want to minimize distribution dependencies, link with -static. This will avoid the need to distribute the libgcc and winpthread DLLs with your binary.

If you're trying to build a mostly-static binary and want to link dynamically against one or two libraries (e.g. libusb, which has an LGPL license), link your binary as follows, wrapping the exceptional library flags with these -Wl directives:

x86_64-w64-mingw32-g++ -m32 -static -o binfile.exe <dependencies> \
    -Wl,-Bdynamic <dynamic dependencies> -Wl,-Bstatic

You can do this for any subset of libraries you want to link against.