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

ven*_*s.w 6 x86 assembly kernel

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

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

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

在此输入图像描述

在此输入图像描述

Ale*_*nze 7

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

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


Cir*_*四事件 5

扩展阿列克谢:

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

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

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

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

所以你需要:

pop %eax
/* Do something with %eax */
iret
Run Code Online (Sandbox Code Playgroud)

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

add $4, %esp
iret
Run Code Online (Sandbox Code Playgroud)

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

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

请注意,如果您执行 simple 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
Run Code Online (Sandbox Code Playgroud)

然后在与以下文件相同的文件中使用它:

.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif
Run Code Online (Sandbox Code Playgroud)

has_error_code=0.

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