How is the shared object loaded? g++ uses the address of the
RTTI information to resolve dynamic_cast
. Traditionally, by
default in Unix, the first symbol to be loaded will be used by
all of the shared objects, so there will be no problem. This
depends on the mode used in dlopen
, however; if RTLD_LOCAL
is specified, the symbols in that shared object (and in shared
objects which are implicitly loaded as a result of loading that
shared object) won't be visible outside the shared object.
(I've had this problem with Java plug-ins. Java loads the
shared object with RTLD_LOCAL
, and dynamic_cast
won't work
accross shared objects loaded implicitly by the shared object
Java loads.)
With regards to the main executable: most Unix linkers will make
the symbols available, as if the executable had been loaded
using RTLD_GLOBAL
. Most, but not all; the GNU linker, for
example, does not do this, and symbols in the main executable
are not available for shared objects. If you need them to be
available, you must use the -rdynamic
option when building the
executable (which translates to the -export-dynamic
option to
the linker). Alternatively, if you need to break your
application down into separate shared objects, you might
consider putting practically everything in shared objects, and
making the main executable nothing but a simple library loader,
calling dlopen
on all of the shared objects, and then dlsym
to get the address of the actual function you want to execute,
and call it through the address. (This is basically how we
solved the problem with the Java plug-ins. All Java loaded was
our loader module, which then did the dlopen
. By doing them
explicitly, we could control the options to dlopen
.)
EDIT:
On rereading your question, I'm not sure this is the right
answer. You're passing through a void*
, which means that you
have no access to the RTTI (or even to vtables). The rules
concerning void*
(C++ rules, this time, not g++) are clear:
the only thing you can do with a void*
is convert it back to
the original pointer type. In particular, the sequence
Derived*
→ void*
→ Base*
is undefined behavior.
It will typically work if only single inheritance is involved
(but even then it is still undefined behavior), but not
otherwise. So if the shared object converts an A*
to
a void*
, and the void*
is later converted to a B*
, you
have undefined behavior, and shouldn't expect it to work.
Converting the void*
to an A*
first, and then converting it
to a B*
, should work. Even better, however: declare the
function you're calling to return an A*
, rather than
a void*
. In general, you should avoid void*
as much as
possible, especially in C++.