|  | /* Target-dependent code for SDE on MIPS processors. | 
|  |  | 
|  | Copyright (C) 2014-2024 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 "osabi.h" | 
|  | #include "elf-bfd.h" | 
|  | #include "symtab.h" | 
|  |  | 
|  | #include "frame.h" | 
|  | #include "frame-unwind.h" | 
|  | #include "frame-base.h" | 
|  | #include "trad-frame.h" | 
|  |  | 
|  | #include "mips-tdep.h" | 
|  |  | 
|  | /* Fill in the register cache *THIS_CACHE for THIS_FRAME for use | 
|  | in the SDE frame unwinder.  */ | 
|  |  | 
|  | static struct trad_frame_cache * | 
|  | mips_sde_frame_cache (const frame_info_ptr &this_frame, void **this_cache) | 
|  | { | 
|  | struct gdbarch *gdbarch = get_frame_arch (this_frame); | 
|  | const struct mips_regnum *regs = mips_regnum (gdbarch); | 
|  | const int sizeof_reg_t = mips_abi_regsize (gdbarch); | 
|  | enum mips_abi abi = mips_abi (gdbarch); | 
|  | struct trad_frame_cache *cache; | 
|  | CORE_ADDR xcpt_frame; | 
|  | CORE_ADDR start_addr; | 
|  | CORE_ADDR stack_addr; | 
|  | CORE_ADDR pc; | 
|  | int i; | 
|  |  | 
|  | if (*this_cache != NULL) | 
|  | return (struct trad_frame_cache *) *this_cache; | 
|  | cache = trad_frame_cache_zalloc (this_frame); | 
|  | *this_cache = cache; | 
|  |  | 
|  | /* The previous registers are held in struct xcptcontext | 
|  | which is at $sp+offs | 
|  |  | 
|  | struct xcptcontext { | 
|  | reg_t	sr;		CP0 Status | 
|  | reg_t	cr;		CP0 Cause | 
|  | reg_t	epc;		CP0 EPC | 
|  | reg_t	vaddr;		CP0 BadVAddr | 
|  | reg_t	regs[32];	General registers | 
|  | reg_t	mdlo;		LO | 
|  | reg_t	mdhi;		HI | 
|  | reg_t	mdex;		ACX | 
|  | ... | 
|  | }; | 
|  | */ | 
|  |  | 
|  | stack_addr = get_frame_register_signed (this_frame, | 
|  | gdbarch_sp_regnum (gdbarch)); | 
|  | switch (abi) | 
|  | { | 
|  | case MIPS_ABI_O32: | 
|  | /* 40: XCPTCONTEXT | 
|  | 24: xcpt_gen() argspace		(16 bytes) | 
|  | 16: _xcptcall() saved ra, rounded up	( 8 bytes) | 
|  | 00: _xcptcall() argspace 		(16 bytes)  */ | 
|  | xcpt_frame = stack_addr + 40; | 
|  | break; | 
|  | case MIPS_ABI_N32: | 
|  | case MIPS_ABI_N64: | 
|  | default:			/* Wild guess.  */ | 
|  | /* 16: XCPTCONTEXT | 
|  | 16: xcpt_gen() argspace 		( 0 bytes) | 
|  | 00: _xcptcall() saved ra, rounded up	(16 bytes)  */ | 
|  | xcpt_frame = stack_addr + 16; | 
|  | break; | 
|  | } | 
|  |  | 
|  | trad_frame_set_reg_addr (cache, | 
|  | MIPS_PS_REGNUM + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 0 * sizeof_reg_t); | 
|  | trad_frame_set_reg_addr (cache, | 
|  | regs->cause + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 1 * sizeof_reg_t); | 
|  | trad_frame_set_reg_addr (cache, | 
|  | regs->pc + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 2 * sizeof_reg_t); | 
|  | trad_frame_set_reg_addr (cache, | 
|  | regs->badvaddr + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 3 * sizeof_reg_t); | 
|  | for (i = 0; i < MIPS_NUMREGS; i++) | 
|  | trad_frame_set_reg_addr (cache, | 
|  | i + MIPS_ZERO_REGNUM + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + (4 + i) * sizeof_reg_t); | 
|  | trad_frame_set_reg_addr (cache, | 
|  | regs->lo + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 36 * sizeof_reg_t); | 
|  | trad_frame_set_reg_addr (cache, | 
|  | regs->hi + gdbarch_num_regs (gdbarch), | 
|  | xcpt_frame + 37 * sizeof_reg_t); | 
|  |  | 
|  | pc = get_frame_pc (this_frame); | 
|  | find_pc_partial_function (pc, NULL, &start_addr, NULL); | 
|  | trad_frame_set_id (cache, frame_id_build (start_addr, stack_addr)); | 
|  |  | 
|  | return cache; | 
|  | } | 
|  |  | 
|  | /* Implement the this_id function for the SDE frame unwinder.  */ | 
|  |  | 
|  | static void | 
|  | mips_sde_frame_this_id (const frame_info_ptr &this_frame, void **this_cache, | 
|  | struct frame_id *this_id) | 
|  | { | 
|  | struct trad_frame_cache *this_trad_cache | 
|  | = mips_sde_frame_cache (this_frame, this_cache); | 
|  |  | 
|  | trad_frame_get_id (this_trad_cache, this_id); | 
|  | } | 
|  |  | 
|  | /* Implement the prev_register function for the SDE frame unwinder.  */ | 
|  |  | 
|  | static struct value * | 
|  | mips_sde_frame_prev_register (const frame_info_ptr &this_frame, | 
|  | void **this_cache, | 
|  | int prev_regnum) | 
|  | { | 
|  | struct trad_frame_cache *trad_cache | 
|  | = mips_sde_frame_cache (this_frame, this_cache); | 
|  |  | 
|  | return trad_frame_get_register (trad_cache, this_frame, prev_regnum); | 
|  | } | 
|  |  | 
|  | /* Implement the sniffer function for the SDE frame unwinder.  */ | 
|  |  | 
|  | static int | 
|  | mips_sde_frame_sniffer (const struct frame_unwind *self, | 
|  | const frame_info_ptr &this_frame, | 
|  | void **this_cache) | 
|  | { | 
|  | CORE_ADDR pc = get_frame_pc (this_frame); | 
|  | const char *name; | 
|  |  | 
|  | find_pc_partial_function (pc, &name, NULL, NULL); | 
|  | return (name | 
|  | && (strcmp (name, "_xcptcall") == 0 | 
|  | || strcmp (name, "_sigtramp") == 0)); | 
|  | } | 
|  |  | 
|  | /* Data structure for the SDE frame unwinder.  */ | 
|  |  | 
|  | static const struct frame_unwind mips_sde_frame_unwind = | 
|  | { | 
|  | "mips sde sigtramp", | 
|  | SIGTRAMP_FRAME, | 
|  | default_frame_unwind_stop_reason, | 
|  | mips_sde_frame_this_id, | 
|  | mips_sde_frame_prev_register, | 
|  | NULL, | 
|  | mips_sde_frame_sniffer | 
|  | }; | 
|  |  | 
|  | /* Implement the this_base, this_locals, and this_args hooks | 
|  | for the normal unwinder.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | mips_sde_frame_base_address (const frame_info_ptr &this_frame, void **this_cache) | 
|  | { | 
|  | struct trad_frame_cache *this_trad_cache | 
|  | = mips_sde_frame_cache (this_frame, this_cache); | 
|  |  | 
|  | return trad_frame_get_this_base (this_trad_cache); | 
|  | } | 
|  |  | 
|  | static const struct frame_base mips_sde_frame_base = | 
|  | { | 
|  | &mips_sde_frame_unwind, | 
|  | mips_sde_frame_base_address, | 
|  | mips_sde_frame_base_address, | 
|  | mips_sde_frame_base_address | 
|  | }; | 
|  |  | 
|  | static const struct frame_base * | 
|  | mips_sde_frame_base_sniffer (const frame_info_ptr &this_frame) | 
|  | { | 
|  | if (mips_sde_frame_sniffer (&mips_sde_frame_unwind, this_frame, NULL)) | 
|  | return &mips_sde_frame_base; | 
|  | else | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mips_sde_elf_osabi_sniff_abi_tag_sections (bfd *abfd, asection *sect, | 
|  | void *obj) | 
|  | { | 
|  | enum gdb_osabi *os_ident_ptr = (enum gdb_osabi *) obj; | 
|  | const char *name; | 
|  |  | 
|  | name = bfd_section_name (sect); | 
|  |  | 
|  | /* The presence of a section with a ".sde" prefix is indicative | 
|  | of an SDE binary.  */ | 
|  | if (startswith (name, ".sde")) | 
|  | *os_ident_ptr = GDB_OSABI_SDE; | 
|  | } | 
|  |  | 
|  | /* OSABI sniffer for MIPS SDE.  */ | 
|  |  | 
|  | static enum gdb_osabi | 
|  | mips_sde_elf_osabi_sniffer (bfd *abfd) | 
|  | { | 
|  | enum gdb_osabi osabi = GDB_OSABI_UNKNOWN; | 
|  | unsigned int elfosabi; | 
|  |  | 
|  | /* If the generic sniffer gets a hit, return and let other sniffers | 
|  | get a crack at it.  */ | 
|  | for (asection *sect : gdb_bfd_sections (abfd)) | 
|  | generic_elf_osabi_sniff_abi_tag_sections (abfd, sect, &osabi); | 
|  | if (osabi != GDB_OSABI_UNKNOWN) | 
|  | return GDB_OSABI_UNKNOWN; | 
|  |  | 
|  | elfosabi = elf_elfheader (abfd)->e_ident[EI_OSABI]; | 
|  |  | 
|  | if (elfosabi == ELFOSABI_NONE) | 
|  | { | 
|  | /* When elfosabi is ELFOSABI_NONE (0), then the ELF structures in the | 
|  | file are conforming to the base specification for that machine | 
|  | (there are no OS-specific extensions).  In order to determine the | 
|  | real OS in use we must look for OS notes that have been added. | 
|  |  | 
|  | For SDE, we simply look for sections named with .sde as prefixes.  */ | 
|  | bfd_map_over_sections (abfd, | 
|  | mips_sde_elf_osabi_sniff_abi_tag_sections, | 
|  | &osabi); | 
|  | } | 
|  | return osabi; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mips_sde_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
|  | { | 
|  | frame_unwind_append_unwinder (gdbarch, &mips_sde_frame_unwind); | 
|  | frame_base_append_sniffer (gdbarch, mips_sde_frame_base_sniffer); | 
|  | } | 
|  |  | 
|  | void _initialize_mips_sde_tdep (); | 
|  | void | 
|  | _initialize_mips_sde_tdep () | 
|  | { | 
|  | gdbarch_register_osabi_sniffer (bfd_arch_mips, | 
|  | bfd_target_elf_flavour, | 
|  | mips_sde_elf_osabi_sniffer); | 
|  |  | 
|  | gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_SDE, mips_sde_init_abi); | 
|  | } |