Пытаюсь написать небольшую программку с возможностью расширения её функционала через модули которые подгружать хочу вручную.
Вроде флаг RTLD_GLOBAL во второй параметр функции dlopen вполне может такое позволить сделать.
Ну так вот. Идея такая.
1. Программка подтягивает библиотечку (libcore.so) которая, в свою очередь добавляет к уже загруженной программе два вызова - core::oninit и core::doinit
2. Дальше программка догружает модуль (module.so) и вызывает функцию (module) которая в нём объявлена - что бы дальше модуль добавлял что-то в систему через обращение к core::oninit
... если будут ещё модули их тоже подгружаем и они тоже в своих функциях «module» вызывают core::oninit, и т.д.
3. Потом в основной программе делается core::doinit что бы по очереди вызвать то, что модули наскладывали в core::oninit
Вроде простая схема, но... упорно вылетает «Segmentation fault» уже ПОСЛЕ ТОГО КАК ВСЁ ПОЛЕЗНОЕ В main УЖЕ ВЫПОЛНЕНО.
Поэтому собственно вопрос - почему?
Ну или, что то же самое, - что я делаю не так?
И как такое надо делать на самом деле правильно?
Сори за небольшое полотно из исходников, но их, на самом деле, не много.
Заранее спасибо за ответы.
start.cpp
#include <stdexcept>
#include <memory>
#include <dlfcn.h>
class Lib
{
public:
Lib(const std::string &filename)
{
auto handle = new void *;
try
{
*handle = dlopen(filename.c_str(), RTLD_LAZY | RTLD_GLOBAL);
if (*handle == nullptr) throw std::runtime_error(std::string(dlerror()));
dlerror();
try
{
this->m_handle = std::shared_ptr<void *>(handle, [] (void **handle)
{
dlclose(*handle);
delete handle;
});
}
catch (...)
{
dlclose(*handle);
throw;
}
}
catch (...)
{
delete handle;
throw;
}
}
void *get(const std::string &itemname)
{
auto item = dlsym(*this->m_handle, itemname.c_str());
auto error = dlerror();
if (error != nullptr) throw std::runtime_error(std::string(error));
return item;
}
private:
std::shared_ptr<void *> m_handle;
};
class Module
{
public:
Module(const std::string &filename)
{
auto state = new State(filename + ".so");
try
{
this->m_state = std::shared_ptr<State>(state, [] (State *state)
{
delete state;
});
}
catch (...)
{
delete state;
throw;
}
}
private:
struct State
{
Lib lib;
State(const std::string &filename): lib(Lib(filename))
{
auto module = (void (*)(void)) this->lib.get("module");
if (module == nullptr) std::runtime_error("Error get 'module' routine from \"" + filename + '.');
module();
}
};
std::shared_ptr<State> m_state;
};
#include <iostream>
#include <vector>
#include "core.h"
int main(void)
{
{
std::vector<Module> modules;
modules.push_back(Module("module"));
core::doinit();
}
std::cout << "Ok\n";
return EXIT_SUCCESS;
}
core.h
#ifndef __CORE__
#define __CORE__
#include <functional>
namespace core
{
extern void oninit(const std::function<void(void)> &init);
extern void doinit(void);
}
#endif // __CORE__
core.cpp
#include "core.h"
#include <vector>
static std::vector<std::function<void(void)>> items;
void core::oninit(const std::function<void(void)> &init)
{
items.push_back(init);
}
void core::doinit(void)
{
for (auto &init : items) if (init) init();
}
module.cpp
#include "core.h"
#include <iostream>
extern "C" void module(void)
{
core::oninit([] () {});
std::cout << "Module loaded.\n";
}
build.sh
#!/usr/bin/env bash
export LD_LIBRARY_PATH=./:${LD_LIBRARY_PATH}
#std=c++11
#std=c++14
std=c++17
gcc --std=$std core.cpp -shared -fPIC -o libcore.so &&
gcc --std=$std module.cpp -shared -fPIC -o module.so &&
gcc --std=$std start.cpp -l stdc++ -ldl -L. -lcore -o start &&
./start
rm *.so
rm start