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

时间:2015-04-02 21:15:54

标签: 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
  Referenced from: libA/libA.so
  Reason: image not found
Trace/BPT trap: 5

所以libA在运行时没有找到libB。我找到了这个解决方案来改变Mac OS X上的运行时路径:

install_name_tool -change libB.so @loader_path /../ libB.so libA.so

但我想找到一个适用于OS X和Linux的解决方案。再一次,我试图让最终用户尽可能少地做,所以我不希望他们必须设置环境变量,我必须使用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时,有没有办法告诉libA.so在哪里查找libB.so?我之后可以在OSX上使用install_name_tool来完成它,但是在编译器上没有办法可以在OSX和Linux上运行吗?

3 个答案:

答案 0 :(得分:7)

最重要的是,您的最终可执行文件必须知道您的库所在的位置。您可以通过以下两种方式完成(1)导出包含您图书馆路径的LD_LIBRARY_PATH,或使用rpath (2)可执行文件知道在哪里找到你的库。导出LD_LIBRARY_PATH通常看起来像这样:

LD_LIBRARY_PATH=/path/to/your/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH

我更喜欢使用rpath。使用rpath正常编译库(以下示例为我的扩展测试函数库libetf.so

gcc -fPIC -Wall -W -Werror -Wno-unused -c -o lib_etf.o lib_etf.c
gcc -o libetf.so.1.0 lib_etf.o -shared -Wl,-soname,libetf.so.1

然后编译使用此库的可执行文件,编译为object,然后将对象链接为rpath作为链接器选项。在您的案例中,您将为libA.solibB.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

答案 1 :(得分:3)

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

  

ld: Using -rpath,$ORIGIN inside a shared library (recursive)

例如:

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 - 见:

  

Can I change 'rpath' in an already compiled binary?

[已编辑添加]

这很有趣!在阅读-rpathinstall_name上的各个帖子并玩各种选项之间,我想我已经找到了可行的组合。主要技巧似乎是在libB.so上设置install_name以及在libA上设置@loader_path:

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而言。

答案 2 :(得分:1)

使用-rpath链接器选项。

  

-rpath dir

     

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