Dlaczego GDB potrzebuje pliku wykonywalnego oraz zrzutu pamięci?


11

Debuguję za pomocą zrzutów pamięci i zauważ, że gdb wymaga dostarczenia pliku wykonywalnego oraz zrzutu pamięci. Dlaczego to? Jeśli zrzut pamięci zawiera całą pamięć używaną przez proces, to czy plik wykonywalny nie zawiera zrzutu pamięci? Być może nie ma gwarancji, że cały plik exe zostanie załadowany do pamięci (poszczególne pliki wykonywalne zwykle nie są tak duże), a może zrzut pamięci nie zawiera jednak całej odpowiedniej pamięci? Czy dotyczy to symboli (być może nie są one normalnie ładowane do pamięci)?


1
Plik wykonywalny zawiera informacje o symbolu, jak odnotowano w dokumentacji gdb ...
Thomas Dickey,

1
Co zaskakujące, w żadnej odpowiedzi (oprócz tej, którą właśnie dodałem) nie wspomina się o formacie DWARF
Basile Starynkevitch

Odpowiedzi:


15

Zrzut podstawowy to tylko zrzut pamięci programów, jeśli wiesz, gdzie wszystko było, możesz po prostu tego użyć.

Używasz pliku wykonywalnego, ponieważ wyjaśnia on, gdzie (pod względem adresów logicznych) rzeczy znajdują się w pamięci, tj. Plik podstawowy.

Jeśli użyjesz polecenia objdump, zrzuci on metadane dotyczące badanego obiektu wykonywalnego. Jako przykład można użyć obiektu wykonywalnego o nazwie a.out.

objdump -h a.outzrzuca tylko informacje nagłówka, zobaczysz sekcje o nazwie np. .data lub .bss lub .text (jest ich znacznie więcej). Informują one moduł ładujący jądra, gdzie w obiekcie można znaleźć różne sekcje i gdzie w przestrzeni adresowej procesu sekcja powinna zostać załadowana, a dla niektórych sekcji (np. .Data .text), co należy załadować. (sekcja .bss nie zawiera żadnych danych w pliku, ale odnosi się do ilości pamięci do zarezerwowania w procesie na niezainicjowane dane, jest wypełniona zerami).

Układ pliku wykonywalnego obiektu jest zgodny ze standardem ELF.

objdump -x a.out - zrzuca wszystko

Jeśli obiekt wykonywalny nadal zawiera tabele symboli (nie został usunięty - man stripa użytkownik -ggenerował debugowanie gcc przy założeniu kompilacji źródła ac), to można sprawdzić zawartość rdzenia według nazw symboli, np. Jeśli miałeś zmienną / bufor o nazwie inputLine w kodzie źródłowym, możesz użyć tej nazwy w gdbcelu sprawdzenia jego zawartości. tzn. znałbygdb przesunięcie od początku zainicjowanych przez program segmentów danych, w których rozpoczyna się inputLine, i długość tej zmiennej.

Dalsza lektura art. 1 , art. 2 oraz szczegółowej specyfikacji pliku wykonywalnego i formatu łączącego (ELF) .


Zaktualizuj po komentarzu @mirabilos poniżej.

Ale jeśli używasz tabeli symboli jak w

$ gdb --batch -s a.out -c core -q -ex "x buf1"

Produkuje

 0x601060 <buf1>:    0x72617453

a następnie nie używając tablicy symboli i nie sprawdzając adresu bezpośrednio w

$ gdb --batch -c core -q -ex "x 0x601060"

Produkuje

0x601060:   0x72617453

Zbadałem pamięć bezpośrednio, bez użycia tablicy symboli w drugim poleceniu.


Widzę również, że odpowiedź @ user580082 stanowi dodatkowe wyjaśnienie i będzie głosować w górę.


6
Nigdy nie słyszałem o „podstawowej sekcji stosu”. .bss to (historycznie) „blok uruchamiany przez symbol” i praktycznie „dane jednostkowe”, podczas gdy .data to „dane zainicjowane”, a tekst (nie .code) służy do przechowywania kodu maszynowego. W pliku binarnym nie ma sekcji stosu, ponieważ stosy są tworzone w czasie wykonywania.
jlliagre

