glibc 知:构建和测试

文章目录

  • 1. 前言
  • 2. 构建
    • 2.1. 只构建不安装
    • 2.2. 构建并安装
  • 3. 测试
    • 3.1. 正常编译应用,在新glibc下运行
    • 3.2. 基于glibc构建树进行编译应用
    • 3.3. 基于glibc安装位置进行编译应用
    • 3.4. 所需的gdb设置
      • 3.4.1. 线程设置
      • 3.4.2. 环境设置
      • 3.4.3. 调试测试用例
  • 4. 后语

1. 前言

构建和测试glibc的wiki主页为:https://sourceware.org/glibc/wiki/Testing/Builds

本章将介绍glibc的构建和使用……

2. 构建

我们知道一般linux系统中都会存在已有的glibc库,现在我们要构建自己的glibc库,那新构建的glibc会不会破坏已有的glibc呢?如果破坏了将会带来什么危害呢?

这里我们测试glibc构建,然后并不将它安装到系统路径下,在系统上安装新的glibc可是一件非常大胆的事情,弄不好就会把系统环境破坏了,所以我们最好不要这么做。

2.1. 只构建不安装

只构建不安装,可以执行标准的configure和make,例如:

$ mkdir $HOME/src
$ cd $HOME/src
$ git clone git://sourceware.org/git/glibc.git
$ mkdir -p $HOME/build/glibc
$ cd $HOME/build/glibc
$ $HOME/src/glibc/configure --prefix=/usr
$ make

切记:不要执行make install

通过使用前缀/usr来创建glibc,它将加载和使用来自标准位置的所有配置文件。前缀/usr被认为是系统glibc的正确前缀。更高级的用户会注意到前缀实际上是 glibc ABI 的一部分。

最后,如果您想使用一组替代的 Linux 内核头文件进行构建,您将需要使用--with-headers=指向 Linux 头文件和 glibc 在构建期间所需的其他头文件的统一安装,这些头文件可能包含也可能不包含 SELinux头文件(–with-selinux)或 NSS头文件(–enable-nss-crypt)。

测试如下:
glibc 知:构建和测试_第1张图片
错误:

  • bison缺少或版本太老
    作者第一次构建glibc,所以难免会缺少一些工具(glibc的依赖),只需要进行安装相应的工具即可。不同的操作系统安装方法不一样,作者的的系统是fedora,所以可以通过dnf进行安装,比如:sudo dnf install bison
    glibc 知:构建和测试_第2张图片

修复configure中出现的所有错误,直到执行成功。成功后,在构建目录下会生成Makefile,之后执行make进行构建即可,构建时间比较漫长……(读者可以尝试用make -jn来并行构建(n表示任务数))
在这里插入图片描述
构建成功后,如下图所示:
glibc 知:构建和测试_第3张图片
glibc 知:构建和测试_第4张图片

2.2. 构建并安装

上面我们提到最好不要在系统中安装新的glibc,是指将glibc安装到系统默认环境中。如果我们将其安装到用户自定义的临时目录中,就不会破坏系统中的glibc了,本节将简述如何将构建的glibc安装到临时目录。

先做带–prefix=/usr目录的configure,然后make和make install DESTDIR=xxx(其中xxx即为用户指定的临时安装目录,将GNU标准变量DESTDIR设置为某个临时安装目录xxx),例如:

$ DESTDIR=<path to the GLIBC install directory>
$ mkdir $HOME/src
$ cd $HOME/src
$ git clone git://sourceware.org/git/glibc.git
$ mkdir -p $HOME/build/glibc
$ cd $HOME/build/glibc
$ $HOME/src/glibc/configure --prefix=/usr
$ make
$ make install DESTDIR=${DESTDIR}

基于上节的构建结果,进行安装测试,如下所示:
在这里插入图片描述
glibc 知:构建和测试_第5张图片
您现在已经在$DESTDIR 的子目录中安装了新构建的 glibc ,您可以构建针对它运行的应用程序。请记住,构建将引用与/usr的’ --prefix配置路径相关的配置文件。

然后你将需要一组 linux 内核头文件:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$ cd linux
$ make headers_install INSTALL_HDR_PATH="${DESTDIR}/usr"

这里可以从官网下载linux压缩包,以linux-5.13.5为例,测试如下:
glibc 知:构建和测试_第6张图片
在这里插入图片描述
说明:一开始的tmp/usr用于测试用,防止中间出错,直接污染了glibc的安装目录(处理之前可以先将glibc的安装目录打包备份)。

最后,您将需要 gcc 的帮助库来消除(cancellation):

$ cp /lib64/libgcc* "${DESTDIR}/lib64/"

测试如下:
glibc 知:构建和测试_第7张图片
从此,这让您可以在 ${DESTDIR} 中使用系统根目录。

疑问?
系统上的/lib64是指向/usr/lib64的软连接,库文件都在/usr/lib64下。而这里安装的glibc,install/lib64是实文件,里面都是动态库。install/usr/lib64下面有一些静态库,部分动态库软连接到install/lib64下,如下:
glibc 知:构建和测试_第8张图片
这个情况,当基于安装的glibc,动态库路径应该使用install/lib64,而不是Install/usr/lib64吧?

3. 测试

3.1. 正常编译应用,在新glibc下运行

编译您的测试程序,并使用您构建的新加载程序调用它。这也是make check运行测试的方式。注意:如果被测试的可执行文件位于当前目录中,请不要忘记在名称前使用./。

在构建目录中使用testrun.sh,例如:

$ cd $HOME/build/glibc
$ ./testrun.sh /path/to/test/application

或者像这样手动执行此操作:

GLIBC=<path to the GLIBC build directory>

GCONV_PATH=${GLIBC}/iconvdata LC_ALL=C     \
${GLIBC}/elf/ld.so.1 --library-path \
${GLIBC}:\
${GLIBC}/math:\
${GLIBC}/elf:\
${GLIBC}/dlfcn:\
${GLIBC}/nss:\
${GLIBC}/nis:\
${GLIBC}/rt:\
${GLIBC}/resolv:\
${GLIBC}/crypt:\
${GLIBC}/nptl:\
${GLIBC}/dfp \
<executable to test> <arguments>

请注意,这样的编译不使用新的 C 运行时对象,即glibc 提供的crt1.o、crti.o和crtn.o。对这些对象所做的更改需要更复杂的编译,有关详细信息,请参阅后面的说明。使用-Wl,-Map,linker.map 进行编译将准确显示最终链接中使用了哪些对象。

下面以hello.c为例进行测试:

#include 

int main(void)
{
    printf("hello glibc\n");
    return 0;
}

编译,执行如下:
glibc 知:构建和测试_第9张图片
手动执行如下:
glibc 知:构建和测试_第10张图片

3.2. 基于glibc构建树进行编译应用

如果您想轻松调试应用程序但尚未安装 glibc,请使用此方法。

GLIBC=<path to the GLIBC build directory>

gcc \
  -Wl,-rpath=${GLIBC}:\
${GLIBC}/math:\
${GLIBC}/elf:\
${GLIBC}/dlfcn:\
${GLIBC}/nss:\
${GLIBC}/nis:\
${GLIBC}/rt:\
${GLIBC}/resolv:\
${GLIBC}/crypt:\
${GLIBC}/nptl:\
${GLIBC}/dfp \
  -Wl,--dynamic-linker=${GLIBC}/elf/ld.so \
  <other compiler flags> -o <application> <application>.c

请注意,这样的编译不使用glibc 提供的新头文件或 C 运行时对象,即crt1.o、crti.o和crtn.o。对头文件或对象所做的更改需要更复杂的编译,有关详细信息,请参阅后面的说明。使用-Wl,-Map,linker.map 进行编译将准确显示最终链接中使用了哪些对象。

测试如下:
glibc 知:构建和测试_第11张图片
在这里插入图片描述

3.3. 基于glibc安装位置进行编译应用

请注意,安装的 glibc 是一个不完整的 C 运行时。为了完成 C 运行时,您可能需要复制与您正在使用的编译器匹配的其他头文件,因为使用–sysroot会将它们的查找限制为sysroot。

如果您想轻松调试应用程序,请使用此方法。

编译您的测试程序并为 gcc 提供一些额外的选项以使用安装目录,并为链接器提供一些选项以设置共享库搜索路径和动态链接器。最好使用“readelf”验证动态链接器的位置,使用“ldd”验证库搜索路径。(请参阅加载器提示和技巧)

SYSROOT=<path to the GLIBC install directory>
gcc \
  -L${SYSROOT}/usr/lib64 \
  -I${SYSROOT}/include \
  --sysroot=${SYSROOT} \
  -Wl,-rpath=${SYSROOT}/lib64 \
  -Wl,--dynamic-linker=${SYSROOT}/lib64/ld-2.18.90.so \
  <other compiler flags> -o <application> <application>.c

构建的应用程序现在将始终使用您编译它的路径中的动态加载器和库。

如果您的静态链接器缺少 sysroot 支持,您可以试试这个:

  • 编辑 ${SYSROOT}/usr/lib64/libpthread.so 以指向您的 sysroot libpthread.so.1。
  • 编辑 ${SYSROOT}/usr/lib64/libc.so 以指向您的 sysroot libc.so.6。
  • 然后在没有 --sysroot 的情况下构建。
