web-dev-qa-db-fra.com

Comment passer des paramètres dans l'appel de fonction assembleur x86

Regardez ce code assembleur. Il est conçu pour x86 32 bits et sera compilé par nasm

   ...
   my_function:
        pop %eax
        ...
        ret


   main:
       Push 0x08
       call my_function

J'ai appris il y a longtemps que nous pouvons utiliser la pile pour passer des paramètres entre le programme principal et les fonctions. Je m'attendrais à ce que eax contienne 0x08, mais c'est faux et je ne peux pas expliquer pourquoi. Comment dois-je faire pour récupérer mes paramètres de fonction?

9
Bob5421

Premièrement, si vous cherchez à vous interfacer avec d'autres langues ou bibliothèques sur votre plateforme, assurez-vous de lire l'interface définie pour cette plateforme. Il existe une variété de mécanismes d'appel qui peuvent être utilisés.

Dans votre cas, l'instruction call pousse l'adresse de retour sur la pile. Vous pouvez accéder à votre paramètre en utilisant de l'arithmétique et esp. Je vais supposer du code 32 bits (et une largeur de pile 32 bits) puisque vous utilisez eax. J'utilise la syntaxe Intel car je peux écrire cela sans rien chercher:

my_function:
    mov eax, [esp+4]    ; Move the contents of ESP+4 into EAX
                        ; ESP should be pointing at the 32 bit RIP.
                        ; ESP+4 should be the pushed parameter.
    ...
    ret


main:
   Push 0x08
   call my_function

Dans vos commentaires, vous demandez, concernant cette réponse, si cela représente une fuite de mémoire. La réponse est non." La raison en est que le appelant est responsable de nettoyer tout ce qu'il ajoute à la pile. Un exemple plus complet basé sur les autres commentaires qui ont été écrits pourrait ressembler à ceci:

my_function:
    Push ebp            ; Store the current stack frame
    mov  ebp, esp       ; Preserve ESP into EBP for argument references
    and  esp, 0xfffffff0; Align the stack to allow library calls
    mov  eax, [ebp+8]   ; Move the contents of EBP+4 into EAX
                        ; EBP should be pointing at the 32 bit RIP.
                        ; EBP+4 should be the pushed parameter.
    ...                 ; Do lots of cool stuff
    mov  esp, ebp       ; Restore the stack and ebp
    pop  ebp
    ret


main:
   Push 0x08
   call my_function
   pop ebx              ; Clean up the stack

Notez que lorsque nous alignons la pile (si vous ne savez pas pourquoi cela se produit, vous la trouverez rapidement lorsque vous recherchez la norme d'appel pour votre plate-forme) à une limite de 16 octets, nous n'essayons même pas de comprendre comment beaucoup esp a changé. Puisque ebp agira comme un "signet" pour nous, nous pouvons laisser esp se déplacer pour l'alignement ou peut-être l'allocation de variable locale sans autre réflexion.

Dans l'épilogue de la fonction, nous déplaçons ebp dans esp, ce qui restaure esp à sa valeur d'origine lors de l'appel de la fonction, nettoyant ainsi toutes les allocations locales et les opérations d'alignement qui ont arrivé. Enfin nous pop ebp hors de la pile, laissant le pointeur de retour comme valeur finale sur la pile dans la fonction. Nous revenons maintenant.

Après le retour, nous nettoyons avec un pop.

Alternativement, il est possible de nettoyer la pile avec un retour spécifiant le nombre d'octets à libérer sur la pile. Tout dépend si votre norme d'appel spécifie le nettoyage de l'appelant ou le nettoyage de l'appelé.

8
David Hoelzer