如何指定库路径的优先级?

119

我正在使用g++ld编译一个C++程序。我有一个.so库文件,希望在链接时使用。但是,在/usr/local/lib目录下存在同名的库文件,并且ld选择了那个库文件而不是我直接指定的那个。我该如何解决这个问题?

以下是我的库文件路径:/my/dir/libfoo.so.0。以下是尝试过但没有成功的方法:

  • 我的g++命令是:g++ -g -Wall -o my_binary -L/my/dir -lfoo bar.cpp
  • /my/dir添加到我的$PATH环境变量的开头或结尾
  • /my/dir/libfoo.so.0作为参数添加到g++中

1
还有哪些 libfoo.* 文件存在,以及它们在哪里 -- .so 没有 .0.a 等等? - Alex Martelli
研究RPATH - Jesper Juhl
5个回答

113

将新库所在路径添加到LD_LIBRARY_PATH(在Mac上名称略有不同...)

您可以使用-L /my/dir -lfoo选项,运行时使用LD_LIBRARY_PATH指向库的位置。

小心使用LD_LIBRARY_PATH - 简而言之(来自链接):

..含义..:
安全性:请记住,在搜索标准位置之前,LD_LIBRARY_PATH中指定的目录会被搜索?这样,一个可怕的人可以让您的应用程序加载包含恶意代码的共享库版本!这是setuid/setgid可执行文件忽略该变量的原因之一!
性能:链接加载器必须搜索所有指定目录,直到找到共享库所在的目录-对于应用程序链接的所有共享库!这意味着需要大量的系统调用open(),这些调用将返回“ENOENT(没有此类文件或目录)”!如果路径包含许多目录,则失败调用的数量将线性增加,并且您可以从应用程序的启动时间开始推断出来。如果某些(或所有)目录在NFS环境中,则应用程序的启动时间可能会真正变长-并且可能会使整个系统变慢!
不一致性:这是最常见的问题。LD_LIBRARY_PATH强制应用程序加载其未链接的共享库,这很可能与原始版本不兼容。这可能非常明显,即应用程序崩溃,或者它可能导致错误的结果,如果选择的库与原始版本不太一样。特别是后者有时很难调试。

或者

通过gcc到链接器使用rpath选项-运行时库搜索路径,将被用于替代在标准目录中查找(gcc选项):

-Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)

这对于临时解决方案来说是不错的。链接器在查找标准目录之前会先在LD_LIBRARY_PATH中查找库。

如果您不想永久更新LD_LIBRARY_PATH,可以在命令行上动态更改:

LD_LIBRARY_PATH=/some/custom/dir ./fooo

你可以通过以下方式查看连接器已知的库:

/sbin/ldconfig -p | grep libpthread
        libpthread.so.0 (libc6, OS ABI: Linux 2.6.4) => /lib/libpthread.so.0

你可以检查你的应用程序使用哪个库:

ldd foo
        linux-gate.so.1 =>  (0xffffe000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xb7f9e000)
        libxml2.so.2 => /usr/lib/libxml2.so.2 (0xb7e6e000)
        librt.so.1 => /lib/librt.so.1 (0xb7e65000)
        libm.so.6 => /lib/libm.so.6 (0xb7d5b000)
        libc.so.6 => /lib/libc.so.6 (0xb7c2e000)
        /lib/ld-linux.so.2 (0xb7fc7000)
        libdl.so.2 => /lib/libdl.so.2 (0xb7c2a000)
        libz.so.1 => /lib/libz.so.1 (0xb7c18000)

30
在运行时搜索LD_LIBRARY_PATH,在编译时设置LIBRARY_PATH。请参阅https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html。 - Bjoern Dahlgren
1
如果你的库与系统库完全不同,即始终要使用它,请使用rpath解决方案。 LD_LIBRARY_PATH是一种测试中的hack方法,不应该需要使可执行文件正常工作。 - user2746401
1
这是Mac的DYLD_LIBRARY_PATH。 - cbinder
以下是一个完整的(C语言)命令示例,基于这个答案而工作:gcc myFile.c -o myFile.o -l myLibraryBaseName -Wl,-rpath,locationOfMyLibrary -L locationOfMyLibrary - Jet Blue
可执行文件在运行时使用“-L /my/dir -lfoo”参数不应该知道库所在的位置吗?为什么我们需要再次使用“-Wl,-rpath(LD_LIBRARY_PATH)”来告诉它库的位置呢? - Ramses Aldama

39

这是一个比较老的问题,但似乎没有人提到过这一点。

你幸运的是那个东西还能链接上。

你需要进行更改。

g++ -g -Wall -o my_binary -L/my/dir -lfoo bar.cpp

到这个:

g++ -g -Wall -o my_binary -L/my/dir bar.cpp -lfoo
你的链接器会跟踪需要解决的符号。如果它首先读取库,则其中不会有任何需要的符号,因此它将忽略其中的符号。请在需要链接到它们的内容之后指定库,以便您的链接器能够在其中找到符号。
另外,-lfoo 将专门搜索名为 libfoo.alibfoo.so 的文件(根据需要)。而不是 libfoo.so.0。因此,请适当地使用 ln 命令重命名库的名称。
引用 gcc 手册的说法:
-l library
   ...
   It makes a difference where in the command you 
   write this option; the linker searches and processes 
   libraries and object files in the order they are 
   specified.  Thus, foo.o -lz bar.o searches library z 
   after file foo.o but before bar.o.  If bar.o refers 
   to functions in z, those functions may not be loaded.

如果将文件直接添加到g++的命令行中,应该可以正常工作,除非您将其放在bar.cpp之前,这会导致链接器因缺少任何所需符号而忽略它,因为尚未需要任何符号。


1
谢谢!在经历了许多死胡同之后,这对我来说是解决方案。我确认-l位置确实非常重要。 :) - Kenny Cason

25

指定库的绝对路径应该可以正常工作:

g++ /my/dir/libfoo.so.0  ...

你确定在添加绝对路径后移除了-lfoo吗?


1
这个解决方案对我也很有效 - 尝试链接到除了发行版开发包之外的Qt5版本。谢谢。 - wump
如何使其适用于@版本符号?最小示例:https://github.com/cirosantilli/cpp-cheat/blob/0b4b1bdeed693d16129b282ce0c8145499bf34f6/shared-library/symbol-version/Makefile#L13 - Ciro Santilli OurBigBook.com

13
作为替代方案,您可以使用环境变量 LIBRARY_PATHCPLUS_INCLUDE_PATH,分别表示库文件和头文件的查找路径(CPATH 同样可以实现此功能),无需指定 -L 和 -I 选项。
编辑: CPATH 使用 -I 来包含头文件,而 CPLUS_INCLUDE_PATH 则使用 -isystem

请问您能否添加一个使用示例? - Hanna Khalil
在编译时的同一控制台会话中,导出库路径命令为:export LIBRARY_PATH=/path/to/lib - Alexandre Hamez
导出LIBRARY_PATH=/path/to/lib - user171780

0

如果你习惯于在Windows中使用DLL并希望在Linux/QT中跳过.so版本号,那么添加CONFIG += plugin将会去除版本号。要使用绝对路径来指定.so,在链接器中指定即可,正如Klatchko先生所提到的。


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