2012-02-14 113 views
2

我想通过IDT处理内核中断。 我在Linux下使用Intel x86。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 

如果我尝试运行followig代码:

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

它的工作原理,在exceution的*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

为什么在Linux下手动操作IDT?那不可能很好地结束。您应该使用Linux的设备驱动程序API来处理中断。 (另外我不确定你如何从内核模式调用'printf' - 我们可能需要更多解释你实际做了什么。) – zwol 2012-02-14 23:06:03

+0

@Zack - 好点 – Stewart 2012-02-15 00:28:12

回答

2

我认为你搞砸了你的堆栈。你需要非常小心你在中断处理程序中对你的堆栈做了什么。在这种情况下,似乎你做到以下几点: -

推错误代码(可以由CPU来完成) 推的REG 推段的REG

添加0x48到堆栈指针,风堆栈一路备份,所以它指向错误代码。

调用你的C函数

这实际上做的是“解放”您的堆栈段寄存器分别存放在的部分是什么。事实上,你甚至不需要担心在C函数所有这些都是因为返回地址在呼叫指令中被压入堆栈,并在您进入C调用之前吹走了ds和es的记录。当你从C调回来时,你试着整理这个callstack,但是你没有把它弄清楚 - 部分原因是你已经搞砸了,部分原因是你没有清理过堆栈函数调用(假定处理程序使用_cdecl调用约定)。

这会导致您弹出ds的伪造值。当你把它加载到ds时,CPU检查GDT的值,发现它是无效的。在这一点上,它提出了一个你正在寻找的GPF(例外13)。这在一定程度上可以恢复堆栈(CPU正在为你照顾SS)并且保留ds的旧值 - 所以你永远不会改变ds,这允许你再次运行printf。

您需要更加小心地调整堆栈,并且每当添加到堆栈指针时,都需要考虑在该范围内的数据永远消失,因为下一个人或者可能是意想不到的打断,会打扰你。