自己动手实现操作系统引导程序(OS bootloader)——借助QEMU/GDB/losetup/dd等工具

    引导程序可以认为是PC加电启动后运行的第一段代码,它是一段长度为512字节的16位运行于实模式的代码。事实上,机器启动后会首先运行0xFFFF0处(也有的资料说是0xFFFFFFF0,BIOS这块我也不熟:-( )ROM中的BIOS代码,之后会跳转到0x07C00处执行引导程序。

        1,首先给出一段完整的示例代码,此代码只为说明引导程序的执行流程,不具有加载实际操作系统的功能,只是在屏幕上打印一段信息。

  1. #define BOOTSEG 0x07C0  
  2.           .code16  
  3.           .section ".bstext""ax"  
  4.           .global bootsect_start  
  5.   bootsect_start:  
  6.     
  7.           # Normalize the start address  
  8.           ljmp    $BOOTSEG, $start2  
  9.     
  10.   start2:  
  11.           movw    %cs, %ax  
  12.           movw    %ax, %ds  
  13.           movw    %ax, %es  
  14.           movw    %ax, %ss  
  15.           xorw    %sp, %sp  
  16.           sti  
  17.           cld  
  18.     
  19.           movw    $bugger_off_msg, %si  
  20.     
  21.   msg_loop:  
  22.           lodsb  
  23.           andb    %al, %al  
  24.           jz      bs_die  
  25.           movb    $0xe, %ah  
  26.           movw    $7, %bx  
  27.           int     $0x10  
  28.           jmp     msg_loop  
  29.     
  30.   bs_die:  
  31.           # Allow the user to press a key, then reboot  
  32.           xorw    %ax, %ax  
  33.           int     $0x16  
  34.           int     $0x19  
  35.     
  36.           # int 0x19 should never return.  In case it does anyway,  
  37.           # invoke the BIOS reset code...  
  38.           ljmp    $0xf000,$0xfff0  
  39.     
  40.   bugger_off_msg:  
  41.           .ascii  "Hello Boot!\r\n"  
  42.           .ascii  "by harvey\r\n"  
  43.           .ascii  "\n"  
  44.           .byte   0  
  45.   
  46.           .org 510  
  47.           .word 0xAA55  
                这段代码有几个地方需要注意:

                1).code16伪指令指示汇编器将此段代码汇编成16位代码。

                2)ljmp    $BOOTSEG, $start2指令中,BOOTSEG定义为0x07C0,并假设标号start2在所在代码段中的偏移为S。我们知道实模式地址模式为:段基址*16+偏移,那么这条ljmp指令执行后,控制会跳转到0x7C00+S处。又因为整个引导代码在之前会被加载到0x7C00处,所以此时控制“正好”跳转到标号start2处代码。

                3)代码末端的伪指令.org 510,将位置计数器(location counter)设置为510,那么紧跟其后的0xAA55就被设置在第511,512字节。0xAA55是BIOS识别并加载引导程序的标志。

        2,编译链接此程序

               1)用as命令汇编生成目标文件。

                       as -gstabs -o boot.o boot.S

               2)用ld命令链接生成可执行文件。

                       ld -o boot boot.o -Tboot.ld

                       boot.ld为链接脚本,内容如下:

  1. OUTPUT_FORMAT("elf32-i386""elf32-i386""elf32-i386")  
  2. OUTPUT_ARCH(i386)  
  3. ENTRY(bootsect_start)  
  4.   
  5. SECTIONS  
  6. {  
  7.     . = 0;  
  8.     .boot : {*(.bstext)}  
  9.     . = ASSERT(. <= 512, "Boot too big!");  
  10. }     

               此脚本指示ld将目标文件boot.o中的.text段链接拷贝到可执行文件boot中的.boot段,并且.boot段的起始VMA地址为0。.boot代码段就是我们需要的引导程序代码。更多链接脚本语法参考http://sourceware.org/binutils/docs/ld/index.html

       3,制作引导软盘镜像

              1)用dd命令新建软盘镜像flp.img。

                      dd if=/dev/zero of=flp.img bs=512 count=2880

              2)用losetup命令将flp.img与loop设备关联,这样我们可以通过/dev/loop3设备,像操作真实软盘样操作flp.img文件。

                      losetup /dev/loop0 flp.img

              3)将可执行文件boot中的引导代码写入flp.img的第一个扇区。首先我们要确定.boot段在可执行文件boot中的位置,注意此位置不是指.boot段的VMA地址,而是指其存储在磁盘文件boot中的物理位置,我们用objdump命令查看:

                      objdump -h boot

                      输出如下:

                      从File off栏可知.boot段位于距boot文件头0x00001000处。然后用dd命令将.boot段写入flp.img的第一个扇区。

                      dd if=boot ibs=512 skip=8 of=/dev/loop0 obs=512 seek=0 count=1

                      其中skip * ibs = 0x00001000为待写数据,即.boot段,在输入源文件,即boot文件中的偏移距离,seek * obs = 0为待写数据将要被写入输出目标文件,即flp.img文件的起始位置,即从flp.img文件头字节开始写入数据,count*obs=512为待写数据的长度。

                      到这里,引导软盘镜像准备好了。关于dd,losetup,objdump命令更多信息可借助man命令,也可参考我前一篇文章

        4,借助QEMU从引导软盘镜像启动系统。

                运行如下命令启动QEMU。

                qemu -boot order=a -fda /dev/loop0

                此时,我们应该可以在QEMU模拟器的窗口中看到Hello Boot!字样。


                QEMU也提供单步调试功能。配合GDB,可以方便的调试引导程序。先用如下命令启动QEMU。

                qemu -s -S -boot order=a -fda /dev/loop0

                其中-s -S选项与gdb调试有关,运行此命令后QEMU模拟器会停止并等待gdb发送单步执行命令。更多QEMU信息可参考http://qemu.weilnetz.de/qemu-doc.html

                在另一个终端调用gdb命令进入gdb命令行,依次输入以下命令。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值