Linux 0.11 Build and Debug Env. on Debian

最近购买并阅读了若干讲Linux早期内核的书,包括赵炯博士的 《Linux内核完全剖析(基于0.12内核)》以及新设计团队的《Linux 内核设计的艺术—图解Linux操作系统架构设计与实现原理》,感觉对 Linux内核的整体概貌有了大体上的感官认识。

为了搭建起一个间便的修改,调试,运行Linux早期内核的学习环境, 费了不少周折,最后完成了Debian下的比较满意的环境。这里记录一 下详细步骤,对此感兴趣的同志们可以参考下。

另外,本文还参考了赵炯基于Linux 0.11的《Linux内核完全注释(修 正版v3.0)》一书的电子版本,特此感谢。

Host OS Environment

看到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/>.

Prepare Bochs

Linux早期版本运行在方便调试的x86模拟器Bochs上,Bochs对我们的 运行环境影响较大,本文中搭建的Linux 0.11构建环境只能运行在 Bochs 2.2.1之上。

Download Bochs

http://sourceforge.net/projects/bochs

(我们使用 bochs-2.2.1.tar.gz以及bochs-2.4.5.tar.gz)

Build Bochs

cd bochs-2.4.5
make
make install     #optional

Run Linux 0.11 and 0.12 Dev. Build

如果你只是想试运行一下早期的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]#

Build by GCC 3.2.2 and Debug with Bochs and GDB

Dowload Root File System

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

Download Linux 0.11 Source

因为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中找 到。

Build Bochs with GDB Stub Enabled

Change bochs-2.2.1/iodev/harddrv.h

需要注释掉 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);

Patch Bochs 2.2.1 — Fixing the "Signal 0" Issue

参考: 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而中断,导致无法调试内核的问题。

Build Bochs 2.2.1

这里必须使用Bochs 2.2.1作为调试运行平台,否则从软盘装载System的时 候会不断出现“Reset-floppy Called"错误。

这里我们只构建但并不安装这个build:

cd bochs-2.2.1
./configure --enable-gdb-stub
make
# make install

Run Bochs with GDB stub

在Bochsrc中首行指定:

gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0

Install BIN86

安装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

也可以从如下地址下载:

ftp://ftp.funet.fi/

http://oldlinux.org/Linux.old/bin/bin86-0.16.9.tar

Install GCC 3.2.2-5 from RH-9

直接用 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

Build Linux 0.11 Source

解压缩

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可以配合下节的文件系统 一起使用。

Build Linux 0.11 Source with Debug Info

下载加入调试信息的源代码包:

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即可启动新的系统映像。

Modify the root fs dev

默认生成的Image是通过floppyb加载根文件系统的,如果需要配合其 他Root Fs Image使用,则需要更改加载选项:

首先安装二进制文件修改工具,并运行:

sudo apt-get install ghex
ghex2

打开build好的Image文件修改:

Debug Linux 0.11 using Bochs and GDB

通过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的环境就建立完毕。

Build by GCC 4.x and Debug with Bochs and GDB

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


References:

1. Old Linux - http://www.oldlinux.org

Wiki comments powered by Disqus