0

Let's say libl1.so, libl2.so and libl3.so all contain function f.

Is there really no way to link all these f's (from libl1.so, libl2.so and libl3.so) into my program without using dlopen?

I even agree for using some wrapper libraries -- but I still need to be finally linked to libl1.so, libl2.so and libl3.so themselves (not to their modified copies).

P.S.: It's an attempt to clarify this question.

lenik
  • 23,228
  • 4
  • 34
  • 43
Sasha
  • 3,599
  • 1
  • 31
  • 52
  • If you statically link a wrapper library for each one of these libraries, hen you won't need to link the original libraries. – Eugene Sh. Jun 13 '16 at 20:15
  • What's wrong with using dlopen? – rici Jun 13 '16 at 20:18
  • 1
    Seems that I was not responsible enough when answering your other question. See this: http://stackoverflow.com/a/6538625/6394138. – Leon Jun 13 '16 at 20:21

1 Answers1

0

With a bit of care, it's possible to use dlopen with shared libraries linked to the application.

dlopen attempts to return the handle for an already loaded shared object. As long as the shared object is correctly named, if it is loaded because of a linked symbol, the use of dlopen will not cause it to be loaded a second time.

An example may make this more clear.

Here are three almost identical libraries. Each one defines a function called f and another function with a unique name. (They also have non-exported initializers, which I added so that loading is visible.):

file f1.c

#include <stdio.h>
int f() { return puts("I'm f1::f"); }
int f1_g() { return puts("I'm f1_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f1"); }

file f2.c

#include <stdio.h>
int f() { return puts("I'm f2::f"); }
int f2_g() { return puts("I'm f2_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f2"); }

file f3.c

#include <stdio.h>
int f() { return puts("I'm f3::f"); }
int f3_g() { return puts("I'm f3_g"); }
static void init() __attribute__((constructor));
void init() { puts("Initializing f3"); }

These are compiled into shared objects, specifying the soname of each shared object:

for i in f1 f2 f3; do
  gcc -Wall --shared -Wl,--soname=lib$i.so -fPIC -o lib$i.so $i.c
done

Here's a main function which explicitly refers to two of the unique function names, but relies on dlopen/dlsym to use the functions with common names. (It refers to two unique names rather than three so as to show the difference in loading behaviours, which is nonetheless transparent). Note that the library names being loaded with dlopen do not contain slashes, so they will match with an already loaded library with the same soname.

file main.c

#include <stdio.h>
#include <dlfcn.h>
extern int f1_g();
extern int f2_g();
extern int f3_g();
int (*f1_f)();
int (*f2_f)();
int (*f3_f)();
int main() {
  puts("Loading f1");
  f1_f = dlsym(dlopen("libf1.so", RTLD_NOW), "f");
  puts("Loading f2");
  f2_f = dlsym(dlopen("libf2.so", RTLD_NOW), "f");
  puts("Loading f3");
  f3_f = dlsym(dlopen("libf3.so", RTLD_NOW), "f");
  f1_f();
  f1_g();
  f2_f();
  f2_g();
  f3_f();
  // f3_g(); /* We don't explicitly use f3_g, so f3.so is not linked */
  return 0;
}

Now, we compile and run it:

$ # Need to specify -L in the build and LD_LIBRARY_PATH when running
$ # because . is not in the default library search path.
$ gcc -o main -Wall main.c -L. -lf1 -lf2 -lf3 -ldl
$ LD_LIBRARY_PATH=. ./main
Initializing f2
Initializing f1
Loading f1
Loading f2
Loading f3
Initializing f3
I'm f1::f
I'm f1_g
I'm f2::f
I'm f2_g
I'm f3::f

So, f1 and f2 are loaded (and initialized) before main starts because they are needed by explicitly used functions f1_g and f2_g. When a handle for these shared objects is requested (using dlopen), the libraries are not loaded a second time. However, the call to dlopen for libf3.so does not find the shared object in the executable, so it is loaded (and initialized) by the call to dlopen.

Subsequently, all of the functions are called and the expected versions of the various f functions are obtained.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Thanks, but it was **obvious** how to do it with dlopen. And I was asking a way to do it without dlopen (i.e. at building time). – Sasha Jun 14 '16 at 06:19
  • @Sasha, You can do it at build time by modifying the libraries you are building, but you didn't want that solution either :) Clearly I was wrong about your concern being libraries loaded twice or not available for resolution of other symbols, but I don't see any (other) reason *not* to use dlopen, since it's an easy solution and the call syntax is identical, so the source changes are minimal. Indeed, you could write wrappers which do the dlopen in their init methods; it's hard (for me) to see how that differs from normal on-demand symbol resolution, really. – rici Jun 14 '16 at 06:37
  • I just can't believe that such trivial thing can't be done in pure `ld`. – Sasha Jun 14 '16 at 11:54