Fun (?) with symbol visibility...
In the last days I had to deal with loading plugins via dlopen.
I expected, that if I have an executable, and load a plugin into it, that the stuff inside the executable (i.e. object files and static libraries) won't be used to resolve missing symbols in the plugin.
So, even if your executable links a library as static, its symbols are visible to plugins.
One more thing I didn't expect, is that even if no shared library is involved, the symbols, i.e. code in your executable is still visible to a plugin. Assume your plugin defines a class with the same name and the same method name, i.e. the same symbol.
Today I once ended up in a state where all the correct functions from the plugin were called, e.g. the correct ctor, but later on, when a virtual function of that object was called, it was the virtual function from the executable and not from the plugin. Weird. How could that happen ?
I added a little project in github for playing around with such stuff:
My conclusion so far is that in general you probably always want to build executables and static libraries with visibility=hidden. Not sure why this is not the default...
Update: Different behaviour with different compilers
I played around more and added an example to reproduce really strange behaviour on github in the vtabletest subdir.
In that example, I have an executable which implements a class Base and a class Derived, which is derived from it. Base has virtual and non-virtual functions, Derived reimplements both virtual functions.
What do you think happens ?
If symbols are not hidden, this did not happen with any of the three compilers. Instead, all three compilers created different results.
With g++, basically nothing from the plugin was called, Base ctor, Derived ctor and virtual and non-virtual function were all executing the code (symbols) from the executable. This was the most consistent and least messed up result.
With Intel icpc, it was more interesting.
clang offered yet another version.
But, there is an easy fix: hiding the visibility of the classes Base and Derived in the executable, or in the plugin makes everything work as expected, for all three compilers.
I plan to have a closer look at the created executables and libraries, using nm and looking at the assembly code...
I wonder if this issue isn't
I wonder if this issue isn't covered by One Definition Rule?
One definition rule
Kind of, yes.
But I think this is not the generic case. When the executable was built, everything was fine. When the plugin was built, everything was fine.
So for the C++ compiler everything was good.
The problem happens at runtime when those two things are combined via dlopen, and initially I assumed the symbols from a "normally" linked executable wouldn't be used to resolve symbols from a loaded shared library, especially not those which are defined in the shared library itself.