Linux Dlopen: Can a Library Be "Notified" When It Is Loaded

linux dlopen: can a library be notified when it is loaded?

Libraries should export initialization
and cleanup routines using the gcc
__attribute__((constructor)) and __attribute__((destructor)) function attributes. See the gcc info pages for
information on these. Constructor
routines are executed before dlopen
returns (or before main() is started
if the library is loaded at load
time). Destructor routines are
executed before dlclose returns (or
after exit() or completion of main()
if the library is loaded at load
time). The C prototypes for these
functions are:

 void __attribute__ ((constructor))  my_init(void);  
void __attribute__ ((destructor)) my_fini(void);

Taken from http://tldp.org/HOWTO/Program-Library-HOWTO/index.html

THat is, you just tack on __attribute__ ((constructor)) to the functions you want to be called when the shared library is loaded. The above docuemtn also notes that the older _ini and _fini functions are considered obsolete.

C++ linux: dlopen can't find .so library

Read the dlopen(3) man page (e.g. by typing man dlopen in a terminal on your machine):

If filename contains a slash ("/"), then it
is interpreted as a (relative or absolute) pathname. Otherwise, the
dynamic linker searches for the library as follows (see ld.so(8) for
further details):

   o   (ELF only) If the executable file for the calling program
contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
then the directories listed in the DT_RPATH tag are searched.

o If, at the time that the program was started, the environment
variable LD_LIBRARY_PATH was defined to contain a colon-separated
list of directories, then these are searched. (As a security
measure this variable is ignored for set-user-ID and set-group-ID
programs.)

o (ELF only) If the executable file for the calling program
contains a DT_RUNPATH tag, then the directories listed in that
tag are searched.

o The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is
checked to see whether it contains an entry for filename.

o The directories /lib and /usr/lib are searched (in that order).

So you need to call dlopen("./libLibraryName.so", RTLD_NOW) -not just dlopen("libLibraryName.so", RTLD_NOW) which wants your plugin to be in your $LD_LIBRARY_PATH on in /usr/lib/ etc .... - or add . to your LD_LIBRARY_PATH (which I don't recommend for security reasons).

As Jhonnash answered you should use and display the result of dlerror when dlopen (or dlsym) fails:

  void* dlh = dlopen("./libLibraryName.so", RTLD_NOW);
if (!dlh)
{ fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(EXIT_FAILURE); };

You might want to read some books like Advanced Linux Programming to get some knowledge about Linux system programming in general.

Is there a load leak if dlopen() is called from a dlopen'd library?

The application need only worry about what the app loads directly. If you load a.so, all you need to be concerned with is unloading a.so.

If a.so refuses to unload b.so, that is a problem with a.so, your app is not responsible for this. The author of a.so needs to get their act together and fix the problem with their library.

Linux and shared libraries, linking vs dlopen - symbol visibility

Your question is exceedingly unclear.

If you dlopen the library, then about the only way to get to any of its symbols is via dlsym.

However, if you dlopen a library with RTLD_GLOBAL, then its symbols become available for subsequently loaded libraries without using dlsym.

For example, if libfoo.so defines symbol foo, and if you dlopen("libfoo.so", RTLD_GLOBAL|...); and later dlopen("libbar.so", ...) which uses foo, that would work -- libbar.so will be able to use foo from libfoo.so without doing any dlsym calls.

Why would dlopen reuse the address of a previously loaded symbol?

The key to answering this question is whether the main executable exports the same symbol in its dynamic symbol table. That is, what is the output from:

nm -D a.out | grep ' mangled_name_of_the_symbol'

If the output is empty, the two libraries should indeed use separate (their own) copies of the symbol. But if the output is not empty, then both libraries should reuse the symbol defined in the main binary (this happens because UNIX dynamic linking attempts to emulate what would have happened if everything was statically linked into the main binary -- UNIX support for shared libraries happened long after UNIX itself became popular, and in that context this design decision made sense).

Demonstration:

// main.c
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>

int foo = 12;

int main()
{
printf("main: &foo = %p, foo = %d\n", &foo, foo);
void *h = dlopen("./foo.so", RTLD_NOW);
assert (h != NULL);
void (*fn)(void) = (void (*)()) dlsym(h, "fn");
fn();

return 0;
}
// foo.c
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>

int foo = 42;

void fn()
{
printf("foo: &foo = %p, foo = %d\n", &foo, foo);
void *h = dlopen("./bar.so", RTLD_NOW);
assert (h != NULL);

void (*fn)(void) = (void (*)()) dlsym(h, "fn");
fn();
}
// bar.c
#include <stdio.h>

int foo = 24;

void fn()
{
printf("bar: &foo = %p, foo = %d\n", &foo, foo);
}

Build this with:

gcc -fPIC -shared -o foo.so foo.c && gcc -fPIC -shared -o bar.so bar.c &&
gcc main.c -ldl && ./a.out

Output:

main: &foo = 0x5618f1d61048, foo = 12
foo: &foo = 0x7faad6955040, foo = 42
bar: &foo = 0x7faad6950028, foo = 24

Now rebuild just the main binary with -rdynamic (which causes foo to be exported from it): gcc main.c -ldl -rdynamic. The output changes to:

main: &foo = 0x55ced88f1048, foo = 12
foo: &foo = 0x55ced88f1048, foo = 12
bar: &foo = 0x55ced88f1048, foo = 12

P.S.
You can gain much insight into the behavior of dynamic linker by running with:

LD_DEBUG=symbols,bindings ./a.out

Update:

It turns out I asked a wrong question ... Added source example.

If you look at LD_DEBUG output, you'll see:

    165089: symbol=object;  lookup in file=./main [0]
165089: symbol=object; lookup in file=./liba.so [0]
165089: binding file ./liba.so [0] to ./liba.so [0]: normal symbol `object'
165089: symbol=object; lookup in file=./main [0]
165089: symbol=object; lookup in file=./liba.so [0]
165089: binding file ./libb.so [0] to ./liba.so [0]: normal symbol `object'

What this means: liba.so is in the global search list (by virtue of having been directly linked to by main). This is approximately equivalent to having done dlopen("./liba.so", RTLD_GLOBAL).

It should not be a surprise then that the symbols in it are available for subsequently loaded shared libraries to bind to, which is exactly what the dynamic loader does.



Related Topics



Leave a reply



Submit