[Win32] Implementation of a debugger (c) exception
Zplutor
Source: http://www.cnblogs.com/zplutor/
This article copyright belongs to the author and the blog Garden altogether, welcome reprint. However, without the author's consent, this statement must be retained, and in the article page obvious location to the original link, otherwise reserves the right to pursue legal responsibility.
This goes on to deal with the question left in the previous article: How to handle debug events such as Exception_debug_event. Such debug events are the most important tool for the debugger to interact with the debugged process, and in later articles you will see how the debugger can use it to complete breakpoints, steps, and so on. Therefore, the handling of such debug events is very free, and the author of the debugger can handle them differently as needed. However, it is important to understand some of the exceptions before dealing with them. (The contents of this article refer to the book "Software Debugging")
Classification of exceptions
Depending on whether you can resume execution when an exception occurs, you can divide the exception into three types, namely, error exceptions, trap exceptions, and abort exceptions.
Error exceptions and trap exceptions can generally be fixed, and the program can resume execution after the repair. The difference between the two is that when the error exception resumes execution, it starts execution from the instruction that threw the exception, and the trap exception is executed from the next instruction that throws the exception. For example, the following three instructions:
I1
I2
i3
If I2 throws an error exception, resume execution starts at I2, and if a trap exception is thrown, execution starts from i3.
The abort exception is a serious error, and the program can no longer continue execution.
Depending on the cause of the exception, you can divide the exception into hardware exceptions and software exceptions. The hardware exception is the exception that is thrown by the CPU, and Windows defines the following hardware exception code:
Abnormal |
Value |
Describe |
Exception_access_violation |
0xC0000005 |
The exception that is thrown when a program attempts to read or write an unreachable address. For example, attempt to read memory at 0 addresses. |
exception_array_bounds_exceeded |
0xc000008c |
The exception that is thrown when array access is out of bounds. |
Exception_breakpoint |
0x80000003 |
The exception that is thrown when a breakpoint is triggered. |
Exception_datatype_misalignment |
0x80000002 |
The exception that is thrown when a program reads an unaligned data. |
Exception_flt_denormal_operand |
0xc000008d |
If the operand of the floating-point operation is unhealthy, the exception is thrown. The so-called abnormal, that is, its value is too small to be represented in the standard format. |
Exception_flt_divide_by_zero |
0xc000008e |
This exception is thrown when the divisor of a floating-point division is 0 . |
Exception_flt_inexact_result |
0xc000008f |
The result of a floating-point number operation cannot be precisely represented as a decimal when the exception is thrown. |
Exception_flt_invalid_operation |
0xc0000090 |
The exception represents a different floating-point number exception that is not included in the table. |
Exception_flt_overflow |
0xc0000091 |
This exception is thrown when the index of a floating-point number exceeds the maximum value that can be represented. |
Exception_flt_stack_check |
0xc0000092 |
This exception is thrown when a stack overflow or underflow occurs while a floating-point number is being calculated. |
Exception_flt_underflow |
0xc0000093 |
This exception is thrown when the index of a floating-point number is less than the minimum value that can be represented. |
Exception_illegal_instruction |
0xC000001D |
The exception is thrown when the program attempts to execute an invalid instruction. |
Exception_in_page_error |
0xC0000006 |
The exception that is thrown when a program accesses a memory page that is not in physical memory. |
Exception_int_divide_by_zero |
0xc0000094 |
This exception is thrown when the divisor of integer division is 0 . |
Exception_int_overflow |
0xc0000095 |
The exception is thrown when the result of an integer operation overflows. |
Exception_invalid_disposition |
0xc0000026 |
This exception is thrown when an exception handler returns an invalid handle. |
Exception_noncontinuable_exception |
0xc0000025 |
When an exception occurs that cannot be resumed, the exception is thrown if the program continues to execute. |
Exception_priv_instruction |
0xc0000096 |
This exception is thrown when a program attempts to execute an instruction that is not allowed by the current CPU mode. |
Exception_single_step |
0x80000004 |
When the TF bit of the flag register is 1 , the exception is thrown each time an instruction is executed. Mainly used for single-step debugging. |
Exception_stack_overflow |
0xC00000FD |
This exception is thrown when the stack overflows. |
Although there are a lot of exception codes and some are not easy to understand, most of these exceptions are rarely encountered when programming with high-level languages. The more common anomalies are exception_access_violation,exception_int_divide_by_zero and exception_stack_overflow.
A software exception is the exception that is thrown by a program called the RaiseException function, and the throw statement of C + + ultimately calls the function to throw an exception. Exception codes for software exceptions can be arbitrarily specified by the programmer when calling RaiseException. The exception code for exceptions thrown by the throw statement is specified by the compiler, and for Visual C + + compilers, the exception code is always 0xe06d7363, corresponding to the ASCII code for ". MSc".
Hardware exceptions and software exceptions can be captured and processed through the structured exception handling mechanism provided by Windows, which allows the program to continue execution where the exception occurred, or go to the exception handling block. The exception handling mechanism provided by C + + can only catch and handle exceptions thrown by the throw statement, which, in short, is determined by checking that the exception code is 0xe06d7363. In addition, the exception handling mechanism of C + + can only be transferred to the exception handling block, and cannot continue execution where the exception occurred. In fact, exception handling for C + + is a wrapper for structured exception handling of Windows.
Distribution of exceptions
Once an exception has occurred, a complex distribution process is going through. In general, an exception has the following possible results:
1. The exception was not processed and the program exited due to "application Error".
2. The exception is handled by the debugger, and the program resumes execution where the exception occurred (depending on whether it is an error exception or a trap exception).
3. The exception is handled by the exception handler within the program, where the program resumes execution where the exception occurred, or goes to the exception handling block to continue execution.
Here's a look at the exception distribution process. In order to highlight the highlights, there are a number of details omitted:
1. The program has an exception, Windows catches this exception, and goes to the kernel state execution.
2.Windows checks if the program that has the exception is being debugged and, if it is, sends a Exception_debug_event debug event to the debugger, which is the first time the debugger receives the event, or if no, skips to step 4th.
3. After the debugger receives an exception debug event, if the third argument is dbg_continue when calling Continuedebugevent, that means the debugger has handled the exception, the program resumes execution where the exception occurred, and the exception distribution ends if the third parameter is Dbg_ Exception_not_handled, which means the debugger did not handle the exception, skip to step 4th.
4.Windows back into the user state, looking for the exception handler that can handle the exception. If it is found, it is executed in the exception handler, then the execution of the program is executed according to the result of execution, and the exception distribution ends; If not found, skip to step 5th.
5.Windows back to the kernel, again to check if the program is being debugged, and if so, send a exception_debug_event debug event again to the debugger, which is the second time the debugger receives the event; If no, skip to step 7th.
6. The debugger handles the exception for the second time, if the third argument is dbg_continue when calling Continuedebugevent, the program resumes execution where the exception occurred and the exception distribution ends; If the third parameter is Dbg_exception_not_ HANDLED, skip to step 7th.
7. The exception is not processed and the program ends with an "Application Error".
The following flowchart expresses this process:
Here are a few examples to deepen your understanding of the exception distribution process. The debugger uses the sample code from the previous article. If you are already familiar with the process of abnormal distribution, you can skip this part of the view.
① throws a hardware exception and calls Continuedebugevent with Dbg_continue when an abnormal debug event is received.
Code of the program being debugged:
1 #include <stdio.h>
2 #include <Windows.h>
3
4 int wmain () {
5
6 OutputDebugString (TEXT ("warning! An exception would be thrown! "));
7
8 __try {
9
Ten int a = 0;
one int b = 10/a;
12
13}
__except (Exception_execute_handler) {
15
OutputDebugString (TEXT ("entered exception handler."));
17}
18}
The debugger's Onexception function code:
1 void onexception (const exception_debug_info* pInfo) {
2
3 std::wcout << TEXT ("An exception is occured.") << Std::endl
4 << TEXT ("Exception Code:") << std::hex << std::uppercase << STD::SETW (8)
5 << Std::setfill (L ' 0 ') << pinfo->exceptionrecord.exceptioncode << std::d EC << S Td::endl;
6
7 if (pinfo->dwfirstchance = = TRUE) {
8
9 std::wcout << TEXT ("first chance.") << Std::endl;
10}
All else {
12
Std::wcout << TEXT ("Second chance.") << Std::endl;
14}
15}
Run the debugger program and see it enter a dead loop, constantly outputting "an exception is occurred ..." message, and always "first chance." Take a look at the process with the above flowchart: We continue to be executed with dbg_continue, which means we have handled the exception, and the debug process continues to execute from where the exception occurred. Because Exception_int_divide_by_zero is an error exception, INT B = 10/a This statement executes again. In fact, however, the debugger does not do anything to handle the exception, and this statement throws an exception. So the cycle, into a dead circle. As can be seen from this example, the debugger is the first to catch an exception, even if the statement that throws the exception is surrounded by a __try block.
② throws a hardware exception and calls Continuedebugevent with dbg_exception_not_handled when an abnormal debug event is received.
The code for the above example is still used, but the third parameter of Continuedebugevent is changed to dbg_exception_not_handled. Run the debugger, this time only output "an exception is occurred ..." information, followed by the debugging process output information, indicating that the debug process of the exception handler was executed. Process: We continue the execution of the debug process with dbg_exception_not_handled, which means that the exception is not handled, so Windows looks for the exception handler. Because of an exception handler, and it returns Exception_execute_handler, the debug process enters the exception handler execution. If the exception_execute_handler is changed to Exception_continue_execution, then the debug process executes the statement that throws the exception again, and the result is caught in a dead loop.
If we remove the __try and __except blocks, there will be no exception handler to handle the exception, and the debugger will receive the exception debug information for the second time. If the continuedebugevent is still called with dbg_exception_not_handled, the debug process exits, and if the call is made in Dbg_continue, the debug process continues to execute and the result is in a dead loop.
The two examples above use hardware exceptions and Windows structured exception handling. What happens if you use software exceptions and exception handling for C + +? Here are some questions for you to solve:
③ the code for the Debugged program is as follows:
1 #include <stdio.h>
2 #include <Windows.h>
3
4 int wmain () {
5
6 OutputDebugString (TEXT ("warning! An exception would be thrown! "));
7
8 try {
9
Ten throw 9;
11}
The catch (int ex) {
13
OutputDebugString (TEXT ("entered exception handler."));
15}
16}
Call Continuedebugevent with Dbg_continue and dbg_exception_not_handled, and look closely at the output of the debugger to explain why.
④ the code from the previous example to this:
1 #include <stdio.h>
2 #include <Windows.h>
3
4 int wmain () {
5
6 OutputDebugString (TEXT ("warning! An exception would be thrown! "));
7
8 try {
9
Ten throw 9;
11
OutputDebugString (TEXT ("Would this message is shown?"));
13}
+ catch (int ex) {
15
OutputDebugString (TEXT ("entered exception handler."));
17}
18}
Call Continuedebugevent with Dbg_continue and dbg_exception_not_handled, and look closely at the output of the debugger to explain why.
⑤ answer according to the above two examples: is the software exception an error exception or a trap exception?
Talk More OutputDebugString
In the first and second examples above, you may notice a small problem: in the first example, the string that is output by the debug process is displayed only once, but in the second example, it is displayed two times. This is because OutputDebugString calls RaiseException internally, which is essentially working with software exceptions, and Windows transforms the exceptions it throws into Output_debug_string_ Event Debug Events to notify the debugger.
So, when we call Continuedebugevent with Dbg_continue, OutputDebugString's exception is handled, and the debugger receives only one output_debug_string_event event; dbg_ When Exception_not_handled is called, the exception is not processed and the debugger receives the output_debug_string_event for the second time. This is why in the second example the information is output two times.
So why is the debug process not over when the debugger calls Continuedebugevent with dbg_exception_not_handled after the second time output_debug_string_event is processed? This can only be said because the exception that OutputDebugString throws is a special exception, and Windows has special handling for it. The purpose of the OutputDebugString is to output debug information to the debugger, not to report an error, and if the debug process ends immediately after the call to OutputDebugString, it is definitely a strange thing to do.
the excpetion_debug_event the processing
Well, there are so many bedding on the top, and finally we can get back to the chase. The EXCEPTION_DEBUG_INFO structure describes the details of this type of debug event. Dwfirstchance indicates whether the first or second receive the same exception, which is 1 for the first time, and 0 for the second time. ExceptionRecord is a exception_record struct that contains the details of the exception:
Exceptioncode Exception Code
The EXCEPTIONFLAGS exception flag, which is 0, indicates that this is an exception that can be resumed, otherwise exception_noncontinuable.
The ExceptionRecord pointer to another exception. An exception can nest another exception to form a chained structure.
ExceptionAddress the address of the instruction that threw the exception.
ExceptionInformation if the exception needs to contain more information, the array is used to hold the information.
Numberparameters the number of elements in the exceptioninformation array.
As can be seen from the above description, the third parameter of Continuedebugevent has a great influence on the behavior of the debugger, so we can't just use dbg_continue or dbg_exception_not_handled, Instead, you should perform different actions based on the exception code, and then call continuedebugevent with the appropriate values. For example, in addition to the 0 exception, we can change the value of the divisor to nonzero, and then continue the execution of the debug process with Dbg_continue. As another example, we want to treat an exception only if it is not handled by the exception handler, so we can continue execution as dbg_exception_not_handled the first time we receive the exception debug event, and then process the Exception debug event the second time it is received.
Finally, the role of Dbg_continue and dbg_exception_not_handled is the same for debug events outside Exception_debug_event and Output_debug_string_event, is to continue to be executed by the debug process, which is no different.
Sample code
This time the sample code adds a global variable, g_continuestatus, as the third parameter when calling Continuedebugevent. The onexception and onoutputdebugstring functions will modify this value. For exceptions, the first time the receive is dbg_exception_not_handled continues to be executed by the debug process, and the second time it is received with dbg_continue to continue its execution.
Http://files.cnblogs.com/zplutor/MiniDebugger3.rar
[Win32] Implementation of a debugger (c) exception