0x01 前言:
今天收到同事RTX消息,反应开发机器PHP无法执行异常,报Floating point exception。
第一次碰到这种怪问题,第一反应先查下这个报错是怎么回事?一查,搜到的结果是:
同一个程序在一台高版本Linux上运行时没有问题,而在另一台低版本机器上运行报Floating Point Exception时,那么这极有可能是由高版本gcc链接造成的。高版本的gcc在链接时采用了新的哈希技术来提高动态链接的速度,这在低版本中是不支持的。因此会发生这个错误。gcc就是一个编译器。编译出来的软件在低版本操作系统上有些技术不支持造成这个原因。
什么意思呢?就是用高版本GCC编译的程序在低版本GCC库环境下是无法运行的。当然了反过来讲也是对的,就是不同主版本的GCC编译出来的环境和程序是存在兼容性问题的。
奇怪了,机器以前运行都正常,不应该存在这种情况啊。后来发现在这台机器上,除了PHP外,mysql和apache运行也报同样的错误。
0x02 分析:
在用程序的gcc版本高于系统的gcc版本,除了是程序的问题,还会有什么问题?莫非是系统的相关库文件被改动了,这样就麻烦了,这台机器上开发人员蛮多的,谁做了什么变动导致了这个问题怎么才能确定呢?问了几个人,没有得到实质性的解决方法和分析思路,刚好也比较忙没有时间去寻根问底,到底是哪个文件被修改了?莫非只能重装系统,那简直就是一个灾难啊。
由于这台开发机器涉及人员较多,由于第一次碰到类似问题,于是将相关开发同事拉了一个临时会话组,一起来讨论这个问题,到底是什么原因导致的。
既然搜索到的结果提示Floating point exception异常跟gcc有关,那会不会跟最近升级glibc rpm有关呢?虽然升级glibc rpm包是在春节前进行的并且是升级相关库文件,当时升级完毕后并未出现任何问题,也不符合程序依赖的库文件版本高于系统的版本这一说法,但是也不能放过这一因素,于是联系相关同事一起查看和恢复glibc的版本,发现升级后还是存在相同问题,那就可以确定这次故障完全和glibc版本的升级没有任何关系。
那会是什么原因导致的呢?
刚好有一个开发同事发了一个内部分享帖子的链接,帖子的内容跟这次问题竟然是一样的问题。
“别乱动系统文件——C++库被替换导致程序FPE错误的思考”,根据文中描述的,通过strace跟踪mysql命令执行的过程,strace -t -f /usr/bin/mysql ,程序运行到调用/lib64/libdl.so.2库时出现
--- SIGFPE (Floating point exception) @ 0 (0) ---
+++ killed by SIGFPE (core dumped) +++
通过相同的命令发现,虽然输出的提示稍有差异,但是都指向了/lib64/libdl.so.2这个文件。
运维三件宝之一,查看日志文件 /var/log/messages,当运行程序报Floating point exception时系统记录的日志:
kernel: [60795.243107] mysql[3127] trap divide error
ip:7fea7d9b065f sp:7fff6bc3f2a0 error:0 in ld-2.4.so[7fea7d9a8000+1b000]
出现问题的程序:apache,php,mysql,正常的程序ls
查看相关引用的库文件:ldd /usr/bin/mysql ,出问题的程序共同引用了C++库(libstdc++.so.6). 而正常的ls程序没有用到。
rpm -qf /usr/lib64/libstdc++.so.6
libstdc++-4.1.2_20070115-0.11
root@xxx:~# rpm -V libstdc++-4.1.2_20070115-0.11
.......T /usr/lib/libstdc++.so.6.0.8
....L... /usr/lib64/libstdc++.so.6
.......T /usr/lib64/libstdc++.so.6.0.8
系统上相同路径下显示的/usr/lib64/libstdc++.so.6被链接到了
/usr/lib64/libstdc++.so.6.0.13,在相同版本的系统下,对应的软链接也是到
/usr/lib64/libstdc++.so.6.0.8,说明这个链接地址被修改过了。
cd /usr/lib64ln -sf libstdc++.so.6.0.8 libstdc++.so.6
根据文章中的描述,进行完这一步操作,之前异常的程序就正常运行了,可是我一运行,还是报错。
error while loading shared libraries: libstdc++.so.6: wrong ELF class: ELFCLASS32
什么情况?怎么又出现这个报错,看似熟悉又很陌生的报错信息。
又有人贴出来,这是32位软件包尝试载入64位库。
自己搜索了下相关错误信息,大体上明白是软件和系统库的架构版本上差异导致。
由于可以确定当前机器是64位Linux机器,安装的软件也是64位的版本。
于是用file命令查看了下/usr/lib64/libstdc++.so.6.0.8的文件信息。
file /usr/lib64/libstdc++.so.6.0.8
/usr/lib64/libstdc++.so.6.0.8: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), stripped
哎,肯定是有人安装软件时用32位的库文件覆盖了/usr/lib64/libstdc++.so.6.0.8的同时修改了目标软链接。
到相同版本的操作系统机器上传了一份相同的库文件到当前机器,同时修改了下软链接的目标地址。
再次运行mysql,正常运行。
0x03 结论:
libstdc++.so.6.0.8对应libstdc++-4.1.2, 而libstdc++.so.6.0.13对应libstdc++-4.4.6。
系统库文件被替换会导致很多软件无法正常运行,严重时会导致系统挂掉无法补救。
这一次就是系统中的libstdc++.so.6.0.8相关文件被替换导致系统中的mysql,php,apapche等报Floating Point Exception错误信息。
0x04 总结:
比较危险的操作除了直接替换系统库文件外,其实还有不少是需要关注的。
python版本的替换,不要直接覆盖,最好用软链接或者指定绝对路径的方式来替换。系统中有很多系统是由python写的,一旦替换的python版本与系统版本差异较大,就会导致系统依赖python的程序无法正常运行,比如yum。
修改配置文件。修改之前做个备份,有的时候真得能救一命。
删除先mv bak,直接删除的后果有时可能会很严重。
多人修改,一个机器使用的人越多,引发故障的可能性越大,重要的信息还是要有备份或者容灾。
0x05 附录
应用二进制接口
http://zh.wikipedia.org/wiki/%E5%BA%94%E7%94%A8%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%8E%A5%E5%8F%A3