通过调用门进行控制转移 ——《x86汇编语言:从实模式到保护模式》读书笔记29

通过调用门进行控制转移

1. 关于堆栈切换

如果通过调用门把控制转移到了更高特权级的非一致代码段中,那么CPL就会被设置为目标代码段的DPL值,并且会引起堆栈切换。为什么要切换堆栈呢?原因有以下几点:

1. 因为栈段的特权级必须同CPL保持一致;

2. 防止高特权级程序由于栈空间不足而崩溃;

3. 防止低特权级程序通过共享的栈有意或无意地干扰高特权级程序。

为了切换栈,每个任务除了自己的固有栈之外,还必须额外定义一套或多套栈,具体是多少取决于任务的特权级别。

0特权级的任务不需要额外的栈,因为除了从调用高特权级的例程(通常是操作系统例程)返回外,不允许将控制从特权级高的代码段转移到特权级低的代码段——操作系统不会引用可靠性比自己低的代码;1特权级的任务需要额外定义一个DPL为0的栈,以便将控制转移到0特权级时使用;2特权级的任务需要额外定义两个栈,其DPL分别为0和1;3特权级的任务最多额外定义三个栈,其DPL分别为0、1、2.

以下文字摘自《Intel Architecture Software Developer’s Manual Volume 3:System Programming》的4.8.5节——Stack Switching.

Each task must define up to 4 stacks: one for applications code (running at privilege level 3) and one for each of the privilege levels 2, 1, and 0 that are used. (If only two privilege levels are used[3 and 0], then only two stacks must be defined.)

操作系统负责为任务用到的所有特权级分配栈空间和创建栈段描述符,并且在任务的TSS中填写栈段的选择子和ESP的初始值(下图是TSS的一部分)。

有几点需要注意:

1. 每个栈必须可读可写,并且具有足够的空间来存放以下信息:

(1)调用过程的SS、ESP、CS和EIP寄存器的内容

(2)被调用过程的参数和临时变量所需使用的空间

(3)当隐含调用一个异常或者中断过程时标志寄存器EFLAGS和出错码使用的空间

2. 由于一个过程可以调用其他过程,因此每个栈必须有足够的空间来容纳多帧信息

3. TSS中的中的SSx、ESPx(x=0,1,2)字段是静态的,除非软件进行修改,处理器从来不会改变它们。举例来说,假设操作系统为一个用户任务的TSS填写了ESP0,其值为0x800;当这个任务通过调用门进入0特权级的代码段时,会切换到0特权级堆栈,堆栈指针ESP的初始值就是0x800;返回时,假设ESP变成了0x808,处理器并不会把0x808更新到TSS中的ESP0域;下次再通过调用门进入0特权级代码段时,使用的还是当初设置的静态值0x800。

2. 通过调用门进行控制转移和返回的具体过程

2.1 转移的过程

首先,通过调用门进行控制转移,可以使用jmp far或者call far指令。指令执行时,段选择子必须指向调用门,32偏移量可以是任意值(会被CPU忽略)。

其次,必须符合下表的特权级检查规则。

再次,当使用call far指令通过调用门转移控制时,如果改变了CPL,则必须切换栈,即从当前任务的固有栈切换到与目标代码段特权级相同的栈上。栈的切换是由处理器固件自动进行的。

当前栈是由SS和ESP的当前内容所指示的。要切换到的新栈的相关信息位于当前任务的TSS中,处理器知道如何找到它。栈切换过程如下:

1. 根据目标代码段的DPL(也就是新的CPL)到当前任务的TSS中读取新栈的选择子和栈指针。在读取栈选择子、栈指针或者栈段描述符的过程中,任何违反段界限的错误都将导致产生一个无效TSS异常。

2. 检查栈段描述符的特权级和类型是否有效,若无效同样产生一个无效TSS异常。

3. 临时保存SS和ESP的当前值,把新栈的选择子和栈指针加载到SS和ESP中。然后把临时保存的SS和ESP的内容压入新栈中。

4. 根据调用门描述符中“参数个数”字段,把旧栈中的所有参数复制到新栈中。如果参数个数为0,则不复制参数。

5. 将当前CS和EIP的内容压入新栈。通过调用门实施的控制转移一定是远转移,所以要压入CS和EIP。

6. 从调用门描述符中把目标代码段的选择子和段内偏移值传送到CS和EIP中,开始执行被调用过程。

相反,如果没有改变特权级别,则不切换栈,继续使用调用者当前的栈,只是在原来的基础上压入当前CS和EIP的内容。

另外,如果通过调用门的控制转移是jmp far指令发起的,结果就是“肉包子打狗——有去无回”,且没有特权级变化,也不需要切换栈。相反,如果是call far指令发起的,则可以使用远返回指令retf把控制返回到调用者。

2.2. 返回的过程

对于相同特权级的返回,CPU从堆栈中弹出EIP和CS;会发生特权级改变的远返回仅允许返回到低特权级程序中,即返回到的代码段的DPL在数值上要大于CPL。返回的全部过程如下:

  1. 检测被调用者栈中CS寄存器的RPL字段值,以确定在返回时特权级是否发生改变。
  2. 弹出并使用被调用过程栈上的值加载EIP和CS寄存器。在此过程中会对代码段描述符和代码段选择子的RPL进行特权级与类型检查。
  3. 如果远返回指令是带参数的,则将参数和ESP寄存器的当前值相加,以跳过被调用者栈中的参数部分,最后的结果是ESP寄存器指向调用者SS和ESP的压栈值。注意,retf指令的参数必须等于调用门中所有参数的总字节数之和。
  4. 如果返回时需要改变特权级,则从栈中将ESP和SS弹出,并把值代入寄存器ESP和SS,切换到调用者的栈。
  5. 如果远返回指令是带参数的,则将参数和ESP寄存器的当前值相加,以跳过调用者栈中的参数部分,最后的结果是调用者的栈恢复平衡。
  6. 如果返回时需要改变特权级,则检查DS,ES,FS和GS的内容,如果段选择子指向数据段或者非一致代码段且段描述符的DPL在数值上小于返回后的新CPL,那么就把数值0传送到该段寄存器。
