問題起緣於編譯一個程序時,使用glib2-2.28.8的動態庫,而系統自帶的是glib2-2.22.5
不想升級系統的glib2庫,而使用程序自帶庫文件的方式加載(類似windows系統,優先加載當前目錄的DLL文件)。
1.首先編譯時,使用-L指定lib路徑,-lglib-2.0指定庫文件名
2.運行時通過,LD_LIBRARY_PATH=./ 指定搜索當前目錄為庫目錄
3.測試發現使用的是系統的庫
ldd ./test
linux-vdso.so.1 => (0x00007fff86fff000)
libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f386e16c000)
libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f386df68000)
libz.so.1 => /lib64/libz.so.1 (0x00007f386dd52000)
libc.so.6 => /lib64/libc.so.6 (0x00007f386d9bf000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f386d7bb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f386e45f000)
的庫路徑,處於搜索路徑的隊列尾部,造成優先找到系統的庫,那麼可不可以通過修改“鏈接的庫文件名”,達到目的了
1.修改libglib-2.0.so文件為libmyglib.so,鏈接時使用-lmyglib指定鏈接庫
2.運行時通過,LD_LIBRARY_PATH=./ 指定搜索當前目錄為庫目錄
3.
ldd ./test
linux-vdso.so.1 => (0x00007fff86fff000)
libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f386e16c000)
libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f386df68000)
libz.so.1 => /lib64/libz.so.1 (0x00007f386dd52000)
libc.so.6 => /lib64/libc.so.6 (0x00007f386d9bf000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f386d7bb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f386e45f000)
“生成可執行文件時,如果鏈接的是靜態庫,那麼鏈接器會按照靜態鏈接規則,將對應的符號引用進行重定位。而如果是動態庫,鏈接器會將這個符號標記為動態鏈接的符號,不進行重定位,而是在裝載時再進行。所以,盡管是動態鏈接,如果是已經進入到了鏈接階段,那麼也需要能在相應的.so中找到某符號的定義,否則也會引發Undefined reference to的鏈接錯誤。因為鏈接器只有通過.so文件,才能判斷某符號是個動態鏈接符號,所以也需要讀取這些.so文件,找到相應符號的定義”
意思就是,連接器只是做確認函數符號可用,就編譯通過來,更本沒記錄動態庫的文件名,這點和WINDOWS不一樣
而實際運行裝載的時候,只通過函數符號,裝載需要的.SO就可以來,索引我定義的libmyglib.so又被沒有機會裝載進來,被系統的替代了。
1.解決加載順序的問題,LD_PRELOAD可以實現
LD_LIBRARY_PATH 將其他目錄加入庫搜索路徑。它的內容應該是由冒號
分隔的目錄列表,與可執行文件的 PATH 變量具有相同的格式。
如果調用設置用戶 ID 或者進程 ID 的程序,該變量被忽略。
LD_PRELOAD 首先裝入用戶定義的庫,使得它們有機會覆蓋或者重新定義標准庫。
export LD_PRELOAD=./libglib.so:./libgmodule.so
ldd ./test
linux-vdso.so.1 => (0x00007fffc31ff000)
./libglib.so (0x00007ffa5d757000)
./libgmodule.so (0x00007ffa5d553000)
libz.so.1 => /lib64/libz.so.1 (0x00007ffa5d330000)
libc.so.6 => /lib64/libc.so.6 (0x00007ffa5cf9d000)
librt.so.1 => /lib64/librt.so.1 (0x00007ffa5cd95000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007ffa5cb91000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffa5da6e000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffa5c974000)
2.替換系統的glib2庫,有三種方式,最好使用第一種,其他方式會遺留垃圾
2.1升級安裝覆蓋
2.2直接拷貝.so覆蓋,或更改libglib-2.0.so.0鏈接的指向
2.3共享當前版本的庫的系統 sudo ldconfig /opt/glib-2.28.8/lib
ldd ../bin/test
linux-vdso.so.1 => (0x00007fff897ff000)
libglib-2.0.so.0 => /opt/glib-2.28.8/lib/libglib-2.0.so.0 (0x00007f59f5f02000)
libgmodule-2.0.so.0 => /opt/glib-2.28.8/lib/libgmodule-2.0.so.0 (0x00007f59f5cfe000)
libz.so.1 => /lib64/libz.so.1 (0x00007f59f5ae8000)
libc.so.6 => /lib64/libc.so.6 (0x00007f59f5755000)
librt.so.1 => /lib64/librt.so.1 (0x00007f59f554d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f59f5349000)
/lib64/ld-linux-x86-64.so.2 (0x00007f59f6226000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f59f512c000)
LD_DEBUG通過設置這個環境變量,可以方便的看到 loader 的加載過程(包括庫的加載,符號解析等過程),使用【LD_DEBUG=help 可執行文件路徑】可查看使用幫助。
LD_PRELOAD環境變量指定的共享庫會被預先加載,如果出現重名的函數,預先加載的函數將會被調用,如在預先加載的庫中包含自定義的puts函數,則在執行程序時將使用自定義版本的puts函數,而不是libc庫中的puts函數。
列出鏈接符號
nm -Du /lib64/libglib-2.0.so.0