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?
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é.