8

我自己写了一个内核,在第一个页面错误中断处理程序之后,当IRET执行时,它会导致一个中断13(一般保护),错误代码是0x18。不知道怎么回事,压栈的内容来自cpu。

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

在 IRET 执行和中断发生之前,可以确定 %ESP 是相同的。

在此处输入图像描述

在此处输入图像描述

4

2 回答 2

9

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

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

于 2012-05-14T14:39:43.607 回答
5

扩展 Alexey:

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

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

英特尔手册第 3 卷系统编程指南 - 325384-056US 2015 年 9 月表 6-1。“保护模式异常和中断”列的“错误代码”准确地告诉我们哪些中断会推送错误代码,哪些不会。

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

因此,您将需要:

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

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

add $4, %esp
iret

有关最小示例,请参阅此页面处理程序并尝试注释掉pop.

将上述情况与不需要出栈的除法错误异常进行比较。

请注意,如果您只是这样做int $14,则不会推送额外的字节:这只发生在实际异常中。

解决这个问题的一种巧妙方法是在堆栈上推送一个虚拟错误代码0,用于不这样做的中断以使事情统一。James Molloy 的教程正是这样做的

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

相关问题:在从中断处理程序返回之前,我是否必须弹出某些异常推送到堆栈的错误代码?

于 2015-10-28T17:46:40.113 回答