Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can some libraries built by older compilers link against modern code, and others cannot?

We have a lot of prebuilt libraries (via CMake mostly), built using Visual Studio 2017 v141. When we try to use these against a project using Visual STudio 2019 v142 we see errors like:

Error C1047 The object or library file ‘boost_chrono-vc141-mt-gd-x32-1_68.lib’ was created by a different version of the compiler than other objects...

On the other hand, we also use pre-compiled .libs from 3rd-party vendors which are over a decade old and these have worked just fine when linked against our codebase.

What determines whether a library needs to be rebuilt, and why can some ancient libraries still be used when others that are only one version behind cannot?

like image 288
Mr. Boy Avatar asked Jul 21 '21 15:07

Mr. Boy


People also ask

Does a compiler link to library code?

Technically your compiler does not figure out which libraries will be used. The linker (commonly ld ) does this. The header files only tell the compiler what interface your library functions use and leaves it up to the linker to figure out where they are.

Was created with an older compiler than other objects?

With reference to MSDN, this error The object or library file 'file' was created with an older compiler than other objects; rebuild old objects and libraries is caused when object files or libraries built with /LTCG are linked together, but where those object files or libraries are built with different versions of the ...

Why different compilers give different results?

This is because there is no sequence point anywhere in that expression. The only requirement is that each of those subexpressions is evaluated before the result is needed (that is, before the result is to be printed).

Can different compilers give different results?

Hi, Yes, it is also possible for results to differ when the compiler version is different.

Which C++ libraries are binary-compatible?

The ABIs, object formats, and runtime libraries are incompatible. We've changed this behavior in Visual Studio 2015, 2017, and 2019. The runtime libraries and apps compiled by any of these versions of the compiler are binary-compatible. It's reflected in the C++ toolset major number, which starts with 14 for all three versions.

Why do I need a library header file when compiling?

This means that when compiling they require inclusion of library header files when you create a .o file, as well as a linking stage when creating an executable. Let’s take a look at how we can link to a library called Google test, which performs modular code testing for files. For more information on Google testing visit our post here.

Can I link different versions of the same library?

You can't link object files, static libraries, dynamic libraries, and executables built by different versions of these toolsets. The ABIs, object formats, and runtime libraries are incompatible.

Why do some C++ libraries not require explicit inclusion or linking?

Some libraries do not require explicit inclusion or linking becuase the compiler g++ already knows where to search. For example, iostream is actually a header file which operates the same way as any other library. It’s placement is computer specific, but mine is stored in /usr/include/c++/4.8/iostream along with other standard c++ header files.


Video Answer


2 Answers

ABI incompatibilities could cause some issues. Even though the C++ standard calls for objects such as std::vector and std::mutex and that they need to have specific public/protected members, how these classes are made is left to the implementation.

In practice, it means that nothing prevents the GNU standard library from having their data fields in another orders than the LLVM standard library, or having completely different private members.

As such, if you try to use a function from a library built with the LLVM libc++ by sending it a GNU libstdc++ vector it causes UB. Even on the same standard library, different versions could have changed something and that could be a problem.

To avoid these issues, popular C++ libraries only use C data structures in their ABIs since (at least for now) every compiler produces the same memory layout for a char*, an int or a struct.

These ABI issues can appears in two places:

  • When you use dynamic libraries (.so and .dll files) your compiler probably won't say anything and you'll get undefined behavior when you call a function of the library using incompatible C++ objects.
  • When you use static libraries (.a and .lib files) I'm not really sure, I'm guessing it could either print an error if it sees there's gonna be a problem or successfully compile some Frankenstein monster of a binary that will behave like the above point
like image 165
Tzig Avatar answered Oct 24 '22 18:10

Tzig


I will try to answer some integral parts, but be aware this answer could be incomplete. With more information from peers we will maybe be able to construct a full answer!

The simples kind of linking is linking towards a C library. Since there is no concept of classes and overloading function names, the compiler creators are able to create entry points to functions by their pure name. This seems to be pretty much quasi-standardized since, I myself, haven't encountered a pure C library not at least linkable to my projects. You can select this behaviour in C++ code by prepending a function declaration with extern "C". (This also makes it easy to link against a library from C# code) Here is a detailed explanation about extern "C". But as far as I am aware this behaviour is not standardized; it is just so simple - it seems - there is just one sane solution.

Going into C++ we start to encounter function, variable and struct names repeating. Lets just talk about overloaded functions here. For that compiler creators have to come up with some kind of mapping between void a(); void a(int x); void a(char x); ... and their respective library representation. Since this process also is not standardized (see this thread) and this process is far more complex than the 1 to 1 mapping of C, the ABIs of different compilers or even compiler versions can differ in any way.

Now given two compilers (or linkers I couldn't find a resource wich specifies wich one exactly is responsible for the mangling but since this process is not standardized it could be also outsourced to cthulhu) with different name mangling schemes, create following function entry points (simplified):

compiler1
_a_
_a_int_
_a_char_

compiler2
_a_NULL_
_a_++INT++_
_a_++CHAR++_

Different linkers will not understand the output of your particular process; linker1 will try to search for _a_int_ in a library containing only _a_++INT++_. Since linkers can't use fuzzy string comparison (this could lead to a apocalypse imho) it won't find your function in the library. Also don't be fooled by the simplicity of this example: For every feature like namespace, class, method etc. there has to be a method implemented to map a function name to a entry point or memory structure.

Given your example you are lucky you use libraries of the same publisher who coded some logic to detect old libraries. Usually you will get something along the lines of <something> could not be resolved or some other convoluted, irritating and/or unhelpful error message.

Some info and experience dump regarding Visual Studio and libraries in general:

  • In general the Visual C++ suite doesn't support crosslinked libs between different versions but you could be lucky and it works. Don't rely on it.
  • Since VC++ 2015 the ABI of the libraries is guaranteed by microsoft to be compatible as drescherjm commented: link to microsoft documentation
  • In general when using libraries from different suites you should always be cautious as n. 1.8e9-where's-my-share m. commented here (here is your share btw) about dependencies to other libraries and runtimes. In general in general not having the control over how libraries are built is a huge pita

Edit addressing memory layout incompatibilities in addition to Tzigs answer: different name mangling schemes seem to be partially intentional to protect users against linkage against incompatible libraries. This answer goes into detail about it. The relevant passage from gcc docs:

G++ does not do name mangling in the same way as other C++ compilers. This means that object files compiled with one compiler cannot be used with another.

This effect is intentional [...].

like image 36
Baumflaum Avatar answered Oct 24 '22 20:10

Baumflaum