Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRT library type

I'm trying to get a better grasp on the CRT library options in Visual Studio 2013 (C++ -> Code Generation -> Runtime Libary) and how to know which option to select (and when to change the default).

From MSDN:

A reusable library and all of its users should use the same CRT library types and therefore the same compiler switch.

So, my understanding is that if you are linking with a third party library, you should use the same CRT version that was used to build the library. Whoever built the library should specify what CRT option was used in the build.

Is there a way to determine which CRT version was used just by looking at the .lib file?

More importantly, how would you decide which option to use if you aren't linking with any third-party libraries? When would you consider changing the default?

like image 546
user974967 Avatar asked Apr 05 '15 21:04

user974967


1 Answers

Short answer:

So, my understanding is that if you are linking with a third party library, you should use the same CRT version that was used to build the library. Whoever built the library should specify what CRT option was used in the build.

That is the least error-prone option. It is possible to mix runtimes, but you can run into unexpected bugs if you do so.

Is there a way to determine which CRT version was used just by looking at the .lib file?

I don't know about the .lib on its own, but if the third party code has a DLL or EXE, you can see what CRT DLL(s) it depends on using the Windows Dependency Walker tool.

If it's a static library, and your code's CRT choice is mismatched, you'll see warnings when you build.

More importantly, how would you decide which option to use if you aren't linking with any third-party libraries? When would you consider changing the default?

For the simplest possible deployment, statically linking is best; you can just ship the executable on its own and it will run. For larger projects, where you have multiple EXEs and DLLs, your code size will be bigger if you statically-link. It would also be preferable to be sharing the same CRT code if you have multiple modules (EXE plus 1 or more of your own DLLs) in the same process.

More detail (based on blog post I wrote previously):


The Library Variants

There are four variants of C/C++ runtime library you can build your code against:

  • Multi-threaded Debug DLL
  • Multi-threaded DLL
  • Multi-threaded Debug
  • Multi-threaded

You can select which library you’re using by right-clicking on your project in Visual Studio and selecting Properties, clicking the Code Generation option under C/C++ on the dialog that pops up, and going to the Runtime Library property.

Visual Studio Code Generation Properties

Remember that this setting is per-configuration, as you’ll want to choose a Debug runtime library for a Debug configuration, and Release runtime library for a Release configuration.

What’s the difference?

The DLL runtime library options mean that you link dynamically against the C/C++ runtime, and for your program to run, that DLL will need to be somewhere your program can find it (more on that later).

The options not mentioning DLL (Multi-threaded Debug and Multi-threaded Release) cause your program to be statically-linked against the runtime. This means that you don’t need an external DLL for the program to be run, but your program will be bigger because of the extra code, and there are other reasons why you might not want to choose it.

By default, when you create a new project in Visual Studio, it will use the DLL runtime.

The multi-threaded in the runtime names is a legacy of when there used to be both non-thread-safe and multi-threaded C/C++ runtimes. You’ll always be using a multi-threaded runtime with modern Visual Studio, even if your own application is single-threaded.

Deploying DLL Runtimes

If you’re linking against the DLL runtimes, then you’ll have to think about how to deploy them when releasing your program (to Test, and to your customers).

If you deliver Debug builds to your Test team, make sure you provide the Debug variant of the DLL runtimes too.

Also, don’t forget to get the right architecture (e.g. x86 vs x64).

Redistributable Installers

Microsoft provide redistributable packages that install the Release (but not the Debug) DLLs. These can be found easily enough by searching for something like Visual C++ Redistributable 2013 (substituting the Visual Studio version for what you need). You can also go straight to Latest Supported Visual C++ Downloads on Microsoft’s website.

These redistributables packages are executables, and you can call them from your program’s installer (or you could run them manually for setting up a test environment). Note that there are separate redistributables for x86 and x64.

Merge Modules

If you’re building an MSI installer, you can merge the Merge Module for the C/C++ runtime you’re using into your installer package. These merge modules are typically found in C:\Program Files (x86)\Common Files\Merge Modules. For example, the x86 C/C++ runtime’s merge module for Visual Studio 2013 is called Microsoft_VC120_CRT_x86.msm.

