2012年1月9日 星期一

追踪 glibc 裡的程式

這篇是一堆試誤心得的中間記錄, 使用的版本是 Ubuntu 11.04。

失敗的作法

  • 在 link 時, 用 -L/usr/lib/debug/lib/x86_64-linux-gnu/ 改變 link 到的 libc.so, 但沒有效果。用 strace -e open 觀察 gcc 做的事, 發現是因為 /usr/lib/debug/lib/x86_64-linux-gnu/ 下沒有 libc.so, 而是 libc-2.13.so。之前沒學清楚 -L 和 -l 的細節, 耍笨。
  • man ld.so 得知可用 LD_LIBRARY_PATH 或 LD_PRELOAD 在執行期換掉 libc.so, 但是也沒有效果。用 LD_PRELOAD 換成 debug 版 libc.so 時, 跑 gdb 會 segmentation fault

成功的作法

前置作業

  • $ sudo aptitude install libc6-dbg # 取得有 debug symbol 的 libc.so
  • $ apt-get source libc6-dev # 取得原始碼目錄 eglibc-2.13

執行

  1. $ gcc myprog.c -g -o myprog
  2. $ gdb myprog
  3. $ directory /path/to/eglibc-2.13/stdio-common/
  4. $ start # 跑到 main 就停下來

然後 gdb 會神奇地去找含 debug symbol 的 libc, 後面就可以用 step 進入 glibc 的函式。不知這個行為寫在那裡, 或許可以從 gdb 原始碼找出來吧。

若有進入但說找不到原始碼, 表示沒有告知 gdb 正確的原始碼位置, 到 eglibc-2.13 下找一找, 再回來用 directory 設位置。

另外在用到 sqrt()、log() 時也是如此, 照一樣的編法 gcc myprog.c -g -lm -o myprog, 然後在 start 後, gdb 會去找 debug 版的 libm.so。不過要記得多執行 directory /path/to/eglibc-2.13/math 載入 math 的原始碼, gdb 才能列出原本的程式。用 ldd 觀察 myprog 也驗證原本的執行檔將 libc 和 libm 連到沒有 debug symbol 的版本。

另外試了直接和 debug 版的 libc.so 或 libm.so 編在一起 (gcc myprog.c -g /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.13.so), 但是一跑就會 segmentation fault。

另外有些函式好像是用組語寫的, 看不懂它們的行為, step into sqrt 沒有效果。

結論

要觀察 glibc 的行為, 要做以下的事:

  • 裝 libc6-dbg, 取得含 debug symbol 的 shared lib
  • 用 apt-get source libc6-dev 取得原始碼。由於 glibc 裡有多個 shared lib, 要先 grep 找看看想觀察的程式放在那個目錄下, 跑 gdb 時再用 directory 載入該目錄, 相對路徑才會對。

2012-01-10 更新: 補充觀察 gdb 找 debug lib 的行為

一樣可以用老招 strace -e open 跑 gdb 看出背後發生的事, 以下是沒有裝 libc6-dbg 跑出的訊息:

$ strace -e open -o gdb.trace gdb myprog
然後執行 tail -f gdb.trace | grep libc 觀察行為。

以下是執行 start 以前的訊息:

open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
open("/lib/libcrypto.so.0.9.8", O_RDONLY) = 3
open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_US.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_US.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)

以下是執行 start 以後的訊息:

open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 6
open("/lib/x86_64-linux-gnu/libc-2.13.so", O_RDONLY) = 7
open("/lib/x86_64-linux-gnu/.debug/libc-2.13.so", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug//lib/x86_64-linux-gnu/libc-2.13.so", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.13.so", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc-2.13.so-gdb.py", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.13.so-gdb.py", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/gdb/auto-load/lib/x86_64-linux-gnu/libc-2.13.so-gdb.py", O_RDONLY) = -1 ENOENT (No such file or directory)
可以看出 gdb 不論如何, 都會試著載入 debug 版的函式庫, 來執行目標程式。找不到的時候, 自然就是用沒有 debug symbol 的函式庫。

2012-01-22 更新

《gdb 如何找到 debug symbol》有進一步說明。

2016-02-16 更新

若 Ubuntu 沒有附 X-dbg 的 package,可以自己編原始碼裝進去,詳見 自行編譯含 debug symbol 的套件 (package)

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...