Linux内核分析-异常处理
2010-10-09 13:15
127 查看
对IDT的初始化就懒得记录了.以后有心情就补.
文件:arch/x86/kernel/entry_32.S
CPU异常的时候有的异常会压入错误码有的不,为了保证栈格式统一,对于没有压入错误码的异常内核会自己压入一个0.
对于有错误吗的就略过压0这一步.代码中的宏基本不用看,貌似不太影响阅读,不过我也真的想弄清这些宏的作用,有谁知道麻烦指导一下.
我们知道对x86CPU会在异常的时候自动向栈中顺序压入ss, esp, eflags, cs, eip,所以就可以得出当前内核栈的情况如下
上面的代码接着又调往error_code处执行,在跳往error_code前压入了一个地址,为什么要在jmp前压入这个地址一会儿再讲,现在先来看看error_code处的代码.可以看到其实error_code的代码是嵌在处理page_fault异常的代码内部的,个人觉得这绝不是写这段代码的人偷懒为了在page_fault中少写一个jmp error_code.我认为因为page_fault这个异常应该是内核中最经常发生的一个异常,所以省去的这一条指令也体现了Linux内核设计者们在细微之处的周到用心.
好了我们直接来看error_code处的代码,从1218行到1245行都是一系列的push操作,非常简单.这个时候栈的变化情况我们也能了解清楚了.
1249-1250:把__KERNEL_PERCPU存入到fs中,__KERNEL_PERCPU是GDT中第27(offset to per-cpu data area)个描述符的偏移值,貌似是给SMP用的.
1252:把gs存入ecx中
1253:意思是把栈中gs的位置的值存入edi中,你也许会说你在栈上找不到gs,其实存gs的位置就是funcaddr的位置.
1254-1255:把栈上的错误码(errcode)存入edx中,并把栈上的该值改写为-1.
1256:把ecx中的值存入栈上的gs的位置,即funcaddr的位置,通过上面分析,ecx中的值其实就是gs.
1258-1260:将ds和es设为用户数据段.
1262:就是将栈的位置存入eax.
现在我们整理一下:
栈上的errcode已经被设为-1,funcaddr已经被设为gs,而errcode和funcaddr分别被存入了edx和edi中,1263行就是要跳到edi指向的地方也就是funcaddr处执行.我们想如果funcaddr处理的函数需要访问这个栈怎么办呢?没事,已经通过eax传递这个参数了.不难看出funcaddr就是在压错误码之后压入的那个参数,当funcaddr完成后会返回到1264行执行jmp ret_from_exception.
To Be Continued...
文件:arch/x86/kernel/entry_32.S
CPU异常的时候有的异常会压入错误码有的不,为了保证栈格式统一,对于没有压入错误码的异常内核会自己压入一个0.
]ENTRY(overflow) RING0_INT_FRAME pushl $0 CFI_ADJUST_CFA_OFFSET 4 pushl $do_overflow CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC END(overflow)
对于有错误吗的就略过压0这一步.代码中的宏基本不用看,貌似不太影响阅读,不过我也真的想弄清这些宏的作用,有谁知道麻烦指导一下.
]ENTRY(invalid_TSS) RING0_EC_FRAME pushl $do_invalid_TSS CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC END(invalid_TSS)
我们知道对x86CPU会在异常的时候自动向栈中顺序压入ss, esp, eflags, cs, eip,所以就可以得出当前内核栈的情况如下
高地址 +-----------+ | ss | +-----------+ | esp | +-----------+ | eflags | +-----------+ | cs | +-----------+ | eip | +-----------+ | errcode|0 | +-----------+ | retaddr | +-----------+<--esp | xxx | +-----------+ | xxx | +-----------+ 低地址
上面的代码接着又调往error_code处执行,在跳往error_code前压入了一个地址,为什么要在jmp前压入这个地址一会儿再讲,现在先来看看error_code处的代码.可以看到其实error_code的代码是嵌在处理page_fault异常的代码内部的,个人觉得这绝不是写这段代码的人偷懒为了在page_fault中少写一个jmp error_code.我认为因为page_fault这个异常应该是内核中最经常发生的一个异常,所以省去的这一条指令也体现了Linux内核设计者们在细微之处的周到用心.
]ENTRY(page_fault) RING0_EC_FRAME pushl $do_page_fault CFI_ADJUST_CFA_OFFSET 4 ALIGN error_code: /* the function address is in %gs's slot on the stack */ pushl %fs CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET fs, 0*/ pushl %es CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET es, 0*/ pushl %ds CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET ds, 0*/ pushl %eax CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET eax, 0 pushl %ebp CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET ebp, 0 pushl %edi CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET edi, 0 pushl %esi CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET esi, 0 pushl %edx CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET edx, 0 pushl %ecx CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET ecx, 0 pushl %ebx CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET ebx, 0 cld movl $(__KERNEL_PERCPU), %ecx movl %ecx, %fs UNWIND_ESPFIX_STACK GS_TO_REG %ecx movl PT_GS(%esp), %edi # get the function address movl PT_ORIG_EAX(%esp), %edx # get the error code movl $-1, PT_ORIG_EAX(%esp) # no syscall to restart REG_TO_PTGS %ecx SET_KERNEL_GS %ecx movl $(__USER_DS), %ecx movl %ecx, %ds movl %ecx, %es TRACE_IRQS_OFF movl %esp,%eax # pt_regs pointer call *%edi jmp ret_from_exception CFI_ENDPROC END(page_fault)
好了我们直接来看error_code处的代码,从1218行到1245行都是一系列的push操作,非常简单.这个时候栈的变化情况我们也能了解清楚了.
高地址 +-----------+ | ss | +-----------+ | esp | +-----------+ | eflags | +-----------+ | cs | +-----------+ | eip | +-----------+ | errcode|0 | +-----------+ | retaddr | +-----------+ | fs | +-----------+ | es | +-----------+ | ds | +-----------+ | eax | +-----------+ | ebp | +-----------+ | edi | +-----------+ | esi | +-----------+ | edx | +-----------+ | ecx | +-----------+ | ebx | +-----------+ <--esp | xxx | +-----------+ 低地址
1249-1250:把__KERNEL_PERCPU存入到fs中,__KERNEL_PERCPU是GDT中第27(offset to per-cpu data area)个描述符的偏移值,貌似是给SMP用的.
1252:把gs存入ecx中
1253:意思是把栈中gs的位置的值存入edi中,你也许会说你在栈上找不到gs,其实存gs的位置就是funcaddr的位置.
1254-1255:把栈上的错误码(errcode)存入edx中,并把栈上的该值改写为-1.
1256:把ecx中的值存入栈上的gs的位置,即funcaddr的位置,通过上面分析,ecx中的值其实就是gs.
1258-1260:将ds和es设为用户数据段.
1262:就是将栈的位置存入eax.
现在我们整理一下:
栈上的errcode已经被设为-1,funcaddr已经被设为gs,而errcode和funcaddr分别被存入了edx和edi中,1263行就是要跳到edi指向的地方也就是funcaddr处执行.我们想如果funcaddr处理的函数需要访问这个栈怎么办呢?没事,已经通过eax传递这个参数了.不难看出funcaddr就是在压错误码之后压入的那个参数,当funcaddr完成后会返回到1264行执行jmp ret_from_exception.
To Be Continued...
相关文章推荐
- Tomcat CPU占用100%异常分析与处理
- ARM的异常处理过程分析
- Nodejs异步回调之异常处理实例分析
- linux内核分析笔记----中断和中断处理程序
- 异常处理:ASM ClassReader failed to parse class file的分析与解决
- 第5节 分析system_call中断处理过程【Linux内核分析】
- R语言︱异常值检验、离群点分析、异常值处理
- linux内核分析笔记----中断和中断处理程序
- Android SQLite数据库SQLiteDatabaseLockedException异常分析及处理
- JAVA异常处理原则和log4j输出详细异常分析
- MYSQL异常处理日志:主从库同步延迟时间过长的分析
- java中异常的处理和分析
- svchost.exe[1348]中发生未处理的win32异常 分析
- linux内核中断、异常、系统调用的分析以及实践
- 关于bug分析与异常处理的一些思考
- (转)J2SE综合:JAVA异常处理方式的区别和分析。
- GO语言异常处理机制panic和recover分析
- JAVA异常处理原则和log4j输出详细异常分析
- JAVA异常处理分析高级进界(下)
- Linux内核学习之四--进程、进程调度、系统调用、proc文件系统和内核异常分析