C++ How to use dlopen() in c++?

Viewed 1068

I am trying to use the prebuilt tensorflow c-api with a cpp-wrapper in my package. Unfortunately I am getting a segfault error. After searching I found out that there is a Git Issue about it: Linking to both tensorflow and protobuf causes segmentation fault during static initializers.

So I could resolve the Issues by doing:

The workarounds sound like (1) only load the second copy of protobuf in a .so that does not use TensorFlow, and you can use both that .so and TensorFlow's .so from your main program, (2) instead of linking normally, dlopen() TensorFlow with RTLD_DEEPBIND set so TensorFlow prefers its own symbols.

I want to try to load the library using dlopen(), unfortunately I have never used it and I cannot find a good example of its use. How would I use it in my case and where?

My initial understanding: Load it into the header of my cpp-wrapper since they use the tensorflow functions/header? But than do I really need to change every single function of the cpp-wrapper header, and put a reference to the loaded handler?

1 Answers

A little dlopen example:


Some lib written in C, we call it foobar.so

#include <stdio.h>
void foo() { printf("foo\n"); }
void bar() { printf("bar\n"); }

gcc -o foobar.so foobar.c -shared -fPIC


A (foobar) wrapper in C++

#include <dlfcn.h>

struct FooBar {

    typedef void (*foo_handle)(); //same signature as in the lib
    typedef void (*bar_handle)(); //same signature as in the lib

    foo_handle foo;
    bar_handle bar;
    void *foobar_lib;

    FooBar() {

        //probably best not in the constructor, but we're lazy (for now)
        //and of course no error checking (so don't)
        foobar_lib = dlopen("./foobar.so", RTLD_LAZY | RTLD_DEEPBIND);
        foo = reinterpret_cast<foo_handle>(dlsym(foobar_lib, "foo"));
        bar = reinterpret_cast<bar_handle>(dlsym(foobar_lib, "bar"));

    }

    ~FooBar() {
        dlclose(foobar_lib);
    }

};

int main()
{
    FooBar foobar;
    foobar.foo();
    foobar.bar();
    
    return 0;
}

or

#include <dlfcn.h>

typedef void (*foo_handle)(); //same signature as in the lib
typedef void (*bar_handle)(); //same signature as in the lib

foo_handle foo;
bar_handle bar;
void *foobar_lib;

int main()
{
    foobar_lib = dlopen("./foobar.so", RTLD_LAZY | RTLD_DEEPBIND);

    foo = reinterpret_cast<foo_handle>(dlsym(foobar_lib, "foo"));
    bar = reinterpret_cast<bar_handle>(dlsym(foobar_lib, "bar"));

    foo();
    bar();

    dlclose(foobar_lib);
    
    return 0;
}

g++ -ldl -o foobar_test foobar_test.cpp


For each and every symbol you use from the original lib, you will have to call dlsym to obtain its address.

So yes, it is a tedious work, since you're implementing a wrapper to provide the full functionality of the underlying library.

For example, opengl developers knows very well what that means. Luckily, over the years there are now many tools available, which helps to load the myriads of symbols in runtime with no/less effort. Maybe there is something similar to tensorflow.

Related