现在的位置: 首页 > 综合 > 正文

保护模式编程

2013年09月13日 ⁄ 综合 ⁄ 共 2515字 ⁄ 字号 评论关闭

随着80286微处理器体系结构的重大变化,它的指令系统也得到了增强和补充,x86的发展进入了保护模式阶段。首先,介绍一下新增加的一些指令:

一、新增高级语言指令

1.       PUSHA/POPA指令

 

功能: 压入所有通用寄存器

语法: PUSHA/POPA

 

该指令是重点指令,用于将通用寄存器的内容压入堆栈,这些寄存器按以下顺序存储到堆栈: AXCXDXBXSP(原始值)、BPSI DI(如果操作数大小属性为 16)。POPA指令执行它的逆操作。SP 寄存器压入的值是它在第一个寄存器压入之前的值。

 

在实地址模式中,如果执行 PUSHA 指令时,SP 寄存器为 13 5,则处理器会由于堆栈空间不足而关闭,产生此情况的异常。

 

2.       BOUND指令

 

功能: 检查数组下标边界

语法: BOUND r16, m16&16

 

该指令用于确定第一个操作数(数组下标)是否在第二个操作数(边界操作数)指定的数组边界内。数组下标是寄存器中的有符号整数。边界操作数是内存位置,它包含一对有符号双字整数(操作数大小属性为 32 时)或一对有符号字整数(操作数大小属性为 16 时)。第一个双字(或字)是数组的下限,第二个双字(或字)是数组的上限。数组下标必须大于或等于下限,且小于或等于上限加上操作数大小(以字节计)。如果下标不在边界内,则会发出超出 BOUND 范围异常 (#BR) 的信号。(生成此异常时,保存的返回指令指针将指向 BOUND 指令)。

 

边界限制数据结构(包含数组上限与下限的两个字或双字)通常就放在数组本身的前面,这样从数组开始处偏移一定的量就可以找到此限制的地址。由于数组的地址已经在寄存器中,因此这种做法可以避免花费额外的总线周期去获取数组边界的有效地址。

 

3.       ENTER指令

 

功能: 生成过程参数的堆栈帧

语法: ENTER imm16,imm8

 

为过程创建堆栈帧。第一个操作数(大小操作数)指定堆栈帧的大小(即堆栈上给过程分配的动态存储空间字节数)。第二个操作数(嵌套层数操作数)给出过程的词法嵌套层级(0 31)。嵌套层级确定要从上一帧复制到新堆栈帧“显示区”的堆栈帧指针数。这两个操作数都是立即数。

 

堆栈大小属性确定是由 BP16 位)还是 EBP32 位)寄存器指定当前帧指针,以及是由 SP16 位)还是 ESP32 位)指定堆栈指针。

 

ENTER 与伴随的 LEAVE 指令提供用于支持块结构语言。ENTER 指令(使用时)通常是过程中的第一条指令,用于为过程建立新的堆栈帧。然后,在过程的末尾(就在 RET 指令的前面),使用 LEAVE 指令释放堆栈帧。

 

如果嵌套层级为 0,则处理器将帧指针从 BP 寄存器压入堆栈,将当前堆栈指针从 SP 寄存器复制到 BP 寄存器,并将当前堆栈指针值减去大小操作数中的值之后的结果加载到 SP 寄存器。如果嵌套层级大于或等于 1,则处理器在调整堆栈指针之前,先将其它帧指针压入堆栈。这些额外的帧指针为被调用过程访问堆栈上的其它嵌套帧提供访问点。

 

4.       LEAVE指令

 

功能: 高级过程退出

语法: LEAVE

 

释放早先的 ENTER 指令设置的堆栈帧。LEAVE 指令将帧指针(在 EBP 寄存器中)复制到堆栈指针寄存器 (ESP),从而释放分配给堆栈帧的堆栈空间。然后,旧的帧指针(由 ENTER 指令保存的调用过程的帧指针)从堆栈弹入 EBP 寄存器,从而恢复调用过程的堆栈帧。

 

LEAVE 指令后面通常执行 RET 指令,以便将程序控制权返回给调用过程。

 

二、控制保护指令

 

接下来就要进入保护模式编程了。注意,保护模式编程并不是什么高深莫测的东西,而是16条只能在保护模式先执行的指令。

 

1.       LAR指令

 

功能: 加载访问权限字节

语法: LAR r16,r/m16

说明: r16 FF00H 掩码 r/m16

 

从第二个操作数(源操作数)指定的段描述符将访问权限加载到第一个操作数(目标操作数),并设置 FLAGS 寄存器中的 ZF 标志。源操作数(可以是寄存器或内存位置)包含要访问的段描述符的段选择器。目标操作数是通用寄存器。

 

作为加载过程的一部分,处理器会执行访问检查。加载到目标寄存器之后,软件就可以在访问权限信息上执行附加的检查。

 

操作数大小为 32 位时,段描述符的访问权限包括类型与 DPL 字段以及 SPAVLD/B G 标志,它们都位于段描述符的第二个双字(字节 4 7)。在加载到目标操作数之前,此双字使用 00FXFF00H 掩码。操作数大小为 16 位时,访问权限包括类型与 DPL 字段。这里,在加载到目标操作数之前,双字的两个低位字节使用 FF00H 掩码。

 

将访问权限加载到目标寄存器之前,此指令执行以下检查:

 

检查段选择器是否为空。检查段选择器指向的描述符是否在访问的 GDT LDT 限制范围内。所有的代码与数据段描述符对于 LAR 指令是否都有效(能够访问)。有效的系统段与门描述符类型见下表。

 

类型

名称

有效

0

保留

1

可用的 16 TSS

2

LDT

3

正忙的 16 TSS

4

16 位调用门

5

16 /32 位任务门

6

16 位中断门

7

16 位陷阱门

8

保留

9

可用的 32 TSS

A

保留

B

正忙的 32 TSS

C

32 位调用门

D

保留

E

32 位中断门

F

32 位陷阱门

 

如果段不是相容代码段,则指令检查在 CPL 处是否可以看见指定的段描述符(即,检查段选择器的 CPL RPL 是否小于或等于段选择器的 DPL)。

 

如果指令无法访问段描述符,或是其类型对于此指令而言无效,则清除 ZF 标志,并且不将访问权限加载到目标操作数。

 

2.       LSL指令

 

功能: 加载段限制

语法: LSL r16,r/m16

说明: r16 段限制与选择器 r/m16

 

从第二个操作数(源操作数)指定的段描述符将规则的段限制加载到第一个操作数(目标操作数),并设置 FLAGS 寄存器中的 ZF 标志。源操作数(可以是寄存器或内存位置)包含要访问的段描述符的段选择器。目标操作数是通用寄存器。

 

作为加载过程的一部分,处理器会执行访问检查。加载到目标寄存器之后,软件就可以比较段限制与指针的偏移量。

 

段限制是一个 20 位值,由段描述符中的字节 01 以及字节 6 的头 4 位组成。如果描述符有字节粒度的段限制(粒度标志设置为

抱歉!评论已关闭.