出于教育目的,我已经从mikeos.berlios.de/write-your-own-os.html改编了这个引导加载程序,将其重写为专门加载地址0x7c00。
最终的代码是:
[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
我可以逐步完成程序,看看寄存器是否正在改变,以及正在执行的指令,使用gdb(si)进行步进并使用QEMU监视器进行检查(信息寄存器,x / i $ eip等)。
进入int 10h(BIOS打印例程)后,事情变得有些奇怪。如果我立刻执行500条指令,我可以在屏幕上看到字符“I”(我的文本字符串的第一个字符)。所以我再次重新启动并迈出了400步(si 400),然后我一步一步地看到“我”打印的确切步骤。它从未发生过。我实际上一个接一个地走了200步,什么都没发生。我一踏上100步(si 100),我又在屏幕上打印了“我”。
所以,我想知道是否存在时序问题(一些系统中断会妨碍我逐步调试)。还有什么可以呢?
无论如何,有没有办法跳过整个BIOS中断和其他功能,只需返回并继续步进引导加载程序代码?正如Peter Quiring在评论中所建议的,我尝试使用下一个。这没用。
(gdb) next
Cannot find bounds of current function
所以我尝试了nexti,它就像si一样。
谢谢!
答案 0 :(得分:3)
这实际上是一个符合我目的的工作。我所做的是设置断点,以便我可以使用"继续"在gdb和" si"并按照屏幕上打印的信息,一次一个字符。以下是步骤。
在第一次运行中,我会执行引导加载程序,因此我可以实际检查存储指令的内存位置。
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)
其他Linux shell(某些行被压制[...]):
# 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 ?? ()
在终端我正在运行QEMU监视器,我在gdb上的每个si之后找到执行此命令的指令的地址:
(qemu) x /i $eip
0x00007c03: add $0x120,%ax
对于QEMU的新用户,x显示寄存器的内容,/ i将其转换为指令,$ eip是指令点寄存器。通过重复这些步骤,我找到了lodsb和int 10h指令的地址:
0x00007c29: lods %ds:(%si),%al
0x00007c2e: int $0x10
所以,在gdb上我只是设置了这些附加位置的断点:
(gdb) br *0x7c29
Ponto de parada 2 at 0x7c29
(gdb) br *0x7c2e
Ponto de parada 3 at 0x7c2e
现在我可以结合使用"继续" (c)和gdb上的stepi(si)并跳过整个BIOS的东西。
可能有更好的方法来做到这一点。但是,出于我的教学目的,这种方法效果很好。
答案 1 :(得分:2)
我使用以下Python脚本自动执行您的程序:
这也适用于任何其他说明,但我没有看到很多其他用例,因为nexti
已跳过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()
将其放入~/.gdbinit.py
并将source ~/.gdbinit.py
添加到~/.gdbinit
文件中。
在GDB 7.7.1,Ubuntu 14.04上测试。