linux 内核异常,Linux内核分析-异常处理

对IDT的初始化就懒得记录了.以后有心情就补.

文件: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 |

+-----------+

| 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 |

+-----------+

| 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...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值