Convenções de chamada no Windows
Aprendendo sobre as convenções de chamada usadas no Windows (x64, cdecl e stdcall).
O Windows tem suas próprias convenções de chamadas e o objetivo desse tópico é aprender sobre as três principais que dizem respeito à linguagem C.

Convenção de chamada x64

Essa é a convenção de chamada padrão usada em x86-64 e portanto é essencial aprendê-la caso vá programar no Windows diretamente em Assembly.

Registradores

Os registradores RBX, RBP, RDI, RSI, RSP, R12 até R15 e XMM6 até XMM15 devem ser preservados pela função chamada (callee). Caso a função chamada precise alterar o valor de algum desses registradores ela tem a obrigação de preservar o valor anterior e restaurá-lo antes de retornar.
Os demais registradores são considerados voláteis, isto é, podem ter seu valor alterado quando uma chamada de função é efetuada. A função chamada pode modificar o valor dos registradores voláteis livremente.

Passagem de parâmetros

  • Os primeiros quatro argumentos inteiros ou ponteiros são passados nos seguintes registradores e na mesma ordem: RCX, RDX, R8 e R9. Os demais argumentos devem ser empilhados na ordem inversa.
  • Os primeiros quatro argumentos float ou double são passados nos registradores XMM0 até XMM3 como valores escalares. Os demais também são empilhados na ordem inversa.
  • Structs e unions de 8, 16, 32 ou 64 bits são passados como se fossem inteiros do respectivo tamanho. Se forem de outro tamanho a função chamadora deve então passar um ponteiro para a struct/union que será armazenada em uma memória alocada pela própria função chamadora. Essa memória deve estar em um endereço alinhado por 16 bytes.
A função chamadora (caller) é responsável por alocar um espaço de 32 bytes na pilha chamado de shadow space. Ele é alocado com o intuito de ser usado pela função chamada (callee) para armazenar os parâmetros passados em registradores caso seja necessário, por exemplo caso a função chamada precise usar esses registradores com outro intuito. Esse espaço vem antes mesmo do primeiro parâmetro empilhado.
Exemplo de protótipo de função:
int sum(int a, int b, int c, int d, int e, int f);
Assim que a função fosse chamada ECX, EDX, R8D e R9D armazenariam os parâmetros a, b, c e d respectivamente. O parâmetro f seria empilhado seguido do parâmetro e.
O 0(%rsp) seria o endereço de retorno. O espaço entre 8(%rsp) e 40(%rsp) é o shadow space. 40(%rsp) apontaria para o parâmetro e, enquanto 48(%rsp) apontaria para o parâmetro f. Como na demonstração abaixo:
mov %ecx, 8(%rsp) # Armazenando o parâmetro A no shadow space
mov %edx, 16(%rsp) # Parâmetro B
mov %r8d, 24(%rsp) # Parâmetro C
mov %r9d, 32(%rsp) # Parâmetro D
# Parâmetro E: 40(%rsp)
# Parâmetro F: 48(%rsp)

Retorno de valores

  • Valores inteiros e ponteiros são retornados em RAX.
  • Valores float ou double são retornados no registrador XMM0.
  • O retorno de structs é feito com a função chamadora alocando o espaço de memória necessário para a struct, ela então passa o ponteiro para esse espaço como primeiro argumento para a função em RCX. A função chamada (callee) deve retornar o mesmo ponteiro em RAX.

Convenção de chamada cdecl

A convenção de chamada __cdecl é a convenção padrão usada em código escrito em C na arquitetura IA-32 (x86).

Registradores

Apenas os registradores EAX, ECX e EDX são considerados voláteis, ou seja, registradores que podem ser modificados livremente pela função chamada. Todos os demais registradores precisam ser preservados e restaurados antes do retorno da função.

Passagem de parâmetros

Todos os parâmetros são passados na pilha e devem ser empilhados na ordem inversa. A função chamadora (caller) é a responsável por remover os argumentos da pilha após a função retornar.
Exemplo:
push $3
push $2
push $1
call my_function
add $12, %esp
# 12 é o tamanho em bytes dos três valores empilhados

Retorno de valores

  • Valores inteiros ou ponteiros são retornados em EAX.
  • Valores float ou double são retornados em ST0.
  • O retorno de structs ocorre da mesma maneira que na convenção de chamada x64. Com a diferença que o primeiro argumento é, obviamente, passado na pilha.

Convenção de chamada stdcall

A convenção de chamada __stdcall é a utilizada para chamar funções da WinAPI.

Registradores

Assim como na __cdecl os registradores EAX, ECX e EDX são voláteis e os demais devem ser preservados pela função chamada.

Passagem de parâmetros

Todos os argumentos são passados na pilha na ordem inversa. A função chamada (callee) é a responsável por remover os argumentos da pilha. Exemplo:
push $3
push $2
push $1
call my_function

Retorno de valores

O retorno de valores funciona da mesma maneira que o retorno de valores da __cdecl.
Export as PDF
Copy link
On this page
Convenção de chamada x64
Registradores
Passagem de parâmetros
Retorno de valores
Convenção de chamada cdecl
Registradores
Passagem de parâmetros
Retorno de valores
Convenção de chamada stdcall
Registradores
Passagem de parâmetros
Retorno de valores