不管你是用annoy還是用tensorflow缸血,用pip安裝后嗜历,然后import的時候會產(chǎn)生類似以下的異常:
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
優(yōu)先嘗試這種方法:
python錯誤:/lib64/libc.so.6: version `GLIBC_2.14’ not found解決辦法
我在這個問題上卡了很久巡揍,也查找了很多資料舷手,都不能圓滿解決glibc依賴的問題煞赢,連重新編譯libc-2.14都試過了啦辐。
后來偶然發(fā)現(xiàn)了這篇文文章:Running new applications on old glibc 參考這篇文章的思路雹拄,這個問題才得以圓滿解決(感謝文章的作者耙册,同時感謝公司內(nèi)部資料)槽袄,下面根據(jù)這篇文章的思路來闡述如何逐步解決annoy或者tf依賴glibc的問題烙无。
解決方案
解決方法主要包括兩部分內(nèi)容:
- 減弱版本依賴以便在程序在啟動的時候不被動態(tài)連接器終止
- 提供缺失的依賴函數(shù)(由最新的glibc版本提供)
下面以annoy為例,TF類似的遍尺,自己替換文件路徑即可
查看依賴
錯誤是由/home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so這個動態(tài)連接庫引起的截酷,那看一看這個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函數(shù)乾戏。
再來看一看annoylib.so中依賴的glibc版本信息迂苛,執(zhí)行:
# readelf -V 文件路徑
readelf -V /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so
輸出:
可以看出在地址偏移量0x0050處,是glibc_2.14的標記地址鼓择,問題的關(guān)鍵是如何減弱這個版本依賴三幻。
其思路就是想辦法讓這個glibc_2.14這個版本依賴變成可選而非強制性的。
更改glic_2.14依賴
- 圖中.gpu.version_r 的地址起始位置在文件偏移量0x002288處
- glibc_2.14版本標記地址偏移量是0x0050,在文件地址偏移量的(0x002288+0x0050)
-
指向0x0090偏移量的位置存放的是結(jié)構(gòu)體Elfxx_Vernaux
- vna_flags 是依賴信息呐能,在結(jié)構(gòu)體的第二個位置念搬,ELFxx_Word是32bit抑堡,也就是4B,因此vna_flag的起始地址偏移量是0x04
通常Flags:none在二進制位置的值是0x0000朗徊,根據(jù)上述分析首妖,為了能減弱glibc_2.14的版本依賴,需要在(0x002288+0x0050+0x04)處填充0x02(對應VER_FLG_WEAK)
因此執(zhí)行:
# 要養(yǎng)成習慣爷恳,更改文件的時候有缆,一定要備份
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
執(zhí)行完了铸豁,我們驗證效果:
# readelf -V 文件路徑
readelf -V /home/users/kinva/tools/lib/python2.7/site-packages/annoy/annoylib.so
看到2.14版本依賴已經(jīng)變成weak了灌曙。成功了一半。
實現(xiàn)缺失的函數(shù)
接下來需要解決缺失的memcpy函數(shù)节芥,最簡單的方式是生成一個本地的動態(tài)庫來實現(xiàn)缺失的函數(shù),并在執(zhí)行程序之前使用LD_PRELOAD去load這個動態(tài)庫逆害。
針對上文中指出的缺失的memcpy函數(shù)头镊,如果查看實際的glibc_2.14實現(xiàn)的memcpy函數(shù)就會發(fā)現(xiàn),其實際上和memmove相同魄幕,這樣我們可以自己實現(xiàn)這個函數(shù)(mylibc.so):
#include <string.h>
void* memcpy(void *dest, const void *src, size_t n) {
return memmove(dest, src, n);
}
執(zhí)行以下命令編譯成so
gcc -s -shared -o mylibc.so -fPIC -fno-builtin mylibc.c
得到mylibc.so共享庫
接下來就是鏈接動態(tài)庫就好了相艇。
- 可以拷貝so到已經(jīng)在引入的路徑,如我的/home/users/kinva/tools/lib
- 或者 export LD_LIBRARY_PATH=/home/users/kinva/tools/mylib:$LD_LIBRARY_PATH
再次執(zhí)行:
[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
>>>
其他問題
如果還是繼續(xù)報錯纯陨,請注意報的so已經(jīng)不一樣了坛芽,把每一個so的問題都解決。
如果你依賴的gcc是4.8+翼抠,請用4.6版本咙轩,否則你要擁有root權(quán)限。
TF需要更改的文件:
_pywrap_tensorflow.so
libstdc++.so.6(root權(quán)限)
librt.so.1
_sparse_feature_cross_op.so
_bucketization_op.so
_set_ops.so
_lstm_ops.so
_sdca_ops.so
需要實現(xiàn)的so有:mylibc.so和mygettime.so
memcpy和gettime相關(guān)阴颖,可以查查別的資料這個實現(xiàn)很簡單的活喊,我就不寫了。
建議
這個東西根本原因就是系統(tǒng)版本庫太老量愧,如果更新glibc將會引起系統(tǒng)不穩(wěn)定钾菊,所以建議還是升級系統(tǒng)吧。如果是centos偎肃,建議用6u3及以上版本煞烫。