Linux, Clang, Cmake, QtCreator: использование общих библиотек - PullRequest
0 голосов
/ 14 ноября 2018

Я изучаю 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 для его сборки. Проект компилируется, но когда я его запускаю, он завершается с segfault при вызове dlinit().

Я натыкаюсь на мое общее отсутствие знаний о C / C ++ здесь, и мне трудно понять, что происходит в GDB вокруг вещей dlsym(). Так что, если я правильно понимаю вещи (скажите мне, если я ошибаюсь), я объявил dlinit как указатель функции, и когда я вызываю dlsym, возвращаемое значение попадает в dlinit, поэтому dlinit должно указывать к функции, которую я ищу внутри своей библиотеки, и я должен быть в состоянии использовать ее. Я ожидал бы, что значение dlinit будет адресом, но после dlsym() его значение по-прежнему равно 0.

Я прочитал много интересных вещей, таких как этот ответ или что-то об атрибуте видимости для символов экспорта ( здесь ), но я нашел много примеров для gcc, и не смог найти эквивалент для clang. Наконец я прочитал о том, как я должен построить свой проект, чтобы волшебство произошло ( там ), но, опять же, мне не удалось найти эквивалент кланга.

Так чего мне здесь не хватает? При необходимости я могу предоставить файл CMakeLists.txt.

1 Ответ

0 голосов
/ 14 ноября 2018

Вы должны проверить значение указателя 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"); вернул ноль, поскольку в экспортной библиотеке нет символа с именем init, вместо этого он имеет искаженный символ C ++ для bool ::Lib::init(void).Если вы хотите получить символы с помощью dlsyn, вы должны предоставить C-интерфейс для своей библиотеки.То есть экспорт

extern "C" int init(void);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...