This chapter discusses specifications related to events that are outside the normal program flow. The topics covered are:
Exception handling
Unwinding
This section discusses the following considerations involved in the notification and handling of exceptional events during the course of normal program execution. An exception is a condition in the current software and/or hardware state that should be noted or fixed. The exception handler deals with the exception condition.
The next sections cover the following topics:
Requirements for exception handling
An overview of exception handling
The kinds of exceptions
Status values and exception codes
Exception records
Frame-based exception handlers
How handlers become established
How exceptions are raised
The search for and invocation of exception handlers
Modification of exception records and context by handlers
Handler completion and return value
Exception synchronization and continuation from exceptions
Coexistence of exception and signal handling
5.1.1 Exception Handling Requirements
This Tru64 UNIX calling standard supports the following exception handling capabilities:
Reliable programmer and program control over response to exceptions and reporting of such exceptions, as well as over the flow of control when exceptions occur
Orderly termination of layered applications
Correct and predictable exception handling in a multilanguage environment
Construction of modular, maintainable multilanguage applications
Parallel multithreaded application execution, including:
Per-thread exception handling
Handling of asynchronous exceptions
Safe thread exit in a multithreaded environment
Coexistence and interoperation with POSIX-defined signal handling
5.1.2 Exception Handling Overview
When an exception occurs (is raised), the following events take place:
The normal flow of control in the current thread is interrupted.
The current context is saved.
Control is transferred to the exception-handling support code.
The exception-handling support code collects the exception information and then enters a section of the support code called the exception dispatcher.
The exception dispatcher searches for exception handlers and invokes them in the proper sequence.
When a handler is invoked, it is called as a procedure with arguments that describe the following:
The nature of the exception
The environment within which the exception was raised
The environment within which the handler was established
When the handler is called, the exception is said to be delivered to the handler.
The handler can respond to the exception in several ways, including various combinations of the following:
Perform some action that affects the context of the thread (such as correcting the circumstances that led to the exception being raised).
Modify or augment the description of the exception.
Raise a nested exception, causing another exception to occur in the context of the exception handler or in a procedure called directly or indirectly by the handler.
When an exception handler has finished processing an exception, it must indicate this state in one of the following ways:
Reraise the exception.
The handler indicates that the exception handling support code should reraise the exception and resume the search for a new handler.
Continue the normal program flow.
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.
Unwind from the current operation.
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.
All exceptions are handled using the same interfaces, data structures,
and algorithms.
That is, exception handling is unified for all kinds of exceptions,
regardless of their origins.
The interfaces and data structures are defined
in the file
/usr/include/excpt.h
.
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).
5.1.3 Kinds of Exceptions
There are three kinds of exceptions:
General exceptions - those caused by general software or hardware notification mechanisms
Unwind exceptions - those caused by unwind operations
Signal exceptions - those caused in support of the POSIX
1003.1
signal()
routine
General exceptions are divided into two categories:
Software-caused
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.
Hardware-caused
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:
Exactly which hardware events can result in exceptions
The state of the machine when a hardware exception occurs
The interpretation of the exception-related information that is delivered to a user mode thread
The circumstances under which execution can be continued
Hardware exceptions are fully defined in the Alpha Architecture Reference Manual.
In the Tru64 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 result from the invocation of the unwind support code by a thread. These exceptions are always delivered to the thread that invoked the unwind.
Unwind exceptions are delivered as part of the notification process
that an unwind is in progress.
(See
Section 5.2
for details.)
5.1.3.3 Signal Exceptions
Signal exceptions result from the delivery
of a POSIX signal.
This signal is subsequently converted into an exception
that can be handled using the capabilities defined by this calling standard.
5.1.4 Status Values and Exception Codes
A status value is a quadword that can be used as a return value from a procedure call to indicate success, failure, or other information about the requested operation. A status value can also be used as an exception code to indicate the reason that an exception is being raised.
Figure 5-1
shows the components of a status
value structure.
Figure 5-1: Status Value Representation
The components of this representation are as follows:
osf_facility
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:
facility
This field has the value ffe(hex) on Tru64 UNIX for Alpha systems to distinguish the status value from those of other operating systems.
facility_dependent_1
This field indicates the particular programming language or system associated with the value.
facility_dependent_2
This field serves the same routine as
facility_dependent_1
.
code
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:
It may already have status values from another operating system, such as OpenVMS.
It can register values for the fields
facility_dependent_1
and
facility_dependent_2
with Digital.
The
facility
field will have the value ffe(hex).
All values of the
code
field will be available.
It can use the predefined
osf_facility
value
EXC_C_USER
(ffe0009(hex)).
However, these status values might conflict
with status values of other operating systems.
The fundamental data structure for describing exceptions is the exception record. Exception records can be joined together by handlers to form a linked list. Each record in a list describes one exception.
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.
Storage for exception records can be allocated in read-only memory. The exception record that is passed to a handler is a separate read-write copy constructed from information in the original exception record augmented with additional information.
Figure 5-2
shows the format of
an exception record.
Figure 5-2: Exception Record Format
ExceptionCode
is an exception code value.
(See
Section 5.1.4.)
ExceptionFlags
is a bit field of flags that further qualify
the exception.
These flag bits are significant only in the primary exception
record; their state is
unpredictable
in secondary exception
records.
ExceptionFlags
bits are logically divided into two groups:
detail flag
and
environment flags.
Detail flags provide additional information
about the exception.
Environment flags provide additional information about
the environment in which the exception is being delivered.
The following
ExceptionFlags
bits are detail flags that give
additional details:
EXCEPTION_NONCONTINUABLE
(bit 0)
If
EXCEPTION_NONCONTINUABLE
is 1, an exception handler must
not return
ExceptionContinueExecution
.
EXCEPTION_EXIT_UNWIND
(bit 2)
If
EXCEPTION_EXIT_UNWIND
is 1, the exception handler is being
invoked because of an unwind operation that will terminate execution of the
thread.
EXCEPTION_UNWINDING
(bit 1)
If
EXCEPTION_UNWINDING
is 1, the exception handler is being
invoked because of a general unwind operation with the semantics of
longjmp()
.
The following
ExceptionFlags
bits are environment flags that
give additional information about the environment at the time of exception
delivery:
EXCEPTION_NESTED_CALL
(bit 4)
If
EXCEPTION_NESTED_CALL
is 1, an exception or unwind is
in progress at the time this exception is delivered.
EXCEPTION_STACK_INVALID
(bit 3)
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.
EXCEPTION_TARGET_UNWIND
(bit 5)
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.)
EXCEPTION_COLLIDED_UNWIND
(bit 6)
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.
ExceptionRecord
is zero or contains the address of the next exception
record in the list.
ExceptionAddress
is the address of the instruction causing the
exception.
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.
NumberParameters
is the number of exception-specific
qualifiers in the exception record.
Each
ExceptionInformation
[n]
value is a single quadword that provides
additional information specific to the exception.
The quadword can also contain
information intended for display in messages.
In the case of software-caused exceptions, the information in the exception records for general and unwind exceptions can vary widely from a simple single-exception value to a long chain of exceptions and exception qualifiers. This calling standard defines the conventions for constructing these exception records. However, a complete enumeration of all possible combinations is beyond the scope of this document.
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.
5.1.5.2 Exception Records for Unwind Exceptions
Unwind exceptions have at least one of the following flags set to 1:
EXCEPTION_UNWINDING
EXCEPTION_EXIT_UNWIND
EXCEPTION_TARGET_UNWIND
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.
5.1.5.3 Exception Records for Signal Exceptions
In exception records
for signal exceptions, the value of the
ExceptionCode
field includes
the signal number.
Any additional exception qualifiers that are present can
further qualify the signal.
These qualifiers are most useful for hardware-generated
signals.
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.
5.1.6 Frame-Based Exception Handlers
A frame-based exception handler is established when a procedure whose descriptor specifies an exception handler becomes current. Thus, frame-based handlers are usually associated with a procedure at compile time and are located at run time through the procedure descriptor. These exception handlers are normally used to implement a particular language's exception handling semantics.
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.
5.1.7 Establishing Handlers
The list of established frame-based handlers for a thread is defined by the thread's procedure invocation chain. (See Chapter 7 for information on procedure invocations and call chains.)
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.
The exception handler remains established as long as that procedure invocation is part of the invocation chain. The handler is revoked when that procedure is removed from the invocation chain; that is, when the procedure invocation designated by the descriptor terminates, either by returning or being unwound.
Thus, the set of frame-based handlers that is established at any moment is defined by the current procedure call chain.
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.
Note
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.
This section describes the four ways
of raising exceptions.
5.1.8.1 Raising General Exceptions
A thread can raise a general exception in its own context by calling a system library routine defined as follows:
exc_raise_exception (ExceptionRecord)
Arguments:
ExceptionRecord |
The address of a primary exception record |
Function Value:
None
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
.
5.1.8.2 Raising General Exceptions Using gentrap
The
Alpha Architecture Reference Manual
defines the
gentrap
PAL
call
as a mechanism for software
to raise hardware-like exceptions at minimum cost.
This mechanism is suitable
for use in low levels of the operating system or during bootstrapping when
only a limited execution environment is generally available.
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)
Argument:
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-1, 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
.
Table 5-1: gentrap EXPT_CODE Values
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 |
5.1.8.3 Raising Unwind Exceptions
The mechanism used to raise an
unwind exception is described in detail in
Section 5.2.
5.1.8.4 Raising Signal Exceptions
Signal exceptions can be raised
asynchronously (such as, for notification of a terminal line hangup) or synchronously.
The exact circumstances that cause an asynchronous signal exception to be
raised vary widely from hardware exception notification to software notification,
as in the POSIX-defined
alarm()
routine.
Section 5.1.13
contains information on exception and signal handling coexistence.
5.1.9 Search for and Invocation of Exception Handlers
The search for and subsequent invocation of an exception handler begins with the program counter value that indicates the address at which the exception was raised. Generally, a program counter value is associated with a procedure descriptor. (Section 8.1 describes procedure descriptors.) The procedure descriptor provides information needed to identify the procedure containing the code and interpret those parts of the stack frame that are needed to traverse the procedure call chain. (Chapter 7 discusses procedure invocation and call chains.)
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.
The next sections discuss the order of invocation for exception handlers
as well as handler invocation and arguments.
5.1.9.1 Invocation Order for Exception Handlers
When an exception is raised, established exception handlers are invoked in a specific order.
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:
The procedure invocation within which the nested exception was raised.
Zero or more procedures invoked indirectly or directly by the most recently invoked (most current) handler.
The most current handler.
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.
The procedure invocation within which the active exception that immediately preceded the nested exception was raised, that is, the invocation in which the exception was raised for which the most current handler was invoked.
Zero or more procedure invocations. All handlers established by these procedures have been invoked for the exception that immediately preceded the nested exception and all have reraised that exception.
The establisher of the most current handler.
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).
Zero or more procedure invocations for which no established handlers have yet been invoked.
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.
Figure 5-3: Procedure Invocation Chain
The following list contains the time-ordered events that gave rise to this invocation chain:
A calls B calls C calls D.
An exception is raised in D.
The handlers established by D and by C are invoked in turn and they reraise the exception.
B.Handler, the handler established by B, is invoked.
B.Handler calls AA calls BB.
A nested exception is raised in BB.
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.
Let
current_invocation
be the procedure invocation
in which the exception was raised.
[loop]
: If
current_invocation
does not
establish a handler, go to step
[check-begin]
.
Invoke the handler established by
current_invocation
.
If the handler returns
ExceptionContinueExecution
or initiates an unwind, exit from these steps.
[check-begin]
: If
current_invocation
is the beginning of the procedure invocation chain, go to step
[last-chance]
.
If
current_invocation
is an active handler, let
current_invocation
be the invocation in which the exception was raised
that invoked this active handler, and go to step
[loop]
.
Let
current_invocation
be the procedure invocation
that invoked
current_invocation
.
Go to step
[loop]
.
[last-chance]
: Invoke the system last chance handler.
If, during the search for and invocation of frame-based handlers, the exception dispatcher detects that the thread's main stack is corrupt, the following actions occur:
The
EXCEPTION_STACK_INVALID
flag is set to 1.
The search for handlers immediately proceeds to the system last chance handler.
5.1.9.2 Handler Invocation and Arguments
Every exception handler is invoked as a function that returns a status value. The function call is defined as follows:
(*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. |
The control record pointed to by
DispatcherContext
provides
communication between the handler and the exception dispatcher
(the system routine that actually invokes the handler).
This record provides
information about the establisher.
The following list discusses three fields
in the record:
ControlPC
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.
collide_info
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.
FunctionEntry
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.
5.1.10 Modification of Exception Records and Context by Handlers
The exception records, exception qualifiers, invocation context blocks, and control records that are passed to an exception handler are always allocated in writable memory. Handlers can write to any location in these data structures. The exception records and exception qualifiers that are passed to a handler are copies of the original ones. Modifications to them are seen by other subsequently called handlers (within the limits described later) but do not affect the original data structures.
The effects of a handler-modifying passed exception information are as follows:
If the
EXCEPTION_NONCONTINUABLE
flag in the primary
exception record is changed from 0 to 1, the exception handler that made the
modification must not return
ExceptionContinueExecution
, nor can
any handler subsequently invoked for the exception return
ExceptionContinueExecution
.
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.
If any flags in
ExceptionFlags
in the primary exception
record are modified except as described in the previous item in this list,
there is no effect after the exception handler completes its operation.
All
handlers subsequently invoked for the exception receive a primary exception
record with the flags unmodified.
However, an exception handler must not change the
EXCEPTION_NONCONTINUABLE
flag from 1 to 0.
If the contents of the record specified by
ContextRecord
or
DispatcherContext
are modified by a handler, except for
ControlPC
and
collide_info
, the results are
unpredictable
and such a handler does not conform to this calling standard.
Except as specified in the previous items in this list, all changes made to the exception information will be visible to handlers subsequently invoked for the exception. Any other effects of modifying the exception information are not defined by this calling standard.
5.1.11 Handler Completion and Return Value
When an exception handler has finished all its processing, it performs one of the following actions:
Reraises the exception
Continues execution of the thread
Initiates procedure invocation unwinding
Section 5.2
contains a complete description
of the unwinding process.
This section discusses the other methods of handler
completion.
5.1.11.1 Completion by Reraising the Exception
If an exception handler determines that additional handlers should be
invoked for the exception because it could not completely handle the exception,
the handler can reraise the exception by returning
ExceptionContinueSearch
.
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.
5.1.11.2 Completion by Continuing Thread Execution
By returning
ExceptionContinueExecution
, an exception handler
can continue execution of the thread at the address specified by the continuation
PC in the
ContextRecord
, with the context of the interrupted procedure
restored.
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.
5.1.11.3 Completion During Unwinding
When an unwind is in progress, the status returned by handlers must
be
ExceptionContinueSearch
.
Otherwise,
EXC_STATUS_INVALID_DISPOSITION
is raised; that is, handlers cannot continue during an unwind operation.
5.1.11.4 Completion from Signal Exceptions
The permissibility and effects of continuing from a signal exception
are governed by the underlying signal, as specified by the implementation
of the POSIX environment.
5.1.12 Other Considerations in Handling Exceptions
This section details certain aspects of the Alpha architecture that have significant implications for exception handling. These aspects are:
Exception synchronization
Continuation from exceptions
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.
5.1.12.1 Exception Synchronization
The Alpha hardware architecture
allows instructions to be completed in a different order than that in which
they were issued.
The architecture also allows for exceptions caused by an
instruction to be raised after subsequently issued instructions have been
completed.
Thus, when a hardware exception occurs, the state of the machine
cannot be assumed with precision unless it has been guaranteed by bounding
the exception range with the appropriate insertion of
trapb
instructions.
The rules for bounding the exception range are as follows:
If a procedure has an exception handler that does not simply
reraise all arithmetic traps caused by code not contained directly within
that procedure, it must issue a
trapb
instruction
before it establishes itself as the
current procedure.
Rationale: This rule is required because a standard procedure is not allowed to handle traps that it might not have caused.
If a procedure has an exception handler that does not simply
reraise all arithmetic traps caused by code contained directly within that
procedure or by any procedure that might have been called while that procedure
was
current, it must issue a
trapb
instruction
in the procedure epilogue while it is still the
current procedure.
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.
If a procedure has an exception handler that is sensitive
to the invocation depth, the procedure must issue a
trapb
instruction immediately
before and after any call.
In addition, the handler must be able to recognize
exception PC values that represent
trapb
instructions immediately
after a call and adjust the depth appropriately.
These rules ensure that exceptions are detected in the context within which exception handlers have been set up to handle them.
However, these rules do not ensure that all exceptions are detected
while the procedure within which the exception-causing instruction was issued
is current.
For example, if a procedure without an exception handler is called
by a procedure that has an exception handler that is not sensitive to invocation
depth, an exception detected while that called procedure is current might
have been caused by an instruction issued while the caller was the current
procedure.
Therefore, the frame, designated by the exception handling information,
is the frame that was current when the exception was detected, not necessarily
the frame that was current when the exception-causing instruction was issued.
5.1.12.2 Continuation from Exceptions
The Alpha architecture does not guarantee that instructions are completed in the same order in which they were fetched from memory or that instruction execution is strictly sequential. Continuation after some exceptions is possible, but there are restrictions.
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.
5.1.13 Exception and Signal Handling Coexistence
The procedure-based exception handling facility defined by this calling standard coexists with a global POSIX-style signal facility. The following list describes the features and limitations of such a coexistence.
The system provides a special signal handler that serves as an interface between a signal facility and a frame-based exception handling facility. The handler is defined as follows:
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.
When an application that utilizes procedure-based exception
handling is initialized in an environment where signals are also supported,
the language subsystem or user should install the special signal handler
exc_raise_signal_exception()
for those signals that normally
result directly from the executing code stream.
Those signals include
SIGFPE
,
SIGSEGV
,
SIGBUS
,
SIGILL
,
SIGEMT
,
SIGABRT
,
SIGSYS
, and
SIGTRAP
.
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.
When
exc_raise_signal_exception()
is invoked as a signal handler,
the delivered signal may be blocked, along with perhaps others that were specified
when it was installed as the handler.
Subsequent occurrences of blocked signals
are held pending until they are unblocked.
Unblocking is a natural result
of a procedure-based exception handler returning
ExceptionContinueExecution
, or invoking an unwind operation or
siglongjmp()
.
Signals can be explicitly unblocked during
handler execution if the handler invokes
sigsetmask()
or
sigprocmask()
.
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.
Application developers should exercise care if their programs
directly utilize signal facility support code in any way not otherwise described
by this section for any signal having
exc_raise_signal_exception()
installed as its handler.
For such applications, the signal facility mechanisms will continue to operate
correctly but there might be
undefined
effects on frame-based
exception handling of those signals.
Some routines to avoid include:
sigaction()
,
sigblock()
,
signal()
,
sigpause()
,
sigsetmask()
,
sigprocmask()
,
and
sigvec()
.
The exception handling capabilities defined in this calling standard are compatible with the use of a separate signal stack. The selection, change, or other use of a separate signal stack is outside the scope of this calling standard.
Environment restrictions regarding which functions are signal reentrant and which can safely be called from signal catching functions also apply to exception handlers.
The unwinding capabilities specified in this section support the following:
Correct and predictable nonlocal
GOTO
s in a multilanguage
environment
Construction of modular, maintainable multilanguage applications
This section discusses the following topics:
Overview of unwinding
Types of unwind operations
Types of unwind invocation
Unwind initiation
Multiply active unwind operations
Unwind completion
Unwind coexistence with
setjmp()
and
longjmp()
The term unwinding, or unwind operation, refers to the action of returning from a procedure or a chain of procedures by some mechanism other than the normal return path. Performing an unwind operation in a thread causes a transfer of control from the location at which the unwind operation is initiated to a target location in a target invocation. This transfer of control results in the termination of all procedure invocations, including the invocation in which the unwind request was initiated, up to the target procedure invocation. Thread execution then continues at the target location.
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.
5.2.2 Types of Unwind Operations
There are two types of unwind requests: general and exit.
The following
sections describe each type.
5.2.2.1 General Unwind
A general unwind transfers control to a specified location in a specified procedure invocation. The target procedure invocation is specified by a frame pointer. (See Section 7.1 for information on procedure invocation.) The target location is specified with an absolute PC value.
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.
5.2.2.2 Exit Unwind
It is valuable for a thread that is terminating execution to be able to clean up its use of shared resources. In a single-threaded process, there might be global resources, such as files, locks, or shared memory, that are shared among processes. For multithreaded processes, global resources as well as process-wide resources like a heap might need to be restored to a known state.
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:
Invokes all established frame-based handlers, passing them an exception record that specifies that an exit unwind is in progress.
Terminates all procedure invocations up to the beginning of the call chain.
Terminates the execution of the thread.
Threads that use any mechanism for termination other than the normal
return process are not considered to be standard and their behavior is
undefined.
5.2.3 Types of Unwind Invocations
There are two types of unwind
invocations: those initiated while an exception is active and those initiated
while no exception is active.
This section describes each type.
5.2.3.1 Unwind Operations with No Active Exception
An unwind that is initiated when no exception is active is usually done
to perform a nonlocal
GOTO
; that is, to transfer control directly
to some code location that is not part of the currently executing procedure
or is not statically known.
Even this type of unwind operation must provide
a mechanism to allow clean-up operations (including restoring a consistent
set of register values) of terminated invocations to be performed.
The unwind
mechanism supports such clean-up operations.
5.2.3.2 Unwind Operations During an Active Exception
By initiating an unwind operation, the handler, or any descendant procedure called directly or indirectly by the handler, can continue execution of the thread at a location different from the one where the exception was raised.
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.
5.2.4 Unwind Initiation
A thread can initiate a general unwind operation by calling one of two system library routines. The routines differ only in the way their first argument specifies the target frame: as a virtual frame pointer or a real frame pointer. (Section 7.1 discusses ways to refer to procedure invocations.) These routines are defined as follows:
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. |
Function Value:
None
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.
5.2.5 Multiply Active Unwind Operations
Sometimes, an unwind operation is initiated while another unwind is already active. Such a situation could occur if a handler that is invoked during the original unwind initiates another unwind, or if an exception is raised in the context of such a handler and a handler invoked for that exception, in turn, initiates another unwind operation.
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.
5.2.5.1 Nested Unwind
A nested unwind operation is an unwind that is initiated while a previous unwind is active. For a nested unwind, the target invocation in the procedure invocation chain is not a predecessor of the most current active unwind handler. That is, a nested unwind does not terminate any procedure invocation that would have been terminated by the previously active unwind.
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.
5.2.5.2 Colliding Unwind
Like the nested unwind, a colliding unwind is an unwind that is initiated while a previous unwind is active. For a colliding unwind, the target invocation in the procedure invocation chain is a predecessor of the most current active unwind handler. That is, a colliding unwind terminates one or more procedure invocations that would have been terminated by the previously active unwind.
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.
5.2.6 Unwind Completion
When an unwind is completed, the following conditions are in effect:
The target procedure invocation is the most current invocation in the procedure invocation chain.
The environment of the target invocation is restored to the state that existed when that invocation was last current, except for the contents of scratch registers.
The GP register contains a pointer to the GOT that is appropriate to the target procedure.
The $0 register contains the return value that was passed by the routine which invoked the unwind.
Execution continues at the target location.
5.2.7 Unwinding Coexistence with setjmp and longjmp
The procedure invocation
unwinding facility defined by this calling standard can coexist and interoperate
with the
setjmp()
and
longjmp()
facilities.
It is sufficient
for the
jmp_buf
array to consist of the frame pointer and program
counter values that are needed as arguments to
exc_unwind()
or
exc_unwind_rfp()
.
A null pointer can be provided for the
ExceptionRecord
argument and the value of the
longjmp()
expression can be provided for the
ReturnValue
argument.
Any environment that conforms to this calling standard must implement
nonlocal
GOTO
s 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.
Note
The
longjmp()
routine for Tru64 UNIX does not use an unwind operation. Therefore, in the presence of frame-based exception handling, it is preferable to useexc_longjmp()
, implemented through an unwind operation.