2014-06-30 8 views
5

Für Bildungszwecke habe ich diesen Bootloader von mikeos.berlios.de/write-your-own-os.html umgeschrieben, um es speziell an Adresse 0x7c00 zu laden.Wie geht man beim Debuggen eines Bootloaders/BIOS mit gdb und QEMU über Interrupt-Aufrufe?

Der letzte Code ist dies:

[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 

ich durch das Programm Schritt und sehen, die Register zu ändern, zusammen mit dem Befehl ausgeführt wird, mit gdb Schritt (si) und Inspektion mit QEMU überwachen (info Register , x/i $ eip, usw.).

Nachdem ich in Int 10h (die BIOS-Druckroutine) komme, werden die Dinge ein bisschen seltsam. Wenn ich 500 Anweisungen auf einmal mache, kann ich das Zeichen "I" (das erste Zeichen meiner Zeichenkette) auf dem Bildschirm sehen. Also habe ich wieder angefangen und bin 400 Schritte gegangen (si 400) und dann habe ich einen Schritt nach dem anderen gemacht, um zu sehen, in welchem ​​genauen Schritt "I" gedruckt wurde. Es ist nie passiert. Ich ging tatsächlich 200 Schritte nacheinander und nichts passierte. Sobald ich 100 Schritte gleichzeitig gegangen bin (si 100), habe ich wieder "Ich" auf den Bildschirm gedruckt.

Also, ich frage mich, ob es ein Timing-Problem (einige System-Interrupt kommt in die Quere, wie ich ein Debugging Schritt für Schritt). Was könnte das noch sein?

Wie auch immer, gibt es eine Möglichkeit, den gesamten BIOS-Interrupt und andere Funktionen zu überspringen und einfach den Bootloader-Code zu ändern? Wie von Peter Quiring in den Kommentaren vorgeschlagen, habe ich versucht, nächste. Das hat nicht funktioniert.

(gdb) next 
Cannot find bounds of current function 

Also versuchte ich nexti und es verhält sich einfach wie si.

Danke!

+0

Vielleicht funktioniert die qemu Bildschirmaktualisierung anders, wenn Sie Einzelschritt sind. – Jester

+0

Warum nicht 'next' statt 'step'ing in den Interrupt verwenden? 'next' erlaubt den Interrupt zu beenden und dann die App an der Zeile nach dem Int 10h –

+0

Umm, ist nicht das erste Zeichen Ihrer Textzeichenfolge (wie angegeben) ... 'I'? (nicht 'F'?), und zweiten Char ist 't', (nicht 'I'?) Warum 500,400,200 ... Schritte in das BIOS gibt unterschiedliche Ergebnisse, habe keine Ahnung. Sofern Sie nicht neugierig sind, würde ich das Debuggen der BIOS-Routinen überspringen und die Tatsache genießen, dass Ihr Boot-Sektor ordnungsgemäß läuft. Seien Sie vorsichtig, dass die 'int 10h'-Aufrufe Ihr' SI'-Register nicht beschädigen, vielleicht ein Push/Pop, der den 'int 10h' umgibt? – lornix

Antwort

3

Dies ist eigentlich eine Arbeit, die meine Zwecke erfüllt. Was ich getan habe, war das Setzen von Breakpoints, so dass ich "continue" auf gdb zusammen mit "si" verwenden und der Nachricht, die auf dem Bildschirm gedruckt wird, jeweils ein Zeichen folgen kann. Hier sind die Schritte.

Im ersten Durchlauf stufe ich meinen Bootloader, damit ich die Speicherpositionen überprüfen kann, wo die Anweisungen gespeichert sind.

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) 

Andere Linux-Shell (einige Zeilen unterdrückt wurden [...]):

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

Im Terminal I QEMU-Monitor leite, finde ich die Adresse der Anweisungen diesen Befehl nach jeder si auf gDB Ausführung:

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

Für neue diejenigen zu QEMU, xd Isplay den Inhalt eines Registers,/i übersetzt es in eine Anweisung und $ eip ist das Befehlspunktregister. Durch Wiederholen dieser Schritte, finde ich die Adressen für die lodsb und int 10h Anweisungen aus:

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

Also, auf GDB Ich habe nur die Grenzwerte für diese aditional Positionen:

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

Jetzt kann ich eine Kombination aus "continue" (c) und stepi (si) auf gdb und überspringe das gesamte BIOS-Zeug.

Es gibt wahrscheinlich bessere Möglichkeiten, dies zu tun. Für meine pädagogischen Zwecke funktioniert diese Methode jedoch sehr gut.

2

Ich habe Ihre Prozedur mit einem Python-Skript automatisiert, dass:

  • die Länge des aktuellen Befehls auf den nächsten Befehl
  • setzt eine temporäre Unterbrechungs
  • weiter

berechnet Dies funktioniert auch für jede andere Anweisung, aber ich sehe nicht viele andere Anwendungsfälle dafür, da nexti bereits über call springt.

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

Einfach fallen Sie, dass in ~/.gdbinit.py und fügen source ~/.gdbinit.py zu Ihrer ~/.gdbinit Datei.

Getestet auf GDB 7.7.1, Ubuntu 14.04.

Verwandte Themen