要开发自己的操作系统,首先就要搭建一个自己的调试开发环境,这里以Ubuntu系统(作者的是ubuntu 12.04.3)为例,来说明如何搭建交叉编译环境,有了交叉编译环境,才可以将系统内核编译为目标机子上...

    要开发自己的操作系统,首先就要搭建一个自己的调试开发环境,这里以Ubuntu系统(作者的是ubuntu 12.04.3)为例,来说明如何搭建交叉编译环境,有了交叉编译环境,才可以将系统内核编译为目标机子上可以运行的机器指令码,这里要搭建的是x86 CPU (i586及以上的处理器)的交叉编译环境(这里的做法不能用于x86_64的目标平台)。

    我将GCC之类的编译器的源代码放在Dropbox和sourceforge里,下载地址如下:

    Dropbox地址:点此进入Dropbox网盘 打开该链接后,从zenglOX_v0.0.1的目录里将所有压缩包下载下来,其中的binutils-2.23.1.tar.bz2里包含了汇编链接相关的工具集,gcc-4.7.2.tar.bz2为GCC编译器的源代码,编译GCC的源代码又需要用到gmp-5.0.5.tar.xz、mpfr-3.0.1.tar.xz及mpc-1.0.1.tar.gz这三个压缩包里的代码,bochs-2.6.tar.gz为bochs虚拟机的源代码,bochs需要编译为支持gdb调试的虚拟机,所以要用源代码来进行编译安装。zenglOX_v0.0.1目录里还有个zenglOX_v0.0.1.zip的压缩包,该压缩包里是v0.0.1版的内核的源代码,该版本目前只有启动代码,没有实质性的代码,只是为了搭建调试环境而准备的。

    sourceforge的地址:https://sourceforge.net/projects/zenglox/files 该链接里有一个zenglOX_v0.0.1的目录,打开该目录,可以看到和Dropbox中zenglOX_v0.0.1里一样的7个压缩包。

    另外还有个github地址:https://github.com/zenglong/zenglOX 该链接里只包含git提交上来的zenglOX的源代码。

    搭建交叉编译环境最好是在命令行下切换到root用户,在Ubuntu里默认没有给root设置密码,可以通过以下命令来设置root密码并切换到root用户:

$ sudo passwd root

$ su -

    第一个sudo passwd root命令输入后,会提示输入两次root的新密码,设置密码后,就可以用su -来切换到root ,下面搭建交叉编译工具的过程都用的是root用户。

    下面使用mkdir命令新建/mnt/zenglOX/opt/cross目录:

mkdir -p /mnt/zenglOX/opt/cross

    最后生成的交叉编译工具集都会安装在/mnt/zenglOX/opt/cross目录内,当然你也可以安装到别的目录里,不过如果安装在其他目录里,在编译zenglOX时,就需要对makefile文件里的相关路径进行修改。

    先将前面的几个和编译链接调试有关的压缩包放置到/mnt/zenglOX目录里,然后使用如下命令进行解压:

[email protected]:/mnt/zenglOX# tar xvf binutils-2.23.1.tar.bz2
[email protected]:/mnt/zenglOX# tar xvf gcc-4.7.2.tar.bz2
[email protected]:/mnt/zenglOX# tar xvf gmp-5.0.5.tar.xz
[email protected]:/mnt/zenglOX# tar xvf mpfr-3.0.1.tar.xz
[email protected]:/mnt/zenglOX# tar xvf mpc-1.0.1.tar.gz
[email protected]:/mnt/zenglOX# tar xvf bochs-2.6.tar.gz

    由于编译gcc需要用到gmp、mpfr和mpc的源代码,所以首先进入解压后的gcc目录,在该目录里建立一些符号链接来指向之前解压的gmp、mpfr和mpc的源代码的位置:

[email protected]:/mnt/zenglOX# cd gcc-4.7.2
[email protected]:/mnt/zenglOX/gcc-4.7.2# ln -s ../gmp-5.0.5 gmp
[email protected]:/mnt/zenglOX/gcc-4.7.2# ln -s ../mpc-1.0.1 mpc
[email protected]:/mnt/zenglOX/gcc-4.7.2# ln -s ../mpfr-3.0.1 mpfr
[email protected]:/mnt/zenglOX/gcc-4.7.2# cd ..
[email protected]:/mnt/zenglOX#

    源代码准备好后,首先组建用于x86 CPU的binutils :

