Core dump 原理探究学习笔记(六)

Posted by Masutangu on November 14, 2018

本系列文章是读《coredump问题原理探究》的读书笔记。

Vector

1
2
3
4
5
6
7
8
9
10
#include <vector>

int main() {
  std::vector<int> vec;
  vec.push_back(0xffeeffab);
  vec.push_back(0xabcdef01);
  vec.push_back(0x12345678);
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
(gdb) r
Starting program: /data/home/tmp/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, 0x0000000000400954 in main ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.tl2.2.x86_64 libgcc-4.8.5-4.el7.x86_64 libstdc++-4.8.5-4.el7.x86_64
(gdb) set print asm-demangle 
(gdb) disassemble
Dump of assembler code for function main:
   0x0000000000400950 <+0>:     push   %rbp
   0x0000000000400951 <+1>:     mov    %rsp,%rbp
=> 0x0000000000400954 <+4>:     push   %rbx
   0x0000000000400955 <+5>:     sub    $0x38,%rsp
   0x0000000000400959 <+9>:     lea    -0x40(%rbp),%rax
   0x000000000040095d <+13>:    mov    %rax,%rdi
   0x0000000000400960 <+16>:    callq  0x4009fc <std::vector<int, std::allocator<int> >::vector()>
   0x0000000000400965 <+21>:    movl   $0xffeeffab,-0x1c(%rbp)
   0x000000000040096c <+28>:    lea    -0x1c(%rbp),%rdx
   0x0000000000400970 <+32>:    lea    -0x40(%rbp),%rax
   0x0000000000400974 <+36>:    mov    %rdx,%rsi
   0x0000000000400977 <+39>:    mov    %rax,%rdi
   0x000000000040097a <+42>:    callq  0x400a7c <std::vector<int, std::allocator<int> >::push_back(int const&)>
   0x000000000040097f <+47>:    movl   $0xabcdef01,-0x18(%rbp)
   0x0000000000400986 <+54>:    lea    -0x18(%rbp),%rdx
   0x000000000040098a <+58>:    lea    -0x40(%rbp),%rax
   0x000000000040098e <+62>:    mov    %rdx,%rsi
   0x0000000000400991 <+65>:    mov    %rax,%rdi
   0x0000000000400994 <+68>:    callq  0x400a7c <std::vector<int, std::allocator<int> >::push_back(int const&)>
   0x0000000000400999 <+73>:    movl   $0x12345678,-0x14(%rbp)
   0x00000000004009a0 <+80>:    lea    -0x14(%rbp),%rdx
   0x00000000004009a4 <+84>:    lea    -0x40(%rbp),%rax
   0x00000000004009a8 <+88>:    mov    %rdx,%rsi
   0x00000000004009ab <+91>:    mov    %rax,%rdi
   0x00000000004009ae <+94>:    callq  0x400a7c <std::vector<int, std::allocator<int> >::push_back(int const&)>
   0x00000000004009b3 <+99>:    mov    $0x0,%ebx
   0x00000000004009b8 <+104>:   lea    -0x40(%rbp),%rax
   0x00000000004009bc <+108>:   mov    %rax,%rdi
   0x00000000004009bf <+111>:   callq  0x400a16 <std::vector<int, std::allocator<int> >::~vector()>
   0x00000000004009c4 <+116>:   mov    %ebx,%eax
   0x00000000004009c6 <+118>:   jmp    0x4009e2 <main+146>
   0x00000000004009c8 <+120>:   mov    %rax,%rbx
   0x00000000004009cb <+123>:   lea    -0x40(%rbp),%rax
   0x00000000004009cf <+127>:   mov    %rax,%rdi
   0x00000000004009d2 <+130>:   callq  0x400a16 <std::vector<int, std::allocator<int> >::~vector()>
   0x00000000004009d7 <+135>:   mov    %rbx,%rax
   0x00000000004009da <+138>:   mov    %rax,%rdi
   0x00000000004009dd <+141>:   callq  0x400850 <_Unwind_Resume@plt>
   0x00000000004009e2 <+146>:   add    $0x38,%rsp
   0x00000000004009e6 <+150>:   pop    %rbx
   0x00000000004009e7 <+151>:   pop    %rbp
   0x00000000004009e8 <+152>:   retq   
End of assembler dump.

1
2
3
   0x0000000000400959 <+9>:     lea    -0x40(%rbp),%rax
   0x000000000040095d <+13>:    mov    %rax,%rdi
   0x0000000000400960 <+16>:    callq  0x4009fc <std::vector<int, std::allocator<int> >::vector()>

得知 vector 的 this 指针存放于 -0x40(%rbp)。在各个成员函数调用处打断点观察:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) tbreak *0x0000000000400960
Note: breakpoint 2 also set at pc 0x400960.
Temporary breakpoint 3 at 0x400960
(gdb) c
Continuing.
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00000001      0x00000000      0x004016ed      0x00000000
0x7fffffffe440: 0x00000000      0x00000000      0x00000000      0x00000000
(gdb) ni
0x0000000000400965 in main ()
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffe440: 0x00000000      0x00000000      0x00000000      0x00000000

构造函数完成后,vector 指向的内存都初始化为 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(gdb) tbreak *0x000000000040097a
Temporary breakpoint 4 at 0x40097a
(gdb) c
Continuing.

Temporary breakpoint 4, 0x000000000040097a in main ()
(gdb) x /8x $rbp-0x40
0x7fffffffe430: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffe440: 0x00000000      0x00000000      0x00000000      0x00000000
(gdb) ni
0x000000000040097f in main ()
(gdb) x /8x $rbp-0x40
0x7fffffffe430: 0x00604010      0x00000000      0x00604014      0x00000000
0x7fffffffe440: 0x00604014      0x00000000      0x00000000      0x00000000
(gdb) x /4wx  0x00604010
0x604010:       0xffeeffab      0x00000000      0x00000000      0x00000000

