2009-03-03 8 views
8

Ich möchte in der Lage sein, zu erkennen, wenn eine Adresse zum Schreiben in den Speicher auftritt - zum Beispiel durch Setzen eines Callbacks, das an einen Interrupt angehängt ist. Weiß jemand wie?Möglicher Schreibzugriff auf Adresse (x86 - linux)

Ich möchte in der Lage sein, dies zur Laufzeit (möglicherweise gdb hat diese Funktion, aber meine besondere Anwendung verursacht GDB zum Absturz).

Antwort

14

Wenn Sie Schreibvorgänge in einen Bereich von Adressen abfangen möchten, können Sie mprotect() den fraglichen Speicher als nicht schreibbar markieren und einen Signalhandler mit sigaction() installieren, um den resultierenden SIGSEGV abzufangen, Ihre Protokollierung durchzuführen oder was auch immer Markieren Sie die Seite erneut als schreibbar.

7

Was Sie brauchen, ist der Zugang zu dem X86 Debug-Register: http://en.wikipedia.org/wiki/Debug_register

Sie werden die Unterbrechungsadresse in eines der DR0 bis DR3 festlegen müssen, und dann die Bedingung (Daten schreiben) in DR7. 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, Sie könnten einen einfacheren/kleinen Debugger versuchen wie http://sourceforge.net/projects/minibug/ - wenn das nicht funktioniert, können Sie zumindest den Code gehen und verstehen, wie die Debug-Hardware auf dem Prozessor verwenden, um sich .

Außerdem gibt es eine große IBM-Entwickler Ressource auf die Beherrschung Linux-Debugging-Techniken, die einige zusätzliche Optionen bieten sollte:

http://www.ibm.com/developerworks/linux/library/l-debug/

Ein ziemlich guter Artikel über tun dies Fenster ist hier (ich weiß, du bist läuft auf linux, aber andere könnten zusammen auf diese Frage kommen wollen es in den Fenstern zu tun):

http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

-Adam

+1

Auf die Debug-Register kann nur auf Berechtigungsstufe 0 zugegriffen werden, d. H. Im Kernel. Siehe http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm –

4

GDB hat diese Funktion hat: es ist Hardware Beobachtungspunkte genannt, und es ist sehr gut auf Linux unterstützt/x86:

(gdb) watch *(int *)0x12345678 

Wenn Ihre Anwendung GDB abstürzt, baut aktuelle GDB von CVS Head.

Wenn diese GDB weiterhin fehlschlägt, geben Sie eine GDB bug ein.

Wahrscheinlich sind wir in der Lage, GDB schneller zu reparieren, als Sie den SIGSEGV-Handler umgehen können (vorausgesetzt, ein guter Testfall), und Korrekturen an GDB helfen Ihnen auch bei zukünftigen Problemen.

+1

+1, ich habe gerade eine neue Verwendung für GDB gefunden :) –

2

mprotect hat einen Nachteil: Ihr Speicher muss Seitenrand ausgerichtet sein. Ich hatte mein problematisches Gedächtnis auf dem Stapel und konnte mprotect() nicht benutzen.

Wie Adam sagte, was Sie wollen, ist die Debug-Register zu manipulieren. Unter Windows habe ich das benutzt: http://www.morearty.com/code/breakpoint/ und es hat super funktioniert. 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 solche Äquivalente nicht hat. 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 http://lwn.net/Articles/317153/)

Aber als ich das gefunden: http://blogs.oracle.com/nike/entry/memory_debugger_for_linux Ich habe 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 eingibt 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 arbeiten, 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 geschäftigen Schleife zu wiederholen, ist das richtige was zu tun ein waitpid() auf Ihrem Pid.

Der einzige Haken mit der Ptrace-Methode ist, dass Ihr Programm nur einen "Debugger" gleichzeitig angeschlossen haben kann. 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, Ihre Erinnerung zu schreiben.

Verwandte Themen