zoukankan      html  css  js  c++  java
  • Linux异常表

    一、为什么需要异常表?

        处于内核态的程序有下面四种情况会产生缺页异常:

        1、内核试图访问属于进程地址空间的页,但是,该页对应的页框不存在或者内核试图去访问一个只读的页,分别对应“请求调页”和“写时复制”两种情况。

        2、内核寻址到属于内核地址空间的页,但是相应的页表项没有被初始化,这个对应“非连续内存区访问”。

        3、内核代码包含编程错误,函数执行产生异常,这是一个内核漏洞。

        4、系统调用服务例程试图读写一个内存区,而该内存区的地址是通过系统调用参数传递过来的,但确不属于进程的地址空间。

        前面两种情况缺页异常处理程序很容易识别出来,但是后面两种情况却比较难区分。为了识别出后面的两种情况,linux内核专门建立了一个异常表来辨别后两种情况。

    二、linux怎样实现异常表?

         异常表存放了内核服务例程访问进程地址空间的每条指令的地址。这个表存放在内核代码段的__ex_table节,其起始和终止地址由C编译器产生的两个符号__start__ex_table和__stop__ex_table来标识,在linux内核链接脚本文件中将每个目标文件中的__ex_table节合并,并定义了__start__ex_table和__stop__ex_table来标识。

      __start___ex_table = .;   //异常表
      __ex_table : { *(__ex_table) }
      __stop___ex_table = .;

         此外,每个动态装载的内核模块包含自己的局部异常表,当模块被加载进内核时,这个表也被装入内存。

         每个异常表的表项都是一个exception_table_entry结构:

    struct exception_table_entry
    {
        unsigned long insn, fixup;
    };

         insn是访问进程地址空间的指令的线性地址。当存放insn单元中的指令所触发的缺页异常发生时,fixup就是要调用的汇编指令代码地址,通常这个汇编代码强制服务例程返回一个出错码给用户态进程。

         一般通过下面的汇编伪指令向异常表中插入一个表项:

    .section __ex_table, “a”
        .long faulty_instruction_address, fixup_code_address
    .previous

         faulty_instruction_address是访问进程地址空间指令的地址,fixup_code_address是faulty_instruction_address指向的指令所引起异常的修正代码,通常是给用户程序返回错误码。

         例如在访问进程地址空间的内核代码中:

    __get_user_1:
        GET_THREAD_INFO(%edx)
        cmpl TI_addr_limit(%edx),%eax
        jae bad_get_user
    1:    movzbl (%eax),%edx
        xorl %eax,%eax
        ret
    
    __get_user_2:
        addl $1,%eax
        jc bad_get_user
        GET_THREAD_INFO(%edx)
        cmpl TI_addr_limit(%edx),%eax
        jae bad_get_user
    2:    movzwl -1(%eax),%edx
        xorl %eax,%eax
        ret
    
    __get_user_4:
        addl $3,%eax
        jc bad_get_user
        GET_THREAD_INFO(%edx)
        cmpl TI_addr_limit(%edx),%eax
        jae bad_get_user
    3:    movl -3(%eax),%edx
        xorl %eax,%eax
        ret
    
    bad_get_user:
        xorl %edx,%edx
        movl $-14,%eax
        ret
    
    .section __ex_table,"a"
        .long 1b,bad_get_user
        .long 2b,bad_get_user
        .long 3b,bad_get_user
    .previous

         从上面的汇编代码中可以知道,真正访问进程地址空间的指令是标号1,2,3处的指令,所以只需要把这三处的指令地址放到异常表中就可以了,bad_get_user是处理这个三条指令引起异常的修改代码。

    三、怎样通过异常表来判断异常类型?

         当内核态发生缺页异常时,缺页异常处理程序先排除了前面两种情况,然后检查异常表:如果表中包含产生异常的指令地址,那么这个错误就是由非法的系统调用参数引起的,也就是第四种情况,否则,就是由某一更严重的内核bug引起的,即第三种情况。

  • 相关阅读:
    BZOJ 2212/BZOJ 3702
    BZOJ 4761 Cow Navigation
    BZOJ 3209 花神的数论题
    BZOJ 4760 Hoof, Paper, Scissors
    BZOJ 3620 似乎在梦中见过的样子
    BZOJ 3940 Censoring
    BZOJ 3942 Censoring
    BZOJ 3571 画框
    BZOJ 1937 最小生成树
    BZOJ 1058 报表统计
  • 原文地址:https://www.cnblogs.com/chengxuyuancc/p/3428944.html
Copyright © 2011-2022 走看看