在实验中用到这一块,就去看源码分析整理了一下,全部为个人理解。有错误的地方,希望和大牛交流。
首先解释一下,我实验的目的是获得系统调用入口函数system_call的起始地址和函数大小。
在linux-3.10.1, x86 64位的系统下,系统调用的入口地址保存在MSR寄存器中,通过rdmsrl(MSR_LSTAR,ksystem_call);便可获得系统调用的入口地址,然后对该入口地址进行解析得到入口函数为system_call,具体的函数实现在/linux-3.10.1/arch/x86/kernel/entry_64.S文件中。
Entry_64.S为一个汇编文件,即system_call函数是有汇编语言实现的,在ENTRY(system_call)与END(system_call)之间有很对其他的函数定义和调用,与C语言程序的结构不同,因此system_call以及它内部包含的所有的内核函数的符号信息都保存在kallsyms文件系统中,因此根据内核栈中的返回地址查询kallsyms得到的内核符号可能只是真正调用函数内部的一个中间函数,通过实验我们也验证了这个猜想,这也是在实验中根据内核函数中一个地址本该获得的内核符号为system_call,结果得到的却是system_call_fast_path的原因。
对system_call的函数实现分析之后我们得到图1的处理流程:
图1 system_call的处理过程
为了获取到正确的内核函数信息,在内核模块中使用kallsyms_lookup_name()函数以system_call为参数可以获得它对应的内核符号信息,在得到system_call结束后的第一个函数对应的地址信息,即可计算出system_call函数所占空间大小。
对于宿主机的系统调用表以及其他内核符号的信息,使用相同的方法获取。
下面是对system_call汇编源码的一些注释:
/*
* System call entry. Up to 6 arguments in registers are supported.
*
* SYSCALL does not save anything on the stack and does not change the
* stack pointer. However, it does mask the flags register for us, so
* CLD and CLAC are not needed.
*/
/*
* Register setup:
* rax system call number
* rdi arg0
* rcx return address for syscall/sysret, C arg3
* rsi arg1
* rdx arg2
* r10 arg3 (--> moved to rcx for C)
* r8 arg4
* r9 arg5
* r11 eflags for syscall/sysret, temporary for C
* r12-r15,rbp,rbx saved by C code, not touched.
*
* Interrupts are off on entry.
* Only called from user space.
*
* XXXif we had a free scratch register we could save the RSP into the stack frame
* and report it properly in ps. Unfortunately we haven't.
*
* When user can change the frames always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
*/
ENTRY(system_call)
CFI_STARTPROCsimple
CFI_SIGNAL_FRAME
CFI_DEF_CFArsp,KERNEL_STACK_OFFSET
CFI_REGISTERrip,rcx
/*CFI_REGISTERrflags,r11*/
SWAPGS_UNSAFE_STACK
//上面的几行代码执行了swapgs指令。修改gs寄存器从用户态切换到内核态,其实就是修改运行级别
/*
* A hypervisor implementation might want to use a label
* after the swapgs, so that it can do the swapgs
* for the guest and jump here on syscall.
*/
GLOBAL(system_call_after_swapgs)