有特权级变换的转移——堆栈的切换

在没有特权级变换的情况下,程序的转移中的一些参数和返回地址都是push进同一个堆栈,这种情况比较简单。而如果转移伴随着特权级变换,那么我们就会涉及到两个堆栈,外层堆栈(调用者堆栈)和内层堆栈(被调用者堆栈)。(特权级变化的时候,堆栈也要发生变化,这个是处理器的机制,其作用是为了避免高特权级的过程由于栈空间不足而崩溃。)

    既然涉及到两个堆栈,那么我们从哪里取得其余堆栈的ss和esp呢?那就得使用到TSS(Task-State Stack)。TSS里面包含多个字段,现在只关心偏移4到偏移27的3个ss和3个esp。我们有四个特权级ring0,ring1,ring2 & ring3,为什么只有ring0,ring1,ring2 的ss 和 esp 呢?原来只有当转移时从外层到内层时(低特权级到高特权级),新的堆栈ss & esp才会从TSS中取得,而特权级从高到低转移时,新堆栈的ss & esp 则通过其他方式获取。

    好,新堆栈问题解决后,我们看下转移过程。下面是CPU在整个过程中所做的工作:

    1. 根据目标代码段的DPL(新的CPL)从TSS中选择切换到的对应的ss和esp。

    2. 从TSS中读取新的ss和esp。在这个过程中如果发现ss、esp或者TSS界限错误都会导致无效TSS异常(#TS)。

    3. 对ss描述符进行检验,如果发生错误,同样产生错误,同样产生#TS异常。

    4. 暂时性地保存当前ss和esp的值。

    5. 加载新的ss和esp。

    6. 将刚刚保存起来的ss和esp的值压入新栈。

    7. 从调用者堆栈中将参数复制到被调用者堆栈(新堆栈)中,复制参数的数目有调用门中Param Count一项来决定。如果Param Count是零的话,将不会复制参数。

    8. 将当前的cs和eip压栈。

    9. 加载调用门中指定的新的cs和eip,开始执行被调用者过程。

 

    那么,正如call指令对应ret,调用门也面临返回的问题。ret基本上就是call的反过程,只是带参数的ret指令会同时释放事先被压栈的参数。由被调用者到调用者的返回过程中,处理器的工作包括一下步骤:

    1. 检查保存的cs中的RPL以判断返回时是否要变换特权级。

    2. 加载被调用者堆栈上的cs和eip(此时会进行代码段描述符和选择子类型和特权级检查)。

    3. 如果ret指令含有参数,则增加esp的值以跳过参数,然后esp指向被保存过的调用者ss和esp。注意,ret的参数必须对应调用门中的Param Count的值。

    4. 加载ss和esp,切换到调用者堆栈,被调用者的ss和esp被丢弃。在这里将会进行ss描述符、esp以及ss段描述符的检查。

    5. 如果ret指令含有参数,增加esp的值以跳过参数(此时已经在调用者堆栈中)。

    6. 检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(此规则不适用于一致代码段),那么一个空描述符会被加载到该寄存器。

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值