-
Notifications
You must be signed in to change notification settings - Fork 955
Migration issues 101
There are two places where incompatibilities arise: [1] in the toolset and [2] in the Visual C++ libraries.
With respect to the toolset:
[1] The obj and lib file formats are well-defined and rarely change. Sometimes additions are made to these file formats, but these additions generally do not affect the ability of newer toolsets to consume object files and libraries produced by older toolsets. The one big exception here is if you compile using /GL (Link-Time Code Generation / Whole Program Optimization). If you compile using /GL, the resulting object file can only be linked using the same toolset that was used to produce it. So, if you produce an object file with /GL and using the VC12 compiler, you must link it using the VC12 linker. This is because the internal data structures within the /GL objects are not stable across major versions of the toolset and newer toolsets do not understand the older data formats.
[2] The C++ ABI is not stable across major versions of Visual C++. That is, we may make breaking changes to C++ type layout, name decoration, exception handling, and other parts of the C++ ABI. Thus, if you have an object file that has external symbols with C++ linkage, that object file may not link correctly with object files produced with a different major version of the Visual C++ toolset. Note that here, “may not work” has many possible outcomes: the link may fail entirely (e.g. if name decoration changed), the link may succeed and things may not work at runtime (e.g. if type layout changed), or things may happen to work in many cases and nothing will go wrong. Note also that while the C++ ABI is not stable, the C ABI and the subset of the C++ ABI required for COM are stable.
With respect to the libraries:
If you compile a source file using a particular version of the Visual C++ libraries headers (by #including the headers), the resulting object file must be linked with the same version of the Visual C++ libraries. So, for example, if your source file is compiled with the VC12 <stdio.h>, you must link with the VC12 CRT library. Similarly, if your source file is compiled with the VC12 , you must link with the VC12 STL library. Mixing-and-matching is not supported.
For the STL, mixing-and-matching has been explicitly disallowed via use of #pragma detect_mismatch in the STL headers for the last several releases (since VC10, if I recall correctly). If you try to link incompatible object files, or if you try to link with the wrong STL library, the link will fail.
For the CRT, mixing-and-matching was never supported, but it often just worked, at least until VC14 and the Universal CRT, because the API surface did not change much over time. We broke backwards compatibility with the Universal CRT so that going forwards we could maintain backwards compatibility (i.e., we aren’t going to be introducing new, versioned Universal CRT binaries in the future; we’ll be updating the existing Universal CRT in-place).
Our long-term goal with the libraries stabilization effort (which started with the Universal CRT project) is to make mixing-and-matching possible in some/most cases.
To answer the bonus question: Yes, the recommended practice is to wrap the static library into a DLL that is built using the old toolset and linked with the old version of the Visual C++ libraries. Use of multiple CRTs in a single process is not in and of itself problematic (indeed, most processes will end up loading multiple CRT DLLs; e.g., Windows operating system components will depend on msvcrt.dll and the CLR will depend on its own private CRT). Problems arise when you jumble state from different CRTs. For example, you should not allocate memory using msvcr110!malloc and attempt to deallocate that memory using msvcr120!malloc, and you should not attempt to open a FILE using msvcr110!fopen and attempt to read from that FILE using msvcr120!fread. As long as you don’t jumble state from different CRTs, you can safely have multiple CRTs loaded in a single process.
Do note that because of the toolset incompatibilities described above, the DLL into which you wrap use of the static library should have only export a C export surface (the C ABI is stable; the C++ ABI is not).
There is a breaking change in the Universal CRT, used by Visual Studio 2015 and Windows 10. Mixing-and-matching object files compiled with different major versions of the Microsoft C and C++ runtime library headers is not supported. For several releases, the C++ Standard Library headers have enforced this via a #pragma detect_mismatch, which causes link errors when mixing-and-matching is detected. The CRT headers have not enforced this, but it is nonetheless not supported. In general, in cases where you want to build your own library that can be used by programs built using different versions of Microsoft Visual C++, the best way to do this is to build your library into a DLL, to encapsulate its dependencies on the particular version of the Visual C++ libraries on which it depends.
This breaking change is a result of some of the redesign work that went into the Universal CRT. To provide partial link compatibility with object files (and libraries) compiled with older versions of the Microsoft C Runtime headers, we provide a library, legacy_stdio_definitions.lib, with Visual Studio 2015. This library provides compatibility symbols for most of the functions and data exports that were removed from the Universal CRT. The set of compatibility symbols provided by legacy_stdio_definitions.lib is sufficient to satisfy most dependencies, including all of the dependencies in libraries included in the Windows SDK. However, there are some symbols that were removed from the Universal CRT for which it is not possible to provide compatibility symbols like this. These symbols include some functions (e.g., __iob_func) and the data exports (e.g., __imp___iob, __imp___pctype, __imp___mb_cur_max).
If you have a static library that was built with an older version of the C Runtime headers, we would recommend the following actions (in this order):
(1) Rebuild the static library using Visual C++ 2015 and the Universal CRT headers to support linking with the Universal CRT. This is the fully supported (and thus best) option.
(2) If you cannot (or do not want to) rebuild the static library, you may try linking with legacy_stdio_definitions.lib. If it satisfies the link-time dependencies of your static library, you will want to thoroughly test the static library as it is used in the binary, to make sure that it is not adversely
affected by any of the behavioral changes that were made to the Universal CRT (see https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT for details).
(3) If your static library’s dependencies are not satisfied by legacy_stdio_definitions.lib or if the library does not work with the Universal CRT due to the aforementioned behavioral changes, we would recommend encapsulating your static library into a DLL that you link with the correct version of the Microsoft C Runtime. For example, if the static library was built using Visual C++ 2013, you would want to build this DLL using Visual C++ 2013 and the Visual C++ 2013 libraries as well. By building the library into a DLL, you encapsulate the implementation detail that is its dependency on a particular version of the Microsoft C Runtime. (Note that you will want to be careful that the DLL interface does not “leak” details of which C Runtime it uses, e.g. by returning a FILE* across the DLL boundary or by returning a malloc-allocated pointer and expecting the caller to free it.)
We understand that this breaking change has been problematic for some of our customers, and we are continuing to investigate opportunities for improving the situation, both with the versions of the libraries that have been released and with future versions of the libraries. What I have described thus far is the current (and foreseeable) state.