AT&T 语法引导扇区与 Setup 程序开发:Ubuntu 20.04 全流程实现(含工具链与镜像构建)
在操作系统启动流程中,引导扇区(bootsect)是 BIOS 加载的第一道程序,而 setup 程序则负责后续初始化工作。本文将基于 Ubuntu 20.04 环境,使用 AT&T 语法开发完整的引导扇区与 setup 程序组合,详细介绍工具链配置、镜像构建流程及 QEMU 运行验证,为内核开发提供基础启动框架。
一、开发环境与工具链详情
1. 核心工具链清单及版本(Ubuntu 20.04 验证通过)
工具名称 | 作用说明 | 验证版本示例 |
---|---|---|
as | GNU 汇编器(支持 AT&T 语法) | 2.34 |
ld | GNU 链接器(生成二进制可执行文件) | 2.34 |
gcc | C 编译器(编译镜像拼接工具) | 9.4.0 |
qemu-system-i386 | x86 架构模拟器(运行启动镜像) | 1:4.2-3ubuntu6.30 |
qemu-img | QEMU 镜像管理工具 | 1:4.2-3ubuntu6.30 |
dd | 磁盘数据复制工具(制作镜像) | 8.30 |
xxd | 二进制文件查看工具(验证扇区) | 2.34 |
objdump | 反汇编工具(调试二进制文件) | 2.34 |
vim/nano | 文本编辑器(编写代码) | 18:12:44 / 4.8 |
2. 工具链安装与验证
(1)一键安装所有工具
bash
sudo apt update && sudo apt install -y \
binutils gcc qemu-system-x86 qemu-utils \
util-linux xxd vim nano
(2)工具链完整性检查
通过脚本验证环境是否满足开发需求:
bash
# 创建检查脚本
cat > check_boot_env.sh << 'EOF'
#!/bin/bash
# 检查核心工具是否安装
tools=(
"as" "ld" "gcc" "qemu-system-i386"
"dd" "xxd" "objdump" "vim"
)
missing=0
echo "===== 工具链版本检查 ====="
for tool in "${tools[@]}"; do
if command -v "$tool" &> /dev/null; then
version=$("$tool" --version 2>&1 | head -n 1 | awk '{print $NF}' | sed 's/[(),]//g')
echo "[OK] $tool: $version"
else
echo "[ERROR] $tool 未安装"
missing=1
fi
done
# 检查汇编器对16位代码的支持
echo -e "\n===== 汇编功能检查 ====="
cat > test.s << 'END'
.code16
.global _start
_start:
movw %ax, %bx
END
if as -32 -o test.o test.s 2>&1; then
echo "[OK] 16位汇编支持正常"
else
echo "[ERROR] 16位汇编支持异常"
missing=1
fi
rm -f test.s test.o
if [ $missing -eq 1 ]; then
echo -e "\n请重新安装缺失工具"
else
echo -e "\n[SUCCESS] 开发环境就绪"
fi
EOF
# 执行检查
chmod +x check_boot_env.sh
./check_boot_env.sh
运行脚本后若显示 [SUCCESS] 开发环境就绪
,则可进入后续开发。
二、QEMU 运行配置详解
QEMU 是模拟 x86 硬件并运行启动镜像的核心工具,针对引导扇区与 setup 程序的配置如下:
1. 基础运行命令
bash
qemu-system-i386 -fda linux.img -boot a -vga std
参数说明:
-fda linux.img
:指定软盘镜像文件(引导扇区需位于镜像起始位置)。-boot a
:设置从软盘(A 盘)启动,符合 BIOS 启动顺序规范。-vga std
:使用标准 VGA 显示模式,确保字符输出正常。
2. 调试增强配置
如需观察 BIOS 加载过程或调试输出,可添加调试参数:
bash
# 显示 BIOS 启动日志与中断调用信息
qemu-system-i386 -fda linux.img -boot a -vga std -debugcon stdio -D qemu_log.txt
-debugcon stdio
:将调试信息输出到终端,可查看引导扇区加载地址等细节。-D qemu_log.txt
:将调试日志保存到文件,便于后续分析。
三、引导扇区与 Setup 程序开发全流程
1. 编写 AT&T 语法汇编代码
(1)引导扇区程序(bootsect.s)
功能:自迁移至安全地址、清屏、加载 setup 程序并跳转执行。
nasm
/* 引导扇区:512字节,BIOS加载的第一个程序 */
.code16 ; 指定16位实模式
.text
.global _start
/* 常量定义 */
SETUPLEN = 4 ; setup程序占用扇区数(4×512=2048字节)
BOOTSEG = 0x07C0 ; BIOS加载引导扇区的段地址
INITSEG = 0x9000 ; 引导扇区迁移后的目标段地址
SETUPSEG = 0x9020 ; setup程序在内存中的加载段地址
ROOT_DEV = 0x0000 ; 根设备标识(可根据实际设备修改)
_start:
jmpl $BOOTSEG, $start2 ; 初始化CS寄存器为BOOTSEG
start2:
/* 将自身从0x07C0:0000迁移到0x9000:0000 */
movw $BOOTSEG, %ax ; AX = 源段地址(BOOTSEG)
movw %ax, %ds ; DS = 源段(数据段指向源地址)
movw $INITSEG, %ax ; AX = 目标段地址(INITSEG)
movw %ax, %es ; ES = 目标段(附加段指向目标地址)
xorw %si, %si ; SI = 0(源地址偏移量)
xorw %di, %di ; DI = 0(目标地址偏移量)
movw $256, %cx ; CX = 256(重复次数,每次移动2字节,共512字节)
rep ; 重复执行movsw直到CX=0
movsw ; 从[DS:SI]复制1字到[ES:DI],自动递增SI和DI
/* 跳转到迁移后的新地址继续执行 */
ljmp $INITSEG, $go ; CS=INITSEG,IP=go
go:
/* 初始化段寄存器与栈指针 */
movw %cs, %ax ; AX = 当前代码段(INITSEG)
movw %ax, %ds ; DS = 代码段(数据访问基准)
movw %ax, %es ; ES = 代码段(附加数据访问)
movw %ax, %ss ; SS = 代码段(栈段基准)
movw $0xFF00, %sp ; SP = 0xFF00(设置栈顶地址)
ok_load_setup:
/* 清屏(BIOS中断0x10,功能0x06) */
movw $0x0600, %ax ; AH=0x06(上滚窗口),AL=0x00(清屏模式)
movw $0x0700, %bx ; BH=0x07(空白区域属性:黑底白字)
movw $0x184F, %cx ; 窗口范围:(0,0)到(24,79)(全屏)
int $0x10 ; 调用BIOS视频服务
/* 打印"Setup has been loaded" */
movw $msg, %ax ; AX = 字符串偏移地址
movw %ax, %bp ; BP = 字符串地址(BIOS中断要求)
movw $0x1301, %ax ; AH=0x13(显示字符串),AL=0x01(光标跟随)
movw $0x000C, %bx ; BH=0x00(页0),BL=0x0C(红底白字)
movw $21, %cx ; 字符串长度(21字符)
xorw %dh, %dh ; DH=0(行号)
xorw %dl, %dl ; DL=0(列号)
int $0x10 ; 调用BIOS视频服务
load_setup:
/* 读取setup程序到内存(BIOS中断0x13,功能0x02) */
movw $0x0000, %dx ; DH=0(磁头号),DL=0(驱动器号:软盘)
movw $0x0002, %cx ; CH=0(柱面号),CL=2(扇区号:从第2扇区开始)
movw $0x0200, %bx ; ES:BX = 0x9020:0000(setup加载地址)
movb $SETUPLEN, %al ; AL=4(读取扇区数)
movb $0x02, %ah ; AH=0x02(读磁盘扇区)
int $0x13 ; 调用BIOS磁盘服务
jnc read_done ; 无错误则跳转至read_done
/* 读取失败时重置磁盘并重试 */
xorw %ax, %ax ; AH=0x00(重置磁盘)
int $0x13
jmp load_setup
read_done:
ljmp $SETUPSEG, $0 ; 跳转到setup程序入口
msg:
.ascii "Setup has been loaded" ; 引导扇区提示字符串
.org 508 ; 定位到508字节处
.word ROOT_DEV ; 根设备标识(2字节)
.word 0xAA55 ; 引导扇区有效标志(BIOS识别用)
(2)Setup 程序(setup.s)
功能:验证引导流程、打印运行状态并进入循环。
nasm
/* setup程序:2048字节,引导扇区的后续执行程序 */
.code16 ; 16位实模式
.text
.global _start_setup
_start_setup:
/* 初始化段寄存器 */
movw %cs, %ax ; AX = 当前代码段(SETUPSEG)
movw %ax, %ds ; DS = 代码段(数据访问)
movw %ax, %es ; ES = 代码段(附加数据访问)
/* 打印"setup is running"(BIOS中断0x10) */
movw $setup_msg, %ax ; AX = 字符串偏移地址
movw %ax, %bp ; BP = 字符串地址
movw $0x1301, %ax ; AH=0x13(显示字符串),AL=0x01(光标跟随)
movw $0x000C, %bx ; BH=0x00(页0),BL=0x0C(红底白字)
movw $16, %cx ; 字符串长度(16字符)
movb $3, %dh ; DH=3(行号:第3行)
movb $0, %dl ; DL=0(列号)
int $0x10 ; 调用BIOS视频服务
hang: ; 无限循环(防止程序跑飞)
jmp hang
setup_msg:
.ascii "setup is running" ; setup提示字符串
/* 填充至2048字节(4扇区) */
.fill 2048-(.-_start_setup), 1, 0
2. 编写镜像拼接工具(build.c)
功能:将引导扇区与 setup 程序按顺序拼接为完整镜像。
c
运行
/* 镜像拼接工具:组合bootsect和setup为可启动镜像 */
#include <unistd.h> // 包含read、write、close系统调用
#include <fcntl.h> // 包含open函数及文件打开模式定义
int main(void)
{
char buf[2048]; // 缓冲区(最大适配setup程序大小)
int fd; // 文件描述符
/* 读取引导扇区(512字节)并输出 */
fd = open("bootsect", O_RDONLY); // 打开bootsect文件
read(fd, buf, 512); // 读取512字节(引导扇区大小)
write(1, buf, 512); // 写入标准输出
close(fd); // 关闭文件
/* 读取setup程序(2048字节)并输出 */
fd = open("setup", O_RDONLY); // 打开setup文件
read(fd, buf, 2048); // 读取2048字节(4扇区)
write(1, buf, 2048); // 写入标准输出
close(fd); // 关闭文件
return 0; // 程序正常退出
}
3. 编写自动化构建脚本(Makefile)
功能:定义编译、链接、镜像生成的完整流程。
makefile
# 工具链定义(适配i386架构)
AS := as --32 # 32位汇编器(兼容16位代码)
LD := ld -m elf_i386 # i386架构链接器
LDFLAGS := -Ttext 0x0 -s --oformat binary # 链接选项:二进制输出,无符号表
# 默认目标:生成最终镜像
all: linux.img
# 生成linux.img:通过build工具拼接bootsect和setup
linux.img: build bootsect setup
./build > linux.img # 拼接输出为镜像文件
# 生成引导扇区二进制文件
bootsect: bootsect.o
$(LD) $(LDFLAGS) -o $@ $< # 链接bootsect.o为二进制
# 编译引导扇区汇编代码
bootsect.o: bootsect.s
$(AS) -o $@ $< # 汇编生成目标文件
# 生成setup程序二进制文件(指定入口点)
setup: setup.o
$(LD) $(LDFLAGS) -e _start_setup -o $@ $< # -e指定入口为_start_setup
# 编译setup程序汇编代码
setup.o: setup.s
$(AS) -o $@ $< # 汇编生成目标文件
# 编译镜像拼接工具
build: build.c
gcc -o $@ $< # 编译C程序
# 清理中间文件与目标文件
clean:
rm -f *.o bootsect setup build linux.img
# 运行镜像(使用QEMU)
run:
qemu-system-i386 -fda linux.img -boot a -vga std
4. 编译与验证流程
(1)编译生成镜像
bash
# 执行Makefile构建流程
make
执行成功后,目录下将生成 linux.img
镜像文件。
(2)验证文件完整性
bash
# 验证引导扇区大小(必须为512字节)
ls -l bootsect | awk '{print "bootsect大小:" $5 "字节"}'
# 验证setup程序大小(必须为2048字节)
ls -l setup | awk '{print "setup大小:" $5 "字节"}'
# 验证引导扇区标志(最后两字节应为0xAA55)
xxd -s 510 -l 2 bootsect
(3)运行镜像
bash
# 启动QEMU运行镜像
make run
成功运行后,QEMU 窗口将先显示 Setup has been loaded
(引导扇区输出),随后显示 setup is running
(setup 程序输出),表明整个启动流程正常。
四、常见问题解决
- 引导扇区无法被 BIOS 识别
- 检查引导扇区大小是否为 512 字节(超出则 BIOS 忽略)。
- 验证末尾是否存在
0xAA55
标志