1.笔记:
CPL遇到一致代码段时,情况稍稍有点特殊,一致代码段可以被相同的或者更低特权级的代码访问
ret分两种: retn 和retf
retn是近返回,用于段内返回,它返回到堆栈内保存的本段内的偏移地址
retf是远返回,一般用于段间返回,它返回堆栈内保存的段地址:偏移地址
————————————————————————————————————————————-
From:http://www.cnblogs.com/wanghj-dz/archive/2011/04/24/2026174.html
retf,call 指令运行的详细情况
call指令的运行情况:(代码段只能从低到高如:ring3到ring0(这时特权级转换了,在不同级之间跳转,这是一致代码段情况)) //call也可以实现同级跳转,这是非一致代码段情况。
1、根据目标代码段的DPL,从Tss中选择应该切换至哪个ss和esp //上例中目标代码段为ring0级的LABEL_SEG_CODE_DEST:
2、从Tss中读取新的ss和esp,在这过程中如果发现ss,esp或者Tss界限错误都会导致Tss异常(#TS)
3、对ss描述符进行校验,如果发生错误,同样产生#TS异常
4、暂时性地保存当前ss和esp的值 //系统自动完成把ring3级得ss和esp暂时保存起来
5、加载新的ss和esp //加载Tss中ring0级ss和esp 到ss,esp寄存器。其实现在已经切换到ring0级得堆栈了。ring3级堆栈已经废弃了。
6、将刚刚保存起来的ss和esp的值压入新栈 //把保存起来的ring3级得ss和esp压入到ring0级的堆栈中。
7、将调用者堆栈中的参数复制到被调用者堆栈(新堆栈)中,复制参数的数目有调用门中的ParamCount一项来决定。如ParamCount为0,将不会复制参数。
8、将当前的cs和eip压栈 //把ring3级得cs和eip压到ring0级的堆栈中。
9、加载调用门中指定的新的cs和eip,开始执行被调用者过程。 //把调用门中的新的cs和eip加载到cs和eip寄存器。
ref指令运行情况:(retf 所在代码段是被调用者,上例中ring0所在的代码段是被调用者)
1、检查保存的cs中的RPL 以判断返回时是否要变换特权级 //检查刚刚压到ring0堆栈中保存的cs选择子的RPL,以判断返回时是否要变换特权级。这个选择子是 SelectorCodeRing3
2、加载被调用者堆栈上的cs和eip(此时会进行代码段描述符和选择子类型和特权级检查)//加载刚刚压到ring0堆栈中的cs和eip,加载时进行各种检查,加载后cs和eip已经指向ring3程序段
3、如果ret指令含有参数,则增加esp的值以跳过参数,然后esp将指向被保存过的调用者的ss和esp,注意,ret参数必须对用调用门中的Param Count的值
4、加载ss和esp,切换到调用者堆栈,被调用者的ss和esp被丢弃。 //此时,ss和esp已经指向ring3级的SelectorStack3这个堆栈。
5、如果ret指令含有参数,增加esp的值以跳过参数(此时已经在调用者堆栈中,也就是在ring3堆栈中了)
6、检查ds、es、fs、gs的值,如果其中哪一个寄存器指向的段的DPL小于CPL(上例cpl已经为ring3),那么一个空描述符被加载到该寄存器。(此规则不适用于一致代码段(特权级转换))