In kernel 2.6.11.5, divide zero exception handler is set up as:
set_trap_gate(0,÷_error);
According to "Understanding The Linux Kernel", Intel trap gate cannot be accessed by a User Mode process. But it's quite possible that a user mode process also generate a divide_error
. So why Linux implement it in this way?
[Edit]
I think that the question is still open, since set_trap_gate()
sets DPL value of IDT entry to 0, which means only CPL=0 (read kernel) code can execute it, so it's unclear to me how this handler may be called from the user mode:
#include<stdio.h>
int main(void)
{
int a = 0;
int b = 1;
b = b/a;
return b;
}
which was compiled with gcc div0.c
. And the output of ./a.out
is:
Floating point exception (core dumped)
So it doesn't look like this was handled by the division by 0 trap code.
DPL bit in IDT is looked at only when software interrupt is called with the int instruction. Division by zero is a software interrupt triggered by the CPU and thus has DPL has no effect in this case
I have the Linux kernel 3.7.1 sources on the hands, and due to this I will try to provide the answer to your question on the base of those sources. What we have in the code. In the
arch\x86\kernel\traps.c
we have functionearly_trap_init()
where the next code line can be found:As we can see the
set_trap_gate()
was replaced byset_intr_gate()
. If in the next turn we expand this call we will achieve:_set_gate
is a routine that is responsible for two things:Installing of the constructed descriptor into the target cell in the IDT descriptors array. The second one is just memory copying and isn't interesting for us. But if we will look at how it constructs descriptor from the supplied parameters we will see:
Or finally
As we can see at the end of the descriptor construction we will have the next 8-bytes data structure in memory
These 8-bytes data structure is a processor defined interrupt descriptor. It is used by processor to identify what actions must be taken in reply to the acceptance of interrupt with particular vector. Let’s now look to the format of the interrupt descriptor defined by Intel for x86 processors family:
In this format the pair of SELECTOR:OFFSET defines the address of the function (in the long format) that will take control in reply to the interrupt acceptance. In our case this is
__KERNEL_CS:divide_error
, where thedivide_error()
is actual handler of the Division By Zero exception. P flag specifies is that descriptor should be considered as a valid descriptor that was correctly setup by OS and in our case it in raised state. DPL - specify the security rings on which thedivide_error()
function can be triggered by using soft interrupts. Some background needed to understand the role of that field.In general there are three kinds of interrupt sources:
The last case has special support from the processor in the form of dedicated instruction int XX. Each time when the program wants the OS service, it setup parameters that describes request and issue int instruction with parameter that describes the interrupt vector, which is used by OS for service providing. Interrupts generated by issuing of the int instruction called soft interrupts. So here, the processor takes DPL field into account only when it handle soft interrupts, a completely ignore them in the case of interrupts generated by processor itself or by external devices. DPL is a very important feature, because it prohibits applications to simulate devices, and by this imply to the system behavior.
Imagine for example that some application will make something like this:
In that case time in your computer will go much faster then in real life, then you expect and your system expect. As result your system will misbehaves very strongly. As you can see the processor and external devices are considered as trusted, but it is not a case for user mode applications. In our case of Division By Zero exception, Linux specify that this exception can be triggered by soft interrupt only from the ring 0, or in other words, only from the kernel. As result, if the int 0 instruction will be executed in the kernel space, processor will pass control to the
divide_error()
routine. If the same instruction will be executed in the user space, kernel will this as a protection violation and will pass control to the General Protection Fault exception handler (this is a default action for all invalid soft interrupts). But if the Division By Zero exception will be generated by processor itself tried to divide some value by zero, control will be passed to thedivide error()
routine regardless of the space where incorrect division was occurred. In general it looks like it won't be a big harm to allow application to trig Division By Zero exception by soft interrupt. But for the first it will be an ugly design and for the second some logic can be behind the scene, which relies to the fact that Division By Zero exception can be generated only by actual incorrect division operation.TYPE field specifies the auxiliary actions that must be taken by processor in reply to the interrupt acceptance. In real life only two types of exception descriptors is used: interrupt descriptor and trap descriptor. They differ only in one aspect. Interrupt descriptor forces the processor to disable future interrupt acceptance and trap descriptor isn't do this. If be honest, I didn't aware, why Linux kernel use the interrupt descriptor for Division By Zero exception handling. As for me, the trap descriptor is more suitable there.
And last note in regard to the confusing output of test program
By historical reasons, Linux kernel replies to the Division By Zero exception by sending SIGFPE (read SIGnal Floating Point Exception) signal to the process attempted to divide by zero. Yes, not SIGDBZ (read SIGnal Division By Zero). I know this is confusing enough. The reason of such behavior is that Linux mimics original UNIX behavior (I think that this behavior was frozen in the POSIX) and original UNIX some why consider "Division By Zero" exception as a "Floating Point Exception". I don't know why.
The kernel is not running under user mode. It has to handle the trap generated by user mode programs (e.g. linux processes in user-land). Kernel code is not expected to divide by zero.
I don't understand well your question. How would you implement it otherwise?
User-mode code has no business accessing system tables such as the segment and interrupt descriptor tables, they aren't intended to be manipulated outside of the OS kernel and there's no need to. Linux handlers for exceptions such as division by zero, general protection exception, page fault and others intercept exceptions originating from both user-mode and kernel-mode code. They may handle them differently based on the origin, but the interrupt descriptor table contains an address of just one handler for every kind of exception (e.g. the above). And every handler knows how to handle its exception.
The answer to part of your question can be found at Section 6.12.1.1 of "Intel(R) 64 and IA-32 Architectures and Software Developer's Manual, Volume 3A"
It's what Alex Kreimer answered
Regarding to the message. I'm not totally sure, but it seems that OS sends the SIGFPE signal to the process.