为什么来自页面错误处理程序的iret会产生中断13(一般保护错误)和错误代码0x18?

时间:2012-05-14 10:06:37

标签: assembly x86 kernel

我自己编写内核,在第一页错误中断处理程序之后,当执行IRET时,它会导致中断13(一般保护),错误代码为0x18。我不知道出了什么问题,堆栈上推送的内容来自cpu。

这是中断发生时的寄存器状态,以及存储寄存器的存储器。此外,IRET从页面错误中断处理程序返回。

确保在执行IRET和中断之前%ESP是相同的。

enter image description here

enter image description here

2 个答案:

答案 0 :(得分:7)

如果异常来自IRET本身,则很可能IRET无法恢复其中一个已保存的段寄存器,但值(8或0x18,顺便说一下?)在某种程度上是错误的。这可能是错误的,因为您从未(重新)在受保护模式下初始化寄存器,或者您的处理程序在执行IRET之前将其设置为错误值或GDT发生了某些事情...

编辑:从图片中可以看出,页面错误处理程序在执行ESP之前没有删除异常代码(IRET中地址的值为4)。因此,IRETEIP的新值解释为4,将{100}作为CS的新值,将0x23解释为EFLAGS的新值,而应使用0x1000018 ,这三个寄存器的0x23和0x3206。显然,数据段选择器(0x1000018被解释为截断为0x0018后)无法加载到CS,这会导致#GP(0x18)。

答案 1 :(得分:2)

扩展Alexey:

当某些中断发生时(但不会发生其他中断),它们会自动将4字节错误代码推送到堆栈。页面错误就是其中之一。

此错误代码包含有关中断的额外信息。

Intel Manual Volume 3 System Programming Guide - 325384-056US September 2015表6-1。 "保护模式异常和中断"栏"错误代码"告诉我们哪些中断推送错误代码,哪些没有。

38.9.2.2"页面错误代码"解释了错误的含义。

所以你需要:

pop %eax
/* Do something with %eax */
iret

或者如果您想忽略错误代码:

add $4, %esp
iret

有关最小示例,请参阅this page handler并尝试发表评论pop

将上述内容与不需要弹出堆栈的Division error exception进行比较。

请注意,如果只执行int $14,则不会推送额外的字节:这只发生在实际的异常上。

处理这个问题的一种巧妙方法是在堆栈上推送一个虚拟错误代码0,用于不执行此操作以使事情统一的中断。 James Molloy的教程does exactly that

Linux内核4.2似乎做了类似的事情。在arch/x86/entry/entry64.S下,它使用has_error_code

模拟中断
trace_idtentry page_fault do_page_fault has_error_code=1

然后在同一个文件上使用它:

.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif

has_error_code=0时执行推送。

相关问题:Do I have to pop the error code pushed to stack by certain exceptions before returning from the interrupt handler?