1 调试grub2

1.1 编译 grub2

首先从 https://cgit.git.savannah.gnu.org/cgit/grub.git 拉下压缩包.

1
2
3
4
# 在grub2安装目录下
# --with-platform=efi 可以编译 UEFI 版本.
./configure --with-platform=pc --target=i386 --disable-werror
make -j$(nproc)

编译完成后,grub-core/ 下会有很多文件.

  • 各种 *.mod 模块

  • kernel.exec(GRUB 核心可执行)

  • 若干 .image / .img 文件

  • gdb_grub, 调试用脚本.

1.2 构建 img

1
2
# 创建一个空的磁盘镜像
dd if=/dev/zero of=disk.img bs=1M count=256

建立分区并格式化.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 建立分区
parted disk.img --script mklabel msdos
parted disk.img --script mkpart primary ext4 1MiB 100%
# parted disk.img print 查看
# 我们可以看一下磁盘的数据存储情况, 511和512是55aa.
jvle@jvle-ThinkPad-X1-Carbon-Gen-8:~/Desktop/works/temp/linux_files$ od -A x -t x2 -w8 disk.img -N512
000000 b8fa 1000 d08e 00bc
000008 b8b0 0000 d88e c08e
000010 befb 7c00 00bf b906
000018 0200 a4f3 21ea 0006
000020 be00 07be 0438 0b75
000028 c683 8110 fefe 7507
000030 ebf3 b416 b002 bb01
000038 7c00 80b2 748a 8b01
000040 024c 13cd 00ea 007c
000048 eb00 00fe 0000 0000
000050 0000 0000 0000 0000
*
0001b8 7dea 1eb5 0000 0000
0001c0 1001 0383 ffe0 0800
0001c8 0000 f800 0007 0000
0001d0 0000 0000 0000 0000
*
0001f8 0000 0000 0000 aa55

映射 loop 设备.

1
2
3
4
sudo losetup -fP disk.img
# 查看映射到哪个设备
losetup -a
# 这里查到是loop18
1
2
3
4
5
6
jvle@jvle-ThinkPad-X1-Carbon-Gen-8:~/Desktop/works/temp/linux_files$ lsblk /dev/loop18
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop18 7:18 0 256M 0 loop
└─loop18p1 259:6 0 255M 0 part
# 格式化分区
sudo mkfs.ext4 /dev/loop18p1

挂载准备文件.

1
2
3
mkdir mnt
sudo mount /dev/loop18p1 mnt
sudo mkdir -p mnt/boot/grub

把你编译好的 GRUB 模块复制进去.

1
2
# 可略过
sudo cp grub-core/*.mod mnt/boot/grub/

安装 BIOS 版本 GRUB.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 这里 grub-install 在编译的 grub2 目录下
sudo grub-install \
--target=i386-pc \
--directory=grub-core
--boot-directory=mnt/boot \
--no-floppy \
/dev/loop18
# --target=i386-pc
# 指定平台目标:这里是 BIOS/MBR 模式 (i386-pc target)。
# 如果是 UEFI,就会写成 --target=x86_64-efi。

# --boot-directory=mnt/boot
# 指定 /boot 目录应该放在哪个分区里。
# 你之前把 /dev/loop18p1 格式化为 ext4 并挂载到 mnt,所以这里就是 mnt/boot。
# 结果:在 mnt/boot/grub/ 下会写入配置文件 (grub.cfg) 和模块 (*.mod)。

# --directory=./markdown/grub-2.06/grub-core
# 告诉 grub-install 去哪里找模块和支持文件。
# 默认会去 /usr/local/lib/grub/i386-pc/ 找,但你没 make install,所以要显式指定为你编译好的 grub-core 目录。

# /dev/loop18
# 目标设备 = 你挂载的整个虚拟磁盘。
# grub-install 会把 boot.img 写进 /dev/loop18 的 MBR(第 0 扇区)。
# 在 MBR 和分区之间的空隙(1MB gap) 写入 core.img。
# 然后在 mnt/boot/grub 里安装 .mod 模块和其他文件。

这一步会把:

  • boot.img 写到 MBR (stage1)

  • core.img 写到 MBR gap

  • /boot/grub 下放置模块和配置

1.3 加载 linux 和 initrd

此时我们的 disk.img 里面还没有内核. 我们将已经编译好的内核和 initrd 塞入到 disk.img 中去.

1
2
3
4
# 放进去之后.
mnt/boot/
├── bar.img
├── bzImage

在 mnt/boot/grub.cfg 写入配置.

1
2
3
4
5
6
7
8
9
10
set default=0
set timeout=10

menuentry 'myos' --class os {
insmod gzio
insmod vga

linux /boot/bzImage
initrd /boot/bar.img
}

启动调试.

server.

1
qemu-system-i386 -drive format=raw,file=disk.img -m 2048 -s -S

client.

1
gdb -x gdb_grub

2 References

  1. https://0xax.gitbooks.io/linux-insides/content/

  2. GNU GRUB: https://www.gnu.org/software/grub/

  3. GRUB repo: https://ftp.gnu.org/gnu/grub/