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 - https://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.
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
https://msdn.microsoft.com/en-us/library/ms235286.aspx
Register Usage
https://msdn.microsoft.com/en-us/library/9z1stfyw.aspx
Parameter Passing
https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
Stack Allocation
https://msdn.microsoft.com/en-us/library/ew5tede7.aspx