web-dev-qa-db-fra.com

Cadre de pile corrompu par GDB - Comment déboguer?

J'ai la trace de pile suivante. Est-il possible d'en tirer quelque chose d'utile pour le débogage?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

Où commencer à regarder le code quand on obtient un Segmentation fault, et la trace de la pile n'est pas si utile?

REMARQUE: Si je poste le code, les experts SO me donneront la réponse. Je souhaite suivre les instructions de SO et trouver la réponse moi-même, donc je ne poste pas le code ici.

111

Ces adresses fictives (0x00000002, etc.) sont en réalité des valeurs PC et non des SP. À présent, lorsque vous obtenez ce type de SEGV, avec une fausse (très petite) adresse PC, 99% des Il est important de noter que les appels virtuels en C++ sont implémentés via des pointeurs de fonction, ainsi tout problème lié à un appel virtuel peut se manifester de la même manière.

Une instruction d’appel indirect pousse simplement le PC après l’appel sur la pile, puis définit le PC sur la valeur cible (faux dans ce cas), donc si cela vaut ce qui s’est passé, vous pouvez facilement l’annuler en retirant manuellement le PC de la pile. Dans le code x86 32 bits, vous faites simplement:

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

Avec le code x86 64 bits dont vous avez besoin

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

Ensuite, vous devriez être capable de faire un bt et de déterminer où se trouve réellement le code.

Les 1% restants, l'erreur sera due à l'écrasement de la pile, généralement en dépassant un tableau stocké dans la pile. Dans ce cas, vous pourrez peut-être obtenir plus de clarté sur la situation en utilisant un outil tel que valgrind

152
Chris Dodd

Si la situation est assez simple, réponse de Chris Dodd est la meilleure. On dirait qu'il a sauté par un pointeur NULL.

Cependant, il est possible que le programme se soit abattu sur le pied, le genou, la nuque et les yeux avant de s'écraser - il a écrasé la pile, a gâché le pointeur du cadre et d'autres maux. Si tel est le cas, il est peu probable que le démaquillage du hachis vous montre des pommes de terre et de la viande.

La solution la plus efficace consiste à exécuter le programme sous le débogueur et à passer en revue les fonctions jusqu’à ce que le programme se bloque. Une fois qu'une fonction de blocage est identifiée, recommencez, entrez dans cette fonction et déterminez quelle fonction elle appelle provoque le blocage. Répétez l'opération jusqu'à trouver la seule ligne de code incriminée. 75% du temps, le correctif sera alors évident.

Dans les 25% restants, la ligne de code incriminée est un fil rouge. Il réagira à des conditions (non valides). Configurez plusieurs lignes avant, peut-être des milliers de lignes avant. Si tel est le cas, le meilleur cours choisi dépend de nombreux facteurs: principalement votre compréhension du code et votre expérience:

  • Peut-être que définir un point de surveillance du débogueur ou insérer des diagnostics printf sur des variables critiques conduira au nécessaire A ha!
  • Peut-être que la modification des conditions de test avec différentes entrées fournira plus d'informations que le débogage.
  • Peut-être qu'une deuxième paire d'yeux vous obligera à vérifier vos hypothèses ou à rassembler des preuves négligées.
  • Parfois, il suffit d'aller dîner et de réfléchir aux preuves réunies.

Bonne chance!

42
wallyk

En supposant que le pointeur de pile est valide ...

Il peut être impossible de savoir exactement où se trouve le SEGV à partir de la trace, car je pense que les deux premières trames de pile sont complètement écrasées. 0xbffff284 semble être une adresse valide, mais les deux suivantes ne le sont pas. Pour examiner de plus près la pile, vous pouvez essayer ce qui suit:

gdb $ x/32ga $ rsp

ou une variante (remplacez le 32 par un autre numéro). Cela affichera un certain nombre de mots (32) à partir du pointeur de pile de taille géante (g), formatés comme des adresses (a). Tapez 'help x' pour plus d'informations sur le format.

Instrumenter votre code avec des "printf" sentinelles peut ne pas être une mauvaise idée, dans ce cas.

25
manabear

Examinez certains de vos autres registres pour voir si le pointeur de pile de l'un d'eux est mis en cache. À partir de là, vous pourrez peut-être récupérer une pile. De plus, si cela est intégré, la pile est souvent définie à une adresse très particulière. En utilisant cela, vous pouvez aussi parfois obtenir une pile décente. Tout cela suppose que lorsque vous avez sauté dans l'hyperespace, votre programme n'a pas perdu toute sa mémoire en cours de route ...

6
Michael Dorgan

S'il s'agit d'un écrasement de pile, les valeurs peuvent bien correspondre à quelque chose de reconnaissable par le programme.

Par exemple, je viens de me retrouver à regarder la pile

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

et 0x342d est 13357, ce qui s’est avéré être un nœud-id lorsque j’ai attrapé les journaux de l’application. Cela a immédiatement aidé à réduire le nombre de sites candidats où l'écrasement de la pile aurait pu avoir lieu.

2
Craig Ringer