Linux下 coredump 定位方法说明
Linux下 coredump 定位方法说明
一. Linux 下 cordump 机制
Linux 系统下存在一种 coredump 机制。
当程序运行过程中异常终止或崩溃,Linux 操作系统会将程序当时的堆栈等内存状态信息记录下来,会在指定文件下生成一个 coredump 相关的 Log 文件。软件开发人员可以通过对 coredump文件进行分析,即可定位到导致程序运行崩溃的 bug。
当程序访问的内存超过了系统给定的内存空间,就会产生 Segmentation fault(core dumped),所以,一般产生段错误的原因有如下:
访问了不存在的内存地址,访问了系统保护的内存地址,数组或堆空间访问越界,,多线程程序使用了线程不安全的函数。多线程读写的数据未加锁保护等等一系列内存非法访问操作。
二. coredump 文件定位方法
根据上一篇配置 coredump 机制的方法,成功配置好之后。就可以运行代码,当程序运行中崩溃时即可产生相应的 coredump 文件。
在进行Coredump分析时,以下是一些基本的技巧:
- 查看函数调用栈: 使用 bt 命令可以查看崩溃时的函数调用栈,找到出错的函数。
- 查看变量值: 使用 info 命令可以查看 寄存器值等,找到出错的变量。
- 使用gdb调试时进行更深入的分析: GDB提供了很多命令,比如 p(打印变量值)、x(查看内存)、watch(设置变量监视器)等,可以帮助开发者进行更深入的分析。熟练掌握这些命令可以提高分析效率。
- 使用符号表: 如果程序是使用编译器编译的,那么可以使用符号表来查看函数名、变量名等信息。使用以下命令生成符号表(以下命令针对 c++):
g++ -g <program_name>.cpp -o <program_name>
其中,-g选项表示生成符号表。生成符号表后,可以在gdb调试中查看函数名、变量名等信息。
这里为了方便起见,不更改 coredump 文件的生成路径,即生成的 coredump 文件会与可执行程序在同一目录下,文件默认叫 core 的文件。
下面以一段测试代码说明一下,Linux 环境下 使用 gdb 调试器对 coredump 文件进行分析调试。
代码如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int fun(void)
{
printf("---fun()\n");
char buffer[3] = {0};
memcpy(buffer, "Hello", 6);
printf("---buffer: %s\n", buffer);
return 0;
}
int main(void)
{
printf("---main()---\n");
int pid = 0;
pid = getpid();
printf("pid %d\n", pid);
fun();
printf("---End main\n");
return 0;
}
1. 编译运行。操作如下:
编译代码。输入 gcc -g main.c -o main.out 命令
运行程序。输入 ./main.out 命令。如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ ./main.out
---main()---
pid 3814
---fun()
---buffer: Hello
*** stack smashing detected ***: terminated
已放弃 (核心已转储)
当程序出现段错误时崩溃时,可查看程序当前目录下是否生成 coredump 相关文件。如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ ls -l
总用量 180
-rw------- 1 wangtian wangtian 393216 12月 13 16:55 core
drwxrwxr-x 2 wangtian wangtian 4096 11月 25 22:24 debug
-rw-rw-r-- 1 wangtian wangtian 90 10月 28 13:32 debug.c
-rw-rw-r-- 1 wangtian wangtian 81 10月 20 18:12 debug.h
-rw-rw-r-- 1 wangtian wangtian 5688 12月 13 16:55 debug.o
-rw-rw-r-- 1 wangtian wangtian 356 12月 13 16:55 main.c
-rw-rw-r-- 1 wangtian wangtian 7144 12月 13 16:55 main.o
-rwxrwxr-x 1 wangtian wangtian 21144 12月 13 16:55 main.out
2. gdb 调试 coredump 文件
查看 coredump 文件。输入" objdump -t +coredump文件名" 命令:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ objdump -t core
core: 文件格式 elf64-x86-64
SYMBOL TABLE:
无符号
可以看到,coredump 文件是不带符号表的。所以调试时,需要同时加上 可执行程序文件。
输入命令 gdb main.out core 进行调试,如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ gdb main.out core
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main.out...
[New LWP 4272]
Core was generated by `./main.out'.
Program terminated with signal SIGABRT, Aborted.
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: 没有那个文件或目录.
可以看到,程序收到 signal SIGABRT 后崩溃了。
输入 bt (backtrace 缩写) 命令,可以查看下程序崩溃时的堆栈信息,如下所示:
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ff8d6f5c859 in __GI_abort () at abort.c:79
#2 0x00007ff8d6fc729e in __libc_message (action=action@entry=do_abort,
fmt=fmt@entry=0x7ff8d70f108f "*** %s ***: terminated\n")
at ../sysdeps/posix/libc_fatal.c:155
#3 0x00007ff8d7069aea in __GI___fortify_fail (
msg=msg@entry=0x7ff8d70f1077 "stack smashing detected") at fortify_fail.c:26
#4 0x00007ff8d7069ab6 in __stack_chk_fail () at stack_chk_fail.c:24
#5 0x000056183642c243 in fun () at main.c:12
#6 0x000056183642c28c in main () at main.c:20
以上的堆栈信息,显示 __stack_chk_fail () 错误。可以看到调用 fun() 函数过程中发生了栈问题。
__stack_chk_fail () 打印信息说明发生了缓冲区溢出,即数组越界问题。
从这段栈信息中并不能直观的定位到出现问题的地方,因为出现问题的地方是之前的数组越界,但是这里的栈信息已经执行到主函数 main中, 特别是在大项目中更是无法准确的定位出错的地方。
越界写坏内存,什么时候会 crash 取决于我们写坏的内存什么时候被用到,有时候会离犯罪现场特别远。所以,导致定位这样情况的崩溃问题比较麻烦。
在发生__stack_chk_fail () 错误的附近函数 fun() 中第11 行打断点,如下所示:
(gdb) b main.c:11
Breakpoint 1 at 0x56183642c22a: file main.c, line 11.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000056183642c22a in fun at main.c:11
(gdb) r
Starting program: /home/wangtian/Code_Learns/C_Learns/debug/debug2/main.out
---main()---
pid 3868
---fun()
---buffer: Hello
Breakpoint 1, fun () at main.c:11
11 return 0;
(gdb) n
12 }
(gdb)
*** stack smashing detected ***: terminated
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: 没有那个文件或目录.
(gdb)
初步可以定位到,是在函数 fun() 内部发生了数组越界问题。目前还不能定位到具体某一行。
canary 栈溢出保护机制的具体原理: gcc 编译器默认开启了 canary 栈保护机制。canray 栈保护机制是专门针对栈溢出攻击涉及的一中保护机制。由于栈溢出攻击的主要目标是通过溢出覆盖函数栈高位的返回地址,因此,其思路是在函数开始执行前,即返回地址前写入一个字长的随机数据(canary),在函数返回前校验该值是否被改变,如果改变则认为是栈溢出,程序直接终止,以此来防止信息泄露。
总结:
以上一系列的操作,说明通过 gdb 对 数据越界问题的调试定位,这种方法不是太好用。
原因:越界写坏内存,什么时候会 crash 取决于我们写坏的内存什么时候被用到,有时候会离犯罪现场特别远。所以,导致定位这样情况的崩溃问题比较麻烦。后续文章再针对数组越界问题定位方法进行新的总结。
更多推荐
所有评论(0)