程序转移的发生,可以由指令jmp call ret sysenter sysexit int n 或 iret引起,也可以由中断和异常机制引起,这个arm单片机有区别,arm单片机的任务切换的实质就是中断。
jmp和call指令可以实现下列4种转移
1.目标操作数包含目标代码段的段选择子
2.目标操作数指向一个包含目标代码段选择子的调用门描述符
3.目标操作数指向一个包含目标代码段选择子的TSS
4.目标操作数指向一个任务门,这个任务门指向一个包含目标代码段选择子的TSS
由上一节CPL DPL的描述可知,通过jmp和call进行直接转移的限制太多所以需要用到门和TSS的帮助
BYTE7 BYTE6 | BYTE5 BYTE4 | BYTE3 BYTE2 | BYTE1 BYTE0 |
---|---|---|---|
31..16 偏移 | 属性等 | 选择子 | 15..0偏移 |
一个门描述了由一个选择子和一个偏移所指定的线性地址,程序正是通过这个地址进行转移的
以下代码来实现目标操作数指向一个包含目标代码段选择子的调用门描述符
这个例子还是不能很好的体现门调用与直接调用的相比的优势在哪里
假设我们想由代码A转移到代码B,运用一个调用门G,即调用门G中的目标选择子指向代码B的段,实际上我们涉及了CPL 、RPL 、代码B的DPL、(记作DPL_B)、调用门的DPL(记作DPL_G)。
A访问G这个调用门,相当于访问一个数据段,要求CPL和RPL比DPL_G特权级高
然后G调用访问B,则需要比较CPL和DPL_B。
如果B是一致代码段,要求DPL_B小于等于CPL,即CPL的特权级比B的要低
若B是非一致代码段,调用call指令时,要求DPL_B小于等于CPL
调用jmp指令时,只能是DPL_B=CPL
综上可以看到,通过调用门和call指令,可以实现从低特权级到高特权级的转移,无论目标代码段是一致还是非一致的
同时,特权级的变换也意味着堆栈也要发生变化