x86:中断处理程序循环

时间:2012-02-14 11:37:14

标签: linux x86 kernel interrupt att

我正在尝试通过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)上循环?

感谢您的回答。

1 个答案:

答案 0 :(得分:3)

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

推送错误代码(可由CPU完成) 推送注册 推送段注册

将0x48添加到堆栈指针,将堆栈一直卷回,以便指向错误代码。

调用你的C函数

这实际上做的是“释放”存储段寄存器的堆栈部分。事实上,您根本不需要担心C函数,因为返回地址被推送到在调用指令处堆叠并在您进入C调用之前消除ds和es的记录。当你从C调用回来时,你试图整理callstack,但是你没有把它弄得很正确 - 部分是因为你已经把它搞砸了,部分是因为你没有清理堆栈之后函数调用(假设处理程序使用_cdecl调用约定)。

这会导致您弹出ds的虚假值。当您将其加载到ds时,CPU会根据GDT检查该值,并发现它无效。此时它会引发你正在观看的GPF(例外13)。那,在某种程度上,恢复堆栈(CPU正在为你寻找SS)并保留ds set的旧值 - 所以你永远不会实际更改ds,这允许你再次运行printf。

你需要更加小心地对齐堆栈,每当你像往常一样添加到堆栈指针时,你需要考虑该范围内的数据永远消失,因为下一个人,或者可能是意外的中断,会打扰你。