|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 兰陵月 于 2017-12-5 21:53 编辑
第12章 存储器的保护
处理器引入保护模式的目的是提供保护功能,其中很重要的一个方面就是存储器保护。存储器的保护功能可以禁止程序的非法内存访问,比如,向代码段写入数据、访问段界限之外的内存位置等。
12.2 进入32位保护模式
【12.2.1 话说mov ds,ax和mov ds,eax】 段寄存器(选择器)的值只能用内存单元或者通用寄存器来传送,一般的指令格式为:mov sreg,r/m16。 以“mov ds,ax”为例: 在16位模式下,传送到DS中的值是逻辑段地址;在32位保护模式下,传送的是段描述符的选择子。在16位模式下和32位模式下,有些老式的编译器会产生不同的机器代码。比如: [bits 16] mov ds,ax ;8E D8 [bits 32] mov ds,ax ;66 8E D8 由于在16位模式下,默认的操作数大小是字(2字节),故生成8E D8也不难理解。在32位模式下,默认的操作数大小是双字(4字节)。由于指令中的源操作数是16位的AX,故编译后的机器码前面应当添加前缀0x66以反转默认的操作数大小,即66 8E D8。 加了一个字节的前缀,处理器在执行时会多花一个额外的时钟周期。而且这样的指令用得很频繁,而且牵扯到内存段的访问,自然也很重要。因此,为了不生成前缀,所以在[bits 32]下,把“mov ds,ax”变成“mov ds,eax”。这样生成的机器指令中就不会带有前缀。 其他从通用寄存器到段寄存器的传送也符合这样的编译规则。 通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP; 段寄存器:CS、SS、DS、ES、FS、GS。 但是,从段寄存器到通用寄存器的传送并不符合这样的规则。如“mov eax,cs”语句编译后的机器码为:66 8C C8。而“mov ax,cs”语句编译后的机器码为:8C C8。如下图图1所示,第8行和第10行属于从通用寄存器到段寄存器的传送,符合编译后不带前缀的规则,因此生成的机器码是相同的,都是“8E D0”;而第7行和第9行是段寄存器到通用寄存器的传送,并不符合上文中所讲的不加前缀的编译规则,因此编译后的机器码是不相同的,分别是“8C C8”和“66 8C C8”。
当然,本书采用的是NASM编译器,它不管处理器模式如何变化,也不管指令形式如何变化,像这种“从通用寄存器到段寄存器的传送”的情况,其编译后结果都不会带0x66前缀。比如: [bits 16] mov ds,ax ;8E D8 mov ds,eax ;8E D8 [bits 32] mov ds,ax ;8E D8 mov ds,eax ;8E D8 作为一个程序,开始运行的时候,肯定是要做一些事情的,既然要做一些事,那当然就要把相关的段寄存器设置好,否则程序要访问的数据要从哪个段去访问我们就会搞不明白,同时栈段和栈段指针也要准备好,要不然我们把数据压入哪里都会搞不清楚。本程序开始运行的时候是16位实模式,16位实模式的程序的运行同样需要设置好各相关段寄存器。假如还需要访问数据,那我们还要设置好数据所在段的段寄存器,当然寄存器CS和IP我们是不用管的,毕竟这是CPU的事情。 如下图图2,第7行~第9行,设置栈段。这个栈段是供程序从现在开始到进入保护模式时止的这段时间使用的,这其实是进入32位保护模式准备工作中的准备工作。指令执行完之后,SS指向当前代码段段地址,SP指向0x7C00。 下面开始要做一些进入保护模式的准备工作了。保护模式下仍然要访问段,因此我们要知道段的相关信息,所以下面这些就是要做的事:创建全局描述符表GDT,并在GDT里写入我们保护模式下的段的描述符;打开第21条地址线A20;将CR0寄存器第1位置“1”;然后清空流水线并串行化处理器;最后真正进入32位保护模式。所以准备工作具体如下: 准备工作1:设置好全局描述符表GDT的线性起始基地址; 准备工作2:设计、设置、整理好进入保护模式时需要用到的各个段的描述符,并安装到GDT的相关位置; 准备工作3:加载全局描述符表的线性基地址和界限到GDTR寄存器; 准备工作4:打开第21条地址线的开关A20; 准备工作5:将寄存器CR0第1位设置为“1”; 准备工作6:用jmp dword清空流水线并串行化处理器,同时最终进入32位保护模式。 |
|