Ich habe die folgende Stapelverfolgung. Ist es möglich, daraus etwas Nützliches für das Debuggen zu erkennen?
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)
Wo soll ich anfangen Segmentation fault
, den Code zu betrachten, wenn wir einen bekommen , und der Stack-Trace ist nicht so nützlich?
HINWEIS: Wenn ich den Code poste, geben mir die SO-Experten die Antwort. Ich möchte die Anleitung von SO übernehmen und die Antwort selbst finden, daher poste ich den Code hier nicht. Entschuldigung.
-fno-omit-frame-pointer
? Bei Speicherbeschädigungen ist diesvalgrind
möglicherweise ein geeigneteres Tool, wenn dies für Sie eine Option ist.Antworten:
Diese gefälschten Adressen (0x00000002 und dergleichen) sind tatsächlich PC-Werte, keine SP-Werte. Wenn Sie diese Art von SEGV mit einer gefälschten (sehr kleinen) PC-Adresse erhalten, ist dies in 99% der Fälle auf den Aufruf eines gefälschten Funktionszeigers zurückzuführen. Beachten Sie, dass virtuelle Aufrufe in C ++ über Funktionszeiger implementiert werden, sodass sich jedes Problem mit einem virtuellen Aufruf auf dieselbe Weise manifestieren kann.
Ein indirekter Aufruf - Befehl schiebt nur den PC nach dem Anruf auf den Stapel und legt dann den PC auf den Zielwert (Schein- in diesem Fall), so dass , wenn dies ist , was passiert ist , können Sie diese leicht rückgängig machen , indem Sie manuell den PC aus dem Stapel knallt . In 32-Bit-x86-Code tun Sie einfach:
Mit 64-Bit-x86-Code benötigen Sie
Dann sollten Sie in der Lage sein, a
bt
auszuführen und herauszufinden, wo sich der Code wirklich befindet.In den anderen 1% der Fälle ist der Fehler auf das Überschreiben des Stapels zurückzuführen, normalerweise durch Überlaufen eines auf dem Stapel gespeicherten Arrays. In diesem Fall können Sie möglicherweise mit einem Tool wie valgrind mehr Klarheit über die Situation gewinnen
quelle
gdb executable corefile
öffnet gdb mit der ausführbaren Datei und der Kerndatei, an welchem Punkt Sie tun könnenbt
(oder die obigen Befehle gefolgt vonbt
) ...sp
, nichtesp
oderrsp
, und sein Aufrufbefehl speichert die Rücksprungadresse imlr
Register, nicht auf dem Stapel. Also für ARM, alles , was Sie wirklich brauchen , ist der Anruf rückgängig zu machenset $pc = $lr
. Wenn$lr
es ungültig ist, haben Sie ein viel schwierigeres Problem beim Abwickeln.Wenn die Situation ziemlich einfach ist, ist Chris Dodds Antwort die beste. Es sieht so aus, als wäre es durch einen NULL-Zeiger gesprungen.
Es ist jedoch möglich, dass sich das Programm vor dem Absturz selbst in Fuß, Knie, Nacken und Auge schoss - überschrieb den Stapel, brachte den Rahmenzeiger durcheinander und andere Übel. Wenn ja, dann zeigt das Enträtseln des Haschischs wahrscheinlich nicht Kartoffeln und Fleisch.
Die effizientere Lösung besteht darin, das Programm unter dem Debugger auszuführen und die Funktionen zu überschreiten, bis das Programm abstürzt. Sobald eine Absturzfunktion identifiziert wurde, starten Sie erneut und rufen Sie diese Funktion auf und bestimmen Sie, welche aufgerufene Funktion den Absturz verursacht. Wiederholen Sie diesen Vorgang, bis Sie die einzelne fehlerhafte Codezeile gefunden haben. In 75% der Fälle ist die Korrektur dann offensichtlich.
In den anderen 25% der Situationen ist die sogenannte beleidigende Codezeile ein roter Hering. Es wird auf (ungültige) Bedingungen reagieren, die viele Zeilen zuvor eingerichtet wurden - vielleicht Tausende von Zeilen zuvor. Wenn dies der Fall ist, hängt der beste gewählte Kurs von vielen Faktoren ab: hauptsächlich von Ihrem Verständnis des Codes und Ihrer Erfahrung damit:
printf
für kritische Variablen zu den erforderlichen A ha!Viel Glück!
quelle
Angenommen, der Stapelzeiger ist gültig ...
Es kann unmöglich sein, genau zu wissen, wo das SEGV vom Backtrace auftritt - ich denke, die ersten beiden Stapelrahmen werden vollständig überschrieben. 0xbffff284 scheint eine gültige Adresse zu sein, die nächsten beiden jedoch nicht. Für einen genaueren Blick auf den Stapel können Sie Folgendes versuchen:
gdb $ x / 32ga $ rsp
oder eine Variante (ersetzen Sie die 32 durch eine andere Nummer). Dadurch wird eine bestimmte Anzahl von Wörtern (32) ausgehend vom Stapelzeiger von Riesengröße (g) ausgedruckt, der als Adressen (a) formatiert ist. Geben Sie 'help x' ein, um weitere Informationen zum Format zu erhalten.
In diesem Fall ist es möglicherweise keine schlechte Idee, Ihren Code mit einigen Sentinel-Drucken zu instrumentieren.
quelle
info symbol
wie dies in gdb gemacht wurde.x/256wa $sp
=)Sehen Sie sich einige Ihrer anderen Register an, um festzustellen, ob in einem von ihnen der Stapelzeiger zwischengespeichert ist. Von dort aus können Sie möglicherweise einen Stapel abrufen. Wenn dies eingebettet ist, wird der Stapel häufig an einer bestimmten Adresse definiert. Damit kann man manchmal auch einen anständigen Stack bekommen. Dies alles setzt voraus, dass Ihr Programm beim Springen in den Hyperraum nicht den gesamten Speicher gekotzt hat ...
quelle
Wenn es sich um ein Stapelüberschreiben handelt, entsprechen die Werte möglicherweise etwas, das aus dem Programm erkennbar ist.
Zum Beispiel habe ich mir gerade den Stapel angesehen
und
0x342d
ist 13357, was sich als Knoten-ID herausstellte, als ich die Anwendungsprotokolle danach durchsuchte. Dies half sofort dabei, Kandidatenstellen einzugrenzen, an denen das Überschreiben des Stapels aufgetreten sein könnte.quelle