According to the gcc wiki on visibility (https://gcc.gnu.org/wiki/Visibility, see the section "Problems with C++ exceptions (please read!)"), but also seemingly an example (dynamic_cast failed when hiding symbol), hiding classes can lead to a valid dynamic_cast
failing.
I want to understand exactly when this happens by example: can anyone give me a small example to properly understand the effect?
Here is my try and understanding (using gcc >7 on Linux):
As far as I understand, what I need is vague linkage happening, which happens when the base class does not have a key method. So I tried with this base hierarchy:
class A {
virtual ~A();
virtual void print() = 0;
}
and the derived class:
class B : public A {
~B() override;
}
and then I'm going to have two implementation classes A_iml
and B_impl
just printing out their names:
#include "a.hpp"
class AImpl : public A {
~AImpl() override = default;
void print() override { printf("AImpl"); }
}
and
#include "b.hpp"
class BImpl : public B {
~BImpl() override = default;
void print() override { printf("BImpl"); }
}
Now from what I understand, I need to link those classes into two different shared libraries, hiding their symbols using -fvisibility-hidden
(and maybe -fvisibility-inlines-hidden
although this won't matter here).
This should then result in the vtables for the two classes being emitted (and hidden) in both shared libraries and a dynamic_cast
in one library using objects from the other library (for example) should fail because the two hierarchies are different.
For instance let the first library do the following:
factory.hpp:
class A;
A * create();
and factory.cpp
:
#include "factory.hpp"
#include "b_impl.hpp"
A * create() {
return new BImpl();
}
(I'll link this and the class hierarchy above into one shared library)
and the other one:
function.hpp
void doSomething();
function.cpp
#include "function.hpp"
#include "factory.hpp"
void doSomething() {
A * b = create();
b.print();
if(dynamic_cast<B *>(b)) printf("Cast was correct");
}
(Another shared library, linked against the first and the hierarchy above).
This is a slightly reduced example - of course, I'd wrap everything in namespaces, etc. but for the sake of brevity, I left those out. Looking at the objdump
and what not, it seems that both shared libraries actually contain a vtable for A
and B
and that one is hidden, but the cast succeeds: I'd just link both into some other library with a main function (for instance) and it prints "Cast was correct".
Can anybody help me change the example or point me to a different example in order to see how the effect can happen and understand its nuances?