1. 生成 core 文件
- 网上很多教程,我这里举一种
- 临时开启 生成 core 文件
bash
# 0 就是没有开 coredump 功能
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# ulimit -c
0
# 设置值临时为 unlimited
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# ulimit -c unlimited
# 检查值为 unlimited
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# ulimit -c
unlimited
# 可以看到下面的字段 core file size 的值为 unlimited
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 31282
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 31282
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
- 修改保存的文件位置
bash
vim /etc/sysctl.conf
# 添加下面两行
kernel.core_pattern=/var/core/core_%e_%p
kernel.core_uses_pid=0
# 使修改生效
sysctl -p /etc/sysctl.conf
# 创建文件夹 /var/core
mkdir -p /var/core
2. 创建一个例子
cpp
// demo1.cc
#include <iostream>
using namespace std;
int main()
{
int *p = nullptr;
cout << *p << endl;
return 0;
}
- 编译、运行,会报 (core dumped)
bash
g++ -o demo1 demo1.cc
./demo1
Segmentation fault (core dumped)
- 查看生成的 core_dump 文件:
bash
# 拷到当前目录
cp /var/core/core_demo1_5392 .
# 查看文件
ls
core_demo1_5392 demo1 demo1.cc
- 使用 gdb 分析,可以看到直接报
Segmentation fault
。
bash
gdb demo1 core_demo1_5392
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo1...(no debugging symbols found)...done.
[New LWP 5392]
Core was generated by `./demo1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00005555a0b4089e in main ()
(gdb)
-
分析:
可以看到,
#0 0x00005555a0b4089e in main ()
指出,是在 main 函数的地址0x00005555a0b4089e
出现错误。 -
继续分析:
bash
# 使用 bt (back trace 回溯),看到是上面的问题
(gdb) bt
#0 0x00005555a0b4089e in main ()
# 使用 disassemble 地址, +line 命令,查看报错的上下文,看到箭头 => 就是出错的地方, => 0x00005555a0b4089e <main+20>: mov (%rax),%eax
(gdb) disassemble 0x00005555a0b40890, +60
Dump of assembler code from 0x5555a0b40890 to 0x5555a0b408cc:
0x00005555a0b40890 <main+6>: in (%dx),%al
0x00005555a0b40891 <main+7>: adc %cl,-0x39(%rax)
0x00005555a0b40894 <main+10>: rex.RB clc
0x00005555a0b40896 <main+12>: add %al,(%rax)
0x00005555a0b40898 <main+14>: add %al,(%rax)
0x00005555a0b4089a <main+16>: mov -0x8(%rbp),%rax
=> 0x00005555a0b4089e <main+20>: mov (%rax),%eax
0x00005555a0b408a0 <main+22>: mov %eax,%esi
0x00005555a0b408a2 <main+24>: lea 0x200777(%rip),%rdi # 0x5555a0d41020 <_ZSt4cout@@GLIBCXX_3.4>
0x00005555a0b408a9 <main+31>: callq 0x5555a0b40760 <_ZNSolsEi@plt>
0x00005555a0b408ae <main+36>: mov %rax,%rdx
0x00005555a0b408b1 <main+39>: mov 0x200718(%rip),%rax # 0x5555a0d40fd0
0x00005555a0b408b8 <main+46>: mov %rax,%rsi
0x00005555a0b408bb <main+49>: mov %rdx,%rdi
0x00005555a0b408be <main+52>: callq 0x5555a0b40740 <_ZNSolsEPFRSoS_E@plt>
0x00005555a0b408c3 <main+57>: mov $0x0,%eax
0x00005555a0b408c8 <main+62>: leaveq
0x00005555a0b408c9 <main+63>: retq
0x00005555a0b408ca <_Z41__static_initialization_and_destruction_0ii+0>: push %rbp
0x00005555a0b408cb <_Z41__static_initialization_and_destruction_0ii+1>: mov %rsp,%rbp
End of assembler dump.
# 使用 info reg 查看寄存器的值,可以看到 rax 的值是 0,mov (%rax),%eax 汇编指的是 对 rax 存的地址进行取值,rax 是 0 即空指针,所以这是踩空指针。
(gdb) info reg
rax 0x0 0
rbx 0x0 0
rcx 0xa0 160
rdx 0x7ffdd25abf58 140728132616024
rsi 0x7ffdd25abf48 140728132616008
rdi 0x1 1
rbp 0x7ffdd25abe60 0x7ffdd25abe60
rsp 0x7ffdd25abe50 0x7ffdd25abe50
r8 0x7f5f3311bd80 140046855421312
r9 0x0 0
r10 0x6 6
r11 0x7f5f3324af30 140046856662832
r12 0x5555a0b40780 93826256734080
r13 0x7ffdd25abf40 140728132616000
r14 0x0 0
r15 0x0 0
rip 0x5555a0b4089e 0x5555a0b4089e <main+20>
eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
分析看上面的 #
注释
- 对源程序进行反汇编
bash
objdump -s -d demo1 > demo1.S
- demo1.S 的重要部分,我们看到上面 gdb 出来的结果:出错的语句在
0x00005555a0b4089e <main+20>: mov (%rax),%eax
我们没看到0x00005555a0b4089e
,但是看到了<main+20>
,我们看到反汇编出来的main
在0x000000000000088a
,将它的位置偏移20
得到:0x89e
所以得到:89e: 8b 00 mov (%rax),%eax
c
0000000000000880 <frame_dummy>:
880: 55 push %rbp
881: 48 89 e5 mov %rsp,%rbp
884: 5d pop %rbp
885: e9 66 ff ff ff jmpq 7f0 <register_tm_clones>
000000000000088a <main>:
88a: 55 push %rbp
88b: 48 89 e5 mov %rsp,%rbp
88e: 48 83 ec 10 sub $0x10,%rsp
892: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
899: 00
89a: 48 8b 45 f8 mov -0x8(%rbp),%rax
89e: 8b 00 mov (%rax),%eax // 也就是这一行,可以看出它的形式跟 gdb 分析出来的也是一样的
8a0: 89 c6 mov %eax,%esi
8a2: 48 8d 3d 77 07 20 00 lea 0x200777(%rip),%rdi # 201020 <_ZSt4cout@@GLIBCXX_3.4>
8a9: e8 b2 fe ff ff callq 760 <_ZNSolsEi@plt>
8ae: 48 89 c2 mov %rax,%rdx
8b1: 48 8b 05 18 07 20 00 mov 0x200718(%rip),%rax # 200fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
8b8: 48 89 c6 mov %rax,%rsi
8bb: 48 89 d7 mov %rdx,%rdi
8be: e8 7d fe ff ff callq 740 <_ZNSolsEPFRSoS_E@plt>
8c3: b8 00 00 00 00 mov $0x0,%eax
8c8: c9 leaveq
8c9: c3 retq
根据汇编,如果熟悉,可以直接定位到代码,如果不熟,那么从上面基本可以定位到是哪个函数,可以加汇编标记定位语句
cpp
#include <iostream>
using namespace std;
int main()
{
// 添加特殊的数字 $0x11111111 这个用不到的数字作为标记,记录是在第几行
__asm__("sub $0x11111111,%rsp");
int *p = nullptr;
__asm__("sub $0x22222222,%rsp");
cout << *p << endl;
__asm__("sub $0x33333333,%rsp");
return 0;
}
重新编译得到的汇编如下,可以看到刚刚那行出问题的那一行,看到在 $0x22222222
和 $0x33333333
之间:
c
0000000000000880 <frame_dummy>:
880: 55 push %rbp
881: 48 89 e5 mov %rsp,%rbp
884: 5d pop %rbp
885: e9 66 ff ff ff jmpq 7f0 <register_tm_clones>
000000000000088a <main>:
88a: 55 push %rbp
88b: 48 89 e5 mov %rsp,%rbp
88e: 48 83 ec 10 sub $0x10,%rsp
892: 48 81 ec 11 11 11 11 sub $0x11111111,%rsp
899: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
8a0: 00
8a1: 48 81 ec 22 22 22 22 sub $0x22222222,%rsp
8a8: 48 8b 45 f8 mov -0x8(%rbp),%rax
8ac: 8b 00 mov (%rax),%eax // 这是刚刚那行出问题的那一行,看到在 $0x22222222 和 $0x33333333 之间
8ae: 89 c6 mov %eax,%esi
8b0: 48 8d 3d 69 07 20 00 lea 0x200769(%rip),%rdi # 201020 <_ZSt4cout@@GLIBCXX_3.4>
8b7: e8 a4 fe ff ff callq 760 <_ZNSolsEi@plt>
8bc: 48 89 c2 mov %rax,%rdx
8bf: 48 8b 05 0a 07 20 00 mov 0x20070a(%rip),%rax # 200fd0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4>
8c6: 48 89 c6 mov %rax,%rsi
8c9: 48 89 d7 mov %rdx,%rdi
8cc: e8 6f fe ff ff callq 740 <_ZNSolsEPFRSoS_E@plt>
8d1: 48 81 ec 33 33 33 33 sub $0x33333333,%rsp
8d8: b8 00 00 00 00 mov $0x0,%eax
8dd: c9 leaveq
8de: c3 retq
对应代码就可以清晰得出,然后就可以进行分析。
cpp
__asm__("sub $0x22222222,%rsp");
cout << *p << endl; // 就是这一行出了问题
__asm__("sub $0x33333333,%rsp");
3. 加 -g
的debug
cpp
#include <iostream>
using namespace std;
int main()
{
int *p = nullptr;
cout << *p << endl;
return 0;
}
- 省去了所有的步骤,直接告诉你是第几行出错!!!!
bash
oot@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# g++ -o demo1 demo1.cc -g
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# ./demo1
Segmentation fault (core dumped)
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# cp /var/core/core_demo1_6898 .
root@swd-Lenovo-G40-80:/home/swd/pros/c--learn/0.test_codes/demos# gdb demo1 core_demo1_6898
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from demo1...done.
[New LWP 6898]
Core was generated by `./demo1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000055e6d02ce89e in main () at demo1.cc:9
9 cout << *p << endl;
(gdb)