The first look at 64bits code

I got a new machine with Windows7 64bits version last week so it’s time to try out the 64bits code debugging.

#include <stdio.h>

int main(){
    printf("Hello Worldn");
    return 0;

The compiled main function is here.

0:000> uf main
00000001`40001000 4883ec28        sub     rsp,28h
00000001`40001004 488d0d55110000  lea     rcx,[test!`string' (00000001`40002160)]
00000001`4000100b ff15df100000    call    qword ptr [test!_imp_printf (00000001`400020f0)]
00000001`40001011 33c0            xor     eax,eax
00000001`40001013 4883c428        add     rsp,28h
00000001`40001017 c3              ret

The address of the instruction is all 64 bits. The registry names are rcx, rsp, and so on. The return type is int and it’s stored in eax register which is 32bits.

One of the biggest surprise is the way function parameter is passed – it uses rcx register instead of a stack. AMD64 architecture has a lot of registers and the compiler happily uses them to pass the function parameters. If you are curious, here is the output of r command.

0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=0000000077c7010a
rdx=0000000000000000 rsi=0000000077d252f8 rdi=0000000077d53650
rip=0000000077cd1220 rsp=000000000012f500 rbp=000007fffffd3000
 r8=000000000012f4f8  r9=000007fffffd3000 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000077d53520
r14=000000000012fb00 r15=000000000000ffff
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

What happens the function has a lot of parameters?

int main(){
    printf("Hello Worldn", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    return 0;

0:000> uf main
00000001`40001000 488bc4          mov     rax,rsp
00000001`40001003 4883ec68        sub     rsp,68h
00000001`40001007 c740e809000000  mov     dword ptr [rax-18h],9
00000001`4000100e c740e008000000  mov     dword ptr [rax-20h],8
00000001`40001015 c740d807000000  mov     dword ptr [rax-28h],7
00000001`4000101c c740d006000000  mov     dword ptr [rax-30h],6
00000001`40001023 33d2            xor     edx,edx
00000001`40001025 c740c805000000  mov     dword ptr [rax-38h],5
00000001`4000102c c740c004000000  mov     dword ptr [rax-40h],4
00000001`40001033 488d0d26110000  lea     rcx,[test!`string' (00000001`40002160)]
00000001`4000103a 448d4a02        lea     r9d,[rdx+2]
00000001`4000103e 448d4201        lea     r8d,[rdx+1]
00000001`40001042 c740b803000000  mov     dword ptr [rax-48h],3
00000001`40001049 ff15a1100000    call    qword ptr [test!_imp_printf (00000001`400020f0)]
00000001`4000104f 33c0            xor     eax,eax
00000001`40001051 4883c468        add     rsp,68h
00000001`40001055 c3              ret

It’s interesting to see how the compiler uses registers. The first parameter (“Hello World”) is passed through rcx. The second parameter (0) is rdx, the 3rd parameter is r8d, and the 4th parameter is r9d. The rest of parameters are stored in the stack and rax has the first address of the stack memory block.

The usage of register must be agreed between caller and called function, where each function may be compiled with different compilers. Therefore, the rule is defined by Microsoft and every compiler vender for Windows AMD64 platform must follow it.


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: Logo

You are commenting using your 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