2.3. 将库与 GCC 搭配使用

了解在代码中使用库。

2.3.1. 库命名规则

用于库的一个特殊文件名称规则:例如称为 foo 的库一般会有文件 libfoo.solibfoo.a。通过 GCC 的链接输入选项(而非输出选项)自动理解此约定:

  • 当对库进行链接时,库可以通过它的名称 foo 以及参数 -l 指定(-lfoo):

    $ gcc ... -lfoo ...
  • 在创建库时,必须指定完整文件名 libfoo.solibfoo.a

2.3.2. 静态和动态链接

开发人员可以选择使用完整编译语言构建应用时使用静态或动态链接。了解静态和动态链接之间的区别很重要,特别是在 Red Hat Enterprise Linux 上使用 C 和 C++ 语言的上下文中。总之,红帽不建议在 Red Hat Enterprise Linux 的应用程序中使用静态链接。

静态和动态链接的比较

静态链接使库成为生成的可执行文件的一部分。动态连接会将这些库保留为单独的文件。

可使用多种方式比较动态和静态链接:

资源使用

静态连接会导致包含更多代码的大型可执行文件。这个来自库的额外代码不能在系统上的多个程序间共享,在运行时增加文件系统的使用情况和内存使用情况。运行相同静态链接程序的多个进程仍将共享代码。

另一方面,静态应用程序需要较少的运行时重新定位,从而缩短启动时间,并需要较少的专用 RSS(resident set size)内存。因为使用独立于位置的代码(PIC)带来的开销,静态链路生成的代码比动态连接效率更高。

安全性
可动态链接库(提供 ABI 兼容性),无需更改可执行文件,具体取决于这些库。这对于由红帽提供的、作为 Red Hat Enterprise Linux 的一部分的库尤为重要,因为红帽会提供安全更新。强烈建议不要对任何此类库进行静态链接。
兼容性

静态连接似乎提供独立于操作系统提供的库版本的可执行文件。但是,大多数库依赖于其他库。使用静态链路时,这个依赖关系会变得不灵活,因此向前和向后兼容都会丢失。静态连接保证仅在构建可执行文件的系统中工作。

警告

从 GNU C 库链接静态库(glibc)的应用程序仍然需要在系统中存在 glibc 作为动态库。另外,在应用程序运行时提供 glibc 的动态库变体必须是与应用程序链接时所用的相同版本。因此,静态连接可以保证仅在构建可执行文件的系统中工作。

支持覆盖范围
红帽提供的大多数静态库都在 CodeReady Linux Builder 频道中,它们不受红帽的支持。
功能

有些库,例如 GNU C 库(glibc)在静态链接时提供的功能会减少。

例如,当静态链接时,glibc 不支持线程以及在同一个程序中的任何形式的 dlopen() 调用。

因为存在这里列出的缺陷,应尽可能避免使用静态链接,特别是整个应用程序以及 glibclibstdc++ 库。

静态链接的情况

在某些情况下,静态连接可能是合理的选择,例如:

  • 使用没有为动态链接启用的库。
  • 在空 chroot 环境或容器中运行代码需要完全静态链接。但是,红帽不支持使用 glibc-static 软件包的静态链接。

2.3.4. 将库与 GCC 搭配使用

库是可以在您的程序中重复使用的代码软件包。C 或 C++ 库由两个部分组成:

  • 库代码
  • 标头文件

使用库的编译代码

标头文件描述了库的接口:库中可用的函数和变量。编译代码需要来自标头文件的信息。

通常,库的标头文件将放在与应用程序代码不同的目录中。要告知 GCC 哪个标头文件,请使用 -I 选项:

$ gcc ... -Iinclude_path ...

使用到标头文件目录的实际路径替换 include_path

可以多次使用 -I 选项来添加包含标头文件的多个目录。查找标头文件时,会按照它们在 -I 选项中的顺序搜索这些目录。

使用库的链接代码

在连接可执行文件时,应用程序的对象代码和库的二进制代码都必须可用。静态和动态库的代码有不同的形式:

  • 静态库作为存档文件提供。它们包含一组对象文件。归档文件名为 .a
  • 动态库作为共享对象提供。它们是可执行文件的一种形式。共享对象具有文件名扩展名 .so

要告知 GCC 一个库的归档或共享对象文件的位置,请使用 -L 选项:

$ gcc ... -Llibrary_path -lfoo ...

使用库目录的实际路径替换 library_path

L 选项可以多次使用来添加多个目录。查找库时,会按照其 -L 选项顺序搜索这些目录。

选项顺序很重要:GCC 无法链接到库 foo,除非它知道这个库的目录。因此,在使用链接库的 -l 选项前,使用 -L 选项指定库目录。

在一个步骤中使用库编译和链接代码

当允许代码在一个 gcc 命令中编译和链接时,请一次性使用上述两个情况的选项。

其他资源

2.3.5. 在 GCC 中使用静态库

静态库作为包含对象文件的存档提供。链接后,它们将成为生成的可执行文件的一部分。

注意

出于安全原因,红帽不建议使用静态链接。请参阅 第 2.3.2 节 “静态和动态链接”。仅在必要时使用静态链接,特别是红帽提供的库。

先决条件

  • 必须在您的系统中安装 GCC。
  • 您必须了解静态和动态链接。
  • 您有一组源或对象文件组成一个有效程序,需要一些静态库 foo 且没有其他库。
  • foo 库作为一个文件 libfoo.a 提供,且没有为动态链接提供文件 libfoo.so
注意

作为 Red Hat Enterprise Linux 一部分的大多数库都只支持动态链接。以下步骤只适用于 没有为 动态链接启用的库。

