Comment l'assemblage fait-il le passage de paramètre: par valeur, référence, pointeur pour différents types / tableaux?
Essayer de regarder cela, j'ai écrit ce code simple où je viens de créer des variables de différents types et les a transmis à une fonction par valeur, par référence et par pointeur:
int i = 1;
char c = 'a';
int* p = &i;
float f = 1.1;
TestClass tc; //has 2 private data members: int i = 1 and int j = 2
la fonction des organes ont été laissés en blanc parce que je suis juste à la recherche à la façon dont les paramètres sont passés dans l'.
passByValue(i, c, p, f, tc);
passByReference(i, c, p, f, tc);
passByPointer(&i, &c, &p, &f, &tc);
voulais voir comment c'est différent pour un tableau et aussi la façon dont les paramètres sont ensuite accessibles.
int numbers[] = {1, 2, 3};
passArray(numbers);
assemblée:
passByValue(i, c, p, f, tc)
mov EAX, DWORD PTR [EBP - 16]
mov DL, BYTE PTR [EBP - 17]
mov ECX, DWORD PTR [EBP - 24]
movss XMM0, DWORD PTR [EBP - 28]
mov ESI, DWORD PTR [EBP - 40]
mov DWORD PTR [EBP - 48], ESI
mov ESI, DWORD PTR [EBP - 36]
mov DWORD PTR [EBP - 44], ESI
lea ESI, DWORD PTR [EBP - 48]
mov DWORD PTR [ESP], EAX
movsx EAX, DL
mov DWORD PTR [ESP + 4], EAX
mov DWORD PTR [ESP + 8], ECX
movss DWORD PTR [ESP + 12], XMM0
mov EAX, DWORD PTR [ESI]
mov DWORD PTR [ESP + 16], EAX
mov EAX, DWORD PTR [ESI + 4]
mov DWORD PTR [ESP + 20], EAX
call _Z11passByValueicPif9TestClass
passByReference(i, c, p, f, tc)
lea EAX, DWORD PTR [EBP - 16]
lea ECX, DWORD PTR [EBP - 17]
lea ESI, DWORD PTR [EBP - 24]
lea EDI, DWORD PTR [EBP - 28]
lea EBX, DWORD PTR [EBP - 40]
mov DWORD PTR [ESP], EAX
mov DWORD PTR [ESP + 4], ECX
mov DWORD PTR [ESP + 8], ESI
mov DWORD PTR [ESP + 12], EDI
mov DWORD PTR [ESP + 16], EBX
call _Z15passByReferenceRiRcRPiRfR9TestClass
passByPointer(&i, &c, &p, &f, &tc)
lea EAX, DWORD PTR [EBP - 16]
lea ECX, DWORD PTR [EBP - 17]
lea ESI, DWORD PTR [EBP - 24]
lea EDI, DWORD PTR [EBP - 28]
lea EBX, DWORD PTR [EBP - 40]
mov DWORD PTR [ESP], EAX
mov DWORD PTR [ESP + 4], ECX
mov DWORD PTR [ESP + 8], ESI
mov DWORD PTR [ESP + 12], EDI
mov DWORD PTR [ESP + 16], EBX
call _Z13passByPointerPiPcPS_PfP9TestClass
passArray(numbers)
mov EAX, .L_ZZ4mainE7numbers
mov DWORD PTR [EBP - 60], EAX
mov EAX, .L_ZZ4mainE7numbers+4
mov DWORD PTR [EBP - 56], EAX
mov EAX, .L_ZZ4mainE7numbers+8
mov DWORD PTR [EBP - 52], EAX
lea EAX, DWORD PTR [EBP - 60]
mov DWORD PTR [ESP], EAX
call _Z9passArrayPi
//parameter access
push EAX
mov EAX, DWORD PTR [ESP + 8]
mov DWORD PTR [ESP], EAX
pop EAX
Je suis en supposant que je suis à la recherche à la droite de l'assemblée concernant le passage de paramètres, car il y a des appels à la fin de chaque!
Mais en raison de ma connaissance très limitée de l'assemblée, je ne peux pas dire ce qui se passe ici. J'ai appris à propos de ccall convention, donc je suis en supposant que quelque chose se passe qui a à voir avec la préservation de l'appelant-sauvé registres et puis en poussant les paramètres sur la pile. De ce fait, je m'attends à voir les choses chargées dans les registres et les "pousser" un peu partout, mais n'ont aucune idée de ce qui se passe avec le mov
s et lea
s. Aussi, je ne sais pas ce que DWORD PTR
est.
Je ne l'ai appris sur les registres: eax, ebx, ecx, edx, esi, edi, esp
et ebp
afin de voir quelque chose comme XMM0
ou DL
juste me confond. Je suppose que cela a du sens pour voir lea
quand il s'agit de passage par référence/pointeur parce qu'ils utilisent des adresses de mémoire, mais je ne peux pas vraiment dire ce qui se passe. Quand il s'agit de passage par valeur, il semble que il ya beaucoup d'instructions, ce qui pourrait avoir à faire avec la copie de la valeur dans des registres. Aucune idée de quand il s'agit de la façon dont les tableaux sont passés et sont accessibles en tant que paramètres.
Si quelqu'un pouvait expliquer l'idée générale de ce qui se passe avec chaque bloc de montage pour moi, je serais très apprécier.
source d'informationauteur kayla
Vous devez vous connecter pour publier un commentaire.
À l'aide de registres du CPU pour le passage d'arguments est plus rapide que l'utilisation de la mémoire, c'est à dire de la pile. Cependant, il est limité en nombre de registres du PROCESSEUR (en particulier dans les x86-compatible Cpu) donc, quand une fonction de nombreux paramètres, puis de la pile est utilisée à la place de registres du PROCESSEUR. Dans votre cas il y a 5 arguments de la fonction de sorte que le compilateur utilise la pile pour les arguments, au lieu de les registres.
En principe, les compilateurs peuvent utiliser
push
instructions pour pousser les arguments de la pile avant mêmecall
à la fonction, mais de nombreux compilateurs (incl. gnu c++) utilisationmov
pour pousser les arguments de la pile. De cette façon, est pratique car il ne change pas de registre ESP (en haut de la pile) dans la partie de code qui appelle la fonction.En cas de
passByValue(i, c, p, f, tc)
les valeurs des arguments sont placés sur la pile. Vous pouvez voir de nombreuxmov
instruction d'un emplacement de mémoire dans un registre et de l'enregistrer à un emplacement approprié de la pile. La raison pour cela est que x86 assemblée interdit direct en mouvement à partir d'un emplacement mémoire à l'autre (à l'exception demovs
qui déplace les valeurs d'un tableau (ou une chaîne de caractères comme vous le souhaitez) à l'autre).En cas de
passByReference(i, c, p, f, tc)
vous pouvez voir un grand nombre de 5 lea instructions qui copie adresses des arguments pour les registres du CPU, et ces valeurs de ces registres sont déplacés dans la pile.Le cas de
passByPointer(&i, &c, &p, &f, &tc)
est similaire àpassByValue(i, c, p, f, tc)
. En interne, sur le niveau de l'assemblée, le passage par référence utilise des pointeurs, alors que sur le plus, C++, niveau un programmeur n'a pas besoin d'utiliser explicitement le&
et*
opérateurs sur des références.Après les paramètres sont déplacés vers la pile
call
est émis, ce qui pousse le pointeur d'instructionEIP
de la pile avant le transfert de l'exécution du programme à la sous-routine. Tous lesmoves
des paramètres de la pile de compte pour les prochainesEIP
sur pile après lacall
instruction.Il y en a de trop dans ton exemple ci-dessus afin de disséquer tous. Au lieu de cela, je vais juste aller sur
passByValue
depuis qui semble être le plus intéressant. Par la suite, vous devriez être en mesure de comprendre le reste.Tout d'abord, quelques points importants à garder à l'esprit tout en étudiant le démontage afin de ne pas être complètement perdu dans la mer de code:
mov [ebp - 44], [ebp - 36]
est pas un instruction. Un registre intermédiaire est nécessaire pour stocker les données de la première et par la suite copié dans la mémoire de destination.[]
en conjonction avec unmov
moyens d'accéder aux données à partir d'un calculées adresse mémoire. Ceci est analogue à derefing un pointeur en C/C++.lea x, [y]
que signifie généralement calculer l'adresse de y et enregistrer dans x. Ceci est analogue à prendre l'adresse d'une variable en C/C++.Avec ce qui précède à l'esprit, voici l'appel à
passByValue
fonction réarrangé un peu pour la rendre plus compréhensible:Le code ci-dessus est unmangled et de l'instruction de mélange pour le rendre clair ce qui se passe réellement, mais certains sont encore les besoins de l'explication. Tout d'abord, le char est
signed
et c'est un seul octet de la taille. Les instructions ici:lit un octet de
[ebp - 17]
(quelque part sur la pile) et la stocke dans la partie inférieure du premier octet deedx
. Cet octet est ensuite copié danseax
à l'aide de signe étendu déplacer. La pleine valeur de 32 bits danseax
est enfin copiés sur la pile quepassByValue
peut accéder. Voir le registre de la mise en page si vous avez besoin de plus de détails.Le quatrième argument:
Utilise l'ESS
movss
instruction pour copier la valeur à virgule flottante à partir de la pile dans unxmm0
registre. En bref, le jeu d'instructions SSE vous permettent d'effectuer la même opération sur plusieurs éléments de données simultanément, mais ici, le compilateur est en l'utilisant comme un stockage intermédiaire pour la copie des valeurs à virgule flottante sur la pile.Le dernier argument:
correspond à la
TestClass
. Apparemment, cette classe est de 8 octets la taille situé sur la pile de[ebp - 40]
à[ebp - 33]
. La classe est en train d'être copiés de 4 octets à la fois, puisque l'objet ne peut pas tenir dans un seul registre.Voici ce que la pile environ ressemble avant
call passByValue
:Ce que vous cherchez sont ABI conventions d'appel. Plates-formes différentes ont différentes conventions. par exemple, Windows x86-64 a différentes conventions que Unix/Linux sur x86-64.
http://www.agner.org/optimize/ a un appel conventions doc détaillant les diverses pour x86 /amd64.
Vous pouvez écrire du code en ASM qui fait tout ce que vous voulez, mais si vous voulez l'appeler d'autres fonctions, et d'être appelé par eux, puis de passer des paramètres /valeurs de retour en fonction de l'ABI.
Il pourrait être utile de faire un usage interne uniquement fonction d'assistance qui n'utilise pas la norme ABI, mais utilise à la place des valeurs dans les registres que l'appel de la fonction alloue. C'est l'esp. probablement si vous êtes en train de rédiger le programme principal dans autre chose que de l'ASM, avec juste une petite partie de l'ASM. Ensuite, l'asm partie seulement des besoins de soins au sujet d'être portable sur des systèmes avec différents ABIs pour être appelée depuis le programme principal, non pas pour son propre fonctionnement interne.