在Linux系统下,glibc是非常底层的系统库,一旦出了问题将会导致大多数的linux命令无法使用。升级glibc是非常麻烦的事情,升级成功经常会遇到段错误,本篇文章将会简单介绍一下如何安装glibc以及段错误的原因。

编译安装glibc

下载glibc

下载地址 : 中科大gnu镜像

本文以 glibc-2.17 为例进行编写。

编译glibc

1
2
3
4
5
6
7
tar xvf glibc-2.17.tar.bz2 #如果解压错误,请尝试 yum install bzip2 安装。
cd glibc-2.17
mkdir build
cd build
../configure --prefix=/opt/glibc --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
make -j 8
make install

依赖库有 gcc

错误: ld.so.conf: No such file or directory

如果执行 make install 报此问题,那么可以复制 /etc下的ld.so.conf到安装目录。

1
2
cp /etc/ld.so.* /opt/glibc/etc/ -r

使用新的glibc生成程序

使用自带的glibc编译程序

  • 有一个 gettime.c 文件如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <time.h>
#include <stdio.h>

int main(void)
{
struct tm newtime;
time_t ltime;
char buf[50];

ltime=time(&ltime);
localtime_r(&ltime, &newtime);
printf("The date and time is %s", asctime_r(&newtime, buf));
}
  • 编译
1
gcc ./gettime.c -o gettime
  • 运行

修改 LD_LIBRARY_PATH的方式使用新的glibc

1
2
export LD_LIBRARY_PATH=/opt/glibc/lib:$LD_LIBRARY_PATH
./gettime

不能直接使用修改LD_LIBRARY_PATH的方式指向新的glibc库,会导致大部分程序无法运行

通过 rpath 和 dynamic-linker 的方式使用新的glibc

  • 使用老的glibc编译

    1
    gcc gettime.c -o gettime_old
  • 使用新的glibc编译

    1
    gcc gettime.c -o gettime_new -Wl,--rpath=/opt/glibc/lib -Wl,--dynamic-linker=/opt/glibc/lib/ld-linux-x86-64.so.2
  • 运行结果

时间不一致的原因是因为默认的时区不一致导致的

如果还不够清楚,那么可以去修改glibc的源代码

  • 修改 glibc-2.17/time/localtime.c 的 27至 33行
1
2
3
4
5
6
7
struct tm *
__localtime_r (t, tp)
const time_t *t;
struct tm *tp;
{
return __tz_convert (t, 1, tp);
}

修改后

1
2
3
4
5
6
7
8
9
struct tm *
__localtime_r (t, tp)
const time_t *t;
struct tm *tp;
{
struct tm *tw = __tz_convert(t,1,tp);
tw->tm_year=1;
return tw;
}

然后重新编译glibc

1
2
3
cd build
make -j 8
make install

重新编译 gettime.c ,结果如下所示

修改已有程序的glibc链接地址

如果编译好的可执行文件可以修改glibc库,那么就能够达到欺骗程序的时间获取,满足一些特殊场景下的要求。

原理

ld-linux.so.2 的绝对路径在链接时被硬编码到可执行文件中,并且在链接完成后不能轻易更改.

  • 查看 gettime_old 的链接器路径,以及 Runpath路径
1
readelf ./gettime_old -l|grep interpreter

1
readelf ./gettime_old -a|grep rpath

  • 查看 gettime_new 的链接器路径,以及Runpath路径
1
readelf ./gettime_new -l|grep interpreter

1
readelf ./gettime_new -a|grep rpath

总结:

  1. -Wl,--rpathLD_LIBRARY_PATH 不是同一种东西
  2. 决定glibc路径的是 interpreter(dynamic-linker)
  3. glibc中interpreter的路径是硬编码到可执行文件中的

修改 interpreter 与 rpath

patchelf 可以做到修改程序的dynamic-linker,以及rpath(Run path)。

下载

路径为:github下载

下载后直接解压即可

使用

  • 修改 interpreter 与 rpath
1
./patchelf --set-interpreter /opt/glibc/lib/ld-linux-x86-64.so.2 --set-rpath /opt/glibc/lib ./gettime_old 
  • 查看 interpreter 与 rpath

  • 运行

  • 可以看到已经修改成功

参考资料

  1. Multiple glibc libraries on a single host