编译Linux0.00内核(《Linux内核完全分析-基于0.12内核》)
老早就买了一本赵炯编撰的《Linux内核完全分析-基于0.12内核》,看来前四章的原理部份,却始终没有勇气来自己动手来实践。近来总算下定决心好好瞧瞧这本书,并准备坚持写一些学习笔记来记录学习过程中的一些收获和经验,一来可以逼迫自己备考所学的东西,二来也可以和其他正在学习Linux内核的同志们分享一下自己的学习经验和在学习过程中的遇见的一些问题。明天是我真正动手学习Linux的第二天,学习编译书上140~146页的Linux-0.00。
首先,鼓捣了几个小时,终于正确无误的录入了书上的代码,接出来就是编译了。
OK,boot编译联接成功,接出来该head了。
十分不幸,编译出错了linux内核完全剖析,按照提示对源代码作如下更改:
第103行:movlscr_loc,%bx改为movlscr_loc,%ebx
第240行:movl$65,%al改为movb$65,%al
第249行:movl$66,%al改为movb$66,%al
同时,将head.s中所有的的.align2改为.align4,.align3改为.align8
之后再度编译联接head.s。
编译即使通过,然而联接出错,通过查找资料,总算找到了解决方式:
先在程序的.text段中添加一行:.globlstartup_32
之后通过使用命令:ld-melf_i386-Ttext0-estartup_32-oheadhead.o进行编译。
OK,到现今为止boot和head都编译成功了。下边就要将二补码代码讲到c盘里面进行测试了,根据907页键入如下命令:
$:sudoddbs=32if=bootof=/dev/fd0skip=1
$:sudoddbs=512if=headof=/dev/fd0skip=2seek=1
写入成功。
太好了,如今可以在虚拟机里面进行测试了。并且不幸的是,我在VirtualBox和Bochs2.3.6上都没有正常引导,运行时仍然是死机。
问题出在那里呢?无奈之下只得到赵炯博士的网站上下载了Linux-0.00-050613.zip。先将自己编译联接生成的boot和head文件按前面的方式写入软驱镜像文件vfd(2).img,在将Image写入vfd.img。通过二补码文件比较程序UltraCompare程序比较vfd.img和vfd(2).img发觉,从0x00000200开始两个文件的内容就不同了linux操作系统简介,后面的部份二者一样,如右图所示:
这说明boot在两个img文件中都正确写入了,并且在写入head的时侯出了差错。
初步假设是文件写入的时侯定位错误,通过在vfd中搜索0x00000200处的数据B810验证了这一假定,如右图所示:
图中说明vfd.img中从地址x00000200开始的数据和vfd(2)中的从0x00000e00开始的数据相同。说明确实是错位导致的错误。
好了,问题找到了就好办了,通过hexdump命令查看head的二补码编码,如下:
$:hexdumphead>head.txt
head.txt的内容如下:
我们听到从0x0001000开始的数据正是我们须要的,我们只须要将从0x0001000处开始的数据写入软驱(也就img文件)中的x00000200处就可以了:
$:sudoddbs=32if=bootof=/dev/fd0skip=1
$:sudoddbs=512if=headof=/dev/fd0skip=8seek=1
OK,写入成功。再度进行测试,在VirtualBox中的测试结果如下:
从记:
上述文章是从别处转载的一篇笔记。总的来说,写的比较详尽了。这儿再补充一些解释。
1、上文使用的是赵炯老师的linux-0.00-050613开发包,上面的makefile中使用两条dd命令来世成image文件(这个文件包含了引导部份boot和系统主体部份system,虽然这个system就是head.o而已)。用中级版本的gcc编译这个系统和用gcc1.4(linux-1.11外置编译器版本,可以直接编译linux-0.00,不需任何更改)的区别是,上述方法生成了elf格式的可执行文件,与标准的a.out可执行文件的颈部是有区别的。image文件只须要system文件的.text以后的内容,不须要之前的各类脑部信息。故而须要除去腹部信息。用gas和gld生成的a.out文件有1024B的腹部信息,然后才是os运行须要的第一条指令,故而须要使用
$:sudoddbs=512if=headof=/dev/fd0skip=2seek=1
去除512*2B的无用内容。而elf格式的system文件的text地址在那里呢?可以在linux下使用readelf工具查看system的信息,得到表:
SectionHeaders:
[Nr]NameTypeAddrOffSizeESFlgLkInfA
[0]NULL0000000000000
[1].textPROGBITS00000159a00AX00
[2].dataPROGBITS0000259c00259c00000000WA00
[3].bssNOBITS0000259c00259c00000000WA00
[4].shstrtabSTRTAB09c00001c0000
可以看见text段从0x001000开始,即从4096开始。
2、如果使用linux-0.00-041217开发包在现今使用的ubuntu或redhat下编译,不仅上文所做的更改以外,还须要更改build.c程序,由于这个开发包没用dd命令生成image,而是用了linux通常使用的build程序,从boot、system文件中抽取须要的部份,生成image。这么就须要更改build.c源程序。原先的build.c程序在读取system文件时linux内核完全剖析,先读取1024字节的文件头,之后将剩下的内容写入image。而当system是elf格式时,text的偏斜是0x1000linux命令大全,即4096B,这么在写之前就须要读4个1024,技巧如下:
if((id=open(argv[2],O_RDONLY,0))die("Unabletoopen'system'");
if(read(id,buf,GCC_HEADER)!=GCC_HEADER)
die("Unabletoreadheaderof'system1'");
if(read(id,buf,GCC_HEADER)!=GCC_HEADER)
die("Unabletoreadheaderof'system2'");
if(read(id,buf,GCC_HEADER)!=GCC_HEADER)
die("Unabletoreadheaderof'system3'");
if(read(id,buf,GCC_HEADER)!=GCC_HEADER)
die("Unabletoreadheaderof'system4'");
还须要将判定a.out头的句子注释掉:
//if(((long*)buf)[5]!=0)
//die("Non-GCCheaderof'system'");
然后再保存,make,让bochs从image文件启动,便万事大吉了。