Chinaunix首页 | 论坛 | 博客
  • 博客访问: 374909
  • 博文数量: 44
  • 博客积分: 2060
  • 博客等级: 上尉
  • 技术积分: 528
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-17 20:50
文章分类
文章存档

2011年(1)

2010年(28)

2008年(15)

分类:

2010-03-08 13:40:39

3.1.1 Exception Table

exception table不是用于处理硬件异常的(那是IDT表的工作),但它确实和硬件异常有一点关系,具体来说是和Page Fault有关系。Exception Table的具体机制在内核文档”Exception”中有详细介绍,你可以在/path_to_your_kernel_src/ Documentation/exception.txt中找到它。这里为了说明问题做一点简要介绍。

我们尊敬的Linus大神为了避免内核在访问用户态地址时进行有效性检查带来的开销(我们总是需要这样的检查,虽然大部分情况下结果是成功的),利用了page fault的处理函数来完成这项任务,这样只有在真正访问了一个坏的用户态地址时检查才会发生。或许你会问:此时检查有什么用?一个例子就很容易说明问题,假设我们有一个函数叫is_user_addr_ok(),用于检查传入的用户态地址是否合法。那么,当地址非法时它能干什么?什么都不能干,仅仅是告诉内核:这是个非法地址,你不要访问。这样便带来了个问题,让它在90%的时间里告诉内核:这是个合法地址,去吧!是件很无聊的事情。既然该函数对非法地址无能为力,我们干脆就什么都不要干,直到内核真访问到一个非法地址时再告诉调用者:噢,抱歉,您访问到一个非法地址。不管用哪种方法,调用者遇到非法地址最终结果都是获得一个错误码,但后者明显省下了对合法地址进行检查的开销。让我们来看看如何用自定义section完成这个任务。

如果你顺着copy_from_user()向下找几层,会看到__get_user_asm宏,该宏展开后可读性太差,我们用下面的伪代码来描述它:

1:      movb (%from),(%to)  /* 这里访问用户态地址,当地址非法时会产生一个page fault*/

  2:  

/*注意,后面的代码在最终的目标文件中不是跟在标号2后的*/ 

  .section .fixup,"ax"

  3:      movl $ERROR_CODE,%eax

          xorb %dl,%dl

          jmp 2b

  .section __ex_table,"a"

          .align 4

          .long 1b,3b


上面的伪代码描述了__get_user_asm宏的用途,它将用户态地址from中的内容拷贝到内核地址to。当from是个非法地址时,会产生page fault从而执行内核的do_page_fault(),在进行一系列检查处理后fixup_exception()被调用,该函数会调用search_exception_tables()查找exception table,将EIP设置成对应handler的地址并返回。至此该非法地址造成的错误就交由exception table中的handler处理了。

所有问题的归结到了exception table的建立和错误处理handler的设置。其实上面的伪代码已经告诉我们答案了。首先,标号”1”代表了可能产生page faultEIP,当page fault产生时这个地址会被记录在struct pt_regsip字段中(不知道的看看do_page_fault()的参数);其次,标号”3”是错误处理handler的地址,很明显,它只是返回了一个错误码(EAXx86的返回值寄存器)jmp 2b跳到了产生page fault的指令的下一条指令继续执行。这里

.section .fixup,"ax"

创建了名为.fixup的自定义section,并将整个handler放入其中。标号”1”后的代码是位于.text section的,故你看到它们在源代码里写在了一起,但在目标文件中去是分开的,它们在不同的section

好了,我们已经有了会产生错误的代码地址,也有了错误处理handler的地址,

.section __ex_table,"a"

将它们放到了自定义的__ex_table section(.long 1b,3b),以如下格式存放:

出错地址,处理函数地址

内核用结构体struct exception_table_entry表示该格式,定义如下:

struct exception_table_entry {

       unsigned long insn, fixup;

};

很明显,exception table的格式简单,表项的前4个字节是出错地址,后4个字节是处理函数的地址。下图展示了通过exception table解决一次访问用户态非法地址产生的错误。

3. 通过execption table处理非法用户态地址访问的过程

    怎样,所有的事实都清楚了。当在内核在不同位置调用copy_from_user()时,展开的__get_user_asm宏都会将可能出错的地址和处理函数的地址存入该源文件对应.o文件的__ex_table section中。链接脚本的如下代码:


__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {

      __start___ex_table = .;

        *(__ex_table)

      __stop___ex_table = .;

  } :text = 0x9090

 

将分散的__ex_table收集起来产生一张完整的exception表,并将表的起始地址和结束地址记录在__start___ex_table__stop___ex_table两个全局变量中,从而search_exception_tables()函数可以顺利的索引该表。

这种通过自定义section和链接脚本构造表的技巧被大量使用,后面我们还会看到两个例子。在此先告一段落。

 

阅读(5785) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~