3

I have an issue when I try to use dlopen() to load a shared library into another shared library. I checked all tutorials on how to use dlopen() correctly. So here is the simplified code:

The main shared library contains a class with pure virtual functions which the sub-shared library (aka plug-in) must implement. Additionally, it has some other functions which are implemented with a default behavior. I created a macro which is added to every plug-in to have a symbol to load and create the class.

Main shared library

plugin.h:

Class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
}

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

plugin.cpp:

bool A::func2() const {   return true; }

Build and link main.so

g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC -c -o plugin.o plugin.cpp
g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC  -rdynamic -shared plugin.o -ldl -o main-plugin.so

The sub-shared library does only implement the pure-virtual functions. The other function can be overridden though it is optional.

Sub shared library

plugin-impl.cpp

Class B : public A {
public:
  virtual int func1() { return 0; }
}

CREATE_CLASS(B)

These are the lines to build and link the sub-shared library.

Build and link sub.so

g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC -c -o subPlugin.o subPlugin.cpp
g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared subPlugin.o  -o subPlugin.so

This is the line to open the sub-shared library. I tried LAZY, NOW, NOW | GLOBAL and so on, without any effects.

dlopen() call somewhere in the main-shared library:

  handle = dlopen(file.c_str(), RTLD_LAZY);

Most of this is working very well. However, when I try to load the sub-shared library into the main shared library, dlopen complains about the undefined symbol of bool A::func2() const. The function does only exists in the main shared library, so I guess it must be exported anyway. Please help me out! I am very confused!

SOLUTION

Because I cannot change the executable, I have to link the main-shared library with the sub-shared librarys by adding the following options to g++:

-L$(PLUGINDIR) -Wl,-R$(PLUGINDIR) -lmain-shared

With this set, it is not required to set the LD_LIBRARY_PATH.

Denis Loh
  • 2,194
  • 2
  • 24
  • 38
  • 1
    Another programmer not having informed himself about name mangling... –  Oct 06 '12 at 19:05
  • @H2CO3 uhh, this is all C++, no C here. Name mangling doesn't have anything to do with it does it? – Seth Carnegie Oct 06 '12 at 19:09
  • @SethCarnegie correct me if I'm wrong, but isn't name mangling present in C++ and not in C? –  Oct 06 '12 at 19:10
  • @H2CO3 yes definitely, but I thought name-mangling problems come in where C tries to use a C++ function which has been mangled and C can't find it. – Seth Carnegie Oct 06 '12 at 19:10
  • @SethCarnegie yes, but if I understand well, it's not the linker who complains about undefined symbols here, but rather `dlopen()` can't find certain functions. OP's language is not flawless so I may be overseeing this one... –  Oct 06 '12 at 19:12
  • @H2CO3 ah, so then `dlopen` expects C linkage? – Seth Carnegie Oct 06 '12 at 19:15
  • @SethCarnegie AFAIK yes - all implementations I have seen so far it used an `strcmp()` to obtain an address for a named symbol. –  Oct 06 '12 at 19:16
  • @H2CO3 Ah ok, that I didn't know, thanks very much. Maybe that is the OP's problem, try posting an answer. – Seth Carnegie Oct 06 '12 at 19:17
  • @H2CO3 Can member functions have C linkage? I don't know but I would guess not. – Seth Carnegie Oct 06 '12 at 19:19
  • @SethCarnegie: `dlopen` expects C linkage *for the symbol you are trying to load with `dlsym`*. This is definitely not what's failing here. – jpalecek Oct 06 '12 at 19:20
  • Well, I thought loading a symbol is done by `dlsym()`. I am using `export "C"` to get C style names to create my class... Does `dlopen()` have to know all symbols in C style? – Denis Loh Oct 06 '12 at 19:21
  • 1
    @DenisLoh As I know, dlopen() expects C linkage. If you use C-functions only (declared as `extern "C"`), there shouldn't be any problem. –  Oct 06 '12 at 19:27
  • Strange thing is that it works for me. I copied your code and your command lines plus I created a dummy executable, and it worked. Is the `main-library.so` loaded? – jpalecek Oct 06 '12 at 19:40
  • Yes, it gets loaded by dlopen with `RTLD_NOW` somewhere else. However, I have no impact on that because I am writing a plugin which itself may load plugins as well. So, it works like this: the executable loads the main-library.so via dlopen(RTLD_NOW). Then, the main-library.so loads some other components by dlopen(RTLD_LAZY). – Denis Loh Oct 06 '12 at 19:52
  • OK, I can reproduce your behavior and (even though I don't know why), it can be fixed by linking the sub-library with main-library. Could you do that? – jpalecek Oct 06 '12 at 19:56
  • Strangely enough, it also works if you link the executable with main-library with `-lmain-library`. – jpalecek Oct 06 '12 at 19:59
  • So, I assume that the linker cannot find the symbols of the main-shared-library, because it is loaded at runtime. If this library itself tries to load another library into it, it cannot export its symbols because they were not known yet. So, when the sub-library gets loaded, it may only find the symbols of the executable but not those of the main-shared. When the executable is linked with the main-shared, it knows its symbols at compile AND runtime, so dlopen() knows where to look for. Right? – Denis Loh Oct 06 '12 at 20:07
  • @DenisLoh: See the edits to my answer. – jpalecek Oct 06 '12 at 20:17

1 Answers1

3

Since your sub-library needs symbols from the main library, I think it you want it to be linked with it. Try linking it like this:

g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared 
    -lmain-plugin subPlugin.o  -o subPlugin.so

Probably you'll need to play with -L as well.

This is what I tried:

jirka@debian:/tmp$ cat executable.cpp 
#include <dlfcn.h>
#include <stdio.h>
int main()
{
  dlopen("./main-library.so", RTLD_NOW);
  void* handle=dlopen("./sub-library.so", RTLD_LAZY);
  printf("%x %s", dlsym(handle, "CreateClass"), dlerror());
}
jirka@debian:/tmp$ cat main-library.cpp 
class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
};

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

