X86汇编语言-从实模式到保护模式—笔记(28)-第13章 程序的动态加载和执行(6)
本帖最后由 兰陵月 于 2017-12-5 21:51 编辑进入保护模式后,程序一样的需要用到段寄存器,当然这里装入段寄存器的不再是段地址,而是段的选择子。因为要访问整个0~4GB数据段,所以要把DS中装入0~4GB数据段的段描述符,从图13-005中,我们可以知道这个段描述符索引号为“1”,TI位为“0”(位于GDT中),RPL为“00”。这里插入复习一下段选择子的构成,如下图图13-009:
因此1#描述符的段选择子为0000_0000_0000_1_0_00B,即0x0008。将该段选择子传送给寄存器DS。如图13-010第59行、第60行。
同理,要使用栈段,也必须将栈段的选择子给寄存器SS,由寄存器来加载相应的段描述符到描述符高速缓存器。栈段的描述符在GDT中的索引号为“3”,TI位为“0”,RPL为“00”,因此该描述父母的选择子为0000_0000_00011_0_00B,即0x0018,将该选择子给寄存器SS,并初始化栈指针ESP为0。如图13-010第62行~第64行。执行完程序第64行后,内存状态示意图如图13-011。
准备工作完成后,主引导程序就开始加载内核程序。加载内核程序用了一个过程“read_hard_disk_0”,但这个过程是32位保护模式下运行的过程,虽然名字与本书第8章的那个过程一样,但是内容还是有区别的。下图图13-012是第8章加载硬盘内容的过程。
下图图13-013是本章读取硬盘程序:为方便理解,将本章加载硬盘内容过程称为32read,将第8章加载硬盘内容的过程称为16read,因为它们之间的之所以有区别,是因为16位模式和32位保护模式的不同而导致的。这两个过程的区别分析如下:1、参数设置32read:(1)起始逻辑扇区号,用EAX。 (2)目标缓冲区地址,用DS:EBX。
16read:(1)起始逻辑扇区号,用DS:SI; (2)目标缓冲区地址,DS:BX。
分析:32位EAX寄存器可以直接保存28位的逻辑扇区号,因此不再需要内存来保存。EBX寄存器在32位模式下,可以访问全部0~4GB内存空间,因此不会再出现16位实模式中需要防止载入内容超过64KB,需要每次加载后重新设定新的目标位置段地址的情况。2、返回值32read:返回EBX,该值=EBX_传入值+512;16read:无返回值。因为在32位保护模式下,EBX可以直接访问0~4GB内存空间,因此当本次加载完成后,EBX的值已经比开始前传入的数值多了512,刚好就是下次加载的目标地址处。因此就不需要像16位保护模式下的情况那样,再进行相关转换设置。
回到程序:主引导程序并不知道内核有多长,但是它可以通过读取内核第一个扇区内的信息,获得内核的长度,因而进行正确加载。程序第69行~第71行,读取内核第一个扇区。读取完成后内存状态如下图图13-014:
内核的长度数据放在内核程序头部偏移量0x00处的一个双字,即内核程序清单第16行。它加载到内存中后,实际物理位置应该是0x00040000+0x00。也就是,即EDI的值。将其传送给eax寄存器。
将取得的长度数据除以512,就知道内核所占用的扇区数目了,不过,在没能整除的情况下,实际扇区总数要比商的值多1。程序第74~77行,将获得的内核长度数据除以512,以获取内核所占用的扇区数目。执行完之后,EAX中放置商,EDX中放置余数。第79行,or edx,edx,一个数与它自己进行or操作,还是它自己,但是会改变相关标志位。如果edx为零(余数为零,代表能被512整除),则EAX中就是总扇区的数目,因为已经读了一个扇区,所以还需要读取的扇区就是DEC EAX;如果不为零,EAX中就是总扇区数目减1,也因为已经读取了一个扇区,因此EAX就是实际还要读取的扇区数目。无论哪种情况,程序都会来到第83行,or eax,eax,这是用来判断eax寄存器是否为零,如果为零,则说明内核长度仅有一个扇区。因此就不需要再读取硬盘,直接跳到第95行标号setup处执行。如果eax不为零,则不跳转,顺序往下执行。程序第87行~第93行,用于从硬盘读取剩余的扇区,用的是loop指令循环读取,循环的次数再ECX中。循环执行完成之后,整个内核就被全部加载至内存中。内核程序的起始物理地址为0x00040000。下面的工作就是为内核运行做相关准备工作了。
页:
[1]