dynamic_cast failed when hiding symbol

2k views Asked by At

I have many static libraries. One is static_lib_a.a. I create a dynamic library, dynamic_lib.so to put them together.

In static_lib_a.a, it uses xerces 3.1.1 to parse xml. The following is the code snippet in static_lib_a.a

xerces::DOMElement *pElementNode = dynamic_cast<xerces::DOMElement *>(pNode);

The type of pNode is xerces::DOMNode. It is assigned to an object of xerces::DOMElement. This line of code will do downcasting.

In order to hide all symbols of static_lib_a.a in dynamic_lib.so, I use -fvisibility=hidden to build this static library. I found if I add -fvisibility=hidden, pElementNode will return a NULL pointer in runtime.

Version of gcc compiler is 3.4.4.

Does anyone have similar issues before?

3

There are 3 answers

1
Troubadour On BEST ANSWER

The root of your problem is described on the gcc wiki under the section entitled "Problems with C++ exceptions". Make sure you follow the "vague linkage" link there and read the sections on virtual tables and typeinfo.

This all applies to your case because the classes xerces::DOMNode and xerces::DOMElement contain no non-pure, non-inline virtual functions (in fact these classes are contained entirely in headers). This means that the virtual table for either class is emitted in every object file that includes its header.

The typeinfo symbols for either class, which are required for dynamic_cast to work properly, are emitted in the same object as the virtual table i.e. in every object file that includes its header.

When you marked your library with hidden visibility all the typeinfo symbols for xerces::DOMNode and xerces::DOMElement in objects from static_lib_a.a were marked hidden. As the wiki page points out, this ensures that the linker will then mark it as hidden in dynamic_lib.so and your dynamic_cast will fail.

0
CashCow On

Using hidden visibility is a great way to ensure your library is only being used through specified access points. This is a huge advantage if you ever modify it, as you know exactly how your library is being used externally and therefore the limitation of what you might potentially break.

It is a very similar technique to Windows making you declspec what is part of the accessible part of a DLL, except the slight difference that you do not state if you are importing or exporting, and your library may therefore be making "visible" functions it uses rather than implements.

To answer your question though, I think visibility is only supported version 4 onward. Certainly we use it here with this

#if defined(__GNUC__) && __GNUC__ >= 4

When you use hidden visibility, you need to explicity state which symbols you do wish to be visible. Thus you have this:

__attribute__((visibility("default")))

which you will probably #define to be something more readable, maybe SO_EXPORT thus:

#define SO_EXPORT __attribute__((visibility("default")))

The define classes:

class SO_EXPORT MyAccessInterface;

and methods something like:

SO_EXPORT int doSomething( parameters );

We actually also have a similar macros for hidden visibility (as above but with "hidden" instead of "default"). Thus even when we use "default" visibility for the whole project we can hide some implementation detail.

#define SO_HIDDEN __attribute__((visibility("hidden")))

class SO_HIDDEN MyClassImpl;

2
Simon Richter On

dynamic_cast requires the typeinfo node for the derived class to point at the typeinfo node for the base class, which is done with a dynamic relocation. If the symbol for the typeinfo node is not visible, then the module containing the node for the derived class will have its own copy, and dynamic_cast then assumes that these classes are from distinct trees and disallows the cast.

You need to add an attribute declaring default visibility to the base class definition.