7

I am getting undefined symbol error while loading library dynamically. Here is my code snippet that generates this error :

int main ()
{

    void *lib_handle = NULL;

    MyClass* (*create)();
    void (*destroy)(MyClass*);
    char *error;


    lib_handle = dlopen ("./libshared.so", RTLD_LAZY);

    if (lib_handle == NULL) 
    {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);

    } 

    create = (MyClass* (*)()) dlsym(lib_handle, "create_object");
    if ((error = dlerror()) != NULL)  
   {
      fprintf(stderr, "%s\n", error);
      exit(1);
   }

    destroy = (void (*)(MyClass*)) dlsym(lib_handle, "destroy_object");

    MyClass *myClass = (MyClass*) create;
    destroy(myClass);   

    dlclose(lib_handle);
}

But when I load library simply by commenting above code and exporting library path everything works like charm.

For dynamic linking I am using the following command on command prompt.

g++ -Wl,--export-dynamic shared_user.cpp -ldl

Any help would be appreciated.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Yuvi
  • 1,344
  • 4
  • 24
  • 46
  • 1
    Ever heard the term "_name mangling_" ? That's what's probably happening here, you've got C++ code but are looking for an unmangled name. If you want this to work, declare the factory function as `extern "C" { create_object(...) ... }` in your code. Beyond that, you need to invoke it as `create()` because you're not performing a function call otherwise. – FrankH. Mar 23 '12 at 10:35
  • And so tag this as C++ and not C – Eregrith Mar 23 '12 at 10:38
  • 1
    To clarify: You need the `extern "C" { ... }` bit in the sourcecode for `libshared.so` to get the symbol names right. Also, if you want to test whether my hypothesis is correct, simply run "nm libshared.so" and see what the _actual_ names are. – FrankH. Mar 23 '12 at 10:39

1 Answers1

14

You're very likely seeing Name Mangling in action here.

If you want to use dlopen() / dlsym() with C++ shared libraries, you either need to:

  1. declare the functions you want to lookup through dlsym() as extern "C" { ... } so that the C++ compiler creates unmangled names for them.
    This is only possible if the function you try to access is a non-member or static member function, and not overloaded (only a single signature); C++ can't create unmangled names in other situations.
    If one requested the compiler to do so via extern "C" { ... } and it's possible to create an unmangled name, it ends up verbatim in the ELF symbol table. You can then look it up using dlsym() exactly like you would for any C function.
  2. Find out what the mangled name of the function is, and use that in your dlsym() call.

The latter you can do via the nm utility. For example:

$ nm libstdc++.a | grep -v '^ ' | grep unexpected
0000000000000000 T __cxa_call_unexpected
0000000000000000 T _ZN10__cxxabiv112__unexpectedEPFvvE
0000000000000000 T _ZSt10unexpectedv
0000000000000000 T _ZSt14set_unexpectedPFvvE
0000000000000000 D _ZN10__cxxabiv120__unexpected_handlerE

These are the mangled names, what the C++ compiler has actually put into the ELF object. If you use the -C option to request nm to demangle the names for you, you get:

$ nm -C libstdc++.a | grep -v '^ ' | grep unexpected
0000000000000000 T __cxa_call_unexpected
0000000000000000 T __cxxabiv1::__unexpected(void (*)())
0000000000000000 T std::unexpected()
0000000000000000 T std::set_unexpected(void (*)())
0000000000000000 D __cxxabiv1::__unexpected_handler

That means for this lib, if you'd want to get the function pointer to std::unexpected() from it, you'd have to request dlsym(hdl, "_ZN10__cxxabiv112__unexpectedEPFvvE"); to make the lookup succeed.

FrankH.
  • 17,675
  • 3
  • 44
  • 63
  • 3
    the explanation is correct in principle, but the example is slightly off: `std::unexpected()` is synonymous to `_ZSt10unexpectedv`, but `_ZN10__cxxabiv112__unexpectedEPFvvE` corresponds to `__cxxabiv1::__unexpected(void (*)())`. – Alex Cohn Dec 23 '13 at 14:26