查看動態庫的加載路徑
patchelf --print-rpath so
$ORIGIN:$ORIGIN/lib
在 低版本 libc 庫運行高版本 libc 庫編譯的程序 這篇博客中我描述了使用 patchelf 來修改動態庫鏈接器的方法,在本篇文章中,我完整的列舉下 patchelf 的功能,並介紹另外一個實際的應用。
patchelf 具有的功能
運行 patchelf -h 能夠得到如下信息:
syntax: patchelf [--set-interpreter FILENAME] [--page-size SIZE] [--print-interpreter] [--print-soname] Prints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist [--set-soname SONAME] Sets 'DT_SONAME' entry to SONAME. [--set-rpath RPATH] [--remove-rpath] [--shrink-rpath] [--allowed-rpath-prefixes PREFIXES] With '--shrink-rpath', reject rpath entries not starting with the allowed prefix [--print-rpath] [--force-rpath] [--add-needed LIBRARY] [--remove-needed LIBRARY] [--replace-needed LIBRARY NEW_LIBRARY] [--print-needed] [--no-default-lib] [--debug] [--version] FILENAME
中文翻譯如下:
設置動態庫解析器
設置頁大小
設置 DT_SONAME
設置 rpath
刪除 rpath
添加允許的 rpath 前綴
打印 rpath
強制使用 rpath
添加需要的動態庫
刪除需要的動態庫
替換舊的動態庫為新的
打印幫助信息
不鏈接默認的動態庫
輸出調試信息
打印版本號
從上面的功能描述中可以看到,patchelf 的主要功能與動態庫解析器、rpath、動態庫本身相關,可能在解決一些動態庫鏈接程序執行的問題時能夠用到。
下面是一個具體的實例。
patchelf 修改 rpath 以使用自動以目錄中的動態庫
最近有同事找我們幫忙解決一個動態庫的問題,問題的具體情況是他編譯出來的 httpd 程序一直使用的是系統默認路徑中的動態庫,而他的需求是要使用自定義目錄中的動態庫。
他嘗試過設定 LD_LIBRARY_PATH 結果沒有生效,就來找我們幫忙看看。
我在 man ld.so 的翻譯 這篇文章中翻譯了 ld.so 動態庫鏈接器執行的過程,其中查找動態庫的步驟如下:
針對 ELF 格式文件,當 DT_RUNPATH 屬性不存在的情況下,使用二進制程序 dynamic section 中存在的 DT_RPATH 屬性指定的路徑來搜索 。DT_RPATH 已經被棄用。
使用環境變量 LD_LIBRARY_PATH 中指定的路徑來搜索。如果可執行程序設定了 setuid/setgid,這一步將被跳過。
從緩存文件 /etc/ld.so.cache 中查找。如果程序在鏈接時使用了 -z nodeflib 選項,默認庫路徑中的庫及那個會被跳過。安裝到硬件兼容目錄中的庫將會比其它庫優先查找。
在默認的 /lib 然后時 /usr/lib 中尋找,如果程序在鏈接時使用了 -z nodeflib 選項,這一步將被跳過
可以看到在搜索 LD_LIBRARY_PATH 之前會先以 ELF 文件中存在的 DT_RPATH 屬性中指定的路徑來搜索動態庫,看上去這個問題就出在這里。
確定問題
運行 readelf -a httpd 搜索與 rpath 相關的內容,果然搜索到了,發現確實設定了這個變量的值,並且指向默認路徑,這就是導致 LD_LIBRARY_PATH 不能生效的原因。
確定了問題后,搜索 httpd 編譯目錄中的 Makefile 文件,發現 rpath 的設定是通過向編譯器傳參設置的,確定問題應該是 configure 的時候沒有進行某種配置。
臨時讓 httpd 程序先跑起來的方法
httpd 的配置與編譯過程相對復雜,要解決上面的問題可能要搞一會,這時我們想用一些更簡單的方法先讓 httpd 程序跑起來,這其實可以通過 patchelf 來實現。
運行如下命令,將 rpath 的只修改為自定義的動態庫目錄就解決了這個問題。
patchelf --set-rpath '/home/xx/local/apr/apr/lib/:/home/xx/local/apr/util/lib/' httpd
有沒有其它的方法?
其實這個問題也可以直接刪除 rpath 的設定,然后設定 LD_LIBRARY_PATH 來解決,這其實與修改 ELF 文件中的 rpath 屬性的內容大同小異。
總結
我們解決問題依賴我們掌握的知識、閱讀過的書、寫過的代碼、運行過的 demo、做過的解決相同問題的記錄。在真正解決問題的時候,可能我們還是存在一定的欠缺,這些欠缺在我看來很多是我們對現有的工具的大致工作原理與其提供的功能存在盲點,其實我們不必要知道所有的細節,但是對於我們的業務范圍內使用到的工具提供的功能需要有全面的了解,這樣我們才可能能夠輕松的解決一些因欠缺知識而看似困難的問題。
原文鏈接:https://blog.csdn.net/Longyu_wlz/article/details/108550528