Linux asm ( "int $0x0" ) против деления на ноль

может кто-то объяснить разницу между командой сборки int $0x00 и выполнить фактическое деление на ноль. У меня есть точка останова на обработчике divide_error() в ядре, связанная с 0-й записью в IDT (ошибка разделения).

Когда я делаю это в своей программе на C:

int i = 5/0;

то я попал в точку останова (как и ожидалось). Тем не менее,

asm volatile ("int $0x00")

не запускает обработчик. Почему?

Ответ 1

int 0h - это не то же самое, что и улавливающая процессорная ловушка 0 из-за деления на ноль.

Эта статья Phrack неплохо объясняет IDT и то, как Linux ее устанавливает. Ключевыми частями являются:

DPL=Descriptor Privilege Level

    The DPL is equal to 0 or 3. Zero is the most privileged level (kernel
mode).  The current execution level is saved in the CPL register (Current
Privilege Level). The UC (Unit Of Control) compares the value of the CPL
register against the DPL field of the interrupt in the IDT. The interrupt
handler is executed if the DPL field is greater (less privileged) or equal
to the value in the CPL register. Userland applications are executed in
ring3 (CPL==3). Certain interrupt handlers can thus not be invoked by
userland applications.

...

linux/arch/i386/kernel/traps.c::set_system_gate(n, addr)
        insert a trap gate.
    The DPL field is set to 3.

These interrupts can be invoked from the userland (ring3).

                set_system_gate(3,&int3)
                set_system_gate(4,&overflow)
                set_system_gate(5,&bounds)
                set_system_gate(0x80,&system_call);

linux/arch/i386/kernel/traps.c::set_trap_gate(n, addr)
        insert a trap gate with the DPL field set to 0.
        The Others exception are initialized with set_trap_gate : 

                set_trap_gate(0,&divide_error)
                set_trap_gate(1,&debug)
                set_trap_gate(2,&nmi)
                set_trap_gate(6,&invalid_op)
                set_trap_gate(7,&device_not_available)
                set_trap_gate(8,&double_fault)
                set_trap_gate(9,&coprocessor_segment_overrun)
                set_trap_gate(10,&invalid_TSS)
                set_trap_gate(11,&segment_not_present)
                set_trap_gate(12,&stack_segment)
                set_trap_gate(13,&general_protection)
                set_trap_gate(14,&page_fault)
                set_trap_gate(15,&spurious_interrupt_bug)
                set_trap_gate(16,&coprocessor_error)
                set_trap_gate(17,&alignement_check)
                set_trap_gate(18,&machine_check)

Описания там объясняют это отлично. Только теги int 3, 4, 5 и 0x80 могут быть вызваны из пользовательского пространства, потому что ядро ​​устанавливает их ловушки затвора с помощью уровня DPL = 3.

Другие векторы исключений процессора имеют DPL = 0 (могут вызываться только из кольца 0).

Когда вы делите на ноль, CPU сначала переходит к Ring 0, а ядро ​​обрабатывает исключение с помощью divide_error. Когда вы вызываете его явно с помощью int 0x00, тем не менее, вы все еще находитесь (уровень текущей привилегии) ​​CPL = 3.

Для очень низкоуровневых подробных сведений вы должны ознакомиться с руководством Intel по разработке программного обеспечения. Том 2 описывает инструкцию int и описывает все шаги принятия решений, которые принимает процессор, чтобы решить, как обращаться с ловушкой/прерыванием. Том 3 описывает интимные подробности IDT, Trap Gates и т.д.

В частности, Таблица 3-61 Таблица принятия решений объясняет, что происходит в каждом из возможных способов прерывания. В вашем примере вызов int 0x00 помещает вас в столбец 2, который по существу говорит:

if PE=1                 # protected mode enabled
and DPL < CPL           # DPL=0 - kernel set up trap gate like this
                        # CPL=3 - b/c you're in user-mode
and int type == S/W     # you executed int instruction (s/w interrupt)
then issue #GP          # General Protection fault
                        # -- kernel delivers this to usermode as SIGSEGV

Дополнительная ссылка:

Ответ 2

Если вам нужен короткий ответ, это означает, что деление на ноль прерывается только ядром. Если вам нужен более длинный ответ, ознакомьтесь с ответом от ZarathustrA здесь.