《coredump问题原理探究》Linux x86版6.7节多继承

类的多继承大致可以分为两种情况.一种是无共同基类的.一种是有共同基类的.

先看一下第一种情况:

  1 #include <stdio.h>
  2 class xuzhina_dump_c06_s5_mother
  3 {
  4     private:
  5         int m_age;
  6         int m_beauty;
  7     public:
  8         virtual void print()
  9         {
 10             printf( "mother\n" );
 11         }
 12
 13         virtual void setBeauty( int age, int beauty )
 14         {
 15             m_age = age - 5;
 16             m_beauty = beauty;
 17         }
 18 };
19
 20 class xuzhina_dump_c06_s5_father
 21 {
 22     private:
 23         int m_strong;
 24         int m_age;
 25     public:
 26         virtual void print()
 27         {
 28             printf( "father\n" );
 29         }
 30         virtual void setStrong( int strong, int age )
 31         {
 32             m_strong = strong;
 33             m_age = age;
 34         }
 35 };
 36
37 class xuzhina_dump_c06_s5_child: public xuzhina_dump_c06_s5_father,
 38     public xuzhina_dump_c06_s5_mother
 39
 40 {
 41     private:
 42         bool m_newMind;
 43     public:
 44         virtual void print()
 45         {
 46             printf( "child\n" );
 47         }
 48
 49         virtual void setGender( bool gender )
 50         {
 51             m_newMind = true;
 52             if ( gender )
 53             {
 54                 setBeauty( 10, 10 );
 55             }
 56             else
 57             {
 58                 setStrong( 20,20 );
 59             }
 60         }
 61 };
 62
63 int main()
 64 {
 65     xuzhina_dump_c06_s5_child* child = new xuzhina_dump_c06_s5_child;
 66     child->setGender( false );
 67     child->print();
 68
 69     xuzhina_dump_c06_s5_father* f = child;
 70     f->print();
 71
 72     xuzhina_dump_c06_s5_mother* m = child;
 73     m->print();
 74
 75     return 0;
 76 }

看一下main函数的汇编:

(gdb) disassemble main
Dump of assembler code for function main:
   0x080485b0 <+0>:     push   %ebp
   0x080485b1 <+1>:     mov    %esp,%ebp
   0x080485b3 <+3>:     push   %ebx
   0x080485b4 <+4>:     and    $0xfffffff0,%esp
   0x080485b7 <+7>:     sub    $0x20,%esp
   0x080485ba <+10>:    movl   $0x1c,(%esp)
   0x080485c1 <+17>:    call   0x8048490 <[email protected]>
   0x080485c6 <+22>:    mov    %eax,%ebx
   0x080485c8 <+24>:    mov    %ebx,(%esp)
   0x080485cb <+27>:    call   0x8048746 <_ZN25xuzhina_dump_c06_s5_childC2Ev>
   0x080485d0 <+32>:    mov    %ebx,0x1c(%esp)
   0x080485d4 <+36>:    mov    0x1c(%esp),%eax
   0x080485d8 <+40>:    mov    (%eax),%eax
   0x080485da <+42>:    add    $0x8,%eax
   0x080485dd <+45>:    mov    (%eax),%eax
   0x080485df <+47>:    movl   $0x0,0x4(%esp)
   0x080485e7 <+55>:    mov    0x1c(%esp),%edx
   0x080485eb <+59>:    mov    %edx,(%esp)
   0x080485ee <+62>:    call   *%eax
   0x080485f0 <+64>:    mov    0x1c(%esp),%eax
   0x080485f4 <+68>:    mov    (%eax),%eax
   0x080485f6 <+70>:    mov    (%eax),%eax
   0x080485f8 <+72>:    mov    0x1c(%esp),%edx
   0x080485fc <+76>:    mov    %edx,(%esp)
   0x080485ff <+79>:    call   *%eax
   0x08048601 <+81>:    mov    0x1c(%esp),%eax
   0x08048605 <+85>:    mov    %eax,0x18(%esp)
   0x08048609 <+89>:    mov    0x18(%esp),%eax
   0x0804860d <+93>:    mov    (%eax),%eax
   0x0804860f <+95>:    mov    (%eax),%eax
   0x08048611 <+97>:    mov    0x18(%esp),%edx
   0x08048615 <+101>:   mov    %edx,(%esp)
   0x08048618 <+104>:   call   *%eax
   0x0804861a <+106>:   cmpl   $0x0,0x1c(%esp)
   0x0804861f <+111>:   je     0x804862a <main+122>
   0x08048621 <+113>:   mov    0x1c(%esp),%eax
   0x08048625 <+117>:   add    $0xc,%eax
   0x08048628 <+120>:   jmp    0x804862f <main+127>
   0x0804862a <+122>:   mov    $0x0,%eax
   0x0804862f <+127>:   mov    %eax,0x14(%esp)
   0x08048633 <+131>:   mov    0x14(%esp),%eax
   0x08048637 <+135>:   mov    (%eax),%eax
   0x08048639 <+137>:   mov    (%eax),%eax
   0x0804863b <+139>:   mov    0x14(%esp),%edx
   0x0804863f <+143>:   mov    %edx,(%esp)
   0x08048642 <+146>:   call   *%eax
   0x08048644 <+148>:   mov    $0x0,%eax
   0x08048649 <+153>:   mov    -0x4(%ebp),%ebx
   0x0804864c <+156>:   leave
   0x0804864d <+157>:   ret
