首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

利用异常表处理 Linux 内核态缺页异常(3)

利用异常表处理 Linux 内核态缺页异常(3)

异常表的实现机制笔者取include/asm-i386/uaccess.h中的宏定义__copy_user编写了一段程序作为例子加以讲解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* hello.c */
#include <stdio.h>
#include <string.h>
#define __copy_user(to,from,size)               \
do {                                \
    int __d0, __d1;                     \
    __asm__ __volatile__(                   \
        "0: rep; movsl\n"               \
        "   movl %3,%0\n"               \
        "1: rep; movsb\n"               \
        "2:\n"                      \
        ".section .fixup,\"ax\"\n"          \
        "3: lea 0(%3,%0,4),%0\n"            \
        "   jmp 2b\n"               \
        ".previous\n"                   \
        ".section __ex_table,\"a\"\n"           \
        "   .align 4\n"                 \
        "   .long 0b,3b\n"              \
        "   .long 1b,2b\n"              \
        ".previous"                     \
        : "=&c"(size), "=&D" (__d0), "=&S" (__d1)       \
        : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from)  \
        : "memory");                        \
} while (0)
int main(void)
{
        const   char    *string = "Hello, world!";
        char    buf[20];
        unsigned long   n, m;
        m = n = strlen(string);
        __copy_user(buf, string, n);
        buf[m] = '\0';
        printf("%s\n", buf);
        exit(0);
}




先看看本程序的执行结果:
1
2
3
        $ gcc hello.c -o hello
    $ ./hello
Hello, world!




显然,这就是一个简单的"hello world"程序,那为什么要写得这么复杂呢?程序中的一大段汇编代码在内核中才能体现出其价值,笔者将其加入到上面的程序中,是为了后面的分析而准备的。
系统在核心态运行的时候,参数是通过寄存器来传递的,由于寄存器所能够传递的信息有限,所以传递的参数大多数是指针。要使用指针所指向的更大块的数据,就需要将用户空间的数据拷贝到系统空间来。上面的__copy_user在内核中正是扮演着这样的一个拷贝数据的角色,当然,内核中这样的宏定义还很多,笔者也只是取其中的一个来讲解,读者如果感兴趣的话可以看完本文以后自行学习。
如果读者对于简单的嵌入式汇编还不是很了解的话,可以参考《Linux内核源代码情景分析》一书。下面我们将程序编译成汇编程序来加以分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    $ gcc -S hello.c
/* hello.s */
    movl    -60(%ebp), %eax
    andl    $3, %eax
    movl    -60(%ebp), %edx
    movl    %edx, %ecx
    shrl    $2, %ecx
    leal    -56(%ebp), %edi
    movl    -12(%ebp), %esi
#APP
    0:  rep; movsl
    movl %eax,%ecx
1:  rep; movsb
2:
.section .fixup,"ax"
3:  lea 0(%eax,%ecx,4),%ecx
    jmp 2b
.previous
.section __ex_table,"a"
    .align 4
    .long 0b,3b
    .long 1b,2b
.previous
#NO_APP
    movl    %ecx, %eax




从上面通过gcc生成的汇编程序中,我们可以很容易的找到访问用户地址空间的指令,也就是程序中的标号为0和1的两条语句。而程序中伪操作.section的作用就是定义了.fixup和__ex_table这样的两个段,那么这两段在可执行程序中又是如何安排的呢?下面就通过objdump给读者一个直观的概念:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
                  $ objdump --section-headers hello
hello:     file format elf32-i386
Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .interp       00000013  080480f4  080480f4  000000f4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
    ………………………………
  9 .init         00000018  080482e0  080482e0  000002e0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
10 .plt          00000070  080482f8  080482f8  000002f8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .text         000001c0  08048370  08048370  00000370  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .fixup        00000009  08048530  08048530  00000530  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini         0000001e  0804853c  0804853c  0000053c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata       00000019  0804855c  0804855c  0000055c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
15 __ex_table    00000010  08048578  08048578  00000578  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .data         00000010  08049588  08049588  00000588  2**2
                  CONTENTS, ALLOC, LOAD, DATA
                  CONTENTS, READONLY
    ………………………………
26 .note         00000078  00000000  00000000  0000290d  2**0
                  CONTENTS, READONLY




上面通过objdump显示出来的可执行程序的头部信息中,有一些是读者所熟悉的,例如.text、.data以及被笔者省略掉的.bss,而我们所关心的是12和15,也就是.fixup和__ex_table。对照hello.s中段的定义来看,两个段声明中的FLAGS字段分别为'ax'和'a',而objdump的结果显示,.fixup段是可重定位的代码段,__ex_table段是可重定位的数据段,两者是吻合的。
返回列表