2014-06-30 26 views
5

Per scopi didattici, ho adattato questo bootloader da mikeos.berlios.de/write-your-own-os.html riscrivendolo per caricare specificamente all'indirizzo 0x7c00.Come ignorare le chiamate di interruzione durante il debug di un bootloader/bios con gdb e QEMU?

Il codice finale è questo:

[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 

posso scorrere il programma e visualizzare i registri cambiando, insieme con l'istruzione in esecuzione, passo con gdb (si) e controllo con monitor QEMU (informazioni registri , x/i $ eip, ecc.).

Dopo essere entrato in int 10h (la routine di stampa del BIOS), le cose si fanno un po 'strane. Se passo istruzioni 500 contemporaneamente, posso vedere il carattere "I" (il primo carattere della mia stringa di testo) stampato sullo schermo. Così ho riavviato di nuovo e fatto un passo di 400 passi (si 400) e poi ho fatto un passo alla volta per vedere in quale fase esatta "I" è stato stampato. Non è mai successo Ho effettivamente fatto 200 passi uno per uno e non è successo nulla. Non appena ho fatto 100 passi alla volta (si 100) ho nuovamente stampato "I" sullo schermo.

Quindi, mi chiedo se c'è un problema di temporizzazione (alcuni interrupt di sistema si intromettono mentre eseguo un debug passo dopo passo). Cos'altro potrebbe essere?

In ogni caso, c'è un modo per saltare l'intero interrupt del BIOS e altre funzioni e tornare indietro e continuare a modificare il codice del bootloader? Come suggerito da Peter Quiring nei commenti, ho provato ad usare il prossimo. Questo non ha funzionato.

(gdb) next 
Cannot find bounds of current function 

Così ho provato nexti e semplicemente si comporta come si.

Grazie!

+0

Forse l'aggiornamento della schermata qemu funziona in modo diverso se si è single stepping. – Jester

+0

Perché non utilizzare 'next' anziché 'step'ing nell'interrupt? 'next' consentirà l'interruzione per terminare e quindi interrompere l'app sulla linea dopo l'int 10h –

+0

Umm, non è il primo carattere della stringa di testo (come dato) ... 'I'? (non 'F'?), e il secondo char è 't', (non 'I'?) Per quanto riguarda il motivo per cui 500,400,200 ... passaggi nel BIOS danno risultati variabili, non ne ho idea. A meno che non siate curiosi morbosamente, salterò il debug delle routine del BIOS e mi godo il fatto che il vostro settore di avvio funzioni correttamente. Fai attenzione che le chiamate "int 10h" non corrompano il tuo registro "SI", forse un push/pop che circonda "int 10h"? – lornix

risposta

3

Questo è in realtà un lavoro che si adatta ai miei scopi. Quello che ho fatto è stato impostare i breakpoint in modo da poter utilizzare "continua" su gdb insieme a "si" e seguire il messaggio che viene stampato sullo schermo, un carattere alla volta. Ecco i passaggi.

Nella prima esecuzione, eseguo il bootloader, in modo da poter effettivamente controllare le posizioni di memoria in cui sono memorizzate le istruzioni.

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) 

Altro shell di Linux (alcune linee sono state soppressa [...]):

# 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 ??() 

Nel terminale sto facendo funzionare il monitor QEMU, trovo l'indirizzo delle istruzioni l'esecuzione di questo comando dopo ogni si on gdb:

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

per chi è nuovo a QEMU, xd isplay il contenuto di un registro,/lo traduco in un'istruzione e $ eip è il registro del punto di istruzione. Ripetendo questa procedura, scopro gli indirizzi per le istruzioni LODSB e int 10h:

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

Così, il gdb ho appena impostato i punti di interruzione per queste posizioni adizionale:

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

ora posso usare una combinazione di "continue" (c) e stepi (si) su gdb e saltare l'intera roba del BIOS.

Probabilmente esistono modi migliori per farlo. Tuttavia, per i miei scopi pedagogici, questo metodo funziona abbastanza bene.

2

ho automatizzato la vostra procedura con uno script Python che:

  • calcola la lunghezza dell'istruzione in corso
  • imposta un punto di interruzione temporanea sulla prossima istruzione
  • continua

Questo funzionerà anche per qualsiasi altra istruzione, ma non vedo molti altri casi d'uso per questo, dal momento che nexti salta già oltre 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() 

Basta inserire che in ~/.gdbinit.py e aggiungere source ~/.gdbinit.py al file ~/.gdbinit.

Testato su GDB 7.7.1, Ubuntu 14.04.