步骤

要从源和目标文件链接程序,添加一个静态链接的库 foo,它会是一个 libfoo.a 文件:

  1. 更改到包含您的代码的目录。
  2. 使用 foo 库的标头编译程序源文件:

    $ gcc ... -Iheader_path -c ...

    使用包含 foo 库的标头文件的目录路径替换 header_path

  3. 将程序与 foo 库链接:

    $ gcc ... -Llibrary_path -lfoo ...

    使用包含文件 libfoo.a 的目录替换 library_path

  4. 要在以后运行该程序,只需:

    $ ./program
警告

与静态链路相关的 -static GCC 选项,用于禁止所有动态链接。应该使用 -Wl,-Bstatic-Wl,-Bdynamic 选项来更精确地控制链接器行为。请参阅 第 2.3.7 节 “在 GCC 中使用静态和动态库”

2.3.6. 使用 GCC 的动态库

动态库可作为独立的可执行文件提供,在链接时间和运行时需要。它们独立于应用程序的可执行文件。

先决条件

  • 必须在系统中安装 GCC。
  • 一组源或对象文件组成一个有效程序,需要一些动态库 foo,没有其他库。
  • foo 库必须作为一个文件 libfoo.so 可用。

将程序与动态库连接

要根据动态库 foo 链接程序:

$ gcc ... -Llibrary_path -lfoo ...

当某个程序与动态库相关联时,生成的程序必须始终加载库。查找库有两个选项:

  • 使用存储在可执行文件本身中的 rpath
  • 在运行时使用 LD_LIBRARY_PATH 变量

使用存储在 Executable 文件中的 rpath

rpath 是一个特殊值,保存在可执行文件时作为可执行文件的一部分保存。之后,当从可执行文件加载程序时,运行时链接器将使用 rpath 值来定位库文件。

当使用 GCC 链接时,将路径 library_path 存储为 rpath

$ gcc ... -Llibrary_path -lfoo -Wl,-rpath=library_path ...

路径 library_path 必须指向包含文件 libfoo.so 的目录。

重要

不要在 -Wl,-rpath= 选项的逗号后添加一个空格。

稍后运行程序:

$ ./program

使用 LD_LIBRARY_PATH 环境变量

如果在程序的可执行文件中找不到 rpath,则运行时链接器将使用 LD_LIBRARY_PATH 环境变量。必须为每个程序更改此变量的值。这个值应该代表共享库对象所在的路径。

要在没有设置 rpath 的情况下运行安装程序,库存在于路径 library_path 中:

$ export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH
$ ./program

不使用 rpath 值可提供一定的灵活性,但每次程序运行时都需要设置 LD_LIBRARY_PATH 变量。

将库放在默认目录中

运行时链路器配置将多个目录指定为动态库文件的默认位置。要使用这个默认行为,请将您的库复制到适当的目录中。

动态链路程序行为的完整描述超出了本文档的范围。如需更多信息,请参阅以下资源:

  • 动态链路器的 Linux 手册页:

    $ man ld.so
  • /etc/ld.so.conf 配置文件的内容:

    $ cat /etc/ld.so.conf
  • 报告由动态链路器识别的库而无需额外的配置,其中包括目录:

    $ ldconfig -v

2.3.7. 在 GCC 中使用静态和动态库

有时需要静态和一些库来动态链接一些库。这种情况给企业带来一些挑战。

先决条件

  • 了解静态和动态链接

简介

GCC 同时识别动态和静态库。当遇到 -lfoo 选项时,gcc 将首先尝试查找包含 foo 库的动态链接版本的共享对象( .so 文件),然后查找包含该库静态版本的存档文件(.a)。因此,以下情况可能会导致此搜索的结果:

  • 仅找到共享对象,并动态 gcc 链接它。
  • 只找到归档,并以静态方式使用 gcc 链接。
  • 可以找到共享对象和存档,默认情况下,gcc 选择对共享对象的动态链接。
  • 未找到共享对象或存档,并且链接失败。

由于这些规则,选择要链接库的静态或动态版本的最佳方法是,只有 gcc 找到的版本。在指定 -Lpath 选项时,可以通过使用或不使用包含库版本的目录来进行一定的控制。

另外,因为动态链接是默认设置的,所以当存在这两个版本的库时,需要明确指定链接的唯一情形是静态链接。有两种可能的解决方法:

  • 通过文件路径而不是 -l 选项指定静态库
  • 使用 -Wl 选项将选项传递给 linker

通过文件指定静态库

通常,gcc 指示针对带有 -lfoo 选项的 foo 库进行链接。但是,可以指定包含库的文件 libfoo.a 的完整路径:

$ gcc ... path/to/libfoo.a ...

通过文件扩展名 .agcc 将了解到这是与程序链接的库。但是,指定库文件的完整路径是一个不太灵活的方法。

使用 -Wl 选项

gcc 选项 -Wl 是用于将选项传递给底层链路器的特殊选项。这个选项的语法与其他 gcc 选项不同。-Wl 选项后跟一个以逗号分隔的 linker 选项列表,而其他 gcc 选项则需要以空格分隔的选项列表。

gcc 使用的 ld linker 提供了选项 -Bstatic-Bdynamic 以指定此选项后的库是否应静态链接或动态链接。将 -Bstatic 和库传递给 linker 后,必须手动恢复默认的动态链接行为,才能将以下库与 -Bdynamic 选项动态链接。

要链接程序,静态链接库 first(libfirst.a),动态链接库 second(libsecond.so):

$ gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
注意

gcc 可以配置为使用默认 ld 以外的链接器。

其他资源