开发环境
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
1, non-static成员函数调用过程
class X定义了一个virtual function foo:函数调用过程(栈帧)class X定义了一个virtual function foo:
代码语言:javascript复制#include<iostream>
using namespace std;
//不分析virtual dctor
class X{
public:
virtual void foo();
private:
int _x;
};
void X::foo()
{
cout<<_x<<endl;
}
X foobar()
{
X xx;
X *px = new X;
xx.foo();
px->foo();
delete px;
return xx;
}
int main()
{
foobar();
return 0;
}
该程序对应的汇编代码如下(g -S -m32 p13.cc):
代码语言:javascript复制_ZN1X3fooEv: //X::foo()
.LFB1021:
.cfi_startproc
pushl �p //ebp压栈([esp]=ebp, esp=esp-4)
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p //ebp=esp
.cfi_def_cfa_register 5
subl $8, %esp //esp=esp-8
movl 8(�p), �x //eax=[ebp 8],即eax=this
movl 4(�x), �x //eax=[eax 4], 即eax=this->_x
subl $8, %esp //esp=esp-8
pushl �x //[esp]=eax, esp=esp-4, 即this->_x压栈
pushl $_ZSt4cout //std::cout压栈
call _ZNSolsEi //operator<< 对应两步操作:1,retAddr压栈 2,IP=operator<< func
addl $16, %esp
subl $8, %esp
pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
pushl �x
call _ZNSolsEPFRSoS_E
addl $16, %esp
nop
leave //ebp=esp, pop ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret //pop IP
.cfi_endproc
.LFE1021:
.size _ZN1X3fooEv, .-_ZN1X3fooEv
.section .text._ZN1XC2Ev,"axG",@progbits,_ZN1XC5Ev,comdat
.align 2
.weak _ZN1XC2Ev
.type _ZN1XC2Ev, @function
代码语言:javascript复制_ZN1XC2Ev: //X::X() 默认构造函数
.LFB1024:
.cfi_startproc
pushl �p //ebp压栈
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p //ebp = esp
.cfi_def_cfa_register 5
movl $_ZTV1X 8, �x //edx=vptr
movl 8(�p), �x //eax=[ebp 8],即eax=this
movl �x, (�x) //[eax]=edx, 即[this]=vptr
nop
popl �p //ebp=old ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1024:
.size _ZN1XC2Ev, .-_ZN1XC2Ev
.weak _ZN1XC1Ev
.set _ZN1XC1Ev,_ZN1XC2Ev
.text
.globl _Z6foobarv
.type _Z6foobarv, @function
代码语言:javascript复制_Z6foobarv: //foobar
.LFB1022:
.cfi_startproc
pushl �p //ebp压栈
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, �p //ebp=esp
.cfi_def_cfa_register 5
pushl �x //ebx压栈
subl $36, %esp //esp=esp-36
.cfi_offset 3, -12
movl 8(�p), �x //eax=tmp.this (返回值的首地址,即this指针)
movl �x, -28(�p) //[ebp-28]=tmp.this
movl %gs:20, �x
movl �x, -12(�p)
xorl �x, �x //eax=0
subl $12, %esp //esp=esp-12
pushl -28(�p) //push xx.this, 即xx对象首地址压栈
call _ZN1XC1Ev //X::X(), 即默认构造函数
addl $16, %esp
subl $12, %esp
pushl $8 //分配内存operator new size=8,
call _Znwj //operator new
addl $16, %esp
movl �x, �x //eax为new函数返回值(px.this指针), ebx=eax,
subl $12, %esp
pushl �x //push px.this
call _ZN1XC1Ev //X::X(),构造px指针指向的对象
addl $16, %esp
movl �x, -16(�p) //[ebp-16]=px.this
subl $12, %esp
pushl -28(�p) //xx对象this指针压栈
call _ZN1X3fooEv //xx.foo()
addl $16, %esp
movl -16(�p), �x //eax=px.this
movl (�x), �x //eax=vptr
movl (�x), �x //eax=[vptr],即eax=virtual foo()
subl $12, %esp
pushl -16(�p) //px.this压栈
call *�x //call virtual foo
addl $16, %esp
subl $12, %esp
pushl -16(�p)
call _ZdlPv //delete px
addl $16, %esp
nop
movl -28(�p), �x
movl -12(�p), �x
xorl %gs:20, �x
je .L5
call __stack_chk_fail
.L5:
movl -4(�p), �x
leave
.cfi_restore 5
.cfi_restore 3
.cfi_def_cfa 4, 4
ret $4
.cfi_endproc
代码语言:javascript复制main:
.LFB1029:
.cfi_startproc
leal 4(%esp), �x
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(�x)
pushl �p
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, �p
pushl �x
.cfi_escape 0xf,0x3,0x75,0x7c,0x6
subl $36, %esp
movl %gs:20, �x
movl �x, -12(�p)
xorl �x, �x
leal -40(�p), �x
subl $12, %esp
pushl �x //tmp.this压栈
call _Z6foobarv //foobar
addl $12, %esp
movl $0, �x
movl -12(�p), �x
xorl %gs:20, �x
je .L8
call __stack_chk_fail
.L8:
movl -4(�p), �x
.cfi_def_cfa 1, 0
leave
.cfi_restore 5
leal -4(�x), %esp
.cfi_def_cfa 4, 4
ret
.cfi_endproc
2,通过分析cpp代码对应的汇编,发现foobar函数在内部被转化为:
代码语言:javascript复制void foobar(X *result)
{
//构造result指向的对象
X::X(result);
px = malloc(sizeof(X));
X::X(px);
//xx.foo()不采用virtual机制
X::foo(result);
//px->foo()采用virtual机制
(px->vptr[0])(px);
//略
}