in the name of zero

April 5, 2007

virtuals

so i’m back with more of the shit that is C++ learning. i’ve gotten the hang of typing “cout” now instead of “printf” but more about this some other time. recently, i’m learning about virtual functions and i have this as an example:

class animal {
        public:
                void normal() { }
                virtual void normvirtual() { }
                virtual void purevirtual() = 0;
};
	
class sheep : public animal {
        public:
                void normvirtual() {
                        cout<<"sheep from normvirtual"<<endl;
                }    
	
                void purevirtual() {
                        cout<<"sheep from purevirtual"<<endl;
                }
};

again, using IDA and objdump …

 sheep   judy;
	
 8048803:       8d 45 e8                lea    eax,[ebp-24]
 8048806:       50                      push   eax
 8048807:       e8 8a 00 00 00          call   8048896 <sheep::sheep()>

nothing much happening here. the usual thing. a *this pointer being passed to a default constructor as a hidden argument.

08048896 <sheep::sheep()>:
 8048896:       55                      push   ebp		; save current stack frame
 8048897:       89 e5                   mov    ebp,esp		; new stack frame
 8048899:       83 ec 08                sub    esp,0x8		; space
 804889c:       83 ec 0c                sub    esp,0xc		; space
 804889f:       ff 75 08                push   DWORD PTR [ebp+8]		; our *this pointer
 80488a2:       e8 4d 00 00 00          call   80488f4 <animal::animal()>	; call animal constructor
 80488a7:       83 c4 10                add    esp,0x10				; cleanup
 80488aa:       8b 45 08                mov    eax,DWORD PTR [ebp+8]		; dereference
 80488ad:       c7 00 80 8a 04 08       mov    DWORD PTR [eax],0x8048a80	; see below...
 80488b3:       c9                      leave  			; epilog
 80488b4:       c3                      ret    			; return to callee
 80488b5:       90                      nop    			; byte pad (no operation)

so what does mov DWORD PTR [eax],0x8048a80 do? following the memory address in ida gives me an array of sheep class functions. objdump tells me that this block is called a “vtable” for my sheep class. 0x8048a80 is prolly what they call the “VPTR” pointer.

and then i called the get_kind() function.

 get_kind(judy);
	
 8048812:       8d 45 e8                lea    eax,[ebp-24]
 8048815:       50                      push   eax
 8048816:       e8 9b 00 00 00          call   80488b6 <get_kind(animal&)>

i learned from prior readings about “upcasting” and how a derived class object can be assigned to base class object. and this is how i interpreted things…

inline void get_kind(animal &a)
{
	a.normal();
	a.normvirtual();
	a.purevirtual();
}   
	
080488b6 <get_kind(animal&)>:
 80488b6:       55                      push   ebp
 80488b7:       89 e5                   mov    ebp,esp			; prolog
 80488b9:       83 ec 08                sub    esp,0x8
 80488bc:       83 ec 0c                sub    esp,0xc			; spaces
 80488bf:       ff 75 08                push   DWORD PTR [ebp+8]		; object as hidden argument
 80488c2:       e8 3b 00 00 00          call   8048902 <animal::normal()> 	; call the animal::normal() method
 80488c7:       83 c4 10                add    esp,0x10			; cleanup
 80488ca:       83 ec 0c                sub    esp,0xc			; space
	
 80488cd:       8b 45 08                mov    eax,DWORD PTR [ebp+8]	; *this pointer (VPTR)
 80488d0:       8b 00                   mov    eax,DWORD PTR [eax]	; dereference
 80488d2:       ff 75 08                push   DWORD PTR [ebp+8]	; pass object again as hidden argument
 80488d5:       8b 00                   mov    eax,DWORD PTR [eax]	; derefence again
 80488d7:       ff d0                   call   eax			; call the first virtual function
 80488d9:       83 c4 10                add    esp,0x10			; cleanup
 80488dc:       83 ec 0c                sub    esp,0xc			; space
	
 80488df:       8b 45 08                mov    eax,DWORD PTR [ebp+8]	; *this pointer (VPTR)
 80488e2:       8b 00                   mov    eax,DWORD PTR [eax]	; dereference
 80488e4:       83 c0 04                add    eax,0x4			; move to next function in array of funtions
 80488e7:       ff 75 08                push   DWORD PTR [ebp+8]	; pass object as hidden argument
 80488ea:       8b 00                   mov    eax,DWORD PTR [eax]	; dereference
 80488ec:       ff d0                   call   eax			; call second virtual function
	
 80488ee:       83 c4 10                add    esp,0x10			; cleanup
 80488f1:       c9                      leave  				; epilog
 80488f2:       c3                      ret    				; return to callee
 80488f3:       90                      nop  				; byte pad (no operation)

i wonder if i got the above things right? dynamic binding entails an additional overhead of locating and assigning the correct memory address for a particular function call to resolve. the whole thing is somehow similar to ELF PLT.GOT fixup (executable and linkable format procedure linkage table global offset table) except that addresses are always being referenced from the VPTR whenever a virtual call will be made.

also, there is a subtle difference between a normal virtual and a pure virtual function. from the looks of things, the pure virtual function takes up no space. in fact, i didn’t even see any single declartion of animal::purevirtual() anywhere on the dissassembly.

4 Comments »

The URI to TrackBack this entry is: http://gnurbs.blogsome.com/2007/04/05/virtuals/trackback/

  1. The book you need is Lipman’s “Inside the C++ Object Model”. Your simple test cases aren’t enough to illustrate what’s really going on — what you’re seeing for pure virtuals isn’t true in the general case.

    Comment by Ciaran McCreesh — April 5, 2007 @ 5:25 pm

  2. thanks for the tip! it’s about time i follow a learning structure of sorts.

    hopefully, i can create more complex test cases as i gain confidence… maybe a few months from now…

    Comment by sleepy jenkins — April 5, 2007 @ 6:04 pm

  3. Wow, that’s a different way of finding out and confirming the fact that dynamic polymorphic types are chock full of overhead. :)

    Perhaps you’ll want to see how other approaches at a higher level will actually be practically useful for whole software projects and whatnot. Performance is not everything you know. ;)

    Comment by Dean Michael Berris — April 23, 2007 @ 10:22 pm

  4. dean michael berris:

    prolly not the most intelligent thing to do imho.. almost to the point of being a masochist.. ..but it’s one of the many things i really enjoy doing. books and a good advice here and there are always very welcome too. thanks!

    Comment by sleepy jenkins — April 25, 2007 @ 5:42 pm

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Get free blog up and running in minutes with Blogsome | Theme designs available here