The OpenRISC 1000 frame structure is described in its ABI [8]. Some of the detail is slightly different in current OpenRISC implementations—this is described in Section 3.3.
The key to frame handling is understanding the prologue (and possibly epilogue) in each function which is responsible for initializing the stack frame. For the OpenRISC 1000, GPR 1 is used as the stack pointer, GPR 2 as the frame pointer and GPR 9 as the return address. The prologue sequence is:
l.addi r1,r1,-
frame_size
l.sw
save_loc(r1),r2
l.addi r2,r1,
frame_size
l.sw
save_loc-4(r1),r9
l.sw
x(r1),r
y
The OpenRISC 1000 stack frame accommodates any local (automatic) variables and temporary values, then the return address, then the old frame pointer and finally any stack based arguments to functions called by this function. This last rule means that the return address and old frame pointer are not necessarily at the end of the stack frame - enough space will be left to build up any arguments for called functions that must go on the stack. Figure 4.1 shows how the stack looks at the end of the prologue.
Not all fields are always present. The function need not save its return address to stack, and there may be no callee-saved registers (i.e. GPRs 12, 14, 16, 18, 20, 22, 24, 26, 28 and 30) which require saving. Leaf functions are not required to set up a new stack frame at all.
The epilogue is the inverse. Callee-saved registers are restored, the return address placed in GPR 9 and the stack and frame pointers restored before jumping to the address in GPR 9.
l.lwz r
y,
x(r1)
l.lwz r9,
save_loc-4(r1)
l.lwz r2,
save_loc(r1)
l.jr r9
l.addi r1,r1,
frame_size
Only those parts of the epilogue which correspond to the prologue
need actually appear. The OpenRISC 1000 has a delay slot after
branch instructions, so for efficiency the stack restoration can be
placed after the l.jr
instruction.
A group of struct gdbarch functions and a value provide information about the current stack and how it is being processed by the target program.
set_gdbarch_skip_prologue (gdbarch, or1k_skip_prologue); set_gdbarch_inner_than (gdbarch, core_addr_lessthan); set_gdbarch_frame_align (gdbarch, or1k_frame_align); set_gdbarch_frame_red_zone_size (gdbarch, OR1K_FRAME_RED_ZONE_SIZE);
or1k_skip_prologue
. This function returns
the end of the function prologue, if the program counter is
currently in a function prologue.
The initial approach is to use the DWARF2 symbol-and-line
(SAL) information to identify the start of the function
(find_pc_partial_function
and hence the
end of the prologue
(skip_prologue_using_sal
).
If this information is not available,
or1k_skip_prologue
reuses the helper
functions from the frame sniffer function,
or1k_frame_unwind_cache
(see Section 4.2.5.5) to step through code that
appears to be function prologue.
core_addr_lessthan
. This standard function
returns 1 (true) if its first argument is a lower address than
its second argument. It provides the functionality required by
the struct gdbarch inner_than
function for
architectures like OpenRISC 1000, which have falling stack
frames.
or1k_frame_align
. This function takes a
stack pointer and returns a value (expanding the frame) which
meets the stack alignment requirements of the ABI. Since the
OpenRISC 1000 ABI uses a falling stack, this uses the
built-in function, align_down
. The
alignment is specified in the constant
OR1K_STACK_ALIGN
defined in
or1k-tdep.h
.
OR1K_FRAME_RED_ZONE_SIZE
. The OpenRISC 1000
reserves the 2,560 bytes below the stack pointer for use by
exception handlers and frameless functions. This is known as a
red zone (an AMD term). This constant is
recorded in the struct gdbarch frame_red_zone_size
field. Any dummy stack frames (see Section 4.2.5.3) will be placed after this point.
set_gdbarch_unwind_pc (gdbarch, or1k_unwind_pc); set_gdbarch_unwind_sp (gdbarch, or1k_unwind_sp);
There are only two functions required here,
or1k_unwind_pc
and
or1k_unwind_sp
. Given a pointer to the NEXT
frame, these functions return the value of respectively the program
counter and stack pointer in THIS frame.
Since the OpenRISC architecture defines standard frame sniffers,
and both these registers are raw registers, the functions can be
implemented very simply by a call to
frame_unwind_register_unsigned
.
Two struct gdbarch provide support for calling code in the target inferior.
set_gdbarch_push_dummy_call (gdbarch, or1k_push_dummy_call); set_gdbarch_unwind_dummy_id (gdbarch, or1k_unwind_dummy_id);
or1k_push_dummy_call
. This function
creates a dummy stack frame, so that GDB can evaluate a
function within the target code (for example in a
call command). The input arguments include
all the parameters for the call, including the return address
and an address where a structure should be returned.
The return address for the function is always breakpointed (so
GDB can trap the return). This return address is written
into the link register (in the register cache) using
regcache_cooked_write_unsigned
).
If the function is to return a structure, the address where the structure is to go is passed as a first argument, in GPR 3.
The next arguments are passed in the remaining argument registers (up to GPR 8). Structures are passed by reference to their locating in memory. For 32-bit architectures passing 64-bit arguments, a pair of registers (3 and 4, 5 and 6 or 7 and 8) are used.
Any remaining arguments must be pushed on the end of the stack. There is a difficulty here, since pushing each argument may leave the stack misaligned (OpenRISC 1000 specifies double-word alignment). So the code first works out the space required, then adjusts the resulting stack pointer to the correct alignment. The arguments can then be written to the stack in the correct location.
or1k_unwind_dummy_id
. This is the inverse
of or1k_push_dummy_call
. Given a pointer
to the NEXT stack frame (which will be the frame of the
dummy call), it returns the frame ID (that is the stack
pointer and function entry point address) of THIS frame.
This is not completely trivial. For a dummy frame, the NEXT
frame information about THIS frame is not necessarily
complete, so a simple call to
frame_unwind_id
recurses back to this
function ad infinitum. Instead the frame
information is built by unwind the stack pointer and program
counter and attempting to use DWARF2 symbol-and-line (SAL)
information to find the start of the function from the PC
with find_pc_partial_function
. If that
information is not available, the program counter is used as a
proxy for the function start address.
The preceding functions all have a 1:1 relationship with struct gdbarch. However for stack analysis (or "sniffing") more than one approach may be appropriate, so a list of functions is maintained.
The low level stack analysis functions are set by
frame_unwind_append_sniffer
. The OpenRISC 1000
has its own sniffers for finding the ID of a frame and getting the
value of a register on the frame specified by
or1k_frame_sniffer
. For all other sniffing
functions, the default DWARF2 frame sniffer is used,
dwarf2_frame_sniffer
.
The high level sniffer finds the base of the stack frame. OpenRISC
defines its own base sniffer,
or1k_frame_base as default. It provides all
the functionality needed, so can be used as the default base
sniffer, set using frame_base_set_default
. The
frame base is a structure, with entries pointing to the
corresponding frame sniffer and functions to give the base address
of the frame, the arguments on the frame and the local variables on
the frame. Since these are all the same for the OpenRISC 1000, the
same function, or1k_frame_base_address
is used
for all three.
The same function, or1k_frame_base_address
is
used to provide all three base functions: for the frame itself,
the local variables and any arguments. In the OpenRISC 1000 these
are all the same value.
or1k_frame_base.unwind = or1k_frame_sniffer (NULL); or1k_frame_base.this_base = or1k_frame_base_address; or1k_frame_base.this_locals = or1k_frame_base_address; or1k_frame_base.this_args = or1k_frame_base_address; frame_base_set_default (gdbarch, &or1k_frame_base);
The specification of this function requires the end of
the stack, i.e. the stack pointer. Rather confusingly the function
is also used to
determine the value of the $fp
variable if
deprecated_fp_regnum
has not been set and there
is no register with the name "fp". However, as noted earlier, GDB
is moving away from an intrinsic understanding of frame
pointers. For the OpenRISC 1000,
deprecated_fp_regnum
is currently defined,
although in time a pseudo register will be defined, with the name
of fp
and mapping to GPR 2.
Like all the frame sniffers, this function is
passed the address of the NEXT frame, and requires the value for
THIS frame, so the value of the stack pointer is unwound from
the stack by using the generic register unwinder,
frame_unwind_register_unsigned
.
The function or1k_frame_sniffer
returns a
pointer to struct frame_unwind with
entries for the functions defined by this sniffer. For the
OpenRISC 1000, this defines a custom function to construct the
frame ID of THIS frame given a pointer to the NEXT frame
(or1k_frame_this_id
) and a custom function to
give the value of a register in THIS frame given a pointer to
the NEXT frame (or1k_frame_prev_register
).
or1k_frame_this_id
. This function's
inputs are a pointer to the NEXT frame and the prologue
cache (if any exists) for THIS frame. It uses the main
OpenRISC 1000 frame analyzer,
or1k_frame_unwind_cache
to generate the
prologue cache if it does not exist (see below).
From the cached data, the function returns the frame ID. This comprises two values, the stack pointer for this frame and the address of the code (typically the entry point) for the function using this stack frame
![]() | Note |
---|---|
Strictly speaking frame IDs can have a third value, the special address for use with architectures which have more complex frame structures. However this is rarely used. |
The result is returned in a
struct frame_id passed by
reference as a third argument. Since the implementation uses
the built in
struct trad_frame_cache for its
register cache, the code can use the
trad_frame_get_id
function to decode the
frame ID from the cache.
or1k_frame_prev_register
. This function's
inputs are a pointer to the NEXT frame, the prologue cache
(if any exists) for THIS frame and a register number. It
uses the main OpenRISC 1000 frame analyzer,
or1k_frame_unwind_cache
to generate the
prologue cache if it does not exist (see below).
From the cached data, a flag is returned indicating if the register has been optimized out (this is never the case), what sort of l-value the register represents (a register, memory or not an l-value), the address where it is saved in memory (if it is saved in memory), the number of a different register which holds the value of this register (if that is the case) and if a buffer is provided the actual value as obtained from memory or the register cache.
Since the implementation uses the built in
struct trad_frame_cache for its
register cache, the code can use the
trad_frame_get_register
function to
decode all this information from the cache.
The OpenRISC 1000 low level sniffers rely on
or1k_frame_unwind_cache
. This is the heart of
the sniffer. It must determine the frame ID for THIS frame given
a pointer to the NEXT frame and then the information in THIS
frame about the values of registers in the PREVIOUS frame.
All this data is returned in a prologue cache (see Section 2.3.6), a reference to which is passed as an argument. If the cache already exists for THIS frame it can be returned immediately as the result.
If the cache does not yet exist, it is allocated (using
trad_frame_cache_zalloc
). The first step
is to unwind the start address of this function from the
NEXT frame. The DWARF2 information in the object file can be
used to find the end of the prologue (using
skip_prologue_using_sal
).
The code then works through each instruction of the prologue to find the data required.
![]() | Caution |
---|---|
The analysis must only consider prologue instructions that have actually been executed. It is quite possible the program counter is in the prologue code, and only instructions that have actually been executed should be analyzed. |
The stack pointer and program counter are found by simply
unwinding the NEXT frame. The stack pointer is the base of
THIS frame, and is added to the cache data using
trad_frame_set_this_base
.
end_iaddr
marks the end of the code we
should analyze. Only instructions with addresses less than
this will be considered.
The l.addi
instruction should be first and
its immediate constant field is the size of the stack. If it
is missing, then this is a frameless call to a function. If
the program counter is right at the start of the function,
before the stack and frame pointers are set up, then it will
also look like a frameless function.
Unless it is subsequently found to have been saved on the stack, the program counter of the PREVIOUS frame is the link register of THIS frame and can be recorded in the register cache.
For a frameless call, there is no more information to be found, so the rest of the code analysis only applies if the frame size was non-zero.
The second instruction in the prologue is where the frame
pointer of the PREVIOUS frame is saved. It is an error if this
is missing. The address where it is saved (the stack pointer
of THIS frame plus the offset in the l.sw
instruction) is saved in the cache using
trad_frame_set_reg_addr
.
The third instruction should be an l.addi
instruction which sets the frame pointer. The frame size set in
this instruction should match the frame size set in the first
instruction. Once this has been set up, the frame pointer can be
used to yield the stack pointer of the previous frame. This
information is recorded in the register cache.
The fourth instruction is optional and saves the return
address to the stack. If this instruction is found, the entry
in the register cache for the program counter in the PREVIOUS
frame must be changed using
trad_frame_set_reg_addr
to indicate it is
found at an address in this frame.
All the subsequent instructions in the prolong should be saves
of callee-savable registers. These are checked for until the
code address has reached the end of the prologue. For each
instruction that is found, the save location of the register
is recorded in the cache using
trad_frame_set_reg_addr
.
The detailed analysis in
or1k_frame_unwind_cache
uses a series of
helper functions: or1k_frame_size
,
or1k_frame_fp_loc
,
or1k_frame_size_check
,
or1k_link_address
and
or1k_get_saved_reg
. These helper routines
check each of the instructions in the prologue. By breaking out
this code into separate functions, they can be reused by
or1k_skip_prologue
.