3

我正在尝试通过 IDT 处理内核中断。我在 Linux 下的 Intel x86 上工作。

我已经设置了我的 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
        .....

为什么处理第一个异常 Page Fault(14) 然后在 General Protection(13) 上循环?

谢谢您的回答。

4

1 回答 1

3

我认为你搞砸了你的堆栈。您需要非常小心地在中断处理程序中处理堆栈。在这种情况下,您似乎执行以下操作:-

推送错误代码(可以由 CPU 完成) Pus​​h regs Push Segment regs

将 0x48 添加到堆栈指针,以将堆栈一直向上缠绕,使其指向错误代码。

调用你的 C 函数

这实际上是在“释放”存储段寄存器的堆栈部分。事实上,您甚至根本不需要担心 C 函数,因为返回地址在call 指令并在您进行 C-call 之前就删除了您的 ds 和 es 记录。当您从 C 调用返回时,您尝试整理调用堆栈,但您并没有完全正确 - 部分原因是您已经把它搞砸了,部分原因是您在调用后没有清理堆栈函数调用(假设处理程序使用 _cdecl 调用约定)。

这会导致您弹出 ds 的虚假值。当您将其加载到 ds 时,CPU 会根据 GDT 检查该值,并发现它是无效的。此时,它会引发您正在查看的 GPF(异常 13)。这在一定程度上恢复了堆栈(CPU 正在为您处理 SS)并保留 ds 的旧值设置 - 因此您永远不会真正更改 ds,这允许您再次运行 printf。

您需要更加小心对齐堆栈,并且每当您添加到堆栈指针时,您都需要考虑该范围内的数据永远消失了,因为下一个人,或者可能是意外中断,是会绊倒你。

于 2012-02-14T23:01:50.710 回答