您的位置:首页 > 其它

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

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

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

#define BOOTSEG 0x07C0
.code16
.section ".bstext", "ax"
.global bootsect_start
bootsect_start:

# Normalize the start address
ljmp    $BOOTSEG, $start2

start2:
movw    %cs, %ax
movw    %ax, %ds
movw    %ax, %es
movw    %ax, %ss
xorw    %sp, %sp
sti
cld

movw    $bugger_off_msg, %si

msg_loop:
lodsb
andb    %al, %al
jz      bs_die
movb    $0xe, %ah
movw    $7, %bx
int     $0x10
jmp     msg_loop

bs_die:
# Allow the user to press a key, then reboot
xorw    %ax, %ax
int     $0x16
int     $0x19

# int 0x19 should never return.  In case it does anyway,
# invoke the BIOS reset code...
ljmp    $0xf000,$0xfff0

bugger_off_msg:
.ascii  "Hello Boot!\r\n"
.ascii  "by harvey\r\n"
.ascii  "\n"
.byte   0

.org 510
.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为链接脚本,内容如下:

OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(bootsect_start)

SECTIONS
{
. = 0;
.boot : {*(.bstext)}
. = ASSERT(. <= 512, "Boot too big!");
}

此脚本指示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命令行,依次输入以下命令。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: