C++ 如何在 C++ 中使用 dlopen()?

Posted

技术标签:

【中文标题】C++ 如何在 C++ 中使用 dlopen()?【英文标题】:C++ How to use dlopen() in c++? 【发布时间】:2021-10-28 18:59:12 【问题描述】:

我正在尝试在我的包中使用预构建的 tensorflow c-apicpp-wrapper。不幸的是,我遇到了段错误。经过搜索,我发现有一个关于它的 Git 问题:Linking to both tensorflow and protobuf causes segmentation fault during static initializers。

所以我可以通过以下方式解决问题:

解决方法听起来像 (1) 仅在不使用 TensorFlow 的 .so 中加载 protobuf 的第二个副本,并且您可以在主程序中同时使用该 .so 和 TensorFlow 的 .so,(2) 而不是链接通常,dlopen() TensorFlow 设置了 RTLD_DEEPBIND,因此 TensorFlow 更喜欢自己的符号。

我想尝试使用 dlopen() 加载库,不幸的是我从未使用过它,也找不到一个很好的使用示例。在我的情况下我将如何使用它以及在哪里使用它?

我的初步理解: 将其加载到我的 cpp-wrapper 的标头中,因为它们使用 tensorflow 函数/标头? 但是,我真的需要更改 cpp-wrapper 标头的每个函数,并引用加载的处理程序吗?

【问题讨论】:

【参考方案1】:

一点dlopen例子:


一些用C编写的lib,我们称之为foobar.so

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

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


C++ 中的 (foobar) 包装器

#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;

#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


对于您从原始库中使用的每个符号,您都必须调用dlsym 来获取其地址。

是的,这是一项乏味的工作,因为您正在实现一个包装器来提供底层库的全部功能。

例如,opengl 开发人员非常清楚这意味着什么。幸运的是,多年来,现在有许多可用的工具,它们有助于在运行时轻松加载无数符号。也许有类似tensorflow的东西。

【讨论】:

哇,谢谢!!!所以现在我想我有两个选择。 1)尝试修改cpp包装器。通过调用 dlsym 到每个符号?或者也许只是我使用的包装器的每个符号,因为我应该为每个使用的符号获得一个未定义的引用? 2) 尝试自己构建 tensorflow c++api,而不仅仅是 dlsym 我真正使用的所有函数。最后一个问题。您可以打开多个 .so 文件吗?由于 tensorflow c-api 为您提供了多个二进制文件,我不知道哪个具有哪个功能.. 哦,我以为你构建了 cpp-wrapper。无论如何,是的,最简单的方法是只检索您需要的符号。你的决定。是的,您可以加载任意数量的库。阅读 dlopen 手册页,了解如何加载依赖项(如果这是一个问题)。 dlopen 只是将库文件加载到内存中,dlsym 会查找指定符号的位置。 好有趣!不,我实际上正在使用这个包装器:github.com/serizba/cppflow,它只包含标题,所以我想我可以以某种方式找出真正调用了哪些函数,并且只链接它们而不是包装器中的所有函数..

以上是关于C++ 如何在 C++ 中使用 dlopen()?的主要内容,如果未能解决你的问题,请参考以下文章

包含 Boost C++ 标头会导致 dlopen() 返回错误:_ZTVN10__cxxabiv117__class_type_infoE

Linux C++ libdl.so dlfcn.h使用方法(dlopen()dlsym()dlclose()dlerror())(用于动态链接库操作)(懒加载立即加载)共享库符号动态库

C++学习(四二七)dlopen failed: cannot locate symbol “atexit“

如何在 C++ 共享库中隐藏业务对象的实现细节并提供接口

C++学习(四零三)-lm -ldl

使用 SWIG 包装调用另一个对象成员函数的 C++ 类