GDB動態庫搜尋路徑
筆記:
當GDB無法顯示so動態庫的資訊或者顯示資訊有誤時,通常是由於庫搜尋路徑錯誤導致的,可使用set sysroot、set solib-absolute-prefix、set solib-search-path來指定庫搜尋路徑。
1. set sysroot 與 set solib-absolute-prefix 是同一條命令,實際上,set sysroot是set solib-absolute-prefix 的別名。
2. set solib-search-path設定動態庫的搜尋路徑,該命令可設定多個搜尋路徑,路徑之間使用“:”隔開(在linux中為冒號,DOS和Win32中為分號)。
3. set solib-absolute-prefix 與 set solib-search-path 的區別:
總體上來說solib-absolute-prefix設定庫的絕對路徑字首,只對絕對路徑有效;而solib-search-path設定庫的搜尋路徑,對絕對路徑和相對路徑均起作用。(編譯器自動連結的so庫多采用絕對路徑)。
詳細規則有:
set solib-search-path由於是路徑字首,所以只能設定一個路徑,而solib-search-path可以設定多個搜尋路徑。
在載入動態庫資訊時Coredump會碰到兩種路徑:絕對路徑和相對路徑。編譯時連結的庫通常是絕對路徑,例如"/lib/libc.so.6"、"/lib/libdl.so.2"等,此時在Coredump檔案中也同樣儲存為絕對路徑;而程式用dlopen函式載入的so庫可能使用相對路徑,例如"./libddd.so",此時Coredump檔案原封不動地儲存相同的路徑。
為便於表述,用A表示set solib-absolute-prefix設定的路徑,R(A)表示A去掉根字首後的路徑(即去掉字首“/”符號),用Bn表示set solib-search-path設定的每一條路徑,用X表示Coredump中儲存的庫路徑,即待搜尋的庫檔案路徑,F(X)表示X中去掉目錄後的檔名(路徑最後“/”符號後的字串)。
對絕對路徑,搜尋順序是:
1) A / X // 先新增solib-absolute-prefix字首進行搜尋,成功則不再繼續,否則繼續2)
2) R(A) / X // 再把1)的根字首去掉後進行搜尋,成功則不再繼續,否則繼續3)
3) Bn / R(A) / X // 再在2)的基礎上逐一新增solib-search-path中的每條路徑進行搜尋,成功則不再繼續,否則繼續4)
4) Bn / F(X) // 再只使用2)中的檔名(去掉目錄段),並逐一新增solib-search-path中的每條路徑進行搜尋,成功則不再繼續,否則繼續5)
5) $PATH / R(A) / X // 在2)的基礎上使用環境變數$PATH中的每條路徑進行搜尋,成功則不再繼續,否則繼續6)
6) $LD_LIBRARY_PATH / R(A) / X // 在2)的基礎上使用環境變數$LD_LIBRARY_PATH中的每條路徑進行搜尋,成功則不再繼續,否則繼續7)
7) 返回失敗
對相對路徑,搜尋順序是:
1) X // 直接使用原始路徑進行搜尋,成功則不再繼續,否則繼續2)
2) Bn / X // 再逐一新增solib-search-path中的每條路徑進行搜尋,成功則不再繼續,否則繼續3)
3) Bn / F(X) // 再只使用檔名(去掉目錄段),並逐一新增solib-search-path中的每條路徑進行搜尋,成功則不再繼續,否則繼續4)
4) $PATH / X // 再使用環境變數$PATH中的每條路徑進行搜尋,成功則不再繼續,否則繼續5)
5) $LD_LIBRARY_PATH / X // 再使用環境變數$LD_LIBRARY_PATH中的每條路徑進行搜尋,成功則不再繼續,否則繼續6)
6) 返回失敗
===================================
舉例說明:
set solib-absolute-prefix /root/temp
set solib-search-path /home/evan:/home/peter
$PATH is /usr/sbin:/usr/bin
$LD_LIBRARY_PATH is /opt:/usr/games
那麼對絕對路徑"/lib/libc.so.6"的搜尋順序是:
1) A / X
/root/temp/lib/libc.so.6
2) R(A) / X
root/temp/lib/libc.so.6
3) Bn / R(A) / X
/home/evan/root/temp/lib/libc.so.6
/home/peter/root/temp/lib/lic.so.6
4) Bn / F(X)
/home/evan/libc.so.6
/home/peter/libc.so.6
5) $PATH / R(A) / X
/usr/sbin/root/temp/lib/libc.so.6
/usr/bin/roo/temp/lib/lic.so.6
6) $LD_LIBRARY_PATH / R(A) / X
/opt/root/temp/lib/libc.so.6
/usr/games/root/temp/lib/libc.so.6
對相對路徑"./libddd.so"的搜尋順序是
1) X
./libddd.so
2) Bn / X
/home/evan/./libddd.so
/home/peter/./libddd.so
3) Bn / F(X)
/home/evan/libddd.so
/home/peter/libddd.so
4) $PATH / X
/usr/sbin/./libddd.so
/usr/bin/./libddd.so
5) $LD_LIBRARY_PATH / X
/opt/./libddd.so
/usr/games/./libddd.so
從上面看到,對絕對路徑和相對路徑都有一步是採用檔名和solib-search-path拼接來查詢(絕對路徑的第4步和相對路徑的第3步),所以只要用set solib-search-path設定了每一個庫檔案所在的直接目錄,那麼就能保證每一個庫都能被找到。
4. 檢視so庫的載入路徑是否正確可使用info sharedlibrary命令,如果已找到對應的檔案則其From和To的載入地址會有值,並且右邊路徑顯示的就是載入檔案所在的地址,這個時候,如果so庫檔案含符號資訊,則syms Read的值為Yes,否則為No,如果未找到對應的檔案則From和To的地址為空,syms Read的值為No,此時右邊路徑顯示的是Coredump檔案中庫檔案路徑。
5. 如果在Coredump檔案載入過程中,或者info sharedlibrary命令時,出現" Cannot access memory at address 0x87000069 "這樣的錯誤,這通常是由於所使用的主執行檔案("file"命令或"exec-file"命令)與Coredump檔案("core"命令或"core-file"命令)兩者不匹配導致的。這個時候應檢查主執行檔案是否是生成Coredump時所用的主執行檔案,只要差一點,就可能導致動態庫資訊讀取錯誤。
6. 如果載入過程中有" warning: .dynamic section for "/lib/librt.so.1" is not at the expected address (wrong library or version mismatch?) "這樣的提示,這通常是庫搜尋路徑設定錯誤,GDB載入了錯誤的庫檔案導致的。這時,應使用info sharedlibrary命令檢視相應庫的載入路徑,並使用set sysroot或set solib-search-path修改搜尋路徑來將錯誤的庫修正到正確的路徑上。
7. 在設定了搜尋路路徑後,最好先用file命令載入主執行檔案,再用core命令載入Coredump檔案,這樣才能保證正確載入庫的符號表。否則,如果先用core命令載入Coredump檔案,再用file命令載入主執行檔案,那麼會造成庫只是被搜尋但並不載入符號(使用info sharedlibrary命令可以看到),這時再重新執行一次core命令就可以了。
8. 一個實際的搜尋例子:
當前目錄為/home
主執行檔案在/home/evan/gdbso/mips/gdbso
Core檔案在/home/evan/gdbso/mips/Coredump
所用動態庫與拷貝到主執行檔案同一目錄下
編譯主執行檔案所用的標準庫被拷貝到主執行檔案的lib目錄下/home/evan/gdbso/mips/lib/libxxx.so
進入GDB,用file命令載入主執行檔案:
evan@ubunu:/home$ mips-linux-gnu-gdb
...
(gdb) file evan/gdbso/mips/gdbso
Reading symbols from /home/evan/gdbso/mips/gdbso...done.
(gdb) info sharedlibrary
No shared libraries loaded at this time.
可以看到只載入了主執行檔案時,是無法得到動態庫資訊的。
再用core命令載入Coredump檔案:
(gdb) core evan/gdbso/mips/Coredump
...
warning: .dynamic section for "/lib/libc.so.6" is not at the expected address (wrong library or version mismatch?)
...
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x2aad98c0 0x2aadd6d8 Yes /lib/librt.so.1
0x2aaf3460 0x2ab0db98 Yes /lib/libm.so.6
0x2ab7e2e0 0x2ab89b28 Yes /lib/libpthread.so.0
0x2abba9a0 0x2acb2bd8 Yes /lib/libc.so.6
0x2ad06a40 0x2ad07988 Yes /lib/libdl.so.2
No /lib/ld.so.1
No ./libddd.so
(gdb)
在同時有了主執行檔案和Coredump檔案後,用info sharedlibrary就可以看到動態庫資訊了。但在載入過程中有庫版本不匹配的提示。通過info sharedlibrary也看到GDB錯誤地載入了系統中自帶的標準庫。
我們將絕對路徑設定到一個不存在的目錄來看看Coredump中儲存的原始路徑名:
(gdb) set sysroot /noexist
...
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
No /lib/librt.so.1
No /lib/libm.so.6
No /lib/libpthread.so.0
No /lib/libc.so.6
No /lib/libdl.so.2
No /lib/ld.so.1
No ./libddd.so
(gdb)
Coredump中儲存的原始路徑名為/lib/librt.so.1,為了讓GDB使用正確的庫/home/evan/gdbso/mips/lib/librt.so.1,只需要將絕對路徑字首設定為/home/evan/gdbso/mips即可,這裡設定為evan/gdbso/mips來演示效果:
(gdb) set sysroot evan/gdbso/mips
...
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x2aad98c0 0x2aade270 Yes evan/gdbso/mips/lib/librt.so.1
0x2aaf3110 0x2ab31b70 Yes evan/gdbso/mips/lib/libm.so.6
0x2ab7e320 0x2ab8e620 Yes evan/gdbso/mips/lib/libpthread.so.0
0x2abba6a0 0x2accc3f0 Yes evan/gdbso/mips/lib/libc.so.6
0x2ad06b50 0x2ad07c70 Yes evan/gdbso/mips/lib/libdl.so.2
0x2aaa8810 0x2aac2e40 Yes evan/gdbso/mips/lib/ld.so.1
No ./libddd.so
(gdb)
可以看到,GDB已經正確地載入了絕對路徑。但相對路徑"./libddd.so"還沒有找到,為了使用/home/evan/gdbso/mips/libddd.so,設定庫搜尋路徑包含/home/evan/gdbso/mips即可。為了檢視效果,這裡還添加了一個不存在的搜尋路徑:
(gdb) set solib-search-path /noexist:/home/evan/gdbso/mips
...
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x2aad98c0 0x2aade270 Yes evan/gdbso/mips/lib/librt.so.1
0x2aaf3110 0x2ab31b70 Yes evan/gdbso/mips/lib/libm.so.6
0x2ab7e320 0x2ab8e620 Yes evan/gdbso/mips/lib/libpthread.so.0
0x2abba6a0 0x2accc3f0 Yes evan/gdbso/mips/lib/libc.so.6
0x2ad06b50 0x2ad07c70 Yes evan/gdbso/mips/lib/libdl.so.2
0x2aaa8810 0x2aac2e40 Yes evan/gdbso/mips/lib/ld.so.1
0x2ad1a590 0x2ad1a770 Yes /home/evan/gdbso/mips/libddd.so
(gdb)
可以看到,所有的庫都找到正確的路徑了,Syms也被正確地載入。