时间: 2024-08-06 10:28:23

通过调用门进行控制转移 ——《x86汇编语言:从实模式到保护模式》读书笔记29的相关文章

任务和特权级保护(二)——《x86汇编语言:从实模式到保护模式》读书笔记32

之前做了那么多铺垫,我们终于可以看看第14章的代码了. 对于引导代码和用户程序,依然采用第13章的:对于内核程序(c14_core.asm),编译的时候有几行报错了,只要加上dword即可解决. 1. 为什么要用调用门 在第13章,为了能使用内核提供的例程,用户程序是用call far指令直接转移到内核例程(非一致代码段).因为CPL=目标代码段描述符的DPL=RPL=0,符合下面表格的条件,所以转移是没有问题的. 但是在本章,用户程序工作在3特权级,而非0特权级,所以是无法直接转移的.不过也不

程序的载入和运行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25

程序的载入和运行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文最终把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都是在命令行输入命令进行编译和写入.源文件少的时候还不认为麻烦,当源文件多了,就会认为特别麻烦.有没有简单的方法呢? 当然有,就是用make工具. 1.1.什么是make工具 make是一个命令工具,它解释Makefile中的指令.在Makefile文件里描写叙述了整个project全部文件的编译顺序.编译规则

存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20

存储器的保护(三) 改动本章代码清单,使之能够检測1MB以上的内存空间(从地址0x0010_0000開始,不考虑快速缓存的影响).要求:对内存的读写按双字的长度进行.并在检測的同一时候显示已检測的内存数量.建议对每一个双字单元用两个花码0x55AA55AA和0xAA55AA55进行检測. 上面的文字选自原书第12章的习题1. 这篇博文就讨论一下这道题.由于是初学,我不正确自己做太高的要求.仅仅要实现功能就可以. 代码清单 ;文件说明:第12章习题-1 ;创建日期:2016-3-7 ;------

程序的加载和执行(六)——《x86汇编语言:从实模式到保护模式》读书笔记26

程序的加载和执行(六)--<x86汇编语言:从实模式到保护模式>读书笔记26 通过本文能学到什么? NASM的条件汇编 用NASM编译的时候,通过命令行选项定义宏 Makefile的条件语句 在make命令行中覆盖Makefile中的变量值 第13章习题解答 复习如何构造栈段描述符 我们接着上篇博文说. 在我修改后的文件中,用到了条件汇编. 比如: %ifdef DEBUG put_core_salt: ;打印内核的符号 ... ... put_usr_salt: ;打印用户的符号 ... .

《X86汇编语言 从事模式到保护模式》问题

书中的42页,监测点4.2第一题,代码如下: mov ax, 0xb800 mov ds, ax mov [0x00], 'a' mov [0x02], 's' mov [0x04], 'm' jmp $ 编译会报错 提示没有指定操作数长度,修改后为 mov ax, 0xb800 mov ds, ax mov byte [0x00], 'a' mov byte [0x02], 's' mov byte [0x04], 'm' jmp $ 然后用书中提供的工具fixvhdwr.exe写入到虚拟机的

存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18

本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄而作<春秋>:屈原放逐,乃赋<离骚>:左丘失明,厥有<国语>:孙子膑脚,<兵法>修列:不韦迁蜀,世传<吕览>……”好了,不煽情了,进入正题. 第12章的代码如下. 1 ;代码清单12-1 2 ;文件名:c12_mbr.asm 3 ;文件说明:硬盘主引

《X86汇编语言 从实模式到保护模式》bochs 配置教程(详细)

本文是写给<X86汇编语言 从实模式到保护模式>读者的一份Bochs配置指南. 我们要做的有: 1.下载并安装bochs 2.配置bochs 3.通过bochs调试虚拟硬盘 bochs的官方网址:http://bochs.sourceforge.net/ bochs的下载地址:http://sourceforge.net/projects/bochs/files/bochs/ 本书附带文件下载地址:https://files-cdn.cnblogs.com/files/leec/booktoo

《80X86汇编语言程序设计教程》十 实模式与保护模式的切换实例

1.  再次声明,需要纯DOS系统才能看到满意测试效果.内容是演示实模式与保护模式切换实例,实现功能是16进制显示从110000H开始的256个字节的值 2.  源代码如下: 1 ;功能:演示实模式与保护模式的切换,16进制显示从110000H开始的256个字节的值 2 ;16位偏移的段间直接转移指令的宏定义,这是一个JMP指令到所描述的地址 3 4 JUMP macro selector,offsetv 5 db 0eah ;操作码 6 dw offsetv ;16位偏移 7 dw selec

程序的加载和执行(三)——《x86汇编语言:从实模式到保护模式》读书笔记23

程序的加载和执行(三)--读书笔记23 接着上次的内容说. 关于过程load_relocate_program的讲解还没有完,还差创建栈段描述符和重定位符号表. 分配栈空间与创建栈段描述符 462 ;建立程序堆栈段描述符 463 mov ecx,[edi+0x0c] ;4KB的倍率 464 mov ebx,0x000fffff 465 sub ebx,ecx ;得到段界限 466 mov eax,4096 467 mul dword [edi+0x0c] 468 mov ecx,eax ;准备为