Assembly x64


Esses dias estive trabalhando em um problema complicado envolvendo o engine do SQL Server rodando assembly .NET. A análise foi feita em um dump de memória x64 e achei que essa seria uma ótima oportunidade para escrever um blog sobre esse assunto.


 


Registradores


A arquitetura x64, também conhecida como AMD64 ou Intel-EMT64, utiliza os mesmos registradores da arquitetura x86 (32-bits: EAX, EBX, ECX, EDX, ESI, EDI) e as versões estendidas 64-bits: RAX, RBX, RCX, RDX, RSI e RDI. Existem 8 novos registradores nomeados R8, R9, .., R15 para serem utilizados livremente.
















































































Register


Status


Use


RAX


Volatile


Return value register


RCX


Volatile


First integer argument


RDX


Volatile


Second integer argument


R8


Volatile


Third integer argument


R9


Volatile


Fourth integer argument


R10:R11


Volatile


Must be preserved as needed by caller; used in syscall/sysret instructions


R12:R15


Nonvolatile


Must be preserved by callee


RDI


Nonvolatile


Must be preserved by callee


RSI


Nonvolatile


Must be preserved by callee


RBX


Nonvolatile


Must be preserved by callee


RBP


Nonvolatile


May be used as a frame pointer; must be preserved by callee


RSP


Nonvolatile


Stack pointer


XMM0


Volatile


First FP argument


XMM1


Volatile


Second FP argument


XMM2


Volatile


Third FP argument


XMM3


Volatile


Fourth FP argument


XMM4:XMM5


Volatile


Must be preserved as needed by caller


XMM6:XMM15


Nonvolatile


Must be preserved as needed by callee.


Ref: MSDN - Register Usage - http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx


Calling Convention


O padrão utilizado no Windows x64 é fast call, no qual os primeiros 4 parâmetros na chamada de função são passados através dos registradores RCX, RDX, R8 e R9. Parâmetros adicionais devem ser passados através da stack.


Stack


A função chamadora (caller) é responsável por reservar o espaço para passagem de parâmetros para a função chamada (callee). Na plataforma x64, essa área é de no mínimo 4 parâmetros (8 bytes) mesmo quando não há parâmetros especificados.


image


Ref: MSDN – Stack Allocation


Exemplo:



__int64 comando(__int64 a, __int64 b, __int64 c, __int64 d, __int64 e)
{
return a+b+c+d+e;
}

int _tmain(int argc, _TCHAR* argv[])
{
__int64 resultado;

resultado = comando(0x1111111111, 0x2222222222,
                        0x333333333333, 0x444444444444, 
                        0x55555555); 


return 0;
}


_tmain(): Os 4 primeiros parâmetros são passados através dos registradores RCX, RDX, R8, R9, e o quinto parâmetro é passado pela stack. O valor de retorno da função é feito no registrador RAX.


00000001`3f992dd6 mov   qword ptr [rsp+20h],55555555h
00000001`3f992ddf mov r9,444444444444h
00000001`3f992de9 mov r8,333333333333h
00000001`3f992df3 mov rdx,2222222222h
00000001`3f992dfd mov rcx,1111111111h
00000001`3f992e07 call Console64!comando
00000001`3f992e0c mov qword ptr [rsp+30h],rax

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

comando(): Os parâmetros são armazenados na stack usando a área previamente reservada pelo chamador. Essa área de home de parâmetros é responsabilidade do callee e, por isso, otimizações de código podem utilizar essa área para outras finalidades.


00000001`3f992d70 mov     qword ptr [rsp+20h],r9
00000001`3f992d75 mov qword ptr [rsp+18h],r8
00000001`3f992d7a mov qword ptr [rsp+10h],rdx
00000001`3f992d7f mov qword ptr [rsp+8],rcx
00000001`3f992d84 push rdi
00000001`3f992d85 mov rcx,qword ptr [rsp+18h]
00000001`3f992d8a mov rax,qword ptr [rsp+10h]
00000001`3f992d8f add rax,rcx
00000001`3f992d92 add rax,qword ptr [rsp+20h]
00000001`3f992d97 add rax,qword ptr [rsp+28h]
00000001`3f992d9c add rax,qword ptr [rsp+30h]
00000001`3f992da1 pop rdi
00000001`3f992da2 ret

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }


Overview of x64 Calling Conventions
http://msdn.microsoft.com/en-us/library/ms235286.aspx



Register Usage
http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx



Parameter Passing
http://msdn.microsoft.com/en-us/library/zthk2dkh.aspx



Stack Allocation
http://msdn.microsoft.com/en-us/library/ew5tede7.aspx

Comments (0)

Skip to main content