The next sections cover the following topics:
When a handler is invoked, it is called as a procedure with arguments that describe the following:
When the handler is called, the exception is said to be delivered to the handler.
When an exception handler has finished processing an exception, it must indicate this state in one of the following ways:
The handler indicates that the exception handling support code should reraise the exception and resume the search for a new handler.
The handler indicates that the exception handling support code should continue execution of the interrupted thread at the location indicated by the saved exception program counter.
The handler performs an unwind operation that causes the exception handling support code to resume execution of the thread at a point other than the point at which it was interrupted or to terminate the execution of the thread.
Each exception has an exception value that identifies the type of exception, such as subscript range violation or memory access control violation. Exceptions can also be associated with one or more exception qualifiers (such as the name of an array and the subscript that was out of range, or an address associated with a memory access control violation).
A software-caused general exception is raised when an exception-raising procedure is invoked. This type of exception is always delivered to the thread that made the call.
A software-caused exception can be raised at any point during thread execution. Applications and language run-time libraries can raise general exceptions to notify a thread of some exceptional (noteworthy) state in the current thread context. For example, subscript range-checking failures and assertion-checking failures can be raised as software-caused general exceptions.
A hardware-caused general exception occurs when a thread performs some action that causes an exceptional state to exist in the hardware. Such a state will cause the currently active thread to be interrupted. A hardware-caused general exception is always delivered to the thread that executed the instruction that caused the exception.
The following characteristics are specific to individual hardware exceptions:
Hardware exceptions are fully defined in the Alpha Architecture Reference Manual.
In the Digital UNIX for Alpha systems environment, hardware exceptions that are not handled by the operating system itself are reported to user level in the form of a POSIX signal. Such signals are then further interpreted as exceptions and are handled as described in this calling standard. See Section 5.1.13 for details on how this signal interpretation is achieved.
Unwind exceptions are delivered as part of the notification process that an unwind is in progress. (See Section 5.2 for details.)
Figure 5-1 shows the components of a status value structure.
The components of this representation are as follows:
A facility code indicating the software component that defines this status value. Only those values defined in the file /usr/include/excpt.h can be used. This component consists of three subfields:
This field has the value ffe(hex) on Digital UNIX for Alpha systems to distinguish the status value from those of other operating systems.
This field indicates the particular programming language or system associated with the value.
This field serves the same routine as facility_dependent_1.
This field contains a value for a particular status condition.
There are several ways for a software system or application to define its own status values:
The first exception record in the list describes the primary exception. Secondary exceptions can be specified by adding exception records to the list. Secondary exceptions qualify or elaborate the primary exception. In some cases, they are raised at the same time as the primary exception. In other cases, a handler can add new secondary exceptions to the list before handling or reraising the exception.
Figure 5-2 shows the format of an exception record.
The following ExceptionFlags bits are detail flags that give additional details:
If EXCEPTION_NONCONTINUABLE is 1, an exception handler must not return ExceptionContinueExecution.
If EXCEPTION_EXIT_UNWIND is 1, the exception handler is being invoked because of an unwind operation that will terminate execution of the thread.
If EXCEPTION_UNWINDING is 1, the exception handler is being invoked because of a general unwind operation with the semantics of longjmp().
If EXCEPTION_NESTED_CALL is 1, an exception or unwind is in progress at the time this exception is delivered.
If EXCEPTION_STACK_INVALID is 1, the stack is invalid.
Note that this flag is for use by system software; it will never be 1 in an exception record delivered to a normal handler.
If EXCEPTION_TARGET_UNWIND is 1, this frame is the target frame of an unwind operation. (This flag can be useful in allowing a language-specific handler to perform proper handling for the last step of an unwind.)
If EXCEPTION_COLLIDED_UNWIND is 1, an unwind collision has occurred. (See Section 5.2.5 for information on multiply active unwind operations).
All FLAGS bits other than those defined in the preceding lists must be zero.
For a hardware, signal, or asynchronous software exception, this field contains the address of the instruction at which the hardware, signal, or asynchronous exception interrupted execution of the thread.
For a synchronous software exception, this field contains the address of the call instruction that invoked the library routine for raising the exception.
This field is significant only in primary exception records; its contents are unpredictable in secondary exception records.
All Alpha hardware exceptions have exception information associated with them. This information can be as little as the exception type and exception PC or as much as three registers worth of additional information. The specific information that is supplied with each exception type is defined in the Alpha Architecture Reference Manual.
Hardware exceptions are reported to user mode in the form of POSIX signals. See Section 5.1.5.3.
The ExceptionCode for unwind exceptions contains the reason code for the unwind along with any supplied qualifiers. This information is similar to the contents of the ExceptionCode field for general exceptions.
For example, in a POSIX-conforming environment, all arithmetic exceptions are delivered with a signal number SIGFPE. The qualifiers can indicate whether the signal was caused by a floating underflow, integer overflow, or other arithmetic exception. (In a POSIX-conformant environment, no mechanism is provided for a user program to deliver a signal with a qualifier of any kind. Thus, the presence of such a qualifier occurs because it was produced by some system facility, such as the hardware exception dispatching code.) See Section 5.1.13 for a discussion of exception and signal handling coexistence.
The frame-based exception handlers that can be invoked are those established by active procedures, from the most current procedure to the oldest predecessor.
An exception handler that conforms to this calling standard should not handle any exception that its establisher did not cause, unless there is a prior agreement between the writers of the exception handler and the writers of the code that raised the exception.
Exceptions can be raised and unwind operations that cause exception handlers to be called can occur when the current value of at least one variable is in a register rather than in memory. Therefore, a handler and any descendant procedure called directly or indirectly by a handler must not access any variables except those explicitly passed to the procedure as arguments or those that exist in the normal scope of the procedure.
This requirement can be violated for specific memory locations only by agreement between the handler and all procedures that might access those memory locations. The effects of such agreements are not specified by this calling standard.
A procedure descriptor for which PDSC_FLAGS_HANDLER_VALID is 1 must specify in PDSC_RPD_HANDLER_ADDRESS the procedure value of an exception handler. The exception handler specified by a procedure descriptor is established when the corresponding procedure is added to the invocation chain; that is, when the procedure designated by the descriptor becomes current.
Dynamic activation or deactivation of exception handlers is not defined by this calling standard (and, in fact, is not permitted within the semantics of many language standards). If this capability is required, it must be defined on a language-by-language basis.
Compilers that choose to support this capability can establish language-specific static exception handlers that provide the dynamic exception handling semantics of that language. Such static handlers would be established by means of the procedure descriptor of the establishing procedure. If a language compiler decides to support dynamic activation of exception handlers, it must be prepared to recognize code that intends to use this feature. This requirement results from the need to add appropriate trapb instructions and other compile-time considerations necessary to make dynamic exception handling function correctly.
There can be additional protocols and conventions for dynamic exception handling. Such protocols and conventions might be needed, for example, to enable a debugger to perform effectively within the language exception handling environment. Such conventions are driven by the requirements of the languages and the language support utilities, and are not addressed by this calling standard. Note
exc_raise_exception (ExceptionRecord)
ExceptionRecord | The address of a primary exception record |
The exc_raise_exception() routine sets ExceptionAddress to the address of the invoking call instruction. If exc_raise_exception() detects that the exception record passed as the first argument is not a valid exception record, the routine raises the exception EXC_INVALID_EXCEPTION_RECORD.
In a constrained environment, gentrap can be handled directly through the SCB vector. In a more complete environment, the gentrap parameter is transformed into a corresponding exception code and reported as a normal hardware exception. Low-level software can use this mechanism to report exceptions in a way that is independent of the execution environment. Compiled code can also use this mechanism to raise common generic exceptions more cheaply than would be possible if the code had to make a full procedure call to exc_raise_exception().
The gentrap PAL call is defined as follows:
gentrap (EXPT_CODE)
EXPT_CODE | Code for the exception to be raised |
If the EXPT_CODE value is one of the small negative values shown in Table 5-3, that value is mapped to the corresponding POSIX signal, as shown. The signal qualifier is set to EXPT_CODE. The signal can be converted to an exception by installing exc_raise_signal_exception() as the signal handler. (See Section 5.1.13.) The gentrap instruction sets ExceptionAddress to the address of the PAL call.
For other values of EXPT_CODE, the behavior of gentrap is not defined by this standard.
Note that there are no mechanisms to associate any parameters with an exception raised using gentrap.
EXPT_CODE Value | Symbol | Meaning | POSIX Signal |
---|---|---|---|
-1 | GEN_INTOVF | Integer overflow | SIGFPE |
-2 | GEN_INTDIV | Integer divide by zero | SIGFPE |
-3 | GEN_FLTOVF | Floating overflow | SIGFPE |
-4 | GEN_FLTDIV | Floating divide by zero | SIGFPE |
-5 | GEN_FLTUND | Floating underflow | SIGFPE |
-6 | GEN_FLTINV | Floating invalid operation | SIGFPE |
-7 | GEN_FLTINE | Floating inexact result | SIGFPE |
-8 | GEN_DECOVF | Decimal overflow | SIGTRAP |
-9 | GEN_DECDIV | Decimal divide by zero | SIGTRAP |
-10 | GEN_DECINV | Decimal invalid operand | SIGTRAP |
-11 | GEN_ROPRAND | Reserved operand | SIGFPE |
-12 | GEN_ASSERTERR | Assertion error | SIGTRAP |
-13 | GEN_NULPTRERR | Null pointer error | SIGTRAP |
-14 | GEN_STKOVF | Stack overflow | SIGTRAP |
-15 | GEN_STRLENERR | String length error | SIGTRAP |
-16 | GEN_SUBSTRERR | Substring error | SIGTRAP |
-17 | GEN_RANGEERR | Range error | SIGTRAP |
-18 | GEN_SUBRNG | Subscript range error | SIGTRAP |
-19 | GEN_SUBRNG1 | Subscript 1 range error | SIGTRAP |
-20 | GEN_SUBRNG2 | Subscript 2 range error | SIGTRAP |
-21 | GEN_SUBRNG3 | Subscript 3 range error | SIGTRAP |
-22 | GEN_SUBRNG4 | Subscript 4 range error | SIGTRAP |
-23 | GEN_SUBRNG5 | Subscript 5 range error | SIGTRAP |
-24 | GEN_SUBRNG6 | Subscript 6 range error | SIGTRAP |
-25 | GEN_SUBRNG7 | Subscript 7 range error | SIGTRAP |
If a null frame procedure or other fragment of code does not have an associated procedure descriptor, it is assumed that an appropriate initial program counter value is located in the normal return address register ($26). If there is a procedure descriptor associated with this address, the search for an exception handler begins using that address; otherwise, a fatal exception is raised and the executing thread is terminated.
Any frame-based handlers are invoked in order, from the handler established by the most current procedure invocation to the handler established by the oldest predecessor in the invocation chain.
If no frame-based handlers have been established, or if all of them reraise the exception, the system last chance handler is invoked.
A nested exception occurs if an exception is raised while an exception handler is active. When a nested exception occurs, the structure of the procedure invocation chain, from the most recent procedure invocation to the oldest predecessor, contains the following elements in the specified order:
If there are zero invocations in item 2, this invocation is the same as item 1 (the invocation in which the nested exception was raised). In this case, items 1 and 3 count as a single invocation.
If there are zero invocations in item 5, this invocation is the same as item 4 (the invocation in which the exception that immediately preceded the nested exception was raised).
Figure 5-3 shows an example of a procedure invocation chain, where BB is the most recent invocation and A is the oldest predecessor. The numbers on the left side of the figure correspond to the elements in the previous list.
The following list contains the time-ordered events that gave rise to this invocation chain:
Established handlers are invoked in reverse order to the order in which their establishers were invoked. That is, the search of stack frames for procedure invocations that have established handlers is in the order 1 to 7.
If further nested exceptions occur, this procedure invocation chain structure is repeated for those further nested exceptions. Frame-based handlers are invoked according to the order previously listed; that is, from those established by the most current procedure to those established by the oldest predecessor.
The following pseudocode shows the steps for locating and invoking exception handlers. Note that these steps cover only the search of stack frames for a handler proper and do not address the mapping of a POSIX signal to an exception.
(*ExceptionHandler) ( ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext )
ExceptionRecord | The address of a primary exception record. |
EstablisherFrame | The virtual frame pointer of the establisher (discussed in Section 7.1). |
ContextRecord | The address of a sigcontext structure containing the saved original context at the point where the exception occurred. During an unwind, this argument contains the address of the sigcontext structure for the establisher. (The sigcontext structure is defined in the file /usr/include/signal.h.) |
DispatcherContext | The address of a control record for the exception dispatcher. |
STATUS | A value indicating the action to be taken upon handler return. The valid values are ExceptionContinueExecution and ExceptionContinueSearch. Note: the exception dispatcher allows additional return values from its own exception handlers. |
This field contains the program counter (PC) where control left the establisher of the exception handler; that is, the PC of the call instruction or the instruction that caused the exception. This field can be updated by a handler. If a nested exception occurs during unwinding while the handler is still active, the value of the PC used for the establisher will be the updated value of ControlPC. This mechanism can be employed to retire nested exception handling scopes that are local to a procedure in order to assure that each is executed only once at most (even in the presence of a nested exception within such a handler). The ControlPC value must, however, always be an address within the procedure whose handler is executing.
This field is a quadword whose meaning is determined by the handler. Typically, a language-specific handler will write new values into this field as it processes nested scopes. If a colliding unwind occurs, the dispatcher sets collide_info to the value it had for the establisher. A handler knows to read this field when EXCEPTION_COLLIDED_UNWIND is 1. This field can be used in the same manner as the ControlPC field to retire nested exception scopes, but it does not have to be an address within the current procedure.
This field contains a pointer to the procedure descriptor for the establisher. The field is read-only to exception handlers, except for handlers for the exception dispatcher itself.
The effects of a handler-modifying passed exception information are as follows:
If ExceptionContinueExecution is returned after the EXCEPTION_NONCONTINUABLE flag has been changed from 0 to 1, a nested exception is raised with ExceptionCode equalling EXC_STATUS_NONCONTINUABLE_EXCEPTION, indicating that an attempt was made to continue from a noncontinuable exception. This second exception is also noncontinuable.
However, an exception handler must not change the EXCEPTION_NONCONTINUABLE flag from 1 to 0.
Reraising causes the next exception handler to be invoked. (See Section 5.1.9.1 for details.)
If all exception handlers established by the thread reraise the exception, the system last chance handler is invoked, with system-dependent results.
If ExceptionContinueExecution is returned and the EXCEPTION_NONCONTINUABLE flag is 1, a nested exception is raised with ExceptionCode equalling EXC_STATUS_NONCONTINUABLE_EXCEPTION. This second exception is also noncontinuable.
The rules presented are designed to assure correct operation across all implementations of that architecture. As with all aspects of this calling standard, optimization information may assure correct behavior as if these rules were followed, without appearing to explicitly do so.
Alternative approaches that exploit implementation-specific characteristics are also possible, but are outside the scope of this calling standard.
The rules for bounding the exception range are as follows:
Rationale: This rule is required because a standard procedure is not allowed to handle traps that it might not have caused.
Rationale: This rule is required because handlers established by previous invocations in the call chain might not be able to handle exceptions from a procedure invocation that is no longer active.
Software-raised general exceptions are, by definition, synchronous with the instruction stream and can have a well-defined continuation point. Thus, a handler has the option of requesting continuation from a software raised exception. However, because compiler-generated code typically relies on error-free execution of previously executed code, continuing from a software-raised exception might produce unpredictable results and unreliable behavior unless the handler has explicitly fixed the cause of the exception in a way that is transparent to subsequent code.
Hardware faults on Alpha systems follow rules that, loosely paraphrased, state the following: if the offending exception is fixed, reexecution of the instruction (as determined from the supplied PC) will yield correct results. This generality does not imply that no instructions following the faulting instruction have been executed. (See the Alpha Architecture Reference Manual for details.) Hardware faults can therefore be viewed as similar to software-raised exceptions and can have well-defined continuation points.
Arithmetic traps cannot be restarted unless all the information required for a restart is available. The most straightforward and reliable way for software to guarantee the ability to continue from this type of exception is by placing appropriate trapb instructions in the code stream. Although this technique does allow continuation, it must be used with extreme caution due to the negative side effects it has on application performance. A more sophisticated technique that requires typically one trapb for each basic block is described in the Alpha Architecture Reference Manual in the section on imprecise software completion trap modes.
exc_raise_signal_exception (... system-defined-arguments ...)
This special signal handler gathers the software and/or hardware information associated with a signal into an exception record where ExceptionCode.osf_facility is EXC_SIGNAL (ffe0003(hex)), ExceptionCode.code is the signal value, and ExceptionInformation[0] contains any signal qualifier. This record is passed to the exception-handling support code in a form that can be processed by normal exception handlers. The exception-handling code then proceeds with its normal search and invocation of procedures as described in Section 5.1.2.
If any handler returns with ExceptionContinueExecution (as described in Section 5.1.2), thread execution resumes at the point where it was interrupted by the signal.
If no exception handlers are located or if all handlers reraise the exception, the system's last chance handler is invoked with unpredictable (in this case, system-dependent) results.
If it is useful, applications and language run-time support code can install exc_raise_signal_exception() for other signals, such as SIGINT. However, such actions might not be appropriate for signals that result from external autonomous events or signals, like SIGIO, that are used for flow control.
Explicit unblocking is necessary if a handler itself might cause a blocked signal to occur. Similarly, explicit unblocking might be needed if the same signal is raised in two threads and must be processed by procedure-based exception handling at the same time; for example, when one thread cannot wait for the other thread to complete its exception handling.
This section discusses the following topics:
Before control is transferred to the unwind target location, the unwind support code invokes all frame-based handlers that were established by procedure invocations which are being terminated, as well as the handler for the target invocation.
This behavior gives each procedure invocation the chance to perform clean-up processing before its context is lost. These handlers are invoked with an indication that an unwind is in progress. The exception record passed to the target invocation's handler also has EXCEPTION_TARGET_UNWIND set to 1.
Once all the relevant frame-based handlers have been called and the appropriate frames have been removed from existence, the target invocation's saved context is restored and execution is resumed at the specified location.
The results of attempting an unwind operation to any invocation previous to the top-level procedure of a thread are undefined by this calling standard.
Unwinding does not require an exception handler to be active. Unwind operations can be used by languages to implement nonlocal GOTOs.
When a general unwind is completed, the registers are updated from the invocation context for the target frame. Register $0 obtains its value from the ReturnValue argument to the unwind operation. This action allows a status to be returned to the target of the unwind.
Because of this need to clean up shared resources, exiting by a user-mode thread can be accomplished only by unwinding. A special type of unwind, called an exit unwind, performs the following actions to terminate execution:
Threads that use any mechanism for termination other than the normal return process are not considered to be standard and their behavior is undefined.
An unwind operation specifies a target invocation in the procedure invocation chain and a location in that procedure. The operation terminates all invocations up to the target invocation and continues thread execution at the specified location in that procedure.
Before control is transferred to the target location, the unwind operation invokes each frame-based handler that was established by any procedure invocations being terminated, and also invokes the handler for the target invocation.
exc_unwind (VirtualTargetFrame, TargetPC, ExceptionRecord, ReturnValue) exc_unwind_rfp (RealTargetFrame, TargetPC, ExceptionRecord, ReturnValue)
VirtualTargetFrame | If nonzero, specifies the virtual frame pointer of the target procedure invocation to where the unwind is to be done. If zero, specifies that an exit unwind is initiated and causes the EXCEPTION_EXIT_UNWIND flag to be set to 1 in the exception record. |
RealTargetFrame | If nonzero, specifies the real frame pointer of the target procedure invocation to which the unwind is to be done. If zero, specifies that an exit unwind is initiated and causes the EXCEPTION_EXIT_UNWIND flag to be set to 1 in the exception record. |
TargetPC | Specifies the address within the target invocation at which to continue execution. If a target frame argument is zero, this argument is ignored. |
ExceptionRecord | If nonzero, specifies the address of a primary exception record. If zero, specifies that a default exception record should be supplied. |
ReturnValue | Specifies the value to use as the return value (contents of $0) at the completion of the unwind. |
If the ExceptionRecord argument is zero, exc_unwind() or exc_unwind_rfp() supplies a default exception record. That default exception record specifies exactly one exception record in which ExceptionCode is EXC_STATUS_UNWIND. For an explicit or default exception record, the EXCEPTION_UNWINDING flag is set to 1; and, if a null target frame argument is specified, the EXCEPTION_EXIT_UNWIND flag is set to 1. The ExceptionAddress is set to TargetPC.
If the ExceptionRecord argument is specified when the unwind is initiated, all other properties of the exception record are determined by ExceptionRecord. If exc_unwind() or exc_unwind_rfp() detects that a specified exception record is not a valid unwind record, the routine will raise the exception of EXC_INVALID_EXCEPTION_RECORD. If the frame corresponding to the target frame argument cannot be found, the system last-chance handler is called because all procedures have been terminated.
Once an unwind is initiated, control never returns from the call.
An unwind that is initiated while a previous unwind is active is either a nested unwind or a colliding unwind. This section discusses both types of multiply active unwind operations.
When a nested unwind is initiated, no special rules apply. The nested unwind operation proceeds as a normal unwind operation. When execution resumes at the target location of the nested unwind, the nested unwind is complete and the previous unwind is once again the most current unwind operation.
A colliding unwind is detected when the most current active unwind handler is terminated. This detection of a colliding unwind is referred to as a collision. When a collision occurs, the second (more recent) unwind operation takes precedence and the previous unwind is abandoned.
The next action is to reinvoke the most current established handler because its establisher has not been unwound. The EXCEPTION_COLLIDED_UNWIND flag is set in the exception record to indicate this situation to the handler.
Any environment that conforms to this calling standard must implement nonlocal GOTOs by using exc_unwind() or exc_unwind_rfp() (or an equivalent means) to allow all procedures being terminated to clean up any local or global states, as appropriate.
The longjmp() routine for Digital UNIX does not use an unwind operation. Therefore, in the presence of frame-based exception handling, it is preferable to use exc_longjmp(), implemented through an unwind operation. Note