|  | /* Target-dependent mdebug code for the ALPHA architecture. | 
|  | Copyright (C) 1993-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "frame.h" | 
|  | #include "frame-unwind.h" | 
|  | #include "frame-base.h" | 
|  | #include "symtab.h" | 
|  | #include "gdbcore.h" | 
|  | #include "block.h" | 
|  | #include "trad-frame.h" | 
|  |  | 
|  | #include "alpha-tdep.h" | 
|  | #include "mdebugread.h" | 
|  | #include "gdbarch.h" | 
|  |  | 
|  | /* FIXME: Some of this code should perhaps be merged with mips.  */ | 
|  |  | 
|  | /* *INDENT-OFF* */ | 
|  | /* Layout of a stack frame on the alpha: | 
|  |  | 
|  | |				| | 
|  | pdr members:	|  7th ... nth arg,		| | 
|  | |  `pushed' by caller.		| | 
|  | |				| | 
|  | ----------------|-------------------------------|<--  old_sp == vfp | 
|  | ^  ^  ^  ^	|				| | 
|  | |  |  |  |	|				| | 
|  | |  |localoff	|  Copies of 1st .. 6th		| | 
|  | |  |  |  |	|  argument if necessary.	| | 
|  | |  |  |  v	|				| | 
|  | |  |  |  ---	|-------------------------------|<-- LOCALS_ADDRESS | 
|  | |  |  |      |				| | 
|  | |  |  |      |  Locals and temporaries.	| | 
|  | |  |  |      |				| | 
|  | |  |  |      |-------------------------------| | 
|  | |  |  |      |				| | 
|  | |-fregoffset	|  Saved float registers.	| | 
|  | |  |  |      |  F9				| | 
|  | |  |  |      |   .				| | 
|  | |  |  |      |   .				| | 
|  | |  |  |      |  F2				| | 
|  | |  |  v      |				| | 
|  | |  |  -------|-------------------------------| | 
|  | |  |         |				| | 
|  | |  |         |  Saved registers.		| | 
|  | |  |         |  S6				| | 
|  | |-regoffset	|   .				| | 
|  | |  |         |   .				| | 
|  | |  |         |  S0				| | 
|  | |  |         |  pdr.pcreg			| | 
|  | |  v         |				| | 
|  | |  ----------|-------------------------------| | 
|  | |            |				| | 
|  | frameoffset    |  Argument build area, gets	| | 
|  | |            |  7th ... nth arg for any	| | 
|  | |            |  called procedure.		| | 
|  | v            |  				| | 
|  | -------------|-------------------------------|<-- sp | 
|  | |				| | 
|  | */ | 
|  | /* *INDENT-ON* */ | 
|  |  | 
|  | #define PROC_LOW_ADDR(proc) ((proc)->pdr.adr) | 
|  | #define PROC_FRAME_OFFSET(proc) ((proc)->pdr.frameoffset) | 
|  | #define PROC_FRAME_REG(proc) ((proc)->pdr.framereg) | 
|  | #define PROC_REG_MASK(proc) ((proc)->pdr.regmask) | 
|  | #define PROC_FREG_MASK(proc) ((proc)->pdr.fregmask) | 
|  | #define PROC_REG_OFFSET(proc) ((proc)->pdr.regoffset) | 
|  | #define PROC_FREG_OFFSET(proc) ((proc)->pdr.fregoffset) | 
|  | #define PROC_PC_REG(proc) ((proc)->pdr.pcreg) | 
|  | #define PROC_LOCALOFF(proc) ((proc)->pdr.localoff) | 
|  |  | 
|  | /* Locate the mdebug PDR for the given PC.  Return null if one can't | 
|  | be found; you'll have to fall back to other methods in that case.  */ | 
|  |  | 
|  | static struct mdebug_extra_func_info * | 
|  | find_proc_desc (CORE_ADDR pc) | 
|  | { | 
|  | const struct block *b = block_for_pc (pc); | 
|  | struct mdebug_extra_func_info *proc_desc = NULL; | 
|  | struct symbol *sym = NULL; | 
|  | const char *sh_name = NULL; | 
|  |  | 
|  | if (b) | 
|  | { | 
|  | CORE_ADDR startaddr; | 
|  | find_pc_partial_function (pc, &sh_name, &startaddr, NULL); | 
|  |  | 
|  | if (startaddr > b->start ()) | 
|  | /* This is the "pathological" case referred to in a comment in | 
|  | print_frame_info.  It might be better to move this check into | 
|  | symbol reading.  */ | 
|  | sym = NULL; | 
|  | else | 
|  | sym = lookup_symbol (MDEBUG_EFI_SYMBOL_NAME, b, LABEL_DOMAIN, | 
|  | 0).symbol; | 
|  | } | 
|  |  | 
|  | if (sym) | 
|  | { | 
|  | proc_desc = (struct mdebug_extra_func_info *) sym->value_bytes (); | 
|  |  | 
|  | /* Correct incorrect setjmp procedure descriptor from the library | 
|  | to make backtrace through setjmp work.  */ | 
|  | if (proc_desc->pdr.pcreg == 0 | 
|  | && strcmp (sh_name, "setjmp") == 0) | 
|  | { | 
|  | proc_desc->pdr.pcreg = ALPHA_RA_REGNUM; | 
|  | proc_desc->pdr.regmask = 0x80000000; | 
|  | proc_desc->pdr.regoffset = -4; | 
|  | } | 
|  |  | 
|  | /* If we never found a PDR for this function in symbol reading, | 
|  | then examine prologues to find the information.  */ | 
|  | if (proc_desc->pdr.framereg == -1) | 
|  | proc_desc = NULL; | 
|  | } | 
|  |  | 
|  | return proc_desc; | 
|  | } | 
|  |  | 
|  | /* Return a non-zero result if the function is frameless; zero otherwise.  */ | 
|  |  | 
|  | static int | 
|  | alpha_mdebug_frameless (struct mdebug_extra_func_info *proc_desc) | 
|  | { | 
|  | return (PROC_FRAME_REG (proc_desc) == ALPHA_SP_REGNUM | 
|  | && PROC_FRAME_OFFSET (proc_desc) == 0); | 
|  | } | 
|  |  | 
|  | /* This returns the PC of the first inst after the prologue.  If we can't | 
|  | find the prologue, then return 0.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | alpha_mdebug_after_prologue (CORE_ADDR pc, | 
|  | struct mdebug_extra_func_info *proc_desc) | 
|  | { | 
|  | if (proc_desc) | 
|  | { | 
|  | /* If function is frameless, then we need to do it the hard way.  I | 
|  | strongly suspect that frameless always means prologueless...  */ | 
|  | if (alpha_mdebug_frameless (proc_desc)) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return alpha_after_prologue (pc); | 
|  | } | 
|  |  | 
|  | /* Return non-zero if we *might* be in a function prologue.  Return zero | 
|  | if we are definitively *not* in a function prologue.  */ | 
|  |  | 
|  | static int | 
|  | alpha_mdebug_in_prologue (CORE_ADDR pc, | 
|  | struct mdebug_extra_func_info *proc_desc) | 
|  | { | 
|  | CORE_ADDR after_prologue_pc = alpha_mdebug_after_prologue (pc, proc_desc); | 
|  | return (after_prologue_pc == 0 || pc < after_prologue_pc); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Frame unwinder that reads mdebug PDRs.  */ | 
|  |  | 
|  | struct alpha_mdebug_unwind_cache | 
|  | { | 
|  | struct mdebug_extra_func_info *proc_desc; | 
|  | CORE_ADDR vfp; | 
|  | trad_frame_saved_reg *saved_regs; | 
|  | }; | 
|  |  | 
|  | /* Extract all of the information about the frame from PROC_DESC | 
|  | and store the resulting register save locations in the structure.  */ | 
|  |  | 
|  | static struct alpha_mdebug_unwind_cache * | 
|  | alpha_mdebug_frame_unwind_cache (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info; | 
|  | struct mdebug_extra_func_info *proc_desc; | 
|  | ULONGEST vfp; | 
|  | CORE_ADDR pc, reg_position; | 
|  | unsigned long mask; | 
|  | int ireg, returnreg; | 
|  |  | 
|  | if (*this_prologue_cache) | 
|  | return (struct alpha_mdebug_unwind_cache *) *this_prologue_cache; | 
|  |  | 
|  | info = FRAME_OBSTACK_ZALLOC (struct alpha_mdebug_unwind_cache); | 
|  | *this_prologue_cache = info; | 
|  | pc = get_frame_address_in_block (this_frame); | 
|  |  | 
|  | /* ??? We don't seem to be able to cache the lookup of the PDR | 
|  | from alpha_mdebug_frame_p.  It'd be nice if we could change | 
|  | the arguments to that function.  Oh well.  */ | 
|  | proc_desc = find_proc_desc (pc); | 
|  | info->proc_desc = proc_desc; | 
|  | gdb_assert (proc_desc != NULL); | 
|  |  | 
|  | info->saved_regs = trad_frame_alloc_saved_regs (this_frame); | 
|  |  | 
|  | /* The VFP of the frame is at FRAME_REG+FRAME_OFFSET.  */ | 
|  | vfp = get_frame_register_unsigned (this_frame, PROC_FRAME_REG (proc_desc)); | 
|  | vfp += PROC_FRAME_OFFSET (info->proc_desc); | 
|  | info->vfp = vfp; | 
|  |  | 
|  | /* Fill in the offsets for the registers which gen_mask says were saved.  */ | 
|  |  | 
|  | reg_position = vfp + PROC_REG_OFFSET (proc_desc); | 
|  | mask = PROC_REG_MASK (proc_desc); | 
|  | returnreg = PROC_PC_REG (proc_desc); | 
|  |  | 
|  | /* Note that RA is always saved first, regardless of its actual | 
|  | register number.  */ | 
|  | if (mask & (1 << returnreg)) | 
|  | { | 
|  | /* Clear bit for RA so we don't save it again later.  */ | 
|  | mask &= ~(1 << returnreg); | 
|  |  | 
|  | info->saved_regs[returnreg].set_addr (reg_position); | 
|  | reg_position += 8; | 
|  | } | 
|  |  | 
|  | for (ireg = 0; ireg <= 31; ++ireg) | 
|  | if (mask & (1 << ireg)) | 
|  | { | 
|  | info->saved_regs[ireg].set_addr (reg_position); | 
|  | reg_position += 8; | 
|  | } | 
|  |  | 
|  | reg_position = vfp + PROC_FREG_OFFSET (proc_desc); | 
|  | mask = PROC_FREG_MASK (proc_desc); | 
|  |  | 
|  | for (ireg = 0; ireg <= 31; ++ireg) | 
|  | if (mask & (1 << ireg)) | 
|  | { | 
|  | info->saved_regs[ALPHA_FP0_REGNUM + ireg].set_addr (reg_position); | 
|  | reg_position += 8; | 
|  | } | 
|  |  | 
|  | /* The stack pointer of the previous frame is computed by popping | 
|  | the current stack frame.  */ | 
|  | if (!info->saved_regs[ALPHA_SP_REGNUM].is_addr ()) | 
|  | info->saved_regs[ALPHA_SP_REGNUM].set_value (vfp); | 
|  |  | 
|  | return info; | 
|  | } | 
|  |  | 
|  | /* Given a GDB frame, determine the address of the calling function's | 
|  | frame.  This will be used to create a new GDB frame struct.  */ | 
|  |  | 
|  | static void | 
|  | alpha_mdebug_frame_this_id (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache, | 
|  | struct frame_id *this_id) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info | 
|  | = alpha_mdebug_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  |  | 
|  | *this_id = frame_id_build (info->vfp, get_frame_func (this_frame)); | 
|  | } | 
|  |  | 
|  | /* Retrieve the value of REGNUM in FRAME.  Don't give up!  */ | 
|  |  | 
|  | static struct value * | 
|  | alpha_mdebug_frame_prev_register (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache, int regnum) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info | 
|  | = alpha_mdebug_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  |  | 
|  | /* The PC of the previous frame is stored in the link register of | 
|  | the current frame.  Frob regnum so that we pull the value from | 
|  | the correct place.  */ | 
|  | if (regnum == ALPHA_PC_REGNUM) | 
|  | regnum = PROC_PC_REG (info->proc_desc); | 
|  |  | 
|  | return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum); | 
|  | } | 
|  |  | 
|  | /* Return a non-zero result if the size of the stack frame exceeds the | 
|  | maximum debuggable frame size (512 Kbytes); zero otherwise.  */ | 
|  |  | 
|  | static int | 
|  | alpha_mdebug_max_frame_size_exceeded (struct mdebug_extra_func_info *proc_desc) | 
|  | { | 
|  | /* If frame offset is null, we can be in two cases: either the | 
|  | function is frameless (the stack frame is null) or its | 
|  | frame exceeds the maximum debuggable frame size (512 Kbytes).  */ | 
|  |  | 
|  | return (PROC_FRAME_OFFSET (proc_desc) == 0 | 
|  | && !alpha_mdebug_frameless (proc_desc)); | 
|  | } | 
|  |  | 
|  | static int | 
|  | alpha_mdebug_frame_sniffer (const struct frame_unwind *self, | 
|  | frame_info_ptr this_frame, | 
|  | void **this_cache) | 
|  | { | 
|  | CORE_ADDR pc = get_frame_address_in_block (this_frame); | 
|  | struct mdebug_extra_func_info *proc_desc; | 
|  |  | 
|  | /* If this PC does not map to a PDR, then clearly this isn't an | 
|  | mdebug frame.  */ | 
|  | proc_desc = find_proc_desc (pc); | 
|  | if (proc_desc == NULL) | 
|  | return 0; | 
|  |  | 
|  | /* If we're in the prologue, the PDR for this frame is not yet valid. | 
|  | Say no here and we'll fall back on the heuristic unwinder.  */ | 
|  | if (alpha_mdebug_in_prologue (pc, proc_desc)) | 
|  | return 0; | 
|  |  | 
|  | /* If the maximum debuggable frame size has been exceeded, the | 
|  | proc desc is bogus.  Fall back on the heuristic unwinder.  */ | 
|  | if (alpha_mdebug_max_frame_size_exceeded (proc_desc)) | 
|  | return 0; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static const struct frame_unwind alpha_mdebug_frame_unwind = | 
|  | { | 
|  | "alpha mdebug", | 
|  | NORMAL_FRAME, | 
|  | default_frame_unwind_stop_reason, | 
|  | alpha_mdebug_frame_this_id, | 
|  | alpha_mdebug_frame_prev_register, | 
|  | NULL, | 
|  | alpha_mdebug_frame_sniffer | 
|  | }; | 
|  |  | 
|  | static CORE_ADDR | 
|  | alpha_mdebug_frame_base_address (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info | 
|  | = alpha_mdebug_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  |  | 
|  | return info->vfp; | 
|  | } | 
|  |  | 
|  | static CORE_ADDR | 
|  | alpha_mdebug_frame_locals_address (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info | 
|  | = alpha_mdebug_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  |  | 
|  | return info->vfp - PROC_LOCALOFF (info->proc_desc); | 
|  | } | 
|  |  | 
|  | static CORE_ADDR | 
|  | alpha_mdebug_frame_args_address (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct alpha_mdebug_unwind_cache *info | 
|  | = alpha_mdebug_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  |  | 
|  | return info->vfp - ALPHA_NUM_ARG_REGS * 8; | 
|  | } | 
|  |  | 
|  | static const struct frame_base alpha_mdebug_frame_base = { | 
|  | &alpha_mdebug_frame_unwind, | 
|  | alpha_mdebug_frame_base_address, | 
|  | alpha_mdebug_frame_locals_address, | 
|  | alpha_mdebug_frame_args_address | 
|  | }; | 
|  |  | 
|  | static const struct frame_base * | 
|  | alpha_mdebug_frame_base_sniffer (frame_info_ptr this_frame) | 
|  | { | 
|  | CORE_ADDR pc = get_frame_address_in_block (this_frame); | 
|  | struct mdebug_extra_func_info *proc_desc; | 
|  |  | 
|  | /* If this PC does not map to a PDR, then clearly this isn't an | 
|  | mdebug frame.  */ | 
|  | proc_desc = find_proc_desc (pc); | 
|  | if (proc_desc == NULL) | 
|  | return NULL; | 
|  |  | 
|  | /* If the maximum debuggable frame size has been exceeded, the | 
|  | proc desc is bogus.  Fall back on the heuristic unwinder.  */ | 
|  | if (alpha_mdebug_max_frame_size_exceeded (proc_desc)) | 
|  | return 0; | 
|  |  | 
|  | return &alpha_mdebug_frame_base; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | alpha_mdebug_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
|  | { | 
|  | frame_unwind_append_unwinder (gdbarch, &alpha_mdebug_frame_unwind); | 
|  | frame_base_append_sniffer (gdbarch, alpha_mdebug_frame_base_sniffer); | 
|  | } |