第一次调用 push_back 之后,vector 的第一个成员指向的内存存放了 pusb back 的值 0xffeeffab,vector 的第二个成员变成 0x00604014,和 0x00604010 相差 4 字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(gdb) tbreak *0x0000000000400994
Temporary breakpoint 2 at 0x400994
(gdb) c
Continuing.

Temporary breakpoint 2, 0x0000000000400994 in main ()
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00604010      0x00000000      0x00604014      0x00000000
0x7fffffffe440: 0x00604014      0x00000000      0x00000000      0x00000000
(gdb) ni
0x0000000000400999 in main ()
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00604030      0x00000000      0x00604038      0x00000000
0x7fffffffe440: 0x00604038      0x00000000      0x00000000      0x00000000

第二个 push_back 之后,第一个成员指向的值变成了 0x00604030,但这个地址指向的内容还是一样,并且后面还存放着第二个 push back 进去的值 0xabcdef01

1
2
(gdb) x /4wx 0x00604030
0x604030:       0xffeeffab      0xabcdef01      0x00000000      0x00000000

vector 的第二个成员变成了 0x00604038,和 0x00604030 相差 8 字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(gdb) tbreak *0x00000000004009ae
Temporary breakpoint 3 at 0x4009ae
(gdb) c
Continuing.

Temporary breakpoint 3, 0x00000000004009ae in main ()
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00604030      0x00000000      0x00604038      0x00000000
0x7fffffffe440: 0x00604038      0x00000000      0x00000000      0x00000000
(gdb) ni
0x00000000004009b3 in main ()
(gdb) x /8wx $rbp-0x40
0x7fffffffe430: 0x00604010      0x00000000      0x0060401c      0x00000000
0x7fffffffe440: 0x00604020      0x00000000      0x00000000      0x00000000
(gdb) x /4wx 0x00604010
0x604010:       0xffeeffab      0xabcdef01      0x12345678      0x00000000

第三个 push_back 之后,第一个成员的指向的地址再次发生变化,变成了 0x00604010,指向的内容不变,并且还新增了第三个 push back 的值 0x12345678。第二个成员 0x0060401c 和第一个成员 0x00604010 相差 12个字节。

因此可以推断,vector 的第一个成员指向 begin(),第二个成员指向 end()。

coredump 分析

1
2
3
(gdb) bt
#0  0x0000000000400dd8 in __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) ()
#1  0x0000000000400b23 in main ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
(gdb) disassemble
Dump of assembler code for function _ZSt5mergeIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_S6_ET1_T_S8_T0_S9_S7_:
   0x0000000000400d47 <+0>:     push   %rbp
   0x0000000000400d48 <+1>:     mov    %rsp,%rbp
   0x0000000000400d4b <+4>:     push   %rbx
   0x0000000000400d4c <+5>:     sub    $0x58,%rsp
   0x0000000000400d50 <+9>:     mov    %rdi,-0x20(%rbp)
   0x0000000000400d54 <+13>:    mov    %rsi,-0x30(%rbp)
   0x0000000000400d58 <+17>:    mov    %rdx,-0x40(%rbp)
   0x0000000000400d5c <+21>:    mov    %rcx,-0x50(%rbp)
   0x0000000000400d60 <+25>:    mov    %r8,-0x60(%rbp)
   0x0000000000400d64 <+29>:    jmpq   0x400df2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+171>
   0x0000000000400d69 <+34>:    lea    -0x40(%rbp),%rax
   0x0000000000400d6d <+38>:    mov    %rax,%rdi
   0x0000000000400d70 <+41>:    callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400d75 <+46>:    mov    (%rax),%ebx
   0x0000000000400d77 <+48>:    lea    -0x20(%rbp),%rax
   0x0000000000400d7b <+52>:    mov    %rax,%rdi
   0x0000000000400d7e <+55>:    callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400d83 <+60>:    mov    (%rax),%eax
   0x0000000000400d85 <+62>:    cmp    %eax,%ebx
   0x0000000000400d87 <+64>:    setl   %al
   0x0000000000400d8a <+67>:    test   %al,%al
   0x0000000000400d8c <+69>:    je     0x400dbb <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+116>
   0x0000000000400d8e <+71>:    lea    -0x60(%rbp),%rax
   0x0000000000400d92 <+75>:    mov    %rax,%rdi
   0x0000000000400d95 <+78>:    callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400d9a <+83>:    mov    %rax,%rbx
   0x0000000000400d9d <+86>:    lea    -0x40(%rbp),%rax
   0x0000000000400da1 <+90>:    mov    %rax,%rdi
   0x0000000000400da4 <+93>:    callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400da9 <+98>:    mov    (%rax),%eax
   0x0000000000400dab <+100>:   mov    %eax,(%rbx)
   0x0000000000400dad <+102>:   lea    -0x40(%rbp),%rax
   0x0000000000400db1 <+106>:   mov    %rax,%rdi
   0x0000000000400db4 <+109>:   callq  0x4012c2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator++()>
   0x0000000000400db9 <+114>:   jmp    0x400de6 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+159>
   0x0000000000400dbb <+116>:   lea    -0x60(%rbp),%rax
   0x0000000000400dbf <+120>:   mov    %rax,%rdi
   0x0000000000400dc2 <+123>:   callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400dc7 <+128>:   mov    %rax,%rbx
   0x0000000000400dca <+131>:   lea    -0x20(%rbp),%rax
   0x0000000000400dce <+135>:   mov    %rax,%rdi
   0x0000000000400dd1 <+138>:   callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400dd6 <+143>:   mov    (%rax),%eax
