$cat dyn.c
{
puts ("Hello.");
}
$cat main.c
{
puts ("before");
dyn ();
puts ("after");
}
$cat Makefile
gcc -g -save-temps -c main.c -o main.o
gcc -g -save-temps -c -fpic -ffunction-sections dyn.c
gcc -g -save-temps -shared dyn.o -o libdyn.so
gcc -g -save-temps main.o libdyn.so -o main
clean:
rm -rf *.o *.so main
下面我們就來編譯和調試程序:
$make main 這之後就會在當前目錄下生成我們所要的libdyn.so 和 main程序
如果我們嚮往常一樣直接輸入 $./main 來執行程序,這是不行的,它會給我們這樣的錯誤提示:
before
./main: relocation error: ./main: undefined symbol: dyn
爲什麼?原來是我們使用了共享庫libdyn.so卻沒有告訴動態鏈接程序到哪裏去找他!好,這回我們告訴它:
$LD_LIBRARY_PATH=`pwd` ./main
before
Hello.
after
怎麼樣,出現我們想要的結果了吧。
以上這些是一些小兒科啦,牛人們不要笑話啊,下面纔是這次要講的。通常情況下我們使用gdb進行調試的時候:
$ gdb -q main
Breakpoint 1 at 0x8048478: file main.c, line 3.
(gdb) r
Starting program: /home/lirui/Test/main
Breakpoint 1, main () at main.c:3
3 puts ("before");
(gdb) next
before
4 dyn ();
(gdb)
/home/lirui/Test/main: relocation error: /home/lirui/Test/main: undefined symbol: dyn
Program exited with code 0177.
(gdb)
方法一:設定gdb環境變量 LD_PRELOAD,在執行程序前先把共享庫代碼load進來不就能找到了嗎
$gdb -q main
(gdb) break dyn
Breakpoint 1 at 0x80483a8
(gdb) run
Starting program: /home/lirui/Test/main
Breakpoint 1 at 0x400176ff: file dyn.c, line 3.
before
Breakpoint 1, dyn () at dyn.c:3
3 puts ("Hello.");
(gdb) list
1 dyn ()
2 {
3 puts ("Hello.");
4 }
(gdb)
方法二:如果你使用的gdb版本中對”pending breakpoint"提供支持(V6.3當中就有支持),那麼恭喜你,你可以先設定一個pending breakpoint,然後有gdb來決定到什麼時候這個斷點起作用。這裏面有一點必須注意,你必須指定你的鏈接庫的位置,可以通過設定環境變量LD_LIBRARY_PATH來實現。在執行gdb之前,我們可以這樣做: $ export LD_LIBARY_PATH=`pwd`,告訴gdb在當前目錄下查找共享庫文件,然後嚮往常一樣調試程序就可以了:
$ export LD_LIBRARY_PATH=`pwd`
$ gdb -q main
(gdb) b dyn
Function "dyn" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (dyn) pending.
(gdb) r
Starting program: /home/lirui/Test/tmp/main
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xffffe000
Breakpoint 2 at 0xb7f4853a: file dyn.c, line 3.
Pending breakpoint "dyn" resolved
before
Breakpoint 2, dyn () at dyn.c:3
3 puts ("Hello.");
(gdb) l
1 dyn ()
2 {
3 puts ("Hello.");
4 }
(gdb)
方法三:這種情況只針對你要調試的程序整個就是一個動態鏈接的可執行程序,它在load到內存之後,入口地址都是動態變化的,如果你使用gdb進行調試,最初的時候你用 b function_name的話,它把斷點設在了以0x0爲基址的offset上,而程序load到內存之後,這個基址已經變了,所以總是不能設置成功斷點。(我在調試qemu的時候就遇到這種情況),怎麼辦呢?最簡單的方法就是不把這個程序編譯成可重載的,像普通程序一樣去編譯它,不要爲gcc 添加 -wl,-shared等參數就行了,這時候編譯出來的就很容易調試了。
針對這種情況,我覺得還應該有一個辦法,但是我沒有試驗成功,就是在使用gdb的時候,最初不要把符號表load進來,如果已經load進來的話,就使用 symbol-file命令(後面不加參數)把已經載入的符號表丟棄掉,這時候再使用add_symbol_file filename start_address命令把符號表從filename中載入到以start_address開始的地址當中,就可以調試了。我以這種方法試驗過不適用共享庫的程序,是可以的,因爲我們知道一般程序的開始地址是0x8048000,但是對於共享對象,我們不知道它的開始地址是多少,所以就不好辦了,如果有一種辦法能夠告訴我們它的.text的起始地址,我們就很容易以這種方法來做了。
應該還有其他的方法吧,如果你知道,請告訴我 [email protected],多謝!