2009-03-03 13 views
8

Chcę być w stanie wykryć, kiedy występuje adres zapisu do pamięci - na przykład ustawiając wywołanie zwrotne dołączone do przerwania. Czy ktoś wie jak?Możliwe pułapki zapisu na adres (x86 - linux)

Chciałbym móc to zrobić w czasie wykonywania (prawdopodobnie gdb ma tę funkcję, ale moja konkretna aplikacja powoduje awarię gdb).

Odpowiedz

14

Jeśli chcesz przechwytywać zapisy do zakresu adresów, możesz użyć mprotect(), aby oznaczyć pamięć jako nieopisywalną, i zainstalować procedurę obsługi sygnału za pomocą sigaction(), aby przechwycić wynikowy SIGSEGV, zrobić rejestrowanie lub cokolwiek i oznacz stronę jako ponownie zapisaną.

7

czego potrzebujesz to dostęp do rejestrów debugowania X86: http://en.wikipedia.org/wiki/Debug_register

Musisz ustawić breakpoint adres w jednym z DR0 do DR3, a to warunek (zapis danych) w DR7. Przerwanie nastąpi i możesz uruchomić swój kod debugowania, aby odczytać DR6 i znaleźć przyczynę punktu przerwania.

Jeśli GDB nie działa, możesz wypróbować prostszy/mniejszy debugger, taki jak http://sourceforge.net/projects/minibug/ - jeśli to nie działa, możesz przynajmniej przejrzeć kod i zrozumieć, jak samemu użyć sprzętu do debugowania na procesorze .

Ponadto, istnieje wielka IBM deweloper zasobów na opanowanie technik linux debugowania, które powinny zapewnić pewne dodatkowe opcje:

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

całkiem dobry artykuł na temat tego rozwiązania jest to Windows jest tutaj (wiem, że jesteś działa na Linuksie, ale inni mogą przyjść na to pytanie chce to zrobić w Windows):

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

-Adam

+1

Dostęp do rejestrów debugowania można uzyskać tylko na poziomie uprawnień 0, tj. W jądrze. Zobacz http://pdos.csail.mit.edu/6.828/2008/readings/i386/s12_02.htm –

4

GDB ma tę cechę: to się nazywa watchpoints sprzętowe i jest bardzo dobrze obsługiwane w systemie Linux/x86:

(gdb) watch *(int *)0x12345678 

Jeśli aplikacja wywala GDB, budować aktualną GDB z CVS Head.

Jeśli to GDB nadal nie powiedzie się, zgłoś GDB bug.

Jest szansa, że ​​możemy naprawić GDB szybciej, niż można zhakować wokół handlerka SIGSEGV (pod warunkiem, że jest to dobry przypadek testowy), a poprawki do GDB pomogą ci również w przyszłych problemach.

+1

+1, właśnie znalazłem nowe zastosowanie dla GDB :) –

2

mprotect ma wadę: Twoja pamięć musi być wyrównana do strony. Miałem problematyczną pamięć na stosie i nie mogłem użyć mprotect().

Zgodnie z tym, co powiedział Adam, chodzi o manipulowanie rejestrami debugowania. W systemie Windows użyłem tego: http://www.morearty.com/code/breakpoint/ i działało świetnie. Przesyłałem go również do Mach-O (Mac OS X) i działało też świetnie. Było to również łatwe, ponieważ Mach-O ma thread_set_state(), co jest równoważne SetThreadContext().

Problem z Linuksem polega na tym, że nie ma takich odpowiedników. Znalazłem ptrace, ale pomyślałem, to nie może być to, musi być coś prostszego. Ale nie ma. Jeszcze. Myślę, że pracują nad interfejsem API hw_breakpoint dla obu przestrzeni jądra i użytkownika.(zobacz http://lwn.net/Articles/317153/)

Ale kiedy znalazłem to: http://blogs.oracle.com/nike/entry/memory_debugger_for_linux Spróbowałem i nie było tak źle. Metoda ptrace działa przez jakiś "proces zewnętrzny" działając jako "debugger", dołączając do twojego programu, wprowadzając nowe wartości do rejestrów debugowania i kończąc z twoim programem kontynuującym z nowym zestawem punktów przerwania hw. Chodzi o to, że możesz samemu stworzyć ten "proces zewnętrzny", używając fork(), (nie miałem żadnego sukcesu z pthreadem) i wykonując te proste kroki w kodzie.

Kod addwatchpoint musi być dostosowany do pracy z 64-bitowym systemem Linux, ale to tylko zmienia USER_DR7 itd. W offset2 (użytkownik struct, u_debugreg [7]). Inną sprawą jest to, że po PTRACE_ATTACH, musisz poczekać, aż debuger faktycznie się zatrzyma. Ale zamiast ponawiania POKEUSER'a w zajętej pętli, poprawną rzeczą byłoby użycie waitpid() na twoim pidzie.

Jedynym haczykiem przy użyciu metody ptrace jest to, że program może mieć tylko jeden "debugger" podłączony w tym samym czasie. Tak więc dołączenie ptrace nie powiedzie się, jeśli twój program jest już uruchomiony pod kontrolą gdb. Ale tak jak w przykładowym kodzie, możesz zarejestrować obsługę sygnału dla SIGTRAP, uruchomić bez gdb, a kiedy złapiesz sygnał, wprowadź pętlę zajętą, czekającą na dołączenie gdb. Stamtąd możesz zobaczyć, kto próbował napisać twoją pamięć.