Question

Existe-t-il un moyen de connaître et de générer la taille de pile nécessaire à une fonction lors de la compilation en C? Voici ce que j'aimerais savoir:

Prenons une fonction:

void foo(int a) {
    char c[5];
    char * s;
    //do something
    return;
}

Lors de la compilation de cette fonction, je voudrais savoir combien d’espace de pile elle consomme lorsqu’elle est appelée. Cela peut être utile pour détecter la déclaration sur pile d’une structure masquant un gros tampon.

Je cherche quelque chose qui pourrait imprimer quelque chose comme ceci:

fichier foo.c: utilisation de la pile foo est n octets

Existe-t-il un moyen de ne pas regarder l'assemblage généré pour le savoir? Ou une limite qui peut être définie pour le compilateur?

Mise à jour: je n'essaie pas d'éviter le dépassement de capacité de la pile d'exécution pour un processus donné. Je cherche un moyen de rechercher avant l'exécution si une utilisation de la pile de fonctions, telle que déterminée par le compilateur, est disponible en tant que sortie de la compilation. processus.

En d'autres termes: est-il possible de connaître la taille de tous les objets locaux d'une fonction? Je suppose que l’optimisation du compilateur ne sera pas mon ami, car une variable disparaîtra, mais une limite supérieure est acceptable.

Était-ce utile?

La solution

Le code du noyau Linux s’exécute sur une pile 4K sous x86. Par conséquent, ils se soucient. Ce qu’ils utilisent pour vérifier cela, c’est un script Perl qu’ils ont écrit, que vous pouvez trouver sous la forme scripts / checkstack.pl dans une archive tar récente du noyau (la version 2.6.25 l’a obtenu). Il fonctionne sur la sortie de objdump, la documentation d'utilisation est dans le commentaire initial.

Je pense que je l'avais déjà utilisé pour les fichiers binaires de l'espace utilisateur il y a bien longtemps. Si vous connaissez un peu la programmation Perl, il est facile de résoudre ce problème s'il est endommagé.

Quoi qu’il en soit, l’essentiel est de regarder automatiquement la sortie de GCC. Et le fait que les pirates informatiques du noyau aient écrit un tel outil signifie qu’il n’ya pas de moyen statique de le faire avec GCC (ou peut-être qu’il a été ajouté très récemment, mais je doute de cela).

Btw, avec objdump du projet mingw et ActivePerl, ou avec Cygwin, vous devriez pouvoir le faire aussi sous Windows et également sur des fichiers binaires obtenus avec d'autres compilateurs.

Autres conseils

StackAnlyser semble examiner le code exécutable lui-même ainsi que des informations de débogage. Ce que décrit cette réponse , c’est ce que je recherche, l’analyseur de pile me semble excessif.

Quelque chose de semblable à ce qui existe pour ADA serait bien. Regardez cette page de manuel du manuel de gnat:

22.2 Analyse de l'utilisation de la pile statique

Une unité compilée avec -fstack-usage générera un fichier supplémentaire spécifiant la quantité maximale de pile utilisée, fonction par fonction. Le fichier a le même nom de base que le fichier objet cible avec une extension .su. Chaque ligne de ce fichier est composée de trois champs:

* The name of the function.
* A number of bytes.
* One or more qualifiers: static, dynamic, bounded. 

Le deuxième champ correspond à la taille de la partie connue du cadre de la fonction.

Le qualificatif statique signifie que la taille du cadre de la fonction est purement statique. Cela signifie généralement que toutes les variables locales ont une taille statique. Dans ce cas, le deuxième champ est une mesure fiable de l’utilisation de la pile de fonctions.

Le qualificatif dynamique signifie que la taille du cadre de la fonction n'est pas statique. Cela se produit principalement lorsque certaines variables locales ont une taille dynamique. Lorsque ce qualificateur apparaît seul, le deuxième champ n'est pas une mesure fiable de l'analyse de la pile de fonctions. Lorsqu'il est qualifié avec borné, cela signifie que le deuxième champ est un maximum fiable de l'utilisation de la pile de fonctions.

Je ne vois pas pourquoi une analyse de code statique ne pourrait pas donner un chiffre assez bon pour cela.

Il est facile de trouver toutes les variables locales dans une fonction donnée, et la taille de chaque variable peut être trouvée via le standard C (pour les types intégrés) ou en le calculant (pour les types complexes tels que structs et unions).

Bien sûr, la réponse ne peut pas être garantie à 100%, car le compilateur peut effectuer diverses optimisations telles que le remplissage, l'insertion de variables dans des registres ou la suppression complète de variables inutiles. Mais toute réponse donnée devrait au moins être une bonne estimation.

J'ai effectué une recherche rapide sur Google et trouvé StackAnalyzer , mais je suppose que cette analyse de code statique est une autre les outils ont des capacités similaires.

Si vous voulez un résultat précis à 100%, vous devrez alors regarder la sortie du compilateur ou la vérifier pendant l'exécution (comme Ralph l'a suggéré dans sa réponse )

Seul le compilateur le saurait vraiment, puisque c’est le gars qui rassemble toutes vos affaires. Vous devez examiner l'assembly généré et voir combien d'espace est réservé dans le préambule, mais cela ne compte pas vraiment pour des éléments tels que alloca qui agissent de la sorte à l'exécution.

En supposant que vous soyez sur une plate-forme intégrée, vous constaterez peut-être que votre chaîne d’outils s’essaie à cela. Les bons compilateurs commerciaux (tels que, par exemple, le compilateur Arm / Keil) produisent souvent des rapports sur l'utilisation de la pile.

Bien sûr, les interruptions et la récursivité les dépassent généralement un peu, mais cela vous donne une idée approximative si quelqu'un a commis quelque chose de terrible avec un tampon de plusieurs mégaoctets sur la pile quelque part.

Pas exactement "compile time", mais je le ferais comme étape de post-construction:

  • laisser l'éditeur de liens créer un fichier de carte pour vous
  • pour chaque fonction du fichier de mappage, lisez la partie correspondante de l'exécutable et analysez le prologue de la fonction.

Cela ressemble à ce que fait StackAnalyzer, mais en beaucoup plus simple. Je pense que l'analyse de l'exécutable ou du désassemblage est le moyen le plus simple d'accéder à la sortie du compilateur. Bien que le compilateur connaisse ces informations en interne, je crains que vous ne puissiez pas les récupérer (vous pouvez demander au fournisseur du compilateur d'implémenter la fonctionnalité ou, si vous utilisez un compilateur open source, vous pouvez le faire vous-même ou laisser quelqu'un le faire. pour vous).

Pour implémenter cela, vous devez:

  • pouvoir analyser le fichier de carte
  • comprendre le format de l'exécutable
  • savoir à quoi un prologue de fonction peut ressembler et pouvoir "décoder" il

La facilité ou la difficulté de cette tâche dépend de votre plate-forme cible. (Embarqué? Quelle architecture de CPU? Quel compilateur?)

Tout cela peut certainement être fait avec x86 / Win32, mais si vous n’avez jamais rien fait de tel et devez tout créer à partir de rien, cela peut prendre quelques jours avant que vous ayez terminé et que quelque chose fonctionne.

Pas en général. Le problème de l’arrêt en informatique théorique suggère que vous ne pouvez même pas prédire si un programme général s’arrête sur une entrée donnée. Le calcul de la pile utilisée pour l'exécution d'un programme en général serait encore plus compliqué. Donc non. Peut-être dans des cas particuliers.

Supposons que vous ayez une fonction récursive dont le niveau de récursivité dépend de l'entrée qui peut être de longueur arbitraire et que vous n'avez déjà pas de chance.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top