I have encountered an unexpected Access Error while running a project I've built using two different versions of Visual Studio. My general configuration is as follows:
LibA is a static lib, static runtime linkage, msvc 8.0
LibB is a static lib, static runtime linkage, msvc 9.0
My target project for integration is a msvc 9.0 COM dll, which statically links the above libraries
This project builds, but crashes up at runtime with an access violation in some STL code. The stack seems to indicate that I've passed through headers both versions (8 and 9) during a call into a stream insertion operator. I realize that this is a problem.
Somehow, this call:
ost << std::dec << port_; //(originating from an object in LibA)
...descends through the following stack trace:
std::basic_ostream::operator<<(...) (ostream:283, msvc 8.0 version <-- expected, since LibA was built with this version)
std::num_put::put(...) (xlocnum:888, msvc 8.0 version <-- expected, since LibA was built with this version)
std::num_put::do_put(...) (xlocnum:1158, msvc 9.0 version!! !##$!%! <-- not expected, since LibA was built with msvc 8.0)
std::ios_base::flags() (xiosbase:374, msvc 9.0 version <-- follows from above)
The access violation happens in std::ios_base::flags(). I suspect it is due to the mix of implementations in the call stack (although I am not sure).
My questions are.
1.) Is the likely cause of this access violation the mixing of msvc header implementations?
2.) Is there a way to prevent these implementations from mixing?
3.) Is there a better way to configure these three projects for integration (assuming moving LibA from msvc 8.0 is undesirable)?
I am aware of the ideas raised in this question and this one. Here I am most interested in this specific problem, and if there is some way to avoid it.
Any insights would be appreciated.
You can't use different STL implementation in the same project. This means even different versions from the same compiler. If your LibA has a function that accepts std::vector as an argument you are only allowed to pass vector object from STL that LibA was built with. This is why many C++ libraries expose only C API.
Either you change your API or you rebuild all your projects using the same compiler.
You are doing something that you shouldn't. You are in the world of undefined behavior. There is no point in trying to debug this particular crash. Even if you managed to make this line work you would get a new crash somewhere else.
There is no guarantee of library binary compatibility between major versions of MSVC. STL code is mostly template code that gets expanded into your code. So you're static libraries probably have incompatible chunks of STL code inside of them.
In general, this shouldn't be a problem, unless that STL code is part of the interface to the library. For example, if you pass iterators or a reference to a vector from one library to another, you're in trouble.
The best solution is to build everything with the same version of the compiler. If you can't do that (e.g., if the one of the libraries is from a third-party), you're probably stuck.
Related
I am trying to maintain a large C++ toolkit: GDCM. The core library is compatible with C++11, but one component (CharLS) is a C++98 only library.
How can I use CMake (monolithic project) so that C++ flags from the toplevel project are not passed down to C++98 only library.
For instance a user may want to exercise the library using clang-3.9 new option: -Wundefined-func-template, however this flag should not propagate down to the CharLS compilation line.
I've used an naive fix (here), but I (actually upstream too) would rather keep CharLS a C++98 (only!) library and not stuff in a C++11 construct.
How one would do that since -Wundefined-func-template is not deactivated when used in combination with -std=c++98.
The definition of mbstate_t has changed from:
typedef int mbstate_t;
to
typedef struct _Mbstatet
{ // state of a multibyte translation
unsigned long _Wchar;
unsigned short _Byte, _State;
} _Mbstatet;
typedef _Mbstatet mbstate_t;
This is not backwards compatible and seems to imply all components of a Windows desktop application will need recompiling with VS 2015 if I am to use VS 2015. Obviously "all" is too strong here, only components using mbstate_t are affected. However, that's still not a good situation.
Are we expected to recompile if we migrate to VS 2015? Am I missing something here which means this isn't an issue?
Given that this struct has changed I have bigger concerns that there may be other breaking changes between VS 2013 and 2015. Is there a list of these anywhere?
Note:
I've already asked this question on MSDN but my gut tells me more eyes will be on it on Stack Overflow: https://social.msdn.microsoft.com/Forums/vstudio/en-US/8e50f348-0b6d-442c-8a1e-b1b8a4288fbc/mbstatet-is-not-backwards-compatible-between-vs-2015-and-vs-2013?forum=vcgeneral#8e50f348-0b6d-442c-8a1e-b1b8a4288fbc
To answer the question. Yes, you are expected to recompile everything if you wish to update to newer versions of Visual Studio. MS reserve the right to change any part of toolchain and libraries. There are clear examples of C headers and C++ headers changing. Plenty of other things could change as well which would make binary compatibility between versions difficult or impossible.
Some discussion / opinion
In my mind it is not 100% obvious that you'd need to recompile everything when updating to a newer version of the toolchain. Forcing a user to recompile everything to update to a new toolchain can be quite burdensome. In large projects you may be dependent on a lot of 3rd party code which you have no way of recompiling. Many SDK expend a lot of energy in ensuring backwards compatibilty so that users don't have to do this.
Interestingly the linker makes no effort to disallow linking against libraries built with older versions of the toolchain. It doesn't even warn! This behaviour might imply that it's acceptable to link old libraries with new code.
Anyway, I could waffle about this all day. One thing I wanted to point out is that if you know what you're doing then it is possible to link binaries built with older versions of VS against newer versions. You have to be very clear about what declarations (effectively your header files) your code was built against and know that these declarations either haven't changed or they have changed in a way that doesn't cause runtime errors. You also have to be sure that the compiler hasn't changed the way it handles builtins/intrinsics etc. in a sufficient way to hurt you. For example, the use of a try/catch block with MSVC will cause code to be automatically generated to handle exceptions. This will lead to implicit calls to functions provided by the runtime libs. That could easily change version to version and you could be left with undefined symbols or unexpected behaviour.
Proceed with caution :)
It used to be that Visual C++ actually did try to maintain C runtime library backwards compatibility at the object file (and thus static library) level. Opaque types like mbstate_t might change their internal representation, but they wouldn't change their size. This allowed an object file compiled with older versions of the CRT headers to work when linked with a newer version of the CRT.
(Note that this doesn't apply when CRT objects are passed across DLL boundaries. Multiple CRTs in the same program, whether statically or dynamically linked, are not compatible.)
This mostly only applied to C code however. The Standard C++ Library was not backwards compatible at all, and there are actually linker checks to enforce this. (Though not if the out of date object files were compiled with Visual Studio 2008 or earlier.) Also the C++ ABI is subject to change between major releases, so potentially the layout of C++ objects can change.
However as you've noticed with Visual Studio 2015, the C runtime library is no longer backwards compatible. The CRT went through a major refactoring and many things changed. Most of it is no even longer part of Visual Studio, and instead is now a part of the Windows operating system. As you've also noticed, there's no error or warning when you to try to link older binaries with the new CRT. As the old CRT didn't include any version symbols in the compiled object files, so there's way it can know if they were compiled using the old CRT headers.
When I need to build some third party library to be used in several of my projects under different version of MSVC, I usually build it for every MSVC version and for both Debug and Release configurations. That's what boost does, and that's what we have been done for our whole life in my team.
However, I still don't get, why couldn't I just build this library with like... whatever. All I need is function prototype and object code, right? Since I'm linking CRT statically, I have no external dependencies. But when I'm trying to link library built in Release under MSVC8 with my project in Debug under MSVC10 I have this annoying "already defined" linker errors which we all hate so much.
But why? Can I just "encapsulate" all this functions inside lib and do not export them so that my project will take only what it needs from the lib? Why can I have precompiled version of libpng and zlib which I can link in every project? Yes, they are not build using MSVC, I guess, but the still uses the same functions of CRT. So can anyone please explain in depth or share a link to some enlightened explanation of this issue?
Since I'm linking CRT statically, I have no external dependencies
Well, that's not true, you do have a dependency. On the static version of the CRT. Debug or Release, depending on your build settings. And it is an external dependency, the linker glues the CRT later, when the library gets linked. The code that uses the library also has a dependency on the CRT. And if the compile settings don't match then the linker barfs.
You isolate that dependency by building a DLL instead of a static link library. You must further ensure that the exported functions don't cause a CRT dependency. You can't return a C++ object from the standard C++ library and can't return a pointer to an object that needs to be released by the client code. Even passing structures is tricky since their packing is an implementation detail, but you usually get away with it. A good practical example is COM automation, it forces you into using a subset of types that are universal. Windows is rife with them and all these servers work with any version of the compiler or CRT. Even any language. This however comes at a cost, writing such a library isn't as simple or convenient as just throwing a bunch of code in a static lib.
Which headers should I not use if I don't want my program to be linked with any of msvc*.dll ?
At the moment my application uses:
kernel32
user32
shell32
msvcp90
msvcr90
I want to get rid of the bottom two files. I don't mind if I will have to rewrite certain aspects of the program.
Because I know if you code in C and then link it won't link any msvc's
I believe you have to change the way the CRT is linked into your program. I think for that you have to change the C++->Code Generation->Runtime-Library to the static version. This is for Visual Studio 2005, don't know about newer versions.
Those libraries contain the C++ runtime - heap management and other stuff hard to get rid from.
You could link the C++ statically instead - use "C++ -> Code Generation -> Runtime Library" setting. Then you will not need those .dll files. However this is not the recommended way - if a vulnerability is found in the C++ runtime you'll have to recompile and reship your program.
Static link is the right answer. A related bit of advice is to use depends.exe to see what functions your exe is actually hitting in the dependent dlls. Those dependencies might be due to explicit use on your part or due to CRT implementation that you don't explicitly invoke.
Is it possible to use a C++ static library (.lib) compiled using Visual Studio 6 in Visual Studio 2008?
It really depends. Does the lib expose only 'extern "C"' functions where memory is either managed by straight Win32 methods (CoTaskMemAlloc, etc) or the caller never frees memory allocated by the callee or vice-versa? Do you only rely on basic libraries that haven't changed much since VS 6? If so, you should be fine.
There are 2 basic things to watch for. Changes to global variables used by 3rd-party libraries, and changes to the structure of structs, classes, etc defined by those 3rd-party libraries. For example, the CRT memory allocator has probably changed its hidden allocation management structures between the 2 versions, so having one version of the library allocate a piece of memory and having another free it will probably cause a crash.
As another example, if you expose C++ classes through the interface and they rely on MS runtime libraries like MFC, there's a chance that the class layout has changed between VS 6 and VS 2008. That means that accessing a member/field on the class could go to the wrong thing and cause unpredictable results. You're probably hosed if the .lib uses MFC in any capacity. MFC defines and internally uses tons of globals, and any access to MFC globals by the operations in the .lib could cause failures if the MFC infrastructure has changed in the hosting environment (it has changed a lot since VS 6, BTW).
I haven't explored exactly what changes were made in the MFC headers, but I've seen unpredictable behavior between MFC/ATL-based class binaries compiled in different VS versions.
On top of those issues, there's a risk for functions like strtok() that rely on static global variables defined in the run-time libraries. I'm not sure, but I'm concerned those static variables may not get initialized properly if you use a client expecting the single-threaded CRT on a thread created on the multi-threaded CRT. Look at the documentation for _beginthread() for more info.
I shouldn't think why not - as long as you keep the usual CRT memory boundaries (ie if you allocate memory inside a library function, always free it from inside the library - by calling a function in the lib to do the freeing).
this approach works fine for dlls compiled with all kinds of compilers, statically linked libs should be ok too.
Yes. There should be no issues with this at all. As gbjbaanb mentioned, you need to mind your memory, but VS2008 will still work with it. As long as you are not trying to mix CLR, (managed) code with it. I'd recommend against that if at all possible. But, if you are talking about raw C or C++ code, sure, it'll work.
What exactly are you planning on using? (What is in this library?) Have you tried it already, but are having issues, or are you just checking before you waste a bunch of time trying to get something to work that just wont?
Sure it'll work.
Are you asking where in VS2008 to code the references?
If so, go to proj props -> Linker -> Input on Configuration properties on the property pages. Look for "additional dependencies" and code the .LIB there.
Go to proj props -> Linker -> General and code the libs path in "Additional Library Directories".
That should do it!!
There are cases were the answer is no, when we moved from VS6 to VS2k5 we had to rebuild all our libraries, as the memory model had changed, and the CRT functions where different.
There were a handful of breaking changes between VC6, VS2003, VS2005 and VS2008. Visual C++ (in VS2005) stopped support for single-threaded, statically linked CRT library. Some breaking changes enumerated here and here. Those changes will impact your use of VC6 built libs in later versions.