[英]Cannot dlopen a shared library from a shared library, only from executables
I have a C++ CMake project that has multiple sub-projects that I package into shared libraries. 我有一个C ++ CMake项目,它有多个子项目,我打包到共享库中。 Then, the project itself, which is an executable, links with all these shared libraries. 然后,项目本身(可执行文件)与所有这些共享库链接。 This is a project that is being ported from Windows to Ubuntu. 这是一个从Windows移植到Ubuntu的项目。 What I do is have the exectable, EXE, use a one subproject, Core, to open all other libraries. 我所做的是使用exectable,EXE,使用一个子项目Core来打开所有其他库。 Problem is that this isn't working on Linux. 问题是这不适用于Linux。
This is EXE: 这是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")
}
This is core::plugin::PluginManager::loadPlugin()
: 这是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;
}
What happens is that libcore gets loaded properly, but then all other libraries fail with no no error message. 会发生什么是libcore正确加载,但所有其他库失败,没有错误消息。 I cannot find out why it's not working. 我无法找出它为什么不起作用。 However, when I do the same thing, but instead of having Core load the libraries, I simply do it in main and it works. 但是,当我做同样的事情,但不是让Core加载库,我只是在main中做它并且它工作。
Basically, I can load libraries from an exe
, but I can't from other shared libraries. 基本上,我可以从exe
加载库,但我不能从其他共享库加载。 What gives and how can I fix this? 给出了什么以及如何解决这个问题?
The most likely reason for dlopen
from the main executable to succeed and for the exact same dlopen
from libcore.so
to fail is that the main executable has correct RUNPATH
to find all the libraries, but libcore.so
does not. 对于最可能的原因dlopen
从主可执行成功和完全一样dlopen
从libcore.so
失败是主要的可执行文件具有正确RUNPATH
找到所有的库,但libcore.so
没有。
You can verify this with: 您可以通过以下方式验证:
readelf -d main-exe | grep R.*PATH
readelf -d libcore.so | grep R.PATH
If (as I suspect) main-exe has RUNPATH
, and libcore.so
doesn't, the right fix is to add -rpath=....
to the link line for libcore.so
. 如果(我怀疑)main-exe有RUNPATH
,而libcore.so
没有,正确的解决方法是将-rpath=....
添加到libcore.so
的链接行。
You can also gain a lot of insight into dynamic loader operation by using LD_DEBUG
envrironment variable: 您还可以通过使用LD_DEBUG
环境变量获得对动态加载程序操作的大量了解:
LD_DEBUG=libs ./main-exe
will tell you which directories the loader is searching for which libraries, and why. 将告诉您加载器正在搜索哪些目录库以及原因。
I cannot find out why it's not working 我无法找出它为什么不起作用
Yes, you can. 是的你可以。 You haven't spent nearly enough effort trying. 你没有花费足够的努力尝试。
Your very first step should be to print the value of dlerror()
when dlopen
fails. 你的第一步应该是在dlopen
失败时打印dlerror()
的值。 The next step is to use LD_DEBUG
. 下一步是使用LD_DEBUG
。 And if all that fails, you can actually debug the runtime loader itself -- it's open-source. 如果一切都失败了,你可以实际调试运行时加载器本身 - 它是开源的。
I managed to find a fix for this issue. 我设法找到了解决这个问题的方法。 I don't quite understand the inner workings nor the explanation of my solution, but it works. 我不太了解我的解决方案的内部工作原理和解释,但它确实有效。 If someone who has a better understanding than my very limited experience with shared libraries could comment on my answer with the real explanation, I'm sure it could help future viewers of this question. 如果有人比我对共享库的非常有限的经验有更好的理解,可以用真实的解释来评论我的答案,我相信它可以帮助未来的观众解决这个问题。
What I was currently doing is dlopen("libcore.so")
. 我目前正在做的是dlopen("libcore.so")
。 I simply changed it to an absolute path dlopen("/home/user/project/libcore.so")
and it now works. 我只是将它改为绝对路径dlopen("/home/user/project/libcore.so")
,它现在可以工作了。 I have not yet tried with relative paths, but it appears we should always use relative or absolute paths instead of just the filename with dlopen
. 我还没有试过相对路径,但看起来我们应该总是使用相对路径或绝对路径,而不仅仅是使用dlopen
的文件名。
If absolute path was help, maybe problem is local dependencies of shared libraries. 如果绝对路径是帮助,可能问题是共享库的本地依赖性。 Another words, maybe libcontroller.so is depend from libos.so or other your library, but cannot find it. 换句话说,libcontroller.so可能依赖于libos.so或其他你的库,但找不到它。 Linux loader means that all shared libraries are placed in /lib, /usr/lib, etc. You need to specify path for find dynamic libraries with environment variable LD_LIBRARY_PATH. Linux加载器意味着所有共享库都放在/ lib,/ usr / lib等中。您需要为环境变量LD_LIBRARY_PATH指定查找动态库的路径。
Try to run your app this way: LD_LIBRARY_PATH=/path/to/your/executable/and/modules ./yourapp 尝试以这种方式运行您的应用程序:LD_LIBRARY_PATH = / path / to / your / executable /和/ modules ./yourapp
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; }
The flags to use with dlopen
depend upon the distro. 与dlopen
一起使用的标志取决于发行版。 I think Debian and derivatives use RTLD_GLOBAL | RTLD_LAZY
我认为Debian和衍生品使用RTLD_GLOBAL | RTLD_LAZY
RTLD_GLOBAL | RTLD_LAZY
, while Red Hat and derivatives use RTLD_GLOBAL
. RTLD_GLOBAL | RTLD_LAZY
,而Red Hat和衍生产品使用RTLD_GLOBAL
。 Or maybe it is vice-versa. 或者反之亦然。 And I seem to recall Android uses RTLD_LOCAL
, too. 我似乎还记得Android也使用RTLD_LOCAL
。
You should just try both to simplify loading on different platforms: 您应该尝试两者来简化在不同平台上的加载:
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 ;
}
What happens is that libcore gets loaded properly, but then all other libraries fail with no no error message 会发生什么是libcore正确加载,但所有其他库失败,没有错误消息
This sounds a bit unusual. 这听起来有点不寻常。 It sounds like the additional libraries from the sub-projects are not in the linker path. 听起来子项目中的附加库不在链接器路径中。
You should ensure the additional libraries are in the linker path. 您应确保其他库位于链接器路径中。 Put them next to libcore.so
in the filesystem since loading libcore.so
seems to work as expected. 将它们放在文件系统中的libcore.so
旁边,因为加载libcore.so
似乎按预期工作。
If they are already next to libcore.so
, then you need to provide more information, like the failure from loadPlugin
, the RUNPATH
used (if present) and the output of ldd
. 如果它们已经在libcore.so
旁边,那么您需要提供更多信息,例如loadPlugin
的失败,使用的RUNPATH
(如果存在)和ldd
的输出。
but then all other libraries fail with no no error message. 但然后所有其他库失败,没有没有错误消息。 I cannot find out why it's not working. 我无法找出它为什么不起作用。
As @Paul stated in the comments, the way to check for a dlopen
error is with dlerror
. 正如@Paul在评论中所说,检查dlopen
错误的方法是使用dlerror
。 It is kind of a crappy way to do it since you can only get a text string and not an error code. 这是一种糟糕的方式,因为你只能获得文本字符串而不是错误代码。
The dlopen
man page is at http://man7.org/linux/man-pages/man3/dlopen.3.html , and it says: dlopen
手册页位于http://man7.org/linux/man-pages/man3/dlopen.3.html ,它说:
RETURN VALUE 返回值
On success, dlopen() and dlmopen() return a non-NULL handle for the loaded library. 成功时,dlopen()和dlmopen()为加载的库返回一个非NULL句柄。 On error (file could not be found, was not readable, had the wrong format, or caused errors during loading), these functions return NULL. 出错(无法找到文件,无法读取,格式错误或在加载过程中导致错误)时,这些函数将返回NULL。
On success, dlclose() returns 0; 成功时,dlclose()返回0; on error, it returns a nonzero value. 出错时,它返回非零值。
Errors from these functions can be diagnosed using dlerror(3). 可以使用dlerror(3)诊断这些函数的错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.