7

I'm dynamically loading (whith dlopen()) a shared object (named libprofile1.so) from main.

In libprofile1.so I have defined factory function CreateProfile and class Profile. CreateProfile function creates an instance of Profile class and returns a pointer to it. Class Profile has a method pMethod.

In main, after loading libprofile1.so, I'm calling CreateProfile method which returns the pointer to the object of Profile class (call it p).
Afterwards, I'm calling pMethod method against object p (p->pMethod). In this method I'm dynamically loading other shared object (libdatasources.so).

In this shared object I have a factory function CreateDataSourceand class DataSource.
CreateDataSource function creates an instance of DataSource class and returns a pointer to it. DataSource class has method dsMethod.

As you can notice, structures of both shared objects are similar.

From pMethod after loading libdatasources.so I'm calling CreateDataSource method, which returns me a pointer to an instance of DataSource class, call it ds. Then I'm calling dsMethod of ds object
(ds->dsMethod).


Now, the problem is following.

When I try to call dsMethod of ds object, shared object that I'm first loading (libprofile1.so) doesn't load. Actually dlopen() returns NULL. When I read dlerror after dlopen I get:

./libprofile1.so: undefined symbol: _ZN18DataSource13dsMethod

So if I have a call ds->Method, than first shared object doesn't load!
If I comment out call ds->dsMethod from the source, then my libprofile1.so and libdatasources.so are loaded without any problems.
I don't see the connection between the call of a method from the second SO, with loading first SO???

Maybe I don't know, but are there any constraints when dynamically loading a shared object, from a shared object that's also been dynamically loaded?

Btw, dlopen is used with RTLD_NOW|RTLD_GLOBAL. I tried with RTLD_LAZY, but still the same problem.

UPDATE:

Libraries are built in Eclipse. Options for G++ compiler and linker are the same for both libraries.
Here are G++ compiler:

-O0 -g3 -Wall -c -fmessage-length=0

and G++ linker:

-shared

options, pasted from Project Properties -> Settings -> Tool Settings

Thanks in advance.

1
  • Can you update your question indicating how libprofile1.so and libdataresources.so are built? Commented Sep 2, 2010 at 13:14

2 Answers 2

4
+100

If you dynamically load a DLL you must make sure that it has no unresolved symbols.

The easiest way to do this is to link it against the other DLLs that it needs so that they load automatically rather than you having to load and resolve all dependencies manually.

So if libprofile1 always uses libdatasources make sure they are linked with each other.

If you must do it manually then reverse the order that you load the libraries. So that when you load libprofile1 the functions that it needs have already been loaded.

Sign up to request clarification or add additional context in comments.

2 Comments

What if the library was already linked with main? I'm finding that if I link my dlopened lib with other libs that are also liked with main that I get multiple copies of them when the .so is loaded, eg. global vars appear twice. If I don't link my dlopended library with the shared lib I get link errors at runtime because it can't find hte symbols. There must be an easy way to resolve this. I think I need something like the RTLD_GLOBAL flag for the main application
Opps, nevermind. I just needed to add the -rdynamic linker flag to my main link step. Resolved everything.
4

As Martin York pointed out, that's the way it works in Linux. When linking against a library, you have to link to all dependencies, too. That's different in Windows, DLLs take care of their dependencies themselves. When dynamically loading a library that has another library as a dependency, you need to load that library first with the RTLD_GLOBAL flag. This is pretty awkard, imho, since you may not be able to know which dependencies another shared objects requires, or the dependencies can change with a newer version that's otherwise binary compatible. From what I know (and from reading the g++ and ld manpages), it is not possible to create a behaviour similar to Windows' DLLs. Here's a little testcase:

two.cpp:

#include <iostream>

extern "C"
{
   void bar()
   {
      std::cout << "bar()\n";
   }
}

one.cpp:

#include <iostream>

extern "C"
{
   void bar();

   void foo()
   {
      std::cout << "foo()\n";
      bar ();
   }
}

test.cpp:

#include <dlfcn.h>
#include <iostream>

int main (int argc, char *argv[])
{
   using namespace std;
//       void *libtwo = dlopen("./libtwo.so", RTLD_NOW | RTLD_GLOBAL);
   void *libone = dlopen("./libone.so", RTLD_NOW);
   if (!libone)
   {
      cout << "dlopen(libone.so) failed: " << dlerror() << "\n";
      return 1;
   }
   typedef void (*foo_t)();
   foo_t foo = reinterpret_cast<foo_t>(dlsym(libone, "foo"));
   if (!foo)
   {
      cout << "dlsym(libone.so, foo) failed\n";
      return 2;
   }
}

one.cpp is compiled into libone.so and two.cpp in libtwo.so, test.cpp is compiled into the test binary. This will fail and only succeed when the commented line is uncommented.

3 Comments

quite similar example, but the thing is that in foo() i'm loading libtwo.so and then getting the function pointer to bar(), which I call from foo() also. I'm using a workaround for this problem with a wrapper function barWrap() for bar(). barWrap() is not the part of the class that I'm using (DataSource),but rather exported independent function . From this function I'm calling bar() (in my case ds->dsMethod) and it works fine. By the way I used RTLD_NOW|RTLD_GLOBAL as the argument of dlopen but still nothing. Quite a weird problem, I can't seem to figure out.
Are you linking libone.so against libtwo.so (-ltwo for the linker)? dlopen() should then have no problem loading this. I'll add my gcc command lines as a new comment.
$ g++ -shared -fpic -o libtwo.so -Wl,-no-undefined two.cpp $ g++ -shared -fpic -o libone.so -Wl,-no-undefined one.cpp -ltwo -L. -Wl,-rpath,. $ g++ test.cpp -ldl $ ./a.out

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.