Contents

在单个主机上多个GLIBC版本

1. 概述

在本教程中,我们将介绍如何在 Linux 机器上拥有多个glibc 库。我们将从讨论拥有多个库的可能性以及随之而来的问题开始。然后,我们将研究在同一台机器上使用多个版本的glibc的几种方法。

2. Linux 上共享库的不同版本

可以在 Linux 机器上安装相同共享库的多个版本。链接器在程序编译期间寻找链接到库的特定版本的软链接。例如,对于glibc,** /usr/lib目录下的libglib-2.0.so链接将指向特定版本的glibc。**

**在 Linux 上,命名共享库的约定是libname.so.major.minor.patch。**约定中的变量有不同的含义:

  • name是共享对象(共享库)文件名
  • major是库的 API 版本——主要升级可能会引入向后不兼容
  • minor是API版本的次要版本,表示新特性
  • patch表示错误的补丁

因此,在某种程度上,我们的机器上可以有多个版本的glibc ,并有一个指向特定版本的**libglib-2.0.so链接。**链接器将在共享对象中查找soname 字段并将其嵌入生成的二进制文件中。soname字段指定目标共享库的文件名。**因此,在运行期间,动态链接器将查找在soname字段中指定的共享库。

出于这个原因,我们将能够在我们的程序中使用不同的glibc版本。**这种方法的一个警告是我们不能在我们的程序中同时使用同一个库的多个版本。**尽管如此,如果绝对需要同一个库的不同版本,我们可以使用静态链接。

3. 为g++指定不同的glibc

glibc不是我们可以在程序中链接的独立文件。它依赖于许多其他模块。其中之一是ld-linux 。** ld工具或链接编辑器是一个辅助实用程序,可将多个目标文件和其他资源组合到一个输出文件中。**因此,我们需要将ld-linux的正确版本与 glibc一起指定为g++

现在,如果我们有不同版本的glibc和适当版本的ld -linux,我们可以使用我们的目标glibc库编译我们的源代码:

$ g++ <main-file> -o <output-name> -Wl,--rpath=/path/glibc -Wl,--dynamic-linker=/path/glibc/ld-linux.so.2

让我们分解一下:

  • 我们使用g++构建文件并使用-o选项指定输出名称
  • -Wl将打开链接glibc库的长警告
  • –rpath选项告诉运行时加载程序在*/path/glibc*目录中查找所需的库
  • –dynamic-linker选项在生成的二进制文件中提供了ld-linux.so.2的正确路径

或者,我们也可以在运行应用程序之前导出 LD_LIBRARY_PATH 以包含glibc目录的路径。但是,程序启动的任何新子进程也应使用此变量中指定的较新的glib 。*否则,如果它们不是用较新的glibc编译的,或者子进程环境没有继承父进程的环境。因此,我们应该坚持使用–rpath*选项并避免使用环境变量。

4. 使用patchelf

当我们有程序的源代码时,这种方法很有效。另一方面,重新链接二进制文件变得有点复杂,因为我们必须做很多手动工作,例如创建 chroot 环境。出于这个原因,我们可以使用patchelf 来节省我们的时间和头痛。

** patchelf是一个实用程序,我们可以使用它轻松修改ELF 可执行文件的rpath动态链接器。ELF 代表可执行和可链接格式。它是 Linux 中二进制文件和库的标准文件格式。这些包括.so.o和没有特定扩展名的可执行文件。

4.1. 安装patchelf

一些发行版预装了patchelf 。但是,如果未安装,我们可以使用包管理器 从我们发行版的官方存储库安装它。

在 Ubuntu 和 Debian 衍生产品上:

# apt install patchelf

对于 RHEL 和 Fedora:

# yum install patchelf

基于 Arch:

# pacman -S patchelf

安装patchelf后,让我们验证一下:

$ patchelf --version
patchelf 0.14.3

4.2. 用法

使用patchelf非常简单。patchelf命令的基本语法是:

$ patchelf [OPERATION] [OPTIONS] <ELF File>

我们将讨论rpath的不同 CRUD 操作。

首先,我们将为我们首选的glibc添加一个rpath到我们的二进制可执行文件:

$ patchelf --add-rpath /path/glibc-older my_prog

同样,我们可以使用*–set-rpath选项更新rpath*。这可能会破坏程序,因此请谨慎使用:

$ patchelf --set-rpath "/path/glibc-older:/path/libsdl:/path/libgl" my_prog

要删除现有的rpath

$ patchelf --remove-rpath /path/glibc-older my_prog

我们还可以使用set -interpreter更新动态链接器

$ patchelf --set-interpreter /path/glibc-older/ld-linux/ld-linux.so.2

现在,假设我们要使用位于/usr/lib目录中的最新现有glibc库。我们可以简单地更新我们的 ELF 文件的soname:**

$ patchelf --set-soname libglib-2.0.so.0.7000.2