Почему 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, чтобы избежать этого исключения.