代码之家  ›  专栏  ›  技术社区  ›  Joao da Silva

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

  •  6
  • Joao da Silva  · 技术社区  · 16 年前

    我已经加载了一个包含256个条目的IDT表,所有条目都指向类似的处理程序:

    • 对于异常8和10-14,推送异常编号(这些异常自动推送错误代码)
    • 对于其他的,推送“假”错误代码和异常编号;
    • 然后跳转到公共处理程序

    因此,当公共处理程序进入时,堆栈正确对齐,并包含异常/中断号、错误代码(可能只是一个伪代码)、EFLAGS、CS和EIP。

    我的问题是从中断处理程序返回。我用 iret 在从堆栈中取出异常号和错误代码后返回,但这对异常nr 8不起作用;如果我将错误代码留在堆栈中,那么它将返回fine!

    问题:

    • 对于将错误代码放在堆栈上的异常,是否必须将错误代码放在堆栈上?如果是这样,怎么办? 伊雷特 确定是否必须弹出错误代码?
    • 一旦我启用了中断,我总是会得到异常8(双故障),但随后一切都运行良好(我正在开发一个爱好操作系统)。这是正常的行为还是我在某个地方有一个bug?
    4 回复  |  直到 9 年前
        1
  •  13
  •   Nathan Fellman    16 年前

    如果CPU自动推送错误代码,则处理程序 必须 把它放在 iret . 这个 伊雷特 指令不知道你从哪里来,如果它是错误、陷阱或外部中断。它总是做同样的事情,并且假定堆栈上没有错误代码。

    引用SDM(软件开发人员手册)第3卷第5章第5.13节“错误代码:

    错误代码被推到堆栈上 作为双字或词(取决于 默认中断、陷阱或任务 浇口尺寸)。保持堆栈对齐 对于双字推,上半部分 保留错误代码的。注释 当 执行IRET指令的目的是 从异常处理程序返回,因此 处理程序必须删除错误代码 在执行返回之前。

    你可以找到 IA-32 Software Developer's Manual here : http://www.intel.com/products/processor/manuals/

    第3卷第1部分第5章描述了异常和中断处理。第2卷第1部分有 伊雷特 指令。

        2
  •  1
  •   QAZ    16 年前

    我写了一篇 small x86 OS 一会儿回来。看一下文件 isr.asm 在cvs存储库中。

    注意我们如何设置处理程序,大多数情况下会将一个虚拟的dword推到堆栈上,以说明自动推送错误代码的少数处理程序。然后,当我们通过IRET返回时,不管中断如何,我们都可以在堆栈上假设2个双字,并在IRET之前执行一个add esp,8,以很好地清理问题。

    这应该能回答你的第一个问题。

    至于第二个问题:当您启用中断时出现双故障,…hmmm可能是分页问题,如果您没有正确设置它。可能是另外一件事:)

        3
  •  1
  •   stalepretzel    10 年前

    一旦启用了中断,我就遇到了类似的“双重错误”问题。嗯,他们 就像双重错误,但它们确实是计时器中断!

    双故障是中断号 8 .

    不幸的是,默认的pic配置将计时器作为中断号中断。 (DEFAULT_PIC_BASE + TIMER_OFFSET) = (8 + 0) = .

    屏蔽掉所有的pic中断(直到我准备好正确配置pic)使这些类似于双故障的计时器中断静音。

    (pics要求CPU在产生下一个中断之前确认中断。因为您的代码没有确认初始计时器中断,所以图片再也没有给您任何信息!这就是为什么你只能得到一个,而不是一个人所期望的那么多。)

        4
  •  0
  •   Community CDub    7 年前

    对于将错误代码放在堆栈上的异常,是否必须将错误代码放在堆栈上?

    正如其他人提到的,您必须执行以下任一操作:

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

    或者如果要忽略错误代码:

    add $4, %esp
    iret
    

    如果你不这样做, iret 将错误代码解释为新的CS,您可能会遇到一般的保护故障,如下所述: Why does iret from a page fault handler generate interrupt 13 (general protection fault) and error code 0x18?

    Minimal Working this page handler 我是为了说明这一点而创造的。尝试评论 pop 看到它爆炸了。

    将上面的内容与 Division error exception 不会弹出堆栈。

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

    Intel Manual Volume 3 System Programming Guide - 325384-056US September 2015 表6-1。“保护模式异常和中断”列“错误代码”包含推送或不推送错误代码的中断列表。

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

    处理这一问题的一个好方法是推送一个伪错误代码 0 在堆栈上为不这样做的中断做统一的事情。詹姆斯·莫洛伊的教程 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 .