6

linux学习2,五分钟学会调试内核,让操作系统打印自己的名字

 3 years ago
source link: https://blog.popkx.com/linux-learns-to-debug-the-kernel-in-2-or-5-minutes-and-let-the-operating-system-print-from/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上一节粗略的介绍了如何下载和编译 linux 内核,可只是编译出内核没什么意思,能不能让它跑起来呢?当然可以,本节的内容就是让 linux 内核跑起来,并且在系统启动时,打印出我们的名字

qemu 模拟器

打算让“linux内核跑起来”,在什么地方跑呢?如果手边有闲置的电脑,那当然好。可是我只有一台电脑,也不想花钱买开发板,有没有办法让“linux内核跑起来”呢?当然有办法了,使用模拟器就好了。本节打算介绍的是 qemu 模拟器,它是纯软件实现的虚拟化模拟器,几乎可以模拟任何硬件设备,我们最熟悉的就是能够模拟一台能够独立运行操作系统的虚拟机,虚拟机认为自己和硬件打交道,但其实是和 qemu 模拟出来的硬件打交道,qemu 将这些指令转译给真正的硬件。正因为 qemu 是纯软件实现的,所有的指令都要经 qemu 过一手,性能会比较低,不过这个无所谓了,我们只是想用它来调试内核,并不是用来做复杂工作的。

在 ubuntu 上安装 qemu 是方便的,只需执行下面这条命令就可以了。

sudo apt-get install qemu

输入完上面的命令,按 tab 键,可以看到以下结果:

3f7084b5f5be4e359a6d301108d09de9.png

有很多种架构的模拟器,例如 qemu-system-x86 可以用来模拟 x86 架构的主机,qemu-system-arm 可以用来模拟 arm 架构的主机等等。如果不在乎存储空间,可以全部安装。也可以只安装一种架构的 qemu,我安装的是 x86 架构的:
sudo apt-get install qemu-system-x86

回车,待安装结束,输入以下命令

qemu-system-x86_64 --version

f5d0167e51f9ddcd3b65a5f2e6ed6f5a.png
发现 qemu 输出版本信息,就说明 qemu 安装好了。

编译 linux 内核

上一节已经介绍如何下载和编译内核了,这里又编译一次是因为我们打算调试内核,因此有些配置项要勾选。而且,在这次编译过程中,我遇到了几个错误,这里再介绍下如果遇到错误,如何解决它。

头条不能留外链,如果有朋友不方便下载内核,可以在评论区留言,我可以把要用到的东西都发过去。

我们新建一个 kernel 目录,把下载好的内核 linux-2.6.26.tar.gz 放进去,然后依次执行以下命令

tar xf linux-2.6.26.tar.gz
cd linux-2.6.26
make x86_64_defconfig
make menuconfig

这几条命令依次是:解压内核源码,进入解压后的命令,按照 x86_64 的架构(因为我的 ubuntu 主机是 x86_64 位的,编写x86_64程序比较方便)默认配置内核,最后一条命令执行后,就会进入到图形配置界面,进入 Kernel hacking,选中下图中的配置,按空格勾选之:

b904a36aea5692ad4f029825f2853baf.png

然后就可以退出图形配置界面了,记得要保存修改后的配置:
567040326a0bb13175562c0e2bf2c4da.png

勾选上图中配置项的目的,是希望在编译时能够保留 debug(调试)信息,方便我们后续的调试。配置完成后,就可以编译了:
make bzImage -j4

这几条命令上一节介绍的比较详细,不清楚的朋友可以再翻回去看看。

在我的 ubuntu 上,编译到这里就报错了:

99bb55e82c2ae6c8844a40e4622ae930.png

提示在编译 vdso 时不识别 -m 命令,也找不到 elf_x86_64。于是去查看编译 vdso 的 Makefile:
vim arch/x86/vdso/Makefile

db374050d2e078c2097b1b02fd5f58cc.png
我们知道,编译时通常可以用 -m32 参数来说明是 32 位主机,-m64 参数用于说明是 64 位主机,这里提示找不到 elf_x86_64,我们直接将“-m elf_x86_64”修改为“-m64”试试。保存修改,继续执行 make bzImage 编译,发现不再报这个错误了。

可是,编译过程中,又报这个错误了:
489d2a7a8f226316c5ca5a5fcac43630.png
提示 copy_user_generic_c 表达式的 size 不是常数,我们去源文件看看:

vim arch/x86/lib/copy_user_64.S
8b870da2496d2aa4adacc892383c75ae.png

ENTRY和 END 不匹配,我们将之改匹配试试:将 copy_user_generic_c 改成 copy_user_generic_string,保存,继续执行 make bzImage 编译,发现不再报这个错误了。但是又继续报下面这个错误:
590af590a2ec1ee917918ccb139a062f.png

提示没有定义 “__mutex_lock_slowpath” 函数。这下没辙了,尝试搜索下这个函数:
grep -r __mutex_lock_slowpath
b838e261ee618d13591b0d2e131697e4.png

还真搜到了,我们进源码看看:
vim kernel/mutex.c

发现明明定义了这个函数,怎么还会提示没定义呢?猜测可能是 CONFIG_DEBUG_LOCK_ALLOC 宏定义搞的鬼:

eb71b3ada7fdc5462e5f6721d72e2d35.png

我们查看以下 .config 配置文件:
vim .config

8b7b4a20290c5b31c3efdde6fb9a13cf.png
发现这个宏没有被定义,解注释,改为

CONFIG_DEBUG_LOCK_ALLOC=y

保存修改,继续编译终于不再报错了,稍后片刻,发现终于成功了:
97b521c0a4aa129c6e6a662199df39c1.png

使用 qemu 模拟运行编译出的 linux 内核

方法是简单的,只需执行以下命令即可

qemu-system-x86_64 \
-m 512M \       # 模拟内存 512M
-smp 1 \        # 模拟器有 1 个核的 cpu
-kernel arch/x86_64/boot/bzImage\       #内核位置
- curses        # 使用 curse 图形库
db3ce94b33c0c6c8c2ad5aa27d02b40b.png

linux 内核的确跑起来了,但是却引发了一个“kernel panic”(内核恐慌),内核发现找不到文件系统,慌得一批。

内核虽然很恐慌,但是我们依然可以修改内核源码,让它在恐慌之前打印出我们的名字,怎么做呢?请继续看:

事实上,一般 C 语言程序都有一个入口函数(这个入口函数通常是 main 函数),linux 内核也有入口函数——start_kernel 函数,在内核源码树的 init/main.c 里。我们打开之,找到 start_kernel 函数,发现它其实就是初始化一些函数而已。我们在最后的几个函数前添加打印信息就好了,请看:

a0767122b6d2048328eebd6b4d4f6922.png

这里要注意的是,内核不能使用 printf 函数,不过内核提供的 printk 函数和 printf 函数的功能很相似。

保存修改后,我们再次编译内核,得到新的 bzImage 后,再使用 qemu 模拟运行 linux 内核:

7704ba1d5c018ce611544462b7174935.png

发现内核打印出我们的名字 embed time(嵌入式时代)后,停在了这里,因为我们添加了 while(1) 死循环嘛。

到这里,其实我们就已经会修改 linux 内核源码了,修改操作系统源码也没有那么复杂,对不?至于如何让 linux 内核不再恐慌,就需要给它提供个文件系统了,怎样提供跟?限于篇幅,我们下一节再介绍。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK