This chapter discusses specifications related to events that are outside the normal program flow. The topics covered are:
Exception handling (Section 5.1)
Unwinding (Section 5.2)
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 (Section 5.1.1)
An overview of exception handling (Section 5.1.2)
The kinds of exceptions (Section 5.1.3)
Status values and exception codes (Section 5.1.4)
Exception records (Section 5.1.5)
Frame-based exception handlers (Section 5.1.6)
How handlers become established (Section 5.1.7)
How exceptions are raised (Section 5.1.8)
The search for and invocation of exception handlers (Section 5.1.9)
Modification of exception records and context by handlers (Section 5.1.10)
Handler completion and return value (Section 5.1.11)
Exception synchronization and continuation from exceptions (Section 5.1.12)
Coexistence of exception and signal handling (Section 5.1.13)
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.6
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 (Section 5.2.1)
Types of unwind operations (Section 5.2.2)
Types of unwind invocation (Section 5.2.3)
Unwind initiation (Section 5.2.4)
Unwinding from inserted code ranges (Section 5.2.5)
Multiply active unwind operations (Section 5.2.6)
Unwind completion (Section 5.2.7)
Unwind coexistence with
setjmp()
and
longjmp()
(Section 5.2.8)
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 not defined 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 Unwinding from an Inserted Code Range
An inserted code range is any number of contiguous instructions that are added to a procedure. These instructions were not part of the original procedure as it was written. The compiler can create inserted code ranges by inlining, and a post-link tool can create inserted code ranges through object modification. One or more code range descriptors (CRD) are used to describe these inserted code ranges.
The inserted code ranges are also described by a run-time procedure
descriptor (RPD).
These code ranges can have stack, local variables, and exception
handlers associated with them.
Additional information is provided in the RPD
of the inserted code range in the form of a
return_address
offset to support unwinding the frame from within these code ranges.
Unwinding from code whose RPD has a nonzero
return_address
field is the same as unwinding from code whose RPD has a zero
return_address
field, with one exception: after all other actions
are performed, the new current PC is set to the address calculated from the
return_address
field in the RPD.
See
Section 8.1
for more information on code range
descriptors and run-time procedure descriptors.
5.2.6 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.6.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.6.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.7 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.8 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.