2019-2020-1 20199313《Linux内核原理与分析》第六周作业 第六周学习——使用gdb调式系统调用 一、首先,本周解决了一个疑难杂症 二、在MenuOS中添加命令 三、分析system_call的代码
-
问题描述:
- 经过上一周的学习,我们进一步学习了计算机操作系统的核心工作机制,构造了一个简单的Linux系统MenuOS,分析运行了这个简单的OS并进行一定的分析,利用了GDB简单分析Start_kernel,我们还学习了使用系统的库函数,并在此基础上,学会了使用系统调用。
-
本周学习:
- 本周在上周学习的基础上使用gdb调试系统调用
- 并观察system_call函数是如何工作的
一、首先,本周解决了一个疑难杂症
对于最新版本的uabntu的gcc编译版本问题的研究
上上周我们进行了如下一个实验:
下载内核源代码编译内核:
$ cd./LinuxKernel/
$ wget https://www.kernel.org/pub/linux/kernel//v3.x/linux-3.18.6.tar.xz
$ xz -d linux-3.18.6.tar.xz
$ tar -xvf linux-3.18.6.tar
这里我们成功解压了安装包,写一部将要对其进行初步编译:
$ cd linux-3.18.6
$ make i386_defconfig
$ make
等待系统将其成功编译后即可进行下一步,制作系统根文件:
cd ./LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
cd ../roofts
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
完成以上步骤后即完成了安装,接下来就可以像在实验楼上的操作一样,进行运行以及gdb调试。
但在我的电脑上出现了问题如下:
解决方法
经各方学习,发现,问题最终还是出在gcc编译器上,我们所使用的gcc编译器的版本为gcc-7.4,对于我们想要编译的内核版本而言,实在是太高了,所以我们并不能成功的编译内核文件,
那么从这一层面上来思考问题,我们就可以更新gcc版本为老版本即可完成修改:
- 我各方查找资料,发现gcc-4.8是一个很好的版本,并且在现在可以很方便的安装
首先安装一些依赖包
sudo apt-get install ncurses-dev
sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install build-essential
然后安装gcc-4.8
sudo apt-get install gcc-4.8 g++-4.8
在安装完之后可以采用下面的指令来查看当前系统中安装的所有的gcc和g++的版本:
ls /usr/bin/gcc*
ls /usr/bin/g++*
然后看表格中我们需要的gcc-4.8加入候选行列:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
同样,我们也可以将我们现在使用的gcc-7.4加入候选行列
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7.4 100
完成之后,就可以在候选行列中选择要使用的gcc版本
sudo update-alternatives --config gcc
却换完成后使用代码gcc -v 即可知道我们的gcc版本是否切换成功。
二、在MenuOS中添加命令
在menu文件夹下
vi test.c
写入如下函数
就可以添加系统调用
三、分析system_call的代码
使用代码:
vi linux-3.18.6/arch/x86/kernel/entry_32.S
即可查看system_call函数代码,如下:
ENTRY(system_call)
RING0_INT_FRAME
#cant unwind into user space anyway
pushl %eax
#save orig_eax ,将系统调用号压入栈中
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
#将寄存器的值压入堆栈当中,压入堆栈的顺序对应着结构体struct pt_regs ,当出栈的时候,就将这些值传递到结构体struct pt_regs里面的成员,从而实现从汇编代码向C程序传递参数。
#GET_THREAD_INFO宏获得当前进程的thread_info结构的地址,获取当前进程的信息。
GET_THREAD_INFO(%ebp)
#system call tracing in operation / emulation
#thread_inof结构中flag字段的_TIF_SYSCALL_TRACE或_TIF_SYSCALL_AUDIT
#被置1。如果发生被跟踪的情况则转向相应的处理命令处。
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry #比较结果不为零的时候跳转。
#对用户态进程传递过来的系统调用号的合法性进行检查。
#如果不合法则跳转到syscall_badsys标记的命令处。
cmpl $(nr_syscalls), %eax
jae syscall_badsys #比较结果大于或者等于最大的系统调用号的时候跳转,不合法
#合法则跳转到相应系统调用号所对应的服务例程当中,
#也就是在sys_call_table表中找到了相应的函数入口点。
#由于sys_call_table表的表项占4字节,因此获得服务例程指针的具体方法
#是将由eax保存的系统调用号乘以4再与sys_call_table表的基址相加。
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp)
#store the return value 将保存的结果返回
- 具体执行过程如下: