Linux 如何设置.so库搜索其他.so库的路径?

Linux 如何设置.so库搜索其他.so库的路径?,linux,macos,shared-libraries,dynamic-linking,cc,Linux,Macos,Shared Libraries,Dynamic Linking,Cc,我有一个libA.so,这取决于libB.so,它位于../libB/(来自libA.c)。我试图以一种不需要设置任何环境变量的方式编译东西。我有: cc -std=c99 -c -fPIC -I../libB/ -Wall libA.c cc -std=c99 -shared libA.o -L../libB -lB -o libA.so 这很好。当我运行一个用dlopen加载libA的程序时,我得到: dyld: Library not loaded: libB.so

我有一个libA.so,这取决于libB.so,它位于../libB/(来自libA.c)。我试图以一种不需要设置任何环境变量的方式编译东西。我有:

    cc -std=c99 -c -fPIC -I../libB/ -Wall libA.c
    cc -std=c99 -shared libA.o -L../libB -lB -o libA.so
这很好。当我运行一个用dlopen加载libA的程序时,我得到:

dyld: Library not loaded: libB.so
  Referenced from: libA/libA.so
  Reason: image not found
Trace/BPT trap: 5
所以libA在运行时找不到libB。我发现这个解决方案可以更改Mac OS X上的运行时路径:

安装_name_工具-更改libB.so@loader_path/。/libB.so libA.so

但是我想找到一个可以同时在OSX和Linux上工作的解决方案。再一次,我试图让最终用户尽可能少地做一些事情,这样我就不希望他们必须设置环境变量,而且我必须使用cc(对我来说,cc是Apple LLVM版本4.2(clang-425.0.27)(基于LLVM 3.2svn),我希望它也能在Linux上工作,因此推测cc=gcc存在)

编辑我的问题可能比我意识到的更复杂。我正在用C语言创建这个动态库,但试图从python中使用它。我可以在python中使用libB.so(它没有依赖项),这没问题,当我从python中加载libA.so时,它会找到它(请参见上面的错误),只是当时libA.so意识到它不知道在哪里可以找到libB.so。如果我正确理解了下面的答案,那么解决方案取决于在编译可执行文件时设置链接器路径,在我的例子中是python


没有办法告诉libA.so在哪里查找libB.so,那么当我编译它时?我可以在OSX上使用install_name_tool完成这项工作,但是编译器没有一种方法可以同时在OSX和linux上工作吗?

使用-rpath链接器选项

-rpath目录

将目录添加到运行库搜索路径。这是在以下情况下使用的 将ELF可执行文件与共享对象链接。All-rpath参数 连接并传递给运行时链接器,运行时链接器使用它们 在运行时定位共享对象。-rpath选项也用于以下情况: 明确定位共享对象所需的共享对象 包括在链接中;请参见-rpath链接选项的说明。 如果链接ELF可执行文件时未使用-rpath,则 如果定义了环境变量LD_RUN_PATH,则将使用它。 -rpath选项也可以在SunOS上使用。默认情况下,在SunOS上 链接器将使用它提供的所有-L选项形成一个运行时搜索补丁 是的。如果使用-rpath选项,则运行时搜索路径将为 以独占方式使用-rpath选项形成,忽略-L选项。 这在使用gcc时非常有用,gcc添加了许多-L选项 可能位于NFS安装的文件系统上。与其他ELF兼容 链接器,如果-R选项后跟目录名,而不是 一个文件名,它被视为-rpath选项


底线是,您的最终可执行文件必须知道库所在的位置。您可以通过两种方法来完成这一任务:(1)导出包含库路径的
LD_LIBRARY_路径
,或者使用
rpath
导出
(2),以便可执行文件知道在哪里可以找到库。导出
LD\u LIBRARY\u路径
通常如下所示:

LD_LIBRARY_PATH=/path/to/your/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH
我更喜欢使用
rpath
。使用
rpath
正常编译库(下面是我的扩展测试函数库
libetf.so

然后,要使用此库编译可执行文件,请编译到对象,然后使用
rpath
作为链接器选项链接该对象。在本例中,您将为
libA.so
libB.so
提供路径。构建我的
testso
可执行文件:

gcc -O2 -Wall -W -Wno-unused -c -o testetf.o testetf.c
gcc -o testso testetf.o -L/home/david/dev/src-c/lib/etf -Wl,-rpath=/home/david/dev/src-c/lib/etf -letf
使用
ldd
确认可执行文件已正确定位您的库:

$ ldd testso
        linux-vdso.so.1 (0x00007fffd79fe000)
        libetf.so.1 => /home/david/dev/src-c/lib/etf/libetf.so.1 (0x00007f4d1ef23000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f4d1eb75000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4d1f126000)

注意:
libetf.so.1
指向
/home/david/dev/src-c/lib/etf/libetf.so.1

虽然您自己并不构建可执行文件,但方法几乎相同,只是将rpath设置为libA.so而不是可执行二进制文件。设置rpath时,请使用特殊的$ORIGIN字符串,以便libB.so的位置始终相对于libA.so

例如:

cc -std=c99 -c -fPIC -I../libB/ -Wall libA.c
cc -std=c99 -shared libA.o -L../libB -lB -o libA.so -Wl,-rpath,\$ORIGIN/../libB
请注意,$ORIGIN不是环境变量,它由运行时加载程序直接解释,因此在传递到链接器时会转义,如上所示

另一方面,如果您希望采用与在OS X上执行的操作类似的方法,则可以在使用chrpath命令编译.so文件后更改该文件中的rpath-请参阅:

[经编辑添加] 这很有趣!在阅读各种帖子和使用各种选项之间,我想我已经找到了一个有效的组合。主要技巧似乎是在libB上设置install_名称。因此,在libA上设置@loader_路径:

cc -shared -o libA.so libA.o -L../libB -lB -Wl,-rpath,@loader_path
cc -shared -o libB.so libB.o -install_name @loader_path/../libB/libB.so

现在libB.so始终位于../libB/相对于libA.so所在的位置

谢谢你的帮助。当我管理cc时,我看不到rpath的任何选项。不幸的是,我不得不使用cc。有什么想法吗?当你说“抄送”时,你需要更具体一些。“cc”依赖于系统。在Linux上,它通常只是指向“gcc”的链接。在任何情况下,“rpath”都是链接器选项,而不是编译器选项。所以请仔细阅读:哥们儿们,情况还不清楚。有了gcc(可能还有你的cc),选项可以通过-Wl选项传递给链接器。谢谢,我需要所有我能得到的帮助和明确性:)cc为我是苹果LLVM版本4.2(clang-425.0.27)(基于LLVM 3.2svn)。我尝试了cc-shared libA.o-Wl,-rpath../libB-L../libB-lB-o libA.so。这很好,但是当我尝试在另一个程序中加载库时,我得到了与上面相同的错误。
rpath
在编译期间使用:
gcc-Wall-Wextra-L/path/to/your/lib-Wl,-rpath=/path/to/your/lib-o progname somefile.c
选项表示将以下选项传递给链接器(直到下一个空格出现为止)
cc -shared -o libA.so libA.o -L../libB -lB -Wl,-rpath,@loader_path
cc -shared -o libB.so libB.o -install_name @loader_path/../libB/libB.so