Ces*_*rod 5 assembly gdb qemu bootloader
出于教育目的,我已经从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
Run Code Online (Sandbox Code Playgroud)
我可以逐步完成程序,看看寄存器是否正在改变,以及正在执行的指令,使用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
Run Code Online (Sandbox Code Playgroud)
所以我尝试了nexti,它就像si一样.
谢谢!
我已经使用 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()
Run Code Online (Sandbox Code Playgroud)
只需将其放入~/.gdbinit.py并添加source ~/.gdbinit.py到您的~/.gdbinit文件中即可。
在 GDB 7.7.1、Ubuntu 14.04 上测试。
这实际上是一种适合我的目的的解决方法。我所做的是设置断点,这样我就可以在 gdb 上使用“继续”和“si”,并按照屏幕上打印的消息进行操作,一次一个字符。以下是步骤。
在第一次运行中,我会执行引导加载程序,因此我实际上可以检查存储指令的内存位置。
Linux 外壳:
# qemu-system-i386 -fda loader.img -boot a -s -S -monitor stdio
QEMU 1.5.0 monitor - type 'help' for more information
(qemu)
Run Code Online (Sandbox Code Playgroud)
其他 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 ?? ()
Run Code Online (Sandbox Code Playgroud)
在我运行 QEMU 监视器的终端中,我在 gdb 上的每个 si 之后找到执行此命令的指令的地址:
(qemu) x /i $eip
0x00007c03: add $0x120,%ax
Run Code Online (Sandbox Code Playgroud)
对于 QEMU 新手来说,x 显示寄存器的内容,/i 将其转换为指令,$eip 是指令点寄存器。通过重复这些步骤,我找到了 lodsb 和 int 10h 指令的地址:
0x00007c29: lods %ds:(%si),%al
0x00007c2e: int $0x10
Run Code Online (Sandbox Code Playgroud)
因此,在 gdb 上我只是为这些附加位置设置断点:
(gdb) br *0x7c29
Ponto de parada 2 at 0x7c29
(gdb) br *0x7c2e
Ponto de parada 3 at 0x7c2e
Run Code Online (Sandbox Code Playgroud)
现在我可以在 gdb 上使用“继续”(c) 和 stepi (si) 的组合,并跳过整个 BIOS 内容。
可能有更好的方法来做到这一点。然而,就我的教学目的而言,这种方法效果很好。