Services - tools - models - for embedded software development
Embecosm divider strip
Prev  Next

4.2.5.  OpenRISC 1000 Frame Handling

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),ry

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.

The OpenRISC 1000 stack frame at the end of the prologue

Figure 4.1.  The OpenRISC 1000 stack frame 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   ry,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.

4.2.5.1.  OpenRISC 1000 Functions Analyzing Frames

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.

    [Note]Note

    The OpenRISC 1000 ABI specifies that frames should be double-word aligned. However the version of GCC in the current OpenRISC tool chain implements single-word alignment. So the current GDB implementation specifies OR1K_STACK_ALIGN to be 4, not 8.

  • 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.

4.2.5.2.  OpenRISC 1000 Functions for Accessing Frame Data

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.

4.2.5.3.  OpenRISC 1000 Functions to Create Dummy Stack Frames

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.

4.2.5.4.  OpenRISC 1000 Frame Sniffers

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.

4.2.5.5.  OpenRISC 1000 Frame Base Sniffer

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.

4.2.5.6.  OpenRISC 1000 Low Level Frame Sniffers

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]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]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.

[Tip]Tip

It is essential to save the register data using the correct function.

  • Use trad_frame_set_reg_realreg when a register in the PREVIOUS frame is obtained from a register in THIS frame.

  • Use trad_frame_set_reg_addr when a register in the PREVIOUS frame is obtained from an address in THIS frame.

  • Use trad_frame_set_reg_value when a register in the PREVIOUS frame is a particular value in THIS frame.

The default entry for each register is that its value in the PREVIOUS frame is obtained from the same register in THIS frame.

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.

Embecosm divider strip