2014-06-30 9 views
5

W celach edukacyjnych zaadaptowałem ten program ładujący z mikeos.berlios.de/write-your-own-os.html, przepisując go tak, aby ładował się pod adresem 0x7c00.Jak przerwać przerywanie wywołań podczas debugowania bootloadera/biosu za pomocą gdb i QEMU?

Ostateczny kod jest taka:

[BITS 16]   ; Tells nasm to build 16 bits code 
[ORG 0x7C00]  ; The address the code will start 

start: 
    mov ax, 0  ; Reserves 4Kbytes after the bootloader 
    add ax, 288 ; (4096 + 512)/ 16 bytes per paragraph 
    mov ss, ax 
    mov sp, 4096 
mov ax, 0 ; Sets the data segment 
    mov ds, ax 
    mov si, texto ; Sets the text position 
    call imprime ; Calls the printing routine 
jmp $  ; Infinite loop 
    texto db 'It works! :-D', 0 
imprime:   ; Prints the text on screen 
    mov ah, 0Eh  ; int 10h - printing function 
.repeat: 
    lodsb   ; Grabs one char 
    cmp al, 0 
    je .done  ; If char is zero, ends 
    int 10h   ; Else prints char 
jmp .repeat 
.done: 
ret 
times 510-($-$$) db 0 ; Fills the remaining boot sector with 0s 
dw 0xAA55    ; Standard boot signature 

mogę przejść przez program i zobaczyć rejestry zmienia, wraz z instrukcją aktualnie wykonywany, wychodząc z gdb (SI) i inspekcji z QEMU monitor (Informacje rejestry , x/i $ eip, itp.).

Po tym, jak dostaję się do 10h (procedura drukowania BIOS), sprawy stają się trochę dziwne. Jeśli zrobię krok 500 instrukcji naraz, na ekranie pojawi się znak "I" (pierwszy znak z mojego ciągu tekstowego). Ponownie uruchomiłem ponownie i wprowadziłem 400 kroków (si 400), a następnie zrobiłem krok po kroku, aby zobaczyć, w którym dokładnie kroku "I" został wydrukowany. To nigdy się nie stało. Właściwie zrobiłem 200 kroków jeden po drugim i nic się nie stało. Jak tylko podniosłem 100 kroków naraz (si 100), znowu "I" wydrukowałem na ekranie.

Zastanawiam się, czy istnieje problem z synchronizacją (niektóre zakłócenia systemowe stają na przeszkodzie podczas wykonywania krok po kroku debugowania). Cóż innego mogłoby to być?

Czy istnieje sposób na pominięcie całego przerwania BIOS i innych funkcji i po prostu wróć i kontynuuj wprowadzanie kodu bootloadera? Jak zasugerował Peter Quiring w komentarzach, spróbowałem użyć next. To nie zadziałało.

(gdb) next 
Cannot find bounds of current function 

Więc spróbowałem nexti i po prostu zachowuje się jak si.

Dzięki!

+0

Może aktualizacja ekranu qemu działa inaczej, jeśli wykonujesz pojedyncze czynności. – Jester

+0

Dlaczego nie używać "następnego" zamiast "stepować" do przerwania? "Dalej" pozwoli przerwać, aby zakończyć, a następnie zatrzymać aplikację na linii po int 10h –

+0

Umm, nie jest pierwszym znakiem ciągu tekstowego (jak podano) ... "ja"? (nie "F"?), a drugi znak to "t", (nie "I"?) Dlaczego 500 40000 ... kroków do BIOS daje różne wyniki, nie mam pojęcia. Jeśli nie jesteście chorobliwie ciekawi, pominęłbym debugowanie procedur BIOS i cieszę się, że twój sektor rozruchowy działa poprawnie. Uważaj, że wywołania 'int 10h' nie psują twojego rejestru' SI', być może push/pop otaczającego 'int 10h'? – lornix

Odpowiedz

3

To jest rzeczywiście praca, która pasuje do moich celów. To, co zrobiłem, to ustawianie punktów przerwania, więc mogę używać "kontynuuj" na gdb razem z "si" i śledzić komunikat drukowany na ekranie, po jednym znaku na raz. Oto kroki.

W pierwszym uruchomieniu robię krok do bootloadera, więc mogę sprawdzić pozycje pamięci, w których przechowywane są instrukcje.

Linux shell:

# qemu-system-i386 -fda loader.img -boot a -s -S -monitor stdio 
QEMU 1.5.0 monitor - type 'help' for more information 
(qemu) 

Inne powłoki Linux (niektóre linie zostały stłumione [...]):

# gdb 
GNU gdb (GDB) 7.6.1-ubuntu 
[...] 
(gdb) target remote localhost:1234 
Remote debugging using localhost:1234 
0x0000fff0 in ??() 
(gdb) set architecture i8086 
[...] 
(gdb) br *0x7c00 
Ponto de parada 1 at 0x7c00 
(gdb) c 
Continuando. 
Breakpoint 1, 0x00007c00 in ??() 
(gdb) si 
0x00007c03 in ??() 

W terminalu Używam monitora QEMU, znajdę adres instrukcji wykonujących to polecenie po każdym si gdb:

(qemu) x /i $eip 
0x00007c03: add $0x120,%ax 

Dla nowych do QEMU, xd przegląda zawartość rejestru,/i tłumaczy ją na instrukcję, a $ eip jest rejestrem punktów instrukcji. Powtarzając kroki, dowiem się adresy do instrukcji lodsb i int 10h:

0x00007c29: lods %ds:(%si),%al 
0x00007c2e: int $0x10 

Więc na gdb po prostu ustawić punkty przerwania tych aditional pozycjach:

(gdb) br *0x7c29 
Ponto de parada 2 at 0x7c29 
(gdb) br *0x7c2e 
Ponto de parada 3 at 0x7c2e 

Teraz mogę używać połączenie "kontynuuj" (c) i stepi (si) na gdb i pomiń cały system BIOS.

Prawdopodobnie istnieją lepsze sposoby na zrobienie tego. Jednak dla moich celów pedagogicznych ta metoda działa całkiem dobrze.

2

Mam zautomatyzowane swoją procedurę z skrypt Pythona, że:

  • oblicza długość bieżącej instrukcji
  • ustala tymczasowy przerwania na następnej instrukcji
  • kontynuuje

Będzie to również działać dla każdej innej instrukcji, ale nie widzę dla niej wielu innych przypadków użycia, ponieważ nexti przeskakuje już ponad call.

class NextInstructionAddress(gdb.Command): 
    """ 
Run until Next Instruction address. 

Usage: nia 

Put a temporary breakpoint at the address of the next instruction, and continue. 

Useful to step over int interrupts. 

See also: http://stackoverflow.com/questions/24491516/how-to-step-over-interrupt-calls-when-debugging-a-bootloader-bios-with-gdb-and-q 
""" 
    def __init__(self): 
     super().__init__(
      'nia', 
      gdb.COMMAND_BREAKPOINTS, 
      gdb.COMPLETE_NONE, 
      False 
     ) 
    def invoke(self, arg, from_tty): 
     frame = gdb.selected_frame() 
     arch = frame.architecture() 
     pc = gdb.selected_frame().pc() 
     length = arch.disassemble(pc)[0]['length'] 
     gdb.Breakpoint('*' + str(pc + length), temporary = True) 
     gdb.execute('continue') 
NextInstructionAddress() 

Wystarczy wrzucić to do ~/.gdbinit.py i dodać source ~/.gdbinit.py do pliku ~/.gdbinit.

Testowany na GDB 7.7.1, Ubuntu 14.04.

Powiązane problemy