[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: dlsym() and C++




Magnus Norddahl wrote:

> > I think I could help you with that one. What are you trying to do
> > exactly? I have seen more than one experienced coder fall prey to some
> > dl intricacy, you wouldn't be the first one!
> 
> I've attached a tgz file with four files demonstrating the problem:
> 
>      If I use dlopen() and dlsym() to get a pointer to a global function in
>      a C++ library, the thing dumps core when I call it.
> 
>      If I use excatly the same procedure when it's a C library, everything
>      is fine, and a small message is printed to the screen.
> 
> I would be _very_ happy, if you can find some reason why this doesn't work.

It's all about symbol mangling. C++ stores signature information in the
symbols names. The trick is to put yourself in positions where that
mangling doesn't have to be used.

The symbol you were asking for was mangled in the lib, which made it
look like "ugga__Fv", but you were asking for "ugga", which didn't
exist. You get a NULL pointer instead, which you go right away and call,
causing a segfault.

coredumper1.patch is one way to fix it, that was found by simply using
"nm -C libcore.so", looking for what I wanted, noting the address, then
grepping the output of "nm libcore.so" for the mangled name (the -C
option interprets the mangling into human-readable stuff). I then used
the mangled symbol name ("ugga__Fv") as parameter to dlsym() instead of
the more natural one.

The typedef at the beginning was just for removing a warning (maybe
there's a way without the typedef, but I don't know it, so there). :-)

The problem with this is that some compilers mangle differently.

coredumper2.patch shows how to do it properly (it has to be applied
*over* the coredumper1.patch). You can see I do a prototype with an
'extern "C"', so that this function uses the "C naming convention" *for
symbols* (that part was missing in the Bjarne Stroustrup's book you were
reading), which means there is no mangling done on the symbol name.

I assume that one goal would be to be able to use C++ libraries from
other C++ programs. There is a big issue with this. C++ has problems. I
hate it in fact (I like Objective-C better in fact, the GNU runtime is
so fast (like C++ vtables) and the new style for method invocation is
just like C++). One specific problem that is visible when doing shared
libraries is the fragile base class. Say you have a shared library, with
header files. Since this is C++, you have class definitions in the
header files. Those are the culprit. The shared library absolutely must
very importantly be compiled with the very same header files that the
applications that will use that shared library used.

This is a confusing explanation, I know. Whatever. What happen is that
if the application has been compiled with a different header file than
the shared library it uses, it will most probably crash very soon.

All you need to know is that 'who has the class definition does the
"new"'. If the class definition is in the header file, when the
application will "new" the class, it could be different in size than
what the shared library expect, and the vtable in the shared library
could be different from what the application expect.

One trick that I use is this. In the header file:

class Foo {
 public:
 /* everything short of the constructor must be pure virtual */
 /* This is stable public interfaces that should change very rarely */
  virtual void doStuff() = 0;
};


extern "C" Foo* newFoo();

And in the .cpp file:

#include "foo.h"

class FooImpl: public Foo {
  /* all the stuff that changes all the time from version to
     version goes here */
 public:
  virtual void doStuff();
};

void FooImpl::doStuff() {
  printf("do the stuff here\n");
}

Foo* newFoo() {
  return new FooImpl;
}

Note that because Foo is pure virtual, it can't be "new"ed by the
application, the application *has* to go thru newFoo() to get one. And
what it get is FooImpl, that the application knows nothing about, but
works just like a Foo would. Since the class definition is in a .cpp
file, nothing can depend on the definition itself, so you avoid the
fragile base class problem. Changing Foo could be a problem though.

-- 
Pierre Phaneuf
Ludus Design, http://ludusdesign.com/
.