=> 0x0000000000400dd8 <+145>:   mov    %eax,(%rbx)
   0x0000000000400dda <+147>:   lea    -0x20(%rbp),%rax
   0x0000000000400dde <+151>:   mov    %rax,%rdi
   0x0000000000400de1 <+154>:   callq  0x4012c2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator++()>
   0x0000000000400de6 <+159>:   lea    -0x60(%rbp),%rax
   0x0000000000400dea <+163>:   mov    %rax,%rdi
   0x0000000000400ded <+166>:   callq  0x4012c2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator++()>
   0x0000000000400df2 <+171>:   lea    -0x30(%rbp),%rdx
   0x0000000000400df6 <+175>:   lea    -0x20(%rbp),%rax
   0x0000000000400dfa <+179>:   mov    %rdx,%rsi
   0x0000000000400dfd <+182>:   mov    %rax,%rdi
   0x0000000000400e00 <+185>:   callq  0x401274 <bool __gnu_cxx::operator!=<int*, std::vector<int, std::allocator<int> > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&)>
   0x0000000000400e05 <+190>:   test   %al,%al
   0x0000000000400e07 <+192>:   je     0x400e27 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+224>
   0x0000000000400e09 <+194>:   lea    -0x50(%rbp),%rdx
   0x0000000000400e0d <+198>:   lea    -0x40(%rbp),%rax
   0x0000000000400e11 <+202>:   mov    %rdx,%rsi
   0x0000000000400e14 <+205>:   mov    %rax,%rdi
   0x0000000000400e17 <+208>:   callq  0x401274 <bool __gnu_cxx::operator!=<int*, std::vector<int, std::allocator<int> > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > const&)>
   0x0000000000400e1c <+213>:   test   %al,%al
   0x0000000000400e1e <+215>:   je     0x400e27 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+224>
   0x0000000000400e20 <+217>:   mov    $0x1,%eax
   0x0000000000400e25 <+222>:   jmp    0x400e2c <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+229>
   0x0000000000400e27 <+224>:   mov    $0x0,%eax
   0x0000000000400e2c <+229>:   test   %al,%al
   0x0000000000400e2e <+231>:   jne    0x400d69 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)+34>
   0x0000000000400e34 <+237>:   mov    -0x60(%rbp),%rdx
   0x0000000000400e38 <+241>:   mov    -0x30(%rbp),%rcx
   0x0000000000400e3c <+245>:   mov    -0x20(%rbp),%rax
   0x0000000000400e40 <+249>:   mov    %rcx,%rsi
   0x0000000000400e43 <+252>:   mov    %rax,%rdi
   0x0000000000400e46 <+255>:   callq  0x4012e2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::copy<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)>
   0x0000000000400e4b <+260>:   mov    %rax,%rdx
   0x0000000000400e4e <+263>:   mov    -0x50(%rbp),%rcx
   0x0000000000400e52 <+267>:   mov    -0x40(%rbp),%rax
   0x0000000000400e56 <+271>:   mov    %rcx,%rsi
   0x0000000000400e59 <+274>:   mov    %rax,%rdi
   0x0000000000400e5c <+277>:   callq  0x4012e2 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::copy<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)>
   0x0000000000400e61 <+282>:   add    $0x58,%rsp
   0x0000000000400e65 <+286>:   pop    %rbx
   0x0000000000400e66 <+287>:   pop    %rbp
   0x0000000000400e67 <+288>:   retq   
End of assembler dump.

1
2
3
4
5
6
7
8
9
10
   0x0000000000400dbb <+116>:   lea    -0x60(%rbp),%rax
   0x0000000000400dbf <+120>:   mov    %rax,%rdi
   0x0000000000400dc2 <+123>:   callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>

   0x0000000000400dc7 <+128>:   mov    %rax,%rbx
   0x0000000000400dca <+131>:   lea    -0x20(%rbp),%rax
   0x0000000000400dce <+135>:   mov    %rax,%rdi
   0x0000000000400dd1 <+138>:   callq  0x4012b0 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const>
   0x0000000000400dd6 <+143>:   mov    (%rax),%eax
=> 0x0000000000400dd8 <+145>:   mov    %eax,(%rbx)

可见崩溃时的指令是 => 0x0000000000400dd8 <+145>: mov %eax,(%rbx) eax 和 rbx 正好是调用了 __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > 函数的返回值,而调用这个函数的 this 指针分别是 -0x20(%rbp) 和 -0x60(%rbp),从

1
2
3
   0x0000000000400d50 <+9>:     mov    %rdi,-0x20(%rbp)
   ...
   0x0000000000400d60 <+25>:    mov    %r8,-0x60(%rbp)

