Use C++ DLLs from the same VS compiled at different times/teams - ABI compatibility?
Asked Answered
E

1

10

To repeat: I'm looking for ABI compatibility between libraries of the same Visual-C++ version!

We want to mix and match some internal C++ DLLs from different teams - built at different times with different project files. Because of long build times, we exactly want to avoid large monolithic builds where each team re-compiles the source code of another team's library.

When consuming C++ DLLs with C++ interfaces it is rather clear that you only can do this if all DLLs are compiled with the same compiler / Visual Studio version.

What is not readily apparent to me is what, exactly needs to be the same to get ABI compatibility.

  • Obviously debug (_DEBUG) and release (NDEBUG) cannot be mixed -- but that's also apparent from the fact that these link to different versions of the shared runtime.
  • Do you need the exact same compiler version, or is it sufficient that the resulting DLL links to the same shared C++ runtime -- that is, basically to the same redistributable? (I think static doesn't fly when passing full C++ objects around)
  • Is there a documented list of compiler (and linker) options that need to be the same for two C++ DLLs of the same vc++ version to be compatible?
    • For example, is the same /O switch necessary - does the optimization level affect ABI compatibility´? (I'm pretty sure not.)
    • Or do both version have to use the same /EH switch?
    • Or /volatile:ms|iso ... ?

Essentially, I'd like to come up with a set of (meta-)data to associate with a Visual-C++ DLL that describes it's ABI compatibility.

If differences exist, my focus is on VS2015 only at the moment.

Epicedium answered 21/10, 2016 at 14:10 Comment(3)
In general, you should try to ensure the same version of CRT for all binaries. It might work otherwise, but not guaranteed. I can't answer to the C++ compiler settings, but it is a best practice to standardize all compiler / linker settings across your teams.Polydipsia
_ITERATOR_DEBUG_LEVEL is also a thing that must be equal , since it influences the std-lib object layout.Epicedium
github.com/microsoft/vcpkg/blob/master/docs/users/triplets.mdEpicedium
E
3

Have been thinking this through the last days, and what I did do was to try to see if some use-cases exists where devs have already needed to categorize their C++ build to make sure binaries are compatible.

One such place is the Native Packages from nuget. So I looked at one package there, specifically the cpprestsdk:

The binaries in the downloadable package as split like this:

native\v120\windesktop\msvcstl\dyn\rt-dyn\x64\Release\
        ^      ^         ^      ^    ^     
  VS version   |       not sure |    uses cpp-runtime dynamically
               |               lib itself dynamic (as opposed to static)
    or WinXP or WinApp(WinRT?)
                 

I pulled this out from this example, because I couldn't find any other docs. I also know that the boost binaries build directory is separated in a similar way.

So, to get to a list of meta data to identify the ABI compatibility, I can preliminarily list the following:

  • VC version (that is, the version of the C and CPP runtime libraries used)
    • one point here is that e.g. vc140 should be enough nowadays - given how the CRT is linked in, all possible bugfixes to the versioned CRT components must be ABI compatible anyway, so it shouldn't matter which version a given precompiled library was built with.
  • pure native | managed (/CLI) | WinRT
  • how the CRT is consumed (statically / dynamically)
  • bitness / platform (Win32, x64, ARM, etc.)
  • Release or Debug version (i.e. which version of the CRT we link to)
  • plus: _ITERATOR_DEBUG_LEVEL ... if everyone goes with the defaults, fine, if a project does not, it must declare so

Additionally my best guess as to the following items:

  • /O must not matter - we constantly mix&match binaries with different optimization settings - specifically, this is even working for object files within the same binary
  • /volatile - since this is a code-gen thing, I have a hard time imagining how this could break an ABI
  • /EH - except for the option to disable all exception, in which case you obviously can't call anything that throws, I'm pretty confident this is save from an ABI perspective: There are possible pitfalls here, but I think they can't really be categorized into ABI compat. (Maybe some complex callback chains could be said to be ABI incompatible, not sure)

Others:

  • Default calling convention (/G..) : I think this would break at link time, when mangled export symbols and header declarations don't match up.
  • /Zc:wchar_t - will break at link time (It's actually ABI compatible, but the symbols won't macth.)
  • Enable RTTI (/GR) - not too sure 'bout this one - I never have worked with this disabled.
Epicedium answered 26/10, 2016 at 20:18 Comment(2)
Very helpful answer. I wonder if "Character Set" also is an issue; Unicode vs MBCS vs ASCII?Chain
@Chain - I'm pretty sure UNICODE vs MBCS does not matter, because all these settings macros do is compile in different APIs, e.g. FoobarA() vs FoobarW() etc.Epicedium

© 2022 - 2024 — McMap. All rights reserved.