Невозможно добавить общую библиотеку из общей библиотеки, только из исполняемых файлов - PullRequest
3 голосов
/ 05 июля 2019

У меня есть проект C ++ CMake, в котором есть несколько подпроектов, которые я упаковываю в общие библиотеки. Затем сам проект, который является исполняемым файлом, связывается со всеми этими общими библиотеками. Это проект, который переносится из Windows в Ubuntu. У меня есть exableable, EXE, использующий один подпроект, Core, чтобы открыть все остальные библиотеки. Проблема в том, что это не работает в Linux.

Это EXE:

int main(int argc, char *argv[])
{
    core::plugin::PluginManager& wPluginManager = core::plugin::PluginManagerSingleton::Instance();
    wPluginManager.loadPlugin("libcore.so");
    wPluginManager.loadPlugin("libcontroller.so")
    wPluginManager.loadPlugin("libos.so")
    wPluginManager.loadPlugin("libnetwork.so")
    wPluginManager.loadPlugin("liblogger.so")
}

Это core::plugin::PluginManager::loadPlugin():

bool PluginManager::loadPlugin(const boost::filesystem::path &iPlugin) {
    void* plugin_file = dlopen(plugin_file_name, RTLD_LAZY);
    std::cout << (plugin_file ? " success" : "failed") << std::endl;
    return true;
}

То, что происходит, - то, что libcore загружается должным образом, но тогда все другие библиотеки терпят неудачу без сообщения об ошибке. Я не могу понять, почему это не работает. Однако, когда я делаю то же самое, но вместо загрузки библиотек Core, я просто делаю это в основном, и это работает.

По сути, я могу загружать библиотеки из exe, но не могу из других общих библиотек. Что дает и как я могу это исправить?

Ответы [ 4 ]

1 голос
/ 07 июля 2019

Наиболее вероятная причина для dlopen из основного исполняемого файла для успеха и для точно такого же dlopen из libcore.so в том, что основной исполняемый файл имеет правильный RUNPATH для поиска всех библиотек, но libcore.so нет.

Вы можете проверить это с помощью:

readelf -d main-exe | grep R.*PATH
readelf -d libcore.so | grep R.PATH

Если (как я подозреваю) main-exe имеет RUNPATH, а libcore.so - нет, то правильным решением будет добавить -rpath=.... к строке ссылки для libcore.so.

Вы также можете получить представление о работе динамического загрузчика, используя LD_DEBUG переменную envrironment:

LD_DEBUG=libs ./main-exe

скажет вам, какие каталоги ищет загрузчик, какие библиотеки и почему.

Я не могу понять, почему он не работает

Да, вы можете. Вы не потратили достаточно усилий, пытаясь.

Ваш самый первый шаг должен быть напечатан значением dlerror(), когда dlopen не удается. Следующим шагом будет использование LD_DEBUG. И если все, что не получается, вы можете отладить сам загрузчик времени выполнения - он с открытым исходным кодом.

0 голосов
/ 08 июля 2019

Если абсолютный путь помог, возможно, проблема в локальных зависимостях разделяемых библиотек. Другими словами, может быть libcontroller.so зависит от libos.so или другой вашей библиотеки, но не может ее найти. Загрузчик Linux означает, что все разделяемые библиотеки помещаются в / lib, / usr / lib и т. Д. Вам нужно указать путь для поиска динамических библиотек с переменной окружения LD_LIBRARY_PATH.

Попробуйте запустить приложение следующим образом: LD_LIBRARY_PATH = / путь / к / вашему / исполняемому файлу / и / модулям ./yourapp

0 голосов
/ 08 июля 2019

Мне удалось найти решение этой проблемы. Я не совсем понимаю внутреннюю работу или объяснение моего решения, но оно работает. Если кто-то, кто лучше понимает, чем мой очень ограниченный опыт работы с общими библиотеками, мог бы прокомментировать мой ответ с реальным объяснением, я уверен, что это могло бы помочь будущим зрителям этого вопроса.

То, что я сейчас делал, это dlopen("libcore.so"). Я просто изменил его на абсолютный путь dlopen("/home/user/project/libcore.so"), и теперь он работает. Я еще не пробовал с относительными путями, но кажется, что мы всегда должны использовать относительные или абсолютные пути вместо только имени файла с dlopen.

0 голосов
/ 06 июля 2019
bool PluginManager::loadPlugin(const boost::filesystem::path &iPlugin) {
    void* plugin_file = dlopen(plugin_file_name, RTLD_LAZY);
    std::cout << (plugin_file ? " success" : "failed") << std::endl;
    return true;
}

Флаги для использования с dlopen зависят от дистрибутива. Я думаю, что Debian и его производные используют RTLD_GLOBAL | RTLD_LAZY, а Red Hat и производные используют RTLD_GLOBAL. Или, может быть, это наоборот. Кажется, я помню, что Android тоже использует RTLD_LOCAL.

Вы должны просто попробовать оба варианта, чтобы упростить загрузку на разных платформах:

bool PluginManager::loadPlugin(const boost::filesystem::path &iPlugin) {
    void* plugin_file = dlopen(plugin_file_name, RTLD_GLOBAL);
    if (!plugin_file) {
        plugin_file = dlopen(plugin_file_name, RTLD_GLOBAL | RTLD_LAZY);
    }
    const bool success = plugin_file != NULL;
    std::cout << (success ? "success" : "failed") << std::endl;
    return success ;
}

То, что происходит, - то, что libcore загружается должным образом, но тогда все другие библиотеки терпят неудачу без сообщения об ошибке

Это звучит немного необычно. Похоже, что дополнительные библиотеки из подпроектов не находятся в пути компоновщика.

Вы должны убедиться, что дополнительные библиотеки находятся в пути компоновщика. Поместите их рядом с libcore.so в файловой системе, поскольку загрузка libcore.so, кажется, работает должным образом.

Если они уже находятся рядом с libcore.so, то вам необходимо предоставить дополнительную информацию, такую ​​как ошибка loadPlugin, использованный RUNPATH (если имеется) и выходные данные ldd.


но тогда все остальные библиотеки перестают работать без сообщения об ошибке. Я не могу понять, почему это не работает.

Как отметил @Paul в комментариях, способ проверки на наличие ошибки dlopen заключается в dlerror. Это довольно дурацкий способ сделать это, поскольку вы можете получить только текстовую строку, а не код ошибки.

Справочная страница dlopen находится по адресу http://man7.org/linux/man-pages/man3/dlopen.3.html, и гласит:

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

В случае успеха dlopen () и dlmopen () возвращают ненулевой дескриптор для загруженная библиотека. При ошибке (файл не найден, не читается, имели неправильный формат или вызвали ошибки при загрузке), эти функции возвращают NULL.

В случае успеха dlclose () возвращает 0; в случае ошибки возвращается ненулевое значение.

Ошибки этих функций можно диагностировать с помощью dlerror (3).

...