MinGW GCC - Selection of the C Runtime Library

2012-06-10: Published on my blog at berlios.de.
2017-12-22: I discovered a copy of this text on my laptop.

MinGW GCC - Selection of the C Runtime Library

A common problem with the MinGW gcc is the C (runtime) library msvcrt.dll used by default. If you combine object files or DLLs compiled with MinGW and Visual Studio 20XX, you end up with executables linking to multiple C libraries. All sort of nasty misbehaviour can occur. For instance, you run into this problem, if you compile python modules, because python uses recent versions of Visual Studio. Python 2.7 is compiled using VS 2008 professional using msvcr90.dll

Older versions of gcc from the GPL2 age were not able to utilise the non system libraries msvcrXX.dll for legal reasons, but since gcc switched to GPLv3 it is legally possible to distribute a libgcc linked against the C-runtime libraries provided by Microsoft. Unfortunately, it is still not well documented how to actually do it.

Actually, you need to perform 3 tasks:

  1. link against the library
  2. add a suitable manifest to the resulting executable or DLL.
  3. add an invalid parameter handler

I'll explain how you can patch your MinGW installation to do all 3 tasks automatically.

Linking msvcrXX

Recent builds of MinGW provide everything you need. Look at the lib subdirectory of your MinGW installation. If it contains the files libmsvcrXX.a and libmoldnameXX.a, where XX is the runtime version number, then your installation can be used.

Now you need to instruct the compiler to use these files instead of the default files libmoldname.a and libmsvcrt.a. In addition you need to define the preprocessor macro __MSVCRT_VERSION__. There are many ways to do that.

%rename cpp msvcrXX_cpp

%rename cc1plus msvcrXX_cc1plus

*cpp:
%(msvcrXX_cpp) -D__MSVCRT_VERSION__=0x0900

*cc1plus:
%(msvcrXX_cc1plus) -D__MSVCRT_VERSION__=0x0900

*libgcc:
%{mthreads:-lmingwthrd} -lmingw32    %{shared-libgcc:-lgcc_s}    -lgcc    -lmoldname90 -lmingwex -lmsvcr90

Adding a manifest

Usually you add a manifest using the tool mt.exe. It is available as part of Visual Studio but also included in various versions of the platform SDK. If you add a manifest, mt.exe adds a special resource of type RT_MANIFEST to your DLL or executable. Usually the manifest of an exe is resource number 1 and the manifest of a DLL is resource number 2. This knowledge leads to a second possibility for adding an manifest. The GNU linker ld.exe is able to place a resource into the generated DLL or executable. You can simply link the manifest.

This is the usual manifest of a program using msvcr90.dll. Look at the documentation in MSDN for details.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

Now follow the description of David Cournapeau and create two resource files, msvcr90.dll.rc for DLLs and msvcr90.exe.rc for executables.

msvcr90.dll.rc

#include "winuser.h"
2 RT_MANIFEST msvcr90.manifest

msvcr90.exe.rc

#include "winuser.h"
1 RT_MANIFEST msvcr90.manifest

Compile these files into *.res files:

windres --input msvcr90.dll.rc --output msvcr90.dll.res --output-format=coff
windres --input msvcr90.exe.rc --output msvcr90.exe.res --output-format=coff

To link the correct *.res-file we have again several options.

%rename link_gcc_c_sequence msvcrXX_link_gcc_c_sequence

*link_gcc_c_sequence:
%(msvcrXX_link_gcc_c_sequence) %{shared|mdll: msvcr90.dll.res%s} %{!shared: %{!mdll: msvcr90.exe.res%s}}

Invalid Parameter Handler

Microsoft decided in its infinite wisdom, that the POSIX specification is unsafe and needs to be improved. Especially if you call a function with an invalid parameter, what you really want is to terminate the program using Dr. Watson. And Microsoft implemented Dr. Watson starting with msvcr80.dll

Since msvcr80.dll you need to set an invalid parameter handler within each DLL or EXE, if you want standard behaviour of POSIX functions. This requires either modification of the source or another trick.

Fortunately it is possible to install an invalid parameter handler by simply linking an object file libcontinue_on_invalid_param.a, that was created using C++ and contains nothing but an initialiser with the desired side effect. This is the source of continue_on_invalid_param.cpp. The initialiser and the invalid parameter handler are plain C functions. This way, we don't need the C++ runtime libraries. Compile it using the command line

g++ -D__MSVCRT_VERSION__=0x800 -c continue_on_invalid_param.cpp -o libcontinue_on_invalid_param.a

Why do I name the output libcontinue_on_invalid_param.a instead of continue_on_invalid_param.o. Because libtool sucks! Libtool tries to improve the link order given in the gcc specs file and sorts *.o files after the libraries. The result are unresolved symbols. Fortunately gcc does not care if a *.a file is really an archive.

Again you have to modify the gcc specs file and add a -lcontinue_on_invalid_param option:

*libgcc:
-lcontinue_on_invalid_param %{mthreads:-lmingwthrd} -lmingw32 %{shared-libgcc:-lgcc_s} -lgcc -lmoldname90 -lmingwex -lmsvcr90

Mingw Runtime

Following the previously given instructions you are able to build simple programs ("hello world"). But you will soon discover, that you need to rebuild the mingw runtime library.

You need at least:

and for gcc 4.7 you need another patch from the fedora fc17 mingw32-runtime source rpm. You may need to build the new runtime package two times: first for msvcrt.dll and then for msvcr90.dll. The reason is a header file bug in older mingwrt versions, that breaks compiling for msvcr90.dll.

If you replace the mingw runtime with your newly compiled version, you will not be able to link against the old msvcrt.dll any longer. Therefore you probably want to create a new default gcc spec file, that incorporates the changes. I made a small python script mingw32-patch-gcc4msvcrXX.py, that creates such a spec file for you and also creates the manifest resources files.

Putting all together

  1. Update mingw runtime to version 3.20 and apply the patches.
  2. Compile continue_on_invalid_param.cpp
  3. Create a new spec file for gcc: run
    mingw32-patch-gcc4msvcrXX.py 90
    This step also creates the res files for the manifest. If you don't like the modified default spec file, you can switch back to msvcrt.dll using the command
    mingw32-patch-gcc4msvcrXX.py 60
    You are still able to compile/link for msvcr90dll, if you add the command line argument "-spec msvcr90.spec" to every invocation of gcc.
  4. Rebuild the mingw runtime.
  5. Rebuild your source.

A last hint: If possible I avoid MSYS and use the MinGW cross compiler that comes with Fedora Linux, because MSYS is very slow compared to Linux. The compiler patches described here also apply to the Fedora MinGW compiler. And rebuilding RPMs is realy simpl. I rebuild all mingw*-RPMs from Fedora 16 for msvcr90.dll.