Linux 系统调用及内核调试

编号后三位 411,参考孟宁老师 https://github.com/mengning/linuxkernel

编译内核并运行简单的测试程序

编译内核

  • 下载并解压 linux-5.0.2.tar.gz 代码包
wget http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v5.x/linux-5.0.2.tar.gz
tar -xzvf linux-5.0.3.tar.gz
  • 安装编译依赖软件
sudo apt install flex bison libssl-dev
  • 使用 i386 架构的默认配置进行编译
make i386_defconfig
# make
make -j8

内核测试

  • 编写简单的HelloWorld程序
#include 

int main()
{
	int i;
	while(0 < 1)
	{
		scanf("%d", &i);
		printf("you input %d\n", i);
	}
}
  • 基于以上 helloworld 程序制作最小文件系统
gcc -o init init.c -m32 -static
mkdir rootfs
cp init rootfs/
cd rootfs
ls | cpio -o -Hnewc | gzip -9 > ../rootfs.img
  • 使用制作的最小文件系统启动内核
qemu-system-i386 -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img

可以看到以下执行效果

Linux 系统调用及内核调试_第1张图片

测试内核调用并编写自己的内核调用程序

编译内核,启用调试选项

使用以下命令重新编译内核,为内核启用调试选项

# 安装依赖
sudo apt install libncurses-dev
# 生成编译配置
make menuconfig

Linux 系统调用及内核调试_第2张图片

# 启动编译
make -j6

编写并测试内核调用程序

  • 编写内核测试 程序

我的编号后两位是11,因此测试11号API

#define __NR_execve 11

查资料了解到这个系统调用的功能是启动一个可执行程序,因此我需要至少两个可执行程序,一个包含execve调用,另一个被启动。

我编写了以下两个程序。

#include 

int main() 
{
	printf("\nHello world!\n");
	return 0;
}
#include
#include

int main()
{
	int temp;
	char *argv_execve[]={"helloworld", NULL};
	char *envp[]={"PATH=/", "USER=coolxxy", "STATUS=testing", NULL};
	printf("Starting systemcall execve......\n");
	if(fork() == 0)
	{
		if(execve("./helloworld", argv_execve, envp) < 0)
		{
			perror("Error on execve");
		}
		printf("Stoped systemcall execve......\n");
	}
	while(0 < 1)
	{
		scanf("%d",&temp);
	}
}

前一个输出简单的 helloworld 信息,后一个包含系统调用,用于启动 helloworld 程序。

  • 汇编形式的系统调用

我还编写了汇编形式的系统调用,但由于gdb可以单步调试汇编,因此这里不作为主要测试代码。

Linux 系统调用及内核调试_第3张图片

  • 制作文件系统
gcc -o init init.c -m32 -static
gcc -o helloworld helloworld.c -m32 -static
cp init rootfs/
cp helloworld rootfs/
cd rootfs
ls | cpio -o -Hnewc | gzip -9 > ../rootfs.img
  • 启动内核调用程序
qemu-system-i386 -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img

可以看到执行结果

Linux 系统调用及内核调试_第4张图片

内核调试

调试时需要添加附加选项

qemu-system-i386 -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
cd linux-5.0.2
gdb vmlinux

可以看到如下调试界面

Linux 系统调用及内核调试_第5张图片

调试 start_kernel 函数, 使用以下 gdb 命令调试

b start_kernel // 设置断点
c // 执行到第一个断点
display /i $pc //显示汇编代码
l // 显示当前c代码
s // 单步执行
s // 单步执行

Linux 系统调用及内核调试_第6张图片

Linux 系统调用及内核调试_第7张图片

调试系统调用

为了调试方便,我将之前编写的init程序直接在Ubuntu上进行调试,着重分析系统调用过程

gcc init.c -o init -m32 -g
gdb ./init

Linux 系统调用及内核调试_第8张图片

可以看到,此处的系统调用通过sysenter进入内核,然后启动了 helloworld 程序,调用过程为:

ecx 寄存器压栈 -> edx 寄存器压栈 -> ebp 寄存器压栈 -> mov esp 到 ebp -> sysenter (此处执行了helloworld程序)-> ebp 寄存器出栈 -> edx 寄存器出栈 -> ecx出栈

你可能感兴趣的:(Linux 系统调用及内核调试)