2015-02-07 75 views
0

我试图为x86实现一个boot-loader。初始版本只是使用BIOS中断调用在屏幕上打印“Hello”。我使用QEmu与GDB一起按顺序浏览我的代码。 下面是代码片段:x86中的中断描述符表

mov ah, 0x0e 
mov al, 'H' 
int 0x10 
mov al, 'e' 

的引导加载器地址0x07c00开始。 根据我的理解,BIOS设置从地址0x0到0x3ff(1024字节)的中断描述符表。 IDT有256个32位条目,每个条目指定16位段和16位偏移量,这是中断服务程序的地址。 因此,当我执行:

int 0x10 

我应该跳转到由IDT的第17项指向的地址。当我检查了内存为0x10的内容,它包含以下数据“0xf000ff53”,所以程序要跳转到该位置0xfff53但我发现它,而不是跳跃执行

int 0x10 

指令后0xc4c71 为什么这是怎么发生的?

回答

2

当我检查了内存为0x10

还有就是你的问题的内容。由于每个向量是4个字节,所以中断0x10的入口位于地址0x40

(gdb) x/a 0x40 
0x40: 0xc0004d65 
(gdb) p/x $cs 
$4 = 0xc000 
(gdb) p/x $eip 
$5 = 0x4d66 

我的qemu + gdb组合似乎跳过中断后的一个字节,这可能是一个错误。

但我们相信这跳过一个字节是一个bug

是。让我们来测试:

xor ax, ax 
mov ds, ax 
mov ax, [0x40] 
mov dx, [0x42] 
mov [old], ax 
mov [old+2], dx 
mov word [0x40], handler 
mov [0x42], cs 
mov ah, 0x0e 
mov al, 'H' 
int 0x10 
jmp $ 
handler: 
inc ax 
jmp far [old] 
old: dd 0 

此挂钩int 0x10我们handler,使用单字节指令(操作码0x40),这增加ax然后去到原来的处理程序。如果你运行这个,你会看到它打印I而不是H,所以inc ax正确执行。此外,还可以将断点的处理程序,并把它停在那里,然后再继续原来的处理程序:

Breakpoint 2, 0x00007c24 in ??() 
(gdb) x/a 0x7c29 
0x7c29: 0xc0004d65 
(gdb) si 
0x00007c25 in ??() 
(gdb) 
0x00004d65 in ??() 

注意,如果你单步,广发行将再次跳过第一个指令:

0x00007c20 in ??() 
(gdb) x/4i $eip 
=> 0x7c20:  int $0x10 
    0x7c22:  jmp 0x7c22 
    0x7c24:  inc %ax 
    0x7c25:  ljmp *0x7c29 
(gdb) p/x $ax 
$4 = 0xe48 
(gdb) si 
0x00007c25 in ??() 
(gdb) p/x $ax 
$5 = 0xe49 

你可以看到它去了0x7c25而不是0x7c24,但ax已经增加了,所以执行了inc ax

替换inc axadd ax, 1(这是一个3字节)指令的工作原理是一样的,所以gdb实际上是跳过第一条指令而不是一个字节。

+0

谢谢....但是我们确定跳过一个字节是一个错误 – sarthak 2015-02-07 14:24:37

+0

在执行int指令之前还有一件事,esp指向0x6f2c,它在执行后变为0x6f26。为什么ESP改变了? – sarthak 2015-02-08 07:27:50

+1

因为'int'将返回地址和标志放在堆栈上。这是16位实模式下的6个字节。 – Jester 2015-02-08 13:34:01