日期:2014-05-16 浏览次数:20807 次
针对它的分析是建立于网上资料之上(快捷、效果好)。
项目如图:
该项目是网上某某已经编译好了,并且对它进行了分析。笔者只是学习他的皮毛。
首先进入项目的是bootsect:这个程序是linuxkernel的第一个程序,包括了linux自己的bootstrap程序,但是在 说明这个程序前,必须先说明一般IBMPC开机时的动作(此处的开机是指"打开PC的电源" )。它是第一个被读入内存中并执行的程序,现在,我们可以开始来看看到底它做了什么。
第一步 :bootsect将它"自己"从被ROMBIOS载入的绝对地址0x7C00处搬到0x90000处, 然后利用一个jmpi(jumpindirectly)的指令,跳到新位置的jmpi的下一行去执行…
表示将跳到CS为0x9000,IP为offset"go"的位置(CS:IP=0x9000:offsetgo),其中I NITSEC=0x9000定义于程序开头的部份,而go这个label则恰好是下一行指令所在的位置。
第二步:接着,将其它segmentregisters包括DS,ES,SS都指向0x9000这个位置,与CS看齐 。
第叁步:接着利用BIOS中断服务int13h的第0号功能,重置磁盘控制器,使得刚才的设定发挥功能。
第四步:完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsect的setup程序, 也就是以后将会介绍的setup.S,此读入动作是利用BIOS中断服务int13h的第2号功能。
第五步:再来,就要读入真正linux的kernel了,也就是你可以在linux的根目录下看到的"v mlinuz"。
第六步:接下来做的事是检查root device,之后就仿照一开始的方法,利用indirect jump跳到刚刚已读入的setup部份。
;//bootsec.s文件说明如下:磁盘的引导块程序,驻留在磁盘的第一扇区。 ;//在PC机加电rom bios自检之后,引导扇区由bios加载到内存0x7c00处,然后将自己移动到内存0x90000处。 ;//该程序的主要作用是首先将setup模块从磁盘加载到内存中,紧接着bootsect的后面位置(0x90200),然后利用bios中断0x13中断去磁盘参数表中当前引导盘的参数, ;//然后在屏幕上显示"Loading system..."字符串。再者将system模块从磁盘上加载到内存0x10000开始的地方。随后确定根文件系统的设备号,如果没有指定, ;//则根据所保存的引导盘的每类型和种类,并保存设备号与boot_dev,最后长跳转到 setup程序开始处0x90200执行setup程序。 .model tiny .386p ;// SYSSIZE是要加载的节数(16字节为1节)。3000h共为30000h字节=192kB ;// 对当前的版本空间已足够了。 SYSSIZE = 3000h ;// 指编译连接后system模块的大小。 ;// 这里给出了一个最大默认值。 SETUPLEN = 4 ;// setup程序的扇区数(setup-sectors)值 BOOTSEG = 07c0h ;// bootsect的原始地址(是段地址,以下同) INITSEG = 9000h ;// 将bootsect移到这里 SETUPSEG = 9020h ;// setup程序从这里开始 SYSSEG = 1000h ;// system模块加载到10000(64kB)处. ENDSEG = SYSSEG + SYSSIZE ;// 停止加载的段地址 ;// DEF_ROOT_DEV: 000h - 根文件系统设备使用与引导时同样的软驱设备. ;// 301 - 根文件系统设备在第一个硬盘的第一个分区上,等等 ROOT_DEV = 301h;//指定根文件系统设备是第1个硬盘的第1个分区。这是Linux老式的硬盘命名 ;//方式,具体值的含义如下: ;//设备号 = 主设备号*256 + 次设备号 ;// (也即 dev_no = (major<<8 + minor) ;//(主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并行口,7-非命名管道) ;//300 - /dev/hd0 - 代表整个第1个硬盘 ;//301 - /dev/hd1 - 第1个盘的第1个分区 ;//... ... ;//304 - /dev/hd4 - 第1个盘的第4个分区 ;//305 - /dev/hd5 - 代表整个第2个硬盘 ;//306 - /dev/hd6 - 第2个盘的第1个分区 ;//... ... ;//309 - /dev/hd9 - 第1个盘的第4个分区 ;/* ************************************************************************ ; boot被bios-启动子程序加载至7c00h(31k)处,并将自己移动到了 ; 地址90000h(576k)处,并跳转至那里。 ; 它然后使用BIOS中断将'setup'直接加载到自己的后面(90200h)(576.5k), ; 并将system加载到地址10000h处。 ; ; 注意:目前的内核系统最大长度限制为(8*65536)(512kB)字节,即使是在 ; 将来这也应该没有问题的。我想让它保持简单明了。这样512k的最大内核长度应该 ; 足够了,尤其是这里没有象minix中一样包含缓冲区高速缓冲。 ; ; 加载程序已经做的够简单了,所以持续的读出错将导致死循环。只能手工重启。 ; 只要可能,通过一次取取所有的扇区,加载过程可以做的很快的。 ;************************************************************************ */ code segment ;// 程序从_main标号开始执行。 assume cs:code start: ;// 以下10行作用是将自身(bootsect)从目前段位置07c0h(31k) ;// 移动到9000h(576k)处,共256字(512字节),然后跳转到 ;// 移动后代码的 go 标号处,也即本程序的下一语句处。 mov ax,BYTE PTR BOOTSEG ;// 将ds段寄存器置为7C0h mov ds,ax mov ax,BYTE PTR INITSEG ;// 将es段寄存器置为9000h mov es,ax mov cx,256 ;// 移动计数值 = 256字 = 512 字节 sub si,si ;// 源地址 ds:si = 07C0h:0000h sub di,di ;// 目的地址 es:di = 9000h:0000h rep movsw ;// 重复执行,直到cx = 0;移动1个字 ; jmp INITSEG:[go] ;// 间接跳转。这里INITSEG指出跳转到的段地址。 db 0eah ;// 间接跳转指令码 dw go dw INITSEG go: mov ax,cs ;// 将ds、es和ss都置成移动后代码所在的段处(9000h)。 mov ds,ax ;// 由于程序中有堆栈操作(push,pop,call),因此必须设置堆栈。 mov es,ax ;// put stack at 9ff00. 将堆栈指针sp指向9ff00h(即9000h:0ff00h)处 mov ss,ax mov sp,0FF00h ;/* 由于代码段移动过了,所以要重新设置堆栈段的位置。 ; sp只要指向远大于512偏移(即地址90200h)处 ; 都可以。因为从90200h地址开始处还要放置setup程序, ; 而此时setup程序大约为4个扇区,因此sp要指向大 ; 于(200h + 200h*4 + 堆栈大小)