定位C程序内存错误

来源:互联网 发布:二手手机价格评估软件 编辑:程序博客网 时间:2024/06/06 03:26

这些信息是 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
-rdynamic
Pass 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(电网), dmalloc, MEMWATCH(mastering linux debugging techniques): malloc调用

 * ltrace:  "dynamic library call"

* nm, objdump:  obj文件信息,信息太繁琐了, 也许要不得以而用之。 没有用过这两个工具。

7。其它

smash the stack

gcc与obj文件,动态链接文件和ELF文件

原创粉丝点击