Mam następujący ślad stosu. Czy można wyciągnąć z tego cokolwiek 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)
Od czego zacząć patrzeć na kod, gdy otrzymamy a Segmentation fault
, a ślad stosu nie jest tak przydatny?
UWAGA: Jeśli wyślę kod, eksperci SO udzielą mi odpowiedzi. Chcę skorzystać ze wskazówek od SO i samemu znaleźć odpowiedź, więc nie umieszczam tutaj kodu. Przeprosiny.
-fno-omit-frame-pointer
? Również w przypadku uszkodzenia pamięcivalgrind
może być bardziej odpowiednim narzędziem, jeśli jest to opcja dla Ciebie.Odpowiedzi:
Te fałszywe adresy (0x00000002 i tym podobne) są w rzeczywistości wartościami PC, a nie wartościami SP. Teraz, kiedy otrzymujesz tego rodzaju SEGV, z fałszywym (bardzo małym) adresem komputera, 99% czasu jest to spowodowane wywołaniem przez fałszywy wskaźnik funkcji. Zwróć uwagę, że wywołania wirtualne w C ++ są implementowane za pomocą wskaźników funkcji, więc każdy problem z wywołaniem wirtualnym może objawiać się w ten sam sposób.
Pośrednią instrukcja wywołanie tylko pcha komputera po wywołaniu na stosie, a następnie ustawia komputer do wartości docelowej (podrobiony w tym przypadku), więc jeśli to jest to, co się stało, można łatwo cofnąć ręcznie pojawiały komputer ze stosu . W 32-bitowym kodzie x86 po prostu robisz:
Z 64-bitowym kodem x86 potrzebujesz
Następnie powinieneś być w stanie zrobić a
bt
i dowiedzieć się, gdzie naprawdę jest kod.W pozostałych 1% przypadków błąd będzie spowodowany nadpisaniem stosu, zwykle przez przepełnienie tablicy przechowywanej na stosie. W takim przypadku możesz uzyskać większą jasność w sytuacji, używając narzędzia takiego jak valgrind
źródło
gdb executable corefile
otworzy gdb z plikiem wykonywalnym i podstawowym, w którym to momencie możesz wykonaćbt
(lub powyższe polecenia, a następniebt
) ...sp
, nieesp
lubrsp
, a jego instrukcja wywołania przechowuje adres powrotu wlr
rejestrze, a nie na stosie. W przypadku ARM wszystko, czego naprawdę potrzebujesz, aby cofnąć połączenie, toset $pc = $lr
. Jeśli$lr
jest nieprawidłowy, masz znacznie trudniejszy problem z odprężeniem.Jeśli sytuacja jest dość prosta, odpowiedź Chrisa Dodda jest najlepsza. Wygląda na to, że przeskoczył przez wskaźnik NULL.
Jednak możliwe jest, że program strzelił sobie w stopę, kolano, szyję i oko przed awarią - nadpisał stos, zepsuł wskaźnik ramki i inne zło. Jeśli tak, to rozwikłanie haszyszu prawdopodobnie nie pokaże ci ziemniaków i mięsa.
Bardziej wydajnym rozwiązaniem będzie uruchomienie programu w debugerze i przechodzenie przez funkcje aż do awarii programu. Po zidentyfikowaniu funkcji powodującej awarię uruchom ponownie, przejdź do tej funkcji i określ, która funkcja wywołuje awarię. Powtarzaj, aż znajdziesz jeden nieprawidłowy wiersz kodu. W 75% przypadków poprawka będzie wtedy oczywista.
W pozostałych 25% sytuacji tak zwana niewłaściwa linia kodu to czerwony śledź. Będzie reagować na (nieprawidłowe) warunki ustawione wcześniej wiele linii - może tysiące linii wcześniej. W takim przypadku wybór najlepszego kursu zależy od wielu czynników: głównie od zrozumienia kodu i doświadczenia z nim:
printf
do krytycznych zmiennych doprowadzi do niezbędnego A ha!Powodzenia!
źródło
Zakładając, że wskaźnik stosu jest prawidłowy ...
Dokładne określenie miejsca wystąpienia SEGV na podstawie śledzenia wstecznego może być niemożliwe - myślę, że pierwsze dwie ramki stosu są całkowicie nadpisane. 0xbffff284 wydaje się być prawidłowym adresem, ale dwa następne nie. Aby dokładniej przyjrzeć się stosowi, możesz spróbować następujących rozwiązań:
gdb $ x / 32ga $ rsp
lub wariant (zamień 32 na inny numer). To wypisze pewną liczbę słów (32) zaczynając od wskaźnika stosu o gigantycznym rozmiarze (g), sformatowanych jako adresy (a). Wpisz „help x”, aby uzyskać więcej informacji na temat formatu.
W tym przypadku oprzyrządowanie twojego kodu za pomocą pewnych wartowniczych 'printf' może nie być złym pomysłem.
źródło
info symbol
jak to zrobić w gdb.x/256wa $sp
=)Spójrz na inne swoje rejestry, aby zobaczyć, czy jeden z nich ma buforowany wskaźnik stosu. Stamtąd możesz odzyskać stos. Ponadto, jeśli jest to osadzone, dość często stos jest definiowany pod bardzo konkretnym adresem. Używając tego, możesz czasami uzyskać przyzwoity stack. Wszystko to zakłada, że kiedy skoczyłeś w nadprzestrzeń, twój program nie rzygał całą pamięcią po drodze ...
źródło
Jeśli jest to nadpisanie stosu, wartości mogą równie dobrze odpowiadać czemuś rozpoznawalnemu w programie.
Na przykład po prostu patrzyłem na stos
i
0x342d
to 13357, który okazał się być identyfikatorem węzła, kiedy grepowałem dla niego dzienniki aplikacji. To natychmiast pomogło zawęzić potencjalne witryny, w których mogło nastąpić nadpisanie stosu.źródło