Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weak dependency on shared library on Linux

Tags:

c

linux

ld

dlopen

I want to make my executable to be "optionally dependent" on other shared object. Thus it will be able to run without some symbols if DSO is absent.

I can achieve this with dlopen/dlsym calls but I have to manually load each symbol and add wrappers for them like this:

void *my_lib = dlopen("my_lib.so", RTLD_LAZY);  
if (!my_lib)  {
    // ok, I promise not to touch my_lib symbols
} else {
    my_foo_ptr = dlsym(my_lib, "my_foo");
    my_bar_ptr = dlsym(my_lib, "my_bar");
}

... my_foo(...) {
    assert(my_foo_ptr);
    return (*my_foo_ptr)(...);
}

... my_bar(...) {
    assert(my_foo_ptr);
    return (*my_bar_ptr)(...);
}

This is a dumb code and it directly depends on "my_lib.so" ABI, that means that I have to update it each time library updates.

I'm searching for some way to make ld.so do this for me. So the ideal would be:

void *my_lib = dlopen("my_lib.so", /* bring me all my symbols */);  
if (!my_lib)  {
    // ok, I promise not to touch my_lib symbols
} else {
    // ok, I can directly call symbols from my_lib.so
    my_foo();
    my_bar();
}

But there are two questions with this:
1. What to do with these symbols during app linkage phase? If I link to my_lib.so explicitly, the app will be strictly dependent on it and therefore unable to start without my_lib.so. If not, ld will complain about undefined symbols.
2. How to force dlopen() to make all my_lib.so symbols available to my app?

Upd: I realized that explicit linking with shared library without marking it as DT_NEEDED would do the trick. But I have no clue how to make ld do this.

like image 456
mrsmith Avatar asked Oct 06 '22 22:10

mrsmith


2 Answers

It might be more sensible to write a module which understands your programs needs that minimises the interaction between your program and this library, then link that code against your library. Think of something like a music player: rather than do this kind of dance for each audio format, create a simple interface, then create a separately compiled module for each audio format and have each module link against teh appropriate support libraries. This has the advantages that you can ensure that your modules all have the same symbols and handling them is simpler: when you load the module, create a struct with function pointers, then when you call the module, just check if the struct is null and call the function pointer (this is probably sensible to do via a macro). This also means you can easily add different versions of this functionality, if that is desirable.

like image 67
apmasell Avatar answered Oct 10 '22 03:10

apmasell


While I appreciate apmasell's object oriented approach for new code, it is not always feasible with an existing code base. Fortunately there is a mechanism to allow the loaded module to export its own functions:

 -Wl,-init,<function name> and -Wl,-fini,<function name>

If your linker does not support this, you can write your own init function and load it with dlsym(). So for each module you would have a function called something like void init_module(void *handle); (where handle is the handle from dlopen) that exports any of its own needed symbols.

like image 33
technosaurus Avatar answered Oct 10 '22 03:10

technosaurus