标签:
本节通过反汇编研究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输出部分,只是为了验证;
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
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
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的函数地址;
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
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
标签:
原文地址:http://blog.csdn.net/skyuppour/article/details/45199509