What are the scoping rules for subsequent calls to dlopen?

53 Views Asked by At

Suppose I have an executable that dlopens libfirst.so with RTLD_LOCAL which then dlopens libsecond.so with RTLD_GLOBAL. Should the symbols from libsecond.so now be directly available (i.e., without dlsym) in (1) only libfirst.so or (2) in both libfirst.so and the executable?

From the manpage on dlopen

RTLD_GLOBAL: The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.

This sounds like the symbols from libsecond.so are not made available to either the executable or libfirst.so (except via dlsym). Is this correct?

2

There are 2 best solutions below

1
Employed Russian On BEST ANSWER

Should the symbols from libsecond.so now be directly available (i.e., without dlsym) in (1) only libfirst.so or (2) in both libfirst.so and the executable?

I don't think you understand what "directly available" means in this context.

Suppose libsecond.so defines second(). That symbol is not available in either libfirst.so or the main a.out (obviously -- they have been already loaded before libsecond.so was brought in).

But if you now load (via dlopen()) libthird.so, and if that library has second() as an unresolved symbol (in other words, libthird.so has a direct dependency on second()), the load of libthird.so would succeed if libsecond.so was loaded with RTLD_GLOBAL, but would fail (with unresolved second()) if libsecond.so was loaded with RTLD_LOCAL.

0
yugr On

Symbols will be visible in both executable and libfirst.

Take this simple example in which executable tries to access symbol from libsecond.so after libsecond.so is loaded.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

#ifdef FIRST
void foo() { dlopen("./libsecond.so", RTLD_LAZY | RTLD_GLOBAL); }
#elif defined SECOND
void bar() { printf("Hello from libsecond\n"); }
#else
extern void foo();
extern void bar();

int main() {
  dlopen("./libfirst.so", RTLD_LAZY | RTLD_GLOBAL);
  foo();
  bar();
  return 0;
}
#endif

When run, the call to bar() in main executable is successfully executed:

$ gcc -fuse-ld=lld -shared -fPIC -DFIRST prog.c -o libfirst.so
$ gcc -fuse-ld=lld -shared -fPIC -DSECOND prog.c -o libsecond.so
$ gcc -fuse-ld=lld -Wl,-z,lazy -Wl,-z,undefs prog.c
$ ./a.out
Hello from libsecond

Interestingly this example does not link with GNU ld (it fails to generate correct relocations for foo and bar in executable).