libuuid.so 崩溃问题

来源:互联网 发布:linux dhcp安装包 编辑:程序博客网 时间:2024/05/18 10:54

前段时间使用的Cetos 6.3有程序崩溃在了uuid_generate () from /lib64/libuuid.so.1,现象很是诡异,libuuid是Centos util-linux工具包中自带的一个系统库,怎么可能在这里出问题。首先怀疑是上层应用不正确的使用libuuid库导致的问题,对应用代码进行review和debug也没有发现问题。最为奇怪的是,这个问题在使用官方ISO镜像安装的Centos 6.5上也会出现,但是在我们一直使用的另外的基于Centos 6.5系统做的系统镜像上不会有问题。排查许久,终于找到了根本原因,这里记录一下。

1 首先重新编译libuuid添加调试信息

    下载到的util-linux-ng-2.17.2-12.24.el6.src.rpm安装后会在rpmbuild/SPECS下生成一个util-linux-ng.spec文件,里面有一行:

export CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $RPM_OPT_FLAGS"

RPM_OPT_FLAGS 是控制编译选项,编译器-g3选项就要在这里加入。man rpmbuild查看手册,得到下面的信息:

rpmrc Configuration
       /usr/lib/rpm/rpmrc
       /usr/lib/rpm/redhat/rpmrc
       /etc/rpmrc
       ~/.rpmrc
The first three files are read in the order listed, such that if a given rmprc entry 
is present in each file, the value of the entry read last is the one used by RPM. 
This means, for example, that an entry in .rpmrc in the user's login directory will 
always override the same entry in /etc/rpmrc. Likewise, an entry in /etc/rpmrc 
will always override the same entry in /usr/lib/rpmrc.

可以通过上面4个文件控制RPM_OPT_FLAGS。这里着重谈一下~/.rpmrc的格式:
~/.rpmrc
The optflags entry looks like this:
optflags: <architecture> <value>
For example, assume the following optflags entries were placed in an rpmrc file:
optflags: i386 -O2 -m486 -fno-strength-reduce
optflags: sparc -O2
   通过rpmbuid命令编译出的libuuid的rpm安装包会被抽取掉debug 信息(extracting debug info),为此需要在util-linux-ng.spec的头部加入
%global _enable_debug_package 0
%global debug_package %{nil}
%global __os_install_post /usr/lib/rpm/brp-compress %{nil}

2 GDB调试定位

#ifdef HAVE_TLS#define THREAD_LOCAL static __thread#else#define THREAD_LOCAL static#endif#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)#define DO_JRAND_MIXTHREAD_LOCAL unsigned short jrand_seed[3];#endifstatic int get_random_fd(void){        struct timeval  tv;        static int      fd = -2;        int             i;        if (fd == -2) {                gettimeofday(&tv, 0);#ifndef _WIN32                fd = open("/dev/urandom", O_RDONLY);                if (fd == -1)                        fd = open("/dev/random", O_RDONLY | O_NONBLOCK);                if (fd >= 0) {                        i = fcntl(fd, F_GETFD);                        if (i >= 0)                                fcntl(fd, F_SETFD, i | FD_CLOEXEC);                }#endif                srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);#ifdef DO_JRAND_MIX                jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);  <=== 崩溃在对thread local 变量 jrand_seed的赋值上

GDB 跟踪调试的时候发现,jrand_seed的地址是一个非法地址,首先想到大概是GCC或GlibC对TLS的支持的问题,如果将jrand_seed修改为非TLS变量或者使用老一点的不使用TLS变量的libuuid,都不会崩溃。最后找到一篇类似问题的报告https://github.com/mkoppanen/php-zmq/issues/11,并且里面提到了一个规避方法:即预先加载libuuid动态库export LD_PRELOAD="/lib64/libuuid.so.1"。持续追查下去,最后发现,这的确是glibc的一个bug,在使用“使用动态加载技术(dlopen),并且使用TLS的时候出现”。为什么我们基于Centos 6.5系统做的系统镜像上没有这个问题呢,原因是:我们后期使用yum upgrade/update更新了系统,glibc也被附带更新了,而新的glibc fix了这个问题。

3 解决办法

  碰到这个问题,解决的办法就是更新glibc了,Centos glibc-2.12-1.149已经修复了这个问题。glibc升级的时候需要考虑abi 兼容性的问题,可以使用abidiff或abipkgdiff比较新老glibc兼容性。如果glibc兼容,则可以直接升级,否则要连同系统一起整体升级,并重新编译上层应用。

0 0
原创粉丝点击