知道是 _ZSt5mergeIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_S6_ET1_T_S8_T0_S9_S7_ 函数的第一个参数和第五个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
(gdb) f 1
#1  0x0000000000400b23 in main ()
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400a60 <+0>:     push   %rbp
   0x0000000000400a61 <+1>:     mov    %rsp,%rbp
   0x0000000000400a64 <+4>:     push   %r14
   0x0000000000400a66 <+6>:     push   %r13
   0x0000000000400a68 <+8>:     push   %r12
   0x0000000000400a6a <+10>:    push   %rbx
   0x0000000000400a6b <+11>:    sub    $0x60,%rsp
   0x0000000000400a6f <+15>:    lea    -0x40(%rbp),%rax
   0x0000000000400a73 <+19>:    mov    %rax,%rdi
   0x0000000000400a76 <+22>:    callq  0x400bfe <std::vector<int, std::allocator<int> >::vector()>
   0x0000000000400a7b <+27>:    movl   $0x1,-0x28(%rbp)
   0x0000000000400a82 <+34>:    lea    -0x28(%rbp),%rdx
   0x0000000000400a86 <+38>:    lea    -0x40(%rbp),%rax
   0x0000000000400a8a <+42>:    mov    %rdx,%rsi
   0x0000000000400a8d <+45>:    mov    %rax,%rdi
   0x0000000000400a90 <+48>:    callq  0x400c7e <std::vector<int, std::allocator<int> >::push_back(int const&)>
   0x0000000000400a95 <+53>:    lea    -0x60(%rbp),%rax
   0x0000000000400a99 <+57>:    mov    %rax,%rdi
   0x0000000000400a9c <+60>:    callq  0x400bfe <std::vector<int, std::allocator<int> >::vector()>
   0x0000000000400aa1 <+65>:    movl   $0x8,-0x24(%rbp)
   0x0000000000400aa8 <+72>:    lea    -0x24(%rbp),%rdx
   0x0000000000400aac <+76>:    lea    -0x60(%rbp),%rax
   0x0000000000400ab0 <+80>:    mov    %rdx,%rsi
   0x0000000000400ab3 <+83>:    mov    %rax,%rdi
   0x0000000000400ab6 <+86>:    callq  0x400c7e <std::vector<int, std::allocator<int> >::push_back(int const&)>
   0x0000000000400abb <+91>:    lea    -0x80(%rbp),%rax
   0x0000000000400abf <+95>:    mov    %rax,%rdi
   0x0000000000400ac2 <+98>:    callq  0x400bfe <std::vector<int, std::allocator<int> >::vector()>
   0x0000000000400ac7 <+103>:   lea    -0x80(%rbp),%rax
   0x0000000000400acb <+107>:   mov    %rax,%rdi
   0x0000000000400ace <+110>:   callq  0x400cf8 <std::vector<int, std::allocator<int> >::begin()>
   0x0000000000400ad3 <+115>:   mov    %rax,%r14
   0x0000000000400ad6 <+118>:   lea    -0x60(%rbp),%rax
   0x0000000000400ada <+122>:   mov    %rax,%rdi
   0x0000000000400add <+125>:   callq  0x400d1e <std::vector<int, std::allocator<int> >::end()>
   0x0000000000400ae2 <+130>:   mov    %rax,%r13
   0x0000000000400ae5 <+133>:   lea    -0x60(%rbp),%rax
   0x0000000000400ae9 <+137>:   mov    %rax,%rdi
   0x0000000000400aec <+140>:   callq  0x400cf8 <std::vector<int, std::allocator<int> >::begin()>
   0x0000000000400af1 <+145>:   mov    %rax,%r12
   0x0000000000400af4 <+148>:   lea    -0x40(%rbp),%rax
   0x0000000000400af8 <+152>:   mov    %rax,%rdi
   0x0000000000400afb <+155>:   callq  0x400d1e <std::vector<int, std::allocator<int> >::end()>
   0x0000000000400b00 <+160>:   mov    %rax,%rbx
   0x0000000000400b03 <+163>:   lea    -0x40(%rbp),%rax
   0x0000000000400b07 <+167>:   mov    %rax,%rdi
   0x0000000000400b0a <+170>:   callq  0x400cf8 <std::vector<int, std::allocator<int> >::begin()>
   0x0000000000400b0f <+175>:   mov    %r14,%r8
   0x0000000000400b12 <+178>:   mov    %r13,%rcx
   0x0000000000400b15 <+181>:   mov    %r12,%rdx
   0x0000000000400b18 <+184>:   mov    %rbx,%rsi
   0x0000000000400b1b <+187>:   mov    %rax,%rdi
   0x0000000000400b1e <+190>:   callq  0x400d47 <__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::merge<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)>
=> 0x0000000000400b23 <+195>:   mov    $0x0,%ebx
   0x0000000000400b28 <+200>:   lea    -0x80(%rbp),%rax
   0x0000000000400b2c <+204>:   mov    %rax,%rdi
   0x0000000000400b2f <+207>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b34 <+212>:   lea    -0x60(%rbp),%rax
   0x0000000000400b38 <+216>:   mov    %rax,%rdi
   0x0000000000400b3b <+219>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b40 <+224>:   lea    -0x40(%rbp),%rax
   0x0000000000400b44 <+228>:   mov    %rax,%rdi
   0x0000000000400b47 <+231>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b4c <+236>:   mov    %ebx,%eax
   0x0000000000400b4e <+238>:   jmp    0x400b8c <main+300>
   0x0000000000400b50 <+240>:   mov    %rax,%rbx
   0x0000000000400b53 <+243>:   lea    -0x80(%rbp),%rax
   0x0000000000400b57 <+247>:   mov    %rax,%rdi
   0x0000000000400b5a <+250>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b5f <+255>:   jmp    0x400b64 <main+260>
   0x0000000000400b61 <+257>:   mov    %rax,%rbx
   0x0000000000400b64 <+260>:   lea    -0x60(%rbp),%rax
   0x0000000000400b68 <+264>:   mov    %rax,%rdi
   0x0000000000400b6b <+267>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b70 <+272>:   jmp    0x400b75 <main+277>
   0x0000000000400b72 <+274>:   mov    %rax,%rbx
   0x0000000000400b75 <+277>:   lea    -0x40(%rbp),%rax
   0x0000000000400b79 <+281>:   mov    %rax,%rdi
   0x0000000000400b7c <+284>:   callq  0x400c18 <std::vector<int, std::allocator<int> >::~vector()>
   0x0000000000400b81 <+289>:   mov    %rbx,%rax
   0x0000000000400b84 <+292>:   mov    %rax,%rdi
   0x0000000000400b87 <+295>:   callq  0x400960 <_Unwind_Resume@plt>
   0x0000000000400b8c <+300>:   add    $0x60,%rsp
   0x0000000000400b90 <+304>:   pop    %rbx
   0x0000000000400b91 <+305>:   pop    %r12
   0x0000000000400b93 <+307>:   pop    %r13
   0x0000000000400b95 <+309>:   pop    %r14
   0x0000000000400b97 <+311>:   pop    %rbp
   0x0000000000400b98 <+312>:   retq   
End of assembler dump.

由下面这段汇编