SYSROOT=<path to the GLIBC install directory>
gcc \
  -L${SYSROOT}/usr/lib64 \
  -I${SYSROOT}/include \
  -Wl,-rpath=${SYSROOT}/lib64 \
  -Wl,--dynamic-linker=${SYSROOT}/lib64/ld-2.18.90.so \
  <other compiler flags> -o <application> <application>.c

您需要在 32 位系统或使用lib的64 位系统(如 Ubuntu)上使用lib。
您需要针对 glibc 的版本调整ld-2.18.90.so。

测试如下:
glibc 知:构建和测试_第12张图片
上面使用–sysroot报引用未定义,下面去掉–sysroot测试如下:
glibc 知:构建和测试_第13张图片
在“构建并安装”一节,我们有个疑问,就是我们构建的glibc在安装后,库文件安装到install/lib64中了,这里面却使用install/usr/lib64,应该是有问题的,而且install下根本就没有include文件,应该是install/usr/include,所以我们这里稍微修改一下,测试如下:
glibc 知:构建和测试_第14张图片
所以,改为下面的方式才更合实际:

SYSROOT=<path to the GLIBC install directory>
gcc \
  -L${SYSROOT}/lib64 \
  -I${SYSROOT}/include \
  -I${SYSROOT}/user/include \
  --sysroot=${SYSROOT} \
  -Wl,-rpath=${SYSROOT}/lib64 \
  -Wl,--dynamic-linker=${SYSROOT}/lib64/ld-xxx.so \
  <other compiler flags> -o <application> <application>.c

3.4. 所需的gdb设置

如果程序是多线程的,或者如果要将环境变量传递给程序,则使用新的 glibc 构建调试程序需要几个额外的步骤。glibc 构建系统为此目的提供了一个辅助脚本,因此,在运行make和make check 之后:

在构建目录中使用debugglibc.sh例如

$ cd $HOME/build/glibc
$ ./debugglibc.sh /path/to/test/application

测试如下:
glibc 知:构建和测试_第15张图片
或者使用以下小节中的说明手动执行这些步骤:

3.4.1. 线程设置

需要采取一个特殊步骤来调试使用带有 gdb 的新 glibc 构建的线程应用程序。必须显式设置线程数据库库。thread db 库允许调试器检查各种内部库状态(包括线程),并且是线程调试所必需的。调试器不能轻易猜出线程数据库库的正确版本和位置。如果您安装了 glibc,调试器通常会发现libthread_db.so没有任何问题,但是如果您没有安装 glibc,那么您在尝试使用系统库调试线程代码时肯定会遇到问题。因此,确切地指定在何处搜索线程数据库库始终是最安全的。

调试前在 gdb 中执行:

set auto-load safe-path <path to libthread_db.so.1 e.g. /build/glibc/nptl_db or /install/lib64>:$debugdir:$datadir/auto-load
set libthread-db-search-path <path to libthread_db.so.1 e.g. /build/glibc/nptl_db or /install/lib64/>

3.4.2. 环境设置

将环境变量传递给应用程序进行调试并不总是像您预期的那样直接。某些环境变量可能会对 gdb 用于启动应用程序的 shell 产生负面影响。在这些情况下,您应该使用exec-wrapper来确保只有被调试的应用程序设置了预期的环境变量。

调试前在 gdb 中执行:

set exec-wrapper env 'LD_PRELOAD=libmalloc-extras.so'

3.4.3. 调试测试用例

在 glibc 中运行的测试用例(即通过make check)通常需要以特定于 glibc 的方式运行,有些需要在测试容器内运行。为了调试这些,glibc 提供了一个特殊的 makefile 目标:

WAIT_FOR_DEBUGGER=1 make test t=nss/tst-nss-test2

当测试以这种方式运行时,它会在运行测试主体之前暂停,并打印通过 gdb 附加到它的指令。
glibc 知:构建和测试_第16张图片
glibc 知:构建和测试_第17张图片
请注意,省略WAIT_FOR_DEBUGGER=1部分将简单地重新运行单个指定的测试.
glibc 知:构建和测试_第18张图片
此处的subdir为nss,实际上subdir并一定是测试用例文件所在的目录,需要以相应目录下Makefile中规定的subdir为准。
关于glibc测试,可以参考其测试套件了解更多。

4. 后语

构建和测试中,还剩下“使用全新的文件构建”一节没有介绍,该节稍微复杂,感兴趣的读者可以自行解锁!

你可能感兴趣的:(libc,glibc)