End of assembler dump.

由上面的汇编,可以看到,对象child的地址存放在esp+0x1c

而下面这几条指令

   0x08048601 <+81>:    mov    0x1c(%esp),%eax
   0x08048605 <+85>:    mov    %eax,0x18(%esp)
   0x08048609 <+89>:    mov    0x18(%esp),%eax
   0x0804860d <+93>:    mov    (%eax),%eax
   0x0804860f <+95>:    mov    (%eax),%eax
   0x08048611 <+97>:    mov    0x18(%esp),%edx
   0x08048615 <+101>:   mov    %edx,(%esp)
   0x08048618 <+104>:   call   *%eax

   0x08048621 <+113>:   mov    0x1c(%esp),%eax
   0x08048625 <+117>:   add    $0xc,%eax
   0x0804862f <+127>:   mov    %eax,0x14(%esp)
   0x08048633 <+131>:   mov    0x14(%esp),%eax
   0x08048637 <+135>:   mov    (%eax),%eax
   0x08048639 <+137>:   mov    (%eax),%eax
   0x0804863b <+139>:   mov    0x14(%esp),%edx
   0x0804863f <+143>:   mov    %edx,(%esp)
   0x08048642 <+146>:   call   *%eax

由于是和代码

69     xuzhina_dump_c06_s5_father* f = child;
 70     f->print();
 71
 72     xuzhina_dump_c06_s5_mother* m = child;
 73     m->print();

相对应的.

   0x08048625 <+117>:   add    $0xc,%eax

可以看到非常奇怪的现象,当类xuzhina_dump_c06_s5_child的指针转换成类xuzhina_dump_c06_s5-_mother的指针时,并不是直接赋值过去,而是比预料的地址加了一个偏移值.

那么这个偏移值0xC是怎么来的呢?根据上一节的经验,看一下构造函数的汇编:

(gdb) disassemble _ZN25xuzhina_dump_c06_s5_childC2Ev
Dump of assembler code for function _ZN25xuzhina_dump_c06_s5_childC2Ev:
   0x08048746 <+0>:     push   %ebp
   0x08048747 <+1>:     mov    %esp,%ebp
   0x08048749 <+3>:     sub    $0x18,%esp
   0x0804874c <+6>:     mov    0x8(%ebp),%eax
   0x0804874f <+9>:     mov    %eax,(%esp)
   0x08048752 <+12>:    call   0x804872a <_ZN26xuzhina_dump_c06_s5_fatherC2Ev>
   0x08048757 <+17>:    mov    0x8(%ebp),%eax
   0x0804875a <+20>:    add    $0xc,%eax
   0x0804875d <+23>:    mov    %eax,(%esp)
   0x08048760 <+26>:    call   0x8048738 <_ZN26xuzhina_dump_c06_s5_motherC2Ev>
   0x08048765 <+31>:    mov    0x8(%ebp),%eax
   0x08048768 <+34>:    movl   $0x8048848,(%eax)
   0x0804876e <+40>:    mov    0x8(%ebp),%eax
   0x08048771 <+43>:    movl   $0x804885c,0xc(%eax)
   0x08048778 <+50>:    leave
   0x08048779 <+51>:    ret
End of assembler dump.

由上面,类xuzhina_dump_c06_s5_child的对象实际就是两个类xuzhina_dump_c06_s5_father,xuzhina_dump_c06_s5_mother的对象组合,偏移值0xC刚好是类xuzhina_dump_c06_s5_father的大小(虚函数表指针+两个成员变量m_strong,m_age).

且由0x08048768, 0x08048771两条指令来看,类xuzhina_dump_c06_s5_child存放两个虚函数表,分别存放在0x8048848, 0x804885c.

看一下这两个表分别存放着什么东西.

第一个:

(gdb) x /4x 0x8048848
0x8048848 <_ZTV25xuzhina_dump_c06_s5_child+8>:  0x080486a8      0x08048690      0x080486c4      0xfffffff4
(gdb) info symbol 0x080486a8
xuzhina_dump_c06_s5_child::print() in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x08048690
xuzhina_dump_c06_s5_father::setStrong(int, int) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x080486c4
xuzhina_dump_c06_s5_child::setGender(bool) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5

第二个:

(gdb) x /4x 0x804885c
0x804885c <_ZTV25xuzhina_dump_c06_s5_child+28>: 0x080486bc      0x08048662      0x00000000      0x00000000
(gdb) info symbol 0x080486bc
non-virtual thunk to xuzhina_dump_c06_s5_child::print() in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x08048662
xuzhina_dump_c06_s5_mother::setBeauty(int, int) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) disassemble 0x080486bc
Dump of assembler code for function _ZThn12_N25xuzhina_dump_c06_s5_child5printEv:
   0x080486bc <+0>:     subl   $0xc,0x4(%esp)
   0x080486c1 <+5>:     jmp    0x80486a8 <_ZN25xuzhina_dump_c06_s5_child5printEv>
