8 Procedure Descriptors
Procedure descriptors serve two functions. They provide:
-
The means for mapping from an arbitrary program counter value
to the descriptive information associated with the code at that address.
-
Information about a procedure, such as which registers are
saved, where they are saved, and the length of the prologue. This information
is needed for call-chain navigation in general and exception handling in particular.
Every procedure, except for null frame procedures, must have an associated
procedure descriptor. (Null frame procedures are discussed in Section 3.1.4.)
Note
The term procedure descriptor
also appears in the chapter on the symbol
table in the Assembly Language Programmer's Guide. The use of the term in that manual refers to a structure defined
in the file /usr/include/sym.h and should not be confused with
procedure descriptors used for exceptions, as described in this Digital UNIX
calling standard.
This chapter covers the following procedure-descriptor topics:
8.1 Procedure Descriptor Representation
Procedure descriptors
on Digital UNIX for Alpha systems use two kinds of structures: code range
descriptors and run-time procedure descriptors.
Code range descriptors
associate a contiguous
sequence of addresses with a run-time procedure descriptor. This mapping
can be many-to-one.
Run-time procedure descriptors
provide descriptive information
about a procedure or part of a procedure.
Several run-time procedure descriptors might be needed if the procedure
has multiple entry points, as in FORTRAN. In this case, each entry point consists
of the following elements:
Although the prologues might contain different code, they must all achieve
the same effect: the same registers saved at the same offset, the same frame
size, and so on. There is a run-time procedure descriptor for every entry
point, with the last entry point's run-time procedure descriptor covering
all the common code of the procedure.
The creation of procedure descriptors involves
the combined actions
of compilers and assemblers, the linker, and the loader. The sections that
follow focus solely on the result of this composite process and describe the
resulting descriptors as seen by the executing run-time environment. The
figures present the physical representation of the procedure descriptor structures.
The logical fields, which have the prefix PDSC_, present the abstract
means to access the fields of these structures. The physical representation
and the logical fields are defined in the file /usr/include/pdsc.h.
8.1.1 Code Range Descriptors
The code-range table
is an array of code-range descriptors, as shown
in Figure 8-1. There are four fields for
each descriptor:
- PDSC_CRD_BEGIN_ADDRESS
-
Specifies the address of the beginning of a code range. The
elements of the array are sorted so that the portion of the address space
covered by a single element starts at the PDSC_CRD_BEGIN_ADDRESS
value contained in that element and extends to, but does not include, the PDSC_CRD_BEGIN_ADDRESS value encoded in the next successive element.
Thus, the last array element provides the end address of the last code range
and does not start a new range.
- PDSC_CRD_PRPD
-
Specifies the address of the associated run-time procedure
descriptor. A PDSC_CRD_PRPD value of zero indicates a null frame
procedure for which an implicit run-time procedure descriptor is assumed with
the characteristics described in Section 3.1.4.
- PDSC_CRD_CONTAINS_PROLOG
-
Indicates whether the code range begins with a procedure prologue.
- PDSC_CRD_MEMORY_SPECULATION
-
Indicates that memory traps (SIGSEGV, SIGBUS)
raised in this procedure should not be delivered.
Figure 8-1: Code Range Descriptor
-
begin_address
Contains a longword that is the offset from the base of the code range
table to the starting point of the code to which the associated run-time procedure
descriptor applies. The low two bits of this longword are reserved for use
as flags; they must be masked out before the containing longword is used as
an offset.
-
rpd_offset
Contains a longword that is the self-relative offset to the associated
run-time procedure descriptor. The low two bits of this longword are used
as flags; they must be masked out before the containing longword is used as
an offset. An rpd_offset value of zero indicates a null frame procedure
for which an implicit run-time procedure descriptor is assumed with the characteristics
defined in Section 3.1.4.
-
memory_speculation (bit 1 of rpd_offset)
Indicates that memory traps (SIGSEGV, SIGBUS)
occurring in this procedure should not be delivered.
-
n (bit 0 of rpd_offset)
Specifies a flag set to indicate that the code range does not begin
with an entry point. This argument might be used for an out-of-line code
segment that is not contiguous with the main body of a procedure.
8.1.2 Run-Time Procedure Descriptors
Run-time procedure descriptors
provide information about a procedure needed for exception handling and other
tools. Table 3-1 lists this information. There
are two forms: long and short. Both forms encode the same information. The
short form is used to save space for the most commonly occurring cases. Figure 8-2 shows the long form; Figure 8-3
shows the short form.
Each figure shows two alternative representations for the first longword.
The first representation applies to stack frame procedures and is shown in
the main part of the figure. The second representation applies to register
frame procedures and is shown as a separate longword at the end of the figure.
The PDSC_FLAGS_REGISTER_FRAME flag, which is one of the flags common
to both forms, determines which representation applies.
Descriptions of the physical fields follow the figures. These descriptions
include the calculations used to obtain the logical field from the physical
field. Most fields are common to both procedure descriptor forms. The long
form has three additional fields shown in Figure 8-2.
Figure 8-2: Long Form Run-Time Procedure Descriptor
Figure 8-3: Short Form Run-Time Procedure Descriptor
-
The flags field represents PDSC_RPD_FLAGS and encodes the following flags:
-
PDSC_FLAGS_SHORT (bit 0) is 1 if and only if this
structure is a short form run-time procedure descriptor.
-
PDSC_FLAGS_REGISTER_FRAME (bit 1) is 1 only for
a register frame procedure.
-
PDSC_FLAGS_BASE_REG_IS_FP (bit 2) is 1 if and only
if $15 is used as the frame pointer.
-
PDSC_FLAGS_HANDLER_VALID (bit 3) is 1 if and only
if an exception handler's address and handler data are specified using the PDSC_RPD_HANDLER and PDSC_RPD_HANDLER_DATA fields.
-
PDSC_FLAGS_EXCEPTION_MODE (bits 4, 5, and 7) encodes
the caller's desired exception reporting behavior when calling certain mathematically
oriented library routines. The 3-bit integer value is formed with bit 7 as
the MSB and bit 4 as the LSB. The possible values for this field and the
corresponding meanings are as follows:
-
PDSC_EXC_SILENT(0)
Raise no exceptions and create only finite values (no infinities, denormals,
or NaNs). In this mode, the function result or the C language errno must be examined for any error indication. This mode is the default.
-
PDSC_EXC_SIGNAL(1)
Raise exceptions for all error conditions except for underflow, which
yields a zero result.
-
PDSC_EXC_SIGNAL_ALL(2)
Raise exceptions for all error conditions (including underflows).
-
PDSC_EXC_IEEE(3)
Raise no exceptions (except as controlled by separate IEEE exception
enable bits), and create infinite, denormal, and NaN values according to the
IEEE floating-point standard.
-
PDSC_EXC_CALLER(4)
Perform the exception mode behavior specified by this procedure's caller.
-
PDSC_FLAGS_EXCEPTION_FRAME (bit 6) is 1 for a frame
that includes a hardware exception context.
-
PDSC_FLAGS_ARITHMETIC_SPECULATION (bit 8) is 1
if arithmetic traps (SIGFPE) ocurring in this procedure should
not be delivered.
-
PDSC_FLAGS_EXTENDER (bit 10) is reserved for future
indication of an extended form of run-time procedure descriptor.
-
The entry_ra field represents PDSC_RPD_ENTRY_RA and is the number of the register in which the return address is passed
to this procedure.
-
The rsa_offset field is the signed difference in
quadwords between the stack frame base (SP or FP as indicated by PDSC_FLAGS_BASE_REG_IS_FP) and the register save area. (See Section 3.1.2
for information on stack frame procedures.)
PDSC_RPD_RSA_OFFSET = rsa_offset * 8
-
The sp_set field is the unsigned offset in instructions
(longwords) from the entry address of the procedure to the single instruction
in the procedure prologue that modifies the stack pointer. This offset must
be zero when there is no such instruction because the procedure has a PDSC_RPD_FRAME_SIZE of 0.
PDSC_RPD_SP_SET = sp_set * 4
-
The entry_length field is the unsigned offset in
instructions (longwords) from the entry address to the first instruction in
the procedure code segment following the procedure prologue.
PDSC_RPD_ENTRY_LENGTH = entry_length * 4
-
The frame_size field is the unsigned size in quadwords
of the fixed portion of the stack frame for this procedure.
PDSC_RPD_FRAME_SIZE = frame_size * 8
The value of SP at entry to this procedure can be calculated by adding PDSC_RPD_FRAME_SIZE to the value SP or FP, as indicated by PDSC_FLAGS_BASE_REG_IS_FP. PDSC_RPD_FRAME_SIZE cannot be 0 for a stack frame procedure
because the stack frame must include space for the register save area.
Note: If a procedure needs a frame size that is too large to be represented
using the frame_size field, a variable-size stack frame should
be used. In this case, the FP register is used to address a fixed size area
that needs to be just large enough to include the preserved state. An arbitrarily
large stack area can then be covered by the SP register.
-
The imask field represents PDSC_RPD_IMASK and is a bit vector (0 - 31) specifying the integer registers
that are saved in the variable portion of the register save area on entry
to the procedure. The least significant bit corresponds to register $0. Bits
31, 30, 28, and the register containing the entry return address of this mask
should never be set because $31 is the integer Read-As-Zero register, $30
is the hardware SP, $29 (GP) is always assumed to be destroyed during a procedure
call or return, and the return address is saved at known offset zero in the
register save area in every stack frame procedure.
-
The fmask field represents PDSC_RPD_FMASK and is a bit vector (0 - 31) specifying the floating-point registers
that are saved in the variable portion of the register save area on entry
to the procedure. The least significant bit corresponds to register $f0. Bit
31 of this mask should never be set because it corresponds to the floating-point
Read-As-Zero register.
-
The handler_address field represents PDSC_RPD_HANDLER and is an absolute procedure value (quadword) for a run-time static
exception handling procedure. This part of the procedure descriptor is optional.
However, it must be supplied if PDSC_FLAGS_HANDLER_VALID is 1.
If PDSC_FLAGS_HANDLER_VALID is 0, the contents or existence of PDSC_RPD_HANDLER is unpredictable.
-
The handler_data_address field represents PDSC_RPD_HANDLER_DATA and is a quadword of data for the exception handler.
This part of the procedure descriptor is optional. However, it must be supplied
if PDSC_FLAGS_HANDLER_VALID is 1. If PDSC_FLAGS_HANDLER_VALID is 0, the contents or existence of PDSC_RPD_HANDLER_DATA
is unpredictable.
-
The save_ra field represents PDSC_PRD_SAVE_RA and is the number of the register in which the return address is maintained
during the body of the procedure. If this procedure uses the standard calling
conventions and does not modify $26, both PDSC_RPD_ENTRY_RA and PDSC_RPD_SAVE_RA will specify $26.
The short form run-time procedure descriptor differs from the long form
in the following ways:
-
An entry_ra of $26 is assumed for the return address
register in a stack frame procedure ( PDSC_FLAGS_REGISTER_FRAME
is 0).
-
The rsa_offset field is limited to at most 255
quadwords (2040 bytes).
-
The fmask field represents only registers $f2 through
$f9.
-
The imask field represents only registers $8 through
$15. (Note that $8 is not normally a preserved register.)
-
The frame_size field is limited to at most 65,535
quadwords (524,280 bytes).
-
The sp_set offset is limited to at most 255 instructions
(1020 bytes).
-
The entry_length offset is limited to at most 255
instructions (1020 bytes).
If any of these restrictions cannot be satisfied, the long
form run-time procedure descriptor must be used.
8.2 Procedure Descriptor Access Routines
A thread can obtain
information from the descriptor of any procedure in the thread's virtual address
space by calling system library functions.
In the course of running and debugging a program, there are times when
it is necessary to identify which procedure is currently executing. During
normal thread execution, the current procedure
must be determinable any time an exception
arises so that the proper handlers will be invoked. In addition, a debugger
must know which procedure invocation is currently executing so it can obtain
information about the current state of the execution environment.
To determine precisely the current execution context, two pieces of
information are required:
This context of the current procedure and the specific instance of that
procedure invocation are referred to as the current procedure invocation or
simply, current procedure.
At any point in the execution of a thread,
only one procedure is considered to be the current procedure.
In this calling standard, the value in the PC is used to indicate the
current procedure by means of the code range table described in Section 8.1.1.
The following system-supplied routine is used to obtain the address
of the procedure descriptor that corresponds with any given PC value within
the current address space.
exc_lookup_function_entry (ControlPC)
ControlPC |
Specifies a PC value in the current address
space for which the procedure value is to be returned. |
Function Value:
PROC_DESC |
Specifies the address of the code range descriptor
for the procedure containing the requested PC. If the return value is null,
the PC is not currently mapped. |
The following system-supplied routine is used to obtain the address
of the base of the code range array for the procedure descriptor that corresponds
B to any given PC value within the current address space.
exc_lookup_function_table (ControlPC)
ControlPC |
Specifies a PC value in the current address
space for which the code range base address is to be returned. |
Function Value:
PROC_CRD |
Specifies the address of the base of the
code range descriptor array for the procedure descriptor of the procedure
containing the requested PC. If the return value is null, the PC is not currently
mapped. |
At times, it is useful to acquire the GOT segment value for a procedure;
that is, the value of the GP register. The following system-supplied routine
is used to obtain the GP value corresponding to any given PC value within
the current address space.
exc_lookup_gp (ControlPC)
ControlPC |
Specifies a PC value in the current address
space for which the GP value is to be returned. |
Function Value:
GP_VALUE |
Specifies a GP value for the procedure containing
the requested PC. If the return value is null, the PC is not currently mapped. |
8.3 Run-Time Generated Code
Code generated at run time is important
for applications that include:
-
Interactive languages
-
Software bit block transfers (for efficient support of graphic
displays that do not provide hardware bit block transfers)
-
String pattern matching
-
Sorting
-
Interpretive execution and instruction stream modification
by programming and debugging tools
-
Construction of bound procedure variables that have a representation
consistent with that of simple procedure values
To maintain stack traceability when code generated at run time is executed,
procedure descriptors must be provided for that code. Such procedure descriptors
must describe correctly the characteristics of the code and the environment
within which that code executes.
Before run-time generated code that uses any exception facilities (directly
or indirectly) can be executed, system library functions must be called to
communicate the code ranges, procedure descriptors, and GP values to the execution
environment. This communication is accomplished by calling the following
two system-supplied routines:
exc_add_pc_range_table (PROC_DESC_ADDR, LENGTH)
PROC_DESC_ADDR |
Specifies the base address of the code range
array for the procedure descriptors. |
LENGTH |
Specifies the number of code range elements
in the array. |
An exception is raised if the exc_add_pc_range() operation
cannot be completed successfully.
exc_add_gp_range (BEGIN_ADDRESS, LENGTH, GP_VALUE)
Arguments:
BEGIN_ADDRESS |
Specifies the first address for which GP_VALUE applies. |
LENGTH |
Specifies the number of bytes from BEGIN_ADDRESS for which GP_VALUE applies. |
GP_VALUE |
Specifies the GP value to be associated with
the addresses in the range BEGIN_ADDRESS .. BEGIN_ADDRESS + LENGTH +
1. |
An exception is raised if the exc_add_gp_range() operation
cannot be completed successfully.
When procedure information is no longer valid or if the code will not
be executed again, two system library routines should be called to remove
the procedure mapping information. These system library routines are defined
as follows:
exc_remove_pc_range_table (PROC_DESC_ADDR)
PROC_DESC_ADDR |
Specifies the base address of the code range
array for the procedure descriptors. |
An exception is raised if the exc_remove_pc_range_table()
operation cannot be completed successfully.
exc_remove_gp_range (BEGIN_ADDRESS)
BEGIN_ADDRESS |
Specifies the beginning address for which
GP-value information should be removed. |
An exception is raised if the exc_remove_gp_range() operation
cannot be completed successfully.
The following steps show how run-time code
should be constructed
and released:
-
Allocate memory for the code.
-
Write the code and any procedure descriptors to memory.
-
Call exc_add_pc_range_table()
and exc_add_gp_range().
-
Invoke an imb (instruction memory barrier) operation
as required by the Alpha architecture.
-
Execute the code.
-
Call exc_remove_pc_range_table()
and exc_remove_gp_range().
-
Deallocate the memory containing the code.