Ich möchte erkennen können, wann eine Adresse in den Speicher geschrieben wird - zum Beispiel indem ich einen Callback an einen Interrupt anlege. Weiß jemand wie?
Ich möchte das zur Laufzeit machen können (möglicherweise hat gdb diese Funktion, aber meine besondere Anwendung verursacht gdb zum Absturz).
Wenn Sie Schreibvorgänge in eine Reihe von Adressen abfangen möchten, können Sie die Option mprotect()
verwenden, um sie zu markieren Speicher in Frage als nicht schreibbar, und installieren Sie eine Signal-Handler mit sigaction()
, um die resultierende SIGSEGV zu fangen, tun Ihre Protokollierung oder was auch immer und markieren Sie die Seite wieder als schreibbar.
Was Sie brauchen, ist der Zugriff auf die X86-Debug-Register: Ссылка
Sie müssen die Haltepunktadresse in einem von DR0 bis DR3 und dann die Bedingung (Daten schreiben) in DR7 setzen. Der Interrupt wird auftreten und Sie können Ihren Debug-Code ausführen, um DR6 zu lesen und herauszufinden, was den Haltepunkt verursacht hat.
Wenn GDB nicht funktioniert, können Sie versuchen, einen einfacheren / kleineren Debugger wie Ссылка - wenn das nicht ist arbeiten, können Sie zumindest den Code durchgehen und verstehen, wie Sie die Debugging-Hardware auf dem Prozessor selbst verwenden.
Außerdem gibt es eine großartige IBM Entwickler-Ressource zur Beherrschung von Linux-Debugging-Techniken, die einige zusätzliche Optionen bieten sollte:
Ein einigermaßen guter Artikel darüber ist, dass Windows hier ist (ich weiß, dass Sie auf Linux laufen, aber andere könnten zu dieser Frage kommen, die es in Windows machen wollen):
-Adam
GDB hat dieses Feature: Es heißt Hardware Watchpoints und es wird sehr gut unter Linux / x86 unterstützt:
%Vor%Wenn Ihre Anwendung GDB abstürzt, erstellen Sie die aktuelle GDB von CVS Head .
Wenn diese GDB weiterhin fehlschlägt, reichen Sie einen GDB Fehler ein.
Wahrscheinlich können wir GDB schneller reparieren, als Sie den SIGSEGV-Handler umgehen können (vorausgesetzt, es handelt sich um einen guten Testfall), und Korrekturen an GDB helfen Ihnen auch bei zukünftigen Problemen.
mprotect hat einen Nachteil: Ihr Speicher muss auf Seiten ausgerichtet sein. Ich hatte mein problematisches Gedächtnis auf dem Stapel und konnte mprotect () nicht verwenden.
Wie Adam sagte, wollen Sie die Debug-Register manipulieren. Unter Windows verwendete ich das: Ссылка und es funktionierte großartig. Ich habe es auch auf Mach-O (Mac OS X) portiert, und es hat auch super funktioniert. Es war auch einfach, weil Mach-O thread_set_state () hat, was SetThreadContext () entspricht.
Das Problem mit Linux ist, dass es keine solchen Entsprechungen gibt. Ich habe ptrace gefunden, aber ich dachte, das kann nicht sein, da muss etwas einfacher sein. Aber da ist es nicht. Noch. Ich denke, dass sie an einer hw_breakpoint API für Kernel und Benutzerraum arbeiten. (Siehe Ссылка )
Aber als ich das gefunden habe: Ссылка habe ich es versucht und es war nicht so schlimm. Die ptrace-Methode arbeitet mit einem "externen Prozess", der als "Debugger" fungiert, der an Ihr Programm angehängt wird, neue Werte für die Debug-Register einfügt und mit Ihrem Programm endet, das mit einem neuen hw-Breakpoint-Satz fortfährt. Die Sache ist, Sie können diesen "externen Prozess" selbst erstellen, indem Sie fork () verwenden (ich hatte keinen Erfolg mit einem Pthread), und diese einfachen Schritte inline in Ihrem Code ausführen.
Der addwatchpoint-Code muss angepasst werden, um mit 64-Bit-Linux zu funktionieren, aber das ändert nur USER_DR7 usw. zu offsetof (struct user, u_debugreg [7]). Eine andere Sache ist, dass Sie nach einem PTRACE_ATTACH warten müssen, bis das Debuggee tatsächlich stoppt. Aber anstatt einen POKEUSER in einer "busy loop" zu wiederholen, wäre das richtige waitpid () auf deiner PID.
Der einzige Haken bei der ptrace-Methode ist, dass Ihr Programm immer nur einen "Debugger" angehängt hat. Ein Ptrace-Attach wird also fehlschlagen, wenn Ihr Programm bereits unter gdb-Kontrolle läuft. Aber genauso wie der Beispielcode, können Sie einen Signalhandler für SIGTRAP registrieren, ohne gdb laufen, und wenn Sie das Signal abfangen, geben Sie eine Busy-Schleife ein, die auf gdb zum Anhängen wartet. Von dort können Sie sehen, wer versucht hat, Ihr Gedächtnis zu schreiben.