不管你是用annoy还是用tensorflow,用pip安装后,然后import的时候会产生类似以下的异常:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/users/kinva/tools/lib/python2.7/site-packages/annoy/__init__.py", line 15, in <module>
from .annoylib import *
ImportError: /lib64/tls/libc.so.6: version `GLIBC_2.14' not found (required by /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so)
# 如果是TF就是lib/python2.7/site-packages/tensorflow/python/_pywrap_tensorflow.so
优先尝试这种方法:
python错误:/lib64/libc.so.6: version `GLIBC_2.14’ not found解决办法
我在这个问题上卡了很久,也查找了很多资料,都不能圆满解决glibc依赖的问题,连重新编译libc-2.14都试过了。
后来偶然发现了这篇文文章:Running new applications on old glibc 参考这篇文章的思路,这个问题才得以圆满解决(感谢文章的作者,同时感谢公司内部资料),下面根据这篇文章的思路来阐述如何逐步解决annoy或者tf依赖glibc的问题。
解决方案
解决方法主要包括两部分内容:
- 减弱版本依赖以便在程序在启动的时候不被动态连接器终止
- 提供缺失的依赖函数(由最新的glibc版本提供)
下面以annoy为例,TF类似的,自己替换文件路径即可
查看依赖
错误是由/home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so这个动态连接库引起的,那看一看这个so里到底哪部分依赖了glibc2.14。
# readelf -s 文件路径|grep GLIBC_2.14
readelf -s /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so | grep GLIBC_2.14
输出如下:
108: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (7)
180: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.14
我们看到依赖了2.14的memcpy函数。
再来看一看annoylib.so中依赖的glibc版本信息,执行:
# readelf -V 文件路径
readelf -V /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so
输出:
可以看出在地址偏移量0x0050处,是glibc_2.14的标记地址,问题的关键是如何减弱这个版本依赖。
其思路就是想办法让这个glibc_2.14这个版本依赖变成可选而非强制性的。
更改glic_2.14依赖
- 图中.gpu.version_r 的地址起始位置在文件偏移量0x002288处
- glibc_2.14版本标记地址偏移量是0x0050,在文件地址偏移量的(0x002288+0x0050)
-
指向0x0090偏移量的位置存放的是结构体Elfxx_Vernaux
- vna_flags 是依赖信息,在结构体的第二个位置,ELFxx_Word是32bit,也就是4B,因此vna_flag的起始地址偏移量是0x04
通常Flags:none在二进制位置的值是0x0000,根据上述分析,为了能减弱glibc_2.14的版本依赖,需要在(0x002288+0x0050+0x04)处填充0x02(对应VER_FLG_WEAK)
因此执行:
# 要养成习惯,更改文件的时候,一定要备份
cp /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so annoylib.so
# 更改为弱依赖
# 其中0x22dc需要自己计算0x002288+0x0050+0x04
# 这里替换2个地方,一个是地址,一个是文件路径,其他保持不变。
for addr in 0x22dc; do printf '\x02' | dd conv=notrunc of=/home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so bs=1 seek=$((addr)) ; done
执行完了,我们验证效果:
# readelf -V 文件路径
readelf -V /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so
看到2.14版本依赖已经变成weak了。成功了一半。
实现缺失的函数
接下来需要解决缺失的memcpy函数,最简单的方式是生成一个本地的动态库来实现缺失的函数,并在执行程序之前使用LD_PRELOAD去load这个动态库。
针对上文中指出的缺失的memcpy函数,如果查看实际的glibc_2.14实现的memcpy函数就会发现,其实际上和memmove相同,这样我们可以自己实现这个函数(mylibc.so):
#include <string.h>
void* memcpy(void *dest, const void *src, size_t n) {
return memmove(dest, src, n);
}
执行以下命令编译成so
gcc -s -shared -o mylibc.so -fPIC -fno-builtin mylibc.c
得到mylibc.so共享库
接下来就是链接动态库就好了。
- 可以拷贝so到已经在引入的路径,如我的/home/users/kinva/tools/lib
- 或者 export LD_LIBRARY_PATH=/home/users/kinva/tools/mylib:$LD_LIBRARY_PATH
再次执行:
[kinva@MacBook-Pro ~]$ python
Python 2.7.3 (default, Jan 24 2017, 17:03:37)
[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import annoy
>>>
其他问题
如果还是继续报错,请注意报的so已经不一样了,把每一个so的问题都解决。
如果你依赖的gcc是4.8+,请用4.6版本,否则你要拥有root权限。
TF需要更改的文件:
_pywrap_tensorflow.so
libstdc++.so.6(root权限)
librt.so.1
_sparse_feature_cross_op.so
_bucketization_op.so
_set_ops.so
_lstm_ops.so
_sdca_ops.so
需要实现的so有:mylibc.so和mygettime.so
memcpy和gettime相关,可以查查别的资料这个实现很简单的,我就不写了。
建议
这个东西根本原因就是系统版本库太老,如果更新glibc将会引起系统不稳定,所以建议还是升级系统吧。如果是centos,建议用6u3及以上版本。