„Jeśli wiesz, gdzie wszystko było, to możesz po prostu tego użyć”, to również nie jest prawdą, ponieważ nie wszystko w programie jest koniecznie uwzględnione w śladzie.
mirabilos

1
@ jlliagre masz rację, przez pomyłkę nazwałem .text .code (ponieważ podczas tworzenia odpowiedzi myślałem o wyjaśnieniu) - zaktualizowałem. Błędnie pomyślałem o bss niepoprawnie z nazwy i zaktualizowałem swoją odpowiedź, ale unikałem * Block Started by Symbol, ponieważ nie sądzę, że tak naprawdę dodaje się do równania i wyjaśniłem, że jest on używany jako dane niezainicjalizowane, co było naszym powszechne rozumienie. Dziękuję - doceniam twój komentarz, aby poprawić ten post.
X Tian

4

Plik podstawowy to migawka obrazu stosu, mapowania pamięci i rejestrów w momencie zakończenia procesu. Z zawartością można manipulować tak, jak podano na głównej stronie podręcznika użytkownika . Domyślnie prywatne mapowania, wspólne mapowania i informacje nagłówka ELF są zrzucane do pliku podstawowego.

Jeśli chodzi o twoje pytanie , powodem, dla którego gdb wymaga pliku wykonywalnego, jest to, że nie symuluje on wykonania, czytając i interpretując instrukcje binarne, takie jak valgrind, zamiast tego staje się rodzicem procesu, aby kontrolować zachowanie procesu podczas uruchamiania czas. Korzysta z pliku podstawowego, aby określić odwzorowania pamięci i stan procesora podczas awarii.

W Linuksie procesy nadrzędne mogą uzyskać dodatkowe informacje o swoich dzieciach, w szczególności możliwość ich śledzenia, co umożliwia debugerowi dostęp do informacji niskiego poziomu procesu, takich jak odczyt / zapis jego pamięci, rejestrów, zmiana mapowania sygnałów, zatrzymanie jego wykonania itp.

Zrozumiesz wymóg wykonywalności pomimo posiadania podstawowego pliku po przeczytaniu, jak działa dowolny debugger.


1

(oprócz innych dobrych odpowiedzi)

W nowoczesnych systemach Linux (i wielu systemach uniksowych) informacje debugowania (w tym metadane dotyczące typów symboli, lokalizacji kodu źródłowego, rodzaju zmiennych itp. Itd.) Są w formacie DWARF i znajdują się w pliku wykonywalnym ELF ( lub biblioteki współdzielone ELF), gdy jest kompilowany z jakąś -gopcją. Polecam kompilowanie programów do debugowania -g3 -O0i być może, -fno-inlinejeśli używasz najnowszej wersji GCC ; jednak za pomocą GCC można nawet skompilować zarówno informacje dotyczące optymalizacji, jak i debugowania, np. z -O2 -g1, chociaż informacje debugowania mogą w tym przypadku być nieco „niewyraźne” (może to nieco pomóc w złapaniu niektórych niegrzecznych Heisenbugs ).

Rozsądne jest unikanie umieszczania tych informacji w plikach podstawowych , ponieważ możesz mieć wiele różnych plików podstawowych (wyobraź sobie powszechnie używane oprogramowanie, w którym wielu użytkowników tworzy raporty o błędach, większość z nich ze corezrzutem) dla tego samego pliku wykonywalnego. Również rdzeń (5) pliki są po cenach dumpingowych przez jądro, które nie powinien dbać o istnieniu sekcji karzeł w elf (5) plików wykonywalnych (ponieważ te odcinki nie są odwzorowywane w wirtualnej przestrzeni adresowej w Błąd procesu , który po cenach dumpingowych rdzeń na jakiś sygnał ( 7) ). Istnieje nawet możliwość umieszczenia informacji debugowania w osobnych plikach (poza plikiem wykonywalnym).

BTW, GDB można boleśnie wykorzystać do debugowania zrzutów rdzenia dla plików wykonywalnych bez żadnych informacji debugowania. Ale wtedy praktycznie debugujesz na poziomie kodu maszynowego (nie na poziomie symbolicznym zapewnianym przez języki programowania i ich kompilatory).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.