我有一个C ++ CMake项目,该项目具有多个子项目,这些子项目打包到共享库中。然后,作为可执行文件的项目本身将与所有这些共享库链接。这是一个从Windows移植到Ubuntu的项目。我要做的是让exectable 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加载库时,我只是在main中进行了工作,并且可以工作。
基本上,我可以从exe
加载库,但是不能从其他共享库加载。有什么好处,我该如何解决?
答案 0 :(得分:1)
主可执行文件中的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
环境变量,您还可以深入了解动态加载程序的操作:
LD_DEBUG=libs ./main-exe
会告诉您加载程序正在搜索哪个目录的库以及原因。
我不知道为什么它不起作用
是的,可以。您尚未花足够的精力进行尝试。
您的第一步应该是在dlerror()
失败时打印dlopen
的值。下一步是使用LD_DEBUG
。而且如果一切失败,您实际上可以调试运行时加载程序本身-它是开源的。
答案 1 :(得分:0)
我设法找到了解决此问题的方法。我不太了解内部工作原理或解决方案的说明,但确实有效。如果有一个比我在共享库方面非常有限的经验更好的理解的人可以对我的回答进行评论并提供真正的解释,那么我相信这可以帮助将来的读者了解此问题。
我目前正在做的是dlopen("libcore.so")
。我只是将其更改为绝对路径dlopen("/home/user/project/libcore.so")
,现在可以使用了。我还没有尝试过相对路径,但是看来我们应该始终使用相对路径或绝对路径,而不仅仅是带有dlopen
的文件名。
答案 2 :(得分:0)
如果绝对路径有所帮助,则可能是共享库的本地依赖性。换句话说,也许libcontroller.so依赖于libos.so或您的其他库,但找不到它。 Linux loader意味着所有共享库都放在/ lib,/ usr / lib等中。您需要指定用于查找具有环境变量LD_LIBRARY_PATH的动态库的路径。
尝试以这种方式运行您的应用程序: LD_LIBRARY_PATH = /路径/到/您的/可执行文件/和/模块。/您的应用
答案 3 :(得分:-1)
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
(如果存在)以及{{ 1}}。
,但是所有其他库失败,没有错误消息。我不知道为什么它不起作用。
如@Paul在评论中所述,检查dlopen
错误的方法是使用dlerror
。因为您只能获取文本字符串而不是错误代码,所以这是一种not脚的方法。
dlopen
手册页位于http://man7.org/linux/man-pages/man3/dlopen.3.html,上面显示:
返回值
成功后,dlopen()和dlmopen()返回一个非NULL句柄,用于 已加载的库。发生错误(找不到文件,不可读, 格式错误,或在加载过程中导致错误),这些 函数返回NULL。
成功时,dlclose()返回0;否则,返回0。错误时,它将返回非零值。
可以使用dlerror(3)诊断这些功能中的错误。