Windows exception handling process

Source: Internet
Author: User
Tags socket error
Let's talk about the difference between exceptions and interruptions. An interruption can occur at any time, regardless of the instructions being executed by the CPU. the interruption is mainly caused by hardware such as the I/O device, processor clock, or timer, and can be allowed or canceled. The exception is caused by CPU execution of certain commands, including memory access violations, Division of 0 or specific Debugging commands. The kernel also regards the system service as an exception. The underlying difference between an interrupt and an exception is that an interrupt (including an exception and a hardware interrupt) in a broad sense) if it is not set in the service register when it occurs (use the command code 0xb to read the port 0x20 from the 8259-1 interrupt controller to the service register 1, and use 0xb to read the port 0xa0 from the 8259-2 interrupt controller to the service register 2) the related in-service bit (each in the service register has 8 bits, which corresponds to a total of IRQ 0-15) is the CPU exception, otherwise the hardware is interrupted.
The following is the definition of the intel X86 processor in Windows2000. The first few items in IDT are registered as corresponding exception handling programs (different operating systems have different implementation standards, the information provided here is different from other materials because it is a specific implementation of Windows ):
Reason of Interrupt number name
0x0 Division Error 1. Div and idiv commands except 0
2. division result Overflow
0x1 debugging Trap 1. TF position of eflag
2. Execute the breakpoint set to the debug register (DR0-DR4)
3. Execute the int 1 command.
0x2 NMI interrupt positions the NMI input pin of the CPU (this exception is retained because the hardware is unshielded)
0x3 breakpoint execute INT 3 command
0x4 integer overflow execute into command and of location
0x5 bound check error bound command compares values outside the specified range
0x6 invalid operation code cannot be identified
0x7 coprocessor unavailable 1. execute any coprocessor commands when the EM position of Cr0
2. Environment switching is performed when the coprocessor is working.
0x8 double exception handling exception another exception occurs
0x9 co-processor segment out of limit floating point instruction Reference Memory exceeds end of segment
Invalid 0xa task segment the descriptor contained in the task segment is invalid (Windows does not use TSS for Environment switching, so this exception indicates other problems)
0 x B segment does not have referenced segment replaced by memory
0xc stack Error 1. The referenced memory exceeds the stack segment limit.
2. Add the present position of the descriptor loaded into the SS register to 0
0xd general protective error all exceptions that cannot be handled by other exception handling routines
0xe page Error 1. The accessed address is not swapped into the memory
2. Access Operations violate page protection rules
0x10 indicates that the wait or escape command is executed when the EM position of Cr0 is incorrect in the coprocessor.
0x11 alignment check error alignment check when enabled (eflag alignment position) other exceptions include getting the system startup time service int 0x2a, user callback int 0x2b, System Service int 0x2e, and debugging service int 0x2d, the trigger method is to execute the corresponding int command.
Here are several important structures for exception handling:
Trap frame trapframe structure (the exception frame predictionframe structure mentioned later is actually a ktrap_frame structure ):
Typedef struct _ ktrap_frame {
Ulong dbgebp;
Ulong dbgeip;
Ulong dbgargmark;
Ulong dbgargpointer;
Ulong tempsegcs;
Ulong tempesp;
Ulong dr0;
Ulong DR1;
Ulong DR2;
Ulong dr3;
Ulong dr6;
Ulong dr7;
Ulong seggs;
Ulong seges;
Ulong segds;
Ulong edX;
Ulong ECx;
Ulong eax;
Ulong previouspreviousmode;
Pexception_registration_record exceptionlist;
Ulong segfs;
Ulong EDI;
Ulong ESI;
Ulong EBX;
Ulong EBP;
Ulong errcode;
Ulong EIP;
Ulong segcs;
Ulong eflags;
Ulong hardwareesp;
Ulong hardwaresegss;
Ulong v86es;
Ulong v86ds;
Ulong v86fs;
Ulong v86gs;
} Ktrap_frame;
Environment Context Structure:
Typedef struct _ context {
Ulong contextflags;
Ulong dr0;
Ulong DR1;
Ulong DR2;
Ulong dr3;
Ulong dr6;
Ulong dr7;
Floating_save_area floatsave;
Ulong seggs;
Ulong segfs;
Ulong seges;
Ulong segds;
Ulong EDI;
Ulong ESI;
Ulong EBX;
Ulong edX;
Ulong ECx;
Ulong eax;
Ulong EBP;
Ulong EIP;
Ulong segcs;
Ulong eflags;
Ulong ESP;
Ulong segss;
Uchar extendedregisters [maximum_supported_extension];
} Context;
Exception record predictionrecord structure:
Typedef struct _ exception_record {
Ntstatus exceptioncode;
Ulong exceptionflags;
Struct _ prediction_record * predictionrecord;
Pvoid exceptionaddress;
Ulong numberparameters;
Ulong_ptr predictioninformatio [prediction_maximum_parameters];
} Prediction_record;
When an exception occurs, the CPU records the status of the current registers and establishes a trapframe in the kernel stack. Then, the control is handed over to the corresponding exception trap handler. When the trap handler can handle exceptions, for example, when a page is missing, the paging program mmaccessfault will switch the page into the physical memory and then return the exception through iret. However, most exceptions cannot be handled. In this case, the commondispatchexception is called to create an exception record exceptionrecord and an exception frame exceptionframe in the kernel stack. Exceptionrecord is very important. It records the Exception Code, exception address, and some other additional parameters. Then, kidispatchexception is called to dispatch exceptions. This function is the core function for exception handling in windows and is responsible for exception dispatching.
Kidispatchexception processing process (each time an exception is processed by a routine, the routines processed will return true to the previous routine, and false if not processed. If any routine processes an exception and returns true, kidispatchexception is returned normally ):
Before distributing user-mode kernel state exceptions, determine whether the exceptions come from the user mode. contextflags (at this time, the context structure has just been initialized and the initial value has not been assigned) or on conext_floating_point, which means that exceptions from user mode always try to assign floating point status, this allows the exception handler or debugger to check and modify the state of the coprocessor. Then, the register value is retrieved from the trap frame and filled in the context structure, and the result is a breakpoint exception (INT 0x3 and INT 0 x 2d). If yes, the context is first taken. the EIP minus one points it to the int 0x3 command (whether it is caused by INT 0x3 or Int 0 x 2D, because the EIP in trapframe has been changed in the previous trap handler ). Then, determine whether the exception occurs in the kernel mode or the user mode, and adopt different processing processes according to different modes.
If an exception occurs in kernel mode, the first and second chance of the kernel debugger to handle the exception is given. After an exception is processed, the trap frame is set and returned to the trap handler, where iret returns the exception and continues execution.
Kernel Mode exception handling process:
(First opportunity) Determine if kidebugroutine is empty. If it is not empty, press context, trap frame, exception record, exception frame, mode in which exception occurs, and submit the control to kidebugroutine.
If kidebugroutine is empty (it is not empty in normal systems. The kidebugroutine of the normal boot system is kdpstub, And the kidebugroutine of the system started with/debug is kdptrap in the boot. ini file. If it is null, the system will crash because it cannot handle the exception of int 0x2d such as dbuplint) or the kidebugroutine will not handle the exception, then, the context structure and exception record predictionrecord are called and the rtldispatchexception in kernel mode is called to find frame-based exception handling routines in the kernel stack.
Rtldispatchexception calls rtlpgetregistrationhead to get the linked list pointer of the current thread Exception Handling from FS: [0] (0xffdff000), and CALLS rtlpgetstacklimits to retrieve the bottom and top of the current thread Stack from 0xffdff004 and 0xffd. Then we start to look up the exception handling routine from the exception handling linked list pointer to the traversal chain table (if veh is processed in XP and 2003 and then seh is processed), this is actually the Seh, the difference is that there is no top-level seh or default exception handling routine. Then, check the linked list pointer for each current exception handling to determine whether the stack is valid (whether the stack range is exceeded or not aligned) and whether the stack is a DPC stack. If the dpcroutineactive at 0xffdff80c is true and the dpcstack at the top and bottom of the stack is taken at 0xffdff81c to the DpcStack-0x3000 (a kernel stack size), if the top and bottom of the stack are updated to the dpcstack and DpcStack-0x3000 and continue processing, otherwise, the exception flag in the exception record structure is predictionrecord. when exceptionflags is set to exception_stack_invalid, it indicates that the stack is invalid and false is returned.
Before calling the exception handling routine on the exception handling linked list, a new node will be inserted into the linked list of the exception handling routine. The corresponding exception handling routine is used to handle nested exceptions, that is, another exception occurs when processing the exception. After processing, rtldispatchexception determines the return value of the exception handling routine:
If it is predictioncontinueexecution, the exception indicates predictionrecord. when predictionflags is not set to prediction_noncontinuable and cannot be resumed, true is returned to the previous layer. Otherwise, after some work is done, rtlraiseexception is called to enter the second opportunity processing part of the kidispatchexception.
If it is predictioncontinuesearch, continue to find the exception handling routine.
If it is predictionnestedexception, an exception occurs in nesting. Keep the pointer to the current Exception Handling linked list as the internal Exception Handling linked list and continue searching for exception handling routines. When the current abnormal handling linked list address is found to be greater than the reserved inner list, it indicates that the current abnormal handling linked list is more inner than the retained list (because the stack is extended from high to low, the higher the address, the earlier it is written into the stack, indicating a more inner layer). Then, the value is assigned to the inner layer of the exception handling linked list pointer, in addition to the initial value assigned for the first time, an internal layer exception is modified and retained to handle the linked list pointer. In this case, more than one exception is nested. When a new exception handling linked list pointer is found to be the same as the reserved internal Exception Handling linked list pointer, the nested exception bit of exceptionrecord. exceptionflags is cleared (&(~ Prediction_nested_call), indicating that the nested exception has been processed.
Other returned values are considered invalid. Call rtlraiseexception to return to the second opportunity of idispatchexception.
When the exception handling linked list pointer is 0xffffffff, the exception handling routine linked list has arrived.
If the rtldispatchexception cannot be handled, the kidebugroutine (the second opportunity) determines whether it is null. If it is not null, it is handed over to kidebugroutine for processing.
When all routines cannot handle exceptions, call the kebugcheckex blue screen. The error code is kmode_exception_not_handled, indicating that no one has handled the exception. The system regards this exception as a fatal exception that cannot be recovered. At this point, exception handling is completed in kernel mode.
If an exception occurs in user mode, the debugger is also given the first and second debugging opportunities. Only because the consumer is a user-type consumer, the message is sent to the Session Manager smss.exe through the message manager, and then forwarded to the debugger by the Session Manager.
User Mode exception handling process:
If kidebugroutine is not empty, if it is not empty, the context, trap frame, exception record, exception frame, and abnormal mode are pushed to the stack and the control is handed over to kidebugroutine. When the processing is complete, use context to set the trap frame and return it to the upper-level routine. (First chance) Otherwise, press the exception record to the stack and call dbgkforwardexception. In dbgkforwardexception, judge the hidefromdebugger Member of the ETHREAD structure of the current thread. If it is false (true, the exception is invisible to the debugger) the LPC message is sent to the debug port (debugport) of the current process.
When the exception cannot be handled in the previous step, copy the context structure to the user stack and set a trap frame in the stack. The EIP of the trap frame is Ke (I) userexceptiondisptcher (I) indicates that the KE and Ki headers of this function are actually the same thing.) then, the trap handler is returned, and the trap handler iret returns the user State to execute Ke (I) userexceptiondispatcher (this function is not a function in the kernel, although it is a Ke (Ki) header. There are also special features such as Ke (I) raiseuserexceptiondispatcher, Ke (I) usercallbackdispatcher, and Ke (I) userapcdispatcher. They are not called. Instead, the kernel routine sets the trapframe. EIP of the trap frame as the function and then the iret executes it here ). Ke (I) userexceptiondisptcher calls rtldispatchexception (in user mode) to find frame-based exception handling routines in the stack (if veh is processed before seh is processed in XP and 2003 ), you should be familiar with this process, that is, searching the seh linked list. If none of them are processed, the top level seh routine will be called. When the processing fails, call the default exception handling routine to terminate the process (if VC is available, the process is changed to VC ). The difference is that rtldispatchexception in user mode only determines whether the returned value is predictioncontinueexecution or predictioncontinuesearch. If rtldispatchexception finds that the exception handling routine can handle the exception, call zwcontinue to continue execution according to the configured context structure. Otherwise, call zwraiseexception and set the third Boolean parameter to false, indicates the second opportunity for processing.
After a series of calls, zwraiseexception directly calls kidispatchexception. Because the Boolean value firstchance is set to false, the Second Chance is handled in kidispatchexception.
(Second Opportunity) send a message to the debugport Of the process. If it cannot be processed, send a message to the exceptionport of the process (if the exception is invisible to the user debugger, it will only be sent to the exceptionport ). The difference between debugport and exceptionport is that if a message is sent to the exceptionport, stop the execution of all the threads of the target process until the thread resumes execution after receiving the Response Message, sending messages to debugport does not need to stop the thread. In addition, debugport sends messages to the Session Manager, while exceptionport sends messages to the Win32 subsystem. When sending messages to the exceptionport, it no longer gives the user-mode debugger any chance :).
If the exception persists, terminate the current thread and call the kebugcheckex blue screen. The error code is kmode_exception_not_handled. So far, exception handling is completed in user mode.
One thing to note is that the process of the operating system is listed here. If _ Try/_ Try T code is added to a driver or application, but I didn't find out the relationship between the exception handling routine corresponding to the node on the seh and what I wrote. The reason is that the M $ Compiler (such as VC ++ and DDK) encapsulates the seh mechanism in the system. The exception handling routine on the linked list node of the exception handling routine is actually _ effect_handler3. This function itself implements a mechanism similar to seh internally, that is, by creating a table for each function, includes all the functions in the _ Try block corresponding to the filter exception routine pointer (_ Try () in brackets, if it is in parentheses such as prediction_execute_handler, the filter exception routine is very simple to assign eax to the corresponding 1, 0 or-1 and then return, corresponding to prediction_execute_handler, prediction_continue_search, prediction_continue_execute) and the processing routine pointer (the address of the Code in _ Finally t {} and _ finally ). Therefore, each called function registers a node on the seh linked list and creates a table. Such as prediction_execute_handler, xception_continue_search, and prediction_continue_execute are actually returned to _ except_handler3 rather than rtldispatchexception.
A little dizzy. Summarize the process (this refers to the complete process in which an exception is not handled. if an exception is handled in any stage, it is exited from this stage to continue the execution of the original normal program code ):
In kernel mode:
Kidispatchexception-> (first chance) kidebugroutine-> tldispatchexception in the kernel stack to find Seh (veh)-> (Second Chance) kidebugroutine-> kebugcheckex
In User Mode:
Kidispatchexception-> kidebugroutine-> (first chance) send a message to the process debugging port-> rtldispatchexception in the user stack to find Seh (veh)-> zwraiseexception return to kidispatchexception-> (Second Chance) send a message to the process debugging or abnormal port-> kebugcheckex.
That's simple. An example is provided to illustrate how to handle exceptions.
In our program, we add int 0x3 to debug the program. How can we handle the exceptions generated by INT 0x3?
If int 0x3 occurs in kernel mode (in the driver), it is also divided into whether the kernel debugger is loaded. If the kernel debugger is not loaded and the kidebugroutine (kdpstub) does not handle the int 0x3 exception, search for the seh registered by the driver writer in the stack. If no exception is handled, then, we had to call the kebugcheckex blue screen. The error code was kmode_exception_not_handled (no one was working, and the system had to settle it angrily ). If the kernel debugger is loaded, kidebugroutine (kdptrap) can handle the int 0x3 exception, and the exception is returned normally. The solution is to send the current processor status (such as important information such as registers) to the kernel debugger on the host connected through the serial port, and wait for the response from the kernel debugger, the system then calls a KD function kdpsendwaitcontinue in kdptrap to wait for the data from the serial port in a loop (the kernel debugger and the debugged system are connected through the serial port) until the kernel debugger releases the command to continue the execution, the system can execute a command after int 0x3 normally.
If int 0x3 occurs in user mode, it is also divided into whether the user debugger is loaded. When the user debugger is not loaded, kidebugroutine does not handle the int 0x3 exception, and the user process does not have a debugport. In this case, the relevant structure of the record exception is merged into the user stack and handed over to the user mode rtldispatchexception to search. If the int 0x3 exception is not handled, the default exception handling routine is called. By default, the abnormal process is terminated. If a user debugger is loaded, an LPC message about the exception is sent to the debugport or exceptionport of the process and the return status of the sending function is determined. If the user debugger continues to execute the message, STATUS_SUCCESS is returned, the kernel is regarded as an exception and has been resolved to continue execution.
It is also illustrated that the same error occurs in the internal kernel state, which is much more critical than in the user State. The process for handling other exceptions is basically the same. A few different exceptions, such as debuuplint, debugprompt, loading and uninstalling symbol, are called by calling debugservice (this is actually by generating an exception int 0x2d ). It can be processed in kdpstub (the processing is very simple, but the eip of the ontext structure is added over the int 0x3 command after the current int 0 x 2d ), if it is processed in kdptrap, it is a further interaction with the kernel debugger. For more details about kdpstub, kdptrap, and debugservice, see my another article "Windows kernel debugger Principle Analysis".
Exception Handling best practices in. net
By Daniel turini
Rule 1 Don/'t throw new exception ()
Exception is a too broad class, And it/'s hard to catch without side-effects. derive your own exception class, but derive it from applicationexception. this way you cocould set a specialized exception handler for exceptions thrown by the framework and another for exceptions thrown by yourself.
You must have your own error handling class to handle framework exceptions and exceptions thrown by applications. In fact, the error class should have some basic custom attributes, such as ID, error category, error level... wait for the information.
Rule 2 don/'t put important exception information on the Message Field
Exceptions are classes. when you return the exception information, create fields to store data. if you fail on doing it, people will need to parse the message field to get the information they need. now, think what will happen to the calling code if you need to localize or even just correct a spelling error in error messages. you may never know how much code you/'ll break by doing it.
Do not put all the wrong keywords in the message, so as not to parse the message later. This principle is generally followed, for example, the error type and the error ID should not be placed in the message.
Rule 3 put a single catch (exception ex) per Thread
Generic Exception Handling shoshould be done in a central point in your application. each thread needs a separate try/Catch Block, Or you/'ll lose tions and you/'ll have problems hard to understand. when an application starts several threads to do some background processing, often you create a class for storing processing results. don/'t forget to add a field for storing an exception that cocould happen or you won/'t be able to communicate it to the main thread. in/"fire and forget/" situations, you probably will need to duplicate the main application exception handler on the thread handler.
Each thread has its own exception capture.
Rule 4 Generic exceptions caught shoshould be published
It really Doesn/'t matter what you use for logging-log4net, EIF, Event Log, tracelisteners, text files, etc. what/'s really important is: if you caught a generic exception, log it somewhere. but log it only once-often code is ridden with catch blocks that log exceptions and you end up with a huge log, with too much repeated information to be useful.
The error information should be recorded. This is also necessary for the convenience of future maintenance.
Rule 5 log exception. tostring (); never log only exception. message!
As we/'re talking about logging, Don/'t forget that you shoshould always log exception. tostring (), and never exception. message. exception. tostring () will give you a stack trace, the inner exception and the message. often, this information is priceless and if you only log exception. message, you/'ll only have something like/"object reference not set to an instance of an object /".
Logs should record all error messages instead of messages.
Rule 6 don/'t ever swallow exceptions
The worst thing you can do is catch (exception) and put an empty code block on it. Never do this.
Do not do anything in catch
Rule 7 use exceptions for errors that shoshould not be ignored
I/'ll use a real world example for this. when developing an API so people cocould access crivo (my product), the first thing that you shoshould do is calling the login method. if login fails, or is not called, every other method call will fail. I chose to throw an exception from the login method if it fails, instead of simply returning false, so the calling program cannot ignore it.
In a function call, do not only return success or failure. An error should be returned.
Rule 8 don/'t clear the stack trace when re-throwing an exception
The stack trace is one of the most useful information that an exception carries. often, we need to put some exception handling on catch blocks (e.g ., to rollback a transaction) and re-Throw the exception. see the right (and the wrong) way of doing it: the wrong way: Try
{
// Some code that throws an exception
}
Catch (exception ex)
{
// Some code that handles the exception
Throw ex;
}
Why is this wrong? Because, when you examine the stack trace, the point of the exception will be the line of the/"Throw ex;/", hiding the real error location. Try it.
Try
{
// Some code that throws an exception
}
Catch (exception ex)
{
// Some code that handles the exception
Throw;
}
What has changed? Instead of/"Throw ex;/", which will throw a new exception and clear the stack trace, we have simply/"Throw ;/". if you Don/'t specify the exception, the throw statement will simply rethrow the very same exception the catch statement caught. this will keep your stack trace intact, but still allows you to put code in your catch blocks.
Do not throw the old exception in nested throw !!!
Rule 9 avoid changing exceptions without adding Semantic Value
Only change an exception if you need to add some semantic value to it-e.g ., you/'re doing a DBMS connection driver, so the user doesn/'t care about the specific socket error and wants only to know that the connection failed.
If you ever need to do it, please, keep the original exception on the innerexception member. don/'t forget that your exception handling code may have a bug too, and if you have innerexception, you may be able to find it easier.
Try not to add your own semantics to the error message, but if necessary, it is best to save innerexception, rather than exception, so that you can find the fastest error.
 
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.