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 Tru64 UNIX calling standard.
This chapter covers the following procedure-descriptor topics:
Representation
Access routines
Use with run-time generated code
8.1 Procedure Descriptor Representation
Procedure descriptors on Tru64 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:
Prologue
Entry-point specific code
Branch to a common join point following the last entry point
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.
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:
The procedure that is currently executing
Which instance of that procedure is currently executing
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. |
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. |
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. |
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. |
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)
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.
Invoke an
imb
(instruction memory barrier)
operation as required by the Alpha architecture.
Execute the code.
Deallocate the memory containing the code.