Procedure descriptors serve the following functions:
They provide the means for mapping from an arbitrary program counter value to the descriptive information associated with the code at that address.
They provide 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.
They provide a means for link-time, post-link-time, and execution-time tools to identify all of the instructions that constitute a complete procedure.
They provide a means for link-time, post-link-time, and execution-time tools to distinguish code from data.
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 manual Symbol Table/Object File Specification. 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 manual.
This chapter covers the following procedure-descriptor topics:
Representation (Section 8.1)
Access routines (Section 8.2)
Use with run-time generated code (Section 8.3)
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.
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.
Macros, which have the prefix
PDSC_
, can be used to access
the fields of these structures.
The physical representation and the macros
are defined in the file
/usr/include/pdsc.h
.
A single run-time procedure descriptor should be used to describe a complete procedure, even if it has multiple entry points, as in FORTRAN. In this case, each entry point typically 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.
Two instructions are said to be
part of the same procedure
if they are described by the same run-time procedure descriptor
(RPD).
However, if code has been inserted into a procedure (for example, through
inlining or instrumentation), this code may have a separate RPD associated
with it.
In this situation, the
return_address
field of
the procedure descriptor will have a nonzero value.
Before determining whether
an instruction within an inserted code range is in the same procedure as another
instruction, the RPD chain must be followed until an RPD with a
return_address
field equal to zero is found.
Then, the comparison
can be done with the top-level RPD.
See
Section 5.2.5
for more information.
Notes on binary compatibility:
Earlier versions of this Calling Standard used a separate procedure descriptor for each entry point of a procedure. This method is now obsolete. However, tools must be able to handle applications compiled under the obsolete model as well as the current model.
Exception handling will operate correctly on both obsolete and current models of RPDs.
The property that two instructions are part of the same procedure only if they have the same procedure descriptor cannot be used on applications compiled under the obsolete model.
Using a single RPD to describe a complete procedure was implemented in Tru64 UNIX Version 5.1. Earlier versions used the obsolete model.
The code-range table is an array of code-range descriptors, as shown in Figure 8-1.
The following macros are used to access data in code range descriptors:
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.
PDSC_CRD_TYPE_STANDARD
PDSC_CRD_TYPE_CONTEXT
PDSC_CRD_TYPE_NON_CONTEXT
PDSC_CRD_TYPE_NON_CONTEXT_STACK
PDSC_CRD_TYPE_DATA
Indicate the procedure context of the code described by the code range descriptor.
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.
s
,
t
,
n
(bits 1,0 of
begin_address
, bit 0 of
rpd_offset
)
These three bits are used together to identify the context of the code
that the code range descriptor is describing, listed below.
If the
rpd_offset
is 0, then the values of
s
,
t
, and
n
are zero as well.
In the following descriptions for the types of code range descriptor,
the occurrence of
CONTEXT
in the name implies that the
associated procedure is current for exception handling purposes during that
part of the procedure specified by the CRD; similarly,
NON_CONTEXT
implies the procedure is not current.
STACK
implies that stack storage has been allocated.
A very common special case
in indicated by the occurence of
STANDARD
in the name because
it corresponds to a standard sequence of up to three states.
DATA
implies storage that is not executable code at all.
CRD_TYPE_STANDARD (s=0, t=0, n=0)
This is the standard range descriptor.
It consists of the prologue up
to, and including, the SP-setting instruction (non_context), the prologue
after the SP-setting instruction (non_context_stack), and the primary (context)
code range.
The fields
sp_set
and
entry_length
are relative to this segment.
There can only be one
CRD_TYPE_STANDARD
code range for each procedure.
CRD_TYPE_CONTEXT (s=0, t=0, n=1)
This is a full routine context descriptor, but it does not contain the prologue.
CRD_TYPE_DATA (s=0, t=1, n=0)
This code range describes data that resides within the text area.
CRD_TYPE_NON_CONTEXT (s=0, t=1, n=1)
This code range is not in a routine context, and it does not contain stack allocation that needs to be deallocated.
CRD_TYPE_NON_CONTEXT_STACK (s=1, t=0, n=1)
This is a non-context region for exception handling purposes, but the stack has been allocated.
The encodings (s=1, t=0, n=0), (s=1, t=1, n=0) and (s=1, t=1, n=1) are
reserved.
One of these encodings should be used for the future extension of
the CRD.
Note on binary compatibility:
In earlier versions of this Calling Standard, bits 0 and 1
of
begin_address
were undefined, and bit 0 of
rpd_offset
was set if no prologue was associated with the code range.
The encodings of
s
,
t
, and
n
maintain the binary compatibility of the original specification.
Be aware that the macro
PDSC_CRD_CONTAINS_PROLOG
will return
TRUE if bit 0 of
rpd_offset
is clear.
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. Inserted code must use the long form of the RPD. 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 and two additional flags as shown in
Figure 8-2.
The additional fields are
entry_ra
,
return_address
, and a field reserved
for future use.
The additional flags are
PSDC_FLAGS_ARITHMETIC_SPECULATION
and
PDSC_FLAGS_EXTENDER
.
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 is the number of the
register in which the return address is passed to this procedure.
The
entry_ra
field can be accessed with
PDSC_RPD_ENTRY_RA
.
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 lower two bits of the
return_address
,
r, are reserved for future use.
These lower two bits must be
masked off the containing longword before the offset is used.
The
return_address
contains a longword that
is the offset from the base of the code range table to the point in the code
where the flow of control should return when the code range described by this
RPD is complete.
The low two bits of this longword are reserved and must be
masked out before the containing longword is used as an offset.
If this field
is not equal to 0, then the code segment that this RPD describes is an inserted
code range.
See
Section 5.2.5
for more information.
PDSC_RPD_RETURN_ADDRESS_FIELD = return_address & (~3) PDSC_RPD_RETURN_ADDRESS = code_range_table_base_address + PDSC_RPD_RETURN_ADDRESS_FIELD
The
imask
field 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
imask
field can be accessed with
PDSC_RPD_IMASK
.
The
fmask
field 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
fmask
field can be accessed with
PDSC_RPD_FMASK
.
The
handler_address
field is an absolute
procedure value (quadword) for a run-time static exception handling procedure.
The
handler_address
field can be accessed with
PDSC_RPD_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
is
unpredictable.
The
handler_data_address
field is a quadword
of data for the exception handler.
The
handler_data_address
field can be accessed with
PDSC_RPD_HANDLER_DATA
.
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 is the number of the register
in which the return address is maintained during the body of the procedure.
The
save_ra
field can be accessed with
PDSC_PRD_SAVE_RA
.
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).
A
return_address
value of 0 is assumed
because the
return_address
field does not exist in the
short form.
This includes the low-order reserved bits.
If any of these restrictions cannot be satisfied, the long
form run-time procedure descriptor must be used.
8.1.3 Examples
This section contains two examples of how to use code range descriptors
and procedure descriptors.
The first example involves multiple entry points
and the second example involves instrumented code.
8.1.3.1 Multiple Entry Point Example
Consider the following routine, written in a hypothetical C-like language that allows multiple entry points.
double sd; double ent1(int i, double d) { LX: if (i || ((int)d) < 5) { return foo() * 2.0 + PI; } return 0.0; /* Alternate entry point */ double ent2(int i, double d): double d2; if (!i) goto LX; if ( (d2 = foo2()) != 0.0) { return tailcallee(d2*d2+sd); } return sd * 10; }
A sample version of code is annotated below. This case is very contrived to show many types of descriptors.
CRD# Types Notes .globl ent1 .ent ent1 ent1: ldgp $gp, 0($27) 0 non-context (1) bne $16, lab1 0 lda $sp, -32($sp) 0 cvttq $f17, $f16 1 non-context-stack stt $f16, ($sp) 1 ldq $0, ($sp) 1 cmplt $0, 5, $1 1 bne $1, lab2 1 lda $sp, 32($sp) 1 lab0: fclr $f0 2 non-context (2) ret $26 2 lab1: lda $sp, -32($sp) 3 standard (5) lab2: stq $26, ($sp) 3 (6) .prologue 1 .frame $sp,32,$26 ...call to foo... 3 ldq $f1, 2.0 3 ldq $26, ($sp) 3 lda $sp, 32($sp) 3 mult $f0, $f1, $f0 4 non-context (2) addt $f0, $f16, $f0 4 ret $26 4 .globl ent2 .aent ent2 ent2: bne $16, lab0 5 non-context (1) lab3: ldgp $gp, 0($27) 5 (4) lda $sp, -32($sp) 5 stq $26, ($sp) 6 non-context-stack .prologue 1 .frame $sp,32,$26 ...call to foo2... 7 context ldq $f16, sd 7 fbeq $f0, lab5 7 mult $f0, $f0, $f0 7 ldq $26, ($sp) 7 lda $sp, 32($sp) 7 addt $f0, $f16, $f16 8 non_context (2) br tailcallee 8 (3) lab5: ldq $f0, 10.0 9 context lda $sp, 32($sp) 9 mult $f0, $f16, $f0 10 non_context (2) ret $26
Notes:
The code from
ent1
through to
lab1
is a
shrinkwrap, where some code is
moved outside of full procedure context, as is the
bne
instruction following
ent2
.
In this very contrived example,
the savings are minimal for the first case (only the return register save
is avoided).
This code is a floating exit sequence where the stack deallocation was scheduled away from the corresponding return. See Section 3.2.6.2.1 for more information.
This is a tailcall, where control does not transfer back to the caller.
In this hypothetical case, the compiler chose to prefer the
bne
over being able to skip the gp prologue for the entry.
If the routine has a stack frame, it is an error for a primary
prologue to not contain a stack adjustment because the adjustment is required
in order to get a correct
sp_set
value.
The following tables show the code range descriptors and the run-time
procedure descriptors associated with the preceding example.
Code range descriptors:
crd | begin_address | crd_type | rpd_offset |
CRD0 | ent1 | non_context | PD0 |
CRD1 | ent1+12 | non_context_stack | PD0 |
CRD2 | lab0 | non_context | PD0 |
CRD3 | lab1 | standard | PD0 |
CRD4 | lab2+20 | non_context | PD0 |
CRD5 | ent2 | non_context | PD0 |
CRD6 | lab3+8 | non_context_stack | PD0 |
CRD7 | lab3+12 | context | PD0 |
CRD8 | lab3+36 | non_context | PD0 |
CRD9 | lab5 | context | PD0 |
CRD10 | lab5+8 | non_context | PD0 |
Run-time procedure descriptors:
rpd | sp_set | entry_length | frame_size | return_address |
PD0 | 0 | 2 | 4 | 0 |
8.1.3.2 Instrumented Code Example
The following example displays an instrumentation point inserted by ATOM, a post-link tool. The standard "Hello world" test case is instrumented with the ATOM tool Third Degree.
main() { printf("Hello world!\n"); }
The following program listing is a disassembly of
main
.
The code range inserted by a post-link tool begins at 0x120063984 and ends
at 0x1200639ac, inclusively.
CRD# Type Notes main: 0x120063978: ldah gp, 8186(r27) 0 standard (1) 0x12006397c: lda gp, 18184(gp) 0 0x120063980: lda sp, -16(sp) 0 0x120063984: lda sp, -48(sp) 1 non-context (2) 0x120063988: stq r16, 24(sp) 2 non-context-stack 0x12006398c: stq r17, 16(sp) 2 0x120063990: stq r26, 8(sp) 2 0x120063994: lda r16, 4(r31) 2 0x120063998: lda r17, 48(sp) 2 0x12006399c: bsr r26, 0x1200069f0(r31) 2 0x1200639a0: ldq r16, 24(sp) 2 0x1200639a4: ldq r17, 16(sp) 2 0x1200639a8: ldq r26, 8(sp) 2 0x1200639ac: lda sp, 48(sp) 2 0x1200639b0: stq r26, 0(sp) 3 non-context-stack (3) 0x1200639b4: ldq r16, -32736(gp) 4 context 0x1200639b8: ldq r27, -32608(gp) 4 0x1200639bc: jsr r26, (r27), 0x1200625d4(r31) 4 0x1200639c0: ldah gp, 8186(r26) 4 0x1200639c4: lda gp, 18112(gp) 4 0x1200639c8: bis r31, r31, r0 4 0x1200639cc: ldq r26, 0(sp) 4 0x1200639d0: lda sp, 16(sp) 4 0x1200639d4: ret r31, (r26), 1 4 implicitly non- (4) context
Notes:
This CRD is still of type
CRD_TYPE_STANDARD
because
sp_set
is still relative to this code range.
The
entry_length
field is no longer relative to this code range, and
should be zero.
This instruction has a type of
CRD_TYPE_NON_CONTEXT
because the previous stack allocation was for a different RPD.
This instruction has a type of
CRD_TYPE_NON_CONTEXT_STACK
because the stack was allocated at
main+8
and
this is still part of the prologue.
The return instructions at the end of the procedure is implicitly non-context because of the reserved instruction sequence rules defined in Section 3.2.6.2.1. Using an additional CRD here would be redundant, but not incorrect.
The following tables show the code range descriptors and the run-time
procedure descriptors associated with the preceding example.
Code range descriptors:
crd | begin_address | crd_type | rpd_offset |
CRD0 | main | standard | PD0 |
CRD1 | main+12 | standard | PD1 |
CRD2 | main+16 | non_context_stack | PD1 |
CRD3 | main+56 | non_context_stack | PD0 |
CRD4 | main+58 | context | PD0 |
Run-time procedure descriptors:
rpd | sp_set | entry_length | frame_size | return_address |
PD0 | 2 | 0 | 2 | 0 |
PD1 | 0 | 0 | 6 | CRD3 |
For the purpose of comparison, this is the non-instrumented version
of
main
:
[hw.c: 1] 0x120001120: 27bb2000 ldah gp, 8192(r27) [hw.c: 1] 0x120001124: 23bd6f60 lda gp, 28512(gp) [hw.c: 1] 0x120001128: 23defff0 lda sp, -16(sp) [hw.c: 1] 0x12000112c: b75e0000 stq r26, 0(sp) [hw.c: 3] 0x120001130: a61d8020 ldq r16, -32736(gp) [hw.c: 3] 0x120001134: a77d80a0 ldq r27, -32608(gp) [hw.c: 3] 0x120001138: 6b5b7b05 jsr r26, (r27), 0x11ffffd50(r31) [hw.c: 3] 0x12000113c: 27ba2000 ldah gp, 8192(r26) [hw.c: 3] 0x120001140: 23bd6f44 lda gp, 28484(gp) [hw.c: 4] 0x120001144: 47ff0400 bis r31, r31, r0 [hw.c: 4] 0x120001148: a75e0000 ldq r26, 0(sp) [hw.c: 4] 0x12000114c: 23de0010 lda sp, 16(sp) [hw.c: 4] 0x120001150: 6bfa8001 ret r31, (r26), 1
The following tables show the code range descriptors and run-time procedure
descriptors for the preceding example.
Code range descriptors (noninstrumented):
crd | begin_address | crd_type | rpd_offset |
CRD0 | main | standard | PD0 |
Run-time procedure descriptors (noninstrumented):
rpd | sp_set | entry_length | frame_size | return_address |
PD0 | 2 | 4 | 2 | 0 |
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.