bool A::func2() const {   return true; }
jirka@debian:/tmp$ cat sub-library.cpp 
class A {
public:
  virtual int func1() = 0;
  virtual bool func2() const;
};

#define CREATE_CLASS(cls) extern "C" void *CreateClass(void) { return new cls; }

class B : public A {
public:
  virtual int func1() { return 0; }
};

CREATE_CLASS(B)

jirka@debian:/tmp$ g++ -g -Wall -Woverloaded-virtual -Wno-parentheses -O2 -fPIC  -rdynamic -shared main-library.cpp -ldl -o main-library.so
jirka@debian:/tmp$ g++ -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses -fPIC  -shared -l:main-library.so sub-library.cpp  -o sub-library.so
jirka@debian:/tmp$  g++ -ldl executable.cpp -o executable
jirka@debian:/tmp$ LD_LIBRARY_PATH=. ./executable 
b7713740 (null)

Another possibility is adding RTLD_GLOBAL when loading main-library:

jirka@debian:/tmp$ cat executable.cpp 
#include <dlfcn.h>
#include <stdio.h>
int main()
{
  dlopen("./main-library.so", RTLD_LAZY | RTLD_GLOBAL);
  void* handle=dlopen("./sub-library.so", RTLD_LAZY);
  printf("%x %s", dlsym(handle, "CreateClass"), dlerror());
}

That way, you needn't link anything with main-library.so.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • Well, this doesn't work, because the subPlugins need to be loaded at runtime. Additionally, I am just providing the interface to the main shared-library and others may add sub-shared libraries dynamically. I don't know the number of plug-ins at compile time. – Denis Loh Oct 06 '12 at 20:01
  • @DenisLoh: I think you misunderstood - I suggest you to link every sub-library with the main-library, not linking the main-library (or the executable) with all the sub-libraries. – jpalecek Oct 06 '12 at 20:10
  • Ah, sorry. I just mixed that. I will give it a try. – Denis Loh Oct 06 '12 at 20:21
  • Thank you very much. I tried your hint with linking main with the subs and it works. However, I have to set LD_LIBRARY_PATH to find the main-library. The library is now successfully loaded with dlopen(). I hope that dlsym works too. – Denis Loh Oct 06 '12 at 20:26