几个月来,我一直在研究“自制”操作系统。 目前,它启动并进入32位保护模式。 我已经加载了中断表,但还没有设置分页。
现在在编写我的异常例程时,我注意到当一条指令抛出一个异常时,执行异常例程,然后CPU跳回到抛出异常的指令!这不适用于每个异常(例如,div by zero异常将跳转到除法指令之后的指令),但让我们考虑以下一般保护异常:
MOV EAX, 0x8
MOV CS, EAX
我的例程很简单:它调用一个显示红色错误消息的函数。
结果:MOV CS,EAX失败 - >显示我的错误消息 - > CPU跳回MOV CS - >无限循环垃圾邮件错误信息。
我在操作系统和unix安全性方面与老师讨论过这个问题。 他告诉我他知道Linux有办法,但他不知道哪一个。
天真的解决方案是从例程中解析抛出指令,以获得该指令的长度。 这个解决方案非常复杂,在每个受影响的异常例程中添加对相对较重的函数的调用感觉有点不舒服......
因此,我想知道这是否是解决问题的另一种方式。也许有一个“神奇”的寄存器,其中包含一些可以改变这种行为的东西?
-
非常感谢您提出任何建议/信息。
-
编辑:似乎很多人想知道为什么我要跳过有问题的指令并恢复正常执行。
我有两个原因:
首先,杀死一个进程可能是一个可能的解决方案,但不是一个干净的解决方案。这不是它在Linux中的表现,例如,(AFAIK)内核发送信号(我认为是SIGSEGV)但不会立即中断执行。这是有道理的,因为应用程序可以阻止或忽略信号并恢复自己的执行。告诉应用程序它做错了IMO是一种非常优雅的方式。
另一个原因:如果内核本身执行非法操作怎么办?可能是由于一个错误,但也可能是由于内核扩展。正如我在评论中所说:在这种情况下我该怎么做?我应该杀掉内核并用笑脸显示一个漂亮的蓝屏吗?
这就是为什么我希望能够跳过指令。 “猜测”指令大小显然不是一个选项,解析指令似乎相当复杂(不是我介意实现这样的例程,但我需要确保没有更好的方法)。
答案 0 :(得分:3)
不同的例外有不同的原因。一些例外是正常的,例外只是告诉内核在允许软件继续运行之前需要做什么。这样的示例包括页面错误告诉内核它需要从交换空间加载数据,一个未定义的指令异常告诉内核它需要模拟一个CPU不支持的指令,或一个调试/断点异常告诉内核它需要通知调试器。对于这些内核来说,修复内容并静默继续是正常的。
一些例外情况表明异常情况(例如软件崩溃)。处理这些类型的异常的唯一理智方法是停止运行该软件。您可以保存信息(例如核心转储)或显示信息(例如“死亡蓝屏”)以帮助调试,但最终软件会停止(要么终止进程,要么内核进入“不执行任何操作”用户重置计算机“状态”。
忽略异常情况只会让人们更难弄清楚出了什么问题。例如,想象一下上厕所的指示:
现在想象第2步失败是因为你穿着短裤(“找不到裤子”的例外)。你想在那一点上停下来(有一个很容易理解的错误信息或其他东西),或者在所有有用的诊断信息消失后,忽略该步骤并试图弄清楚出了什么问题?
答案 1 :(得分:2)
如果我理解正确,你想跳过导致异常的指令(例如mov cs, eax
)并继续执行下一条指令的程序。
你为什么要这样做?通常,程序的其余部分是否应该依赖于该指令成功执行的效果?
一般来说,有三种异常处理方法:
将异常视为无法修复的条件并终止该进程。例如,除以零通常以这种方式处理。
修复环境,然后再次执行该指令。例如,页面错误有时会以这种方式处理。
使用软件模拟指令并在指令流中跳过它。例如,复杂的算术指令有时会以这种方式处理。
答案 2 :(得分:2)
您所看到的是一般保护例外的特征。英特尔系统编程指南明确指出(6.15异常和中断参考/中断13 - 一般保护例外(#GP)):
Saved Instruction Pointer
The saved contents of CS and EIP registers point to the instruction that generated the
exception.
因此,您需要编写一个异常处理程序,它将跳过该指令(这有点奇怪),或者只是简单地使用“$SAVED_EIP
处的常规保护异常”或类似消息来终止违规进程
答案 3 :(得分:0)
我可以设想一些情况,其中一个人想要通过解析失败的指令,模拟其操作,然后返回到之后的指令来响应GPF。正常模式是设置,以便指令(如果重试)将成功,但可以例如有一些代码需要访问地址0x000A0000-0x000AFFFF的某些硬件,并希望在缺少这种硬件的机器上运行它。在这种情况下,人们可能不希望永远存入该空间中的“真实”存储器,因为每个访问都必须被捕获并单独处理。我不确定是否有办法解决这个问题而不必解码试图访问该内存的任何指令,尽管我知道有些虚拟PC程序似乎很好地管理它。
否则,我建议你应该为每个线程提供一个跳转向量,当系统遇到GPF时应该使用该向量。通常,该向量应该指向一个线程退出例程,但是即将对指针执行“可疑”操作的代码可以将其设置为适合该代码的错误处理程序(代码应该在设置区域时取消设置向量)错误处理程序本来是合适的。)
我可以想象一个人可能想要在没有执行它的情况下模拟指令的情况,以及可能想要将控制转移到错误处理程序例程的情况,但我无法想象任何人想要简单地跳过的地方导致GPF的指令。