Errors when linking to protobuf 3 on MS Visual C

2019-01-08 00:51发布

问题:

Encountered on Visual Studio 2013, but it's reproducible with any version.

I cloned the protocol buffer library from github, ran CMake-gui on it (I left everything to default, so it's the static version), only built libprotobuf (other project failed for some reason, cmd.exe error, might have something to do with tests, but libprotobuf builds fine).

My project uses headers generated with the .proto file found on the mapbox vector tiles spec's github.

When I link, I first have this error

Error 1 error C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' s:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility

I tried disabling it with -D_SCL_SECURE_NO_WARNINGS in additional command line arguments, but then I have other errors:

Error 1 error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in main.obj S:\eiogit3\misc-projs\mapload\mapload\libprotobufd.lib(common.obj)

回答1:

It's a mismatch of how the VStudio C (and C++) runtime library (VCRTLib) is used by your project and by libprotobuf project. Let me detail:

Let's say there's some C code. The purpose of that code is to be run. Than can be achieved:

  • Directly: including that code in an VC Application type project - which will generate an .exe
  • Indirectly: including the code in an VC Library type project - which will generate a library which will only be able to run when called from another .exe (that calls that library). The library can be:
    • static: all the C code will be compiled and stored in a .lib file. You will need that file when using the library in another project (whether it's an application or a library) - at link time. Note that all the needed code from your .lib will be "copied" into the other project
    • dynamic: you will have 2 files now: a .dll file which will contain the compiled (and linked) code, and a .lib file1 which will contain "pointers" (if you will) to the code in the .dll file. When using the library in another project, you will also need the .lib file at link time, but now it won't contain the code so it won't be copied in the other library (the other library will be smaller), but at runtime the other library will need the .dll file

You can check [SO]: LNK2005 Error in CLR Windows Form (@CristiFati's answer) for details of how C code gets to be transformed in executable format. Also Google is full of articles about differences between static and dynamic libraries, when to use one or the other, an example can be found on [SO]: When to use dynamic vs. static libraries.

As you guessed, the CRT or C runtime library (that contains the underlying system that makes C code able to run - one example are memory management functions: malloc, free) makes no exception - it's the equivalent of Ux's libc.a (static or archive) vs. libc.so (dynamic or shared object) - but in VStudio it's a little bit more complicated:

  • Static CRT resides in libcmt.lib
  • Dynamic CRT resides in msvcrt.lib which "points" to msvcr###.dll2 (msvcr120.dll for VStudio 2013)

Notes:

  • A "d" at the end of the library name (msvcrd.lib), means that it's compiled with debug symbols
  • C++ runtime library is under the exact situation; the names have an extra p: libcpmt.lib, msvcprt.lib, msvcp120.dll
  • For more details, check [MS.Docs]: CRT Library Features

Now, VCRTLib parts, are not included in the project like any other lib (Project Properties -> Linker -> Input -> Additional Dependencies), but because their nature (static or dynamic) is required at compile time they are configured from: [MSDN]: /MD, /MT, /LD (Use Run-Time Library), where there are 4 available choices:

  1. Multi-threaded (/MT)
  2. Multi-threaded Debug (/MTd)
  3. Multi-threaded DLL (/MD)
  4. Multi-threaded Debug DLL (/MDd)

Obviously, the ones that contain "Debug" are when building for Debug configuration while the other ones for Release; the key point is that the ones that have DLL are using the dynamic runtime version, while the other ones the static version.

Back to your error: the linker complains that main.obj (part of your project) has MDd_DynamicDebug (linking against the dynamic debug version), while common.obj (part of libprotobuf project) has MTd_StaticDebug (linking against the static debug version), so you link against 2 runtimes in the same executable (or .dll) - which is not possible.

In order to fix it, you should make sure that both libprotobuf and your main project have the same value for VCRTLib.
Of course it's simpler to change your main project setting to match libprotobuf's one, but it's recommended to use the dynamic runtime version (things can get messy in larger projects that have .dlls involved) even if this requires to recompile libprotobuf (well, if changing that option generates errors that make libprotobuf very hard to build, and your project is going to stay this simple, you can use the static VCRTLib).

Note: Not to mistake VCRTLib type (static / dynamic) with the way libprotobuf is being built (static at this point, but I'm sure that it can be built as dynamic too).

@EDIT0: Adding some additional info on the above note, as some comments requested it, and it might be useful to other users.

There are 2 aspects about a library (including libprotobuf), that are totally unrelated:

  1. Library type (the way it is being built): dynamic / static
  2. VCRTLib type (the way it uses VCRTLib): again, dynamic / static

So, there are 4 perfectly valid combinations:

  1. Dynamic library using dynamic VCRTLib
  2. Dynamic library using static VCRTLib
  3. Static library using dynamic VCRTLib
  4. Static library using static VCRTLib

For libprotobuf, each of the aspects is controlled by a boolean cmake option:

  1. Library type: protobuf_BUILD_SHARED_LIBS
  2. VCRTLib type: protobuf_MSVC_STATIC_RUNTIME

The 2 flags can be set by either:

  • cmake-gui
  • cmake cmdline (passing them as arguments - e.g.: -Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF)

The above 4 combinations are thus possible (at least in v3.5), but #2. is disabled by default (specifying -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON will build a .dll which will link to the dynamic VCRTLib), in order to avoid possible runtime problems, and enabling it requires manual intervention.

For more details regarding build instructions (via cmake), check: [GitHub]: protocolbuffers/protobuf - (master) protobuf/cmake/README.md.



1: The .lib file will only be created if the library exports symbols, as it wouldn't make sense otherwise (nothing needed at link time, and the .dll will be created, but pretty much unusable)

2: For newer VStudio versions (starting with 2015), the msvcr(t) part has been replaced by vcruntime (or at least this is the entrypoint, as it was split in smaller logical pieces - [SO]: How to circumvent Windows Universal CRT headers dependency on vcruntime.h (@CristiFati's answer))