1
2
3
4
5
6
7
8
9
10
11
12
   0x0000000000400ac7 <+103>:   lea    -0x80(%rbp),%rax
   0x0000000000400acb <+107>:   mov    %rax,%rdi
   0x0000000000400ace <+110>:   callq  0x400cf8 <std::vector<int, std::allocator<int> >::begin()>
   0x0000000000400ad3 <+115>:   mov    %rax,%r14
   ...
   0x0000000000400b03 <+163>:   lea    -0x40(%rbp),%rax
   0x0000000000400b07 <+167>:   mov    %rax,%rdi
   0x0000000000400b0a <+170>:   callq  0x400cf8 <std::vector<int, std::allocator<int> >::begin()>
   ...
   0x0000000000400b0f <+175>:   mov    %r14,%r8
   ...
   0x0000000000400b1b <+187>:   mov    %rax,%rdi

可以看到,传给 __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > 的第一个参数,正是 <std::vector<int, std::allocator<int> >::begin()> 的返回值,而调用 <std::vector<int, std::allocator<int> >::begin()> 的 this 指针,是变量 -0x40(%rbp)。

1
2
3
(gdb) x /6wx $rbp-0x40
0x7ffee5fc2f40: 0x018ab010      0x00000000      0x018ab014      0x00000000
0x7ffee5fc2f50: 0x018ab014      0x00000000

而第五个参数,也是调用的 <std::vector<int, std::allocator<int> >::begin()> 的返回值,调用的 this 指针为 -0x80(%rbp):

1
2
3
(gdb) x /6wx $rbp-0x80
0x7ffee5fc2f00: 0x00000000      0x00000000      0x00000000      0x00000000
0x7ffee5fc2f10: 0x00000000      0x00000000

可见这个 vector 只是执行了构造函数,并没有申请空间。调用 merge 会导致 core。

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <vector>
#include <algorithm>
#include <iostream>

int main()
{
  std::vector<int> a;
  a.push_back(1);

  std::vector<int> b;
  b.push_back(8);

  std::vector<int> c;
  std::merge(a.begin(), a.end(), b.begin(), b.end(), c.begin());

  return 0;
}                         

List

1
2
3
4
5
6
7
8
9
10
11
12
#include <list>

int main() {
  std::list<int> lst;

  lst.push_back(0x12345678);
  lst.push_front(0xabcdef01);
  lst.push_back(0x24242522);

  return 0;
}
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
(gdb) set print asm-demangle 
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400840 <+0>:     push   %rbp
   0x0000000000400841 <+1>:     mov    %rsp,%rbp
   0x0000000000400844 <+4>:     push   %rbx
   0x0000000000400845 <+5>:     sub    $0x28,%rsp
   0x0000000000400849 <+9>:     lea    -0x30(%rbp),%rax
   0x000000000040084d <+13>:    mov    %rax,%rdi
   0x0000000000400850 <+16>:    callq  0x400906 <std::list<int, std::allocator<int> >::list()>
   0x0000000000400855 <+21>:    movl   $0x12345678,-0x1c(%rbp)
   0x000000000040085c <+28>:    lea    -0x1c(%rbp),%rdx
   0x0000000000400860 <+32>:    lea    -0x30(%rbp),%rax
   0x0000000000400864 <+36>:    mov    %rdx,%rsi
   0x0000000000400867 <+39>:    mov    %rax,%rdi
   0x000000000040086a <+42>:    callq  0x400982 <std::list<int, std::allocator<int> >::push_back(int const&)>
   0x000000000040086f <+47>:    movl   $0xabcdef01,-0x18(%rbp)
   0x0000000000400876 <+54>:    lea    -0x18(%rbp),%rdx
   0x000000000040087a <+58>:    lea    -0x30(%rbp),%rax
   0x000000000040087e <+62>:    mov    %rdx,%rsi
   0x0000000000400881 <+65>:    mov    %rax,%rdi
   0x0000000000400884 <+68>:    callq  0x4009b6 <std::list<int, std::allocator<int> >::push_front(int const&)>
   0x0000000000400889 <+73>:    movl   $0x24242522,-0x14(%rbp)
   0x0000000000400890 <+80>:    lea    -0x14(%rbp),%rdx
   0x0000000000400894 <+84>:    lea    -0x30(%rbp),%rax
   0x0000000000400898 <+88>:    mov    %rdx,%rsi
   0x000000000040089b <+91>:    mov    %rax,%rdi
   0x000000000040089e <+94>:    callq  0x400982 <std::list<int, std::allocator<int> >::push_back(int const&)>
   0x00000000004008a3 <+99>:    mov    $0x0,%ebx
   0x00000000004008a8 <+104>:   lea    -0x30(%rbp),%rax
   0x00000000004008ac <+108>:   mov    %rax,%rdi
   0x00000000004008af <+111>:   callq  0x4008ec <std::list<int, std::allocator<int> >::~list()>
   0x00000000004008b4 <+116>:   mov    %ebx,%eax
   0x00000000004008b6 <+118>:   jmp    0x4008d2 <main+146>
   0x00000000004008b8 <+120>:   mov    %rax,%rbx
   0x00000000004008bb <+123>:   lea    -0x30(%rbp),%rax
   0x00000000004008bf <+127>:   mov    %rax,%rdi
   0x00000000004008c2 <+130>:   callq  0x4008ec <std::list<int, std::allocator<int> >::~list()>
   0x00000000004008c7 <+135>:   mov    %rbx,%rax
   0x00000000004008ca <+138>:   mov    %rax,%rdi
   0x00000000004008cd <+141>:   callq  0x400740 <_Unwind_Resume@plt>
   0x00000000004008d2 <+146>:   add    $0x28,%rsp
   0x00000000004008d6 <+150>:   pop    %rbx
   0x00000000004008d7 <+151>:   pop    %rbp
   0x00000000004008d8 <+152>:   retq   
End of assembler dump.

在构造函数调用处打断点:

