持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
X86使用的qemu和arm不同,x86使用的是qemu-system-x86_64
-
sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi buildessent qemu-system-x86安装编译需要的工具包。
-
下载linux源码,和下载busybox工具包:busybox.net/downloads/b…
-
编译linux的x86_64内核。
编译之前需要打开内核调试功能:Kernel hacking --> Compile-time checks and compiler options --> Compile the kernel with debug info
make x86_64_defconfig #生成x86_64版本配置文件. make bzImage #编译内核 make modules #编译内核模块 -
启动内核.
qemu-system-x86_64 -m 512M -smp 4 -kernel ./bzImage上述命令假设编译好的 bzImage 内核文件就存放在当前目录下。不出意外的话,就可以在启动窗口中看到内核的启动日志了。在内核启动的最后,会出现一条 panic 日志:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0, 0)从日志内容可以看出,内核启动到一定阶段后尝试加载根文件系统,但我们没有指定任何磁盘设备,所以无法挂载根文件系统。而且上一节中编译出来的内核模块现在也没有用上,内核模块也需要存放到文件系统中供内核需要的时候进行加载。所以,接下来需要制作一个磁盘镜像文件供内核作为根文件系统加载。
-
创建磁盘镜像文件,使用 qemu-img创建一个 512M 的磁盘镜像文件:
qemu-img create -f raw disk.raw 512M现在 disk.raw 文件就相当于一块磁盘,为了在里面存储文件,需要先进行格式化,创建文件系统。
mkfs -t ext4 ./disk.raw -
挂载磁盘镜像文件。
sudo mount -o loop ./disk.raw ./img -
安装内核模块。 将之前编译好的内核模块安装到磁盘镜像中,命令如下:
sudo make modules_install INSTALL_MOD_PATH=./img # 指定安装路径完成后会在./img/lib/modules/下看到安装好的内核模块。
-
使用磁盘文件启动qemu:
qemu-system-x86_64 -m 512M -smp 4 -kernel ./arch/x86_64/boot/bzImage -drive format=raw,file=./disk.raw -append "root=/dev/sda"file=./disk.raw : 指定文件作为磁盘。 root=/dev/sda :内核启动参数,指定根文件系统所在设备。
不出意外的话,会显示:
Kernel panic - not syncing: No working init found. Try passing init= option to Kernel. See Linux Documentation/admin-guide/init.rst for guidance.说明启动参数里边没有指定init选项,磁盘镜像中也没有相应的init程序。
-
编译busybox:
make defconfig make menuconfig这里有一个重要的配置,因为 busybox 将被用作 init 程序,而且我们的磁盘镜像中没有任何其它库,所以 busybox 需要被静态编译成一个独立、无依赖的可执行文件,以免运行时发生链接错误。配置路径如下:
Busybox Settings ---> --- Build Options [*] Build BusyBox as a static binary (no shared libs)最后,编译并安装到磁盘镜像中:
make make CONFIG_PREFIX=需要安装的文件夹路径 install -
加入init内核启动参数来指定busybox作为init进程,再次尝试启动。
qemu-system-x86_64 -m 512M -smp 4 -kernel ./bzImage -drive format=raw,file=./disk.raw -append "init=/linuxrc root=/dev/sda"还是有问题,会出现:
can't run '/etc/init.d/rcS': No such file or directory can't open /dev/tty3: No such file or directory can't open /dev/tty4: No such file or directoryinit进程执行报错,需要配置。
-
init 启动后会扫描/etc/inittab配置文件,这个配置文件决定了init程序的行为。而busybox init再没有/etc/inittab文件的情况下也能工作,因为它有默认行为。它的默认行为相当于如下配置:
::sysinit:/etc/init.d/rcS ::askfirst:/bin/sh ::ctrlaltdel:/sbin/reboot ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r ::restart:/sbin/init tty2::askfirst:/bin/sh tty3::askfirst:/bin/sh tty4::askfirst:/bin/sh但是不能这样用,需要去掉后边的三行。 如下:
::sysinit:/etc/init.d/rcS ::askfirst:/bin/ash ::ctrlaltdel:/sbin/reboot ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r ::restart:/sbin/init -
创建可执行文件/etc/init.d/rcS 内容如下(暂时啥都不做,后边要加):
#!/bin/sh这次应该可以进入控制台。 但是/dev 这些都不能访问。
-
创建并在脚本中挂载/dev,/proc, /sys 文件系统:
mkdir ./img/dev ./img/proc ./img/sys并修改etc/init.d/rcS :
#!/bin/sh mount -t proc proc /proc mount -t sysfs sysfs /sys重启系统,可以查看/dev,/proc,/sys挂载点都有了相应的内容。
-
到此处qemu已经可以启动linux内核了, 下来介绍启动时使用GDB调试并打断点到方法。
开始启动内核,并在启动时暂停:
sudo umount /dev/loop0 #先将上面挂载的img文件夹卸载掉 qemu-system-x86_64 -m 512M -smp 4 -kernel ./bzImage -drive format=raw,file=./disk.raw -append "init=/linuxrc rw root=/dev/sda nokaslr" -S -snokaslr:不加nokaslr可能导致断点不生效。因为kernel address space layout randomation(内核地址空间布局随机化),这样内核地址不就不一致了,禁掉就好了。
-S :Do not start CPU at startup (you must type 'c' in the monitor).
-s:Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234(see gdb_usage).
qemu参数介绍参考文档:blog.csdn.net/wj_j2ee/art…
-
步骤14会在qemu启动时暂停,必须使用另一个终端启动gdb并加载未压缩内核(内核符号表),然后链接通过tcp链接本地1234端口,然后加断点,最后使用c,开始执行到断点处,后边使用和GDB调试相同,不做赘述。命令如下:
sh> gdb vmlinux (gdb)target remote localhost:1234 (gdb)b start_main (gdb)c断点调试效果如下图所示: