开发者

dynamic_cast fails when used with dlopen/dlsym

开发者 https://www.devze.com 2022-12-21 17:33 出处:网络
Intro Let me apologise upfront for the long question. It is as short as I could make it, which is, unfortunately, not very short.

Intro

Let me apologise upfront for the long question. It is as short as I could make it, which is, unfortunately, not very short.

Setup

I have defined two interfaces, A and B:

class A // An interface
{
public:
  virtual ~A() {}

  virtual void whatever_A()=0;
};

class B // Another interface
{
public:
  virtual ~B() {}

  virtual void whatever_B()=0;
};

Then, I have a shared library "testc" constructing objects of class C, implementing both A and B, and then passing out pointers to their A-interface:

class C: public A, public B
{
public:
  C();
  ~C();

  virtual void whatever_A();
  virtual void whatever_B();
};

A* create()
{
  return new C();
}

Finally, I have a second shared library "testd", which takes a A* as input, and tries to cast it to a B*, using dynamic_cast

void process(A* a)
{
  B* b = dynamic_cast<B*>(a);
  if(b)
    b->whatever_B();
  else
    printf("Failed!\n");
}

Finally, I have main application, passing A*'s between the libraries:

A* a = create();
process(a);

Question

If I build my main application, linking against the 'testc' and 'testd' libraries, everything w开发者_开发知识库orks as expected. If, however, I modify the main application to not link against 'testc' and 'testd', but instead load them at runtime using dlopen/dlsym, then the dynamic_cast fails.

I do not understand why. Any clues?

Additional information

  • Tested with gcc 4.4.1, libc6 2.10.1 (Ubuntu 9.10)
  • Example code available


I found the answer to my question here. As I understand it, I need to make the typeinfo available in 'testc' available to the library 'testd'. To do this when using dlopen(), two extra things need to be done:

  • When linking the library, pass the linker the -E option, to make sure it exports all symbols to the executable, not just the ones that are unresolved in it (because there are none)
  • When loading the library with dlopen(), add the RTLD_GLOBAL option, to make sure symbols exported by testc are also available to testd


In general, gcc does not support RTTI across dlopen boundaries. I have personal experience with this messing up try/catch, but your problem looks like more of the same. Sadly, I'm afraid that you need to stick to simple stuff across dlopen.


I have to add to this question since I encountered this problem as well.

Even when providing -Wl,-E and using RTLD_GLOBAL, the dynamic_casts still failed. However, passing -Wl,-E in the actual application's linkage as well and not only in the library seem to have fixed it.


If one have no control over the source of the main application, -Wl,-E is not applicable. Passing -Wl,-E to the linker while building own binaries (the host so and the plugins) do not help either. In my case the only working solution was to load and unload my host so from the _init function of the host so itself using RTLD_GLOBAL flag (See code below). This solution works in both cases:

  1. the main application links against the host so.
  2. the main application loads host so using dlopen (without RTLD_GLOBAL).

In both cases one has to follow the instructions stated by gcc visibility wiki.

If one makes the symbols of the plugin and the host so visible to each other (by using #pragma GCC visibility push/pop or corresponding attribute) and loads the plugins (from the host so) by using RTLD_GLOBAL 1. will work also without loading and unloading the own so (as mentioned by link given above). This solution makes 2. also work which has not been the case before.


// get the path to the module itself
static std::string get_module_path() {
    Dl_info info;
    int res = dladdr( (void*)&get_module_path, &info);
    assert(res != 0); //failure...

    std::string module_path(info.dli_fname);
    assert(!module_path.empty()); // no name? should not happen!
    return module_path;
}

void __attribute__ ((constructor)) init_module() {
    std::string module = get_module_path();

    // here the magic happens :)
    // without this 2. fails
    dlclose(dlopen(module.c_str(), RTLD_LAZY | RTLD_GLOBAL));
}
0

精彩评论

暂无评论...
验证码 换一张
取 消