End of assembler dump.

从上面看,类xuzhina_dump_c06_s5_child的对象内存分布如下:

由上可知,在多继承情况下,有这样的结论:

1.     有多个虚函数表.

2.     类对象的大小就是等于各个基类的大小(虚函数表指针+成员变量)与自身成员变量之和.

3.     各个基类在子类里的”隐含对象”顺序是按照继承顺序来排列,和基类的声明/定义顺序无关.

4.     当子类对象指针转换成基类指针,实际上是把子类对象的对应基类”隐含对象”地址赋值给基类指针.

对于有共同基类的多继承,也可以按照上面思路来探索.对于基类和子类有同名虚函数,也可以这样探索.

时间: 2024-08-28 02:50:32

《coredump问题原理探究》Linux x86版6.7节多继承的相关文章

《coredump问题原理探究》Linux x86版6.8节多继承coredump例子

下面看一个coredump的例子: (gdb) bt #0 0x08048662 in xuzhina_dump_c06_s5_ex_child::inheritFrom(char*, int) () #1 0x08048609 in main () 先看一下xuzhina_dump_c06_s5_ex_child::inheritFrom的汇编: (gdb) disassemble 0x08048662 Dump of assembler code for function _ZN28xuzh

《coredump问题原理探究》Linux x86版6.6节单继承

在C++里,类的多态是通过继承由虚函数来体现的.那么在单继承中,类的成员变量和虚函数的分布又是怎样的呢? 看一下例子: 1 #include <stdio.h> 2 class xuzhina_dump_c06_s4_base 3 { 4 private: 5 int m_a; 6 public: 7 xuzhina_dump_c06_s4_base() { m_a = 1; } 8 virtual void inc() 9 { 10 m_a++; 11 } 12 virtual void p

《coredump问题原理探究》Windows版 笔记

<coredump问题原理探究>Windows版 笔记 Debug 一.环境搭建 1.Win7捕获程序dump 2.Windbg符号表设置(Symbols Search Path) 二.WinDbg命令 三.函数栈帧 1.栈内存布局 2.栈溢出 3.栈的规律 4.定位栈溢出问题的经验方法 四.函数逆向 五.C内存布局 1.基本类型 2.数组类型 3.结构体 六.C++内存布局 1.类的内存布局 2.this指针 3.虚函数表及虚表指针 4.单继承 5.多继承(无公共基类) 七.STL容器内存布

《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子

在探究完类成员变量分布后,来定位一个coredump例子来实践一把: (gdb) bt #0 0x0804863c in xuzhina_dump_c06_s2_ex::print() () #1 0x08048713 in main () 看一下xuzhina_dump_c06_s2_ex::print的汇编: (gdb) disassemble 0x0804863c Dump of assembler code for function _ZN22xuzhina_dump_c06_s2_ex

《coredump问题原理探究》Linux x86版7.2节vector coredump例子

看一个coredump的例子: [[email protected] s1_ex]$ gdb xuzhina_dump_c07_s1_ex core.27776 GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6) Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses

《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类

上面一节已经探究出this指针的辨别,由this指针就可以看到类的内容.在这里,就由this指针来看一下类的成员变量是如何排列. 先看一个例子 1 #include <stdio.h> 2 class xuzhina_dump_c06_s2 3 { 4 private: 5 short m_c; 6 char m_d; 7 int m_e; 8 9 public: 10 xuzhina_dump_c06_s2( int a, int b ) 11 { 12 m_c = (short)(a +

《coredump问题原理探究》Linux x86版7.5节 Map对象

先看一个例子: 1 #include <map> 2 3 int main() 4 { 5 std::map<int,int> iMap; 6 7 iMap[5] = 6; 8 iMap[8] = 20; 9 iMap[2] = 80; 10 11 return 0; 12 } 看一下汇编: (gdb) disassemble main Dump of assembler code for function main: 0x080486e4 <+0>: push %eb

《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类

在探究完C风格数据结构内存布局之后,接着探究C++风格数据结构内存布局. 虽然最简单的类是没有任何成员变量和成员函数,但由于没什么意义,不值得探究.在这里,就先探究一下没有任何成员变量和虚函数,只有成员函数的类. 先看一下例子: 1 #include <stdio.h> 2 class xuzhina_dump_c06_s1 3 { 4 public: 5 void hello() 6 { 7 printf( "hello\n" ); 8 } 9 void print()

《coredump问题原理探究》Linux x86版7.4节List coredump例子

看一个coredump例子: 看一个coredump例子: Core was generated by `./xuzhina_dump_c07_s2_ex'. Program terminated with signal 11, Segmentation fault. #0 0x0285b9b7 in std::_List_node_base::hook(std::_List_node_base*) () from /usr/lib/libstdc++.so.6 Missing separate