目录
当按下计算机开机键,BIOS 为我们做完一系列检测和初始化工作后,就会尝试从存储设备的第一个扇区,加载可以引导操作系统启动的程序,并将执行权交给它,所以这个特殊的扇区被称为引导扇区(Boot Sector)。引导扇区上存储的内容,被称为主引导记录(MBR,Master Boot Record)。记录在引导扇区上的这段程序,我们称为 MBR 程序。
原文链接:https://blog.csdn.net/word_world/article/details/129105044
1 编写 MBR 程序
为使 MBR 程序可以被 BIOS 正确加载,需要满足三个条件 :
- 位于存储设备第一个扇区
- 长度为
512
字节 - 最后两字节内容为
0x55
0xaa
下面是一个极简的 mbr 程序,从 BIOS 获得执行权后,并不做实质的引导,只是打印一个字符串,然后进入死循环。
.code16
.global main
main:
// 初始化寄存器 ds=cs,es=cs
mov %cs, %ax
mov %ax, %ds
mov %ax, %es
// puts(str, len, 20, 8)
mov $str, %bp
mov $len, %cx
mov $0x14, %dh # 行号 +20
mov $0x08, %dl # 缩进 +8 字符
call puts
// while(true);
jmp .
// void puts(void *str, int len, int row, int col)
// @str <- %bp
// @len <- %cx
// @row <- %dh
// @col <- %dl
puts:
mov $0x1301, %ax # 文字属性
mov $0xc, %bx # 颜色
int $0x10 # 调用 0x10 中断
ret
str:
.ascii "Hello Lubanix"
len = . - str # str长度 = 当前地址 - str首地址
// 引导扇区结束标志 0xaa55
.org 510
magic:
.byte 0x55, 0xaa
1.1 GNU/as 汇编器
# 汇编得到目标文件 mbr.o
as -o boot/mbr.o boot/mbr.s
# 链接 mbr.o 得到二进制文件 mbr.bin
ld -e main \
--oformat=binary \
-o boot/mbr.bin \
boot/mbr.o \
--Ttext=0
ld 选项
- -o 输出文件
- --oformat 输出格式
- --Text 代码段起始地址
- -e 程序入口函数
1.2 AT&T 汇编代码说明
GNU/as 采用 AT&T 汇编,它与 Intel 汇编有一些不同,特别是源操作数和目标操作数的顺序是相反的。所以这一节对整个 mbr 程序做逐行的说明。
行号 | 语法 | 说明 |
---|---|---|
1 | .code16 | 使用 16 位指令 |
2 | .global | 导出符号 |
5 | mov src, dst | 数据移动 |
5~7 | %reg | 将寄存器 cs 的值赋给寄存器 ds、es |
9 | $tag | 将 str 地址放入寄存器 bp,为 0x10 中断设置字符串地址 |
10 | $var | 将变量 len 的值放入寄存器 cx,为0x10中断设置字符串长度 |
11 | %dl | 访问 dx 寄存器低 16(0~15) 位,为 0x10 中断设置行偏移 |
12 | %dh | 访问 dx 寄存器高 16(16~31) 位,为0x10中断设置列偏移 |
13 | call | 调用 puts 函数,显示字符串 |
15 | jmp . | 跳转到当前(指令首)地址 |
23 | %ax | ax 为 0x10 中断,设置文字属性 |
24 | %bx | bx 为 0x10 中断,设置文字颜色 |
25 | ret | puts 函数返回 |
29 | .ascii | 定义 ascii 字符串 |
30 | len = . - str | 计算 str 长度(当前指令首地址-str起始地址),存入变量 len |
33 | .org 510 | 将下面的指令对齐到地址 510 |
35 | .byte | 在地址 510、511 位置填入两个字节 0x55、0xaa |
2 调试运行
2.1 用 Bochs 调试运行
# 制作 cd 镜像文件 lubanix-1.0.iso
mkdir -p lubanix-1.0/boot/
mkisofs -r -q \
-input-charset=utf-8 \
-o lubanix-1.0.iso \
lubanix-1.0
# 将 mbr.bin 写进 cd 镜像文件
cp boot/mbr.bin lubanix-1.0/boot/
mkisofs -R -q \
-input-charset=utf-8 \
-no-emul-boot \
-boot-load-seg 0x7c00 \
-b boot/mbr.bin \
-c boot/mbr.catalog \
-o lubanix-1.0.iso \
lubanix-1.0
# 启动 bochs
bochs -q \
-rc bochs/bochs_debug \
-f bochs/bochsrc_cd
bochs 选项
- -rc 指定调试脚本
写入 bochs 自定义 debug 命令
如b 0x7c00:0
断点在 mbr 程序入口 - -f 指定 bochs 启动配置
如boot: cdrom
指定光驱引导
2.2 用 QEMU 运行
# 链接 mbr.o 为 qemu 准备 mbr.bin.qemu 程序
ld -e main \
--oformat=binary \
-o boot/mbr.bin.qemu \
boot/mbr.o \
--Ttext=0x7c00
# 使用 qemu 运行 mbr.bin.qemu 程序
/usr/bin/qemu-system-x86_64 \
boot/mbr.bin.qemu \
-S -s
qemu 选项
- -s / -gdb tcp::1234
启用 gdb 调试功能 - -S 虚拟机启动后挂起
等待 qemu-monitor 或 gdb 的 continue 命令
2.3 用 gdb 调试
# 用 gdb 连接 qemu 调试 mbr.bin.qemu 程序
gdb boot/mbr.bin.qemu \
-ex "target remote:1234" \
-ex "b *0x7c00" \
-ex "c" \
-ex "layout asm"
在 qemu 虚拟环境下,可以很方便的用 gdb 对操作系统程序进行调试,节约了 monitor 调试命令的学习成本,这很符合 gnu 精神。作为后端程序员的神器,gdb 值得拥有单独的篇幅,来展示其对服务器程序的调试和干预能力。
参考
- 于渊, Orange’s 一个操作系统的实现.
- 赵炯, Linux 内核 0.12完全注释.
- 蒋炎岩, 操作系统:设计与实现 (2022 春季学期)
- https://linux.die.net/man/1/ld
- gnu/as-2.9.1 manual: Assembler Directives
- https://wiki.osdev.org/QEMU
- https://wiki.gentoo.org/wiki/QEMU/Options
- QEMU/Monitor
- Debugging with QEMU
- (QEMU)调试引导扇区
- How to disassemble 16-bit x86 boot sector code in GDB
写在最后
感谢大家评论、转发、点赞、收藏
欢迎质疑、批评、斧正
一起辩论,追求卓越