Uszkodzona ramka stosu GDB - jak debugować?

Mam następujący ślad stosu. Czy można z tego wyciągnąć coś przydatnego do debugowania?

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) 

Gdzie zacząć patrzeć na kod, gdy otrzymamy Segmentation fault, A ślad stosu nie jest tak przydatny?

Uwaga: jeśli wyślę kod, to eksperci SO dadzą mi odpowiedź. Chcę skorzystać z wskazówek SO I sam znaleźć odpowiedź, więc nie zamieszczam tutaj kodu. Przepraszam.

Author: Sangeeth Saravanaraj, 2012-03-21

5 answers

Te fałszywe adresy (0x00000002 i tym podobne) są w rzeczywistości wartościami PC, a nie wartościami SP. Teraz, kiedy dostajesz tego rodzaju SEGV, z fałszywym (bardzo małym) adresem PC, w 99% przypadków jest to spowodowane wywołaniem przez fałszywy wskaźnik funkcji. Zauważ, że wirtualne wywołania w C++ są zaimplementowane za pomocą wskaźników funkcji, więc każdy problem z wirtualnym wywołaniem może manifestować się w ten sam sposób.

Instrukcja wywołania pośredniego po prostu popycha komputer po wywołaniu na stos, a następnie ustawia komputer do celu wartość (w tym przypadku fałszywa), więc jeśli to jest , co się stało, możesz łatwo cofnąć, ręcznie wyrzucając komputer ze stosu. W 32-bitowym kodzie x86 po prostu robisz:

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

Z 64-bitowym kodem x86 potrzebujesz

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

Wtedy powinieneś być w stanie zrobić bt i dowiedzieć się, gdzie naprawdę jest kod.

W pozostałych 1% czasu błąd będzie spowodowany nadpisaniem stosu, zwykle przez przepełnienie tablicy przechowywanej na stosie. W takim przypadku możesz uzyskać więcej przejrzystość sytuacji za pomocą narzędzia typu valgrind

 140
Author: Chris Dodd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 00:10:39

Jeśli sytuacja jest dość prosta, odpowiedź Chrisa Dodda jest najlepsza. Wygląda na to, że przeskoczył przez wskaźnik NULL.

Jest jednak możliwe, że program strzelił sobie w stopę, kolano, szyję i oko przed upaść-overwrote stosu, pomieszane wskaźnik ramki, i inne zło. Jeśli tak, to rozwikłanie hash nie jest prawdopodobne, aby pokazać ziemniaki i mięso.

Bardziej efektywnym rozwiązaniem będzie uruchomienie programu pod debugerem i przejście działa do momentu awarii programu. Po zidentyfikowaniu funkcji powodującej awarię, uruchom ponownie i przejdź do tej funkcji i określ, która funkcja powoduje awarię. Powtarzaj, aż znajdziesz pojedynczy wiersz kodu. 75% czasu, poprawka będzie wtedy oczywiste.

W pozostałych 25% sytuacji tzw. linia kodowa jest czerwonym śledziem. Będzie reagować na (nieprawidłowe) warunki skonfigurowane wiele linii wcześniej-może tysiące linii wcześniej. Jeśli to w przypadku, najlepszy wybrany kurs zależy od wielu czynników: głównie od twojego zrozumienia kodu i doświadczenia z nim:

  • być może ustawienie punktu kontrolnego debuggera lub wstawienie diagnostycznych printf Na krytycznych zmiennych doprowadzi do koniecznego A ha!
  • być może zmiana warunków testowych z różnymi wejściami zapewni więcej wglądu niż debugowanie.
  • może druga para oczu zmusi cię do sprawdzenia swoich założeń lub zebrania przeoczonych dowód.
  • Czasami wystarczy pójść na kolację i pomyśleć o zebranych dowodach.
Powodzenia!
 38
Author: wallyk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Zakładając, że wskaźnik stosu jest prawidłowy...

Może być niemożliwe, aby dokładnie wiedzieć, gdzie SEGV występuje z backtrace -- myślę, że dwie pierwsze klatki stosu są całkowicie nadpisane. 0xbffff284 wygląda na poprawny adres, ale dwa następne nie są. aby przyjrzeć się bliżej stosowi, możesz spróbować:

Gdb$ x / 32ga $rsp

Lub wariant (zastąp 32 inną liczbą). Który wyświetli pewną liczbę słów (32) zaczynającą się od wskaźnika stosu gigantycznego (g) rozmiaru, sformatowanego jako adresy (a). Wpisz "pomoc x", aby uzyskać więcej informacji na temat formatu.

Oprzyrządowanie kodu za pomocą niektórych sentinel 'printf' ów może nie być złym pomysłem, w tym przypadku.

 22
Author: manabear,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-21 18:02:58

Spójrz na inne rejestry, aby sprawdzić, czy w jednym z nich znajduje się wskaźnik stosu. Stamtąd możesz odzyskać stos. Ponadto, jeśli jest to osadzone, często stos jest definiowany pod konkretnym adresem. Używając tego, możesz również czasami uzyskać przyzwoity stos. To wszystko zakłada, że kiedy wskoczyłeś w hiperprzestrzeń, Twój program nie wymiotował po drodze na całą pamięć...

 6
Author: Michael Dorgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-21 17:45:11

Jeśli jest to nadpisanie stosu, wartości mogą odpowiadać czemuś, co jest rozpoznawalne w programie.

Na przykład, znalazłem się patrząc na stos

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

I 0x342d to 13357, który okazał się być identyfikatorem węzła, gdy grepped logów aplikacji dla niego. To natychmiast pomogło zawęzić miejsca kandydowania, w których mogło dojść do nadpisania stosu.

 2
Author: Craig Ringer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-13 10:18:37