转载于 https://blog.csdn.net/jasonLee_lijiaqi/article/details/80967912
1、编译源码
make menuconfig
执行make menuconfig时报错缺少库文件,需要安装依赖库
sudo apt-get install aptitude sudo aptitude install libncurses5-dev
sudo apt-get install libssl-dev
首先编译内核,编译内核时注意要选两个选项。(注意:除此之外kernel hacking选项下其他的选项都不要选,否则会出现断点无法拦截的情况。这个说法有待验证)
kernel hacking –> kernel debugging –> compile the kernel with debug
info kernel hacking –> compile the kernel with frame pointers
make bzImage -j4 【make -j4】
在编译过程中会出现如下的warning,最好解决掉
解决方法:
进入make menuconfig的图形界面,选择kernel hacking,将frame size由1024改成2048即可。
这样就生成了bzImage和vmlinux:
bzImage 位于Linux-4.6.2/arch/x86/boot/bzImage;
vmlinux 位于linux-4.6.2/vmlinux;
2、编译QEMU
2.1 下载源码
https://download.qemu.org/
2.2 编译前的工作
编译之前需要安装一些库文件,否则会卡在下面的页面不动;
因此需要安装SDL图形库:(注意是2.0版本的SDL)
sudo apt-get install libsdl2-2.0
sudo apt-get install libsdl2-dev
运行下列代码:
./configure --enable-debug --target-list=x86_64-softmmu
运行上述命令,会出现不存在pixman错误,运行如下命令解决:
sudo apt-get install libpixman-1-dev
紧接着,执行:
sudo make && make install
又会遇到如下问题: 缺少flex和bison ,运行如下命令:
sudo apt-get install flex bison
再运行
sudo make install
3、编译gdb
3.1 下载7.8版本
http://ftp.gnu.org/gnu/gdb/
3.2 修改源码
https://blog.csdn.net/barry283049/article/details/42970739
修改代码【否则调试内核会出现remote ‘g’ packet reply is too long的问题】
cd gdb-7.8/gdb
vim remote.c
按照如图所示修改代码【针对7.8版本】:
代码如下:
if (buf_len > 2 * rsa->sizeof_g_packet) {
rsa->sizeof_g_packet = buf_len ;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
{
if (rsa->regs->pnum == -1)
continue;
if (rsa->regs->offset >= rsa->sizeof_g_packet)
rsa->regs->in_g_packet = 0;
else
rsa->regs->in_g_packet = 1;
}
}
3.3 开始编译:
mkdir _install
./configure --prefix=./_install
make
make install
运行gdb需要进入_install/bin目录下,执行./gdb;
4、busybox建立最小文件系统
4.1 动态编译的方法(不推荐)
动态编译 https://siweixiang.github.io/notes/2016_06_19_build_minimal_linux_hack_env_with_busybox_x86_64.html
4.2 另一种静态库编译方法(推荐):
静态编译 https://github.com/chyyuu/tiny_linux_ChildIsTalent
具体的流程如下:
4.2.1 安装busybox
https://busybox.net/ 下载并解压源码
编译busybox源码
cd busybox-1.25.0
make defconfig
make menuconfig
在menuconfig中修改配置,使用静态编译busybox,否则在程序运行期间需要对相应的库进行动态加载,那么在根文件系统中则需要提供其所需的共享库。
进入menuconfig图形界面,选择
-> Busybox Settings
-> Build Options
[*] Build BusyBox as a static binary (no shared libs)
make -j4
make 期间会遇到许多的warning,不用管它,对安装影响不大。
sudo make install
此时可以在busybox-1.25.0/中看到生成的_install目录。通过下面的命令可以验证busybox是否安装正确:
./busybox ls
4.3 生成initrd
4.3.1 首先将上一步生成的_install文件夹复制到其他位置
cd …
mkdir ramdisk
cd ramdisk
cp -r …/busy-1.25.0/_install/* .
4.3.2 设置初始化进程init(建立一个软链接,一定不能直接复制过去)
cd ramdisk
ln -s bin/busybox init
4.3.3 设置开机启动程序
首先,我们需要先设定一些程序运行所需要的文件夹
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}
init程序首先会访问etc/inittab文件,因此,我们需要编写inittab,指定开机需要启动的所有程序
cd etc
vim inittab
inittab文件的内容如下所示:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
赋予可执行权限
chmod +x inittab
编写系统初始化命令
从inittab文件中可以看出,首先执行的是/etc/init.d/rcS脚本,因此,我们生成初始化脚本
mkdir init.d
cd init.d
vim rcS
rcS文件的内容如下所示:
#!/bin/sh
mount proc
mount -o remount,rw /
mount -a
clear
echo "My Tiny Linux Start :D ......"
赋予可执行权限
chmod +x rcS
在rcS脚本中,mount -a 是自动挂载 /etc/fstab 里面的东西,可以理解为挂在文件系统,因此我们还需要编写 fstab文件来设置我们的文件系统。
cd ramdisk/etc/
vim fstab
fstab文件内容如下:
# /etc/fstab
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
至此,我们已经完成了RAM Disk中相关文件的配置,可以压缩生成文件镜像了。
cd ramdisk
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
最后生成的initramfs.img就是我们的根文件系统。
4.4 测试根文件系统
下面就可以测试根文件系统了。
qemu-system-x86_64 -kernel /usr/src/Linux-4.6.2/arch/x86/boot/bzImage -initrd …/initramfs.img
这时qemu上会显示出内核打印的各种信息,最终显示:
My Tiny Linux Start ? …
Please press Enter to activate this console.
按Enter键后,就可以进入到文件系统中,运行命令
ls /dev
如果能够成功显示目录下所有文件,则文件系统挂载成功。
5. QEMU+GDB调试内核
在之前的四个步骤中,已经准备好了全部的工具,下面就可以使用QEMU+GDB进行内核调试了。
运行如下命令:
qemu-system-x86_64 -kernel /usr/src/linux-4.6.2/arch/x86/boot/bzImage -initrd …/initramfs.img -smp 2 -S -s
先使用命令启动qemu。
qemu-system-x86_64的参数比较多,这里简单说下:
-kernel是指定一个大内核文件,当仁不让的是bzImage。
-initrd是指定一个 initrd.img文件,这个文件就是我们使用busybox生成的initramfs.img。
-smp可以从名字猜想,它是给qemu指定几个处理器,或者是几个线程<嗯,大概意思就thread吧>。
-gdb则是启动qemu的内嵌gdbserver,监听的是本地tcp端口1234—如果这样写: -gdb tcp:192.168.1.100:1234 ,似乎也是没问题的。
-S 就是挂起gdbserver,让gdb remote connect it。
-s 默认使用1234端口进行远程调试,和-gdb tcp::1234类似。
-m 2048指定内存大小为2048M
运行上个命令之后,弹出下面这个页面:
此时,开启另一个terminal,运行如下命令:
gdb /usr/src/linux-4.6.2/vmlinux (修改成自己的vmlinux路径)
target remote:1234 (默认端口是1234,进行远程连接)
b start_kernel (设置断点)
c (continue 运行到断点处)
具体如下图:
可以看到gdb运行start_kernel断点处停下来了,接下来就可以使用gdb的基本命令进行单步调试了。
如果遇到b start_kernel停不下来,可以使用下面的命令: (普通的break断点无法使得gdb在断点处运行,这是可能是一个gdb的bug())
hb start_kernel //设置硬件断点
如果需要查看启动过程中的信息,则需要将qemu监视器中的内容重定向到terminal中。
使用如下命令 -nographic 配合-append”console=ttyS0”