【问题标题】:Linker library path in makefile confusionmakefile 混淆中的链接器库路径
【发布时间】:2016-01-20 19:40:35
【问题描述】:

我已经编程了一段时间,但我仍然不完全了解链接器的行为方式。

例如,今天我下载并安装了一个我想在我的 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。

感谢您对此的任何回答。这个问题有点长,希望不会离题,但我希望其他人也可以从中学习。谢谢

【问题讨论】:

  • 我建议从你的 makefile 中添加最少的规则集来演示这个问题,最多 10 行。照原样,我们只能推测您的 makefile 中的确切内容以及是否可以提供最佳改进。祝你好运。
  • 查看这篇文章:stackoverflow.com/questions/1904990/…,其中已经回答了类似的问题
  • -LLD_LIBRARY_PATH 影响两个非常不同(但相关)的操作。编译时链接和运行时链接。

标签: c++ c linux makefile linker


【解决方案1】:

编译和运行是不同的东西。 :)

1) 生成文件包含有关如何构建应用程序的规则。因此,当您编写如下规则时:

    -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 风格——不能权威地谈论其他风格。

【讨论】:

  • 这可能是其他地方的问题,但是可执行文件如何决定是使用提供的静态库还是共享库?
  • @Engineer999 应用程序不决定任何事情。运行时链接器会。使用静态链接库(在编译时),根本不需要为该库完成共享对象的运行时链接。
【解决方案2】:

使用-Wl,-rpath=/usr/local/lib 编译您的程序。这样您就可以将 /usr/local/lib 添加到程序的运行时库搜索补丁中,并且不需要 LD_LIBRARY_PATH
警告:因为现代动态链接器认为 rpath 已弃用您还可以通过指定-Wl,-rpath=/usr/local/lib,--enable-new-dtags 来设置runpath(取代它)

【讨论】:

  • rpath 已过时,不能使用。已经很久了。
  • @SergeyA:你能详细说明一下吗?
  • 应该使用 runpath 而不是 rpath - 因为后者仍然可以被 LD_LIBRARY_PATH 覆盖,而前者不能。
  • @SergeyA:好吧,我的man ld 没有提到运行路径,所以它不会被淘汰很长时间,因为我的系统还不算太旧。
  • @lima.sierra,我当然可以。这是来自 man ld.so 的复制粘贴:(ELF only) Using the directories specified in the DT_RPATH dynamic section attribute of the binary if present and DT_RUNPATH attribute does not exist. Use of DT_RPATH is deprecated.
【解决方案3】:

这里没有什么神秘之处。链接器的默认库路径(您调用它来生成可执行文件,例如,ld)和运行时链接器(负责在您执行程序时加载共享库的路径,例如,ld.so)是不同的。运行时链接器使用 LD_LIBRARY_PATH,而链接器使用构建 ld 时配置的任何内容。

在您的情况下,/usr/local/lib 似乎是其中的一部分,而不是另一个。

【讨论】:

  • 谢谢。运行时链接器如何知道要链接哪个库? LD_LIBRARY_PATH 只有库目录的路径,并没有命名实际的库
  • @Engineer999,因为您的可执行文件所需的实际 .so 的名称已经记录在可执行文件中。您可以运行ldd <your executable> 来查看您的文件需要哪个 .so,以及运行时链接器可以在给定环境中的何处找到它们
  • 我的可执行文件如何知道 .so 时我什至在编译时都不给它信息,只包含头文件?
  • @Engineer999,因为你还有 -l 规则。
  • 但即使我从 makefile 中删除它,它仍然找到它
【解决方案4】:

如果您使用静态链接,您所要做的就是在编译/链接时告诉链接器您的库在哪里。库(或尽可能多的库)被复制到您的可执行文件中,并且您的可执行文件是独立的。

但是由于各种原因,现在我们一般使用动态链接,而不是静态链接。使用动态链接,您必须在编译/链接时告诉链接器在哪里可以找到库,并且动态链接器 (ld.so) 必须能够在运行时找到库。

如果库位于标准位置之一(/lib/usr/lib 等),则没有问题。但是,如果您链​​接到一个非标准位置的库,一般来说,您必须将该非标准位置添加到您的 LD_LIBRARY_PATH,以便运行时链接器始终能够找到它。

【讨论】:

    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 2017-03-15
    • 2015-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多