2019-2020-1 20199313《Linux内核原理与分析》第六周作业

第六周学习——使用gdb调式系统调用

  • 问题描述:
    • 经过上一周的学习,我们进一步学习了计算机操作系统的核心工作机制,构造了一个简单的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 将保存的结果返回
  • 具体执行过程如下:


猜你喜欢

转载自www.cnblogs.com/dhr9313/p/11749295.html
今日推荐