ven*_*s.w 6 x86 assembly kernel
我自己编写内核,在第一页错误中断处理程序之后,当执行IRET时,它会导致中断13(一般保护),错误代码为0x18.我不知道出了什么问题,堆栈上推送的内容来自cpu.
这是中断发生时的寄存器状态,以及存储寄存器的存储器.此外,IRET从页面错误中断处理程序返回.
确保在执行IRET和发生中断之前%ESP是相同的.
如果异常来自IRET
自身,则很可能IRET
无法恢复其中一个保存的段寄存器,但值(8或0x18,顺便说一下?)在某种程度上是错误的.这可能是错误的,因为您从未(重新)在受保护模式下初始化寄存器,或者您的处理程序在执行IRET
GDT 之前将其设置为错误值...
编辑:从图中可以看出,页面错误处理程序ESP
在执行之前没有删除异常代码(地址处的值为4 )IRET
.因此IRET
将4的新值解释为EIP
0x1000018的新值,CS
并将0x23作为新值EFLAGS
,而对于这三个寄存器应该使用0x1000018,0x23和0x3206.显然,无法加载数据段选择器(0x1000018被解释为截断为0x0018后)CS
,这会导致#GP(0x18).
扩展阿列克谢:
当某些中断发生时(而不是其他中断),它们会自动将一个 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
.
相关问题:在从中断处理程序返回之前,我是否必须弹出由某些异常推入堆栈的错误代码?