[email protected]:/mnt/zenglOX# mkdir binutils-build
[email protected]:/mnt/zenglOX# cd binutils-build
[email protected]:/mnt/zenglOX/binutils-build# ../binutils-2.23.1/configure --prefix=/mnt/zenglOX/opt/cross --target=i586-elf --disable-nls

    上面通过mkdir binutils-build命令创建一个binutils-build目录,然后cd进入该目录进行相关组建工作,先用configure配置脚本生成Makefile文件,configure后面的参数--prefix=/mnt/zenglOX/opt/cross用于指定安装目录,这样后面make和make install安装后,binutils的工具集就会位于/mnt/zenglOX/opt/cross目录内,--target=i586-elf参数用于指定binutils工具集链接生成的目标二进制为用于i586的ELF文件格式,这是一种比较通用的格式,可以在很多实际的机子上运行,--disable-nls表示不使用本地语言支持,这样可以让工具集体积更小,所有错误信息都用英文来显示。

    在configure配置脚本生成Makefile文件后,就可以使用make和make install命令来进行编译安装:

[email protected]:/mnt/zenglOX/binutils-build# make
[email protected]:/mnt/zenglOX/binutils-build# make install

    make install安装完binutils后,就可以在opt/cross/bin/目录里查看到生成的as、ld之类的工具:

[email protected]:/mnt/zenglOX/binutils-build# ls ../opt/cross/
bin/      i586-elf/ lib/      share/ 
[email protected]:/mnt/zenglOX/binutils-build# ls ../opt/cross/bin/
i586-elf-addr2line  i586-elf-elfedit  i586-elf-nm       i586-elf-readelf
i586-elf-ar         i586-elf-gprof    i586-elf-objcopy  i586-elf-size
i586-elf-as         i586-elf-ld       i586-elf-objdump  i586-elf-strings
i586-elf-c++filt    i586-elf-ld.bfd   i586-elf-ranlib   i586-elf-strip

    下面组建gcc交叉编译工具:

[email protected]:/mnt/zenglOX# mkdir gcc-build
[email protected]:/mnt/zenglOX# cd gcc-build
[email protected]:/mnt/zenglOX/gcc-build# ../gcc-4.7.2/configure --prefix=/mnt/zenglOX/opt/cross --target=i586-elf --enable-languages=c,c++ --disable-nls --without-headers

    也是在/mnt/zenglOX目录里建立一个gcc-build目录用于组建工作,cd进入该目录,通过gcc-4.7.2里的configure脚本来组建Makefile文件,configure后面的参数里--prefix、--target及--disable-nls这三个和之前的binutils的一致,--enable-languages=c,c++参数表示让生成的GCC支持C和C++语言的编译,如果省略该参数,则make时就会将GCC能支持的所有语言都进行组建,这样会耗费很多时间,而且zenglOX内核开发也用不到其他的语言。--without-headers参数表示不使用C标准库,因为内核开发时,里面的所有函数都需要自己去写,该参数可以让gcc生成的文件不依赖于开发主机里的库,这样才能生成可以独立运行的系统内核。

    configure生成Makefile后,就可以用make命令来进行编译安装:

[email protected]:/mnt/zenglOX/gcc-build# make all-gcc
[email protected]:/mnt/zenglOX/gcc-build# make install-gcc

    这里用的是make all-gcc进行编译,make install-gcc进行安装,在编译安装后,就可以在opt/cross/bin/目录里查看到生成的gcc工具:

[email protected]:/mnt/zenglOX/gcc-build# ls ../opt/cross/bin/
i586-elf-addr2line  i586-elf-g++         i586-elf-gprof    i586-elf-readelf
i586-elf-ar         i586-elf-gcc         i586-elf-ld       i586-elf-size
i586-elf-as         i586-elf-gcc-4.7.2   i586-elf-ld.bfd   i586-elf-strings
i586-elf-c++        i586-elf-gcc-ar      i586-elf-nm       i586-elf-strip
i586-elf-c++filt    i586-elf-gcc-nm      i586-elf-objcopy
i586-elf-cpp        i586-elf-gcc-ranlib  i586-elf-objdump
i586-elf-elfedit    i586-elf-gcov        i586-elf-ranlib

    生成gcc后还需要生成对应的libgcc:

