2015-02-07 3 views
0

Я пытаюсь реализовать загрузчик для x86. Первоначальная версия просто использует вызов прерывания 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 

я должен перейти к адресу, на который указывает 17 записи в IDT. Когда я проверил содержимое памяти 0x10, в нем содержатся следующие данные «0xf000ff53», поэтому программа должна перейти на место 0xfff53, но я обнаружил, что вместо того, чтобы переход к 0xc4c71 после выполнения команды

int 0x10 

Почему это происходит ???

ответ

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 кажется, чтобы пропустить байт после прерывания, что, вероятно, является ошибкой.

, но мы уверены, что это пропуск байт является ошибка

Да. Давайте тест:

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, который увеличивает ax с помощью одной команды байт (опкода 0x40), а затем переходит к первоначальному обработчику. Если вы запустите это, вы увидите, что он печатает I вместо H, поэтому inc ax выполнен правильно. Кроме того, вы можете поставить точку останова на обработчик и увидеть его останавливаться на достигнутом, а затем переходите к первоначальному обработчику:

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

Обратите внимание, что если вы один шаг, GDB будет снова пропустить первую команду:

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 ax с помощью команды add ax, 1 (которая представляет собой 3 байта) работает так же, поэтому gdb действительно пропускает первую инструкцию не только байт.

+0

Спасибо ....но мы уверены, что это пропущение байта - ошибка – sarthak

+0

еще одна вещь перед выполнением команды int esp указывала на 0x6f2c, которая после ее выполнения изменила на 0x6f26. Почему esp изменился? – sarthak

+1

Потому что 'int' помещает обратный адрес и флаги в стек. Это 6 байтов в реальном режиме 16 бит. – Jester

Смежные вопросы