1
2
3
4
5
6
7
8
9
10
11
(gdb) tbreak *0x0000000000400850
Temporary breakpoint 1 at 0x400850
(gdb) r
(gdb) x /8wx $rbp-0x30
0x7fffffffe440: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffe450: 0x00400e10      0x00000000      0x00400750      0x00000000
(gdb) ni
0x0000000000400855 in main ()
(gdb) x /8wx $rbp-0x30
0x7fffffffe440: 0xffffe440      0x00007fff      0xffffe440      0x00007fff
0x7fffffffe450: 0x00400e10      0x00000000      0x00400750      0x00000000

构造函数完成后,内存数据发生了变化,猜测 list 有两个成员,且这两个成员是指针,构造完成时指向了自己(0x7fffffffe440)。接下来在 push_back 调用后打断点:

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) tbreak *0x000000000040086f
Temporary breakpoint 2 at 0x40086f
(gdb) c
Continuing.

Temporary breakpoint 2, 0x000000000040086f in main ()
(gdb) x /8wx $rbp-0x30
0x7fffffffe440: 0x00603010      0x00000000      0x00603010      0x00000000
0x7fffffffe450: 0x00400e10      0x12345678      0x00400750      0x00000000
(gdb) x /8wx 0x00603010
0x603010:       0xffffe440      0x00007fff      0xffffe440      0x00007fff
0x603020:       0x12345678      0x00000000      0x00020fe1      0x00000000

结构如下图(注:文本框内为内存地址,蓝色方框为内存地址上的内容):

push_front 调用后打断点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) tbreak *0x0000000000400889
Temporary breakpoint 4 at 0x400889
(gdb) c
Continuing.

Temporary breakpoint 4, 0x0000000000400889 in main ()
(gdb) x /8wx $rbp-0x30
0x7fffffffe440: 0x00603030      0x00000000      0x00603010      0x00000000
0x7fffffffe450: 0x00400e10      0x12345678      0xabcdef01      0x00000000
(gdb) x /8wx 0x00603030
0x603030:       0x00603010      0x00000000      0xffffe440      0x00007fff
0x603040:       0xabcdef01      0x00000000      0x00020fc1      0x00000000
(gdb) x /8wx 0x00603010
0x603010:       0xffffe440      0x00007fff      0x00603030      0x00000000
0x603020:       0x12345678      0x00000000      0x00000021      0x00000000

可以看出 list 的两个成员,第一个指向链表头,第二个指向链表尾。每个成员有三个元素:

1
2
3
4
5
6
template <class T>
struct Node {
  Node* next;
  Node* prev;
  T data;
};

coredump 分析 II

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(gdb) bt
#0  0x0000000000400b18 in std::_List_base<int, std::allocator<int> >::_M_clear() ()
#1  0x00000000004009e1 in std::_List_base<int, std::allocator<int> >::~_List_base() ()
#2  0x0000000000400992 in std::list<int, std::allocator<int> >::~list() ()
#3  0x0000000000400943 in main ()
(gdb) disassemble
Dump of assembler code for function _ZNSt10_List_baseIiSaIiEE8_M_clearEv:
   0x0000000000400af2 <+0>:     push   %rbp
   0x0000000000400af3 <+1>:     mov    %rsp,%rbp
   0x0000000000400af6 <+4>:     push   %rbx
   0x0000000000400af7 <+5>:     sub    $0x38,%rsp
   0x0000000000400afb <+9>:     mov    %rdi,-0x38(%rbp)
   0x0000000000400aff <+13>:    mov    -0x38(%rbp),%rax
   0x0000000000400b03 <+17>:    mov    (%rax),%rax
   0x0000000000400b06 <+20>:    mov    %rax,-0x18(%rbp)
   0x0000000000400b0a <+24>:    jmp    0x400b73 <std::_List_base<int, std::allocator<int> >::_M_clear()+129>
   0x0000000000400b0c <+26>:    mov    -0x18(%rbp),%rax
   0x0000000000400b10 <+30>:    mov    %rax,-0x20(%rbp)
   0x0000000000400b14 <+34>:    mov    -0x18(%rbp),%rax
=> 0x0000000000400b18 <+38>:    mov    (%rax),%rax
   0x0000000000400b1b <+41>:    mov    %rax,-0x18(%rbp)
   0x0000000000400b1f <+45>:    mov    -0x20(%rbp),%rax
   0x0000000000400b23 <+49>:    add    $0x10,%rax
   0x0000000000400b27 <+53>:    mov    %rax,%rdi
   0x0000000000400b2a <+56>:    callq  0x400d46 <int* std::__addressof<int>(int&)>
   0x0000000000400b2f <+61>:    mov    %rax,%rbx
   0x0000000000400b32 <+64>:    lea    -0x21(%rbp),%rax
   0x0000000000400b36 <+68>:    mov    -0x38(%rbp),%rdx
   0x0000000000400b3a <+72>:    mov    %rdx,%rsi
   0x0000000000400b3d <+75>:    mov    %rax,%rdi
   0x0000000000400b40 <+78>:    callq  0x400cf8 <std::_List_base<int, std::allocator<int> >::_M_get_Tp_allocator() const>
   0x0000000000400b45 <+83>:    lea    -0x21(%rbp),%rax
   0x0000000000400b49 <+87>:    mov    %rbx,%rsi
   0x0000000000400b4c <+90>:    mov    %rax,%rdi
   0x0000000000400b4f <+93>:    callq  0x400d54 <__gnu_cxx::new_allocator<int>::destroy(int*)>
   0x0000000000400b54 <+98>:    lea    -0x21(%rbp),%rax
   0x0000000000400b58 <+102>:   mov    %rax,%rdi
   0x0000000000400b5b <+105>:   callq  0x400d2c <std::allocator<int>::~allocator()>
   0x0000000000400b60 <+110>:   mov    -0x20(%rbp),%rdx
   0x0000000000400b64 <+114>:   mov    -0x38(%rbp),%rax
   0x0000000000400b68 <+118>:   mov    %rdx,%rsi
   0x0000000000400b6b <+121>:   mov    %rax,%rdi
   0x0000000000400b6e <+124>:   callq  0x400d62 <std::_List_base<int, std::allocator<int> >::_M_put_node(std::_List_node<int>*)>
   0x0000000000400b73 <+129>:   mov    -0x38(%rbp),%rax
   0x0000000000400b77 <+133>:   cmp    -0x18(%rbp),%rax
   0x0000000000400b7b <+137>:   jne    0x400b0c <std::_List_base<int, std::allocator<int> >::_M_clear()+26>
   0x0000000000400b7d <+139>:   add    $0x38,%rsp
   0x0000000000400b81 <+143>:   pop    %rbx
   0x0000000000400b82 <+144>:   pop    %rbp
   0x0000000000400b83 <+145>:   retq   
