Почему linux установил для сегмента данных значение __USER_DS в прологе обработчика исключений

Я пытаюсь прочитать исходный код Linux (2.6.11)

В обработчике исключений на entry.s код ошибки:

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

Я не знаю, зачем загружать сегмент пользовательских данных здесь. Поскольку предполагается, что он вводит код обработчика исключений, который выполняется в режиме ядра, селектор должен быть __KERNEL_DS.

Я проверил другие версии кода, они тоже делают то же самое в этом месте.

2 ответа

Решение

Если обработчик исключения введен с ds а также es уже установлен на сегмент данных, это не имеет значения, за исключением, возможно, микросекунды задержки. Обработчики исключений обычно не должны быть быстрыми.

Но что может вызвать переход к обработчику исключений? Может ли это быть потому, что в регистр сегмента загружено неверное значение, а затем на него ссылаются? В таких случаях для кода важно создать безопасную среду. cs устанавливается вызовом исключения. Быть пуленепробиваемым, ss а также esp должен быть настроен тоже.


Следовать за:

Глядя на ядро ​​2-6.22.18 для i386, я не вижу именно этого:

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

Символ __KERNEL_PERCPU определен макрос (в include/asm-i386/segment.h) как 0 для не SMP машин и (GDT_ENTRY_PERCPU * 8) для SMP. 8 для размера записи GDT (я думаю) и GDT_ENTRY_PERCPU относится к записям в GDT для каждого процессора. Его ценность <base> + 15 в комментариях указывается "пользовательский DS по умолчанию", так что это фактически одно и то же.

Сегмент данных ядра доступен через fs а также ss, Большая часть доступа к данным ядра находится в стеке. Сохраняя дескрипторы режима пользователя доступными через dsтребуется очень небольшая загрузка регистров сегментов.

В записи.

#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, что показывает, что iret сама вызовет исключение, если DPL ds&es не равен 3(привилегия пользователя).

Поэтому linux установил ds&es в USER_DS в самом начале исключения / прерывания / syscalls, чтобы избежать этого исключения.

Другие вопросы по тегам