錯誤:/lib64/libc.so.6: version `GLIBC_2.14’ not found解決辦法

不管你是用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

輸出:


readelf1.png

可以看出在地址偏移量0x0050處,是glibc_2.14的標(biāo)記地址,問題的關(guān)鍵是如何減弱這個版本依賴。
其思路就是想辦法讓這個glibc_2.14這個版本依賴變成可選而非強制性的。

更改glic_2.14依賴

  • 圖中.gpu.version_r 的地址起始位置在文件偏移量0x002288處
  • glibc_2.14版本標(biāo)記地址偏移量是0x0050,在文件地址偏移量的(0x002288+0x0050)
  • 指向0x0090偏移量的位置存放的是結(jié)構(gòu)體Elfxx_Vernaux


    glibc1.png
  • vna_flags 是依賴信息,在結(jié)構(gòu)體的第二個位置,ELFxx_Word是32bit,也就是4B,因此vna_flag的起始地址偏移量是0x04

通常Flags:none在二進制位置的值是0x0000,根據(jù)上述分析,為了能減弱glibc_2.14的版本依賴,需要在(0x002288+0x0050+0x04)處填充0x02(對應(yīng)VER_FLG_WEAK)

因此執(zhí)行:

# 要養(yǎng)成習(xí)慣,更改文件的時候,一定要備份
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
readelf2.png

看到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及以上版本。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容