分析system_call中断处理过程
2015-03-28 12:58
441 查看
第一章 环境
Ubuntu 14.10
Linux Kernel 3.18.6
第二章 源代码
第三章 分析
由于这个是无法用gdb调试的,所以不能用截图表达。网上的文章说可以用kdb,kgdb来调试,但是复杂程度超过我的能力范围,所以无法截图分析。只总结并画出流程图。
S***E_ALL:将寄存器的值压入堆栈当中,压入堆栈的顺序对应着结构体struct pt_regs ,当出栈的时候,就将这些值传递到结构体struct pt_regs里面的成员,从而实现从汇编代码向C程序传递参数。
struct pt_regs 位于:linux/arch/x86/include/asm/ptrace.h。
GET_THREAD_INFO 宏获得当前进程的thread_info结构的地址,获取当前进程的信息。(后面会有文章讲task_struct 和 thread_info等内核线程的数据结构和相关内容) 525行的jnz syscall_trace_entry比较结果不为零的时候跳转。对用户态进程传递过来的系统调用号的合法性进行检查。如果不合法则跳转到syscall_badsys标记的命令处。
比较结果大于或者等于最大的系统调用号的时候跳转,合法则跳转到相应系统调用号所对应的服务例程当中,也就是在sys_call_table表中找到了相应的函数入口点。由于sys_call_table表的表项占4字节,因此获得服务例程指针的具体方法是将由eax保存的系统调用号乘以4再与sys_call_table表的基址相加。
接下来,会进入到系统调用表查找到系统调用服务程序的入口函数的地址,再进行跳转,整个过程大概是这样:system_call -> 4x%eax ->sys_call_table ->sys_xxx
sys_call_table位于:linux/arch/x86/kernel/syscall_table_32.S
此部分来源于:http://www.cnblogs.com/zhuyp1015/archive/2012/05/29/2524936.html
参考资料:http://blog.chinaunix.net/uid-27767798-id-3507139.html
如果遇到了错误,则要通过
参考资料:http://read.pudn.com/downloads83/ebook/321730/%D6%D0%B6%CF.pdf
RESTORE_ALL:恢复现场。
在linux-3.18.6/arch/x86/kernel/head_64.S中定义了一个宏INTERRUPT_RETURN:
参考资料:http://blog.chinaunix.net/uid-27767798-id-3507139.html
接着转向:
接着便是退出
从而不再进行内核处理过程,返回用户态。
第五章 总结
查阅相关资料后,我明白:
1、基本上用户态到内核态需要int 0x80进行中断,产生中断向量后才可以转换;
2、系统内部调用涉及CPU架构等内容,不同的CPU对于系统调用的汇编具体代码是不一样的。
3、之所以要有高级语言,是因为汇编语言对于底层而言太过于依赖,没有跨平台可移植性。这也加深了我对高级语言的理解。高级语言通过对系统底层细节的屏蔽完成了跨平台实现,其中最出名的例子可以说是Java了。而C则是第一个跨平台的语言。对此,对前辈们的艰苦努力表示敬意。
附录
卢晅 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Ubuntu 14.10
Linux Kernel 3.18.6
第二章 源代码
ENTRY(system_call) RING0_INT_FRAME # can't unwind into user space anyway ASM_CLAC pushl_cfi %eax # save orig_eax S***E_ALL GET_THREAD_INFO(%ebp) # system call tracing in operation / emulation testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) jnz syscall_trace_entry cmpl $(NR_syscalls), %eax jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) syscall_after_call: movl %eax,PT_EAX(%esp) # store the return value syscall_exit: LOCKDEP_SYS_EXIT DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx testl $_TIF_ALLWORK_MASK, %ecx # current->work jne syscall_exit_work restore_all: TRACE_IRQS_IRET restore_all_notrace: #ifdef CONFIG_X86_ESPFIX32 movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS # Warning: PT_OLDSS(%esp) contains the wrong/random values if we # are returning to the kernel. # See comments in process.c:copy_thread() for details. movb PT_OLDSS(%esp), %ah movb PT_CS(%esp), %al andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax CFI_REMEMBER_STATE je ldt_ss # returning to user-space with LDT SS #endif restore_nocheck: RESTORE_REGS 4 # skip orig_eax/error_code irq_return: INTERRUPT_RETURN .section .fixup,"ax" ENTRY(iret_exc) pushl $0 # no error code pushl $do_iret_error jmp error_code .previous _ASM_EXTABLE(irq_return,iret_exc) #ifdef CONFIG_X86_ESPFIX32 CFI_RESTORE_STATE ldt_ss: #ifdef CONFIG_PAR***IRT /* * The kernel can't run on a non-flat stack if paravirt mode * is active. Rather than try to fixup the high bits of * ESP, bypass this code entirely. This may break DOSemu * and/or Wine support in a paravirt VM, although the option * is still available to implement the setting of the high * 16-bits in the INTERRUPT_RETURN paravirt-op. */ cmpl $0, pv_info+PAR***IRT_enabled jne restore_nocheck #endif /* * Setup and switch to ESPFIX stack * * We're returning to userspace with a 16 bit stack. The CPU will not * restore the high word of ESP for us on executing iret... This is an * "official" bug of all the x86-compatible CPUs, which we can work * around to make dosemu and wine happy. We do this by preloading the * high word of ESP with the high word of the userspace ESP while * compensating for the offset by changing to the ESPFIX segment with * a base address that matches for the difference. */ #define GDT_ESPFIX_SS PER_CPU_VAR(gdt_page) + (GDT_ENTRY_ESPFIX_SS * 8) mov %esp, %edx /* load kernel esp */ mov PT_OLDESP(%esp), %eax /* load userspace esp */ mov %dx, %ax /* eax: new kernel esp */ sub %eax, %edx /* offset (low word is 0) */ shr $16, %edx mov %dl, GDT_ESPFIX_SS + 4 /* bits 16..23 */ mov %dh, GDT_ESPFIX_SS + 7 /* bits 24..31 */ pushl_cfi $__ESPFIX_SS pushl_cfi %eax /* new kernel esp */ /* Disable interrupts, but do not irqtrace this section: we * will soon execute iret and the tracer was already set to * the irqstate after the iret */ DISABLE_INTERRUPTS(CLBR_EAX) lss (%esp), %esp /* switch to espfix segment */ CFI_ADJUST_CFA_OFFSET -8 jmp restore_nocheck #endif CFI_ENDPROC ENDPROC(system_call)
第三章 分析
由于这个是无法用gdb调试的,所以不能用截图表达。网上的文章说可以用kdb,kgdb来调试,但是复杂程度超过我的能力范围,所以无法截图分析。只总结并画出流程图。
S***E_ALL:将寄存器的值压入堆栈当中,压入堆栈的顺序对应着结构体struct pt_regs ,当出栈的时候,就将这些值传递到结构体struct pt_regs里面的成员,从而实现从汇编代码向C程序传递参数。
struct pt_regs 位于:linux/arch/x86/include/asm/ptrace.h。
GET_THREAD_INFO 宏获得当前进程的thread_info结构的地址,获取当前进程的信息。(后面会有文章讲task_struct 和 thread_info等内核线程的数据结构和相关内容) 525行的jnz syscall_trace_entry比较结果不为零的时候跳转。对用户态进程传递过来的系统调用号的合法性进行检查。如果不合法则跳转到syscall_badsys标记的命令处。
比较结果大于或者等于最大的系统调用号的时候跳转,合法则跳转到相应系统调用号所对应的服务例程当中,也就是在sys_call_table表中找到了相应的函数入口点。由于sys_call_table表的表项占4字节,因此获得服务例程指针的具体方法是将由eax保存的系统调用号乘以4再与sys_call_table表的基址相加。
接下来,会进入到系统调用表查找到系统调用服务程序的入口函数的地址,再进行跳转,整个过程大概是这样:system_call -> 4x%eax ->sys_call_table ->sys_xxx
sys_call_table位于:linux/arch/x86/kernel/syscall_table_32.S
此部分来源于:http://www.cnblogs.com/zhuyp1015/archive/2012/05/29/2524936.html
jne syscall_exit_work //退出系统调用之前,检查是否需要处理信号
参考资料:http://blog.chinaunix.net/uid-27767798-id-3507139.html
如果遇到了错误,则要通过
jmp error_code进行异常处理,以便及时纠正。
参考资料:http://read.pudn.com/downloads83/ebook/321730/%D6%D0%B6%CF.pdf
RESTORE_ALL:恢复现场。
irq_return: INTERRUPT_RETURN
在linux-3.18.6/arch/x86/kernel/head_64.S中定义了一个宏INTERRUPT_RETURN:
#define INTERRUPT_RETURN iretq
syscall_exit: LOCKDEP_SYS_EXIT DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx testl $_TIF_ALLWORK_MASK, %ecx # current->work jne syscall_exit_work上述代码是为了能过处理内核中进程的信号量问题。
参考资料:http://blog.chinaunix.net/uid-27767798-id-3507139.html
接着转向:
jmp restore_nocheck
RESTORE_REGS 4 //忽略orig_eax和error_code
接着便是退出
CFI_ENDPROC
从而不再进行内核处理过程,返回用户态。
第五章 总结
查阅相关资料后,我明白:
1、基本上用户态到内核态需要int 0x80进行中断,产生中断向量后才可以转换;
2、系统内部调用涉及CPU架构等内容,不同的CPU对于系统调用的汇编具体代码是不一样的。
3、之所以要有高级语言,是因为汇编语言对于底层而言太过于依赖,没有跨平台可移植性。这也加深了我对高级语言的理解。高级语言通过对系统底层细节的屏蔽完成了跨平台实现,其中最出名的例子可以说是Java了。而C则是第一个跨平台的语言。对此,对前辈们的艰苦努力表示敬意。
附录
卢晅 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
相关文章推荐
- 作业五:分析system_call中断处理过程
- 分析system_call中断处理过程
- Linux内核分析学习笔记:system_call中断处理过程
- 分析system_call中断处理过程
- 通过分析system_call中断处理过程来深入理解系统调用
- Linux内核设计第五周学习总结 分析system_call中断处理过程
- system_call中断处理过程分析
- Linux内核分析:分析system_call中断处理过程
- 分析system_call中断处理过程
- 分析system_call中断处理过程
- Linux内核分析——分析system_call中断处理过程
- 分析system_call中断处理过程
- 实验五分析system_call中断处理过程
- Linux内核分析之简析system_call中断处理过程
- Linux内核分析实验五—分析system_call中断处理过程
- 实验五:分析system_call中断处理过程
- 分析system_call中断处理过程
- Linux内核分析课程--分析system_call中断处理过程
- 分析system_call中断处理过程
- Linux内核分析-分析system_call中断处理过程