End of assembler dump.

崩溃指令在:

1
2
0x0000000000400b14 <+34>:    mov    -0x18(%rbp),%rax
=> 0x0000000000400b18 <+38>:    mov    (%rax),%rax

1
2
(gdb) x /2wx $rbp-0x18
0x7ffccd449f68: 0x00000000      0x00000000

可见崩溃是由于 -0x18(%rbp) 为空指针导致的。

1
2
3
4
   0x0000000000400afb <+9>:     mov    %rdi,-0x38(%rbp)
   0x0000000000400aff <+13>:    mov    -0x38(%rbp),%rax
   0x0000000000400b03 <+17>:    mov    (%rax),%rax
   0x0000000000400b06 <+20>:    mov    %rax,-0x18(%rbp)

知道 -0x18(%rbp) 从 -0x38(%rbp) 而来,gdb 看下值:

1
2
3
4
(gdb) x /2wx $rbp-0x38
0x7ffccd449f48: 0xcd449fe0      0x00007ffc
(gdb) x /2wx 0x00007ffccd449fe0
0x7ffccd449fe0: 0x00000000      0x00000000

可见 -0x38(%rbp) 指向的内存地址解引用后就是空地址。从调用栈往上查看传进 rdi 寄存器的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(gdb) f 1
#1  0x00000000004009e1 in std::_List_base<int, std::allocator<int> >::~_List_base() ()
(gdb) disassemble 
Dump of assembler code for function _ZNSt10_List_baseIiSaIiEED2Ev:
   0x00000000004009c8 <+0>:     push   %rbp
   0x00000000004009c9 <+1>:     mov    %rsp,%rbp
   0x00000000004009cc <+4>:     push   %rbx
   0x00000000004009cd <+5>:     sub    $0x18,%rsp
   0x00000000004009d1 <+9>:     mov    %rdi,-0x18(%rbp)
   0x00000000004009d5 <+13>:    mov    -0x18(%rbp),%rax
   0x00000000004009d9 <+17>:    mov    %rax,%rdi
   0x00000000004009dc <+20>:    callq  0x400af2 <std::_List_base<int, std::allocator<int> >::_M_clear()>
=> 0x00000000004009e1 <+25>:    mov    -0x18(%rbp),%rax
   0x00000000004009e5 <+29>:    mov    %rax,%rdi
   0x00000000004009e8 <+32>:    callq  0x4009ae <std::_List_base<int, std::allocator<int> >::_List_impl::~_List_impl()>
   0x00000000004009ed <+37>:    jmp    0x400a09 <std::_List_base<int, std::allocator<int> >::~_List_base()+65>
   0x00000000004009ef <+39>:    mov    %rax,%rbx
   0x00000000004009f2 <+42>:    mov    -0x18(%rbp),%rax
   0x00000000004009f6 <+46>:    mov    %rax,%rdi
   0x00000000004009f9 <+49>:    callq  0x4009ae <std::_List_base<int, std::allocator<int> >::_List_impl::~_List_impl()>
   0x00000000004009fe <+54>:    mov    %rbx,%rax
   0x0000000000400a01 <+57>:    mov    %rax,%rdi
   0x0000000000400a04 <+60>:    callq  0x400790 <_Unwind_Resume@plt>
   0x0000000000400a09 <+65>:    add    $0x18,%rsp
   0x0000000000400a0d <+69>:    pop    %rbx
   0x0000000000400a0e <+70>:    pop    %rbp
   0x0000000000400a0f <+71>:    retq   
End of assembler dump.

1
2
3
4
   0x00000000004009d1 <+9>:     mov    %rdi,-0x18(%rbp)
   0x00000000004009d5 <+13>:    mov    -0x18(%rbp),%rax
   0x00000000004009d9 <+17>:    mov    %rax,%rdi
   0x00000000004009dc <+20>:    callq  0x400af2 <std::_List_base<int, std::allocator<int> >::_M_clear()>

继续往调用栈上层查看传进 rdi 的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(gdb) f 2
#2  0x0000000000400992 in std::list<int, std::allocator<int> >::~list() ()
(gdb) disassemble
Dump of assembler code for function _ZNSt4listIiSaIiEED2Ev:
   0x000000000040097a <+0>:     push   %rbp
   0x000000000040097b <+1>:     mov    %rsp,%rbp
   0x000000000040097e <+4>:     sub    $0x10,%rsp
   0x0000000000400982 <+8>:     mov    %rdi,-0x8(%rbp)
   0x0000000000400986 <+12>:    mov    -0x8(%rbp),%rax
   0x000000000040098a <+16>:    mov    %rax,%rdi
   0x000000000040098d <+19>:    callq  0x4009c8 <std::_List_base<int, std::allocator<int> >::~_List_base()>
=> 0x0000000000400992 <+24>:    leaveq 
   0x0000000000400993 <+25>:    retq   
End of assembler dump.

