CPGuide - Exception Management: Difference between revisions
Line 1,100: | Line 1,100: | ||
=====XCPT_FLOAT_STACK_CHECK===== | =====XCPT_FLOAT_STACK_CHECK===== | ||
;Invalid Floating-Point Stack Operation | |||
An invalid floating-point stack check is raised when a floating-point processor attempts an illegal operation on a private stack. The Intel 80387 processor maintains eight internal 10-byte "registers" that are individually addressable and yet behave as a push-down stack under the influence of the FLD (push real) and FST (pop real to destination) instructions. Overflow and underflow are checked with each instruction, and this exception is raised when appropriate. This is one of the class of exceptions for which the Intel 80387 processor signals the Intel 80386 processor to raise trap #16. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_FLOAT_STACK_CHECK (0xC0000099) | |||
;Default Action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_ILLEGAL_INSTRUCTION===== | =====XCPT_ILLEGAL_INSTRUCTION===== | ||
;Illegal Instruction | |||
An illegal instruction exception is generated when an attempt is made to execute an instruction whose operation is not defined for the host machine architecture. On the Intel 80386 processor, this corresponds to the invalid opcode fault (#6), caused by any invalid instruction. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_ILLEGAL_INSTRUCTION (0xC000001C) | |||
;Default action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_PRIVILEGED_INSTRUCTION===== | =====XCPT_PRIVILEGED_INSTRUCTION===== | ||
;Privileged Instruction | |||
A privileged instruction exception is generated when an attempt is made to execute an instruction whose operation is not allowed in the current machine mode. For example, an attempt is made to execute an instruction in user mode that is only allowed in kernel mode. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_PRIVILEGED_INSTRUCTION (0xC000009D) | |||
;Default Action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_INVALID_LOCK_SEQUENCE===== | =====XCPT_INVALID_LOCK_SEQUENCE===== | ||
;Invalid Lock Sequence | |||
An invalid lock sequence exception is generated when an attempt is made to execute an operation within an interlocked section of code, and the sequence is invalid for the host machine architecture. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_INVALID_LOCK_SEQUENCE (0xC000001D) | |||
;Default Action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_INTEGER_DIVIDE_BY_ZERO===== | =====XCPT_INTEGER_DIVIDE_BY_ZERO===== | ||
;Integer Divide-by-Zero | |||
An integer divide-by-zero exception is generated when an attempt is made to divide an integer dividend by an integer divisor of zero. On the Intel 80387 processor, this is a divide by zero fault (#0), caused by a DIV or IDIV by zero operation. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_INTEGER_DIVIDE_BY_ZERO (0xC000009B) | |||
;Default action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_INTEGER_OVERFLOW===== | =====XCPT_INTEGER_OVERFLOW===== | ||
;Integer Overflow | |||
An integer overflow exception is generated when the result of an integer operation causes a carry-out of the most significant bit of the result, which is not the same as the carry-into of the most significant bit of the result. For example, the addition of two positive integers produces a negative result. On the Intel 80387 processor, this corresponds to overflow trap (#4), caused by executing an INTO instruction with the OF flag set. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_INTEGER_OVERFLOW (0xC000009C) | |||
;Default action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_SINGLE_STEP===== | =====XCPT_SINGLE_STEP===== | ||
;Single Step | |||
A single-step exception is generated when a trace trap or other single instruction execution mechanism signals that one instruction has been executed. This exception is intended for use by debuggers. This exception is continuable. | |||
;Default Action: | |||
:The process is ended | |||
;Exception Code: | |||
:XCPT_SINGLE_STEP (0xC00000A0) | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_GUARD_PAGE_VIOLATION===== | =====XCPT_GUARD_PAGE_VIOLATION===== | ||
;Guard Page Violation | |||
A guard page violation exception is generated when an attempt is made to load or store data in a location that is contained within a guard page. Memory management software immediately turns the guard page into a demand zero page and initiates a guard page violation exception. | |||
;Exception Code: | |||
:XCPT_GUARD_PAGE_VIOLATION (0x800001) | |||
;Default Action: | |||
:Execution continues. If possible, the memory page immediately below the guard page is allocated and marked as a guard page. The higher guard page is marked to no longer be a guard page, and the instruction is restarted. This allows for dynamic stack growth. If it is not possible to allocate another page below the faulting page, an XCPT_UNABLE_TO_GROW_STACK exception is raised. This exception is continuable. | |||
;Additional Parameters (2): | |||
:;ExceptionInfo[ 0 ] | |||
::Read/Write Flag | |||
:::XCPT_READ_ACCESS, or | |||
:::XCPT_WRITE_ACCESS. | |||
:;ExceptionInfo[ 1 ] | |||
::Virtual Address | |||
::The virtual address of the data within a guard page. | |||
=====XCPT_UNABLE_TO_GROW_STACK===== | =====XCPT_UNABLE_TO_GROW_STACK===== | ||
;Unable to Grow Stack | |||
The default action for a guard page violation is to attempt to allocate another page of memory immediately below the page on which the fault occurred, thereby implementing dynamic stack growth. If this attempt fails, XCPT_UNABLE_TO_GROW_STACK is generated, indicating that the thread has, at most, one more page of stack space available. This exception is continuable. | |||
;Exception Code: | |||
:XCPT_UNABLE_TO_GROW_STACK (0x80010001) | |||
;Default Action: | |||
:Execution continues. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_BAD_STACK===== | =====XCPT_BAD_STACK===== | ||
;Bad Stack | |||
This exception is raised when an ExceptionRegistrationRecord is reached that is not properly aligned or is not within the current stack boundaries. It is also raised if an unwind target is specified that does not point to an ExceptionRegistrationRecord. This exception is noncontinuable. | |||
;Exception Code: | |||
:XCPT_BAD_STACK (0xC0000027) | |||
;Default Action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_INVALID_UNWIND_TARGET===== | =====XCPT_INVALID_UNWIND_TARGET===== | ||
;Invalid Unwind Target | |||
This exception is raised when the address of the target ExceptionRegistrationRecord is below the current stack pointer. This exception is noncontinuable. | |||
;Exception Code: | |||
:XCPT_INVALID_UNWIND_TARGET | |||
;Default Action: | |||
:The process is ended. | |||
;Additional Parameters: | |||
:None. | |||
=====XCPT_IN_PAGE_ERROR===== | =====XCPT_IN_PAGE_ERROR===== | ||
=====XCPT_INVALID_DISPOSITION===== | =====XCPT_INVALID_DISPOSITION===== |
Revision as of 18:04, 26 March 2020
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
An exception is an abnormal condition that can occur during program execution. Common causes of exceptions include:
- I/O errors
- Protection violations
- Math errors
- Intervention by the user or by another process
Activities that can cause exceptions include:
- Trying to use memory that you do not have permission to access
- Dividing by 0
- The user pressing Ctrl+Break
Exceptions include both unexpected errors (such as a memory protection violation) and expected errors (such as guard-page exceptions). Exceptions can be a synchronous exception, that is, caused by an action of the executing thread, or an asynchronous exception, caused by an event external to the executing thread (such as the user pressing Ctrl+Break). When an exception is caused by the user pressing Ctrl+Break or Ctrl+C, or by another process issuing DosKillProcess for your process, the exception is called a signal exception.
In most cases, the default action taken by OS/2 when an exception occurs is to terminate the application that caused the exception. Rather than having OS/2 default action occur, an application can register its own subroutine to handle exceptions. These routines are called exception handlers. Exception handlers enable an application to handle some errors itself, allowing the application to avoid termination (or at least to terminate gracefully).
When exception handlers are registered, they are added to an exception handler chain. The chain starts empty and each new handler is added to the head of the chain. Exceptions are passed to the exception handlers in the chain in Last-In-First-Out order, so the last exception handler to be registered is the first one to get an opportunity to handle each exception.
Exception handlers have the capability to complete critical code sections without being interrupted by other asynchronous exceptions; these critical code sections are called must-complete sections.
Exception handlers can be removed from the exception handler chains with DosUnsetExceptionHandler. Another way that exception handlers can be removed from the chain is with an unwind operation. When unwinding an exception handler, the exception handler is first called, then removed from the exception handler chain.
The following topics are related to the information in this chapter:
- Memory
- Program execution and control
About Exception Management
A multitasking operating system must manage applications carefully. A serious error (such as an attempt to access protected memory) occurring in one application cannot be permitted to damage any other application in the system. To manage errors that might damage other applications, OS/2 defines a class of error conditions called exceptions and defines default actions for those errors.
When an exception occurs, the default action taken by OS/2 is usually to terminate the application causing the exception (unless the application has registered its own exception handling routines). In some cases, when the exception can safely be ignored, execution is allowed to continue.
Rather than having OS/2 default action occur, an application can register its own exception handlers routines. An exception handler routine could be written to correct certain error conditions-when these error conditions occur, the thread's exception handler gets the exception, corrects the condition, and the thread continues executing rather than being terminated immediately by OS/2. OS/2's default action is taken if there are no user-defined exception handling routines or if all user-defined routines return without handling the exception.
An application can use DosSetExceptionHandler to register an exception handling routine. DosSetExceptionHandler takes a pointer to an EXCEPTIONREGISTRATIONRECORD data structure as its only argument. The first field in this data structure is a pointer to the previous exception handler in the chain. This field is maintained by OS/2 and must never be modified by an application. The second field is a pointer to the exception handling routine that will be registered by OS/2.
A single exception handler can be used to handle all the exceptions that you choose to handle. It is not necessary to have a separate exception handler for each exception.
Once an exception handling routine is registered, the system will notify it when an exception occurs. OS/2 sends synchronous exceptions only to the thread causing the exception. An application must register an exception handler for each thread that is handling exceptions. When OS/2 terminates an application, however, a process-termination exception is sent to all threads used by the application to be terminated. When the user presses Ctrl+Break, an asynchronous signal exception is sent only to Thread 1, the main thread, of the executing process.
The exception handling routine is passed the following four parameters that provide exception-specific information:
- EXCEPTIONREPORTRECORD
- Describes the exception and its parameters. The first field of this data structure contains the number of the exception that occurred.
- EXCEPTIONREGISTRATIONRECORD
- The EXCEPTIONREGISTRATIONRECORD data structure used to initially register the exception handler. This is a microprocessor-specific value.
- ContextRecord
- Describes the machine state at the time the exception occurred.
- DispatcherContext
- Contains state information on nested exception and collided unwinds. This information must not be modified by the application.
Details of the parameters and data structures can be found in Exception Handler Interface.
OS/2 places the exception handlers for each thread in an exception handler chain. Registering an exception handler adds it to the head of the chain.
When an application registers an exception handler, the exception handler is added to the head of the chain. If the application calls a routine in a dynamic link library (DLL), the DLL might register an exception handler in case there is an exception while its code is executing; the DLL deregisters the exception handler before returning control to the application. The DLL's exception handler would be ahead of the application's exception handler in the chain.
Exception handlers in the chain are notified of an exception in Last-In-First-Out (LIFO) order. Thus, if an exception occurs while your thread is executing, the exception handler for your thread is notified of the exception first. If your exception handler chooses to handle the exception, the earlier exception handlers in the chain never see the exception. If your exception handler chooses not to handle the exception, it is passed along to the next earlier exception handler in the chain. If no exception handler in the chain chooses to handle the exception, OS/2 takes the default action for the exception.
If an exception happens while DLL code is executing, and if the DLL's exception handler chooses to handle the exception, your application's exception handlers will never be aware it.
System Exceptions
OS/2 defines a class of error conditions called system exceptions, and specifies the default actions that are taken when these system exceptions occur. The default action taken by OS/2 in most cases is to terminate the thread that caused the system exception.
System exceptions include both synchronous and asynchronous exceptions. Synchronous exceptions are caused by events that are internal to the execution of a thread. For example, synchronous exceptions could be caused by invalid parameters, or by the request of a thread to end its own execution.
Asynchronous exceptions are caused by events that are external to the execution of a thread. For example, an asynchronous exception can be caused by a user entering a Ctrl+C or Ctrl+Break key sequence, or by a process calling DosKillProcess to end the execution of another process.
The Ctrl+Break, Ctrl+C, and DosKillProcess-generated exceptions are also known as signals, or as signal exceptions.
OS/2 delivers exceptions that occur in 16-bit as well as 32-bit code. The sequence or hierarchy for delivering exceptions is as follows:
- When an exception occurs in 32-bit code, the system gives control only to the 32-bit exception handlers registered for the current thread. If the thread has not registered any 32-bit handlers, the system default action occurs.
- When an exception occurs in 16-bit code, the system first gives control to the 32-bit exception handlers registered for the current thread. If the exception is not handled by one of these handlers, control is passed to the 16-bit handler, if one exists for the given exception. If there is no 16-bit handler for the exception, the system default action occurs.
Notification of an exception is usually sent only to the thread that caused the exception. However, if a thread uses DosExit to terminate all the threads in the process, notification of the process-termination exception is sent to every thread in the process. The thread that used DosExit gets a XCPT_PROCESS_TERMINATE exception, all the other threads in the process get a XCPT_ASYNC_PROCESS_TERMINATE exception.
Exit-list processing occurs on a per-process basis after a process-termination exception has been delivered to each thread in the process and each thread has finally ended except Thread 1 (the main thread). Therefore, any thread that handles a process-termination exception must eventually end its own execution voluntarily. Otherwise, the process-termination sequence will not conclude properly.
The following tables briefly list the possible exceptions. For more detailed information about the system exceptions, including default system action, parameters, and related trap numbers, see the Control Program Programming Reference.
Non-Fatal, Software-Generated Exceptions
┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_GUARD_PAGE_VIOLATION │A guard page has been │ │ │accessed. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_UNABLE_TO_GROW_STACK │The system is unable │ │ │to allocate the memory│ │ │page directly below │ │ │the guard page just │ │ │accessed. │ └──────────────────────────────────────┴──────────────────────┘
Fatal, Software-Generated Exceptions
┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_IN_PAGE_ERROR │An I/O error occurred │ │ │while reading a memory│ │ │page into memory. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_PROCESS_TERMINATE │The thread has │ │ │terminated itself with│ │ │DosExit. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ASYNC_PROCESS_TERMINATE │Another thread in the │ │ │process has caused the│ │ │thread to terminate. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_NONCONTINUABLE_EXCEPTION │An exception handler │ │ │has attempted to │ │ │continue execution in │ │ │response to a │ │ │non-continuable │ │ │exception. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INVALID_DISPOSITION │An exception handler │ │ │has returned an │ │ │invalid value. │ └──────────────────────────────────────┴──────────────────────┘
Fatal, Hardware-Generated Exceptions
┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ACCESS_VIOLATION │An access violation or│ │ │page fault has │ │ │occurred. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INTEGER_DIVIDE_BY_ZERO │An attempt to divide │ │ │by 0 has occurred in │ │ │an integer operation. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_DIVIDE_BY_ZERO │An attempt to divide │ │ │by 0 has occurred in a│ │ │floating point │ │ │operation. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_INVALID_OPERATION │An invalid floating │ │ │point operation was │ │ │attempted. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ILLEGAL_INSTRUCTION │An attempt was made to│ │ │execute an instruction│ │ │that is not defined on│ │ │the host machine's │ │ │architecture. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_PRIVILEGED_INSTRUCTION │An attempt was made to│ │ │execute an instruction│ │ │that is not permitted │ │ │in the current machine│ │ │mode or that the │ │ │application does not │ │ │have permission to │ │ │execute. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INTEGER_OVERFLOW │An integer operation │ │ │generated a carry-out │ │ │of the most │ │ │significant bit. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_OVERFLOW │A floating point │ │ │operation generated a │ │ │resulting exponent │ │ │that is greater than │ │ │the magnitude │ │ │permitted for the │ │ │operands. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_UNDERFLOW │A floating point │ │ │operation generated a │ │ │resulting exponent │ │ │that is less than the │ │ │magnitude provided for│ │ │the operands. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_DENORMAL_OPERAND │An attempt was made to│ │ │perform an arithmetic │ │ │operation on a │ │ │denormal operand. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_INEXACT_RESULT │The result of an │ │ │operation is not │ │ │exactly representable │ │ │in the target format. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_STACK_CHECK │An illegal stack │ │ │operation was │ │ │attempted by the │ │ │floating point │ │ │coprocessor. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_DATATYPE_MISALIGNMENT │An attempt was made to│ │ │store a data in an │ │ │address that is not │ │ │naturally aligned on a│ │ │hardware architecture │ │ │that does not provide │ │ │alignment hardware. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_BREAKPOINT │A breakpoint │ │ │instruction was │ │ │executed. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_SINGLE_STEP │One instruction has │ │ │been executed in │ │ │single-step mode. │ └──────────────────────────────────────┴──────────────────────┘
Fatal Exceptions
┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INVALID_LOCK_SEQUENCE │An invalid operation │ │ │was attempted within │ │ │an interlocked section│ │ │of code. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ARRAY_BOUNDS_EXCEEDED │An array index outside│ │ │its upper and lower │ │ │boundary was detected.│ └──────────────────────────────────────┴──────────────────────┘
Unwind Operation Exceptions
┌──────────────────────────────────────┬────────────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_UNWIND │An unwind operation is in │ │ │process. │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_BAD_STACK │An │ │ │EXCEPTIONREGISTRATIONRECORD │ │ │data structure was reached │ │ │that is not properly aligned│ │ │or that is not within the │ │ │current stack boundaries. │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_INVALID_UNWIND_TARGET │The address of the target │ │ │EXCEPTIONREGISTRATIONRECORD │ │ │is below the current stack │ │ │pointer or not in the │ │ │exception handler chain. │ └──────────────────────────────────────┴────────────────────────────┘
Fatal Signal Exceptions
┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_SIGNAL │A signal was made to │ │ │your process (usually │ │ │to stop). All the │ │ │signal exceptions │ │ │(Ctrl+Break, Ctrl+C, │ │ │and │ │ │XCPT_SIGNAL_KILLPROC) │ │ │come under this │ │ │exception. │ └──────────────────────────────────────┴──────────────────────┘
Signal Exceptions
Signal exceptions are special events sent to a thread when the user presses certain key sequences or when another thread or process explicitly initiates the exception. There are three types of signal exceptions:
- XCPT_SIGNAL_BREAK
- When the user presses Ctrl+Break
- XCPT_SIGNAL_INTR
- When the user presses Ctrl+C
- XCPT_SIGNAL_KILLPROC
- When another process uses DosKillProcess to send a XCPT_SIGNAL_KILLPROC exception to your process.
Signal exceptions are sent only to Thread 1 (the main thread) in the process receiving the exception. If an exception handler is registered on Thread 1, it must be prepared to receive signal exceptions. The thread 1 exception handler can always ignore the signal exception by returning XCPT_CONTINUE_SEARCH.
If the thread 1 exception handler is to receive signal exceptions, it must use DosSetSignalExceptionFocus to notify OS/2 that it wants to receive the XCPT_SIGNAL_INTR (Ctrl+C) and XCPT_SIGNAL_BREAK (Ctrl+Break) signals. Otherwise, these exceptions are not passed to the exception handler and the default action-to terminate the process-is taken by OS/2. The thread will get XCPT_SIGNAL_KILLPROC signals whether it uses DosSetSignalExceptionFocus or not.
All three of these signals are delivered by a single exception-XCPT_SIGNAL-and the exception handler for Thread 1 can choose to handle none, some, or all of the signals. The signal being sent can be determined by examining the exception information in EXCEPTIONREPORTRECORD.
The following table provides information about each type of signal.
Signal Exceptions
┌────────────┬──────────────────────────┬───────────────────────────┐ │Signal │Symbolic Constant │Description │ ├────────────┼──────────────────────────┼───────────────────────────┤ │Ctrl+Break │XCPT_SIGNAL_BREAK │This exception is sent to │ │ │ │Thread 1 in the current │ │ │ │keyboard-focus process when│ │ │ │a Ctrl+Break key sequence │ │ │ │is received from the │ │ │ │keyboard. The default │ │ │ │action taken by OS/2 for │ │ │ │this exception is forced │ │ │ │process termination. │ ├────────────┼──────────────────────────┼───────────────────────────┤ │Ctrl+C │XCPT_SIGNAL_INTR │This exception is sent to │ │ │ │Thread 1 in the current │ │ │ │keyboard-focus process when│ │ │ │a Ctrl+C key sequence is │ │ │ │received from the keyboard.│ │ │ │The default action taken by│ │ │ │OS/2 for this exception is │ │ │ │forced process termination.│ ├────────────┼──────────────────────────┼───────────────────────────┤ │Kill Process│XCPT_SIGNAL_KILLPROC │This exception is sent to │ │Signal │ │Thread 1 in the process │ │ │ │specified when an │ │ │ │application uses │ │ │ │DosKillProcess. The │ │ │ │XCPT_SIGNAL_KILLPROC signal│ │ │ │exception results from an │ │ │ │action external to the │ │ │ │process. The default action│ │ │ │taken by OS/2 for this │ │ │ │exception is forced process│ │ │ │termination. │ └────────────┴──────────────────────────┴───────────────────────────┘
Handling Signal Exceptions
To handle signal exceptions, a process must first call DosSetExceptionHandler to register a handler for the exceptions. Next, the process must call DosSetSignalExceptionFocus, with the Flag parameter set to ON, in order to receive signal exceptions.
After a process calls DosSetSignalExceptionFocus, it remains the signal focus for its screen group until it calls DosSetSignalExceptionFocus again with the Flag parameter set to OFF, or until another process in the screen group makes a call to the same function with Flag set to ON.
Each call to DosSetSignalExceptionFocus with Flag set to ON increments a counter in the per-task data area of the process. Each call with Flag set to OFF decrements the counter. When a signal exception occurs, the system checks to see whether the value of the counter is greater than 0. If it is, the signal is sent.
DosSetSignalExceptionFocus returns ERROR_NESTED_TOO_DEEP if the value of the counter exceeds 65535. If a thread tries to turn off the signal focus when the value of the counter is 0, ERROR_ALREADY_RESET is returned.
All 32-bit exception handlers that are attached to thread 1 of the process will be given an opportunity to handle the signal. If no 32-bit exception handler returns XCPT_CONTINUE_EXECUTION in response to the signal, and if a 16-bit exception handler is registered, then the 16-bit handler for the signal will be executed. If none exists, then the process will be terminated.
In order to continue receiving signals, the process must either return XCPT_CONTINUE_EXECUTION from a 32-bit exception handler, or it must call the 16-bit DosSetSigHandler function, specifying SIG_ACKNOWLEDGE as the value of the Action parameter to acknowledge the signal, or it must call DosAcknowledgeSignalException.
The typematic facility of the keyboard could cause a Ctrl+C or Ctrl+Break signal exception to repeat. For this reason, the system holds these exceptions until an exception handler returns XCPT_CONTINUE_EXECUTION, or calls DosAcknowledgeSignalException. However, only one signal exception is actually held; they are not queued by the system.
See Must-Complete Sections for information about how a process can defer the handling of signal exceptions.
Sending Signal Exceptions
A process can send the XCPT_SIGNAL signal exception to another process by calling DosSendSignalException.
In order for the specified process to receive the exception, it must have an exception handler registered for Thread 1, and it must designate itself as the signal focus for its screen group by calling DosSetSignalExceptionFocus.
Presentation Manager applications cannot request exception focus for Ctrl+C and Ctrl+Break. However, establishing an exception handler for Ctrl+C and Ctrl+Break is supported for Vio-Window and full-screen applications.
Raising Exceptions
Asynchronous exceptions that have been deferred in a must-complete section are dispatched automatically by the system when the thread exits the must-complete section. However, a synchronous exception that has been deferred must be raised by calling DosRaiseException.
DosRaiseException can also be used to simulate either an asynchronous or synchronous exception. For example, a floating point emulator (a program that emulates a numeric coprocessor) can use this function to simulate an NPX exception.
Raising a software exception captures the machine state of the current thread in a ContextRecord data structure. The ExceptionAddress field of EXCEPTIONREPORTRECORD is set to the return address of the caller, as are the corresponding fields of the ContextRecord data structure. The system then calls each exception handler on the list, passing each a pointer to EXCEPTIONREPORTRECORD and the created ContextRecord data structures. In the case of a continuable exception for which XCPT_CONTINUE_EXECUTION is returned, DosRaiseException restores the potentially modified context back into the machine before returning. Note that control cannot return to the caller of DosRaiseException if the instruction pointer in ContextRecord has been modified.
The caller of DosRaiseException can set the EH_NONCONTINUABLE bit in the flags field of the EXCEPTIONREPORTRECORD data structure. By doing so, the caller guarantees that it is never returned to after the call to DosRaiseException. Note that once set, the EH_NONCONTINUABLE bit cannot be modified by any exception handler. The system will enforce this.
Following are some possible scenarios that might occur after a call to DosRaiseException has been made:
- If one of the exception handlers returns from a continuable exception with a status of XCPT_CONTINUE_EXECUTION, DosRaiseException returns NO_ERROR to the caller, and the thread resumes execution.
- If one of the exception handlers returns from a noncontinuable exception with a status of XCPT_CONTINUE_EXECUTION, the process is terminated, because it is illegal to return XCPT_CONTINUE_EXECUTION from a noncontinuable exception.
- If none of the exception handlers in the thread's chain of handlers returns with a status of XCPT_CONTINUE_EXECUTION, then the action taken depends on the exception number:
- If the exception number indicates a user-assigned exception or an unassigned system exception, the process is terminated.
- If the exception number is assigned to a system exception, and CS:EIP points to 32-bit code, no 16-bit handlers are called and the system default action is taken. Depending on which system exception has been raised, the default action is either to terminate the process, or to continue execution of the thread with NO_ERROR returned to the caller.
- If the exception number is assigned to a system exception that maps to a 16-bit exception and CS:EIP points to 16-bit code, a 16-bit exception handler is called, if one is registered. Otherwise OS/2 takes the default action.
User-Defined Exceptions
Exceptions can also be defined by the application. These are called user-defined exceptions (as opposed to system-defined exceptions, which are those exceptions defined by OS/2). Applications can define an exception in the following fashion:
#define XCPT_YOUR_EXCEPTION 0xE004ABCD
The application then raises the exception, using DosRaiseException:
EXCEPTIONREPORTRECORD ERepRec;
ERepRec.ExceptionNum = XCPT_YOUR_EXCEPTION; ERepRec.fHandlerFlags = 0; ERepRec.NestedExceptionReportRecord = NULL; ERepRec.ExceptionAddress = NULL; ERepRec.cParameters = 0;
DosRaiseException(&ERepRec);
The exception handlers in the exception handler chain that are ahead of the application's exception handler will see the exception, but they will not recognize it, so they will return XCPT_CONTINUE_SEARCH. Only the application's exception handler will recognize the exception.
The application's exception handler must return XCPT_CONTINUE_EXECUTION so that the exception will not continue to be passed down the exception handler chain.
Must-Complete Sections
A thread can defer the handling of asynchronous exceptions by creating a must-complete section. A must-complete section is a section of code that cannot be safely interrupted; it must be allowed to complete its execution even if an asynchronous exception occurs while within its boundaries. For example, a must-complete section can be used:
- When modifying shared-memory data structures that cannot be modified through an atomic operation
- Across database update operations
- During a remote communications operation.
Creating a must-complete section ensures that the execution of critical instructions will be completed and that resources will be cleaned up before the thread ends. When used in conjunction with a mutual exclusion (mutex) semaphore, a must-complete section also ensures that a thread will have exclusive access to a resource.
The boundaries of the must-complete section are defined by DosEnterMustComplete and DosExitMustComplete requests. While a thread is executing instructions in a must-complete section, the system will hold asynchronous exceptions, which include signal exceptions and asynchronous process terminations.
The system increments a counter each time DosEnterMustComplete is called, and decrements the counter when DosExitMustComplete is called. Any asynchronous exceptions that have been held are dispatched when the counter reaches 0. A count greater than 1 indicates the degree of nesting of the must-complete section. If DosExitMustComplete is called when the count is already 0, ERROR_ALREADY_RESET is returned.
The handling of synchronous system exceptions and user-defined exceptions is not deferred by the system. To defer the handling of these exceptions, a procedure typically registers an exception handler (by calling DosSetExceptionHandler) and initializes a local Raise Exception flag to 0 before entering the must-complete section. The flag is set to 1, and the information is stored, if the exception handler receives a synchronous exception that it wants to reraise later.
If the value of the raise exception flag is 0 after the thread exits from the must-complete section, then no exceptions occurred, and the thread continues its normal operation.
If the value of the flag is 1 after the must-complete section has been completed, then an exception occurred, and the thread must call DosRaiseException to raise the deferred exception for handling.
- Note
- A thread must not call a function that is outside the scope of the must-complete section (for example, a DLL routine), because an error in the called routine could cause the process to end without returning. Keep must-complete sections as short as possible.
Unwinding Exception Handlers
In addition to handling exceptions, exception handlers are used to clean up resources during the execution of a nonlocal GOTO instruction or during thread termination. (A nonlocal GOTO instruction jumps to a label outside the current procedure. The label is a procedure address or an address within a procedure that is on the stack, higher in the call frame chain.)
DosUnwindException calls and removes exception handlers from a thread's chain of registered exception handlers up to, but not including, a specified exception handler. This is known as an unwind operation. DosUnwindException can also be used to unwind all exception handlers from the thread's exception handler chain and to terminate the thread.
For example, with the C language setjmp() and longjmp() routines, the setjmp() would save the address of the current exception handler structure, along with any other information that is necessary to perform the longjmp() routine. (The address of the current exception handler structure is obtained from the head of the exception handler chain. A pointer to the head of the chain is located in the Thread Information Block.)
The longjmp() routine would initiate the unwind of procedure call frames by calling DosUnwindException and passing to it the saved address of the EXCEPTIONREGISTRATIONRECORD data structure. If the address of the EXCEPTIONREGISTRATIONRECORD data structure is not found in the chain, then the XCPT_INVALID_UNWIND_TARGET exception is raised, and the chain is not unwound.
The machine state at the time of the call to DosUnwindException is captured in ContextRecord. The EH_UNWINDING flag is set in the exception flags field of the EXCEPTIONREPORTRECORD data structure. The EH_EXIT_UNWIND flag is also set if the EXCEPTIONREGISTRATIONRECORD parameter is set to 0 (if the application does not provide its own EXCEPTIONREPORTRECORD parameter OS/2 will construct one). A backward walk through the procedure call frames is then performed to find the target of the unwind operation.
- Note
- Even though a ContextRecord is used to capture the state of the machine, unwinding is not considered an exception. It is simply delivered through the exception mechanism.
The first parameter to DosUnwindException is the address of an exception handler's EXCEPTIONREGISTRATIONRECORD. DosUnwindException will unwind exception handlers up to, but not including that exception handler. If a -1 is passed to DosUnwindException for this parameter, DosUnwindException will unwind all the exception handlers on the chain. If a 0 is passed to DosUnwindException for this parameter, DosUnwindException will unwind all the exception handlers on the chain and exit.
There is no return from a call to DosUnwindException, unless the stack is invalid. Control is transferred to the specified instruction pointer address. If DosUnwindException encounters an error during its processing, it raises another exception rather than return control to the caller.
If the target call frame is reached and an exit unwind is not being performed (that is, an EXCEPTIONREGISTRATIONRECORD is not 0), then the computed machine state is restored from ContextRecord and control is transferred to the address specified by the target-IP address parameter. Note that the stack pointer is not restored, making it possible to transfer information on the stack. It is the responsibility of the code at the target address to reset the stack pointer as necessary.
DosUnwindException is called with C language calling conventions, which permits the use of a variable number of arguments. Thus, the caller can pass any amount of information on the stack, to be picked up at the target-IP address.
If an exit unwind is being performed (the EXCEPTIONREGISTRATIONRECORD parameter is 0), then all call frames are unwound until the base of the stack is reached.
If the EXCEPTIONREPORTRECORD parameter is specified, then each exception handler encountered during the unwind operation is called, using the specified record. If this parameter is not specified, then DosUnwindException constructs an EXCEPTIONREPORTRECORD that specifies the exception XCPT_UNWIND.
- Colliding Unwinds
During an unwind operation, it is possible for one unwind to collide with a previous unwind. This occurs when the scope of the second unwind overlaps the scope of the first unwind. Following are two situations:
- The target frame of the second unwind is a frame that has already been unwound by the first unwind.
- The target frame of the second unwind is a valid frame that is positioned before or after the target frame of the first unwind.
Either of these situations could occur during the following scenarios:
- An unwind handler calls unwind, or
- An unwind handler hits an exception that has called unwind.
In the first scenario, the second unwind is attempting to unwind to an invalid target. This causes the exception XCPT_INVALID_UNWIND_TARGET to be raised.
In the second scenario, the first unwind is abandoned, and the second unwind continues to its target. The second scenario is far more likely.
- Note
- A user program that uses high level language exception mechanisms must never call DosUnwindException, because this could create conflicts with the runtime exception strategy of the high level language. Unwind operations in this case are performed through language-supported facilities such as the C language longjmp() routine.
Nested Exit Lists
A nested exception is an exception that occurs while another exception is being handled.
OS/2 supports nested exceptions because an unhandled exception that occurs in an exception handler should be handled at a higher level-that is, by an ancestor of the procedure that registered the offending handler.
When a nested exception occurs, the EH_NESTED_CALL flag is set in the exception structure to indicate that a nested function call is being made. The normal convention then is for the handler to return immediately without handling the exception if the EH_NESTED_CALL flag is set. Without this flag, it would be easy to create an infinitely recursive situation.
For example, suppose we have the following scenario:
- Procedure main calls procedure PA, which establishes exception handler HA.
- Procedure PA calls procedure PB, which establishes exception handler HB.
- Procedure PB calls procedure PC, which establishes exception handler HC.
- Procedure PC calls procedure PD.
Now suppose that procedure PD causes an exception. The system refers to the current thread's chain of exception handlers.
Because procedure PD has no handler, the system calls HC, the handler for procedure PC, with the EH_NESTED_CALL flag clear. If handler HC returns CONTINUE_SEARCH, the system calls the next handler in the chain, handler HB, again with the EH_NESTED_CALL flag clear.
Now suppose that exception handler HB causes an exception while it is processing the original exception. The call frames for the procedures are arranged in the following order on the stack:
- Procedure main
- Procedure PA
- Procedure PB
- Procedure PC
- Procedure PD
- OS/2's exception dispatcher
- Procedure HB, which is the exception handler procedure
- OS/2's exception dispatcher
The system will now start traversing the exception handler chain again. Exception handler HB could have registered an exception handler, which would be the first handler in the chain. If it had registered a handler, it would be called with the EH_NESTED_CALL flag clear.
The range of the nested exception is exception handlers HC and HB. The end of this range can be determined by the fact that exception handler HB is the currently active handler.
These exception handlers have already been given a chance to handle the original exception. They are now about to be called again in a nested range. Therefore, when handlers HC and HB are called again, they will be called with the EH_NESTED_CALL flag set. If they do not handle the exception, then exception handler HA will be called with the EH_NESTED_CALL flag clear, because it is outside the nested range.
Process Exit Lists
A process executes any routines registered in its exit list (with DosExitList) after the Process Termination exception has been delivered to each thread in the process and after each thread except Thread 1 has finally been terminated. If a thread handles the process termination exception, it must eventually voluntarily terminate, or the exit-list sequence will not finish running properly. Threads must not use DosCreateThread, DosExecPgm, DosStartSession, or DosExit when they are delivered a process termination exception.
Error Pop-Un Interface
Some error conditions, such as general protection violations, cause OS/2 to display a pop-up screen containing information about the error. An application can use DosError to disable error pop-up screens. Typically, a Presentation Manager application would disable error pop-up screens if it sets up its own routines to handle errors that would ordinarily generate pop-up screens.
DosError is also used to control and disable hard errors, which usually have to do with reading from and writing to disks.
Exception Handler Interface
Exception handlers are passed four parameters. The interface for writing a 32-bit exception handler is:
ExceptionHandler (ExceptionReportRecord, ExceptionRegistrationRecord, ContextRecord, DispatcherContext);
The exception handler returns XCPT_CONTINUE_EXECUTION to indicate that the exception has been handled and is to be dismissed, or XCPT_CONTINUE_SEARCH to indicate that the exception has not been handled and is to be passed to the next exception handler on the chain.
Note that there are no invalid exception numbers; if a handler does not recognize an exception number, it simply returns XCPT_CONTINUE_SEARCH.
In addition to handling exceptions, exception handlers are used in unwind operations. An unwind operation simply calls and removes exception handlers from the exception handler chain of the thread. Unwind exceptions are not actually being delivered to the handlers, so the individual return codes are irrelevant, and they do not affect the unwind operation.
A single exception handler can be used to handle all the exceptions that you choose to handle. It is not necessary to have a separate exception handler for each exception.
A handler is not required to return to the system; it can handle the exception, and then continue thread execution directly. For example, when an application executes a longjmp(), the C language compiler adds code that essentially performs an unwind operation to clean up the stack. Execution then resumes at the point where the target setjmp() occurred.
For synchronous exceptions, an exception handler can alter the contents of the interrupted thread's context, except for the fields that cannot normally be altered during thread execution. For asynchronous exceptions (signal and termination) changes made to the context of the thread are ignored.
Some exceptions are continuable; if the thread's exception handler handles the exception, execution can continue. If the exception condition is such that execution cannot be continued safely, the exception is said to be noncontinuable. If an exception is noncontinuable the EH_NONCONTINUABLE bit is set in the exception structure, and it is an error to indicate the exception has been handled. Returning XCPT_CONTINUE_EXECUTION causes an XCPT_NONCONTINUABLE_EXCEPTION exception to be raised.
Generally, exception handlers can use any function while they are handling an exception. However, while handling a process-termination exception, an exception handler must not call DosCreateThread, DosExecPgm, or DosStartSession, because unpredictable results can occur. A handler also must not call DosExit while handling a process-termination exception, because this request will cause the exception to be dispatched as a nested exception to the current thread's entire chain of handlers.
Exception Handler Parameters
- EXCEPTIONREPORTRECORD (EXCEPTIONREPORTRECORD) - input/output
- A pointer to the exception report record, which describes the exception and its parameters.
- EXCEPTIONREGISTRATIONRECORD ( EXCEPTIONREGISTRATIONRECORD) - input/output
- This is a microprocessor-specific value. For the 80386 microprocessor, this is a pointer to the exception registration record data structure that was used to register the current exception handler.
- ContextRecord (CONTEXTRECORD) - input/output
- A pointer to a context record, which describes the machine state at the time the exception occurred.
- DispatcherContext (DISPATCHERCONTEXT) - output
- A pointer to a reserved field that receives state information on nested exceptions and collided unwinds. This field returns information to either the exception dispatcher (in the case of nested exceptions) or to the unwind routine (in the case of collided unwinds). User code must not modify the DispatcherContext field at any time.
- When the system's exception handler is called (it is already registered by the exception dispatcher), the exception handler returns NESTED and fills in the DispatcherContext field with the address of the EXCEPTIONREGISTRATIONRECORD corresponding to the exception handler most recently called by the exception dispatcher. This indicates how far the exception dispatcher progressed through the call chain before the nesting occurred. The EH_NESTED_CALL bit is set in the EXCEPTIONREPORTRECORD flags field for each exception handler that is called between handler of the exception dispatcher and the establisher of the most recently called handler.
- In the case of a collided unwind, the exception handler registered by the unwind dispatcher will return COLLIDED_UNWIND and the DispatcherContext field will contain a pointer to the target frame of the current unwind.
Exception Management Data Structures
Applications use three data structures for exception management (the DispatcherContext parameter is for system use).
- EXCEPTIONREPORTRECORD data structure
- ExceptionRegistrationRecord data structure
- ContextRecord data structure.
An overview of each of these data structures is presented below.
ExceptionReportRecord Data Structure
The EXCEPTIONREPORTRECORD data structure describes an exception and any additional parameters associated with the exception. The data structure contains fields for the following information:
- Exception number
- Exception flags, describing exception attributes
- A pointer to a nested exception report record, if any
- The address where the exception occurred
- Information for any additional parameters.
For descriptions of the system exceptions see the Control Program Programming Reference.
Following are the flags that are set to indicate exception attributes. Only the EH_NONCONTINUABLE flag can be set (but not cleared) by the user. All other flags are set by the system.
- EH_NONCONTINUABLE (0x1)
- The exception is not continuable, and any attempt to continue causes the exception XCPT_NONCONTINUABLE_EXCEPTION to be raised.
- EH_UNWINDING (0x2)
- The EXCEPTIONREPORTRECORD data structure describes an exception for which an unwind is in progress.
- EH_EXIT_UNWIND (0x4)
- An exit unwind operation implies that call frames are being unwound until the base of the stack is reached. Note that EH_UNWINDING is also set.
- EH_STACK_INVALID (0x8)
- Following are causes for this flag to be set:
- The user stack exceeds the limits specified by the Thread Information Block. Applications can get the Thread Information Block by calling DosGetInfoBlocks.
- A call frame exceeds the stack limits specified by the Thread Information Block.
- A call frame is not aligned on the stack.
- This flag is set only when the EXCEPTIONREPORTRECORD is passed to an associated debugger. It is not possible to build exception information on the user's stack when the stack is invalid.
- EH_NESTED_CALL (0x10)
- EXCEPTIONREPORTRECORD describes an exception raised while the current exception handler was active. That is, a nested exception is in progress, and the current handler was also called to handle the previous exception.
EXCEPTIONREPORTRECORD data structures can be chained together to provide additional information when nested exceptions are raised.
ExceptionRegisterRecord Data Structure
The application is responsible for the creation and registration of the EXCEPTIONREGISTRATIONRECORD data structure. This is the data structure used by the application when it established the exception handler on the chain.
The only restrictions are that each pointer in the linked list must either point directly to the next pointer in the list or contain END_OF_CHAIN (-1), and the field immediately following the pointer field must be the pointer to the exception handler code. No fields other than these two will be examined by OS/2. The application can keep any state information that it chooses in this data structure, as long as it does not alter either of the fields used by the system.
When a procedure begins, it must create an EXCEPTIONREGISTRATIONRECORD on the stack, fill in the pointer to the exception handler routine, and link the data structure to the front of the exception handler chain by calling DosSetExceptionHandler.
Similarly, when the procedure ends, it must remove EXCEPTIONREGISTRATIONRECORD from the chain by calling DosUnsetExceptionHandler. This maintains the necessary frame-exception handler correspondence.
- Note
- For the benefit of assembly language programmers, the Thread Information Block (TIB) is located at FS:[0]. This speeds access to the TIB data structure.
- Because the FS is used to point to the TIB, applications that use the FS register must restore the original value when they are finished. Exception handling depends on the FS register pointing to the TIB.
- EXCEPTIONREGISTRATIONRECORD data structure must be created on the stack of the application. That is, it must be a data structure that is local to the routine that registers the exception handler. It cannot be stored in the application's data segment. The reason for this is that OS/2 must be able to determine the relative ordering of ExceptionRegistration records by examining their addresses.
ContextRecord Data Structure
The ContextRecord data structure describes the machine state at the time of an exception. This data structure is hardware dependent and is not portable. Therefore, as a rule, software should not use the information contained in this data structure. However, hardware dependent code, such as math libraries, can make use of this information to optimize certain operations.
For a hardware-initiated exception, ContextRecord contains the complete machine state at the time of the exception. For a software-initiated exception, ContextRecord contains the machine state at the time the software raised the exception.
The ContextRecord data structure consists of fields for the following:
- General purpose registers
- Segment registers
- The flags register
- The floating point environment and stack.
- Note
- With asynchronous exceptions (signal and termination exceptions), the context in ContextRecord is read-only. The exception handler can modify it, but the changes with be ignored.
- With synchronous exceptions, changes to ContextRecord will be used when the context is restored.
Exception Handler Return Values
Exception handlers can return one of the following values:
- XCPT_CONTINUE_SEARCH (0x00000000)
- Indicates that the exception has not been handled. The system responds by passing the exception to the previously installed handler in the thread's chain of exception handlers.
- XCPT_CONTINUE_EXECUTION (0xFFFFFFFF)
- Indicates that the exception has been handled. OS/2 responds by dismissing the exception, restoring the context of the thread, and continuing the execution of the thread.
Using Exception Management
When an exception occurs, the system default action in most cases is to end the application that caused the exception. Instead of having the system default action occur, an application can register its own exception handling routines. Exception handlers can be written to take corrective action so that a thread can continue running rather than being terminated by the system.
If an exception is handled by the application's exception handler, the exception handler must return XCPT_CONTINUE_EXECUTION. If the application's exception handler does not handle the exception, the exception handler must return XCPT_CONTINUE_SEARCH. If all the exception handlers in the exception handler chain return XCPT_CONTINUE_SEARCH, OS/2 takes the default action, which is usually to terminate the process that caused the exception.
- Note
- In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.
Example Exception Handler
This section of the chapter will present a simple exception handler. Because exception handlers are commonly used to handle memory faults, the example will show the exception handler working with a memory fault.
Memory exceptions can occur when an application attempts to access a guard page, attempts to use memory that has been allocated but not committed (a sparse memory object), or when an application attempts to write to memory that has read-only access. Without an application-registered exception handler, some of these exceptions might cause the application to terminate. If the application registers its own exception handler, it can correct the cause of the memory fault and continue to run.
If the application's exception handler handles the exception, it returns XCPT_CONTINUE_EXECUTION. If the routine does not handle the exception, it returns XCPT_CONTINUE_SEARCH so that the exception will be passed to the next handler in the chain.
The following code fragment shows an exception handling routine set up to deal with memory errors:
#define INCL_BASE #define INCL_DOSEXCEPTIONS #include <os2.h> #define HF_STDERR 2 /* Standard error handle */ ULONG _cdecl myHandler(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p) { ULONG ulWritten, ulMemSize, flMemAttrs; APIRET ulrc; /* Access violation at a known location */ if (pERepRec->ExceptionNum == XCPT_ACCESS_VIOLATION && pERepRec->ExceptionAddress != (PVOID) XCPT_DATA_UNKNOWN) { /* Page fault */ if ((pERepRec->ExceptionInfo[0] == XCPT_READ_ACCESS || pERepRec->ExceptionInfo[0] == XCPT_WRITE_ACCESS) && pERepRec->ExceptionInfo[1] != XCPT_DATA_UNKNOWN) { DosWrite(HF_STDERR, "\r\nPage Fault\r\n", 15, &ulWritten); /* Now query the memory to find out why we faulted. */ ulMemSize = 1; DosQueryMem((PVOID) pERepRec->pExceptionInfo[1], &ulMemSize, &flMemAttrs); /* If the memory is free or committed, */ /* we have some other problem. */ /* If it is not free or not committed, commit it. */ if (!(flMemAttrs & (PAG_FREE | PAG_COMMIT))) { DosWrite(HF_STDERR, "\r\nAttempt to access uncommitted memory\r\n", 40, &ulWritten); ulrc = DosSetMem((PVOID) pERepRec->ExceptionInfo[1], 4096, PAG_DEFAULT | PAG_COMMIT); if (ulrc) { DosWrite(HF_STDERR, "\r\nError committing memory\r\n", 27, &ulWritten); return (XCPT_CONTINUE_SEARCH); } else return (XCPT_CONTINUE_EXECUTION); } } } return (XCPT_CONTINUE_SEARCH); }
Registering an Exception Handler
An application uses DosSetExceptionHandler to register its own exception handling routines. More than one routine can be registered; the last routine registered will be called first.
One or more exception handlers can be registered for each thread in a process. Moreover, exception handlers can be specified not only for system exceptions, but also for user-defined exceptions that are anticipated for a particular thread.
Only Process Termination exceptions are sent to all threads in a process. Other exceptions (synchronous exceptions) are sent only to the exception handler registered for the thread where the exception occurred. The application must register an exception handler for each thread that is handling exceptions.
The following code fragment shows how an application registers an exception handling routine:
#define INCL_BASE #define INCL_DOSEXCEPTIONS #include <os2.h> ULONG _System myHandler(PEXCEPTIONREPORTRECORD, PEXCEPTIONREGISTRATIONRECORD, PCONTEXTRECORD, PVOID); VOID main(VOID) { EXCEPTIONREGISTRATIONRECORD xcpthand = { 0, &myHandler }; DosError(FERR_DISABLEEXCEPTION | FERR_DISABLEHARDERR); DosSetExceptionHandler(&xcpthand); /* . . Other processing occurs here; myHandler will handle the exceptions. . */ DosUnsetExceptionHandler(&xcpthand); }
If a procedure registers an exception handler, it must deregister the handler by calling DosUnsetExceptionHandler before returning.
Note:
A procedure must not call DosSetExceptionHandler if it performs language-specific exception or unwind handling. This restriction is not enforced, but unpredictable results could occur if it is violated.
DosSetExceptionHandler and DosUnsetExceptionHandler provide the portable means of implementing exception handlers. The non-portable approach is taken by directly manipulating the exception handler chain. High level languages generate code that abides by this restriction. Assembly language programmers must assume responsibility for verifying that handler registration and deregistration occur correctly.
EXCEPTIONREGISTRATIONRECORD must be created on the application's stack. That is, it must be local to the routine that registers the exception handler, rather than a global variable. It cannot be stored in the data segment of the program.
Note that in the code fragment above, the declaration is placed inside the braces (see figure below). Therefore xcpthand is local to the main() routine and is stored on the program's stack.
EXCEPTIONREGISTRATIONRECORD xcpthand = { 0, &myHandler };
System Exceptions
The operating system defines a class of error conditions called exceptions, and specifies the default actions that are taken when these exceptions occur. The system default action in most cases is to terminate the thread that caused the exception.
Exception values have the following 32-bit format:
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ┌───┬─┬─────────────────────────┬───────────────────────────────┐ │Sev│C│ Facility │ Code │ └───┴─┴─────────────────────────┴───────────────────────────────┘
- Sev
- Severity code. Possible values are described in the following list:
00 Success 01 Informational 10 Warning 11 Error
- C
- Customer code flag.
- Facility
- Facility code.
- Code
- Facility's status code.
Exceptions that are specific to OS/2 Version 2.X (for example, XCPT_SIGNAL) have a facility code of 1.
System exceptions include both synchronous and asynchronous exceptions. Synchronous exceptions are caused by events that are internal to a thread's execution. For example, synchronous exceptions could be caused by invalid parameters, or by a thread's request to end its own execution.
Asynchronous exceptions are caused by events that are external to a thread's execution. For example, an asynchronous exception can be caused by a user's entering a Ctrl+C or Ctrl+Break key sequence, or by a process' issuing DosKillProcess to end the execution of another process.
The Ctrl+Break and Ctrl+C exceptions are also known as signals, or as signal exceptions.
The following tables show the symbolic names of system exceptions, their numerical values, and related information fields.
- Portable, Non-Fatal, Software-Generated Exceptions
┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_GUARD_PAGE_VIOLATION │0x80000001│ │ ExceptionInfo[0] - R/W flag │ │ │ ExceptionInfo[1] - FaultAddr │ │ ├─────────────────────────────────────┼──────────┤ │XCPT_UNABLE_TO_GROW_STACK │0x80010001│ └─────────────────────────────────────┴──────────┘
- Portable, Fatal, Hardware-Generated Exceptions
┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ACCESS_VIOLATION │0xC0000005│0x09, 0x0B, │ │ ExceptionInfo[0] - Flags │ │0x0C, 0x0D, │ │ XCPT_UNKNOWN_ACCESS 0x0 │ │0x0E │ │ XCPT_READ_ACCESS 0x1 │ │ │ │ XCPT_WRITE_ACCESS 0x2 │ │ │ │ XCPT_EXECUTE_ACCESS 0x4 │ │ │ │ XCPT_SPACE_ACCESS 0x8 │ │ │ │ XCPT_LIMIT_ACCESS 0x10 │ │ │ │ ExceptionInfo[1] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INTEGER_DIVIDE_BY_ZERO │0xC000009B│0 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_DIVIDE_BY_ZERO │0xC0000095│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_INVALID_OPERATION │0xC0000097│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ILLEGAL_INSTRUCTION │0xC000001C│0x06 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_PRIVILEGED_INSTRUCTION │0xC000009D│0x0D │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INTEGER_OVERFLOW │0xC000009C│0x04 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_OVERFLOW │0xC0000098│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_UNDERFLOW │0xC000009A│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_DENORMAL_OPERAND │0xC0000094│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_INEXACT_RESULT │0xC0000096│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_STACK_CHECK │0xC0000099│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_DATATYPE_MISALIGNMENT │0xC000009E│0x11 │ │ ExceptionInfo[0] - R/W flag │ │ │ │ ExceptionInfo[1] - Alignment │ │ │ │ ExceptionInfo[2] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_BREAKPOINT │0xC000009F│0x03 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_SINGLE_STEP │0xC00000A0│0x01 │ └─────────────────────────────────────┴──────────┴─────────────┘
- Portable, Fatal, Software-Generated Exceptions
┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_IN_PAGE_ERROR │0xC0000006│0x0E │ │ ExceptionInfo[0] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_PROCESS_TERMINATE │0xC0010001│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ASYNC_PROCESS_TERMINATE │0xC0010002│ │ │ ExceptionInfo[0] - TID of │ │ │ │ terminating thread │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_NONCONTINUABLE_EXCEPTION │0xC0000024│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INVALID_DISPOSITION │0xC0000025│ │ └─────────────────────────────────────┴──────────┴─────────────┘
- Non-Portable, Fatal Exceptions
┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INVALID_LOCK_SEQUENCE │0xC000001D│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ARRAY_BOUNDS_EXCEEDED │0xC0000093│0x05 │ └─────────────────────────────────────┴──────────┴─────────────┘
- Unwind Operation Exceptions
┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_UNWIND │0xC0000026│ ├─────────────────────────────────────┼──────────┤ │XCPT_BAD_STACK │0xC0000027│ ├─────────────────────────────────────┼──────────┤ │XCPT_INVALID_UNWIND_TARGET │0xC0000028│ └─────────────────────────────────────┴──────────┘
- Fatal Signal Exceptions
┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_SIGNAL │0xC0010003│ │ ExceptionInfo[ 0 ] - Signal │ │ │ Number │ │ └─────────────────────────────────────┴──────────┘
List of System Exceptions
XCPT_ACCESS_VIOLATION
- Access Violation
An access violation exception is generated when an attempt is made either to load or store data in an inaccessible location, or to execute an inaccessible instruction. This exception corresponds to both the Intel 80386 general protection fault (#13), caused by an invalid access attempt; and the page fault (#14), caused by an attempt to access an uncommitted page or a page with incorrect attributes for the desired operation.
- Exception Code
- XCPT_ACCESS_VIOLATION (0xC0000005)
- Handler Information
- The ExceptionAddress field in the ExceptionReportRecord points to the instruction that caused the exception. This exception is continuable.
- Default Action
- The process is ended.
- Additional Parameters (2)
- Exception Info[ 0 ]
- Flags
XCPT_UNKNOWN_ACCESS(0x00) XCPT_READ_ACCESS(0x01) XCPT_WRITE_ACCESS(0x02) XCPT_EXECUTE_ACCESS(0x04) XCPT_SPACE_ACCESS(0x08) XCPT_LIMIT_ACCESS(0x10)
- Exception Info[1]
- FaultAddr The virtual address (if available) of the data that is not accessible, or XCPT_DATA_UNKNOWN.
XCPT_BREAKPOINT
- Breakpoint
A breakpoint exception occurs when a breakpoint instruction is executed. This exception is intended for use by debuggers. This exception is continuable.
- Exception Code
- XCPT_BREAKPOINT (0xC0000006)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_ARRAY_BOUNDS_EXCEEDED
- Bounds Check
The bounds check exception corresponds to the Intel 80386 bounds check fault (#5), caused by a BOUND instruction that fails.
- Exception Code
- XCPT_ARRAY_BOUNDS_EXCEEDED (0xC0000093)
- Handler Information
- The CS:EIP in the exception context structure points to the instruction that caused the exception. This exception is continuable.
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_DATATYPE_MISALIGNMENT
- Data-Type Misalignment
A data-type misalignment exception is generated when an attempt is made to load or store data in an address that is not naturally aligned on a hardware architecture that does not provide alignment hardware. For example, 16-bit entities must be aligned on two-byte boundaries, and 32-bit entities must be aligned on four-byte boundaries. This exception does not occur on the Intel 80386 processor. This exception is continuable.
- Exception Code
- XCPT_DATATYPE_MISALIGNMENT (0xC000009E)
- Default Action
- The process is ended.
- Additional Parameters (3)
- Exception Info[ 0 ]
- Read/Write Flag
XCPT_READ_ACCESS, or XCPT_WRITE_ACCESS.
- Exception Info[ 1 ]
- Data-type Mask
- A data-type mask that specifies how many low-address bits must be zero. For example, the data-type mask for a 16-bit entity is one, a 32-bit entity three, and so on.
- Exception Info[ 2 ]
- Virtual Address
- The virtual address of the misaligned data.
XCPT_FLOAT_DIVIDE_BY_ZERO
- Floating Divide-by-Zero
A floating divide-by-zero exception is generated when an attempt is made to divide a floating-point dividend by a floating-point divisor of zero. This exception is continuable.
- Exception Code
- XCPT_FLOAT_DIVIDE_BY_ZERO (0xC0000095)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_FLOAT_OVERFLOW
- Floating Overflow
A floating overflow exception is generated when the resulting exponent of a floating-point operation is greater than the magnitude allowed for the respective floating point data type. This exception is continuable.
- Exception Code
- XCPT_FLOAT_OVERFLOW (0xC0000098)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_FLOAT_UNDERFLOW
- Floating Underflow
A floating underflow exception is generated when the resulting exponent of a floating-point operation is less than the magnitude provided for the respective floating-point data type. This exception is continuable.
- Exception Code
- XCPT_FLOAT_UNDERFLOW (0xC000009A)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_FLOAT_INVALID_OPERATION
- Invalid Floating-Point Operation
This exception usually indicates a programming error corresponding to the invalid floating-point operations defined in IEEE Standard 754. The Intel 80386 processor raises trap #16. This exception is continuable.
- Default Action
- The process is ended.
- Exception Code
- XCPT_FLOAT_INVALID_OPERATION (0xC0000097)
- Additional Parameters
- None.
XCPT_FLOAT_DENORMAL_OPERAND
- Denormalized Operand
A denormalized operand exception occurs when the 80387 NPX processor attempts an arithmetic operation on a denormal operand, and the user has not masked off denormal operations. This exception is continuable.
- Exception Code
- XCPT_FLOAT_DENORMAL_OPERAND (0xC0000094)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_FLOAT_INEXACT_RESULT
- Loss of Precision
A loss of precision exception occurs when the result of an operation is not exactly representable in the destination format. For example, the fraction 1/3 cannot be exactly represented in binary form. For the Intel 80386 and 80387 processors, this corresponds to one of the class of exceptions for which the 80387 processor signals the 80386 processor to raise trap #16. This exception is continuable.
- Default Action
- The process is ended.
- Exception Code
- XCPT_FLOAT_INEXACT_RESULT (0xC0000096)
- Additional Parameters
- None.
XCPT_FLOAT_STACK_CHECK
- Invalid Floating-Point Stack Operation
An invalid floating-point stack check is raised when a floating-point processor attempts an illegal operation on a private stack. The Intel 80387 processor maintains eight internal 10-byte "registers" that are individually addressable and yet behave as a push-down stack under the influence of the FLD (push real) and FST (pop real to destination) instructions. Overflow and underflow are checked with each instruction, and this exception is raised when appropriate. This is one of the class of exceptions for which the Intel 80387 processor signals the Intel 80386 processor to raise trap #16. This exception is continuable.
- Exception Code
- XCPT_FLOAT_STACK_CHECK (0xC0000099)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_ILLEGAL_INSTRUCTION
- Illegal Instruction
An illegal instruction exception is generated when an attempt is made to execute an instruction whose operation is not defined for the host machine architecture. On the Intel 80386 processor, this corresponds to the invalid opcode fault (#6), caused by any invalid instruction. This exception is continuable.
- Exception Code
- XCPT_ILLEGAL_INSTRUCTION (0xC000001C)
- Default action
- The process is ended.
- Additional Parameters
- None.
XCPT_PRIVILEGED_INSTRUCTION
- Privileged Instruction
A privileged instruction exception is generated when an attempt is made to execute an instruction whose operation is not allowed in the current machine mode. For example, an attempt is made to execute an instruction in user mode that is only allowed in kernel mode. This exception is continuable.
- Exception Code
- XCPT_PRIVILEGED_INSTRUCTION (0xC000009D)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_INVALID_LOCK_SEQUENCE
- Invalid Lock Sequence
An invalid lock sequence exception is generated when an attempt is made to execute an operation within an interlocked section of code, and the sequence is invalid for the host machine architecture. This exception is continuable.
- Exception Code
- XCPT_INVALID_LOCK_SEQUENCE (0xC000001D)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_INTEGER_DIVIDE_BY_ZERO
- Integer Divide-by-Zero
An integer divide-by-zero exception is generated when an attempt is made to divide an integer dividend by an integer divisor of zero. On the Intel 80387 processor, this is a divide by zero fault (#0), caused by a DIV or IDIV by zero operation. This exception is continuable.
- Exception Code
- XCPT_INTEGER_DIVIDE_BY_ZERO (0xC000009B)
- Default action
- The process is ended.
- Additional Parameters
- None.
XCPT_INTEGER_OVERFLOW
- Integer Overflow
An integer overflow exception is generated when the result of an integer operation causes a carry-out of the most significant bit of the result, which is not the same as the carry-into of the most significant bit of the result. For example, the addition of two positive integers produces a negative result. On the Intel 80387 processor, this corresponds to overflow trap (#4), caused by executing an INTO instruction with the OF flag set. This exception is continuable.
- Exception Code
- XCPT_INTEGER_OVERFLOW (0xC000009C)
- Default action
- The process is ended.
- Additional Parameters
- None.
XCPT_SINGLE_STEP
- Single Step
A single-step exception is generated when a trace trap or other single instruction execution mechanism signals that one instruction has been executed. This exception is intended for use by debuggers. This exception is continuable.
- Default Action
- The process is ended
- Exception Code
- XCPT_SINGLE_STEP (0xC00000A0)
- Additional Parameters
- None.
XCPT_GUARD_PAGE_VIOLATION
- Guard Page Violation
A guard page violation exception is generated when an attempt is made to load or store data in a location that is contained within a guard page. Memory management software immediately turns the guard page into a demand zero page and initiates a guard page violation exception.
- Exception Code
- XCPT_GUARD_PAGE_VIOLATION (0x800001)
- Default Action
- Execution continues. If possible, the memory page immediately below the guard page is allocated and marked as a guard page. The higher guard page is marked to no longer be a guard page, and the instruction is restarted. This allows for dynamic stack growth. If it is not possible to allocate another page below the faulting page, an XCPT_UNABLE_TO_GROW_STACK exception is raised. This exception is continuable.
- Additional Parameters (2)
- ExceptionInfo[ 0 ]
- Read/Write Flag
- XCPT_READ_ACCESS, or
- XCPT_WRITE_ACCESS.
- ExceptionInfo[ 1 ]
- Virtual Address
- The virtual address of the data within a guard page.
XCPT_UNABLE_TO_GROW_STACK
- Unable to Grow Stack
The default action for a guard page violation is to attempt to allocate another page of memory immediately below the page on which the fault occurred, thereby implementing dynamic stack growth. If this attempt fails, XCPT_UNABLE_TO_GROW_STACK is generated, indicating that the thread has, at most, one more page of stack space available. This exception is continuable.
- Exception Code
- XCPT_UNABLE_TO_GROW_STACK (0x80010001)
- Default Action
- Execution continues.
- Additional Parameters
- None.
XCPT_BAD_STACK
- Bad Stack
This exception is raised when an ExceptionRegistrationRecord is reached that is not properly aligned or is not within the current stack boundaries. It is also raised if an unwind target is specified that does not point to an ExceptionRegistrationRecord. This exception is noncontinuable.
- Exception Code
- XCPT_BAD_STACK (0xC0000027)
- Default Action
- The process is ended.
- Additional Parameters
- None.
XCPT_INVALID_UNWIND_TARGET
- Invalid Unwind Target
This exception is raised when the address of the target ExceptionRegistrationRecord is below the current stack pointer. This exception is noncontinuable.
- Exception Code
- XCPT_INVALID_UNWIND_TARGET
- Default Action
- The process is ended.
- Additional Parameters
- None.