在Mac OS X上使用DYLD_LIBRARY_PATH是否可行?还有,它的动态库搜索算法是什么?

85

我阅读了一些关于使用DYLD_LIBRARY_PATH的文章,它们认为应该使用-install_name、@rpath和@loader_path来固定动态库路径。

在制作既能在Linux上运行又能在Mac OS X上运行的程序时,Mac OS X上的DYLD_LIBRARY_PATH与Linux上的LD_LIBRARY_PATH完全相同。我们可以共享(几乎)相同的make文件,而不需要使用-install_name和@rpath。

  • 在Mac OS X上使用DYLD_LIBRARY_PATH是否可行?
  • 当二进制文件找不到动态库时,Mac OS X的动态库搜索算法是什么?是当前目录-> DYLD_LIBRARY_PATH目录...吗?

1
我的建议是尽量避免使用DYLD_LIBRARY_PATH和LD_LIBRARY_PATH。有时使用它们很方便,但在我的经验中,它们通常被错误地使用或作为临时解决方法,最终会导致问题的出现。我对Mac搜索和指定动态链接库的方式不够熟悉,无法完全回答你的问题。man gcc和man dyld应该可以帮助你找到在Mac平台上正确的使用方法。如果你最终需要编写两个Makefile来适配不同的平台,那么Make -f会很有帮助。 - RC.
3个回答

105

正如您所注意到的,DYLD_LIBRARY_PATH 在其他*nix系统上的行为类似于 LD_LIBRARY_PATH。但是,还有另一个环境变量需要注意,它称为 DYLD_FALLBACK_LIBRARY_PATH

一般来说,这些(在 osx 和 linux 上都是)仅建议用于开发,因为它们可能会导致符号查找错误,当您使用一个没有相同符号表的库覆盖时。一个很好的例子是当您尝试使用自定义安装覆盖 VecLib 的默认安装(例如blas lapack)时。如果设置了 DYLD_LIBRARY_PATH,则这将导致链接到系统 VecLib 的应用程序中出现符号未找到错误,反之则会出现符号查找错误(在自定义应用程序中)。这是因为系统 blas/lapack 不是 ATLAS libs 的完整实现。

DYLD_FALLBACK_LIBRARY_PATH 将不会出现这些问题。

当将库安装到非标准位置时,DYLD_FALLBACK_LIBRARY_PATH 更为合理。这将在默认路径提供的库中查找符号,如果在那里找不到符号,则回退到指定的路径。

好处是此过程不会在编译针对默认库的应用程序时导致符号查找错误。

一般来说,当在非标准位置安装库时,应指定绝对路径,从而消除动态查找的歧义。


3
另一个好处是,如果你设置了DYLD_LIBRARY_PATH,X11将拒绝运行。但是,如果你设置了DYLD_FALLBACK_LIBRARY_PATH,你可以同时拥有自定义库和X11。非常好的回答! - ObscureRobot
6
这里似乎有些可疑:根据文档,如果未设置 DYLD_FALLBACK_LIBRARY_PATH,则会使用默认的库搜索路径。如果将其设置,则会忽略默认路径,引起很多问题。(至少在 Mountain Lion 上是这样的。)如果修改了 DYLD_FALLBACK_LIBRARY_PATH,那么似乎必须小心地手动添加默认值。(参见 man dlopen。)我是否漏掉了什么? - mforbes
3
Homebrew 的开发人员不喜欢 DYLD_FALLBACK_LIBRARY_PATH 这个东西:https://github.com/mxcl/homebrew/issues/13463 - Stefan Schmidt
9
由于安全原因,在El Capitan中DYLD_FALLBACK_LIBRARY_PATH似乎无法使用。有什么想法吗? - Lupurus
安装CUDA时,我们被告知要设置export DYLD_LIBRARY_PATH=/Developer/NVIDIA/CUDA-7.0/lib:$DYLD_LIBRARY_PATH - aquagremlin

13

DYLD_LIBRARY_PATH不能像LD_LIBRARY_PATH一样使用。OS X dlopen文档(https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/dlopen.3.html)指定,提供绝对路径时,它首先会查找DYLD_LIBRARY_PATH指定的位置:

当路径包含斜杠但不是框架路径(即dylib的完整路径或部分路径)时, dlopen()会搜索以下路径,直到找到兼容的Mach-O文件:$DYLD_LIBRARY_PATH(使用path中的叶子名称),然后是提供的路径(对于相对路径,使用当前工作目录),然后是$ DYLD_FALLBACK_LIBRARY_PATH(使用path中的叶子名称)。

换句话说,如果您将DYLD_LIBRARY_PATH设置为/Hello,则以下两个dlopen调用:

返回结果:
dlopen("/Hello/libfoo.so", RTLD_NOW);
dlopen("/World/libfoo.so", RTLD_NOW);

两者都将解析为/Hello/libfoo.so。这相当令人费解,并代表了一种安全漏洞。使用dlopen的软件无法保证加载了正确的库(可能在自己的环境中覆盖DYLD_LIBRARY_PATH吗?)


很多时候我发现$DYLIB_LIBRARY_PATH是空的,而且看起来dyld在我的当前工作目录中寻找库文件。这是否是因为句子“然后提供的路径(使用当前工作目录作为相对路径)”的结果,在相对路径为空的情况下? - michael_fortunato

12

要了解动态链接编辑器的环境变量以及它们如何影响动态库的搜索,请参阅man dyld

DYLD_LIBRARY_PATH

这是一个由冒号分隔的目录列表,其中包含库文件。在搜索默认库文件位置之前,动态链接器会先搜索这些目录。它允许您测试现有库的新版本。

对于程序使用的每个库,动态链接器会依次在DYLD_LIBRARY_PATH中的每个目录中查找它。如果仍然找不到该库,则依次搜索DYLD_FALLBACK_FRAMEWORK_PATH和DYLD_FALLBACK_LIBRARY_PATH。

使用otool(1)的-L选项,可以发现可执行文件链接的框架和共享库。

DYLD_FALLBACK_LIBRARY_PATH

这是一个由冒号分隔的目录列表,其中包含库文件。如果未在其安装路径中找到库文件,则将其用作默认位置。默认情况下,它设置为$(HOME)/lib:/usr/local/lib:/lib:/usr/lib。

DYLD_VERSIONED_LIBRARY_PATH

这是一个由冒号分隔的目录列表,其中包含潜在的覆盖库文件。动态链接器会在这些目录中搜索动态库文件。对于找到的每个库,dyld都会查看它的LC_ID_DYLIB并获取当前版本和安装名称。然后,Dyld会在安装名称路径中寻找该库。每当需要具有该安装名称的dylib时,将使用具有较大current_version值的库。这类似于DYLD_LIBRARY_PATH,不同之处在于它只会覆盖较新的库文件。


11
请注意,otool -L 命令会告诉你一个可执行文件所链接的库,并不一定代表这些库在该可执行文件运行时实际被加载。你可以通过设置 DYLD_LIBRARY_PATH 环境变量并运行 otool 命令来验证这一点(此时显示的库路径不会考虑 DYLD_LIBRARY_PATH 的影响)。 - Perry

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