Zdalne debugowanie: GDB server


Jedną z wielkich zalet systemów wbudowanych opartych na Linuxie nad innymi systemami jest mnogość narzędzi do debugowania. Jest ich tak dużo, że czasem trudno wybrać właściwe. Dlatego też niesłabnącą popularnością cieszy się używanie funkcji printf() w podejrzanych miejscach 🙂

Jednym z najpopularniejszych programów do debugowania w trudnych sytuacjach jest GDB, czyli GNU Debugger.

W pracy przy aplikacjach embedded często nasze główne środowisko jest na hoście (np. laptop z procesorem x86) a naszym targetem jest urządzenie o małych zasobach sprzętowych i innej architekturze, na przykład ARM. W takiej sytuacji bardzo wygodnie jest debugować program zdalnie, czyli z użyciem programu GDB server.

Skąd wziąć GDB i server?

Załóżmy, że pracujemy z systemem na Raspberry Pi. Na początku będziemy potrzebować toolchaina, czyli zbioru aplikacji, które umożliwiają tworzenie programów na docelową architekturę, czyli w tym wypadku ARM 64bit. Chodzi tutaj m.in. o kompilator, linker, assembler i tym podobne.

Możemy korzystać z gotowych, pre-built toolchainów np. ze strony ARM: https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads

Często jednak lepiej jest mieć pełną kontrolę nad naszymi narzędziami. Wtedy najlepiej sięgnąć po rozwiązania open source, które możemy dostosować pod nasze konkretne zastosowanie. Jednym z takich toolchainów jest crosstool-ng.

crosstool-ng – setup

Na początku pobieramy źródła projektu z oficjalnego githuba:

git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng/

crosstool jest oparty o system budowania GNU autotools. Musimy odpalić skrypty, które przygotowują pliki Make i ustawią odpowiednio cały proces kompilacji toolchaina:

./bootstrap
./configure --enable-local
make

Opcja –enbale-local oznacza wejście w tryb „hakerski” (zgodnie z oficjalną dokumentacją). Dzięki temu toolchain nie będzie instalowany w naszym systemie jako ogólnodostępne narzędzie, tylko pliki wynikowe razem ze źródłami będą w jednym miejscu. To ułatwi wprowadzanie zmian w konfiguracji.

Po kompilacji dostaniemy dostęp do narzędzia ct-ng, czyli konfiguratora crosstool-ng. Możemy teraz wylistować wszystkie możliwe konfiguracje (jest ich BARDZO dużo) i wpisać odpowiednie ustawienia dla danego targetu. W tym przypadku Raspberry Pi 4

./ct-ng list-samples | grep -i rpi
./ct-ng show-aarch64-rpi4-linux-gnu

# wpisanie odpowiedniego configa
./ct-ng aarch64-rpi4-linux-gnu

Jeśli chcemy wprowadzić dodatkowe zmiany, możemy to zrobić znanym m.in. z kernela narzędziem menuconfig:

./ct-ng menuconfig

Jesteśmy teraz gotowi do budowania. Potrwa około 20-30 minut.

./ct-ng build

Końcowym efektem budowania będzie powstanie folderu o nazwie x-tools, gdzie będą się znajdować wszystkie narzędzia potrzebne do cross-kompilacji oraz GDB i gdbserver.

gdbserver

Przechodzimy do ścieżki, gdzie znajduje się gdbserver i przerzucamy go za pomocą SCP na nasz target

cd crosstool-ng/x-tools/aarch64-rpi4-linux-gnu/aarch64-rpi4-linux-gnu/debug-root/usr/bin
scp gdbserver  root@<TARGET IP>:/tmp

Logujemy się na target. W moim przypadku aplikacja, którą chcę debugować to prosty hello world, skompilowany wcześniej na hoście. Odpalamy gdbserver wraz z wybranym portem (tutaj 1234) oraz ścieżką do pliku, który chcemy zbadać:

cd /tmp
./gdbserver :1234 <SCIEZKA DO BINARKI>

W tym momencie na porcie 1234 mamy wystawiony interfejs do GDB. Pozostaje tylko połączyć się z nim z naszego komputera.

Na hoście przechodzimy do katalogu gdzie znajduje się główny program GDB:

cd crosstool-ng/x-tools/aarch64-rpi4-linux-gnu/bin
./aarch64-rpi4-linux-gnu-gdb <SCIEZKA DO BINARKI>

Musimy podać ścieżkę do skompilowanego pliku.

UWAGA: Żeby wszystko zadziałało jak trzeba, plik musi być skompilowany razem z tzw. 'debug symbols’. Dzięki temu będziemy dokładnie wiedzieć jakie są w nim zdefiniowane funkcje, zmienne itp. Możemy to zrobić dodając przy kompilacji flagę ’-g’. Tutaj więcej informacji: https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html

Została ostatnia rzecz. Musimy przekazać GDB do jakiego targetu chcemy się podłączyć. Używamy tego samego portu, który określiliśmy przy wywołaniu gdbserver

target remote <IP TARGETU>>:1234

Możliwości GDB

Po połączeniu serwera z targetem mamy dostęp do najpopularniejszych funkcji gdb. Możemy np.

Ustawić breakpoint w programie, w konkretnym pliku, w konkretnej linijce:

break hello.c:12

Sprawdzić co doprowadziło do crashu aplikacji:

backtrace

Obserwować wartość zmiennych lokalnych:

info locals

Lub po prostu sprawić że program zacznie się wykonywać:

continue

Daj znać jeśli znasz inne ciekawe techniki debugowania poza GDB. Zawsze warto rozszerzać swój arsenał narzędziowy!

Źródła

W tym wpisie korzystałem z linków:

https://crosstool-ng.github.io/docs

https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html

https://www.cs.umd.edu/~srhuang/teaching/cmsc212/gdb-tutorial-handout.pdf


Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *