7

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?

Martin
  • 237
  • 2
  • 9
  • How does `function.cpp` know that `B` is a class? As the code stands now, it there is an include of `function.hpp` which consists of just a function declaration, and an include of `factory.hpp` which consists of a declaration of the incomplete type `A` and a function declaration. Shouldn't compilation fail? – JaMiT Jun 18 '18 at 08:31

1 Answers1

0

vtable for those object is not hidden, otherwise calling virtual methods would be impossible. There is a pointer to vtable in each class instance. The problem may arise if typeinfo for those objects is not available, for example if dll is compiled with -fno-rtti. vtable will be still available, however dynamic cast won't work.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • kind reminder, the question was: _"When does dynamic_cast fail due to hidden symbols?"_ – YSC Jun 18 '18 at 07:57