[email protected]:/mnt/zenglOX/gcc-build# make all-target-libgcc
[email protected]:/mnt/zenglOX/gcc-build# make install-target-libgcc

    上面使用make all-target-libgcc命令来编译libgcc,使用make install-target-libgcc安装libgcc,编译安装后,可以在opt/cross/lib目录里查看到生成的libgcc:

[email protected]:/mnt/zenglOX/gcc-build# ls ../opt/cross/lib/gcc/i586-elf/4.7.2/ -l
total 452
-rw-r--r-- 1 root root   2352  2月 23 16:42 crtbegin.o
-rw-r--r-- 1 root root   1316  2月 23 16:42 crtend.o
drwxr-xr-x 2 root root   4096  2月 23 16:42 include
drwxr-xr-x 2 root root   4096  2月 23 16:26 include-fixed
drwxr-xr-x 3 root root   4096  2月 23 16:36 install-tools
-rw-r--r-- 1 root root 403240  2月 23 16:42 libgcc.a
-rw-r--r-- 1 root root  30262  2月 23 16:42 libgcov.a
drwxr-xr-x 3 root root   4096  2月 23 16:36 plugin

    先切换回/mnt/zenglOX目录:

[email protected]:/mnt/zenglOX/gcc-build# cd ..
[email protected]:/mnt/zenglOX#

    下面组建bochs虚拟机:

[email protected]:/mnt/zenglOX# mkdir bochs-build
[email protected]:/mnt/zenglOX# cd bochs-build
[email protected]:/mnt/zenglOX/bochs-build# ../bochs-2.6/configure --enable-gdb-stub

    同样是在/mnt/zenglOX目录里新建一个bochs-build目录,然后cd进入该目录,接着使用之前解压的bochs-2.6里的configure脚本来生成Makefile文件,--enable-gdb-stub参数表示生成的bochs支持gdb调试。

    由于生成bochs时需要g++(Ubuntu默认没装g++)和xorg-dev,没有xorg-dev则make时会提示error: X11/extensions/Xrandr.h: No such file or directory的错误,所以先安装g++和xorg-dev:

[email protected]:/mnt/zenglOX/bochs-build# apt-get install g++
[email protected]:/mnt/zenglOX/bochs-build# apt-get install xorg-dev

    然后使用make和make install来进行安装:

[email protected]:/mnt/zenglOX/bochs-build# make
[email protected]:/mnt/zenglOX/bochs-build# make install

    以上就是交叉编译环境及bochs虚拟机的搭建过程。

    下面就可以编译和调试zenglOX的源代码了,编译zenglOX就不需要root权限了。

    解压zenglOX_v0.0.1.zip:

[email protected]:~/Downloads$ unzip zenglOX_v0.0.1.zip -d zenglOX_v0.0.1
[email protected]:~/Downloads$ cd zenglOX_v0.0.1
[email protected]:~/Downloads/zenglOX_v0.0.1$

    在zenglOX_v0.0.1目录里可以看到makefile文件,在该文件里有交叉编译环境的路径设置信息:

#makefile for zenglOS

GCC_PATH = /mnt/zenglOX/opt/cross/bin
AS = $(GCC_PATH)/i586-elf-as
CC = $(GCC_PATH)/i586-elf-gcc

    上面的GCC_PATH指向交叉编译工具的bin路径,AS为GNU汇编器的名称,CC为GCC编译器的名称,如果你的路径不在此位置,则可以进行相应的调整。

    在路径信息正确的情况下,直接make :

[email protected]:~/Downloads/zenglOX_v0.0.1$ make
/mnt/zenglOX/opt/cross/bin/i586-elf-as zlox_boot.s -o zlox_boot.o -gdwarf-2 -g3
/mnt/zenglOX/opt/cross/bin/i586-elf-gcc -c zlox_kernel.c -o zlox_kernel.o -std=gnu99 -ffreestanding -gdwarf-2 -g3 -Wall -Wextra
/mnt/zenglOX/opt/cross/bin/i586-elf-gcc -T linker.ld -o zenglOX.bin -ffreestanding -gdwarf-2 -g3 -nostdlib zlox_boot.o zlox_kernel.o
[email protected]:~/Downloads/zenglOX_v0.0.1$

    make后会生成zenglOX.bin文件,在系统已安装grub2的情况下,make iso生成zenglOX.iso镜像文件:

[email protected]:~/Downloads/zenglOX_v0.0.1$ make iso
cp zenglOX.bin isodir/boot/zenglOX.bin
cp grub.cfg isodir/boot/grub/grub.cfg
grub-mkrescue -o zenglOX.iso isodir
Enabling BIOS support ...
xorriso 1.1.8 : RockRidge filesystem manipulator, libburnia project.
 

    make iso命令会生成zenglOX.iso镜像文件,该iso镜像文件就可以用bochs或virtualBox来启动运行,如果在make iso时,出现grub-mkrescue: xorriso: not found的错误信息,则根据错误提示安装xorriso,Ubuntu下直接输入sudo apt-get install xorriso。

    接着就可以用startBochs脚本来启动bochs,不过在一开始,startBochs不具有可执行权限,可以用chmod来设置可执行权限:

[email protected]:~/Downloads/zenglOX_v0.0.1$ chmod +x startBochs

    另外在运行startBochs脚本前,需要先查看下bochsrc.txt文件,该文件是bochs的启动配置文件:

romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0xfffe0000
vgaromimage: file="/usr/local/share/bochs/VGABIOS-elpin-2.40"

    上面是bochsrc.txt文件内容的片段,romimage要指向你系统里正确的BIOS-bochs-latest的路径,vgaromimage要指向你系统里正确的VGABIOS-elpin-2.40的路径。

    接着就可以运行startBochs脚本:

[email protected]:~/Downloads/zenglOX_v0.0.1$ ./startBochs
========================================================================
                        Bochs x86 Emulator 2.6
            Built from SVN snapshot on September 2nd, 2012
                  Compiled on Feb 23 2014 at 16:58:20
========================================================================
00000000000i[     ] reading configuration from bochsrc.txt
00000000000i[     ] Enabled gdbstub
00000000000e[     ] bochsrc.txt:16: 'vga_update_interval' will be replaced by new 'vga: update_freq' option.
00000000000e[     ] bochsrc.txt:25: 'i440fxsupport' will be replaced by new 'pci' option.
00000000000i[     ] installing x module as the Bochs GUI
00000000000i[     ] using log file bochsout.log
Waiting for gdb connection on port 1234
 

    当出现Waiting for gdb connection on port 1234的字样时,就说明bochs此时可以接受gdb的调试了。

    再重新开一个shell终端,切换到zenglOX_v0.0.1的目录,然后使用gdb命令进行调试:

[email protected]:~/Downloads/zenglOX_v0.0.1$ gdb -q zenglOX.bin
Reading symbols from /home/zengl/Downloads/zenglOX_v0.0.1/zenglOX.bin...done.
(gdb) break *_zlox_boot_start
Breakpoint 1 at 0x100020: file zlox_boot.s, line 26.
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb) info breakpoints
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x00100020 zlox_boot.s:26
(gdb) c
Continuing.
 

    上面通过gdb -q zenglOX.bin来进入调试状态,在调试输入框里,通过break *_zlox_boot_start命令在zlox_boot.s文件的26行设置断点。

    然后必须输入如上所示的target remote localhost:1234命令,这样在之前startBochs的shell终端里Waiting for gdb connection on port 1234的消息下面才会出现Connected to 127.0.0.1的消息,以表示bochs和gdb调试器成功连接上了。

    在gdb里通过c命令继续执行,接着bochs x86的图形界面的模拟器会进入grub启动画面:



图1

    回车符进入zenglOX后,gdb的shell就会停在_zlox_boot_start下的第一条指令处:

Breakpoint 1, code () at zlox_boot.s:26
26      pushl %ebx
(gdb)

    接着就可以用s命令单步调试了:

(gdb) s
27      cli
(gdb) s
28      call zlox_kernel_main
(gdb) s
zlox_kernel_main (mboot_ptr=0x10000) at zlox_kernel.c:4
4        return (int)mboot_ptr;
(gdb)

    可以看到zlox_boot.s启动汇编代码通过call zlox_kernel_main指令进入zlox_kernel.c里的zlox_kernel_main函数,目前该函数里什么也没有,直接返回。

    以上就是zenglOX v0.0.1的编译调试过程。

    下面是zlox_boot.s里的多启动头部分:

#zlox_boot.s -- zenglOX kernel start assemble
.equ ZLOX_MBOOT_PAGE_ALIGN,1 #Load Kernel on a page boundary
.equ ZLOX_MBOOT_GETMEM_INFO,1<<1 #Tell MBoot provide your kernel with memory info
.equ ZLOX_MBOOT_MAGIC,0x1BADB002 #Multiboot Magic value

.equ ZLOX_MBOOT_FLAGS,ZLOX_MBOOT_PAGE_ALIGN | ZLOX_MBOOT_GETMEM_INFO
.equ ZLOX_MBOOT_CHECKSUM,-(ZLOX_MBOOT_MAGIC + ZLOX_MBOOT_FLAGS)

.section .zlox_multiboot
.align 4
.global _zlox_boot_mb_header
_zlox_boot_mb_header:
  .long ZLOX_MBOOT_MAGIC
  .long ZLOX_MBOOT_FLAGS
  .long ZLOX_MBOOT_CHECKSUM

  .long _zlox_boot_mb_header
  .long _code
  .long _bss
  .long _end
  .long _zlox_boot_start

    上面是grub之类的多系统启动加载器的标准启动汇编代码,该启动头的结构如下:
	+-------------------+
0	| magic: 0x1BADB002 |	(required)
4	| flags		    |	(required)
8	| checksum	    |	(required)
	+-------------------+
8	| header_addr	    |	(present if flags[16] is set)
12	| load_addr	    |	(present if flags[16] is set)
16	| load_end_addr	    |	(present if flags[16] is set)
20	| bss_end_addr	    |	(present if flags[16] is set)
24	| entry_addr	    |	(present if flags[16] is set)
	+-------------------+
    详情参考http://www.uruk.org/orig-grub/boot-proposal.html里的文章。    

    v0.0.1里还有一个linker.ld文件,该文件的内容如下:

/* The bootloader will look at this image and start execution at the symbol
   designated as the entry point. */

ENTRY(_zlox_boot_start)

/* Tell where the various sections of the object files will be put in the final
   kernel image. */

SECTIONS
{
    /* Begin putting sections at 1 MiB, a conventional place for kernels to be
       loaded at by the bootloader. */

    . = 1M;

    /* First put the multiboot header, as it is required to be put very early
       early in the image or the bootloader won't recognize the file format.
       Next we'll put the .text section. */

    .text BLOCK(4K) : ALIGN(4K)
    {
        *(.zlox_multiboot)
        code = .; _code = .; __code = .;
        *(.text)
    }

    /* Read-only data. */
    .rodata BLOCK(4K) : ALIGN(4K)
    {
        rodata = .; _rodata = .; __rodata = .;
        *(.rodata)
    }

    /* Read-write data (initialized) */
    .data BLOCK(4K) : ALIGN(4K)
    {
        data = .; _data = .; __data = .;
        *(.data)
    }

    /* Read-write data (uninitialized) and stack */
    .bss BLOCK(4K) : ALIGN(4K)
    {
        bss = .; _bss = .; __bss = .;
        *(COMMON)
        *(.bss)
    }

    end = .; _end = .; __end = .;
    /* The compiler may produce other sections, by default it will put them in
       a segment with the same name. Simply add stuff here as needed. */

}
   
    该文件规划了zenglOX内核镜像的结构,例如 . = 1M;表示整个内核会被grub加载到1M的内存地址,从1M位置开始先是多启动头,然后是代码段,只读数据段,可读写数据段, 未初始化数据段,end表示内核结束位置。

    内核开发相关参考资料如下:     以上开发过程仅供个人学习研究,起参考作用,作者发布在这里仅仅为了备忘,因为之前在slackware下搭建了交叉环境,现又在Ubuntu下搭建环境,由于步骤有点多,所以将Ubuntu下的搭建过程记录下来,以防止以后忘记了。

    OK,就到这里,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: zenglOX v0.0.2 VGA输出显示字符串

上一篇: 暂无

相关文章

zenglOX v0.0.11 ELF format(ELF可执行文件格式)与execve系统调用

zenglOX v0.0.5 分页

zenglOX v0.0.10 Keyboard(获取键盘的输入)

zenglOX v0.0.9 User Mode(用户模式)

zenglOX v0.0.8 Multitask(多任务)

zenglOX v0.0.7 VFS(虚拟文件系统)与initrd(初始化ram disk)