码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2014-11-30 10:21:23      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:linux   x86   调试技术   程序崩溃   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 + b);
 13	             m_d = 'd';
 14	             m_e = a - b;
 15	         }
 16	         void print( )
 17	         {
 18	             printf( "member %d, %c, %d\n", m_c, m_d, m_e );
 19	         }
 20	 };
 21 
 22	 int main()
 23	 {
 24	     xuzhina_dump_c06_s2 test( 2, 3 );
 25	     test.print();
 26	     return 0;
 27	 }

汇编代码:

(gdb) disassemble main
Dump of assembler code for function main:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp
   0x08048579 <+9>:     movl   $0x3,0x8(%esp)
   0x08048581 <+17>:    movl   $0x2,0x4(%esp)
   0x08048589 <+25>:    lea    0x18(%esp),%eax
   0x0804858d <+29>:    mov    %eax,(%esp)
   0x08048590 <+32>:    call   0x80485b2 <_ZN19xuzhina_dump_c06_s2C2Eii>
   0x08048595 <+37>:    lea    0x18(%esp),%eax
   0x08048599 <+41>:    mov    %eax,(%esp)
   0x0804859c <+44>:    call   0x80485de <_ZN19xuzhina_dump_c06_s25printEv>
   0x080485a1 <+49>:    mov    $0x0,%eax
   0x080485a6 <+54>:    jmp    0x80485b0 <main+64>
   0x080485a8 <+56>:    mov    %eax,(%esp)
   0x080485ab <+59>:    call   0x8048460 <_Unwind_Resume@plt>
   0x080485b0 <+64>:    leave  
   0x080485b1 <+65>:    ret    
End of assembler dump.

由上面代码可得知,test在构造完成后,它三个成员的值分别是5, ‘d’,-1(0xffffffff), 且由上一节得知,在调用类的成员函数时,this指针作为第一个参数放入栈里。

所以,可以在指令地址0x0804859c打断点看看,当函数运行到断点时,ecx所指向内存是怎样存放着5,’d’,-1这三个值。

(gdb) tbreak *0x0804859c
Temporary breakpoint 1 at 0x804859c
(gdb) r
Starting program: /home/buckxu/work/6/2/xuzhina_dump_c6_s2 

Temporary breakpoint 1, 0x0804859c in main ()
 (gdb) x /8x $esp+0x18
0xbffff478:     0x08640005      0xffffffff      0x08048620      0x00000000
0xbffff488:     0x00000000      0x4362f635      0x00000001      0xbffff524
(gdb) x /c 0xbffff47a
0xbffff47a:     100 'd'

可以看到this指针指向内存的内容按照由低到高的顺序分别是5, d, -1.由于m_c, m_d分别是char,short类型,所以,它们就挤在同一个32-bit单元里,进行内存对齐。

由此可知,类的成员变量排列和结构体没什么区别,只是在调用成员函数时,this指针会作为成员函数第一个参数放入栈中。即定位coredump问题,可以看看在调用类成员函数时看一下它的第一个参数,找到this指针,然后根据this指针查看类每个成员变量的值。

同时,上面可以看到,调用类成员函数和调用普通函数的区别就是,在每次调用类成员函数时,都会把this指针作为第一个参数传递。这个区别应该就是类成员函数可以直接调用类成员变量的原因,因为可以把第一个参数作为基址,来访问变量。

可以看一下类xuzhina_dump_c06_s2的print函数的汇编:

