Linux,Clang,Cmake,QtCreator:使用共享库

时间:2018-11-14 07:07:46

标签: c++ linux shared-libraries

我正在学习C ++,我来自Java,我想写一些加载插件的程序。 基本上我想做的是:

  • 运行程序
  • 加载共享库(插件)
  • 在主程序列表中的共享库中注册一个功能
  • 在主程序中运行这些功能
  • 能够使用库中的主程序中编写的某些功能

正如我所说,我来自Java,而我要做的就是import somestuff;以能够使用它。所以我试图为C ++弄清楚这一点。我已经读过dlopen / dlsym是一个解决方案,所以我阅读了这些手册页和一些示例,这就是我所做的:

main.h

#ifndef MAIN_H
#define MAIN_H
#include <functional>
#include <vector>

class Test{
    public :
    static std::vector <std::function<void()>> initFuncList;
    static bool registerInitFunc(std::function<void()> Func);
};

#endif // MAIN_H

main.cpp

#include <dlfcn.h>
#include "main.h"

std::vector <std::function<void()>> Test::initFuncList;

bool Test::registerInitFunc(std::function<void()> Func)
{
    initFuncList.push_back(Func);
    return true;
}

int main()
{
    static bool (*dlinit)(void);
    printf("opening library.\n");
    void* dlh = dlopen("./libtest.so", RTLD_NOW);
    if (!dlh)
    {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }
    printf("Library opened.\n Reading init function address.\n");
    *(void **) (&dlinit) = dlsym(dlh, "init");
    printf("Function address is %p.\n", dlinit);
    if(!dlinit())
    {
        exit(EXIT_FAILURE);
    }
    printf("Library initialized, function registered.\n");
    for(auto func : Test::initFuncList)
    {
        printf("Looping through registered functions.\n");
        func();
    }
    return EXIT_SUCCESS;
}

exportlib.h

#ifndef LIB_H
#define LIB_H

class Lib
{
public:
    Lib();
    static void func(void);
    static bool init(void);
};

#endif // LIB_H

exportlib.cpp

#include "exportlib.h"
#include "main.h"

Lib::Lib(){}

bool Lib::init()
{
    printf("Initializing library.\n");
    return (Test::registerInitFunc(func));
}

void Lib::func()
{
    printf("This is the library function called after initialization.\n");
}

我正在使用QtCreator作为IDE来解析CMake项目,并且正在使用CLang 7.0.0来构建它。该项目已构建,但是在我运行它时,它在dlinit()调用中因段错误而崩溃。

在这里,我对自己普遍缺乏C / C ++的知识感到沮丧,并且我很难理解dlsym()周围的GDB中会发生什么。因此,如果我正确理解了事情(如果我错了,请告诉我),我已经声明dlinit作为函数指针,并且当我调用dlsym时,返回值进入dlinit因此dlinit应该指向我正在库中寻找的函数,并且我应该能够使用它。我希望dlinit的值是一个地址,但是在dlsym()之后,它的值仍为0。

我已经阅读了很多有趣的内容,例如this answer或有关导出符号的可见性属性(here)的内容,但是我发现了很多gcc的示例,但未能找到等效的lang。最终,我了解了有关构建项目以使魔术发生的方式(there)的内容,但是再次,我找不到等效的clang。

那么我在这里想念的是什么?如果需要,我可以提供CMakeLists.txt文件。

1 个答案:

答案 0 :(得分:2)

在调用其指向的函数之前,应检查dlinit指针的值:

if(not dlinit)
{
    auto const psz_error{::dlerror()};
    fprintf(stderr, "dlsym failed to fetch init: %s\n", (psz_error ? psz_error : "unknown"));
    exit(EXIT_FAILURE);
}

现在最有可能dlsym(dlh, "init");返回null,因为export lib没有名为init的符号,而是bool ::Lib::init(void)的C ++符号有误。如果要使用dlsyn获取符号,则应为库提供一个C接口。那就是出口

extern "C" int init(void);