C++ Linux:dlopen无法找到.so库

7

重新提出的问题(尽管它已经被解决):

我一直在使用dlopen(3)在Linux上加载共享对象库时遇到问题。该库是由我构建的一组系统库的一部分,所有这些库都由一个中央可执行文件在运行时加载。所有这些都组织在Code::Blocks中的单个工作区中,其中每个项目都在名为Source的目录中拥有自己的文件夹,该目录将随程序一起发布。可执行文件的构建目录与其自身源代码相隔两个目录,因此可执行文件和Source文件夹位于同一个目录中。库也构建到与可执行文件相同的目录中,因此我按如下所示传递了我要打开的库的名称:

int main(int argc, char** argv) {
    void* hLibrary = dlopen("libLibrary.so", RTLD_NOW | RTLD_GLOBAL);
    if(hLibrary == NULL) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }

    return 0;
}

这曾经在构建目录与源代码相同时工作过,直到我调整了源代码的目录结构以符合上述安排。此时问题在于dlerror()返回“无法打开libLibrary.so:没有这样的文件或目录”,尽管该文件明显存在并且与可执行文件位于同一目录中。然后我尝试传递“/libLibrary.so”,因为根据dlopen(3)手册页,添加“/”表示相对目录。这返回了相同的错误。
解决方案是需要一个“./” - 其中“.”表示可执行文件的工作目录 - 并且Code :: Blocks中的工作目录需要更改为构建可执行文件的位置。以下内容完美地解决了这个问题:
void* hLibrary = dlopen("./libLibrary.so", RTLD_NOW | RTLD_GLOBAL);

这并没有完全展示解决方案,但以下基本上就是我所做的等价物:

void* hLibrary = dlopen("./../../libLibrary.so", RTLD_NOW | RTLD_GLOBAL);

希望这能更好地解释情况。
2个回答

18

阅读dlopen(3)手册(例如,在您的计算机终端上键入man dlopen):

如果文件名包含斜杠("/"),则将其解释为相对或绝对路径名。否则,动态链接器按以下方式搜索库(有关详细信息,请参见ld.so(8)):

   o   (ELF only) If the executable file for the calling program
       contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
       then the directories listed in the DT_RPATH tag are searched.

   o   If, at the time that the program was started, the environment
       variable LD_LIBRARY_PATH was defined to contain a colon-separated
       list of directories, then these are searched.  (As a security
       measure this variable is ignored for set-user-ID and set-group-ID
       programs.)

   o   (ELF only) If the executable file for the calling program
       contains a DT_RUNPATH tag, then the directories listed in that
       tag are searched.

   o   The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is
       checked to see whether it contains an entry for filename.

   o   The directories /lib and /usr/lib are searched (in that order).

所以你需要调用dlopen("./libLibraryName.so", RTLD_NOW) - 而不是只调用dlopen("libLibraryName.so", RTLD_NOW),后者要求插件在$LD_LIBRARY_PATH或者/usr/lib/等目录下。或者将.加入到LD_LIBRARY_PATH中(因为安全原因我不建议这样做)。

正如Jhonnash的回答所述,当dlopen(或dlsym)失败时,你应该使用并显示dlerror的结果:

  void* dlh = dlopen("./libLibraryName.so", RTLD_NOW);
  if (!dlh) 
    { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
      exit(EXIT_FAILURE); };

你可能想阅读一些类似于《高级 Linux 编程》的书籍,以获取关于 Linux 系统编程的一般知识。


1
在我提问之前,我确实阅读了那个页面——让我困惑的是“相对或绝对”(它们就像相反的……哪一个是正确的?),而且没有提到“.”。我大部分时间都在Windows上编程,所以我不太清楚纯名称、名称前面的/和名称前面的./之间的区别。不过这样修复了,谢谢。 - NmdMystery
不过有一件事 - 为什么在库名前面没有加上"./"的时候,dlopen之前还是能够找到它呢?它可以很好地找到库和其符号,这让我感到困惑。(另外,我已经弄清楚"."代表的是工作目录,只是在Windows上我从来没有需要使用它) - NmdMystery
1
Linux不是Windows,而是一个Unix系统。因此,请不要使用Windows的习惯思维。 - Basile Starynkevitch
1
好的,现在它给我跟之前一样的错误。我仔细检查了一切 - 库和可执行文件在同一目录中,名称前面有 "./",名称本身没有拼写错误... 我让它在源代码的两个目录向后构建,并设置工作目录为 "."。"." 是指源代码目录还是可执行文件目录? - NmdMystery
原来是执行工作目录引起了这个问题。我看了一下你发布的书,受益匪浅,正在采纳其中的建议。 - NmdMystery

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接