Asembler x86/Jak używać debuggera ALD: Różnice pomiędzy wersjami
Usunięta treść Dodana treść
m poprawa linków do podstron podręcznika Asembler x86 z powodu zmiany nazwy |
m rewert - błędny kod asemblera |
||
Linia 1:
==Wprowadzenie==
Debugger jest programem służącym do analizy kodu wykonywalnego. Pozwala zrozumieć to, co dzieje się w trakcie wykonywania skompilowanego programu jak również odnaleźć ewentualne błędy (z ang. ''bugs''). Wykorzystamy w tym celu program '''ald''' (''Assembly Language Debugger''). Jest to przykładowy debugger przeznaczony dla języka asembler. Pozwala on śledzić wykonywanie programu
==Instalacja==
Linia 38 ⟶ 37:
</source>
W przypadku, gdy nie posiadamy konta administratora, skompilowany plik wykonywalny '''ald''' znajdujemy w katalogu ''ald-x.y.z/source'', skąd możemy go skopiować do katalogu, z którego będziemy go wykonywać. W praktyce może to być ten sam katalog, w którym znajdują się pliki ''
<source lang=bash>
Linia 112 ⟶ 111:
Widzimy, że pierwsze wiersze przykładowego listingu w ostatniej kolumnie do złudzenia przypominają niedawno kompilowany kod źródłowy napisany w języku asembler. Zauważamy jedynie, że używane etykiety często zastąpione są już konkretnymi adresami liczbowymi w używanej pamięci. Lewa kolumna zawiera kolejno adresy pamięci zajmowanej przez dane polecenie. Środkowa kolumna zawiera polecenia w kodzie maszynowym. W tym momencie nie chcemy znać zawartości dalszej pamięci, więc naciskamy klawisz '''q''' a następnie '''ENTER'''.
==Wykonanie
Aby wykonać pierwszą instrukcję, naciskamy '''n''' po czym klawisz '''ENTER'''.
Linia 158 ⟶ 157:
ald>
eax = 0x00000001 ebx =
esp = 0xBFE4B130 ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds = 0x007B es = 0x007B fs = 0x0000 gs = 0x0000
Linia 166 ⟶ 165:
080480A0 CD80 int
ald>
Program terminated normally (Exit status:
ald>
</source>
Linia 175 ⟶ 174:
==Wstrzymanie wykonania==
W przypadku dłuższych i bardziej skomplikowanych algorytmów wykonywanie
Przykładowo poniższy program miał za zadanie wyświetlenie zawartości rejestru EAX w systemie dziesiętnym.
<source lang=asm>
;; decimalPrint.asm
;; Wypisuje zawartość EAX w systemie dziesiętnym
;;
segment .text
_start:
mov eax,0x0007
call _printEAXdecimal
int
segment .data
msg
segment .text
_printEAXdecimal:
N1:
div ebx
add edx,48
dec ecx
mov [msg+ecx],dl
jne N1
int
pop eax
pop ebx
pop ecx
pop edx
ret
</source>
Na pierwszy rzut oka wszystko wydaje się być w porządku. Kompilacja wraz z linkowaniem przebiegła pomyślnie. Próba wykonania programu daje jednak
<source lang=bash>
user@myhost:~$ ./decimalPrint
Floating point exception
user@myhost:~$
</source>
Program nie miał wykonywać żadnych operacji zmiennoprzecinkowych. Przypuszczamy jednak, że problem może mieć miejsce w wierszu, w którym wykonujemy operację dzielenia całkowitego:
<source lang=asm>
</source>
Chcielibyśmy zatem wstrzymać działanie programu tuż przed wykonaniem tej operacji, by dokładnie sprawdzić argumenty instrukcji oraz wynik jej działania. W tym celu uruchamiamy debugger poleceniem:
<source lang=
Assembly Language Debugger 0.1.7
Copyright (C) 2000-2004 Patrick Alken
decimalPrint: ELF Intel 80386 (32 bit), LSB - little endian, Executable, Version 1 (Current)
Loading debugging symbols...(11 symbols loaded)
ald>
</source>
Następnie przeprowadzamy deasemblację (klawisz '''d''' i '''ENTER''') w celu poznania adresu wskaźnika do interesującej na instrukcji:
<source lang=
ald> d
080480A6:<N1> F7F3 div ebx
080480AE 49 dec ecx
Hit <return> to continue, or <q> to quit
</source>
Naciskamy klawisz '''q''' oraz '''ENTER''', by nie wyświetlać dalszych obszarów pamięci. Szukany adres to ''0x080480A6''. Trzeba zatem pod wskazanym adresem ustawić pułapkę. Do tego celu służy polecenie '''break''':
<source lang=
ald> break 0x080480a6
ald>
</source>
Dla pewności możemy wyświetlić wszystkie zdefiniowane pułapki, aby mieć pewność, czy program nie będzie dodatkowo wstrzymany w żadnym innym miejscu. Do tego celu służy polecenie '''lbreak''':
<source lang=
ald> lbreak
ald>
</source>
Jeśli wszystko się zgadza, możemy uruchomić program poleceniem '''run''':
<source lang=
ald> run
ald>
</source>
Przede wszystkim interesują nas zawartości rejestrów EAX, EBX oraz EDX, gdyż tylko one są argumentami powyższej instrukcji. Dzielna znajduje się w rejestrach EDX:EAX, natomiast dzielnik umieszczony jest w
<source lang=
ald> n
eax = 0x00000000 ebx = 0x0000000A ecx = 0x0000000A edx = 0x00000007
esp = 0xBFC9251C ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds = 0x007B es = 0x007B fs = 0x0000 gs = 0x0000
ss = 0x007B cs = 0x0073 eip = 0x080480A8 eflags = 0x00000246
Flags: PF ZF IF
080480A8 81C230000000 add edx, 0x30
ald>
</source>
Dzielenie całkowite zostało wykonane poprawnie. Wynik dzielenia znajduje się w rejestrze EAX, natomiast reszta z dzielenia w rejestrze EDX. Być może w następnym przebiegu pętli ujawnią się jakieś problemy. Kontynuujemy zatem wykonanie programu poleceniem '''continue''' (lub w skrócie '''c'''):
<source lang=text>
ald> c
Breakpoint 1 encountered at 0x080480A6
eax = 0x00000000 ebx = 0x0000000A ecx = 0x00000009 edx = 0x00000037
esp = 0xBFC9251C ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds = 0x007B es = 0x007B fs = 0x0000 gs = 0x0000
ss = 0x007B cs = 0x0073 eip = 0x080480A6 eflags = 0x00000206
Flags: PF IF
080480A6:<N1> F7F3 div ebx
ald>
</source>
Na pierwszy rzut oka tym razem również wszystko jest w porządku. Jednak po wnikliwej analizie zawartości rejestrów zauważymy, że w rejestrze EDX znajdują się pozostałości z poprzedniego obiegu pętli. Aby uniknąć błędnych wyników, należy poprawić kod tak, aby zerowanie rejestru EDX przeprowadzać na początku każdego obiegu pętli przed wykonaniem operacji dzielenia. Spróbujemy mimo wszystko kontynuować działanie programu krok po kroku:
<source lang=
ald> n
Program received signal SIGFPE (Arithmetic exception)
Location: 0x080480A6
eax = 0x00000000 ebx = 0x0000000A ecx = 0x00000009 edx = 0x00000037
esp = 0xBFBA9ADC ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds = 0x007B es = 0x007B fs = 0x0000 gs = 0x0000
ss = 0x007B cs = 0x0073 eip = 0x080480A6 eflags = 0x00010206
Flags: PF IF RF
080480A6:<N1> F7F3 div ebx
ald>
</source>
Widzimy, że operacja dzielenia nie została wykonana i zgłoszony jest wyjątek za pomocą sygnału ''SIGFPE''. Kolejna próba wykonania operacji skończy się zamknięciem programu wraz ze zwróceniem kodu błędu:
<source lang=text>
ald> n
Program terminated with signal SIGFPE (Arithmetic exception)
ald>
</source>
Po wprowadzeniu stosownych poprawek prawidłowy kod źródłowy będzie miał postać:
<source lang=asm>
;;
;; Wypisuje zawartość EAX w systemie dziesiętnym
;;
segment .text
global _start
_start:
mov eax,0x0007
call _printEAXdecimal
mov ebx,5
int 0x80
segment .data
msg
segment .text
_printEAXdecimal:
N1:
div ebx
add edx,48
dec ecx
mov [msg+ecx],dl
jne N1
mov eax,4
mov ebx,1
mov ecx,msg
mov edx,10
int 0x80
ret
</source>
Linia 411 ⟶ 448:
<source lang=asm>
</source>
Linia 417 ⟶ 454:
<source lang=asm>
mov
</source>
Linia 430 ⟶ 467:
<noinclude>{{Nawigacja|Asembler X86|
[[Asembler
[[Asembler
}}</noinclude>
|