x86: цикл обработки прерываний - PullRequest
3 голосов
/ 14 февраля 2012

Я пытаюсь обработать прерывания ядра через IDT.Я работаю на Intel x86 под Linux.

Я установил IDT и мои записи прерываний, и я запустил несколько тестов для просмотра моих обработчиков прерываний.

Когда я пытаюсь int $0x0, он работает отлично: мой обработчик вызывается, но когда я пытаюсь выполнить какое-то исключение с нажатым кодом ошибки, я вхожу в бесконечный цикл.

Схема следующая:

Когда приходит исключение, первая частьмой обработчик находится в ASM и вызывает общую часть C.

my_handler.c

void handler(int i)
{
  printf("Exception %d caught\n", i);
}

my_handlers.S

common:
    pushal

    pushl %ds
    pushl %es
    pushl %fs
    pushl %gs

    addl $48, %esp                  // 4 4-bytes segments pushed
                                    // + 8 4-bytes registers (pushal)
`                                   // esp points on exception code

    call handler                    // call the C handler with exception code

    subl $48, %esp

    popl %gs
    popl %fs
    popl %es
    popl %ds

    popal

    addl $8, %esp                   // 4-byte error code + 4-byte exception number
    iret


exception_de_handler:
    pushl $0                        // Fake error code
    pushl $0                        // interrupt number
    jmp common

exception_gp_handler:
    // error code is pushed by µproc.
    pushl $13                       // interrupt number
    jmp common

exception_pf_handler:
    // error code is pushed by µproc.
    pushl $14                       // interrupt number
    jmp common

Если я пытаюсь запустить следующий код:

int* a = 0x0;
*a = 42;

Работает, исключение возобновляется после *a = 42;

Но если я попытаюсь:

int* a = 0x0;
*a = 42;
*a = 1337;

Идет в бесконечном цикле:

Exception 14 caught
Exception 13 caught
Exception 13 caught
Exception 13 caught
Exception 13 caught
        .....
Exception 13 caught
Exception 13 caught
Exception 13 caught
        .....

Почему обрабатывается ошибка первого исключения (14), а затем зацикливается на Общей защите (13)?

Спасибо за ваши ответы.

1 Ответ

3 голосов
/ 15 февраля 2012

Я думаю, вы испортили свой стек.Вы должны быть очень осторожны с тем, что вы делаете со своим стеком в обработчике прерываний.В этом случае кажется, что вы делаете следующее: -

Push-код ошибки (может быть выполнен CPU) Push-регистры Push-сегменты reg

добавляют 0x48 к указателю стека, чтобы свернуть стекобратный путь, чтобы он указывал на код ошибки.

вызовите вашу функцию C

В сущности, это «освобождение» части вашего стека, в которой хранятся регистры сегментов. Фактически вам даже не нужно беспокоиться офункция C вообще, потому что адрес возврата помещается в стек по команде вызова и сносит ваши записи о ds и es еще до того, как вы дойдете до C-вызова.Когда вы возвращаетесь из вызова C, вы пытаетесь привести в порядок стек вызовов, но вы не понимаете его совершенно правильно - отчасти потому, что вы уже испортили его, а отчасти потому, что вы не очищаете стек послевызов функции (при условии, что обработчик использует соглашение о вызовах _cdecl).

Это заставляет вас выдвигать поддельное значение для ds.Когда вы загружаете это в ds, процессор проверяет значение по GDT и обнаруживает, что оно недопустимо.В этот момент он поднимает GPF (исключение 13), который вы отправляете.Это в определенной степени восстанавливает стек (процессор присматривает за SS) и оставляет старое значение ds установленным, так что вы фактически никогда не меняете ds, что позволяет вам снова запускать printf.

Вы должны быть намного более осторожными при выравнивании стека, и всякий раз, когда вы добавляете к указателю стека, как вы делаете, вы должны учитывать, что данные, которые были в этом диапазоне, уходили навсегда, потому что либо следующий парень, либовозможно, неожиданное прерывание сбивает вас с толку.

...