Trouver pile la taille d'image
Le cadre de la pile d'un appel de fonction peut être facilement obtenue par __builtin_frame_address(1)
, mais quid de la pile de la taille d'image?
Est-il une fonction qui me permettra de savoir quelle est la taille de la frame de pile de l'appelant la fonction?
(char *)__builtin_frame_address(1) - (char *)__builtin_frame_address(0)
?- Laissez-moi essayer, mais je ne suis pas sûr de savoir comment les EIP (x86, RIP pour x64) seront traités.
- c'est une bonne question et je ne suis pas un faible niveau de sorte de gourou, alors ne prenez pas mon mot pour cela 🙂
- Nope, ne pas faire beaucoup de sens: pastebin.com/GUaSrY7c
- Ce n'est pas logique dans tout cela?
- il y a 7 ints. Sur ma machine,
sizeof(int)
retourne4
. Donc je suppose que le frame de pile doit être de 4*7=28. Peut-être que je me trompe, dans l'affirmative, veuillez expliquer pourquoi? - Encore plus loin, je viens de objdump-ed le binaire, et je vois
48 83 ec 20 sub rsp,0x20
dans la main, ce qui signifie que GCC réservés 32 octets au lieu de le 28, j'attends (je suppose à cause des optimisations?). Mais de toute façon, ce n'est pas 48 - "Donc je suppose que le frame de pile doit être de 4*7=28" - non, ce n'est pas la façon dont il fonctionne. La pile doit souvent être alignés (tout comme la taille d'une structure n'est pas la somme de la taille de ses membres, un cadre de pile est souvent plus grand qu'il serait "semble" nécessaire). Aussi, puisque les variables ne sont plus utilisés, ils peuvent très bien être optimisé à l'écart. Aussi, n'existe-il pas plus d'opérations impliquant des "sp", par le chemin? (gardez à l'esprit que l'adresse de retour doit être stocké quelque part aussi, donc peut-être qui s'ajoute à la 32 octets? etc.)
- c'est le vidage de la main (exclusing l'appel à la fonction de test): pastebin.com/4ZDPd7LL Il y a une poussée de 8 octets (rbp), donc si ça fait de la pile plus que 28 + 8 = 36 octets de la pile, ce qui n'est toujours pas 48. Je suppose que l'alignement de la pile ne semble raisonnable, mais peut-être pas dans ce code exact (comme la décharge montre clairement le contraire)
- Bien, en voyant le cliché, cela fait vraiment sens:
0x20
est de 32, pas 28. 32 + 8 est de 40. Si vous ajoutez de l'espace nécessaire pour l'adresse de retour, vous obtenez 48. (Je ne suis pas sûr que ce doit en effet être fait, mais quelque chose me dit que cette façon de l'obtention de la taille de l'image ne fait sens.) - Selon cette image 1.bp.blogspot.com/_n5BzA0M85tM/SdbJduaGHKI/AAAAAAAAARM/... (ou toute autre représentation visuelle d'une pile), l'adresse de retour ne fait pas partie du cadre de pile, de sorte que l'ajout d' + 8 + 8 ne semble pas être la solution propre.
- Oh, non, je suis désolé, il ne s'en fait.
- À mon humble avis il y a quelque chose de buggy avec cette solution. pastebin.com/2ERgnXsf GCC réserves de 32 octets de la pile, j'ai 8 entiers de 4 octets chacun. Il n'existe pas de "l'espace vide" dans la pile en raison de l'alignement. Je m'attend à mon code pour afficher les valeurs de chaque variable, mais ils semblent être déplacée par un. Si je reste 4 à
tmp
, il est résolu, mais je ne peux pas vraiment savoir pourquoi? Je suis sur une machine x64, donc l'adresse de retour est de 8 octets. - laissez-nous continuer cette discussion dans le chat
Vous devez vous connecter pour publier un commentaire.
Ma première réaction aurait été, pourquoi quelqu'un voudrait cela? Il devrait être considéré comme une mauvaise pratique pour une fonction C pour déterminer dynamiquement la taille de la frame de pile. Le point de l'ensemble de cdecl (la classique convention d'appel C), c'est que la fonction elle-même (le "destinataire de l'appel') n'a pas connaissance de la taille de la frame de pile. Tout détournement de cette philosophie peut causer votre code à la pause lors de la commutation sur une autre plate-forme, une adresse différente de la taille (ex: à partir de 32 bits à 64 bits), un compilateur différent ou de même les différents paramètres du compilateur (en particulier les optimisations).
D'autre part, depuis gcc offre déjà cette fonction
__builtin_frame_address
, il sera intéressant de voir comment beaucoup d'informations peuvent être dérivées à partir de là.De la la documentation:
Sur x86, une fonction commence généralement par:
En d'autres termes,
__builtin_frame_address
renvoie la de la base de pointeur de l'appelant cadre de pile.Malheureusement, le pointeur de la base, dit que peu ou rien sur l'endroit où toute la trame de pile commence ou se termine;
la base d'un pointeur sur un emplacement qui est quelque part au milieu de la trame de pile (entre le paramètres et la variables locales).
Si vous êtes uniquement intéressé par la partie de la trame de pile qui contient les variables locales, alors la fonction elle-même dispose de toutes les connaissances. La taille de cette partie est la différence entre le pointeur de pile et le pointeur de la base.
Veuillez garder à l'esprit que gcc semble allouer de l'espace sur la pile dès le début qui est utilisé pour contenir des paramètres pour d'autres fonctions appelées depuis l'intérieur de l'appelé. Strictement parlant, que l'espace appartient à la pile d'images de ces autres fonctions, mais la limite n'est pas claire. Si c'est un problème, dépend de votre objectif, ce que vous allez faire avec le calcul de la pile la taille d'image?
Comme pour l'autre partie (les paramètres), cela dépend. Si votre fonction a un nombre fixe de paramètres, il vous suffit de mesurer la taille de l' (formel) paramètres. Il ne garantit pas que l'appelant poussa le même nombre de paramètres sur la pile, mais en supposant que l'appelant compilé sans les mises en garde contre le destinataire de l'appel du prototype, il devrait être OK.
Vous pouvez combiner les deux techniques pour obtenir la pleine structure de pile (y compris l'adresse de retour et enregistré base pointer):
Si en revanche, votre fonction a un nombre variable de paramètre (une "points de suspension", comme
printf
a), alors la taille des paramètres n'est connue que de l'appelant. À moins que le destinataire de l'appel a un moyen de calculer la taille et le nombre de paramètres (dans le cas d'uneprintf
de style de la fonction, par l'analyse de la chaîne de format), vous devez laisser l'appelant passer que l'information sur le destinataire de l'appel.EDIT:
Veuillez noter que ceci ne fonctionne que pour laisser une fonction de mesure de son propre cadre de pile. Un destinataire ne peut pas calculer son appelant pile de la taille de l'image; le destinataire de l'appel devrez demander à l'appelant pour l'information.
Toutefois, le destinataire de l'appel peut faire une supposition éclairée au sujet de la taille de l'appelant variables locales. Ce bloc commence là où le destinataire de l'appel des paramètres de la fin (
sizeof d + (char *)&d
), et se termine à l'appelant de la base de pointeur (__builtin_frame_address(1)
). L'adresse de début peut être légèrement inexacte en raison de l'adresse de l'alignement imposé par le compilateur; la taille calculée peut inclure un morceau de inutilisés espace de pile.sub esp, 56
(généralement la troisième instruction dans une fonction C)? Qui va dans le sens de l'analyse statique plutôt que sur une analyse dynamique. Avant d'entrer dans une discussion détaillée, il y a (ce qui pourrait devenir une réponse différente en elle-même), j'aimerais beaucoup en savoir plus sur vos motivations; pourquoi vous avez besoin de la pile de la taille d'image? Est-il purement hypothétique situation, est-il pour le débogage ou de l'exploitation forestière, ou pensez-vous réellement avoir une vraie vie de cas d'utilisation les cas où cela serait utile?void *
s devrait vraiment êtrechar *
s.struct
. Effectivement, vous définissez vos propres sous-structure de pile, avec un système bien défini de l'adresse et de la taille. Syntaxique verbosité et le manque d'ad-hoc d'échange de données: à mon avis, c'est un petit prix à payer, par rapport à la simplicité et à la facilité de maintenance de simple passage de paramètres.