O macete do uso de estruturas para acessar argumentos de funções…

Alguns de meus leitores acharam estranho a maneira com que acesso os argumentos de funções passadas pela pilha, como é o caso de convenções de chamadas usadas no modo i386, na maioria dos sistemas operacionais. Algumas pessoas estão acostumadas a usar um prólogo e um epílogo nas suas rotinas e usam o registrador EBP como ponteiro base, assim:

MyFunc:
  push ebp     ; Prólogo
  mov  ebp,esp

  mov  eax,[ebp+8] ; Pega o 1º argumento.
  ...

  pop  ebp     ; Epílogo
  ret

Ok… esse é o método padrão, mas apresenta um problema: Se você errar o offset em relação a EBP vai pegar o valor errado! E se pudéssemos explicitar a posição, na pilha, usando símbolos? Well… podemos! Usando estruturas. O mesmo fragmento de código, acima, poderia ser escrito assim:

struc MyFuncStack
  .oldebp:   resd 1 ; ESP aponta para o EBP empilhado.
  .retaddr:  resd 1 ; CALL empilha o endereço de retorno.
  .arg1:     resd 1 ; Primeiro argumento.
endstruc

MyFunc:
  push ebp     ; Prólogo
  mov  ebp,esp

  mov  eax,[ebp+MyFuncStack.arg1] ; Pega o 1º argumento.
  ...

  pop  ebp     ; Epílogo
  ret

A expressão MyFuncStack.arg1, no NASM, será traduzida como o offset de arg1 em relação ao início da estrutura, ou seja, 8. Isso evitará erros e, uma vez que você se acostume com esse “macete”, torna seu código mais legível.

Aliás, usar um prólogo e um epílogo é supérfluo. Podemos escrever o mesmo fragmento de rotina assim:

struc MyFuncStack
  .retaddr:  resd 1
  .arg1:     resd 1
endstruc

MyFunc:
  mov  eax,[esp+MyFuncStack.arg1] ; Pega o 1º argumento.
  ...
  ret

Mas… e se eu quiser usar variáveis locais alocadas na pilha, como faço? Basta alterar a estrutura e modificar ESP de acordo:

struc MyFuncStack
  .local1:   resd 1 ; Nossa variável local
  .localsize:
  .retaddr:  resd 1
  .arg1:     resd 1
endstruc

MyFunc:
  add  esp,MyFuncStack.localsize
  mov  eax,[esp+MyFuncStack.arg1] ; Pega o 1º argumento.
  ...
  sub  esp,MyFuncStack.localsize
  ret

Note que a estrutura do seu stack frame precisa conter o estado em que você deixou a pilha… É o exemplo do uso do epílogo e do prólogo: Coloquei, lá em cima, um oldebp no frame… se eu tivesse salvo algo mais na pilha, teria que colocar no frame também, por exemplo:

struc MyFuncStack
  .local1:   resd 1 ; Nossa variável local
  .localsize:
  .oldebx:   resd 1
  .oldebp:   resd 1
  .retaddr:  resd 1
  .arg1:     resd 1
endstruc

MyFunc:
  push ebp
  push ebx
  add  esp,MyFuncStack.localsize
  mov  eax,[esp+MyFuncStack.arg1] ; Pega o 1º argumento.
  ...
  sub  esp,MyFuncStack.localsize
  pop  ebx
  pop  ebp
  ret

Assim eu posso usar EBP para outra coisa que não a base da pilha, por exemplo…

Anúncios