最近购买并阅读了若干讲Linux早期内核的书,包括赵炯博士的 《Linux内核完全剖析(基于0.12内核)》以及新设计团队的《Linux 内核设计的艺术—图解Linux操作系统架构设计与实现原理》,感觉对 Linux内核的整体概貌有了大体上的感官认识。
为了搭建起一个间便的修改,调试,运行Linux早期内核的学习环境, 费了不少周折,最后完成了Debian下的比较满意的环境。这里记录一 下详细步骤,对此感兴趣的同志们可以参考下。
另外,本文还参考了赵炯基于Linux 0.11的《Linux内核完全注释(修 正版v3.0)》一书的电子版本,特此感谢。
看到oldlinux坛子上许多按照教程搭建环境不成功的案例,都是因为 案例中没有详细记录所用的软件的版本号,导致后来者走了不少弯路。 这里先介绍一下本文的宿主操作系统的环境。
我的Debian Squeeze GNU/Linux系统运行在VMWare虚拟机上,32bits 单CPU配置。
Linux Justin 2.6.32-5-686 #1 SMP Fri Dec 10 16:12:40 UTC 2010 i686 GNU/Linux
有三个版本的GCC,默认版本的gcc是gcc-4.4:
gcc-4.1 gcc-4.3 gcc-4.4
GDB版本为:
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Linux早期版本运行在方便调试的x86模拟器Bochs上,Bochs对我们的 运行环境影响较大,本文中搭建的Linux 0.11构建环境只能运行在 Bochs 2.2.1之上。
http://sourceforge.net/projects/bochs
(我们使用 bochs-2.2.1.tar.gz以及bochs-2.4.5.tar.gz)
cd bochs-2.4.5 make make install #optional
如果你只是想试运行一下早期的Linux系统,下载:
http://www.oldlinux.org/Linux.old/bochs/linux-0.12-080324.zip
(或者:
http://www.oldlinux.org/Linux.old/bochs/linux-0.11-devel-060625.zip)
并解压,这个是比较新的Linux 0.12的映象,包含一个根文件系统, 并且可以在其中构建Linux 0.11和0.12的代码。
总计 256644 -rw-r--r-- 1 justin justin 3078642 2008-03-24 bochs-2.3.6-1.i586.rpm -rw-r--r-- 1 justin justin 3549736 2008-03-24 Bochs-2.3.6.exe -rw-r--r-- 1 justin justin 15845 2008-03-24 bochsout.txt -rw-r--r-- 1 justin justin 1774 2008-03-24 bochsrc-0.12-fd.bxrc -rw-r--r-- 1 justin justin 5904 2008-03-24 bochsrc-0.12-hd.bxrc -rw-r--r-- 1 justin justin 35732 2007-12-24 bochsrc-sample.txt -rw-r--r-- 1 justin justin 150016 2004-03-06 bootimage-0.12-fd -rw-r--r-- 1 justin justin 154624 2006-08-27 bootimage-0.12-hd -rw-r--r-- 1 justin justin 68 2008-03-24 debug.bat -rw-r--r-- 1 justin justin 1474560 2008-03-24 diska.img -rw-r--r-- 1 justin justin 1474432 2006-08-27 diskb.img -rw-r--r-- 1 justin justin 7917 2008-03-24 linux-0.12-README -rw-r--r-- 1 justin justin 1474560 2008-03-24 rootimage-0.12-fd -rw-r--r-- 1 justin justin 251338752 2008-03-24 rootimage-0.12-hd -rw-r--r-- 1 justin justin 21253 2004-03-13 SYSTEM.MAP
这个版本的系统映象只能运行在Bochs 2.4.5以及之前版本的Bochs安 装上。如果你不想安装你所下载Bochs,则需要修改run脚本, 使用本 地编译的Bochs:
export BXSHARE=~/oldlinux/bochs-2.4.5/bios #../bochs-2.4.5/bochs -q -f bochsrc-0.12-fd.bxrc ../bochs-2.4.5/bochs -q -f bochsrc-0.12-hd.bxrc
老的bochsrc文件在新版本的Bochs上运行的时候需要修改,参考赵炯的提示:
Incompatible issues with recent Bochs version (2.4.x)
——————————————————
Jiong Zhao gohigh@gmail.com
2010.10.15
The bochs image files collected in this directory are built several years ago.
They can be ran normally under Buchs 2.2.X version. But when you use these image
files under one of the recent high Bochs verions, you may encounter some incompatible
problems when using those old .bxrc with them.
The fix method is easy. Just modify the .bxrc file as following:
1. Modify the "romimage" line to the exact like this:
romimage: file=$BXSHARE/BIOS-bochs-latest
2. Modify the "vgaromimage" line like this:
vgaromimage: file = $BXSHARE/VGABIOS-lgpl-latest
3. Comment out or delete the following lines:
#parport1: enable=0
#floppy_command_delay: 50000
#ips: 4000000
That's all!
其中:
运行:
./run
以硬盘启动为例,如果遇到询问:
Plex86/Bochs VGABios 0.6c 08 Apr 2009
This VGA/VBE Bios is released under the GNU LGPL
Please visit :
. http://bochs.sourceforge.net
. http://www.nongnu.org/vgabios
Bochs VBE Display Adapter enabled
Bochs BIOS - build: 04/05/10
$Revision: 1.247 $ $Date: 2010/04/04 19:33:50 $
Options: apmbios pcibios pnpbios eltorito rombios32
ata0 master: Generic 1234 ATA-6 Hard-Disk ( 239 MBytes)
Press F12 for boot menu.
Booting from Floppy...
Loading........................
Press <RETURN> to see SVGA-modes available or any other key to continue.
只需要用回车或者空格跳过即可。登录用户名为root:
8 virtual consoles 4 pty's Partition table ok. Swap device ok: 7999 pages (32763904 bytes) swap-space 13922/64000 free blocks 19428/21333 free inodes 3365 buffers = 3445760 bytes buffer space Free mem: 12582912 bytes Linux 0.12 i386 Baby login: root |^^^^^^| ______________________ | | / \ | | | Welcome to Baby Linux.| | (o)(o) | | @ _) | A public access Linux | | ,___| ,,| system. | | / ..'' | | /____\ \_____________________/ No mail. [/root]#
http://oldlinux.org/Linux.old/images/
bootimage-0.11-20040305 19-Jun-2008 12:55 121344 bootimage-0.12-20040306 19-Jun-2008 12:55 150016 bootroot-0.11 19-Jun-2008 12:55 1474560 bootroot-0.11.zip 19-Jun-2008 12:55 350019 rootimage-0.11-20040305 19-Jun-2008 12:55 1474560 rootimage-0.11-for-orig 19-Jun-2008 12:55 1474560 rootimage-0.12-20040306 19-Jun-2008 12:55 1474560
bootroot-xxx 为集成启动盘。
各个启动盘和文件盘的不同参考赵炯的提示:
The rootimage-0.11-for-orig or rootimage-0.11.Z can be used with the
orignal bootimage-0.11. That is the bootimage compiled without any
modification to the kernel source of linux 0.11.
Other bootimage-0.11 images must be used with the rootimage having
the same dating tag.The difference between rootimage-0.11.Z and
rootimage-0.1x-XXXXXX is they have different bash(sh).
rootimage-0.11.Z bash version 1.05, No job control.
rootimage-0.11-XXXXXX bash version 1.12, No job control.
rootimage-0.12.Z bash version 1.12, Has job control.
rootimage-0.12.XXXXXX bash version 1.12, Has job control.
Jiong Zhao (gohigh@sh163.net)
2004-03-23
也可以单独下载Linux 0.11和0.12的系统盘:
http://oldlinux.org/Linux.old/Linux-0.12/images/
bootimage-0.12.Z 19-Jun-2008 12:50 70345 rootimage-0.12.Z 19-Jun-2008 12:50 825931
http://oldlinux.org/Linux.old/Linux-0.11/images/
bootimage-0.11.Z 19-Jun-2008 12:50 49068 rootimage-0.11.Z 19-Jun-2008 12:50 752911
因为Linux 0.11的资料比较全面,而且可以找到大致修改好的源代码,这里以Linux 0.11为例。
原始的源代码可以在这里找到:
http://oldlinux.org/Linux.old/kernel/0.1x/
linux-0.11.tar.gz linux-0.12.tar.gz
可以用来在主机环境下编译的大致修改好的Source有:
http://oldlinux.org/Linux.old/kernel/0.1x/
linux-0.11-040327-rh9.diff.gz 19-Jun-2008 12:50 8784 linux-0.11-040327-rh9.tar.gz 19-Jun-2008 12:50 96579 linux-0.11-060617-gcc4-diff.gz 19-Jun-2008 12:50 1698 linux-0.11-060618-gcc4.tar.gz 19-Jun-2008 12:50 96061
本文使用最后一个: linux-0.11-060618-gcc4.tar.gz
以下包中带有调试信息的可运行映像,可编译的大致修改好带调试信 息的Source有:
http://oldlinux.org/Linux.old/bochs/
linux-0.11-gdb-050619.tar.gz 19-Jun-2008 12:55 996927 linux-0.11-gdb-rh9-050619.tar.gz 19-Jun-2008 12:55 1164489
其中,前者是在Linux 0.11编译出来的,后者是在Redhat 9中编译出 来的,我们这里只使用后者,前者可以在Linux 0.11的Dev Build中找 到。
需要注释掉 iodev/harddrv.h
中的290和295两行上的
sparse_image_t::
,才能保证编译通过:
off_t #ifndef PARANOID /*sparse_image_t::*/ #endif get_physical_offset(); void #ifndef PARANOID /* sparse_image_t::*/ #endif set_virtual_page(Bit32u new_virtual_page);
参考: http://www.oldlinux.org/oldlinux/viewthread.php?action=printable&tid=10761
对 bochs-2.2.1/gdbstub.cc
使用如下patch(具体行数需调整):
** gdbstub.cc.orig Thu Oct 18 18:44:38 2007 --- gdbstub.cc Sat Jan 12 17:25:22 2008 *************** static void debug_loop(void) *** 455,460 **** --- 455,464 ---- { write_signal(&buf[1], SIGTRAP); } + else if (last_stop_reason == GDBSTUB_STOP_NO_REASON) + { + write_signal(&buf[1], SIGSEGV); + } else { write_signal(&buf[1], 0);
此处修改使Bochs不再送出 Signal 0
中断信号,而是改为 SIGSEGV
因为gdb无法忽略Signal 0 但是可以通过命令忽略SIGSEGV:
(gdb) handle SIGSEGV nostop noprint ignore
或者
(gdb) handle SIGSEGV nostop noprint pass
避免了因为gdb连续收到signal 0而中断,导致无法调试内核的问题。
这里必须使用Bochs 2.2.1作为调试运行平台,否则从软盘装载System的时 候会不断出现“Reset-floppy Called"错误。
这里我们只构建但并不安装这个build:
cd bochs-2.2.1 ./configure --enable-gdb-stub make # make install
在Bochsrc中首行指定:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
安装as86以及ld86:
sudo apt-get install bin86
该软件包的具体信息为:
Package: bin86 Priority: optional Section: devel Installed-Size: 220 Maintainer: Juan Cespedes <cespedes@debian.org> Architecture: i386 Source: linux86 Version: 0.16.17-3.1 Depends: libc6 (>= 2.7) Conflicts: linux86 Filename: pool/main/l/linux86/bin86_0.16.17-3.1_i386.deb Size: 88998 MD5sum: 1148c8d674a8a06bc85ee7623c184c58 SHA1: b65c7434b77c66ec4e6d09fc03a82daa54a08c1f SHA256: 7fd5a6f96a3342881ae858e5c00b142dc1cbfcea568f7685a7b06d7d68c37030 Description: 16-bit x86 assembler and loader This is the as86 and ld86 distribution written by Bruce Evans. It's a complete 8086 assembler and loader which can make 32-bit code for the 386+ processors. Tag: devel::machinecode, interface::commandline, role::program, scope::utility
也可以从如下地址下载:
http://oldlinux.org/Linux.old/bin/bin86-0.16.9.tar
直接用 gcc-4.x
编译Linux 0.1x虽然能正确编译通
过,但会造成无法load system。因此需要使用Red Hat 9的
GCC-3.2.2-5编译。
在Debian下安装alien, 可转换Red Hat RPM包到DEB或者TGZ格式。
sudo apt-get install alien
下载符合Host环境的GCC 3.2.2-5的RPM包,这里安装GCC 3.2.2 i386 RPM, 在这里下载:
ftp://archive.download.redhat.com/pub/redhat/linux/9/en/os/i386/RedHat/RPMS/gcc-3.2.2-5.i386.rpm
用alien转换为压缩包,并解压缩:
alien -t gcc-3.2.2-5.i386.rpm tar zxvf gcc-3.2.2-5.tgz
我们得到一个./usr目录,其中bin中有需要的GCC-3.2.2-5:
bin ├── c89 ├── c99 ├── cc -> gcc ├── gcc ├── gcov ├── i386-redhat-linux-gcc ├── protoize └── unprotoize
解压缩
tar zxvf linux-0.11-060618-gcc4.tar.gz mv linux linux-0.11-060618-gcc4-src cd linux-0.11-060618-gcc4-src
新建一个脚本文件 envsetup.sh
export PATH=/home/justin/oldlinux/usr/bin/:$PATH
当前shell执行这个脚本,并执行编译:
. envsetup.sh make
发现会报如下错误:
gcc -mcpu=i386 -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -I../../include \ -c -o console.o console.c gcc -E -nostdinc -I../../include -traditional keyboard.S -o keyboard.s gcc: installation problem, cannot exec `tradcpp0': no such file or directory make[1]: *** [keyboard.s] Error 1 make[1]: Leaving directory `/home/justin/oldlinux/linux/kernel/chr_drv' make: *** [kernel/chr_drv/chr_drv.a] Error 2
需要修改kernel/chr_drv/Makefile:
CPP =gcc-4.1 -E -nostdinc -I../../include
这里使用gcc-4.x的cpp预编译器处理这个错误,如果今后需要用 gcc-4.x编译该代码,还需要修改一处:
diff -r linux/kernel/blk_drv/blk.h linux-0.11-060618-gcc4-src//kernel/blk_drv/blk.h 87c87,88 < #elif --- > /*#elif*/ > #else
但这步这里可以省略,再次编译该代码应该可以通过,生成一个
Image
顶层目录。生成的Image可以配合下节的文件系统
一起使用。
下载加入调试信息的源代码包:
http://oldlinux.org/Linux.old/bochs/linux-0.11-gdb-rh9-050619.tar.gz
解压缩:
tar zxvf linux-0.11-gdb-rh9-050619.tar.gz cd linux-0.11-gdb-rh9-050619 cp -r linux mylinux cd mylinux
按上节方法修改源代码,需要另外添加两行代码在
mylinux/tools/build.c
头部:
diff -r linux//tools/build.c mylinux//tools/build.c < #include <unistd.h> /* contains read/write */ --- > #include <unistd.h> /* contains read/write */ 31a32,34 > #define MAJOR(a) (((unsigned)(a))>>8) > #define MINOR(a) ((a)&0xff) >
然后运行make,可以成功编译。
cd linux-0.11-gdb-rh9-050619 cp bochsrc-fd1-gdb.bxrc bochsrc-fd1-gdb-my.bxrc
修改脚本 bochsrc-fd1-gdb-my.bxrc
:
--- bochsrc-fd1-gdb.bxrc 2011-09-29 12:16:40.156170247 +0800 +++ bochsrc-fd1-gdb-my.bxrc 2011-09-29 20:13:55.958812695 +0800 @@ -53,7 +53,7 @@ #floppya: 1_44=/dev/fd0, status=inserted #floppya: file=../1.44, status=inserted #floppya: 1_44=/dev/fd0H1440, status=inserted -floppya: 1_44=linux/Image, status=inserted +floppya: 1_44=mylinux/Image, status=inserted #======================================================================= # FLOPPYB:
同时修改 run
文件:
export BXSHARE=~/oldlinux/bochs-2.2.1/bios #../bochs-2.2.1/bochs -q -f bochsrc-fd1-gdb.bxrc ../bochs-2.2.1/bochs -q -f bochsrc-fd1-gdb-my.bxrc
运行 ./run
即可启动新的系统映像。
默认生成的Image是通过floppyb加载根文件系统的,如果需要配合其 他Root Fs Image使用,则需要更改加载选项:
首先安装二进制文件修改工具,并运行:
sudo apt-get install ghex ghex2
打开build好的Image文件修改:
通过Gdb和Bochs GDB Stub调试内核,首先需要在构建bochs时打开gdb-stub选项:
cd bochs-2.2.1 ./configure --enable-gdb-stub make
运行前确保bochsrc文件开头包含:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
运行 bochs
:
cd linux-0.11-gdb-rh9-050619 ./run
可见:
========================================================================
Bochs x86 Emulator 2.2.1
Build from CVS snapshot on July 8, 2005
========================================================================
00000000000i[ ] reading configuration from bochsrc-fd1-gdb-my.bxrc
00000000000i[ ] Enabled gdbstub
00000000000i[ ] installing x module as the Bochs GUI
00000000000i[ ] using log file bochsout.txt
Waiting for gdb connection on localhost:1234
现在打开另外一个控制台终端,启动gdb:
cd linux-0.11-gdb-rh9-050619 cd mylinux gdb tools/system
在gdb的命令环境下加入断点:
(gdb) break main Breakpoint 1 at 0x6621: file init/main.c, line 110. (gdb) break init Breakpoint 2 at 0x6784: file init/main.c, line 169. (gdb) break init/main.c:111 Breakpoint 3 at 0x662d: file init/main.c, line 111. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00006621 in main at init/main.c:110 2 breakpoint keep y 0x00006784 in init at init/main.c:169 3 breakpoint keep y 0x0000662d in main at init/main.c:111
禁止拦截SIGSEGV信号:
(gdb) handle SIGSEGV nostop noprint pass Signal Stop Print Pass to program Description SIGSEGV No No Yes Segmentation fault
连接bochs的gdb-stub:
(gdb) target remote localhost:1234 Remote debugging using localhost:1234 warning: unrecognized item "ENN" in "qSupported" response 0x0000fff0 in sys_mkdir (pathname=0x0, mode=0) at namei.c:481 481 return -EPERM;
检查当前寄存器状态:
(gdb) info r eax 0x0 0 ecx 0x0 0 edx 0x543 1347 ebx 0x0 0 esp 0x0 0x0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0xfff0 0xfff0 <sys_mkdir+163> eflags 0x2 [ ] cs 0xf000 61440 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
看到加电后第一条指令为: 0xffff0 反汇编之:
(gdb) disassemble 0xffff0 0xfffff Dump of assembler code from 0xffff0 to 0xfffff: 0x000ffff0: ljmp $0x3730,$0xf000e05b 0x000ffff7: das 0x000ffff8: xor %dh,(%esi) 0x000ffffa: das 0x000ffffb: xor %dh,0xe4fc00 End of assembler dump.
这是一条bios跳转指令,单步执行这一条汇编语句,并查看CS:EIP:
(gdb) si do_execve (eip=0x0, tmp=0, filename=0x0, argv=0x0, envp=0x0) at exec.c:196 196 panic("execve called from supervisor mode"); (gdb) info r eax 0x0 0 ecx 0x0 0 edx 0x543 1347 ebx 0x0 0 esp 0x0 0x0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0xe05b 0xe05b <do_execve+49> eflags 0x2 [ ] cs 0xf000 61440 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
继续执行代码:
(gdb) cont Continuing. Breakpoint 3, main () at init/main.c:111 111 drive_info = DRIVE_INFO; (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000661c in main at init/main.c:110 breakpoint already hit 1 time 2 breakpoint keep y 0x00006784 in init at init/main.c:169 3 breakpoint keep y 0x0000662d in main at init/main.c:111 breakpoint already hit 1 time
可以发现有的时候 main
断点不会被中断,需要明确指
明代码行数 init/main.c:111
也可以在具体符号所对应地址处加断点,语法为:
(gdb) b *0x662d Note: breakpoint 3 also set at pc 0x662d. Breakpoint 4 at 0x662d: file init/main.c, line 111. (gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000661c in main at init/main.c:110 breakpoint already hit 1 time 2 breakpoint keep y 0x00006784 in init at init/main.c:169 3 breakpoint keep y 0x0000662d in main at init/main.c:111 breakpoint already hit 1 time 4 breakpoint keep y 0x0000662d in main at init/main.c:111
符号对应的地址可以在目录 mylinux/System.map
中找到。
至此,一个完整能够修改调试Linux 0.11的环境就建立完毕。
PS,最近又看到一些网上资料,发现已经有人将Linux 0.11的代码移植到GCC-4.x, 请参考的如下帖子:
GCC4.3.4 - http://www.blogjava.net/menglee/archive/2011/08/02/355555.html
GCC4.x - http://www.oldlinux.org/oldlinux/viewthread.php?tid=11651&extra=page%3D1
GCC4.x(推荐) - http://www.oldlinux.org/oldlinux/viewthread.php?tid=13681&extra=page%3D1
1. Old Linux - http://www.oldlinux.org
Wiki comments powered by Disqus