Más contenido relacionado Similar a Embedded Debugging, czyli co kryje się w jądrze? Similar a Embedded Debugging, czyli co kryje się w jądrze? (20) Embedded Debugging, czyli co kryje się w jądrze?2. Copyright © 2018 Semihalf. All rights reserved.
2
Jakie znamy metody debugowania
programu?
3. Copyright © 2018 Semihalf. All rights reserved.
3
Metoda gumowej kaczuszki
“Droga Kaczuszko, wykonujemy pętelkę,
dopóki x nie będzie równe 3, rozumiesz?”
Źródło [1]
9. Copyright © 2018 Semihalf. All rights reserved.
9
Nie taki kernel straszny...
Metoda User space Kernel
Kaczuszka ✔ ✔
Logowanie ✔ ✔
Asercje ✔ ✔
Crash dump ✔ ✔
Debugger ✔ ✔
Profilowanie ✔ ✔
10. Copyright © 2018 Semihalf. All rights reserved.
10
Jakie funkcje powinien posiadać debugger?
11. Copyright © 2018 Semihalf. All rights reserved.
11
Jakie funkcje powinien posiadać debugger?
● Sterowanie wykonywaniem instrukcji - step, break, continue itp.
● Stawianie breakpointów - zatrzymanie przepływu programu po dotarciu do danej
instrukcji w pamięci.
● Stawianie watchpointów - zatrzymanie przy dostępie do pewnego adresu w pamięci
(zmiennej).
● Modyfikacja i podgląd pamięci programu - do podglądania zawartości zmiennych i
ich modyfikacji.
● Podgląd zawartości rejestrów procesora - w szczególności przy programowaniu
niskopoziomowym.
12. Copyright © 2018 Semihalf. All rights reserved.
12
Gdzie w tym wszystkim procesor?
Bazujemy na architekturze x86_64 Intel 64 i ARMv8-A AARCH64.
Kernel
Obsługa
wyjątków
Wyjątki
CPU
Debugger
PMU
13. Copyright © 2018 Semihalf. All rights reserved.
13
Halo, system! Tu procesor.
System operacyjny może zgłaszać komunikaty do systemu operacyjnego za pomocą:
Intel
● przerwań
● wyjątków
○ Fault
○ Trap
○ Abort
14. Copyright © 2018 Semihalf. All rights reserved.
14
Halo, system! Tu procesor.
System operacyjny może zgłaszać komunikaty do systemu operacyjnego za pomocą:
Intel
● przerwań
● wyjątków
○ Fault - podczas zgłaszania, adres powrotu wskazuje na instrukcję, która spowodowała ten
wyjątek. Po jego obsłudze można wznowić program.
○ Trap - podczas zgłaszania, adres powrotu wskazuje na instrukcję następną po tej
powodującej wyjątek. Po jego obsłudze można wznowić program.
○ Abort- nie zawsze da się go ściśle powiązać z instrukcją, która go spowodowała. Zgłasza
poważne błędy. Po jego obsłudze nie można zrestartować programu, który go spowodował.
Źródła wyjątków
● Błędy programowe wykryte przez procesor
● Wygenerowanie przerwania programowo (instrukcje INT n, INT3, INT1, INTO)
● Układ sprawdzający poprawność stanu szyn danych i procesora
15. Copyright © 2018 Semihalf. All rights reserved.
15
Wyjątki (Intel 64 i IA-32)
16. Copyright © 2018 Semihalf. All rights reserved.
16
Wyjątki (Intel 64 i IA-32)
17. Copyright © 2018 Semihalf. All rights reserved.
17
Narzędzia do debugowania (Intel)
Rejestry do debugowania:
DR0 - DR3 - pozwalają ustawić liniowy adres, który ma powodować wyjątek 1 (#DB).
DR4 - DR5 - jeśli flaga DE nie jest ustawiona w CR4, to aliasują na DR6 i DR7, w przeciwnym wypadku są
zarezerwowane, a dostęp do nich powoduje wyjątek.
DR6 (Debug Status Register) - zawiera informacje, dlaczego wyjątek #DB lub #BP się wywołał.
DR7 - (Debug Control Register) - pozwala m.in. włączać/wyłączać breakpointy DR0 - DR3 oraz ustawiać,
co ma powodować ich wywołanie:
● Uruchomienie instrukcji pod danym adresem
● Zapis do pamięci pod danym adresem
● Odczyt i zapis do pamięci (ale bez pobierania instrukcji) pod danym adresem - tylko jeśli flaga
DE w CR4 jest aktywowana
● Zapis I/O pod danym adresem
18. Copyright © 2018 Semihalf. All rights reserved.
18
Narzędzia do debugowania (Intel)
Stepping
Flaga TF w EFLAGS - kiedy jest aktywowana, wykonanie każdej instrukcji powoduje wyjątek.
Software breakpoint
INT3 - instrukcja procesora, która powoduje programowe wywołanie wyjątku nr 3 (#BP). Stosowane przez
debuggery.
19. Copyright © 2018 Semihalf. All rights reserved.
19
Halo, system! Tu procesor.
System operacyjny może zgłaszać komunikaty do systemu operacyjnego za pomocą
Intel
● przerwań
● wyjątków
○ Fault
○ Trap
○ Abort
ARMv8-A
● Wyjątków
○ Asynchronicznych
■ Przerwania (IRQ) i szybkie przerwania (FIQ)
■ System Error (SError)
○ Synchronicznych
■ Abort
■ Wyjątki generowane programowo
○ Reset
20. Copyright © 2018 Semihalf. All rights reserved.
20
Tabela wyjątków AArch64
Dla każdego typu poziomu wyjątków osobna
tabela:
● EL0 - User space
● EL1 - Kernel
● EL2 - Hypervisor
● EL3 - Secure firmware
Każdy wpis może się składać z 32 instrukcji
(w przeciwieństwie do Intela, gdzie posiadał
on jedynie adres segmentu w GDT do
procedury obsługującej).
21. Copyright © 2018 Semihalf. All rights reserved.
21
Przykłady wyjątków (ARMv8-A)
Rozróżniane na podstawie typu (SError, Synchronous), a dalej na podstawie wpisu w rejestrach ESR_ELn.
System Error
● Błąd parzystości
● Nienaprawialny błąd ECC
Abort
● Dostęp do pamięci, do której nie mamy dostępu (błąd wygenerowany przez MMU)
● Wyjątki do debugowania
● Nieznana instrukcja
Wyjątki wygenerowane programowo
● SVC (Supervisor Call) - pozwala aplikacji w przestrzeni użytkownika uzyskać dostęp do usług
systemu operacyjnego (system call)
22. Copyright © 2018 Semihalf. All rights reserved.
22
Narzędzia do debugowania (AArch64)
Obsługa zdarzeń debugowych może przyjąć dwie formy, zależnie od konfiguracji:
● Halting debug (external debug) - gdy podpinamy się zewnętrznym debuggerem. Procesor
przechodzi wtedy w stan debug.
● Monitor debug (self-hosted debug) - gdy nasz system wspiera debugowanie. Procesor wtedy
zgłasza wyjątki debugowe.
Zdarzenia powodujące wyjątki debugowe:
● Breakpoint
● Watchpoint
● Software Step
● Software Breakpoint Instruction (BRK)
Jeśli jesteśmy wpięci zewnętrznym debuggerem, instrukcja HLT powoduje przejście procesora do stanu
debugowego.
23. Copyright © 2018 Semihalf. All rights reserved.
23
Profilowanie procesora
Współczesne procesory udostępniają wiele danych, do których mamy dostęp z
poziomu systemu operacyjnego, takich jak:
● Liczba cykli procesora
● Odwołania do cache i cache miss
● Statystyki branch predictora (ile nie trafiono, a ile trafiono)
I wiele, wiele, wiele innych… zależnie od architektury i konkretnego układu.
24. Copyright © 2018 Semihalf. All rights reserved.
24
Halo, procesor! Tu system. Już to naprawiam.
1. Procesor zgłasza wyjątek
2. Uruchamia on odpowiednią procedurę obsługi wyjątku
3. Procedura sprawdza, co dokładnie go spowodowało i próbuje go naprawić.
a. Jeśli wyjątek nastąpił w kernelu, to zazwyczaj zabijany jest proces, który
go spowodował, chyba że był to Page Fault
4. Jeśli wyjątek nastąpił w przestrzeni użytkownika, to wysyłany jest sygnał do
procesu
5. Proces obsługuje sygnał (chyba że wysłany został SIGKILL)
25. Copyright © 2018 Semihalf. All rights reserved.
25
Debugowanie aplikacji w Linuxie
Debuggery, takie jak lldb i gdb, korzystają z system calla ptrace. Pozwala on na:
● Dołączenie się do istniejącego procesu (wątku)
● Dostęp do pamięci oraz rejestrów procesu (wątku)
● Przechwytywanie sygnałów wysyłanych do procesu (wątku)
● Wykonywanie instrukcji krok po kroku
● Wznawianie wykonywania procesu (wątku)
● Zamknięcie procesu
Przechwytywanie sygnału:
● Śledzony proces zatrzymuje się za każdym razem, gdy otrzymuje sygnał (za wyjątkiem SIGKILL)
● Debugger, może określić jego stan i powód stopa, za pomocą syscalla waitpid (np. sygnał
SIGTRAP, jeśli był to wyjątek debugowy)
● Debugger może wznowić działanie wątku, zignorować sygnał lub wysłać inny
26. Copyright © 2018 Semihalf. All rights reserved.
26
Co się stanie, gdy w kernelu zdarzy się
niespodziewany błąd, którego nie da się
naprawić?
27. Copyright © 2018 Semihalf. All rights reserved.
27
Haters gonna hate,
kernel gonna panic
30. Copyright © 2018 Semihalf. All rights reserved.
30
Windows - Blue-screen (of death)
34. Copyright © 2018 Semihalf. All rights reserved.
34
Linux - kdump i kexec
W momencie paniki, Linux może stworzyć core dumpa (zapisać obecny obraz
pamięci i stan procesora) - funkcjonalność ta nazywa się kdump.
kexec pozwala na uruchomienie się drugiego kernela w specjalnym miejscu w
pamięci w momencie crasha, który powoduje zrzut pamięci.
Zapobiega to używaniu wypaczonego kernela (tego, który spanikował) do
zrzucania pamięci, co mogłoby powodować dalsze błędy.
Domyślnie brak narzędzi, należy je pobrać (zależnie od dystrybucji).
Core dump można dalej analizować gdb.
35. Copyright © 2018 Semihalf. All rights reserved.
35
Linux - kdb i kgdb
Debugger kernelowy składa się z:
1. debug core
Generyczna obsługa wyjątków, zatrzymywanie procesorów podczas wchodzenia do debuggera,
programowe breakpointy, bezpieczny dostęp do pamięci, API do komunikacji frontend <-> debug
core <-> kgdb drivers.
2. kgdb arch-specific implementation
Wsparcie sprzętowych struktur do debugowania i wyjątków.
3. gdbstub frontend (kgdb)
Logika do zdalnego debugowania po serialu przez gdb.
4. kdb frontend
Frontend do debugowania, przypominający shella.
5. kgdb I/O driver
Sterowniki do urządzeń I/O, które mogą być używane bezpiecznie z kontekstu debuggera.
36. Copyright © 2018 Semihalf. All rights reserved.
36
Linux - kdb i kgdb
kdb
● Prosty shell
● Można debugować maszynę bezpośrednio lub poprzez konsolę serialową
● Poziom assemblera + symbole funkcji (jeśli dostępne)
● Możliwość sprawdzenia pamięci, rejestrów, procesów, dmesga
● Ustawianie breakpointów i podstawowa kontrola flow
kgdb
● Używa gdb na drugiej maszynie do debugowania
● Używa sterowników I/O kgdb do komunikacji
● Możliwość działania na kodzie źródłowym (potrzebny plik vmlinux)
● Ustawianie breakpointów i podstawowa kontrola flow
37. Copyright © 2018 Semihalf. All rights reserved.
37
Linux - kmemleak
● Służy do wykrywania wycieków pamięci w kernelu.
● Śledzi wywołania funkcji alokujących pamięć i zapisuje je w drzewie.
● Skanuje pamięć i rejestry co pewien okres czasu, sprawdzając, czy jest
jakiekolwiek w niej odwołanie do początku zarezerwowanej pamięci lub
dowolnego miejsca w bloku.
● Zgłoszone miejsca można odczytać w pliku: /sys/kernel/debug/kmemleak.
● Wymaga wsparcia w kernelu (CONFIG_DEBUG_KMEMLEAK).
38. Copyright © 2018 Semihalf. All rights reserved.
38
Windows - core dump
● Complete memory dump
Zrzuca całą fizyczną pamięć + dane specyficzne dla driverów.
● Active Memory Dump
Podobny do Complete Mem Dump, ale nie zawiera pamięci nieistotnej dla debugowania.
● Kernel memory dump
Zrzuca wyłącznie pamięć należącą do kernela w momencie paniki.
● Automatic Memory Dump
To samo co Kernel mem dump, ale daje możliwość posiadania pliku stronicowania mniejszego niż
ilość pamięci.
● Small Memory Dump
Rozmiar 64KB. Zawiera dane z blue screena oraz debugowe logi, kontekst procesora, informacje o
procesie który spowodował crash, informacje o wątku który spowodował crash, backtrace (max
16KB) kernela, listę załadowanych modułów, niektóre strony w pamięci.
39. Copyright © 2018 Semihalf. All rights reserved.
39
Windows - debuggery
Visual Studio zintegrowane z Windows debuggerem
● Debugowanie zdalne (Ethernet, USB 2.0/3.0, FireWire, serial)
● Możliwość debugowania podobnie do zwykłych aplikacji
● Ograniczone do sterowników
WinDBG
● Debugowanie zdalne
● Poziom kodu źródłowego i assemblera
● Interfejs graficzny
● Debugownie kernela i userspace’a
KD (lub NTKD)
● Debugowanie zdalne
● Odpowiedni CLI WinDBG
● Debugowanie kernela
40. Copyright © 2018 Semihalf. All rights reserved.
40
FreeBSD - core dump
FreeBSD tez ma możliwość tworzenia core dumpa. Wykorzystuje swap device do
tworzenia zrzutów pamięci.
Po ponownym uruchomieniu systemu, swap jest zapisywany na dysku w folderze
/var/crash.
Istnieją trzy typy zrzutów:
● Full dumps - zrzut całej pamięci
● Minidumps - zrzut wyłącznie stron używanych przez kernel
● Textdumps - tekstowy zapis panic loga i debuggera
41. Copyright © 2018 Semihalf. All rights reserved.
41
FreeBSD - kgdb i ddb
ddb
● Prosty shell
● Można debugować maszynę bezpośrednio lub poprzez konsolę serialową
● Poziom assemblera + symbole funkcji (jeśli dostępne)
● Możliwość sprawdzenia pamięci, rejestrów, procesów, dmesga
● Ustawianie breakpointów, kontrola flow, możliwość uruchamiania funkcji systemu operacyjnego,
modyfikacji pamięci
kgdb
● Używa gdb na drugiej maszynie do debugowania
● Używany do analizy core dumpa
● Debugowanie po serialu
● Możliwość działania na kodzie źródłowym (kernel z makeoptions DEBUG=-g)
● Debugowanie jak podczas używania gdb
42. Copyright © 2018 Semihalf. All rights reserved.
42
macOS - ddb i lldb
ddb
● Wbudowany w kernel
● Debugowanie po serialu (adaptery nie działają)
● Debugowanie na poziomie asemblera
● Możliwość stawiania breakpointów, watchpointów
● Przydaje się, gdy debugujemy wbudowany interfejs sieciowy, przerwania sprzętowe, lub zanim stos
sieciowy zostanie zainicjalizowany
lldb
● Zdalne debugowanie przez sieć
● Debugowanie na poziomie kodu źródłowego
● Przypomina debugowanie zwykłej aplikacji za pomocą lldb
43. Copyright © 2018 Semihalf. All rights reserved.
43
DTrace
● Natywne wsparcie wyłącznie we FreeBSD i macOS.
● Nieoficjalny port na Linuxa.
● Dynamicznie dodaje i usuwa próbniki.
● Skrypty pisane w języku D.
● We FreeBSD możliwość definiowania własnych próbników.
46. Copyright © 2018 Semihalf. All rights reserved.
46
Źródła
[1] https://en.wikipedia.org/wiki/Rubber_duck_debugging#/media/File:Rubber_duck_assisting_with_debugging.jpg
[2] https://docs.microsoft.com/en-us/visualstudio/profiling/profiling-feature-tour?view=vs-2017
[3] https://www.kernel.org/doc/html/v4.18/dev-tools/kgdb.html
[4] https://help.ubuntu.com/lts/serverguide/kernel-crash-dump.html.en
[5] https://www.kernel.org/doc/html/v4.18/dev-tools/kmemleak.html
[6] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/index
[7] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/crash-dump-files
[8] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/blue-screen-data
[9] https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/build/build.html
[10] https://www.freebsd.org/doc/en/books/developers-handbook/kerneldebug.html
[11] The Design and Implementation of the FreeBSD Operating System (2nd Edition); Marshall Kirk McKusick; George
V. Neville-Neil; Robert N.M. Watson
[12] Understanding the LINUX Kernel: From I/O Ports to Process Management; Daniel Pierre Bovet; Marco Cesati
[13] Intel® 64 and IA-32 Architectures Software Developer’s Manual, November 2018
[14] Programmer’s Guide for ARMv8-A, v1.0 2015