Est ce que de l'adressage de base avec ESP existe
Exemple :
ou doit-on passer par les registres EBX, EBP ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part mov dword ptr [esp+4],20
Est ce que de l'adressage de base avec ESP existe
Exemple :
ou doit-on passer par les registres EBX, EBP ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part mov dword ptr [esp+4],20
Noe,
Oui, ça existe.
Mais il faut être conscient que ça adresse la pile.
Si les cons volaient, il ferait nuit à midi.
Merci,
Je sais que ça adresse la pile, c'est bien pour cela que je pose la question.
C'est au sujet des fonctions en langage C, elle utilise le registre EBP pour faire cela (pour lire les paramètres de la fonction par exemple), alors que ce n'est pas toujours necessaire. On pourait passer directement par ESP.
Non, parce que ESP varie. Tu peux stocker des choses toi-même dans la pile, et tu peux descendre dans des sous-fonctions sans définir de nouveaux cadres de pile.
En outre, c'est très pratique en cas d'exception, quand tu dois quitter une procédure en plein traitement avant qu'elle ait pu vider la pile elle-même. Ça te permet de ramener ESP « au début ».
Quand je suis dans une fonction (simple), je sais bien si ESP va varier ou pas. Donc je ne vois pas ce qui pose problème ?Envoyé par Obsidian
Je te l'ai dit. Tu peux tout gérer à partir du pointeur de pile (tous les microprocesseurs ne proposent pas un registre dédié comme EBP), mais c'est beaucoup plus compliqué :
- D'abord, ton pointeur de pile évolue au cours de la fonction. Si ces évolutions ne sont pas « statiques » (comprendre par là « connues à la compilation »), par exemple si tu empiles récursivement des valeurs tant qu'un registre est différent de zéro, par exemple, tu es obligé d'impliquer ce registre dans ton calcul d'index et ça devient très compliqué de retrouver le début de ta pile. Autant directement utiliser un registre qui conserve sa valeur ;
- Ensuite, les langages de haut niveau sont suceptibles de mettre fin prématurément à ta fonction, à n'importe quel moment. Ça peut arriver si le programmeur à mis un « return » en plein milieu.
Dans ce dernier cas, il te faut remettre directement le pointeur de pile à l'état où il était en rentrant dans la fonction, pour retrouver celui des registres qui y ont été sauvegardés et rendre la main à la fonction du niveau du dessus.
Pire encore, lorsque tu utilises un langage compilé qui gère les exceptions (C++, par exemple), si celle-ci n'est pas rattrapée en interne, tu peux « dévisser » de trois ou quatre fonctions avant de reprendre la main. Dans ce cas, le fait d'utiliser EBP pour pointer le cadre de pile te permet de remonter itérativement jusqu'à la fonction souhaitée. Tu restaures le pointeur de pile initial en remettant EBP dans ESP, tu fais un POPA (qui restaure EBP), et tu recommences n fois.
C'est notamment ce que font automatiquement les instructions ENTER et LEAVE. C'est pourquoi il est intéressant de respecter le bon format.
D'autre part, « BP » signifie « Base Pointer ». C'est donc un registre dédié à l'indexation par offset par rapport à un point de départ, même si on peut le faire avec d'autres registres, et heureusement. Tu n'es pas obligé de t'en servir uniquement pour référencer le cadre de pile. C'est utile pour exploiter des structures, par exemple.
Je me suis peut-être mal exprimé.
Je parlais de fonction C et non C++.
Je parlais dans le contexe ou c'est moi qui écrit la fonction en asm.C'est au sujet des fonctions en langage C
Je sais donc bien si ESP va être modifié ou pas quand même. Il va pas bougé tout seul ?
(Désolé de défoncer les portes ouvertes
Les instructions enter/leave, indiquées par Obsidian, sont typiques des concessions faites aux HLL de type C et sont prévues pour faciliter l'interfaçage entre HLL et assembleur.
Pour ton cadre de pile, les règles sont simples:
Toutes les remarques faites par Obsidian sont valides et pertinentes: Elles t'éviteront bien des surprises et des incompréhensions liées à la constatation de phénomènes erratiques en tout genres.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 PUSH EBP MOV EBP ESP Lp argument1 = EBP+04 Lp argument2 = EBP+08 MOV ESP EBP POP EBP RET NombreArguments*4 (4 pour 4 bytes -> DWORD)
Je sais comment on monte un cadre de pile, ce n'était pas le sujet de la question ?
la sortie d'une fonction en appel C est RET tout court. C'est la fonction appelante qui retire les paramètres.
Oui elle sont valide, mais ne reponde pas à ma question.Toutes les remarques faites par Obsidian sont valides et pertinentes
ça j'ai répondu, C'est moi qui écrit la fonction. je sais si ESP change ou pas!D'abord, ton pointeur de pile évolue au cours de la fonction.
Là faut m'expliquer comment tu fait pour mettre un return au milieu de ma fonction.Ensuite, les langages de haut niveau sont suceptibles de mettre fin prématurément à ta fonction, à n'importe quel moment. Ça peut arriver si le programmeur à mis un « return » en plein milieu.
Je ne vois pas par quelle artifice un langage peut mettre fin à une fonction qu'il n'a pas compilé.
- OuiEst-ce que de l'adressage de base avec ESP existe (?)
- Nondoit-on passer par les registres EBX, EBP ?
Puisque tu sais déjà tout-ça, pourquoi le demandes-tu ?
Ou: Si tu pouvais indiquer la réponse que tu attends, à Obsidian (par exemple), cela permettrait à un plus grand nombre de te la fournir...
Est-ce juste un prétexte pour te permettre d'être désagréable ? (je n'ai rien contre remarques bien, il faut que certaines frustrations soit évacuées d'une manière ou d'une autre selon les moyens de chacun et le côté bouffon m'amuse toujours )
Alors, pour mettre fin au suspens, oui, l'adressage basé sur le pointeur de pile, ça se fait (j'ai déssassemblé et commenté quelques programmes qui le faisaient) mais ça reste compliqué.
J'ai répondu à ta question. Je t'ai cité le cas où l'usage de la pile interne à une fonction nécessite un registre pour en suivre la trace, ce qui fait qu'il devient plus simple d'utiliser ce même registre pour sauvegarder l'état initial de la pile.
Ensuite, utiliser le mode indexé pour retrouver l'adresse de départ n'est pas plus efficace que d'utiliser un registre du processeur, qui reste de loin la ressource la plus rapide. Faire un accès bus à la fin de chaque fonction pour aller lire une constante sur trente-deux bits n'est pas meilleur que d'utiliser EBP. C'est plus long en temps et c'est quatre ou cinq octets plus grand. Si tu as des ressources, c'est idiot de ne pas les exploiter.
Ça, c'est pour les langages de haut niveau. Si tu écris directement en assembleur, tu peux utiliser ESP mais à ce moment, il faut que tu suives toi-même et sur papier toute l'évolution de ta pile sans te tromper, et que tu fasses les adaptations à la volée. Bon courage.
Autrement, ça ne t'apporte rien de plus de t'en tenir à ESP si tu disposes d'un autre registre. Ces arguments justifient déjà son utilisation, à mon avis, mais la raison la plus importante est encore à venir :
Chaque fois que tu ouvres un bloc, tu descends d'un niveau dans la pile (ce qui te permet accessoirement d'y déclarer des variables locales). Ce n'est pas propre aux corps de fonction. Quand tu fais :Là faut m'expliquer comment tu fait pour mettre un return au milieu de ma fonction.
Code C : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 int fonction (int x,int y) { if (x>1) { char v; if (y>1) { FILE * f; f = fopen ("fichier","r"); if (f==NULL) { fprintf (stderr,"Echec ouverture\n"); return -1; } } } }
... ton return quitte la fonction et remonte donc quatre niveaux d'imbrication. Cependant, à ce stade, le compilo sait encore s'y retrouver, et il n'ouvre pas forcément un cadre de pile pour chaque bloc.
D'autre part, quoi que l'on en pense, le goto existe toujours en C, et permet d'atteindre directement le bas de la fonction si on le souhaite. C'est pratique, quand on sait s'en servir, pour mettre justement en place des mécanismes s'apparentant aux exceptions. Quand tu en es là, tu ne sais pas où en était ta pile.
Mais la raison la plus importante pour moi est la suivante : la pile des appels, dont l'utilité principale se révèle avec le déboguage.
Lorsque tu utilises ton débugger, celui-ci te donne la pile des appels de fonction, depuis celle où le programme se trouve ou a planté, en remontant jusqu'à main(). Ça, ce n'est possible que parce que les cadres de pile se font successivement référence et forment une liste chaînée qui peut être remontée. C'est peut-être là que se trouve le principal argument en défaveur d'un usage direct du pointeur de pile : il est dépendant de ce qui se passe au runtime et tu es obligé de laisser le programme se terminer pour retomber sur tes pattes.
Tu peux demander à ton compilateur d'omettre volontairement les pointeurs de cadre de pile à l'aide d'options explicites. Pour gcc, c'est le fameux -fomit-frame-pointer.
Essaie de compiler ton programme sans les symboles de déboguage et de provoquer volontairement une segfault dans une fonction au cinquième ou sixième niveau d'imbrication. Avec les cadres de pile, tu retrouves l'adresse de début de toutes les fonctions. Sans ces pointeurs, tu te retrouves au début de ton programme.
C'est également très important pour les outils de profilage, car c'est avec eux qu'ils pourront tenir les statistiques des appels de fonctions.
On a vu également que c'était important également pour les langages de haut niveau autres que le C, et pour tout outil d'exploration externe qui, lui, étudie le programme objet indépendament du langage source.
Un signal en provenance du système d'exploitation, tel que Abort, par exemple. Bon, sous Unix, c'est un signal comme un autre, et il est de ceux qui, en principe, provoquent la terminaison du processus. Mais dans d'autres cas, celui-ci peut être interprété à juste titre comme une exception, et nécessiter la remontée d'urgence de la pile des appels jusqu'à un point précis, plutôt que tout en haut du processus.Je ne vois pas par quelle artifice un langage peut mettre fin à une fonction qu'il n'a pas compilé.
À contrario, virer les cadres de pile est surtout intéressant en C, mais n'est pas toujours possible.
Enfin, qu'est-ce que ça t'apporte ? Un peu de gain de place au niveau du cadre, (sensible quand il y a beaucoup d'appels de fonctions), rien du tout au niveau du référencement des variables locales et de la gestion de la pile, ni en temps ni en place.
À noter comme décrit précédement que l'usage de EBP est indépendant de celui des cadres de pile. Il n'empêche que ce que l'on a dit demeure.
Bel effort de limpidité, bravo !
J'en deduis que ce n'est pas interdit mais pas conseillé.
Merci pour ton exposé sur le sujet.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager