Reputation: 794
I am trying to implement a boot-loader for x86. The initial version just uses the BIOS interrupt call to print "Hello" on the screen. I am using QEmu along with GDB for going through my code sequentially. Here is a snippet of the code:
mov ah, 0x0e
mov al, 'H'
int 0x10
mov al, 'e'
The boot-loader starts from address 0x07c00. From what I understood, the BIOS sets up the Interrupt Descriptor table from address 0x0 till 0x3ff (1024 bytes). The IDT has 256 32bit entries, each entry specifies 16bit segment and 16bit offset which is the address of the Interrupt service routine. Thus , when I execute:
int 0x10
I should jump to the address pointed by the 17th entry in the IDT. When I checked the contents of the memory 0x10, it contained the following data " 0xf000ff53", so the program should jump to the location 0xfff53 but I found that it instead jumps to 0xc4c71 after executing the
int 0x10
instruction Why is this happening??
Upvotes: 1
Views: 1639
Reputation: 58762
When I checked the contents of the memory 0x10
There is your problem. Since each vector is 4 bytes, the entry for interrupt 0x10
is at address 0x40
.
(gdb) x/a 0x40
0x40: 0xc0004d65
(gdb) p/x $cs
$4 = 0xc000
(gdb) p/x $eip
$5 = 0x4d66
My qemu+gdb combination seems to skip a byte after the interrupt, that is probably a bug.
but are we sure this skipping a byte is a bug
Yes. Let's test:
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
This hooks int 0x10
to our handler
, which increments ax
using a single byte instruction (opcode 0x40
) then goes to the original handler. If you run this, you will see it prints I
instead of H
, so the inc ax
executed correctly. Also, you can put a breakpoint on the handler and see it stop there, then continue to the original handler:
Breakpoint 2, 0x00007c24 in ?? ()
(gdb) x/a 0x7c29
0x7c29: 0xc0004d65
(gdb) si
0x00007c25 in ?? ()
(gdb)
0x00004d65 in ?? ()
Note that if you single step, gdb will again skip the first instruction:
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
You can see it went to 0x7c25
instead of 0x7c24
, but ax
has been incremented so the inc ax
executed.
Replacing inc ax
with add ax, 1
(which is a 3 byte) instruction works the same, so gdb
is really skipping the first instruction not just a byte.
Upvotes: 4