我已经编程了一段时间,但我仍然不完全理解链接器的行为方式。
例如,今天我下载并安装了一个我想在Linux应用程序中使用的库。 (它是Xerces - 用于解析XML文件)。
我创建了一个makefile并在其命令中为它提供了.so和.a文件的路径:-L / usr / local / lib并且还告诉它要包含的库的名称:-lxerces-c-3.1 。
我的应用程序编译正常但在运行时失败“无法打开共享对象文件libxerces-c-3.1.so”。当我在makefile中正确地给出路径和名称时,为什么会出现这种情况呢?
然后我将库路径添加到我的.bashrc文件中的LD_LIBRARY_PATH变量,然后它就可以了。这没关系,但是如果我现在在makefile中删除了库的路径,甚至没有包含库的名称,它仍然可以工作。
我很困惑这里发生了什么。如何通过分配LD_LIBRARY_PATH变量的路径仍然可以找到正确的库,并且只有在我这样做的情况下才能工作?我在别处读过甚至不使用LD_LIBRARY_PATH。
我很感激任何答案。问题有点长,希望不是偏离主题,但我希望其他人也可以从中学习。感谢
答案 0 :(得分:4)
编译和运行是不同的事情。 :)
1)make文件包含有关如何构建应用程序的规则。所以当你写一条规则时:
-L/usr/local/lib -lxerces-c-3.1
您正在将选项传递给链接器。 -L
选项告诉链接器将其他库(在本例中为&us; / usr / local / lib')添加到链接器的搜索路径中。 -l
选项命名应链接的库。
2)当你去运行可执行文件时,加载器需要找到所有必需的库。例如,在Linux系统上,您可以使用ldd命令查看使用的共享库。例如,在我的系统上:
ldd FEParser
linux-vdso.so.1 => (0x00007ffcdc7c9000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f835b143000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f835ae3d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f835ac27000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f835a862000)
/lib64/ld-linux-x86-64.so.2 (0x00007f835b447000)
通过此,您可以看到链接到' =>'左侧的库的名称。令牌,以及右侧库的路径。如果找不到库,它将显示为缺失。
现在,在您的情况下,由于提供了要使用的路径和库名称,您可以成功编译和链接您的程序。由于加载程序无法在运行时找到该库,因此无法运行该程序。
这里有几个选项:
1)您可以将库移动到加载器搜索路径中的目录中。
2)您可以修改LD_LIBRARY_PATH
,这会将其他目录添加到加载器搜索路径中。此外,LD_LIBRARY_PATH
中指定的目录将传递给链接器(它将在所有-L
标志之后附加)。你需要小心这一点,就好像你全局设置它(比如在.bashrc
中),然后它将影响你所做的所有编辑。您可能想要也可能不想要这种行为。
3)正如其他人指定的那样你可以使用-Wl,rpath=....
,但它已被弃用,我很少使用它。
4)如果你需要在异常位置安装库,你可以在/etc/ld.so.conf.d
下添加一个创建文件,例如(文件是yaml.conf):
# yaml default configuration
/opt/yaml/lib
在系统引导时,加载程序读取此目录中的所有文件,并将路径附加到加载程序路径。如果对此目录进行了修改,则可以使用ldconfig
命令使加载程序重新处理/etc/ld.so.con.d
。注:我知道这适用于GNOS / Linux的centOS和Ubuntu风格 - 无法在其他风格上发表权威。
答案 1 :(得分:2)
使用-Wl,-rpath=/usr/local/lib
编译您的程序。这样你就可以将/ usr / local / lib添加到程序的运行库搜索补丁中,你不需要LD_LIBRARY_PATH
警告:由于现代动态链接器将rpath
视为已弃用,因此您还可以通过指定runpath
来设置-Wl,-rpath=/usr/local/lib,--enable-new-dtags
(取代它)
答案 2 :(得分:1)
这里没有神秘感。链接器的默认库路径(您调用以生成可执行文件的文件,例如ld
)和运行时链接程序(负责在执行程序时加载共享库的路径,例如,ld.so
) 是不同的。运行时链接程序使用LD_LIBRARY_PATH,而链接器使用在构建ld时配置的任何内容。
在您的情况下,/usr/local/lib
看起来像是一部分,而不是另一部分。
答案 3 :(得分:0)
如果您正在使用静态链接,那么您所要做的就是告诉链接器您的库在编译/链接时的位置。该库(或必要时的大部分内容)被复制到您的可执行文件中,您的可执行文件是独立的。
但由于各种原因,这些天我们通常使用动态链接,而不是静态链接。使用动态链接,您必须告诉链接器在编译/链接时在哪里找到库,和动态链接器(ld.so
)必须能够在运行时找到库
如果图书馆位于其中一个标准位置(/lib
,/usr/lib
等),则没有问题。但是,如果您链接到非标准位置的库,通常,您必须将该非标准位置添加到LD_LIBRARY_PATH,以便运行时链接程序始终能够找到它。