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

igsr9ssn  于 2022-11-13  发布在  其他
关注(0)|答案(3)|浏览(134)

我自己在写一个内核,在第一个页面错误中断处理程序之后,当执行IRET时,它引起了一个中断13(一般保护),错误代码是0x18,我不知道是哪里出了问题,推到堆栈上的内容来自cpu。
这里是中断发生时的寄存器状态,以及寄存器的存储器。此外,IRET是从页面错误中断处理程序返回的。
在执行IRET和发生中断之前,应确保%ESP相同。

yeotifhr

yeotifhr1#

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

编辑:从图中可以明显看出,页面错误处理程序没有删除异常代码因此,IRET将4解释为EIP的新值,将0x1000018解释为CS的新值,将0x23解释为EFLAGS的新值,显然,数据段选择器(0x1000018被解释为在截断为0x0018之后)不能被加载到CS中,并且这导致#GP(0x18)。

uwopmtnx

uwopmtnx2#

关于Alexey的详细信息:
当某些中断发生时(而不是其他中断),它们会自动将一个4字节的错误代码推到堆栈中,页面错误就是其中之一。
此错误代码包含有关中断的额外信息。
Intel Manual Volume 3 System Programming Guide - 325384-056US September 2015表6-1.“保护模式异常和中断”列“错误代码”告诉我们哪些中断会推送错误代码,哪些不会。
38.9.2.2 “Page Fault Error Codes”(页面错误错误代码)解释了错误的含义。
因此,您需要:

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时进行推送。
相关问题:Do I have to pop the error code pushed to stack by certain exceptions before returning from the interrupt handler?

wz1wpwve

wz1wpwve3#

我知道这是一个很老的问题,但为了完整起见,在这里加上我的答案。
根据您的汇编程序,您可能需要显式使用iretd(32位变量)而不是iret(16位)。如果您试图使用16位变量返回32位段,CPU将引发GP。

相关问题