Member functions in 64bits code

Let’s take a look how 64bits compiler compiles C++ code. Here is the sample code:

// main function
#include "foo.hpp"

int main(){
    Foo foo(123);
    foo.Func();
    return 0;
}
// Header File.
class Foo {
public:
    Foo(int value);
    void Func();
private:
    __int64 m_value;
};
// foo.cpp, the class implementation file
Foo::Foo(int value){
    m_value = value * static_cast< __int64>(0x100000000);
}

void Foo::Func(){
    printf("Value = %dn", m_value);
}

First of all, let’s see how this pointer is passed to the member function. The main function is like this.

test!main:
00000001`40001000 4883ec28        sub     rsp,28h
00000001`40001004 488d4c2430      lea     rcx,[rsp+30h]
00000001`40001009 ba7b000000      mov     edx,7Bh
00000001`4000100e e81d000000      call    test!Foo::Foo (00000001`40001030)
00000001`40001013 488d4c2430      lea     rcx,[rsp+30h]
00000001`40001018 e823000000      call    test!Foo::Func (00000001`40001040)
00000001`4000101d 33c0            xor     eax,eax
00000001`4000101f 4883c428        add     rsp,28h
00000001`40001023 c3              ret

The this pointer is passed in @rcx just like as 32 bits code. In other words, the this pointer can be considered as the first parameter of the function.

Fair enough. Let’s look at the Foo constructor.

test!Foo::Foo:
00000001`40001030 4863c2          movsxd  rax,edx
00000001`40001033 48c1e020        shl     rax,20h
00000001`40001037 488901          mov     qword ptr [rcx],rax
00000001`4000103a 488bc1          mov     rax,rcx
00000001`4000103d c3              ret

The compiler optimization works very well here. It takes the passes parameter (@edx) and multiplied by 0x100000000 by shifting 0x20 bits. Compared to the same code with 32bits compiler, it’s actually really efficient.

; 32bits code
test!Foo::Foo:
00401020 8b442404        mov     eax,dword ptr [esp+4]
00401024 56              push    esi
00401025 6a01            push    1
00401027 99              cdq
00401028 6a00            push    0
0040102a 52              push    edx
0040102b 50              push    eax
0040102c 8bf1            mov     esi,ecx
0040102e e82d000000      call    test!_allmul (00401060)
00401033 8906            mov     dword ptr [esi],eax
00401035 895604          mov     dword ptr [esi+4],edx
00401038 8bc6            mov     eax,esi
0040103a 5e              pop     esi
0040103b c20400          ret     4

In the 32bits code, the passed parameter is multiplied by calling _allmul function.

Another thing you may notice in 64 bits code is the return value of the constructor. Surely constructors don’t have any return value, but it actually set the this pointer to @rax register. It’s same as 32bits code except that the pointer variable is @rax instead of @eax.

 

Advertisements

About Moto

Engineer who likes coding
This entry was posted in Advanced Debugging. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s