用gdb調試程序時,一般的函數都可以step進去,可是C庫函數卻直接跳過了。
網上找了些資料,記錄一下!
1.安裝C庫的debug版本
[plain] view plain copy PRint?安裝完后,在/usr/lib目錄下會多出一個debug目錄,里面有安裝的debug版c庫的動態鏈接文件
2.編譯程序,使用debug版本C庫
例如程序test.c,使用如下命令編譯。
[html] view%20plain copy print?可以使用ldd%20test來查看是否使用了debug版c庫。我們可以比較前后的信息
使用debug版C庫輸出的信息:
[plain] view%20plain copy print?可以看出成功了!
3.調試
[plain] view%20plain copy print?
進入gdb后在相應位置下斷點,運行到該位置后,使用s,發現能進入c庫,但是找不到c庫 知道了對應的版本后,去glibc官網去下載吧:http://ftp.gnu.org/gnu/glibc/ 參考鏈接: http://blog.csdn.net/summerhust/article/details/5966751 時間:%202015-11-12%2010:38:00 最近在研究動態鏈接原理這塊,想通過GDB跟蹤動態鏈接器(ld-Linux.so.2)是如何工作的,發現Ubuntu提供的/usr/lib/debug并不能很好的工作,跟蹤進去后,發現源碼不是對不上,就是錯誤的,所以萌發了自己編譯C庫的想法,以下是我的操作記錄,歡迎指正。 開始前,需要確保你的磁盤剩余空間不小于3G空間,你不會想到編譯調試版本的C庫需要這么大的磁盤空間。 首先下載源碼,我的系統是Ubuntu%2015.10,使用sudo%20apt-get%20source%20libc6-dbg下載的C庫版本是glibc-2.21。我的源碼目錄是~/libc-dbg/glibc-2.2.1。 在INSTALL編譯安裝說明中說,C庫不能在源碼目錄安裝,所以我在home目錄下新建立了一個目錄用于編譯C庫,目錄為~/libc。又建立了一個~/lib目錄用于最后的C庫安裝目錄。 好,進入~/libc,輸入../libc-dbg/glibc-2.21/configure%20--prefix=/home/astrol/lib%20CFLAGS="-O1%20-g3%20-ggdb"%20CXXFLAGS="-O1%20-g3%20-ggdb"%20--disable-werror 注意,我為了調試,所以加了-g3%20-ggdb調試選項,-Ox是必須得,因為C庫必須要指定,還有最后的--disable-werror也是必須得,否則會將編譯過程中的很多警告信息歸為錯誤,那么就沒法繼續編譯了。這里我只是根據我自身的要求加的幾個選項,你也可以根據自己的需求自行添加,參考../libc-dbg/glibc-2.21/configure%20--help的提示幫助。 根據上面命令的結果提示,看能否通過,如果不行就盡量想辦法滿足它,比如在configure過程中提示我系統需要gawk,那么我就sudo%20apt-get%20install%20gawk來滿足它就OK了。 到了這里,就開始編譯吧,鍵入make,接下來就等吧,要很久的。 最后make%20install,就將編譯好的庫安裝到我指定的~/lib中。 進入~/lib,哬,文件還真多,咦,怎么沒有生成的庫呢,仔細一看,原來所有的庫都在子目錄lib下: 這些都是帶有符號信息的動態庫。 好了,我們寫個hello world看如何使用它們。 gcc -g -o hello hello.c 然后ldd hello,輸出如下 linux-gate.so.1 => (0xb7732000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7563000) /lib/ld-linux.so.2 (0x80080000) 看來這樣編譯不行,根本沒用上我編譯好的哪些庫,改變編譯參數 gcc -Wl,-rpath,/home/astrol/lib/lib -Wl,--dynamic-linker,/home/astrol/lib/lib/ld-linux.so.2 -g -o hello hello.c 或者gcc -Wl,-rpath=/home/astrol/lib/lib -Wl,--dynamic-linker=/home/astrol/lib/lib/ld-linux.so.2 -g -o hello hello.c,其實都是一樣的。 再ldd hello,輸出如下: linux-gate.so.1 => (0xb7732000) libc.so.6 => /home/astrol/lib/lib/libc.so.6 (0xb758a000) /home/astrol/lib/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x8001d000) 看來OK了,我們再使用readelf確認下,使用readelf --program-headers hello輸出: Elf file type is EXEC (Executable file)Entry point 0x8048340There are 9 program headers, starting at offset 52Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4 INTERP 0x000154 0x08048154 0x08048154 0x00023 0x00023 R 0x1 [Requesting program interpreter: /home/astrol/lib/lib/ld-linux.so.2] LOAD 0x000000 0x08048000 0x08048000 0x005f4 0x005f4 R E 0x1000 LOAD 0x000f00 0x08049f00 0x08049f00 0x00120 0x00124 RW 0x1000 看來都可以了。 現在使用gdb調試我們的hello。gdb hello -q進入調試。使用set verbose on打開gdb信息打印,可以更好的看到調試信息。 astrol@astrol:~/test$ gdb hello -qReading symbols from hello...done.(gdb) set verbose on(gdb) startTemporary breakpoint 1 at 0x804843c: file hello.c, line 5.Starting program: /home/astrol/test/helloReading symbols from /home/astrol/lib/lib/ld-linux.so.2...done.Reading symbols from system-supplied DSO at 0xb7fdd000...(no debugging symbols found)...done.Reading in symbols for dl-debug.c...done.Reading in symbols for rtld.c...done.Reading symbols from /home/astrol/lib/lib/libc.so.6...done.Temporary breakpoint 1, main () at hello.c:55 printf("hello world/n");(gdb) gdb成功加載了兩個庫和它們的符號信息。那么接下來的調試就能很好的繼續了。這里我演示下printf的工作過程,觀察下PLT的大致工作過程。 (gdb) disassemble /mDump of assembler code for function main:4 { 0x0804842b <+0>: lea 0x4(%esp),%ecx 0x0804842f <+4>: and $0xfffffff0,%esp 0x08048432 <+7>: pushl -0x4(%ecx) 0x08048435 <+10>: push %ebp 0x08048436 <+11>: mov %esp,%ebp 0x08048438 <+13>: push %ecx 0x08048439 <+14>: sub $0x4,%esp5 printf("hello world/n");=> 0x0804843c <+17>: sub $0xc,%esp 0x0804843f <+20>: push $0x80484e0 0x08048444 <+25>: call 0x8048300 <puts@plt> 0x08048449 <+30>: add $0x10,%esp6 return 0; 0x0804844c <+33>: mov $0x0,%eax7 } 0x08048451 <+38>: mov -0x4(%ebp),%ecx 0x08048454 <+41>: leave 0x08048455 <+42>: lea -0x4(%ecx),%esp 0x08048458 <+45>: retEnd of assembler dump. 地址0x8048300就是puts的PLT入口處。跟蹤進去 (gdb) disassemble /m 0x8048300Dump of assembler code for function puts@plt: 0x08048300 <+0>: jmp *0x804a00c 0x08048306 <+6>: push $0x0 0x0804830b <+11>: jmp 0x80482f0End of assembler dump. 繼續跟進,最后jmp到0x80482f0,可以通過x命令看到0x80482f0處的指令如下: (gdb) x/3i $eip=> 0x80482f0: pushl 0x804a004 0x80482f6: jmp *0x804a008 0x80482fc: add %al,(%eax) 繼續jmp到*0x804a008,這就是_dl_runtime_resolve函數的地址,它是最終進入_dl_fixup函數的“跳板”。繼續跟進,看最后進入_dl_fixup函數后效果如何。 最終進入_dl_fixup函數后,發現是很正常的,gdb能很好的進行源碼級調試,不會出現Ubuntu提供的/usr/lib/debug出現的哪些情況了,即行號和源碼是一一對應的。 好了,本文就到此結束吧。ll /usr/lib/debug/i386-linux-gnu/libc*
新聞熱點
疑難解答