mrsmith
mrsmith

Reputation: 391

Weak dependency on shared library on Linux

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.

Upvotes: 5

Views: 2290

Answers (3)

technosaurus
technosaurus

Reputation: 7802

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.

Upvotes: 1

apmasell
apmasell

Reputation: 7153

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.

Upvotes: 3

Aaron Digulla
Aaron Digulla

Reputation: 328614

This is possible but probably more work than you're willing to invest. To do it:

  1. You'll need to create a patches version of GCC and expecially the binutils which allows you to create a list of "optional" symbols (i.e. ones for which the dynamic loader won't complain when they're missing).

  2. You'll have to patch the dynamic loader to understand your symbols.

A much more simple approach is probably to use a DSL which a tool (= script or simple C program) can read and use to generate the boiler plate C code for you.

The DSL would define all the symbols (and the parameters) and the tool would convert that to .h and .c files using the code templates above.

When my_lib.so changes (or when you need more symbols), you just edit the (small) DSL and rerun the scripts.

Upvotes: 0

Related Questions