5

我正在尝试阅读 Linux 源代码(2.6.11)

在异常处理程序中,在 entry.s,error_code:

movl $(__USER_DS), %ecx
movl %ecx, %ds
movl %ecx, %es

我不知道为什么在这里加载用户数据段。由于它应该进入在内核模式下运行的异常处理程序代码,因此选择器应该是__KERNEL_DS。

我检查了其他版本的代码,他们在这个地方也专门做同样的事情。

4

2 回答 2

3

如果输入异常处理程序dses已设置为数据段,则除了可能延迟一微秒之外,它没有任何区别。异常处理程序通常不需要很快。

但是什么可能导致进入异常处理程序?可能是因为错误的值被加载到段寄存器然后被引用?在这种情况下,代码建立一个安全的环境很重要。 cs由异常调用设置。为了防弹,ssesp应该设置。


跟进:

查看 i386 的 2-6.22.18 内核,我看不到这一点:

error_code:   /* the function address is in %fs's slot on the stack */
     pushl  %es
     ...  pushes %ds, %eax, %ebp, %edi, %esi, %edx, %ecx, %ebx, %fs
     ...  along with pseudo-ops to manage stack frame layout
     movl  $(__KERNEL_PERCPU), %ecx
     movl  %ecx, %fs
     popl  %ecx   // retrieves saved %fs
     ... sets up registers for the exception function

该符号是为非 SMP 机器和SMP__KERNEL_PERCPU定义的宏(在 中include/asm-i386/segment.h)。8 代表 GDT 条目大小(我认为),它与 per-CPU GDT 中的条目有关。它的值是注释指示的“默认用户 DS”,因此实际上是相同的。0(GDT_ENTRY_PERCPU * 8)GDT_ENTRY_PERCPU<base> + 15

内核数据段通过fs和访问ss。许多内核数据访问都在堆栈上。通过保持通过 访问用户模式描述符ds,只需要很少的段寄存器加载。

于 2013-08-20T01:36:29.540 回答
1

在 entry.s 中:

#define RESTORE_ALL
    RESTORE_REGS
    addl $4, %esp;
1:  iret;
.section .fixup,"ax";
2:  sti;
    movl $(__USER_DS), %edx;
    movl %edx, %ds;
    movl %edx, %es;
    movl $11,%eax;
    call do_exit;
.previous;
.section __ex_table,"a";
    .align 4;
    .long 1b,2b;
.previous

该宏将在异常/中断/系统调用结束时调用。修复代码将 ds&es 设置为 USER_DS,这表明一旦 ds&es 的 DPL 不是 3(用户权限),iret 本身就会引发异常。

所以linux在异常/中断/系统调用一开始就将ds&es设置为USER_DS来避免这个异常。

于 2013-08-23T11:33:26.430 回答