[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

What should the call stack look like when calling a C++ virtual function

From: Dev1024
Subject: What should the call stack look like when calling a C++ virtual function?
Date: 23 Sep 2006 14:31:05 -0700
User-agent: G2/1.0

Having been a long time user of Microsoft Visual C++ .Net 2003 (MSVC),
I've recently been using g++ on a project.  I've found a difference
between the two compilers when calling a virtual function that has me
puzzled.  I'm hoping someone will know the answer or point me in the
right direction to find the answer.

The issue is when calling virtual functions.  In MSVC the parameters
are pushed on to the stack as expected and then the address of the
function is loaded into EAX.  Finally "call EAX" is issues to call the
virtual function.  In g++ (Cygwin g++ v3.4.4) the parameters are pushed
onto the stack as expected, but then a pointer (32 bit pointer in my
case) to the vtable is pushed on the stack as well then finally EAX is
loaded with the address of the function.

- Why does g++ load a pointer to the vtable on the stack after pushing
on the params?
- Is there a compiler option that can be used to avoid putting a
pointer to the vtable on the stack?  (I looked but couldn't find one.)

Sincere thanks for your time and attention!

Details and background material:
C++ Source Code
#include <stdio.h>

class ITest
        void virtual TestFunc(int x, int y, int z) = 0;

class Test : ITest
        Test() {}
        ~Test() {}

        void TestFunc(int x, int y, int z)
                int a;
                a = x + y + z;

int main (void)
        ITest *testptr;
        testptr = (ITest *) new Test();
        testptr->TestFunc(1, 2, 3);
        delete testptr;

To build with g++:
g++ -g -o myapp main.cpp

In MSVC build as release to get the code below.  this remove all the
debug stuff Microsoft adds for error cheching.

In gdb it is disassembled as follows.  Note my comments
(gdb) disas
Dump of assembler code for function main:
0x00401050 <main+0>:    push   %ebp
0x00401051 <main+1>:    mov    %esp,%ebp
0x00401053 <main+3>:    push   %ebx
0x00401054 <main+4>:    sub    $0x24,%esp
0x00401057 <main+7>:    and    $0xfffffff0,%esp
0x0040105a <main+10>:   mov    $0x0,%eax
0x0040105f <main+15>:   add    $0xf,%eax
0x00401062 <main+18>:   add    $0xf,%eax
0x00401065 <main+21>:   shr    $0x4,%eax
0x00401068 <main+24>:   shl    $0x4,%eax
0x0040106b <main+27>:   mov    %eax,0xfffffff4(%ebp)
0x0040106e <main+30>:   mov    0xfffffff4(%ebp),%eax
0x00401071 <main+33>:   call   0x403d50 <_alloca>
0x00401076 <main+38>:   call   0x404a90 <__main>
0x0040107b <main+43>:   movl   $0x4,(%esp)
0x00401082 <main+50>:   call   0x4022a0 <_Znwj>
0x00401087 <main+55>:   mov    %eax,%ebx
0x00401089 <main+57>:   mov    %ebx,(%esp)
0x0040108c <main+60>:   call   0x404e14 <_ZN4TestC1Ev>
0x00401091 <main+65>:   mov    %ebx,%eax
0x00401093 <main+67>:   mov    %eax,0xfffffff8(%ebp)
0x00401096 <main+70>:   mov    0xfffffff8(%ebp),%eax
0x00401099 <main+73>:   mov    (%eax),%edx        //save the address of
the function in EDX
0x0040109b <main+75>:   movl   $0x3,0xc(%esp)    //push params onto
0x004010a3 <main+83>:   movl   $0x2,0x8(%esp)
0x004010ab <main+91>:   movl   $0x1,0x4(%esp)
0x004010b3 <main+99>:   mov    0xfffffff8(%ebp),%eax  //Address of the
vtable is loaded into EAX
0x004010b6 <main+102>:  mov    %eax,(%esp)           //Now it is pushed
on the stack.  Why???????
0x004010b9 <main+105>:  mov    (%edx),%eax   //Load EAX with address of
virtual function
0x004010bb <main+107>:  call   *%eax
0x004010bd <main+109>:  mov    0xfffffff8(%ebp),%eax
0x004010c0 <main+112>:  mov    %eax,(%esp)
0x004010c3 <main+115>:  call   0x402280 <_ZdlPv>
0x004010c8 <main+120>:  mov    $0x0,%eax
0x004010cd <main+125>:  mov    0xfffffffc(%ebp),%ebx
0x004010d0 <main+128>:  leave
0x004010d1 <main+129>:  ret

In MSVC it compiles as follows. Note it does not put a pointer to the
vtable on the stack.

int main (void)
00401010  push        esi
        ITest *testptr;
        testptr = (ITest *) new Test();
00401011  push        4
00401013  call        operator new (401044h)
00401018  add         esp,4
0040101B  test        eax,eax
0040101D  je          main+19h (401029h)
0040101F  mov         dword ptr [eax],offset Test::`vftable' (4060ECh)
00401025  mov         esi,eax
00401027  jmp         main+1Bh (40102Bh)
00401029  xor         esi,esi
        testptr->TestFunc(1, 2, 3);
0040102B  mov         eax,dword ptr [esi]
0040102D  push        3
0040102F  push        2
00401031  push        1
00401033  mov         ecx,esi
00401035  call        dword ptr [eax]
        delete testptr;
00401037  push        esi
00401038  call        operator delete (401108h)
0040103D  add         esp,4
00401040  xor         eax,eax 
00401042  pop         esi  
00401043  ret

reply via email to

[Prev in Thread] Current Thread [Next in Thread]