Skip to content
Advertisement

Linking shared objects at runtime depending on user configuration

TL;DR

I have a library I want to use in my program which comes in two different versions. Both versions provide the same interface but differ in options used to compile them.

I now want to use a specific version of the library, however, since both versions are suitable for different tasks and the user should define the task to perform I need to decide which library to use at runtime.

I know I could use dlopen and dlsym to load the right library at runtime according to the users choice, however the interface is quite large, and loading everything I need into different function pointers would be quite tedious…

The Problem

I have a library which is shipped in two different versions. Both versions provide the same interface however differ in the task they are suitable for. This is how the file tree looks like:

lib
  - lib_task1
       - libsharedobj.so
  - lib_task2
       - libsharedobj.so

I want to provide the possibility for the user to choose which task to perform at runtime. Therefore I need to decide which library to choose at runtime as well. My idea was to write a wrapper which provides the same interface as the library and in which I would dlopen the desired lib and dlsymthe according symbols into function pointers. However, the library interface is quite large and wrapping it as described would be quite tedious, plus its a C interface so it also contains a lot of raw pointers I’d like to not see outside the wrapper.

Here is a small example

// library interface
typedef struct {
  // ...
} a_type;
void do_something(a_type* param);

// wrapper 
class LibWrapper {
private:
    void (*do_something)(a_type*);

    void* lib;
public:
    LibWrapper(const bool task_one) { // specified by the user
        if (task_one) {
            lib = dlopen("/usr/lib/lib_task1/libsharedobj.so", RTLD_NOW);
        } else {
            lib = dlopen("/usr/lib/lib_task2/libsharedobj.so", RTLD_NOW);
        }
        do_something = dlsym(lib, "do_something");
    }

    ~LibWrapper() {
        if (lib) {
            dlclose(lib);
        }
    }

    void do_something(std::unique_ptr<a_type> param) {
        do_something(param.get());
    }
};

The Question

Is there a better way in doing this, or do I really need to load each symbol one by one?

OS: ubuntu 14.04.
Compatibility: C++11

Advertisement

Answer

One way to obfuscate the setting of LD_PRELOAD might be that when your program starts, if LD_PRELOAD is not set suitably, determine which setting you want, add it, and immediately execv with your own arguments to restart! I think LD_PRELOAD makes the better sense here, if it does what you need, as it is easier to detect if it is set the way you need.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement