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

【反汇编分析】C++成员函数和虚函数

时间:2015-04-22 20:43:53      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

       本节通过反汇编研究C++非static成员函数和虚函数的执行流程;


代码片段如下

class Animal
{
public:
  virtual void print()
  {
    cout << "Animal::print "<< endl;
  }
  
  void print2()
  {
    cout << "Animal::print2 "<< endl;
  }
};

class Dog : public Animal
{
public:
  Dog():_age(1)
  {

  }

  virtual void print()
  {
    cout << "Dog::print " << _age << endl;
  }
  
  void print2()
  {
    cout << "Dog::print2 "<< endl;
  }
private:
  int _age;
};


int main(void)
{
  Dog d;
  Animal* p1 = &d;

  p1->print();
  p1->print2();
  
  cout << "_age address: "<< ((int*)&d+1) << " value: " << *((int*)&d+1)<< endl;  
  cout << "virtual fun address: "<< (int*)&d << endl;  
  cout << "virtual fun[1] address: "<< (int*)*(int*)&d << endl;  
  cout << "virtual fun[2] address: "<< ((int*)*(int*)&d+1) << endl;  
}
输出如下:

sykpour@sykpour:~/Desktop$ ./test
Dog::print 1
Animal::print2 
_age address: 0xbfc5c7f8 value: 1
virtual fun address: 0xbfc5c7f4
virtual fun[1] address: 0x8048c30
virtual fun[2] address: 0x8048c34
说明几点:

(1)本节只会研究p1->print()和p1->print2()的反汇编代码,对于mian函数的下面cout输出部分,只是为了验证;


Dog类的虚函数表示意图如下:

技术分享



print和print2调用的函数栈帧如下:

技术分享




main函数的反汇编

print和print2相关部分反汇编如下

0804882c <main>:
 804882c:	8d 4c 24 04          	lea    0x4(%esp),%ecx
 8048830:	83 e4 f0             	and    $0xfffffff0,%esp
 8048833:	ff 71 fc             	pushl  -0x4(%ecx)
 8048836:	55                   	push   %ebp         #放入ebp
 8048837:	89 e5                	mov    %esp,%ebp
 8048839:	53                   	push   %ebx
 804883a:	51                   	push   %ecx
 804883b:	83 ec 10             	sub    $0x10,%esp       
 804883e:	83 ec 0c             	sub    $0xc,%esp
 8048841:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048844:	50                   	push   %eax
 8048845:	e8 0a 02 00 00       	call   8048a54 <_ZN3DogC1Ev>    #调用dog的构造函数
 804884a:	83 c4 10             	add    $0x10,%esp
 804884d:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048850:	89 45 f4             	mov    %eax,-0xc(%ebp)     #将Dog d的地址赋值给p1
 8048853:	8b 45 f4             	mov    -0xc(%ebp),%eax     #取p1
 8048856:	8b 00                	mov    (%eax),%eax         #取virtual虚函数表的地址
 8048858:	8b 00                	mov    (%eax),%eax         #取第一个虚函数的函数地址
 804885a:	83 ec 0c             	sub    $0xc,%esp           
 804885d:	ff 75 f4             	pushl  -0xc(%ebp)          #将p1压入作为参数
 
 #取0x8048c3处的内容执行,即执行第一个虚函数,其实是函数地址08048a7e,即_ZN3Dog5printEv的函数地址,调用Dog::print
 8048860:	ff d0                	call   *%eax 
 
 8048862:	83 c4 10             	add    $0x10,%esp
 8048865:	83 ec 0c             	sub    $0xc,%esp
 8048868:	ff 75 f4             	pushl  -0xc(%ebp)
 804886b:	e8 a8 01 00 00       	call   8048a18 <_ZN6Animal6print2Ev>  #调用Animal::print2
 8048870:	83 c4 10             	add    $0x10,%esp
 8048873:	8d 45 ec             	lea    -0x14(%ebp),%eax


Dog的构造函数反汇编如下

08048a54 <_ZN3DogC1Ev>:
 8048a54:	55                   	push   %ebp
 8048a55:	89 e5                	mov    %esp,%ebp
 8048a57:	83 ec 08             	sub    $0x8,%esp
 8048a5a:	8b 45 08             	mov    0x8(%ebp),%eax     #取Dog d的地址
 8048a5d:	83 ec 0c             	sub    $0xc,%esp
 8048a60:	50                   	push   %eax
 8048a61:	e8 e0 ff ff ff       	call   8048a46 <_ZN6AnimalC1Ev> #调用Animal的构造函数
 8048a66:	83 c4 10             	add    $0x10,%esp
 8048a69:	8b 45 08             	mov    0x8(%ebp),%eax
 8048a6c:	c7 00 30 8c 04 08    	movl   $0x8048c30,(%eax)  #Dog虚函数表地址
 8048a72:	8b 45 08             	mov    0x8(%ebp),%eax
 8048a75:	c7 40 04 01 00 00 00 	movl   $0x1,0x4(%eax)   #_age为1
 8048a7c:	c9                   	leave  
 8048a7d:	c3                   	ret    

Animal的构造函数反汇编如下

08048a46 <_ZN6AnimalC1Ev>:
 8048a46:	55                   	push   %ebp
 8048a47:	89 e5                	mov    %esp,%ebp
 8048a49:	8b 45 08             	mov    0x8(%ebp),%eax
 8048a4c:	c7 00 40 8c 04 08    	movl   $0x8048c40,(%eax)  #Animal虚函数表地址
 8048a52:	5d                   	pop    %ebp
 8048a53:	c3                   	ret    


Dog虚函数表反汇编如下

08048c28 <_ZTV3Dog>:
 8048c28:	00 00                	add    %al,(%eax)
 8048c2a:	00 00                	add    %al,(%eax)
 8048c2c:	4c                   	dec    %esp
 8048c2d:	8c 04 08             	mov    %es,(%eax,%ecx,1)
 8048c30:	7e 8a                	jle    8048bbc <_IO_stdin_used+0x28>
 8048c32:	04 08                	add    $0x8,%al
 8048c34:	00 00                	add    %al,(%eax)
说明几点:

(1)gcc采用小端存放,因此0x8048c30处的内容为0x08048a7e;具体如下

 8048c30:	7e 8a                	jle    8048bbc <_IO_stdin_used+0x28>
 8048c32:	04 08                	add    $0x8,%al
                小  大

(2)我们知道虚函数表的地址为0x8048c30,而print为Dog虚函数表的第一个函数,因此call   *%eax实际上执行的是0x8048c30地址存放的函数地址,即为08048a7e地址,正好对应Dog::print的函数地址;


Dog::print函数反汇编如下

08048a7e <_ZN3Dog5printEv>:
 8048a7e:	55                   	push   %ebp
 8048a7f:	89 e5                	mov    %esp,%ebp
 8048a81:	53                   	push   %ebx
 8048a82:	83 ec 04             	sub    $0x4,%esp
 8048a85:	8b 45 08             	mov    0x8(%ebp),%eax   #获得Dog d的地址
 8048a88:	8b 58 04             	mov    0x4(%eax),%ebx   #此时_age的值在ebx中
 8048a8b:	83 ec 08             	sub    $0x8,%esp
 8048a8e:	68 b7 8b 04 08       	push   $0x8048bb7
 8048a93:	68 80 91 04 08       	push   $0x8049180   #调用cout的地址
 8048a98:	e8 1f fc ff ff       	call   80486bc <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
 8048a9d:	83 c4 10             	add    $0x10,%esp
 8048aa0:	83 ec 08             	sub    $0x8,%esp
 8048aa3:	53                   	push   %ebx
 8048aa4:	50                   	push   %eax
 8048aa5:	e8 b2 fb ff ff       	call   804865c <_ZNSolsEi@plt>
 8048aaa:	83 c4 10             	add    $0x10,%esp
 8048aad:	83 ec 08             	sub    $0x8,%esp
 8048ab0:	68 ec 86 04 08       	push   $0x80486ec
 8048ab5:	50                   	push   %eax
 8048ab6:	e8 21 fc ff ff       	call   80486dc <_ZNSolsEPFRSoS_E@plt>
 8048abb:	83 c4 10             	add    $0x10,%esp
 8048abe:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048ac1:	c9                   	leave  
 8048ac2:	c3                   	ret    


Animal::print2函数反汇编如下

08048a18 <_ZN6Animal6print2Ev>:
 8048a18:	55                   	push   %ebp
 8048a19:	89 e5                	mov    %esp,%ebp
 8048a1b:	83 ec 08             	sub    $0x8,%esp
 8048a1e:	83 ec 08             	sub    $0x8,%esp
 8048a21:	68 a7 8b 04 08       	push   $0x8048ba7
 8048a26:	68 80 91 04 08       	push   $0x8049180
 8048a2b:	e8 8c fc ff ff       	call   80486bc <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
 8048a30:	83 c4 10             	add    $0x10,%esp
 8048a33:	83 ec 08             	sub    $0x8,%esp
 8048a36:	68 ec 86 04 08       	push   $0x80486ec
 8048a3b:	50                   	push   %eax
 8048a3c:	e8 9b fc ff ff       	call   80486dc <_ZNSolsEPFRSoS_E@plt>
 8048a41:	83 c4 10             	add    $0x10,%esp
 8048a44:	c9                   	leave  
 8048a45:	c3                   	ret    

【反汇编分析】C++成员函数和虚函数

标签:

原文地址:http://blog.csdn.net/skyuppour/article/details/45199509

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