[GDB] 在 dlopen/dlclose 函式設定中斷點,觀察 DLL 的載入與卸載過稱
今天在查一個 Linux 上的 shared library 沒有釋放掉的問題,
從程式來看,應該呼叫 dlopen() 和 dlclose() 的次數是相當的,
不過因為中間牽扯到其他的 .so 檔,有點不太能肯定會不會有什麼例外,
想說有沒有方法可以確定 dlopen 和 dlclose 各被呼叫了幾次呢?
後來想到用 gdb 在 dlopen/dlclose 上設中斷點的方式來觀察~
像下面的例子就是在 dlopen 上設了中斷點後,繼續執行程式,
但最好是在接近要觀察的點再打開這個中斷點,不然程式一開始可是會載入非常多的 .so 的:
(gdb) b dlopen Breakpoint 2 at 0x7fffdf376b50 (10 locations) (gdb) c Continuing.
當 dlopen 被呼叫到時,由它的參數可以得知,第一個參數就是 DLL 的名稱,
因此可以秀出 rdi 指到的字串內容,就是 DLL 的名稱了 (本例中的 7z.so):
Breakpoint 2, 0x00007fffdf376b50 in dlopen@plt () from /lib/lib7zip.so.0 (gdb) x/s $rdi 0x1be7468: "7z.so"
按 c 繼續程式的執行,下一次的 dlopen() 開啟的是 Rar.so:
(gdb) c Continuing. Breakpoint 2, 0x00007fffdf376b50 in dlopen@plt () from /lib/lib7zip.so.0 (gdb) x/s $rdi 0x1d14d48: "/p7zip/Codecs/Rar.so"
不過每次停在中斷點時都得自己打 x/s $di 有點麻煩,
其實可以利用 gdb 可以在中斷點上加上命令的功能,自動執行某些指令~
用 command <中斷點號碼> 這個指令,接下來就可以打想要執行的指令,最後 end 結束~
像我先用 x/s $rdi 印出第一個參數 (DLL 名稱),
接著用 fin (finish) 執行完 dlopen() 後,再用 p/x $rax 印出函式的回傳值:
(gdb) command 2 Type commands for breakpoint(s) 2, one per line. End with a line saying just "end". >x/s $rdi >fin >p/x $rax >end
重新執行程式,當 dlopen 的中斷點彈出時,的確自動秀出 DLL 的名稱 7z.so 了,
而且 fin 指令也執行了,但卻沒有執行 p/x 指令秀出 dlopen 的回傳值…
這一點還不知道為什麼… 只好手動自己打了,傳回的 handle 是 0x1daf3a0:
Breakpoint 2, 0x00007fffdf376b50 in dlopen@plt () from /lib/lib7zip.so.0 0x1c87a78: "7z.so" Load7ZLibrary (name=L"7z") at OSFunctions_UnixLike.cpp:173 173 if (pHandler) (gdb) p/x $rax $10 = 0x1daf3a0 (gdb) c Continuing.
繼續執行,接下來 dlopen 開啟的是 Rar.so,handle 是 0x1c05ef0:
Breakpoint 2, 0x00007fffdf376b50 in dlopen@plt () from /lib/lib7zip.so.0 0x1c03e08: "/p7zip/Codecs/Rar.so" myselect (pDir=pDir@entry=0x1dfa3b0) at OSFunctions_UnixLike.cpp:112 112 if (pHandler != NULL) (gdb) p/x $rax $11 = 0x1c05ef0 (gdb) c Continuing.
同樣在 dlclose 上設定中斷點,可以看到第一個要關閉的 handle 是 0x1c05ef0,
這也就是剛剛開啟 Rar.so 得到的 handle,
而第二個關閉的就是 7z.so 的 handle 0x1daf3a0:
Breakpoint 3, __dlclose (handle=0x1c05ef0) at dlclose.c:43 43 if (__builtin_expect (_dlfcn_hook != NULL, 0)) (gdb) c Continuing. Breakpoint 3, __dlclose (handle=0x1daf3a0) at dlclose.c:43 43 if (__builtin_expect (_dlfcn_hook != NULL, 0))
運用上述的方法,就可以確認 dlopen/dlclose 是否有如自己預期的在運作囉~
參考資料:
stackoverflow: Inspect the return value of a function in gdb
stackoverflow: Do specific action when certain breakpoint hits in gdb