Wszystkim się może zdarzyć, że nieustanne, wielogodzinne gapienie się w kod programu nic nie daje i program ciągle nie chce nam działać. Wtedy z pomocą przychodzą debugery. W tej części zaprezentuję kilka wartych uwagi programów tego typu. Nie będę szczegółowo mówił, jak ich używać, bo zwykle posiadają albo menu, albo dokumentację czy inną pomoc.
UWAGA: Niektóre debugery mogą wpływać na wartości widziane w rejestrach. Może
się tak stać na przykład wtedy, gdy przerwanie, które wywołujemy, jest wewnętrznie wykorzystywane przez debuger.
W szczególności, może to mieć miejsce w przypadku Turbo Debuggera
i funkcji 3Fh
i 40h
przerwania
DOS-a (int 21h
).
Debuggery programów DOS-owych:
Rozpowszechniany z pakietem OpenWatcom, WD jest najlepszym z darmowych debugerów. Umożliwia wyświetlanie rejestrów procesora, flag, koprocesora, MMX i SSE, śledzenie wartosci zmiennych, stawianie pułapek (breakpoint, klawisz F9), podgląd wyjścia programu (klawisz F4), wykonywanie do kursora i wiele innych działań. Posiada obsługę myszy. Pozwala debugować wszystko, co może być wytworzone przez pakiet OpenWatcom - .com, .exe (MZ i LE) i wiele innych.
Jeden z najlepszych dostępnych. Niestety, nie darmowy.
Umożliwia wyświetlanie rejestrów 16/32-bit, rejestrów
koprocesora, stosu i pewnych regionów pamięci (standardowo DS:0000) oraz flag i
daje możliwość modyfikacji ich wszystkich. Można obserwować zmienne oraz to, co się
dzieje na ekranie poza debuggerem.
Gdy testowałem program działający w trybie graficznym, to po każdej komendzie monitor
przełączał się w ten tryb, po czym wracał do debuggera, co umożliwia podglądanie
naprawdę każdego kroku. Niestety, zrobienie czegokolwiek poza przejściem do kolejnej
instrukcji wymagało przejścia w tryb pełnego ekranu, gdyż okienko w
Windows nie za bardzo było potem odświeżane...
Niestety, TD nie ma on pojęcia o technologiach takich, jak
MMX czy SSE (jest po prostu za stary).
Posiada wiele opcji debugowania: trace-over, step-into, execute-to,
animate, ...
Nadaje się do plików .com/.exe (najnowsza wersja 5.5 obsługuje tylko pliki 32-bitowe).
Można go ściągnąć ze stron Borlanda po zarejestrowaniu się.
Darmowy obsługuje tylko procesory 16-bitowe (brak rozszerzonych rejestrów), ale można
podglądać rejestry, flagi, pamięć i koprocesor. D86 jest rozprowadzany razem z A86,
darmowym kompilatorem języka asembler, i rozpoznaje symbole (nazwy zmiennych itp.),
które A86 umieszcza w plikach .sym, co ułatwia proces debugowania.
Posiada pełną dokumentację. Pomoc w sprawie klawiszy jest w każdej chwili dostępna pod
kombinacją klawiszy Alt-F10.
Niestety, miałem problem z przeglądaniem aplikacji graficznej: po uruchomieniu trybu
graficznego nie mogłem wrócić do ekranu debuggera i musiałem poczekać, aż program się
zakończy. D86 zna instrukcje koprocesora.
Płatna wersja, D386, zna MMX, SSE i 3DNow!.
Moje doświadczenie z tym debuggerem jest krótkie, gdyż nie spełnił on wszystkich moich
oczekiwań. Po uruchomieniu od razu trzeba otworzyć jakiś program (i z tego co pamiętam,
aby otworzyć inny program, trzeba wyjść z całego debuggera. Te programy, które
chciałem otworzyć, CV otwierał tak długo, że można było pomyśleć, że komputer się
zawiesił...
Nawet chyba nie jest rozprowadzany osobno, tylko razem z
MASMem (za darmo).
Trzeba przejść długi proces instalacji, ustawiać zmienne środowiska, ...
Natrafiłem na niego, gdy szukałem czegoś (nowszego), co mogło by zastąpić Turbo
Debuggera. Wyglądem nawet przypomina TD, ale ma kilka wad. Pierwszą jest brak
rozpoznawania instrukcji koprocesora (wszystkie dekoduje jako ESC + coś tam). O MMX
nie ma co myśleć. Drugą wadą, którą znalazłem jest to, że po wejściu w tryb graficzny
okienko Tryb MS-DOS
z debuggerem przestaje się odświeżać i
trzeba się przełączyć na pełny ekran.
Ale jako-tako, działa. Posiada opcje step-over, trace-into, animate.
Można zmieniać wartości rejestrów.
Nawet ładne narzędzie. Pozwala w jednej chwili oglądać kod, stos, rejestry i 2 bloki pamięci (standardowo ustawiane na DS:0000). Obsługa jest prosta: na dole ekranu jest pokazane, co robią klawisze funkcyjne, ale można też wpisywać komendy. Bardzo pomocne jest to, że po wpisaniu pierwszej literki pojawiają się dostępne komendy zaczynające się od niej. Niestety, ma te dwa problemy, które ma Insight: po uruchomieniu trybu graficznego okienku z debuggerem przestaje być odświeżane (trzeba się przełączyć na pełny ekran) i nie rozpoznaje instrukcji koprocesora.
Umie debugować programy typu .com i .exe. Jednak coś jest nie tak z obsługą myszy a praca z nim nie jest zbyt wygodna. Strona domowa TRW: www.hktk.com/soft/soft_tools/trw_1.html
Debuggery programów dla Windows:
Część pakietu GoDevTools (www.godevtool.com).
Poza nim są m.in kompilator języka asembler oraz
resource compiler.
Wszystko to przydać się może przy pisaniu programów dla Windows.
Ja osobiście używam
FASMa,
ale moim debuggerem jest właśnie GoBug.
Ma miły dla oka
wygląd, zna rejestry
FPU, MMX.
Wyświetla kod programu, stan rejestrów, oraz stos
względem ESP oraz EBP. Obsługuje wieloprocesowość oraz symbole użyte w kodzie, o ile
znajdzie odpowiedni plik z nimi. Po przytrzymaniu prawego klawisza myszki na
instrukcji pojawiają się bajty zapisane szesnastkowo, które się na tę instrukcję
składają.
GoBug rozpoznaje uruchomienia procedur Windows-owych
z bibliotek DLL.
Dobre narzędzie.
Można go za darmo ściągnąć z jego strony domowej: ollydbg.de. Wygląda bardziej profesjonalnie
niż GoBug i
podobnie jak on, rozpoznaje uruchomienia procedur systemowych. Stos jest wyświetlany
tylko względem ESP. Wyświetla rejestry i flagi. Stara się łączyć umieszczanie parametrów
na stosie z uruchomieniem procedury, ale nie zawsze to wychodzi. Przewijając okienko z
kodem niektóre instrukcje mogą się nagle zmieniać. Obsługa jest według mnie trudniejsza.
Czcionka instrukcji jest mniejsza, co jeszcze bardziej utrudnia ich rozczytanie.
Bardziej nie wnikałem w jego obsługę.
W tej chwili bardziej polecam GoBug niż OllyDbg.
Wiem, że nie wszyscy od razu z entuzjazmem rzucą się do ściągania
i testowania przedstawionych wyżej programów i do debugowania własnych.
Niektórzy mogą uważać, że
debugger nie jest im potrzebny. Może i tak być, ale nie zawsze i nie u wszystkich. Czasem
(zwykle po długim sterczeniu przed ekranem) przychodzi chęć do użycia czegoś, co tak
bardzo może ułatwić nam wszystkim życie.
Pomyślcie, że gdyby nie było debuggerów, znajdowanie błędów w programie musielibyśmy pozostawić
naszej nie zawsze wyćwiczonej wyobraźni. Dlatego zachęcam Was do korzystania z programów tego
typu (tylko tych posiadanych legalnie, oczywiście).
Środowisko programowania (Integrated Development Environment, IDE) to, jak wszyscy wiemy, program, który umożliwia edycję kodu, jego kompilację i uruchamianie programu wynikowego. Znanych jest wiele IDE dla języków wysokiego poziomu, ale język asembler też ma kilka swoich:
Jeśli mimo tego ktoś nie chce lub nie lubi używać IDE, zawsze może skorzystać z któregoś ze zwykłych edytorów. Przedstawione poniżej propozycje to co prawda nie muszą być edytorami napisanymi specjalnie do programowania w asemblerze, ale może coś Wam przypadnie do gustu:
Jeśli nie podoba się Wam żaden z wymienionych, to możecie wejść na stronę The Free Country.com - edytory, gdzie przedstawionych jest wiele edytorów dla programistów.
Kolejną przydatną rzeczą może okazać się disasembler lub hex-edytor. Jest to program, który
podobnie jak debugger czyta plik i ewentualnie tłumaczy zawarte w nim bajty na instrukcje asemblera,
jednak bez możliwości uruchomienia czytanego programu.
Disasemblery mogą być przydatne w wielu sytuacjach, na przykład gdy chcemy modyfikować pojedyncze
bajty po kompilacji programu, zobaczyć adresy zmiennych, itp.
Oto kilka przykładów programów tego typu:
I ponownie, jeśli nie spodoba się Wam żaden z wymienionych, to możecie wejść na stronę The Free Country.com - disasemblery, aby poszukać wśród pokazanych tam programów czegoś dla siebie.
Programy typu MAKE (na przykład GNU MAKE) służą do automatyzacji budowania dużych
i małych projektów.
Taki program działa dość prosto: uruchamiamy go, a on szuka pliku o nazwie Makefile
w
bieżącym katalogu i wykonuje komendy w nim zawarte. Teraz zajmiemy się omówieniem podstaw
składni pliku Makefile
.
W pliku takim są zadania do wykonania. Nazwa zadania zaczyna się w pierwszej kolumnie, kończy
dwukropkiem. Po dwukropku są podane nazwy zadań (lub plików) , od wykonania których zależy wykonanie
tego zadania. W kolejnych wierszach są komendy służące do wykonania danego zadania.
UWAGA: komendy NIE MOGĄ
zaczynać się od pierwszej kolumny!
Należy je pisać je po jednym tabulatorze (ale nie wolno zamiast tabulatora stawiać ośmiu spacji).
Aby wykonać dane zadanie, wydajemy komendę make nazwa_zadania
.
Jeśli nie podamy nazwy zadania
(co jest często spotykane), wykonywane jest zadanie o nazwie all
(wszystko).
A teraz krótki przykład:
all: kompilacja linkowanie echo "Wszystko zakonczone pomyslnie" kompilacja: nasm -O999 -f obj -o plik1.obj plik1.asm nasm -O999 -f obj -o plik2.obj plik2.asm nasm -O999 -f obj -o plik3.obj plik3.asm tasm /z /m plik4.asm tasm /z /m plik5.asm tasm /z /m plik6.asm linkowanie: plik1.obj plik2.obj plik3.obj plik4.obj plik5.obj plik6.obj alink -o wynik.exe plik1.obj plik2.obj plik3.obj plik4.obj \ plik5.obj plik6.obj -c- -oEXE -m- help: echo "Wpisz make bez argumentow"
Ale MAKE jest mądrzejszy, niż może się to wydawać!
Mianowicie, jeśli stwierdzi, że wynik.exe został stworzony PÓŹNIEJ niż pliki .obj podane w
linii zależności, to nie wykona bloku linkowanie
, bo nie ma to sensu skoro program wynikowy
i tak jest aktualny. MAKE robi tylko to, co
trzeba. Oczywiście, niezależnie od wieku
plików .obj, dział kompilacja
i tak zostanie
wykonany (bo nie ma zależności, więc MAKE nie będzie sprawdzał wieku plików).
Znak odwrotnego ukośnika \
powoduje zrozumienie, że następna linia jest kontynuacją bieżącej,
znak krzyżyka #
powoduje traktowanie reszty linijki jako komentarza.
Jeśli w czasie wykonywanie któregokolwiek z poleceń w bloku wystąpi błąd (ściśle mówiąc, to gdy błąd zwróci wykonywane polecenie, jak u nas TASM czy NASM), to MAKE natychmiast przerywa działanie z informacją o błędzie i nie wykona żadnych dalszych poleceń (pamiętajcie więc o umieszczeniu w zmiennej środowiskowej PATH ścieżki do kompilatorów).
W powyższym pliku widać jeszcze jedno: zmiana nazwy któregoś z plików lub jakieś opcji sprawi,
że trzeba ją będzie zmieniać wielokrotnie, w wielu miejscach pliku. Bardzo niewygodne w
utrzymaniu, prawda?
Na szczęście z pomocą przychodzą nam ... zmienne, które możemy deklarować w Makefile
i które zrozumie program MAKE.
Składnia deklaracji zmiennej jest wyjątkowo prosta i wygląda tak:
NAZWA_ZMIENNEJ = wartosc
A użycie:
$(NAZWA_ZMIENNEJ)
Polecam nazwy zmiennych pisać wielkimi literami w celu odróżnienia ich od innych elementów. Pole wartości zmiennej może zawierać dowolny ciąg znaków.
Jeśli chcemy, aby treść polecenia NIE pojawiała się na ekranie, do nazwy tego polecenia
dopisujemy z przodu znak małpki @
, na przykład
@echo "Wszystko zakonczone pomyslnie"
Uzbrojeni w te informacje, przepisujemy nasz wcześniejszy Makefile
:
# Mój pierwszy Makefile NASM = nasm # ale można tu w przyszłości wpisać pełną ścieżkę NASM_OPCJE = -O999 -f obj TASM = tasm TASM_OPCJE = /z /m ALINK = alink ALINK_OPCJE = -c- -oEXE -m- PLIKI_OBJ = plik1.obj plik2.obj plik3.obj plik4.obj plik5.obj plik6.obj PROGRAM = wynik.exe all: kompilacja linkowanie @echo "Wszystko zakonczone pomyslnie" kompilacja: $(NASM) $(NASM_OPCJE) -o plik1.obj plik1.asm $(NASM) $(NASM_OPCJE) -o plik2.obj plik2.asm $(NASM) $(NASM_OPCJE) -o plik3.obj plik3.asm $(TASM) $(TASM_OPCJE) plik4.asm $(TASM) $(TASM_OPCJE) plik5.asm $(TASM) $(TASM_OPCJE) plik6.asm linkowanie: $(PLIKI_OBJ) $(ALINK) -o $(PROGRAM) $(PLIKI_OBJ) $(ALINK_OPCJE) help: @echo "Wpisz make bez argumentow"
Oczywiście, w końcowym Makefile
należy
napisać takie regułki, które pozwolą na ewentualną kompilację pojedynczych plików, na przykład
plik1.obj: plik1.asm plik1.inc $(NASM) $(NASM_OPCJE) -o plik1.obj plik1.asm
Choć na razie być może niepotrzebna, umiejętność pisania plików Makefile
może się przydać już
przy projektach zawierających tylko kilka modułów (bo nikt nigdy nie pamięta, które pliki są
aktualne, a które nie).
O tym, ile Makefile
może zaoszczędzić czasu przekonałem się sam, pisząc swoją bibliotekę -
kiedyś kompilowałem każdy moduł z osobna, teraz wydaję jedno jedyne polecenie make
i
wszystko się samo robi. Makefile
z biblioteki jest spakowany razem z nią i możecie go sobie
zobaczyć.