老早就买了一本赵炯编写的《Linux内核完全剖析-基于0.12内核》,看来前四章的原理部分,却一直没有勇气来自己动手来实践。最近终于下定决心好好看看这本书,并打算坚持写一些学习笔记来记录学习过程中的一些收获和经验,一来可以强迫自己复习所学的东西,二来也可以和其他正在学习Linux内核的同志们分享一下自己的学习经验和在学习过程中的遇到的一些问题。今天是我真正动手学习Linux的第二天,学习编译书上140~146页的Linux-0.00。
首先,忙活了几个小时,总算正确无误的录入了书上的代码,接下来就是编译了。
OK,boot编译连接成功,接下来该head了。
非常不幸,编译出错了,根据提示对源代码作如下修改:
第103行: movl scr_loc, %bx改为movl scr_loc, %ebx
第240行: movl $65, %al 改为movb $65, %al
第249行: movl $66, %al改为movb $66, %al
同时,将head.s中所有的的.align 2改为.align 4,.align 3改为.align 8
然后再次编译连接head.s。
编译虽然通过,但是连接出错,通过查找资料,终于找到了解决方法:
先在程序的.text段中添加一行:.globl startup_32
然后通过使用命令:ld -m elf_i386 -Ttext 0 -e startup_32 -o head head.o进行编译。
OK,到现在为止boot和head都编译成功了。下面就要将二进制代码写到磁盘上面进行测试了,按照907页键入如下命令:
$:sudo dd bs=32 if=boot of=/dev/fd0 skip=1
$:sudo dd bs=512 if=head of=/dev/fd0 skip=2 seek=1
写入成功。
太好了,现在可以在虚拟机上面进行测试了。 但是不幸的是,我在VirtualBox和Bochs 2.3.6上都没有正常引导,运行时一直是黑屏。
问题出在哪里呢?无奈之下只好到赵炯博士的网站上下载了Linux-0.00-050613.zip。先将自己编译连接生成的boot和head文件按上面的方法写入软盘镜像文件vfd(2).img,在将Image写入vfd.img。通过二进制文件比较程序UltraCompare程序比较vfd.img和vfd(2).img发现,从0x00000200开始两个文件的内容就不同了,前面的部分两者一样,如下图所示:
这说明boot在两个img文件中都正确写入了,但是在写入head的时候出了差错。
初步假定是文件写入的时候定位错误,通过在vfd中搜索0x00000200处的数据B8 10验证了这一假设,如下图所示:
图中说明vfd.img中从地址x00000200开始的数据和vfd(2)中的从0x00000e00开始的数据相同。说明确实是错位引起的错误。
好了,问题找到了就好办了,通过hexdump命令查看head的二进制编码,如下:
$: hexdump head>head.txt
head.txt的内容如下:
我们看到从0x0001000开始的数据正是我们需要的,我们只需要将从0x0001000处开始的数据写入软盘(也就img文件)中的x00000200处就可以了:
$:sudo dd bs=32 if=boot of=/dev/fd0 skip=1
$: sudo dd bs=512 if=head of=/dev/fd0 skip=8 seek=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运行需要的第一条指令,故而需要使用
$:sudo dd bs=512 if=head of=/dev/fd0 skip=2 seek=1
去掉512*2B的无用内容。而elf格式的system文件的text地址在哪里呢?可以在linux下使用readelf工具查看system的信息,得到表:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf A
l
[ 0] NULL 00000000 000000 000000 00 0 0
0
[ 1] .text PROGBITS 00000000 001000 00159a 00 AX 0 0
8
[ 2] .data PROGBITS 0000259c 00259c 000000 00 WA 0 0
4
[ 3] .bss NOBITS 0000259c 00259c 000000 00 WA 0 0
4
[ 4] .shstrtab STRTAB 00000000 00259c 00001c 00 0 0
1
可以看到text段从0x001000开始,即从4096开始。
2、如果使用linux-0.00-041217开发包在现在使用的ubuntu或redhat下编译,除了上文所做的修改以外,还需要修改build.c程序,因为这个开发包没用dd命令生成image,而是用了linux一般使用的build程序,从boot、system文件中抽取需要的部分,生成image。那么就需要修改build.c源程序。原来的build.c程序在读取system文件时,先读取1024字节的文件头,然后将剩下的内容写入image。而当system是elf格式时,text的偏移是0x1000,即4096B,那么在写之前就需要读4个1024,方法如下:
if ((id=open(argv[2],O_RDONLY,0))<0)
die("Unable to open 'system'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system1'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system2'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system3'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system4'");
还需要将判断a.out头的语句注释掉:
//if (((long *) buf)[5] != 0)
// die("Non-GCC header of 'system'");
之后再保存,make,让bochs从image文件启动,便万事大吉了。