Note that the version numbers in these merge module names are not the year-based product versions, but the internal version numbers. This table on Wikipedia shows the mapping between the version numbers for avoidance of confusion.

Unlike the stand-alone executable redistributable installers above, the merge modules also come in Debug variants.

Some versions of Visual Studio have support for a Visual Studio Installer project type (under the Setup and Deployment category in Other Project Types), and if you include the output from your program’s projects in one of these installers, the merge modules for the runtime will be included automatically. You can also use this as a trick to get an installer that will install the Debug runtimes for internal testing purposes (any dummy C/C++ project will do, it doesn’t have to actually install your program).

Copy From Redist Folder

You can also just copy the DLLs from the redist folder in your Visual C++ installation into where your program is installed. For example, for Visual Studio 2013, you’ll find the x64 C/C++ runtime libraries somewhere like C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\x64\Microsoft.VC120.CRT\.

The debug variants can be found somewhere like C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\Debug_NonRedist.

(Adjust paths as appropriate for your Visual Studio version and installation location).

Mixing C/C++ Runtimes

In an ideal world, you would use the same C/C++ runtime library variant (Debug vs Release, DLL vs statically-linked), and all from the same version of Visual Studio, for all libraries being linked into your program.

If you do mix runtimes, you may get linker errors, your program may simply fail to run at all, or worse still, it may seem to be working but crash or give the wrong result only in some cases.

Ignoring Default Libraries

A common scenario is that you only have the Release version of some third-party library, but you still want to be able to build the Debug variant of your own code using this library. Another scenario is that you have a library that uses the static C/C++ runtime and you want the DLL version in your program, or the reverse.

If the third-party library is C code then you’ll probably be able to get away with this and have it actually work, using the /NODEFAULTLIB linker option.

If the library is C++ code, you are probably out of luck as too much of the generated code is tied to symbols in a particular runtime.

These are the different library names:

  • LIBCMT.LIB: Statically-linked Release runtime (a.k.a. Multi-threaded)
  • LIBCMTD.LIB: Statically-linked Debug runtime (a.k.a. Multi-threaded Debug)
  • MSVCRT.LIB: Dynamically-linked Release runtime (a.k.a. Multi-threaded DLL)
  • MSVCRTD.LIB: Dynamically-linked Debug runtime (a.k.a. Multi-threaded Debug DLL)

Remember, the runtime libraries you want to ignore are the ones that the third-party code is using, i.e. it will be different from the library that your own program is using. If you look at your build output you will be prompted to choose the right one anyway, e.g.:

LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library

You can specify the library to ignore by right-clicking on your project, selecting Properties, clicking the Input entry under Linker and adding the runtime library name into the entry.

Ignoring Default Libs in Visual Studio

Crossing Module Boundaries

You can also run into C/C++ runtime mismatch problems in more subtle ways across module boundaries (between an EXE and a DLL it loads for example).

For example, data structures in the C library may be defined differently by different runtimes. I’ve seen this cause crashes in programs that used a DLL that made use of a FILE* in its API. A file is opened in one module using one C runtime, and interacted with by another module which has a different, incompatible implementation. Safer options would include passing Windows API HANDLE objects for things like this, or else wrapping up the file interactions in a way that is runtime-agnostic.

Different runtimes also use their own memory heap for allocations. If an object is allocated in one module on one heap, but deallocated in another module, a crash is likely to occur if the C/C++ runtimes are mismatched. This only applies of course to allocations using the C or C++ runtime such as malloc or new. If the default Windows process heap is being used everywhere, for example, everything is fine.

Note that if the modules statically link against the C/C++ runtime, they will have this problem even if it was the same variant of the runtime they linked against, because there will still be two different runtimes with their own memory heaps in play. Therefore, you will want to use the DLL C/C++ runtime in such circumstances.

It would also be wise to use the (same version of) DLL runtimes in each module if runtime-implemented functionality like exceptions or classes with vtables cross the module boundaries, though some combinations of mismatch may work in practice.

like image 148
softwariness Avatar answered Sep 17 '22 10:09

softwariness