旧版glibc兼容旅程

一.背景

在新环境编译程序部署到老环境运行。

为什么不直接在老环境里面编译?因为用到了一点c++11的特性,需要较高版本的gcc进行编译;老环境默认的gcc版本太低,新版gcc的rpm包安装部署不上(glibc版本太低),源码安装高版本gcc又太麻烦。

二. 错误表现

注: server名字做过修改, 暂且叫my_server.

 

运行时报错,找不到GLIBC_2.14的符号:


而本机glibc的版本才2.12:

 旧版glibc兼容旅程_第1张图片


glibc官方最新版本虽然为glibc_2.23. 但是编译机gblic版本为glibc_2.17.

所以只需要查看my_server用到了哪些>2.12, <=2.17的glibc符号即可:

如图所示, 只有一个memcpy超过了本机glibc的版本.

 

三. 问题解决

现在得想办法去除对高版本(2.14)memcpy的引用, 改为引用对应的低版本.

3.1静态链接

最简单的办法无非是静态链接了,但是在链接时jemalloc跟libc有重复的符号定义:

旧版glibc兼容旅程_第2张图片

 

去除对jemalloc的依赖, 编译通过, 但是有一堆告警, 部分告警如下:

 旧版glibc兼容旅程_第3张图片


考虑到多线程环境下, 最好使用jemalloc(或者tcmalloc)来管理内存分配和释放. 所以不能去除对它的依赖, 也就不能用静态链接.

 

3.2符号版本指定

3.2.1编译时符号指定

如果用动态链接的话, 就要想办法在编译的时候强制引用2.12(运行环境的glibc版本)以下版本的memcpy.

 

先看看运行环境下的memcpy的版本:

旧版glibc兼容旅程_第4张图片

nm没有看到版本信息.

 

试试objdump:

旧版glibc兼容旅程_第5张图片

显示当前环境的memcpy版本是GLIBC_2.2.5.

 

接下来就是想办法在动态链接的时候强制引用memcpy的2.2.5版.

用汇编指令.symver可以完成版本指定(确保在所有调用 memcpy之前完成汇编指令的版本指定):


但是编译之后发现依旧引用了2.14版的memcpy

 旧版glibc兼容旅程_第6张图片

一开始以为是汇编指令不管用, 后来发现链接的若干公共库(.a)也有对memcpy的引用,因为公共库在编译的时候已经指定了对memcpy的2.14版, 所以这里的汇编指令对其是没有效果的.看来还是要在链接的时候做一些手脚.

3.2.2链接时符号指定

编译时指定符号版本对编译好的公共库不管用, 可以在链接的时候对引用的符号进行接管.

 

答案是使用ld的wrap功能, 选项说明如下:

 旧版glibc兼容旅程_第7张图片

a)     首先创建一个wrap函数

旧版glibc兼容旅程_第8张图片

因为是在cpp代码中引用, 所以这里需要加extern “C”, 否则链接的时候会报错:

undefinedreference to __wrap_memcpy

 

b)      然后编译的时候注意加上-Wl,--wrap=memcpy(或者-Xlinker --wrap=memcpy)选项, 将—wrap选项传递给链接器. 编译完成后检查memcpy的符号:

很好,现在没有对memcpy的高本版的引用了. 放到运行环境运行看看:

对GLIBC的引用正常了, 现在又出现对GLIBCXX的引用问题.

 

my_server引用了3个GLIBCXX_3.4.14版的符号:

而运行环境最高只支持GLIBCXX_3.4.13:

旧版glibc兼容旅程_第9张图片

c)     对GLIBC静态链接不满足需求, 但是单独将c++库进行静态链接是没有问题的, 于是编译时加上-static-libstdc++选项.

但是链接找不到stdc++库:

 

原来是因为编译环境只有c++的so库, 没有对应的静态库:

 

最后从一台云主机(gcc 4.8.4)下载了一个libcstdc++.a到开发机(gcc4.8.2),然后再编译链接,一切变得正常了.

 

四. 总结

1.  整体静态链接可以解决符号问题,但是会导致不能使用jemalloc(或者tcmalloc)

2.  用汇编指令.symver可以指定当前编译的memcpy版本, 却无法指定已经编译好的库中使用的memcpy的版本

3.  定义一个包装函数__wrap_memcpy, 在链接阶段(需要指定相应的—wrap选项)接管所有对memcpy的引用.

4.  单独将c++库进行静态链接.


你可能感兴趣的:(glibc)