有必要 LD_LIBRARY_PATH(linux) 还是 DYLD_LIBRARY_PATH mac

Is it necessary LD_LIBRARY_PATH(linux) or DYLD_LIBRARY_PATH mac

我正在使用 openCV 库进行计算机视觉研究,在编译时遇到了一些问题,这些问题让我试图了解操作系统如何将库与源代码链接起来。在互联网上寻找了一段时间以获得 g++/gcc 的良好概述和阅读手册后,ld...我有一些结论,我希望有更多经验的人来解释我。

首先是我使用的编译行。这是:

-输入:g++pkg-config --cflags --libs opencvimage-conversion.cpp -o image-conversion

我的代码需要的库是 -lopenhighgui,但我更喜欢用这种方式编译,因为这个库依赖于其他库。问题是,当我去 /opt/local/lib 查看图书馆时,我有三个文件:

-libopencv_highgui.3.1.0.dylib
-libopencv_highgui.3.1.dylib
-libopencv_highgui.dylib

我不知道 -lopenhighgui 指的是哪个图书馆。我在 g++ 的手册中发现 -l 标志指定库名称避免 lib 前缀和 *.a *.so (linux)/*.dylib (mac) 后缀。执行 otool -L 可执行文件后,我得到输出:

那么为什么它使用这个而不是其他的,它的使用方式是什么?三个库有什么区别?

另一个问题是关于链接和执行过程。我已经了解使用静态库时的链接过程。我的问题是在编译动态库时。在下一个示例中:

-输入:g++ -I/opt/local/include/opencv -I/opt/local/include -L/opt/local/lib -lopencv_highgui image-conversion.cpp -o image-conversion

我发现一个程序从编译到执行的过程可以分为三个部分。

问题是下一个:我在网上发现一些人说如果他们不设置$LD_LIBRARY_PATH(在linux)或$DYLD_LIBRARY_PATH(在mac OSx) 与库的非标准目录(在我的例子中是 export LD_LIBRARY_PATH="/opt/local/lib"),动态链接器找不到库并且程序执行失败。我发现我的程序没有崩溃,如果我执行 otool(用于查看链接的内容)。我得到这个(这是所有链接的库的摘要):

/opt/local/lib/libopencv_shape.3.1.dylib (compatibility version 3.1.0, current version 3.1.0) /opt/local/lib/libopencv_imgproc.3.1.dylib (compatibility version 3.1.0, current version 3.1.0) ... /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

这是怎么回事?我发现 LD_LIBRARY_PATH 可用于测试新库,但为什么这些人说他们只需要为执行程序设置此变量?这最后一部分是如何工作的?我没有设置它,它工作得很好。

感谢任何人。

实际上有两个问题(顺便说一句,这不是一个很好的 SO 实践),所以我将独立回答它们。

此外,我将重点关注 Linux - 这是我熟悉的领域。从问题的文本来看,我认为 Mac 世界中的事情非常相似。

库命名约定

在 Linux 环境中使用 .so 库时,经常会看到动态库通常以三元组的形式出现。例如,库 foo 可能存在于 3 个文件中:libfoo.solibfoo.so.6libfoo.so.6.5.4。如果你仔细看,你会发现它们都是同一个文件——通常其中两个只是符号 links 到第三个。为了进一步讨论,libfoo.so 将被称为 unversioned 库,ibfoo.so.6 将被称为 major-versioned 和 libfoo.so.6.5.4 *full-versioned。你为什么需要那些?为了更好的版本控制。

当您 link 您的应用程序时,您总是使用 linker 规则用于未版本化的 livbary - 考虑到 linker 添加 lib.so 按照规则,它看起来像

g++    ... -lfoo ...

当您的应用程序被 linked 时,linker 打开 libfoo.so 并检查它的几项内容。它检查的内容之一是 so-called SONAME header。此 header 是在 linked .so 库时创建的,它的文件名可以与 linker 当前正在查看的文件名不同。例如,它可能有一个 major-versioned 文件,link 用户会看到它:SONAME = libfoo.so.6。

当 linker 看到 SONAME 时,它会将生成的应用程序文件标记为需要 libfoo.so.6 - 即使您实际上要求 libfoo.so.

通过这样做 linker 保留了库的特定版本。您的应用程序最初是使用版本 6 编译和 linked 的,因此只要应用程序是 运行,就需要版本 6。

如果稍后系统升级(或应用程序 运行s 在不同的系统上),其中 foo 是不同的版本(比如 7),.so 文件将不同: libfoo.so, libfoo.so.7, libfoo.so.7.6.5。由于您的应用程序需要 libfoo.so.6,它将无法启动 - 这是一件好事,因为谁知道版本 7 是否仍然兼容?如果没有此保护,应用程序将启动并使用不同的库版本,其影响可能是毁灭性的。

LD_LIBRARY_PATH 搜索

你的第二个问题是 LD_LIBRARY_PATH。确实如此,run-time linker 在查找动态库时参考这个变量。然而,这并不是它咨询的唯一内容。除此之外,除了默认搜索路径外,还有一个per-application动态库路径,它在应用程序linked时记录在应用程序中,通常由[=85=的rpath参数控制]呃,比如:

g++ ... -Wl,rpath,/path/to/so/library

当这样记录路径时,run-time link 用户将在加载应用程序时将这些路径添加到搜索路径列表中。

可以在没有 LD_LIBRARY_PATH 的情况下为您的应用程序找到库的事实意味着以下两种情况之一:在 linked 应用程序时记录了 rpath,或者实际上包含了 /opt/local/lib在您平台的默认搜索路径中。