2

几个月来,我一直在研究“自制”操作系统。目前,它启动并进入 32 位保护模式。我已经加载了中断表,但还没有设置分页(还)。

现在,在编写我的异常例程时,我注意到当一条指令引发异常时,异常例程会被执行,但随后 CPU 会跳回到引发异常的指令!这并不适用于所有异常(例如,除零异常将跳回到除法指令之后的指令),但让我们考虑以下一般保护异常:

MOV EAX, 0x8
MOV CS, EAX

我的例程很简单:它调用一个显示红色错误消息的函数。

结果:MOV CS,EAX 失败 -> 显示我的错误消息 -> CPU 跳回 MOV CS -> 无限循环发送错误消息。

我已经和一位操作系统和 unix 安全方面的老师讨论过这个问题。他告诉我他知道 Linux 有办法绕过它,但他不知道是哪一种。

天真的解决方案是从例程中解析抛出指令,以获得该指令的长度。该解决方案非常复杂,我觉得在每个受影响的异常例程中添加对相对繁重的函数的调用有点不舒服......

因此,我想知道这是否是解决问题的另一种方法。也许有一个“魔法”寄存器包含可以改变这种行为的位?

--

非常感谢您提前提供任何建议/信息。

--

编辑:似乎很多人想知道为什么我要跳过有问题的指令并恢复正常执行。

我有两个原因:

  1. 首先,杀死一个进程将是一种可能的解决方案,但不是一个干净的解决方案。这不是它在 Linux 中的做法,例如,(AFAIK)内核发送信号(我认为是 SIGSEGV)但不会立即中断执行。这是有道理的,因为应用程序可以阻止或忽略信号并恢复自己的执行。这是一种非常优雅的方式来告诉应用程序它做错了 IMO。

  2. 另一个原因:如果内核本身执行了非法操作怎么办?可能是由于错误,但也可能是由于内核扩展。正如我在评论中所说:在这种情况下我该怎么办?我应该杀死内核并显示一个带有笑脸的漂亮蓝屏吗?

这就是为什么我希望能够跳过指令。“猜测”指令大小显然不是一种选择,并且解析指令似乎相当复杂(我不介意实现这样的例程,但我需要确保没有更好的方法)。

4

4 回答 4

3

不同的异常有不同的原因。有些异常是正常的,异常只是告诉内核在允许软件继续运行之前需要做什么。这方面的例子包括一个页面错误告诉内核它需要从交换空间加载数据,一个未定义的指令异常告诉内核它需要模拟一条 CPU 不支持的指令,或者一个调试/断点异常告诉内核它需要通知调试器。对于这些,内核修复并默默地继续是正常的。

一些异常表示异常情况(例如软件崩溃)。处理这些类型的异常的唯一明智的方法是停止运行软件。您可以保存信息(例如核心转储)或显示信息(例如“蓝屏死机”)以帮助调试,但最终软件停止(进程终止,或者内核进入“什么都不做,直到用户重置计算机”状态)。

忽视异常情况只会让人们更难找出问题所在。例如,想象一下上厕所的指令:

  • 进入浴室
  • 脱裤子
  • 开始生成输出

现在想象第 2 步失败,因为您穿着短裤(“找不到裤子”例外)。您是想在这一点停下来(带有易于理解的错误消息或其他内容),还是忽略该步骤并在所有有用的诊断信息都消失后尝试找出问题所在?

于 2012-02-08T13:59:21.840 回答
2

如果我理解正确,您想跳过导致异常的指令(例如mov cs, eax)并在下一条指令处继续执行程序。

你为什么想做这个?通常,程序的其余部分不应该取决于该指令成功执行的效果吗?

一般来说,异常处理有以下三种方式:

  • 将异常视为不可修复的情况并终止该进程。例如,除以零通常以这种方式处理。

  • 修复环境,然后再次执行指令。例如,有时以这种方式处理页面错误。

  • 使用软件模拟指令并在指令流中跳过它。例如,有时以这种方式处理复杂的算术指令。

于 2012-02-05T18:15:41.700 回答
2

您所看到的是一般保护例外的特征。英特尔系统编程指南明确指出(6.15 异常和中断参考/中断 13 - 一般保护异常 (#GP)):

Saved Instruction Pointer
The saved contents of CS and EIP registers point to the instruction that generated the
exception.

因此,您需要编写一个异常处理程序来跳过该指令(这会有点奇怪),或者只是简单地使用“General Protection Exception at $SAVED_EIP”或类似消息杀死有问题的进程。

于 2012-02-05T18:32:45.950 回答
0

我可以想象一些情况,在这种情况下,人们希望通过解析失败的指令、模拟其操作,然后返回到指令来响应 GPF。正常的模式是进行设置,以使指令在重试时会成功,但可能有一些代码希望访问地址 0x000A0000-0x000AFFFF 的某些硬件,并希望在缺少此类硬件的机器上运行它. 在这种情况下,人们可能不想在那个空间中存储“真实”内存,因为每个访问都必须被单独捕获和处理。我不确定是否有任何方法可以处理这个问题,而不必解码任何试图访问该内存的指令,尽管我知道一些虚拟 PC 程序似乎可以很好地管理它。

否则,我建议您应该为每个线程设置一个跳转向量,当系统遇到 GPF 时应该使用该跳转向量。通常,该向量应该指向一个线程退出例程,但是将要使用指针执行“可疑”操作的代码可以将其设置为适合该代码的错误处理程序(代码应该在使用区域时取消设置向量错误处理程序本来是合适的)。

我可以想象一个人可能想模拟一条指令而不执行它的情况,以及一个人可能想将控制权转移到错误处理程序例程的情况,但我无法想象有任何人想简单地跳过一条指令会导致GPF。

于 2012-02-06T02:50:12.007 回答