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)?
11
Odpowiedzi:
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.out
zrzuca 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 wszystkoJeśli obiekt wykonywalny nadal zawiera tabele symboli (nie został usunięty -
man strip
a użytkownik-g
generował debugowaniegcc
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 wgdb
celu 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
Produkuje
a następnie nie używając tablicy symboli i nie sprawdzając adresu bezpośrednio w
Produkuje
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ę.
źródło
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.
źródło
(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ąś
-g
opcją. Polecam kompilowanie programów do debugowania-g3 -O0
i być może,-fno-inline
jeś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
core
zrzutem) 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).
źródło