定位C程序內存錯誤

這些信息是 ubuntu 7.10 , gcc 4.1.3, gdb 6.6-debian 跟蹤的

當printf不夠有效時。。。。

1。讀懂錯誤信息

一個簡單的例子;

#include <stdio.h>

#include <stdlib.h>

void a(){

char *s = "ee";

free(s);

}

int main(int argc, char **argv){

a();

return 1;

}

程序運行可以得到一下錯誤信息:

 *** glibc detected *** ./r: free(): invalid pointer: 0x0804865c ***
  ======= Backtrace: =========
 /lib/tls/i686/cmov/libc.so.6[0xb7e2dd65]
 /lib/tls/i686/cmov/libc.so.6(cfree+0x90)[0xb7e31800]
 ./r[0x804838c]
 ./r[0x80483a4]
 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe0)[0xb7dda050]
 ./r[0x8048311]
 ======= Memory map: ========
 08048000-08049000 r-xp 00000000 08:05 1047431    /home/jing/Desktop/ee/r
 08049000-0804a000 rw-p 00000000 08:05 1047431    /home/jing/Desktop/ee/r
 0804a000-0806b000 rw-p 0804a000 00:00 0          [heap]
 b7db8000-b7dc2000 r-xp 00000000 08:05 3499334    /lib/libgcc_s.so.1
 b7dc2000-b7dc3000 rw-p 0000a000 08:05 3499334    /lib/libgcc_s.so.1
 b7dc3000-b7dc4000 rw-p b7dc3000 00:00 0
 b7dc4000-b7f08000 r-xp 00000000 08:05 3532035    /lib/tls/i686/cmov/libc-2.6.1.so
 b7f08000-b7f09000 r--p 00143000 08:05 3532035    /lib/tls/i686/cmov/libc-2.6.1.so
 b7f09000-b7f0b000 rw-p 00144000 08:05 3532035    /lib/tls/i686/cmov/libc-2.6.1.so
 b7f0b000-b7f0e000 rw-p b7f0b000 00:00 0
 b7f22000-b7f24000 rw-p b7f22000 00:00 0
 b7f24000-b7f3e000 r-xp 00000000 08:05 3499469    /lib/ld-2.6.1.so
 b7f3e000-b7f40000 rw-p 00019000 08:05 3499469    /lib/ld-2.6.1.so
 bff2d000-bff42000 rw-p bff2d000 00:00 0          [stack]
 ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]
 Aborted (core dumped)

backtrace 提供函數的調用情況。

memroy map 是程序分配是虛擬內存的分配情況, 它的格式在各個操作系統下是不同的。 以上信息是linux下的運行。實際上輸出的是/proc/pid/maps的信息。

(這個圖在我的瀏覽器下顯示不正常,可以點擊memory map的連接查看源)

40049000-4035c000 r-xp 00000000 03:05 824473  /jdk1.5/jre/lib/i386/client/libjvm.so
|<------------->|  ^      ^       ^     ^        |<----------------------------------->|
  Memory region    |      |       |     |                           |
                   |      |       |     |                           |
  Permission   --- +      |       |     |                           |
    r: read               |       |     |                           |
    w: write              |       |     |                           |
    x: execute            |       |     |                           |
    p: private            |       |     |                           |
    s: share              |       |     |                           |
                          |       |     |                           |
  File offset   ----------+       |     |                           |
                                  |     |                           |
  Major ID and minor ID of -------+     |                           |
  the device where the file             |                           |
  is located (i.e. /dev/hda5)           |                           |
                                        |                           |
  inode number  ------------------------+                           |
                                                                    |
  File name  -------------------------------------------------------+
 代碼段(code segement)的權限是r-xp,  數據段(data segment)是rw-p
如果能馬上明白錯在哪裏就好了, 如果不能馬上找出錯誤, 就需要一些技巧快速找到問題的所在。
2. 找出出錯的函數
鏈接程序時用到 -rdynamic 選項
gcc -g -o memleak memleak.c -rdynamic -ldl
-rdynamicPass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of dlopen or to allow obtaining backtraces from within a program.

-ldl 可以查看man dlopen (我不怎麼明白這個dynamic linking 的實質.)

運行, 報錯, 可以得到帶符號表的信息:

 ======= Backtrace: =========
 /lib/tls/i686/cmov/libc.so.6[0xb7dfed65]
 /lib/tls/i686/cmov/libc.so.6(cfree+0x90)[0xb7e02800]
 ./r(a+0x18)[0x804859c]
 ./r(main+0x16)[0x80485b4]
 /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe0)[0xb7dab050]

從這裏可以看出, a()的 free()調用錯誤了。

3. stack trace定位錯誤到行

如果一個函數被多次調用, 函數名就不足以區分到底是哪個調用出問題了。下面的方法可以試一下:

  1. 調用gdb調試
  2. 設置對出錯函數的斷點, 比如b free
  3. 到達斷點後, 命令: bt, 查看函數調用情況, 就可以找出是哪個函數被調用, 直到找到出錯的調用

 * 把每一個函數檢查一遍是很麻煩的, 可以捕捉系統發出的錯誤信號,

#include <stdio.h>
#include 
<stdlib.h>
#include 
<string.h>
#include 
<signal.h>

void a(){

  
char *= "33";

  
char *= NULL;
  strcpy(d,s);
   
   free(s);
 
return;
}


void sighandler(){
}


int main(int argc, char **argv){
signal(SIGSEGV, sighandler);
  a(); 
  
return 1;
}

 調試的時候, b sighandler, 然後bt.

但捕捉sigsegv是危險的,不能無限的捕捉, 不然無限循環了。捕捉一次就夠。

5. 或者gdb下,在程序被強制退出後, 試一下list命令

6。工具:valgrind

內存差錯的工具很多,valgrind是最強大的一個。有些工具提供一個庫,連接到源代碼,完了還要改回去,不方便。

有些工具檢查一些特定的調用:

* strace: 系統調用, 比如文件打開,關閉

* mtrace, electronice fence(電網), dmallocMEMWATCH(mastering linux debugging techniques): malloc調用

 * ltrace:  "dynamic library call"

* nm, objdump:  obj文件信息,信息太繁瑣了, 也許要不得以而用之。 沒有用過這兩個工具。

7。其它

smash the stack

gcc與obj文件,動態鏈接文件和ELF文件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章