编译代码时动态链接库

编译软件让您在如何运行系统方面拥有很大的灵活性。LD_LIBRARY_PATH 变量以及 -L-l GCC 选项,都是这种灵活性的组成部分。
1 位读者喜欢这篇文章。
women programming

WOCinTech Chat。由 Opensource.com 修改。CC BY-SA 4.0

编译软件是开发人员经常做的事情,在开源领域,一些用户甚至选择自己编译。Linux 播客 Dann Washko 将源代码称为“通用软件包格式”,因为它包含了使应用程序在任何平台上运行所需的所有组件。当然,并非所有源代码都是为所有系统编写的,因此它仅在目标系统的子集中才是“通用”的,但重点是源代码非常灵活。通过开源,您可以决定代码的编译和运行方式。

当您编译代码时,通常会处理多个源文件。开发人员倾向于将不同的类或模块保存在单独的文件中,以便可以单独维护它们,甚至可以被不同的项目使用。但是,当您编译这些文件时,其中许多文件会被编译成一个可执行文件。

这通常通过创建共享库来完成,然后从可执行文件动态链接回这些库。这通过将模块化功能保持在外部来保持可执行文件的小巧,并确保库可以独立于使用它们的应用程序进行更新。

在编译期间定位共享对象

当您使用 GCC 编译时,通常需要您的工作站上安装一个库,以便 GCC 能够找到它。默认情况下,GCC 假定库位于系统库路径中,例如 /lib64/usr/lib64。但是,如果您要链接到您自己的尚未安装的库,或者您需要链接到未安装在标准位置的库,那么您必须帮助 GCC 找到这些文件。

GCC 中有两个选项对于查找库非常重要:

  • -L (大写 L) 向 GCC 的搜索位置添加额外的库路径。
  • -l (小写 l) 设置您要链接的库的名称。

例如,假设您编写了一个名为 libexample.so 的库,并且您想在编译应用程序 demo.c 时使用它。首先,从 demo.c 创建一个目标文件:
 

$ gcc -I ./include -c src/demo.c

-I 选项将目录添加到 GCC 的头文件搜索路径。在本例中,我假设自定义头文件位于名为 include 的本地目录中。-c 选项阻止 GCC 运行链接器,因为此任务仅是创建目标文件。这正是发生的情况:
 

$ ls
demo.o   include/   lib/    src/

现在您可以使用 -L 选项为您的库设置路径,并进行编译:
 

$ gcc -L`pwd`/lib -o myDemo demo.o -lexample

请注意,-L 选项位于 -l 选项之前。这很重要,因为如果在您告诉 GCC 查找非默认库之前,-L 尚未添加到 GCC 的搜索路径中,则 GCC 将不知道在您的自定义位置进行搜索。编译按预期成功,但是当您尝试运行时会出现问题:
 

$ ./myDemo
./myDemo: error while loading shared libraries:
libexample.so: cannot open shared object file:
No such file or directory

使用 ldd 进行故障排除

ldd 实用程序打印共享对象依赖项,它在解决此类问题时非常有用:

$ ldd ./myDemo
        linux-vdso.so.1 (0x00007ffe151df000)
        libexample.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f514b60a000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f514b839000)

您已经知道无法找到 libexample,但是 ldd 输出至少确认了从工作库中期望得到的结果。例如,libc.so.6 已被找到,并且 ldd 显示了它的完整路径。

LD_LIBRARY_PATH

LD_LIBRARY_PATH 环境变量 定义了库的路径。如果您运行的应用程序依赖于未安装到标准目录的库,则可以使用 LD_LIBRARY_PATH 将其添加到系统的库搜索路径中。

有几种设置环境变量的方法,但最灵活的方法是在运行命令之前设置它们。看看设置 LD_LIBRARY_PATH 对分析“损坏”的可执行文件的 ldd 命令有什么作用:
 

$ LD_LIBRARY_PATH=`pwd`/lib ldd ./
   linux-vdso.so.1 (0x00007ffe515bb000)
   libexample.so => /tmp/Demo/lib/libexample.so (0x0000...
   libc.so.6 => /lib64/libc.so.6 (0x00007eff037ee000)
   /lib64/ld-linux-x86-64.so.2 (0x00007eff03a22000)

它同样适用于您的自定义命令:
 

$ LD_LIBRARY_PATH=`pwd`/lib myDemo
hello world!

但是,如果您移动库文件或可执行文件,它会再次中断:
 

$ mv lib/libexample.so ~/.local/lib64
$ LD_LIBRARY_PATH=`pwd`/lib myDemo
./myDemo: error while loading shared libraries...

要修复它,您必须调整 LD_LIBRARY_PATH 以匹配库的新位置:
 

$ LD_LIBRARY_PATH=~/.local/lib64 myDemo
hello world!

何时使用 LD_LIBRARY_PATH

在大多数情况下,LD_LIBRARY_PATH 不是您需要设置的变量。按照设计,库安装在 /usr/lib64 中,因此应用程序自然会在其中搜索所需的库。您可能需要在两种情况下使用 LD_LIBRARY_PATH

  • 您正在编译的软件需要链接到刚刚编译但尚未安装的库。良好的构建系统,例如 AutotoolsCMake,可以帮助处理这种情况。
  • 您正在捆绑旨在从单个目录运行的软件,没有安装脚本或安装脚本将库放置在非标准目录中。一些应用程序的发布版本 Linux 用户可以下载,复制到 /opt 并“无需安装”即可运行。LD_PATH_LIBRARY 变量通过包装器脚本设置,因此用户通常甚至没有意识到它已被设置。

编译软件让您在如何运行系统方面拥有很大的灵活性。LD_LIBRARY_PATH 变量以及 -L-l GCC 选项,都是这种灵活性的组成部分。

接下来阅读什么
标签
Seth Kenlon
Seth Kenlon 是一位 UNIX 极客、自由文化倡导者、独立多媒体艺术家和 D&D 爱好者。他曾在电影和计算行业工作,通常同时进行。

评论已关闭。

© . All rights reserved.