makefile混淆链接器库路径

makefile混淆链接器库路径

问题描述:

我已经编程了一段时间,但我仍然不完全理解链接器的行为。makefile混淆链接器库路径

例如,今天我下载并安装了一个我想在我的Linux应用程序中使用的库。 (这是Xerces - 用于解析XML文件)。

我创建了一个makefile,并在我的命令中为它指定了.so和.a文件的路径:-L/usr/local/lib并且还告诉它要包含的库的名称:-lxerces-c- 3.1。

我的应用程序编译得很好,但在运行时失败,出现“无法打开共享对象文件libxerces-c-3.1.so”。为什么会这样,当我正确地给它的路径和名称在生成文件?

然后,我将库路径添加到我的.bashrc文件中的LD_LIBRARY_PATH变量,然后它工作。这很好,但如果我现在删除我的makefile中的库的路径,甚至不包括库的名称,它仍然有效。

我很困惑这是怎么回事。如何通过将路径分配给LD_LIBRARY_PATH变量来找到正确的库,并且仅在我这样做时才能正常工作?我已经读过其他地方甚至不使用LD_LIBRARY_PATH。

我很感激任何答案。这个问题有点长,希望不是脱离主题,但我希望别人也可以从中学习。谢谢

+0

我建议从你的makefile文件中添加最小的一组规则来演示这个问题,最多10行。现在,我们仍然在猜测makefile中的内容,以及是否可以提供最佳的改进。祝你好运。 – shellter

+0

看看这个帖子:http://*.com/questions/1904990/what-is-the-difference-between-ld-library-path-and-l-at-link-time,其中类似的问题已经回答 –

+0

'-L'和'LD_LIBRARY_PATH'影响两个非常*不同的(但相关的)操作。编译时链接和运行时链接。 –

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

在你的情况下,看起来像/usr/local/lib是一个部分,但不是另一个。

+0

谢谢你。运行时链接程序如何知道链接tho的库? LD_LIBRARY_PATH只有库目录的路径,并且没有命名实际库 – Engineer999

+0

@ Engineer999,因为可执行文件中所需的实际.so所需的实际名称.so已被记录在可执行文件中。你可以运行'ldd '来查看哪一个。这是您的文件需求,以及运行时链接程序可以在给定环境中找到它们的位置 – SergeyA

+0

当我甚至在编译时没有提供信息时,我的可执行文件如何知道.so,只有头文件包含在内? – Engineer999

-Wl,-rpath=/usr/local/lib编译程序。这样,您将添加在/ usr/local/lib目录到运行程序的库搜索补丁,你会不会需要LD_LIBRARY_PATH
警告:由于现代动态链接程序会考虑rpath为废弃还可以runpath设置(它取代它)通过指定-Wl,-rpath=/usr/local/lib,--enable-new-dtags

+0

rpath已过时,不被使用。一直为looooong时间。 – SergeyA

+0

@SergeyA:你能否详细说明一下? – nsilent22

+0

而不是rpath,应该使用运行路径 - 因为后者可以仍然与LD_LIBRARY_PATH重叠,而前者不能。 – SergeyA

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

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

如果图书馆在标准地点之一(/lib,/usr/lib等)没有问题。但是,如果您在非标准位置链接到库,则通常必须将该非标准位置添加到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版本 - 不能在其他版本上进行权威性的说明。

+0

谢谢。非常有帮助 – Engineer999

+0

这可能是其他地方的问题,但可执行文件如何决定是使用提供的静态库还是共享库? – Engineer999

+1

@ Engineer999应用程序不做任何决定。运行时链接程序的确如此。使用静态链接库(在编译时),根本不会为该库完成共享对象的运行时链接。 –