MSVCR90.DLL and MinGW
Sunday, June 10th, 2012MinGW 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:
- link against the library
- add a suitable manifest to the resulting executable or DLL.
- 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.
- You can link against msvcrt.dll first and observe how the command gcc invokes the various subcommands using the gcc option -v. Then you invoke the subcommands yourself using suitable options. While this method might be a good exercise, it is not suitable for serious work.
- You can change the gcc spec file. The spec file controls, how the gcc command invokes its subcommands. Today, the default spec file is compiled into the gcc executable, but gcc still reads a spec file from the file system, it the file is present. Fortunately, there is a more elegant option: you can amend the default spec file by specifying an supplementary spec file using the gcc option -spec. This supplementary spec file contains only spec strings we need to modify. Look at the gcc documentation for the details.
%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.
- Add the file to command line used to invoke the linker
- Add rules to the spec file to link the right spec file automatically and copy the *.res files into a directory on the library search path of gcc. This option has the advantage, that gcc now creates valid executables by default. Here is the second part of our supplementary spec file.
%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:
- mingwrt-3.20-mingw32-src.tar.gz from upstream
- patch 1: http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/mingw/tlssup.c.diff?r1=1.4&r2=1.5&cvsroot=src
- patch 2: https://sourceforge.net/tracker/download.php?group_id=2435&atid=302435&file_id=436925&aid=3495292
- patch 3: https://sourceforge.net/tracker/download.php?group_id=2435&atid=302435&file_id=436927&aid=3495304
- patch 4: https://sourceforge.net/tracker/download.php?group_id=2435&atid=302435&file_id=438046&aid=3502499
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
- Update mingw runtime to version 3.20 and apply the patches.
- Compile continue_on_invalid_param.cpp
- 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 commandmingw32-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. - Rebuild the mingw runtime.
- 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.