我已经编程了一段时间,但我仍然不完全理解链接器的行为方式。
例如,今天我下载并安装了一个库,我想在我的 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。
我很感激任何对此的回答。这个问题有点长,希望不是题外话,但我希望其他人也能从中吸取教训。谢谢
最佳答案
编译和运行是两回事。 :)
1) make 文件包含有关如何构建应用程序的规则。因此,当您编写如下规则时:
-L/usr/local/lib -lxerces-c-3.1
您正在将选项传递给链接器。 -L
选项告诉链接器将附加库(在本例中为“/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
。注意我知道这适用于 GNU/Linux 的 centOS 和 Ubuntu 风格 - 不能权威地谈论其他风格。
关于c++ - makefile 混淆中的链接器库路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34909040/