1.问题基本信息
问题级别 | 时间 | 问题简述 |
严重 | 2017-11-23 | 安装环境与编译环境GLIBC版本差异;Redis启动报错,编译问题,操作系统库函数依赖 |
2. 问题场景
安装AC大包过程中报错:Start module: redis process FAILED. 定位发现Redis-server进程没有拉起,尝试手动拉起Redis-server进程失败,报错:/lib64/libc.so.6: version `GLIBC_2.14' not found(required by /opt/…/redis-server)。 |
3. 表因分析
很明显,报错显示当前Linux系统找不到GLIBC_2.14版本库,而Redsi-server依赖GLIBC_2.14,使用命令:strings /lib64/libc.so.6 | grep GLIBC查看当前系统GLIBC版本:可以看出,当前系统最高支持GLIBC_2.11,低于需要的2.14。至此,可定性为:编译redis-server的编译机GLIBC版本(2.14)高于目标安装机的GLIBC版本(2.11),即:高版本编译,低版本安装,因此安装失败。但根因并不在此! controller-192-168-1-3:/opt/controller/redis/bin # strings /lib64/libc.so.6 | grep GLIBC GLIBC_2.2.5 GLIBC_2.2.6 GLIBC_2.3 GLIBC_2.3.2 GLIBC_2.3.3 GLIBC_2.3.4 GLIBC_2.4 GLIBC_2.5 GLIBC_2.6 GLIBC_2.7 GLIBC_2.8 GLIBC_2.9 GLIBC_2.10 GLIBC_2.11 GLIBC_PRIVATE |
使用命令:strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC (编译机是Ubuntu)查看编译机的GLIBC版本:编译机GLIBC版本高达2.18(为谨慎起见,查看libc.so.6的软连接,确认实际采用的GLIBC版本)。如果是GLIBC版本问题,编译机的版本远高于目标安装机,上述问题不应该为“偶现”,应该“必现”,因此,GLIBC版本不是导致上述问题的根因。 controller-192-168-1-3:/opt/controller/redis/bin # strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC GLIBC_2.2.5 GLIBC_2.2.6 GLIBC_2.3 GLIBC_2.3.2 GLIBC_2.3.3 GLIBC_2.3.4 GLIBC_2.4 GLIBC_2.5 GLIBC_2.6 GLIBC_2.7 GLIBC_2.8 GLIBC_2.9 GLIBC_2.10 GLIBC_2.11 GLIBC_2.12 GLIBC_2.13 GLIBC_2.14 GLIBC_2.15 GLIBC_2.16 GLIBC_2.17 GLIBC_2.18 GLIBC_PRIVATE |
4. 根因分析
在redis-server所在路径下输入命令:objdump -T redis-server| fgrep GLIBC_2.14 查看redis-server依赖的GLIBC_2.14版本库的具体函数:截图可见,只有一个函数memcpy,依赖版本为GLIBC_2.14。 controller-192-168-1-3:/opt/controller/redis/bin # objdump -T redis-server| fgrep GLIBC_2.14 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy |
输入命令:objdump -T /lib64/libc.so.6 | fgrep memcpy,查看目标安装机支持的memcpy版本:截图可见,目标安装机仅支持GLIBC_2.2.5。 controller-192-168-1-3:/opt/controller/redis/bin # objdump -T /lib64/libc.so.6 | fgrep memcpy 000000000008c400 w DF .text 0000000000000009 GLIBC_2.2.5 wmemcpy 00000000000eef00 g DF .text 000000000000001b GLIBC_2.4 __wmemcpy_chk 0000000000084670 g DF .text 0000000000000465 GLIBC_2.2.5 memcpy 0000000000084660 g DF .text 0000000000000009 GLIBC_2.3.4 __memcpy_chk |
输入命令:objdump -T /lib/x86_64-linux-gnu/libc.so.6 | fgrep memcpy 查看编译机(docker编译)支持的memcpy版本:可见,编译机支持两种版本(2.14和2.2.5)。至此,根因找到:redis源码依赖memcpy函数,编译机概率性的采用memcpy[GLIBC_2.2.5]和memcpy[GLIBC2.14]编译redis-server,而目标安装机仅支持memcpy[GLIBC_2.2.5],由此导致redis-server概率性安装失败。 controller-192-168-1-3:/opt/controller/redis/bin # objdump -T /lib/x86_64-linux-gnu/libc.so.6 | fgrep memcpy 00000000000alcc0 w DF .text 0000000000000009 GLIBC_2.2.5 wmemcpy 000000000010bdf0 g DF .text 000000000000001b GLIBC_2.4 __wmemcpy_chk 0000000000091620 g DF .text 0000000000000465 GLIBC_2.14 memcpy 000000000008c420 g DF .text 0000000000000465 (GLIBC_2.2.5) memcpy 0000000000108990 g DF .text 0000000000000009 GLIBC_2.3.4 __memcpy_chk |
5. 解决方案
可在redis源码中添加约束,显式指定所依赖的memcpy函数的GLIBC版本,需添加的约束代码如下: __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); 【注意】只需在调用函数memcpy的源文件中加入此约束 |
解决方案验证例子
【步骤1】编写一个简单的C测试程序:test.c,功能:将s中的字符串复制到字符数组d中 |
#include<stdio.h>
#include<string.h>
int main()
{
char*s="Golden Global View";
char d[20];
memcpy(d,s,strlen(s));
d[strlen(s)]='\0';
printf("%s",d);
getchar();
return 1;
}
【步骤2】在test.c同级目录下执行编译命令:gcc -o test test.c
【步骤3】运行test可执行文件:正常
【步骤4】查看test依赖的memcpy函数的GLIBC版本:可以看出,memcpy采用的是GLIBC_2.14
将编译的好的可执行文件test复制到目标机执行,报错:version `GLIBC_2.14′ not found ,查看test依赖GLIBC_2.14的函数名称,为memcpy,查看目标机支持的memcpy函数版本:GLIBC_2.2.5 |
【步骤5】在源码中对依赖的memcpy函数进行版本约束,使其按指定版本编译。添加约束代码:__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); |
#include<stdio.h>
#include<string.h>
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
int main()
{
char*s="Golden Global View";
char d[20];
//clrscr();
memcpy(d,s,strlen(s));
d[strlen(s)]='\0';
printf("%s",d);
getchar();
return 1;
}
【步骤6】执行编译,运行,检测memcpy的版本,可以看到,test依赖的memcpy的版本为GLIBC_2.2.5,约束是有效的。
【特别说明】如有任何疑问,请扫描二维码提问(GitChat):