继续往上层找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
(gdb) f 3
#3  0x0000000000400943 in main ()
(gdb) disassemble
Dump of assembler code for function main:
   0x0000000000400890 <+0>:     push   %rbp
   0x0000000000400891 <+1>:     mov    %rsp,%rbp
   0x0000000000400894 <+4>:     push   %rbx
   0x0000000000400895 <+5>:     sub    $0x28,%rsp
   0x0000000000400899 <+9>:     lea    -0x30(%rbp),%rax
   0x000000000040089d <+13>:    mov    %rax,%rdi
   0x00000000004008a0 <+16>:    callq  0x400994 <std::list<int, std::allocator<int> >::list()>
   0x00000000004008a5 <+21>:    movl   $0x5,-0x20(%rbp)
   0x00000000004008ac <+28>:    lea    -0x20(%rbp),%rdx
   0x00000000004008b0 <+32>:    lea    -0x30(%rbp),%rax
   0x00000000004008b4 <+36>:    mov    %rdx,%rsi
   0x00000000004008b7 <+39>:    mov    %rax,%rdi
   0x00000000004008ba <+42>:    callq  0x400a10 <std::list<int, std::allocator<int> >::push_back(int const&)>
   0x00000000004008bf <+47>:    movl   $0x3,-0x1c(%rbp)
   0x00000000004008c6 <+54>:    lea    -0x1c(%rbp),%rdx
   0x00000000004008ca <+58>:    lea    -0x30(%rbp),%rax
   0x00000000004008ce <+62>:    mov    %rdx,%rsi
   0x00000000004008d1 <+65>:    mov    %rax,%rdi
   0x00000000004008d4 <+68>:    callq  0x400a44 <std::list<int, std::allocator<int> >::push_front(int const&)>
   0x00000000004008d9 <+73>:    lea    -0x30(%rbp),%rax
   0x00000000004008dd <+77>:    mov    %rax,%rdi
   0x00000000004008e0 <+80>:    callq  0x400a78 <std::list<int, std::allocator<int> >::size() const>
   0x00000000004008e5 <+85>:    lea    0x0(,%rax,4),%rdx
   0x00000000004008ed <+93>:    lea    -0x30(%rbp),%rax
   0x00000000004008f1 <+97>:    mov    $0x0,%esi
   0x00000000004008f6 <+102>:   mov    %rax,%rdi
   0x00000000004008f9 <+105>:   callq  0x400710 <memset@plt>
   0x00000000004008fe <+110>:   movl   $0x8,-0x18(%rbp)
   0x0000000000400905 <+117>:   lea    -0x18(%rbp),%rdx
   0x0000000000400909 <+121>:   lea    -0x30(%rbp),%rax
   0x000000000040090d <+125>:   mov    %rdx,%rsi
   0x0000000000400910 <+128>:   mov    %rax,%rdi
   0x0000000000400913 <+131>:   callq  0x400a10 <std::list<int, std::allocator<int> >::push_back(int const&)>
   0x0000000000400918 <+136>:   movl   $0xc,-0x14(%rbp)
   0x000000000040091f <+143>:   lea    -0x14(%rbp),%rdx
   0x0000000000400923 <+147>:   lea    -0x30(%rbp),%rax
   0x0000000000400927 <+151>:   mov    %rdx,%rsi
   0x000000000040092a <+154>:   mov    %rax,%rdi
   0x000000000040092d <+157>:   callq  0x400a10 <std::list<int, std::allocator<int> >::push_back(int const&)>
   0x0000000000400932 <+162>:   mov    $0x0,%ebx
   0x0000000000400937 <+167>:   lea    -0x30(%rbp),%rax
   0x000000000040093b <+171>:   mov    %rax,%rdi
   0x000000000040093e <+174>:   callq  0x40097a <std::list<int, std::allocator<int> >::~list()>
=> 0x0000000000400943 <+179>:   mov    %ebx,%eax
   0x0000000000400945 <+181>:   jmp    0x400961 <main+209>
   0x0000000000400947 <+183>:   mov    %rax,%rbx
   0x000000000040094a <+186>:   lea    -0x30(%rbp),%rax
   0x000000000040094e <+190>:   mov    %rax,%rdi
   0x0000000000400951 <+193>:   callq  0x40097a <std::list<int, std::allocator<int> >::~list()>
   0x0000000000400956 <+198>:   mov    %rbx,%rax
   0x0000000000400959 <+201>:   mov    %rax,%rdi
   0x000000000040095c <+204>:   callq  0x400790 <_Unwind_Resume@plt>
   0x0000000000400961 <+209>:   add    $0x28,%rsp
   0x0000000000400965 <+213>:   pop    %rbx
   0x0000000000400966 <+214>:   pop    %rbp
   0x0000000000400967 <+215>:   retq   
End of assembler dump.

1
2
3
   0x0000000000400937 <+167>:   lea    -0x30(%rbp),%rax
   0x000000000040093b <+171>:   mov    %rax,%rdi
   0x000000000040093e <+174>:   callq  0x40097a <std::list<int, std::allocator<int> >::~list()>

可见 rdi 是取变量 -0x30(%rbp) 的地址

1
2
(gdb) x /2wx $rbp-0x30
0x7ffccd449fe0: 0x00000000      0x00000000

1
2
3
4
5
6
   0x00000000004008e0 <+80>:    callq  0x400a78 <std::list<int, std::allocator<int> >::size() const>
   0x00000000004008e5 <+85>:    lea    0x0(,%rax,4),%rdx
   0x00000000004008ed <+93>:    lea    -0x30(%rbp),%rax
   0x00000000004008f1 <+97>:    mov    $0x0,%esi
   0x00000000004008f6 <+102>:   mov    %rax,%rdi
   0x00000000004008f9 <+105>:   callq  0x400710 <memset@plt>

可见这里把 list(0x30(%rbp)) 给 memset 成 0 了。

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <list>
#include <string.h>

int main()
{
  std::list<int> list;

  list.push_back(5);
  list.push_front(3);

  memset(&list, 0, list.size() * sizeof(int));

  list.push_back(8);
  list.push_back(12);

  return 0;
}