(gdb) shell c++filt _ZN19xuzhina_dump_c06_s25printEv
xuzhina_dump_c06_s2::print()
(gdb) disassemble _ZN19xuzhina_dump_c06_s25printEv
Dump of assembler code for function _ZN19xuzhina_dump_c06_s25printEv:
   0x080485de <+0>:     push   %ebp
   0x080485df <+1>:     mov    %esp,%ebp
   0x080485e1 <+3>:     sub    $0x18,%esp
   0x080485e4 <+6>:     mov    0x8(%ebp),%eax	//this指针
   0x080485e7 <+9>:     mov    0x4(%eax),%ecx	//m_e
   0x080485ea <+12>:    mov    0x8(%ebp),%eax	//this指针
   0x080485ed <+15>:    movzbl 0x2(%eax),%eax		// m_d
   0x080485f1 <+19>:    movsbl %al,%edx
   0x080485f4 <+22>:    mov    0x8(%ebp),%eax	//this指针
   0x080485f7 <+25>:    movzwl (%eax),%eax		//m_c
   0x080485fa <+28>:    cwtl   
   0x080485fb <+29>:    mov    %ecx,0xc(%esp)
   0x080485ff <+33>:    mov    %edx,0x8(%esp)
   0x08048603 <+37>:    mov    %eax,0x4(%esp)
   0x08048607 <+41>:    movl   $0x80486b4,(%esp)
   0x0804860e <+48>:    call   0x8048440 <printf@plt>
   0x08048613 <+53>:    leave  
   0x08048614 <+54>:    ret    
End of assembler dump.

从而可以看到,类成员函数和普通的区别确实在于会把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 + b);
 13	             m_d = 'd';
 14	             m_e = a - b;
 15	         }
 16	         void print( )
 17	         {
 18	             printf( "member %d, %c, %d\n", m_c, m_d, m_e );
 19	         }
 20	 };
 21	 typedef void (xuzhina_dump_c06_s2::*func_ptr)();
 22	 int main()
 23	 {
 24	     xuzhina_dump_c06_s2 test( 2, 3 );
 25	     func_ptr clsFuncPtr = &xuzhina_dump_c06_s2::print;
 26	     (test.*clsFuncPtr)();
 27	     return 0;
 28	 }

看一下main函数的汇编:

(gdb) disassemble main
Dump of assembler code for function main:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp
   0x08048579 <+9>:     movl   $0x3,0x8(%esp)
   0x08048581 <+17>:    movl   $0x2,0x4(%esp)
   0x08048589 <+25>:    lea    0x18(%esp),%eax
   0x0804858d <+29>:    mov    %eax,(%esp)
   0x08048590 <+32>:    call   0x80485ee <_ZN19xuzhina_dump_c06_s2C2Eii>
   0x08048595 <+37>:    movl   $0x804861a,0x10(%esp)
   0x0804859d <+45>:    movl   $0x0,0x14(%esp)
   0x080485a5 <+53>:    mov    0x10(%esp),%eax
   0x080485a9 <+57>:    and    $0x1,%eax
   0x080485ac <+60>:    test   %eax,%eax
   0x080485ae <+62>:    jne    0x80485b6 <main+70>
   0x080485b0 <+64>:    mov    0x10(%esp),%eax
   0x080485b4 <+68>:    jmp    0x80485cd <main+93>
   0x080485b6 <+70>:    mov    0x14(%esp),%eax
   0x080485ba <+74>:    lea    0x18(%esp),%edx
   0x080485be <+78>:    add    %edx,%eax
   0x080485c0 <+80>:    mov    (%eax),%edx
   0x080485c2 <+82>:    mov    0x10(%esp),%eax
   0x080485c6 <+86>:    sub    $0x1,%eax
   0x080485c9 <+89>:    add    %edx,%eax
   0x080485cb <+91>:    mov    (%eax),%eax
   0x080485cd <+93>:    mov    0x14(%esp),%edx
   0x080485d1 <+97>:    lea    0x18(%esp),%ecx
   0x080485d5 <+101>:   add    %ecx,%edx
   0x080485d7 <+103>:   mov    %edx,(%esp)
   0x080485da <+106>:   call   *%eax
   0x080485dc <+108>:   mov    $0x0,%eax
   0x080485e1 <+113>:   jmp    0x80485eb <main+123>
   0x080485e3 <+115>:   mov    %eax,(%esp)
   0x080485e6 <+118>:   call   0x8048460 <_Unwind_Resume@plt>
   0x080485eb <+123>:   leave  
   0x080485ec <+124>:   ret    
End of assembler dump.

可见,类成员函数指针的使用和类成员函数一样,都会把this指针作为成员函数的第一个参数。这也是为什么调用类成员函数时要指定对象或对象指针。如这个例子的

(test.*clsFuncPtr)();


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

标签:linux   x86   调试技术   程序崩溃   c++   

原文地址:http://blog.csdn.net/xuzhina/article/details/41620563

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!