* config/mips/embed.mt (TDEPFILES): Add mips-mdebug-tdep.o.
* mips-mdebug-tdep.h, mips-mdebug-tdep.c: New files.
* mips-tdep.c (mips_mdebug_frame_cache)
(mips_mdebug_frame_this_id, mips_mdebug_frame_prev_register)
(mips_mdebug_frame_unwind, mips_mdebug_frame_sniffer)
(mips_mdebug_frame_base_address, mips_mdebug_frame_sniffer)
(mips_mdebug_frame_base_sniffer, non_heuristic_proc_desc)
(compare_pdr_entries, the_bfd, mips16_fetch_instruction): Delete.
(mips_gdbarch_init): Do not append mdebug sniffers.
(PROC_LOW_ADDR, PROC_HIGH_ADDR, PROC_FRAME_OFFSET)
(PROC_FRAME_REG, PROC_FRAME_ADJUST, PROC_REG_MASK)
(PROC_FREG_MASK, PROC_REG_OFFSET, PROC_FREG_OFFSET, PROC_PC_REG)
(PROC_SYMBOL, _PROC_MAGIC_): Delete.
* Makefile.in: Update dependencies.
2004-10-30 Andrew Cagney <cagney@gnu.org>
+ * config/mips/embed.mt (TDEPFILES): Add mips-mdebug-tdep.o.
+ * mips-mdebug-tdep.h, mips-mdebug-tdep.c: New files.
+ * mips-tdep.c (mips_mdebug_frame_cache)
+ (mips_mdebug_frame_this_id, mips_mdebug_frame_prev_register)
+ (mips_mdebug_frame_unwind, mips_mdebug_frame_sniffer)
+ (mips_mdebug_frame_base_address, mips_mdebug_frame_sniffer)
+ (mips_mdebug_frame_base_sniffer, non_heuristic_proc_desc)
+ (compare_pdr_entries, the_bfd, mips16_fetch_instruction): Delete.
+ (mips_gdbarch_init): Do not append mdebug sniffers.
+ (PROC_LOW_ADDR, PROC_HIGH_ADDR, PROC_FRAME_OFFSET)
+ (PROC_FRAME_REG, PROC_FRAME_ADJUST, PROC_REG_MASK)
+ (PROC_FREG_MASK, PROC_REG_OFFSET, PROC_FREG_OFFSET, PROC_PC_REG)
+ (PROC_SYMBOL, _PROC_MAGIC_): Delete.
+ * Makefile.in: Update dependencies.
+
* mips-tdep.h (mips_abi_regsize): Declare.
* mips-tdep.c (mips_abi_regsize): Make non-static.
macrotab_h = macrotab.h
main_h = main.h
memattr_h = memattr.h
+mips_mdebug_tdep_h = mips-mdebug-tdep.h
mipsnbsd_tdep_h = mipsnbsd-tdep.h
mips_tdep_h = mips-tdep.h
monitor_h = monitor.h
$(osabi_h) $(target_h) $(gdb_assert_h) $(gdb_string_h) \
$(i386_tdep_h) $(i387_tdep_h) $(solib_svr4_h)
i386-sol2-nat.o: i386-sol2-nat.c $(defs_h) $(regcache_h) $(gregset_h) \
- i386v4-nat.c
+ $(amd64_nat_h) $(amd64_tdep_h) $(i386v4_nat_c)
i386-sol2-tdep.o: i386-sol2-tdep.c $(defs_h) $(value_h) $(osabi_h) \
$(gdb_string_h) $(i386_tdep_h) $(solib_svr4_h)
i386-stub.o: i386-stub.c
$(target_h)
minsyms.o: minsyms.c $(defs_h) $(gdb_string_h) $(symtab_h) $(bfd_h) \
$(symfile_h) $(objfiles_h) $(demangle_h) $(value_h) $(cp_abi_h)
-mips64obsd-nat.o: mips64obsd-tdep.c $(defs_h) $(inferior_h) $(regcache_h) \
+mips64obsd-nat.o: mips64obsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \
$(target_h) $(inf_ptrace_h)
mips64obsd-tdep.o: mips64obsd-tdep.c $(defs_h) $(osabi_h) $(regcache_h) \
$(regset_h) $(gdb_string_h) $(mips_tdep_h) $(solib_svr4_h)
mips-linux-tdep.o: mips-linux-tdep.c $(defs_h) $(gdbcore_h) $(target_h) \
$(solib_svr4_h) $(osabi_h) $(mips_tdep_h) $(gdb_string_h) \
$(gdb_assert_h) $(frame_h) $(trad_frame_h) $(tramp_frame_h)
+mips-mdebug-tdep.o: mips-mdebug-tdep.c $(defs_h) $(frame_h) $(mips_tdep_h) \
+ $(trad_frame_h) $(block_h) $(symtab_h) $(objfiles_h) $(elf_mips_h) \
+ $(elf_bfd_h) $(gdb_assert_h) $(frame_unwind_h) $(frame_base_h) \
+ $(mips_mdebug_tdep_h)
mipsnbsd-nat.o: mipsnbsd-nat.c $(defs_h) $(inferior_h) $(regcache_h) \
$(mips_tdep_h) $(mipsnbsd_tdep_h)
mipsnbsd-tdep.o: mipsnbsd-tdep.c $(defs_h) $(gdbcore_h) $(regcache_h) \
- $(target_h) $(value_h) $(osabi_h) $(nbsd_tdep_h) $(mipsnbsd_tdep_h) \
- $(mips_tdep_h) $(solib_svr4_h)
+ $(target_h) $(value_h) $(osabi_h) $(gdb_string_h) $(nbsd_tdep_h) \
+ $(mipsnbsd_tdep_h) $(mips_tdep_h) $(solib_svr4_h)
mipsread.o: mipsread.c $(defs_h) $(gdb_string_h) $(bfd_h) $(symtab_h) \
$(objfiles_h) $(buildsym_h) $(stabsread_h) $(coff_sym_h) \
$(coff_internal_h) $(coff_ecoff_h) $(libcoff_h) $(libecoff_h) \
$(floatformat_h)
mipsv4-nat.o: mipsv4-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) $(target_h) \
$(regcache_h) $(gregset_h)
-mn10300-tdep.o: mn10300-tdep.c $(defs_h) $(frame_h) $(inferior_h) \
- $(target_h) $(value_h) $(bfd_h) $(gdb_string_h) $(gdbcore_h) \
- $(objfiles_h) $(regcache_h) $(arch_utils_h) $(gdb_assert_h) \
- $(dis_asm_h)
+mn10300-tdep.o: mn10300-tdep.c $(defs_h) $(arch_utils_h) $(dis_asm_h) \
+ $(gdbtypes_h) $(regcache_h) $(gdb_string_h) $(gdb_assert_h) \
+ $(frame_h) $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) \
+ $(symtab_h) $(dwarf2_frame_h) $(regcache_h)
monitor.o: monitor.c $(defs_h) $(gdbcore_h) $(target_h) $(gdb_string_h) \
$(command_h) $(serial_h) $(monitor_h) $(gdbcmd_h) $(inferior_h) \
$(gdb_regex_h) $(srec_h) $(regcache_h)
-TDEPFILES= mips-tdep.o remote-mips.o
+TDEPFILES= mips-tdep.o remote-mips.o mips-mdebug-tdep.o
DEPRECATED_TM_FILE= tm-mips.h
SIM_OBS = remote-sim.o
SIM = ../sim/mips/libsim.a
--- /dev/null
+/* Target-dependent code for the MDEBUG MIPS architecture, for GDB,
+ the GNU Debugger.
+
+ Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
+ 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "mips-tdep.h"
+#include "trad-frame.h"
+#include "block.h"
+#include "symtab.h"
+#include "objfiles.h"
+#include "elf/mips.h"
+#include "elf-bfd.h"
+#include "gdb_assert.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "mips-mdebug-tdep.h"
+
+#define PROC_LOW_ADDR(proc) ((proc)->pdr.adr) /* least address */
+#define PROC_HIGH_ADDR(proc) ((proc)->high_addr) /* upper address bound */
+#define PROC_FRAME_OFFSET(proc) ((proc)->pdr.frameoffset)
+#define PROC_FRAME_REG(proc) ((proc)->pdr.framereg)
+#define PROC_FRAME_ADJUST(proc) ((proc)->frame_adjust)
+#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)
+/* FIXME drow/2002-06-10: If a pointer on the host is bigger than a long,
+ this will corrupt pdr.iline. Fortunately we don't use it. */
+#define PROC_SYMBOL(proc) (*(struct symbol**)&(proc)->pdr.isym)
+#define _PROC_MAGIC_ 0x0F0F0F0F
+
+struct mips_objfile_private
+{
+ bfd_size_type size;
+ char *contents;
+};
+
+/* Global used to communicate between non_heuristic_proc_desc and
+ compare_pdr_entries within qsort (). */
+static bfd *the_bfd;
+
+static int
+compare_pdr_entries (const void *a, const void *b)
+{
+ CORE_ADDR lhs = bfd_get_32 (the_bfd, (bfd_byte *) a);
+ CORE_ADDR rhs = bfd_get_32 (the_bfd, (bfd_byte *) b);
+
+ if (lhs < rhs)
+ return -1;
+ else if (lhs == rhs)
+ return 0;
+ else
+ return 1;
+}
+
+static const struct objfile_data *mips_pdr_data;
+
+static mips_extra_func_info_t
+non_heuristic_proc_desc (CORE_ADDR pc, CORE_ADDR *addrptr)
+{
+ CORE_ADDR startaddr;
+ mips_extra_func_info_t proc_desc;
+ struct block *b = block_for_pc (pc);
+ struct symbol *sym;
+ struct obj_section *sec;
+ struct mips_objfile_private *priv;
+
+ find_pc_partial_function (pc, NULL, &startaddr, NULL);
+ if (addrptr)
+ *addrptr = startaddr;
+
+ priv = NULL;
+
+ sec = find_pc_section (pc);
+ if (sec != NULL)
+ {
+ priv = (struct mips_objfile_private *) objfile_data (sec->objfile, mips_pdr_data);
+
+ /* Search the ".pdr" section generated by GAS. This includes most of
+ the information normally found in ECOFF PDRs. */
+
+ the_bfd = sec->objfile->obfd;
+ if (priv == NULL
+ && (the_bfd->format == bfd_object
+ && bfd_get_flavour (the_bfd) == bfd_target_elf_flavour
+ && elf_elfheader (the_bfd)->e_ident[EI_CLASS] == ELFCLASS64))
+ {
+ /* Right now GAS only outputs the address as a four-byte sequence.
+ This means that we should not bother with this method on 64-bit
+ targets (until that is fixed). */
+
+ priv = obstack_alloc (&sec->objfile->objfile_obstack,
+ sizeof (struct mips_objfile_private));
+ priv->size = 0;
+ set_objfile_data (sec->objfile, mips_pdr_data, priv);
+ }
+ else if (priv == NULL)
+ {
+ asection *bfdsec;
+
+ priv = obstack_alloc (&sec->objfile->objfile_obstack,
+ sizeof (struct mips_objfile_private));
+
+ bfdsec = bfd_get_section_by_name (sec->objfile->obfd, ".pdr");
+ if (bfdsec != NULL)
+ {
+ priv->size = bfd_section_size (sec->objfile->obfd, bfdsec);
+ priv->contents = obstack_alloc (&sec->objfile->objfile_obstack,
+ priv->size);
+ bfd_get_section_contents (sec->objfile->obfd, bfdsec,
+ priv->contents, 0, priv->size);
+
+ /* In general, the .pdr section is sorted. However, in the
+ presence of multiple code sections (and other corner cases)
+ it can become unsorted. Sort it so that we can use a faster
+ binary search. */
+ qsort (priv->contents, priv->size / 32, 32,
+ compare_pdr_entries);
+ }
+ else
+ priv->size = 0;
+
+ set_objfile_data (sec->objfile, mips_pdr_data, priv);
+ }
+ the_bfd = NULL;
+
+ if (priv->size != 0)
+ {
+ int low, mid, high;
+ char *ptr;
+ CORE_ADDR pdr_pc;
+
+ low = 0;
+ high = priv->size / 32;
+
+ /* We've found a .pdr section describing this objfile. We want to
+ find the entry which describes this code address. The .pdr
+ information is not very descriptive; we have only a function
+ start address. We have to look for the closest entry, because
+ the local symbol at the beginning of this function may have
+ been stripped - so if we ask the symbol table for the start
+ address we may get a preceding global function. */
+
+ /* First, find the last .pdr entry starting at or before PC. */
+ do
+ {
+ mid = (low + high) / 2;
+
+ ptr = priv->contents + mid * 32;
+ pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
+ pdr_pc += ANOFFSET (sec->objfile->section_offsets,
+ SECT_OFF_TEXT (sec->objfile));
+
+ if (pdr_pc > pc)
+ high = mid;
+ else
+ low = mid + 1;
+ }
+ while (low != high);
+
+ /* Both low and high point one past the PDR of interest. If
+ both are zero, that means this PC is before any region
+ covered by a PDR, i.e. pdr_pc for the first PDR entry is
+ greater than PC. */
+ if (low > 0)
+ {
+ ptr = priv->contents + (low - 1) * 32;
+ pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
+ pdr_pc += ANOFFSET (sec->objfile->section_offsets,
+ SECT_OFF_TEXT (sec->objfile));
+ }
+
+ /* We don't have a range, so we have no way to know for sure
+ whether we're in the correct PDR or a PDR for a preceding
+ function and the current function was a stripped local
+ symbol. But if the PDR's PC is at least as great as the
+ best guess from the symbol table, assume that it does cover
+ the right area; if a .pdr section is present at all then
+ nearly every function will have an entry. The biggest exception
+ will be the dynamic linker stubs; conveniently these are
+ placed before .text instead of after. */
+
+ if (pc >= pdr_pc && pdr_pc >= startaddr)
+ {
+ struct symbol *sym = find_pc_function (pc);
+
+ if (addrptr)
+ *addrptr = pdr_pc;
+
+ /* Fill in what we need of the proc_desc. */
+ proc_desc = (mips_extra_func_info_t)
+ obstack_alloc (&sec->objfile->objfile_obstack,
+ sizeof (struct mips_extra_func_info));
+ PROC_LOW_ADDR (proc_desc) = pdr_pc;
+
+ /* Only used for dummy frames. */
+ PROC_HIGH_ADDR (proc_desc) = 0;
+
+ PROC_FRAME_OFFSET (proc_desc)
+ = bfd_get_32 (sec->objfile->obfd, ptr + 20);
+ PROC_FRAME_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
+ ptr + 24);
+ PROC_FRAME_ADJUST (proc_desc) = 0;
+ PROC_REG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
+ ptr + 4);
+ PROC_FREG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
+ ptr + 12);
+ PROC_REG_OFFSET (proc_desc) = bfd_get_32 (sec->objfile->obfd,
+ ptr + 8);
+ PROC_FREG_OFFSET (proc_desc)
+ = bfd_get_32 (sec->objfile->obfd, ptr + 16);
+ PROC_PC_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
+ ptr + 28);
+ proc_desc->pdr.isym = (long) sym;
+
+ return proc_desc;
+ }
+ }
+ }
+
+ if (b == NULL)
+ return NULL;
+
+ if (startaddr > BLOCK_START (b))
+ {
+ /* 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. */
+ return NULL;
+ }
+
+ sym = lookup_symbol (MIPS_EFI_SYMBOL_NAME, b, LABEL_DOMAIN, 0, NULL);
+
+ /* If we never found a PDR for this function in symbol reading, then
+ examine prologues to find the information. */
+ if (sym)
+ {
+ proc_desc = (mips_extra_func_info_t) SYMBOL_VALUE (sym);
+ if (PROC_FRAME_REG (proc_desc) == -1)
+ return NULL;
+ else
+ return proc_desc;
+ }
+ else
+ return NULL;
+}
+
+struct mips_frame_cache
+{
+ CORE_ADDR base;
+ struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct mips_frame_cache *
+mips_mdebug_frame_cache (struct frame_info *next_frame, void **this_cache)
+{
+ CORE_ADDR startaddr = 0;
+ mips_extra_func_info_t proc_desc;
+ struct mips_frame_cache *cache;
+ struct gdbarch *gdbarch = get_frame_arch (next_frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ /* r0 bit means kernel trap */
+ int kernel_trap;
+ /* What registers have been saved? Bitmasks. */
+ unsigned long gen_mask, float_mask;
+
+ if ((*this_cache) != NULL)
+ return (*this_cache);
+ cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache);
+ (*this_cache) = cache;
+ cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+ /* Get the mdebug proc descriptor. */
+ proc_desc = non_heuristic_proc_desc (frame_pc_unwind (next_frame),
+ &startaddr);
+ /* Must be true. This is only called when the sniffer detected a
+ proc descriptor. */
+ gdb_assert (proc_desc != NULL);
+
+ /* Extract the frame's base. */
+ cache->base = (frame_unwind_register_signed (next_frame, NUM_REGS + PROC_FRAME_REG (proc_desc))
+ + PROC_FRAME_OFFSET (proc_desc) - PROC_FRAME_ADJUST (proc_desc));
+
+ kernel_trap = PROC_REG_MASK (proc_desc) & 1;
+ gen_mask = kernel_trap ? 0xFFFFFFFF : PROC_REG_MASK (proc_desc);
+ float_mask = kernel_trap ? 0xFFFFFFFF : PROC_FREG_MASK (proc_desc);
+
+ /* Must be true. The in_prologue case is left for the heuristic
+ unwinder. This is always used on kernel traps. */
+ gdb_assert (!in_prologue (frame_pc_unwind (next_frame), PROC_LOW_ADDR (proc_desc))
+ || kernel_trap);
+
+ /* Fill in the offsets for the registers which gen_mask says were
+ saved. */
+ {
+ CORE_ADDR reg_position = (cache->base + PROC_REG_OFFSET (proc_desc));
+ int ireg;
+
+ for (ireg = MIPS_NUMREGS - 1; gen_mask; --ireg, gen_mask <<= 1)
+ if (gen_mask & 0x80000000)
+ {
+ cache->saved_regs[NUM_REGS + ireg].addr = reg_position;
+ reg_position -= mips_abi_regsize (gdbarch);
+ }
+ }
+
+ /* Fill in the offsets for the registers which float_mask says were
+ saved. */
+ {
+ CORE_ADDR reg_position = (cache->base
+ + PROC_FREG_OFFSET (proc_desc));
+ int ireg;
+ /* Fill in the offsets for the float registers which float_mask
+ says were saved. */
+ for (ireg = MIPS_NUMREGS - 1; float_mask; --ireg, float_mask <<= 1)
+ if (float_mask & 0x80000000)
+ {
+ if (mips_abi_regsize (gdbarch) == 4
+ && TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ {
+ /* On a big endian 32 bit ABI, floating point registers
+ are paired to form doubles such that the most
+ significant part is in $f[N+1] and the least
+ significant in $f[N] vis: $f[N+1] ||| $f[N]. The
+ registers are also spilled as a pair and stored as a
+ double.
+
+ When little-endian the least significant part is
+ stored first leading to the memory order $f[N] and
+ then $f[N+1].
+
+ Unfortunately, when big-endian the most significant
+ part of the double is stored first, and the least
+ significant is stored second. This leads to the
+ registers being ordered in memory as firt $f[N+1] and
+ then $f[N].
+
+ For the big-endian case make certain that the
+ addresses point at the correct (swapped) locations
+ $f[N] and $f[N+1] pair (keep in mind that
+ reg_position is decremented each time through the
+ loop). */
+ if ((ireg & 1))
+ cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
+ .addr = reg_position - mips_abi_regsize (gdbarch);
+ else
+ cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
+ .addr = reg_position + mips_abi_regsize (gdbarch);
+ }
+ else
+ cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
+ .addr = reg_position;
+ reg_position -= mips_abi_regsize (gdbarch);
+ }
+
+ cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc]
+ = cache->saved_regs[NUM_REGS + MIPS_RA_REGNUM];
+ }
+
+ /* SP_REGNUM, contains the value and not the address. */
+ trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base);
+
+ return (*this_cache);
+}
+
+static void
+mips_mdebug_frame_this_id (struct frame_info *next_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
+ this_cache);
+ (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame));
+}
+
+static void
+mips_mdebug_frame_prev_register (struct frame_info *next_frame,
+ void **this_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *valuep)
+{
+ struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
+ this_cache);
+ trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
+ optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static const struct frame_unwind mips_mdebug_frame_unwind =
+{
+ NORMAL_FRAME,
+ mips_mdebug_frame_this_id,
+ mips_mdebug_frame_prev_register
+};
+
+static const struct frame_unwind *
+mips_mdebug_frame_sniffer (struct frame_info *next_frame)
+{
+ CORE_ADDR pc = frame_pc_unwind (next_frame);
+ CORE_ADDR startaddr = 0;
+ mips_extra_func_info_t proc_desc;
+ int kernel_trap;
+
+ /* Don't use this on MIPS16. */
+ if (mips_pc_is_mips16 (pc))
+ return NULL;
+
+ /* Only use the mdebug frame unwinder on mdebug frames where all the
+ registers have been saved. Leave hard cases such as no mdebug or
+ in prologue for the heuristic unwinders. */
+
+ proc_desc = non_heuristic_proc_desc (pc, &startaddr);
+ if (proc_desc == NULL)
+ return NULL;
+
+ /* Not sure exactly what kernel_trap means, but if it means the
+ kernel saves the registers without a prologue doing it, we better
+ not examine the prologue to see whether registers have been saved
+ yet. */
+ kernel_trap = PROC_REG_MASK (proc_desc) & 1;
+ if (kernel_trap)
+ return &mips_mdebug_frame_unwind;
+
+ /* In any frame other than the innermost or a frame interrupted by a
+ signal, we assume that all registers have been saved. This
+ assumes that all register saves in a function happen before the
+ first function call. */
+ if (!in_prologue (pc, PROC_LOW_ADDR (proc_desc)))
+ return &mips_mdebug_frame_unwind;
+
+ return NULL;
+}
+
+static CORE_ADDR
+mips_mdebug_frame_base_address (struct frame_info *next_frame,
+ void **this_cache)
+{
+ struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
+ this_cache);
+ return info->base;
+}
+
+static const struct frame_base mips_mdebug_frame_base = {
+ &mips_mdebug_frame_unwind,
+ mips_mdebug_frame_base_address,
+ mips_mdebug_frame_base_address,
+ mips_mdebug_frame_base_address
+};
+
+static const struct frame_base *
+mips_mdebug_frame_base_sniffer (struct frame_info *next_frame)
+{
+ if (mips_mdebug_frame_sniffer (next_frame) != NULL)
+ return &mips_mdebug_frame_base;
+ else
+ return NULL;
+}
+
+void
+mips_mdebug_append_sniffers (struct gdbarch *gdbarch)
+{
+ frame_unwind_append_sniffer (gdbarch, mips_mdebug_frame_sniffer);
+ frame_base_append_sniffer (gdbarch, mips_mdebug_frame_base_sniffer);
+}
+
+
+extern void _initialize_mips_mdebug_tdep (void);
+void
+_initialize_mips_mdebug_tdep (void)
+{
+ mips_pdr_data = register_objfile_data ();
+}
--- /dev/null
+/* Target-dependent code for the MDEBUG MIPS architecture, for GDB,
+ the GNU Debugger.
+
+ Copyright 2004 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef MIPS_MDEBUG_TDEP_H
+#define MIPS_MDEBUG_TDEP_H
+
+extern void mips_mdebug_append_sniffers (struct gdbarch *gdbarch);
+
+#endif
#define VM_MIN_ADDRESS (CORE_ADDR)0x400000
-struct mips_frame_cache;
-static mips_extra_func_info_t non_heuristic_proc_desc (CORE_ADDR pc,
- CORE_ADDR *addrptr);
-
static CORE_ADDR heuristic_proc_start (CORE_ADDR);
static CORE_ADDR read_next_frame_reg (struct frame_info *, int);
static unsigned int heuristic_fence_post = 0;
-#define PROC_LOW_ADDR(proc) ((proc)->pdr.adr) /* least address */
-#define PROC_HIGH_ADDR(proc) ((proc)->high_addr) /* upper address bound */
-#define PROC_FRAME_OFFSET(proc) ((proc)->pdr.frameoffset)
-#define PROC_FRAME_REG(proc) ((proc)->pdr.framereg)
-#define PROC_FRAME_ADJUST(proc) ((proc)->frame_adjust)
-#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)
-/* FIXME drow/2002-06-10: If a pointer on the host is bigger than a long,
- this will corrupt pdr.iline. Fortunately we don't use it. */
-#define PROC_SYMBOL(proc) (*(struct symbol**)&(proc)->pdr.isym)
-#define _PROC_MAGIC_ 0x0F0F0F0F
-
/* Number of bytes of storage in the actual machine representation for
register N. NOTE: This defines the pseudo register type so need to
rebuild the architecture vector. */
return extract_unsigned_integer (buf, instlen);
}
-static ULONGEST
-mips16_fetch_instruction (CORE_ADDR addr)
-{
- char buf[MIPS16_INSN_SIZE];
- int status;
-
- addr = unmake_mips16_addr (addr);
- status = deprecated_read_memory_nobpt (addr, buf, sizeof (buf));
- if (status)
- memory_error (status, addr);
- return extract_unsigned_integer (buf, sizeof (buf));
-}
-
/* These the fields of 32 bit mips instructions */
#define mips32_op(x) (x >> 26)
#define itype_op(x) (x >> 26)
struct trad_frame_saved_reg *saved_regs;
};
-
-static struct mips_frame_cache *
-mips_mdebug_frame_cache (struct frame_info *next_frame, void **this_cache)
-{
- CORE_ADDR startaddr = 0;
- mips_extra_func_info_t proc_desc;
- struct mips_frame_cache *cache;
- struct gdbarch *gdbarch = get_frame_arch (next_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- /* r0 bit means kernel trap */
- int kernel_trap;
- /* What registers have been saved? Bitmasks. */
- unsigned long gen_mask, float_mask;
-
- if ((*this_cache) != NULL)
- return (*this_cache);
- cache = FRAME_OBSTACK_ZALLOC (struct mips_frame_cache);
- (*this_cache) = cache;
- cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
-
- /* Get the mdebug proc descriptor. */
- proc_desc = non_heuristic_proc_desc (frame_pc_unwind (next_frame),
- &startaddr);
- /* Must be true. This is only called when the sniffer detected a
- proc descriptor. */
- gdb_assert (proc_desc != NULL);
-
- /* Extract the frame's base. */
- cache->base = (frame_unwind_register_signed (next_frame, NUM_REGS + PROC_FRAME_REG (proc_desc))
- + PROC_FRAME_OFFSET (proc_desc) - PROC_FRAME_ADJUST (proc_desc));
-
- kernel_trap = PROC_REG_MASK (proc_desc) & 1;
- gen_mask = kernel_trap ? 0xFFFFFFFF : PROC_REG_MASK (proc_desc);
- float_mask = kernel_trap ? 0xFFFFFFFF : PROC_FREG_MASK (proc_desc);
-
- /* Must be true. The in_prologue case is left for the heuristic
- unwinder. This is always used on kernel traps. */
- gdb_assert (!in_prologue (frame_pc_unwind (next_frame), PROC_LOW_ADDR (proc_desc))
- || kernel_trap);
-
- /* Fill in the offsets for the registers which gen_mask says were
- saved. */
- {
- CORE_ADDR reg_position = (cache->base + PROC_REG_OFFSET (proc_desc));
- int ireg;
-
- for (ireg = MIPS_NUMREGS - 1; gen_mask; --ireg, gen_mask <<= 1)
- if (gen_mask & 0x80000000)
- {
- cache->saved_regs[NUM_REGS + ireg].addr = reg_position;
- reg_position -= mips_abi_regsize (gdbarch);
- }
- }
-
- /* The MIPS16 entry instruction saves $s0 and $s1 in the reverse
- order of that normally used by gcc. Therefore, we have to fetch
- the first instruction of the function, and if it's an entry
- instruction that saves $s0 or $s1, correct their saved addresses. */
- if (mips_pc_is_mips16 (PROC_LOW_ADDR (proc_desc)))
- {
- ULONGEST inst = mips16_fetch_instruction (PROC_LOW_ADDR (proc_desc));
- if ((inst & 0xf81f) == 0xe809 && (inst & 0x700) != 0x700)
- /* entry */
- {
- int reg;
- int sreg_count = (inst >> 6) & 3;
-
- /* Check if the ra register was pushed on the stack. */
- CORE_ADDR reg_position = (cache->base
- + PROC_REG_OFFSET (proc_desc));
- if (inst & 0x20)
- reg_position -= mips_abi_regsize (gdbarch);
-
- /* Check if the s0 and s1 registers were pushed on the
- stack. */
- /* NOTE: cagney/2004-02-08: Huh? This is doing no such
- check. */
- for (reg = 16; reg < sreg_count + 16; reg++)
- {
- cache->saved_regs[NUM_REGS + reg].addr = reg_position;
- reg_position -= mips_abi_regsize (gdbarch);
- }
- }
- }
-
- /* Fill in the offsets for the registers which float_mask says were
- saved. */
- {
- CORE_ADDR reg_position = (cache->base
- + PROC_FREG_OFFSET (proc_desc));
- int ireg;
- /* Fill in the offsets for the float registers which float_mask
- says were saved. */
- for (ireg = MIPS_NUMREGS - 1; float_mask; --ireg, float_mask <<= 1)
- if (float_mask & 0x80000000)
- {
- if (mips_abi_regsize (gdbarch) == 4
- && TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- {
- /* On a big endian 32 bit ABI, floating point registers
- are paired to form doubles such that the most
- significant part is in $f[N+1] and the least
- significant in $f[N] vis: $f[N+1] ||| $f[N]. The
- registers are also spilled as a pair and stored as a
- double.
-
- When little-endian the least significant part is
- stored first leading to the memory order $f[N] and
- then $f[N+1].
-
- Unfortunately, when big-endian the most significant
- part of the double is stored first, and the least
- significant is stored second. This leads to the
- registers being ordered in memory as firt $f[N+1] and
- then $f[N].
-
- For the big-endian case make certain that the
- addresses point at the correct (swapped) locations
- $f[N] and $f[N+1] pair (keep in mind that
- reg_position is decremented each time through the
- loop). */
- if ((ireg & 1))
- cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
- .addr = reg_position - mips_abi_regsize (gdbarch);
- else
- cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
- .addr = reg_position + mips_abi_regsize (gdbarch);
- }
- else
- cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->fp0 + ireg]
- .addr = reg_position;
- reg_position -= mips_abi_regsize (gdbarch);
- }
-
- cache->saved_regs[NUM_REGS + mips_regnum (current_gdbarch)->pc]
- = cache->saved_regs[NUM_REGS + MIPS_RA_REGNUM];
- }
-
- /* SP_REGNUM, contains the value and not the address. */
- trad_frame_set_value (cache->saved_regs, NUM_REGS + MIPS_SP_REGNUM, cache->base);
-
- return (*this_cache);
-}
-
-static void
-mips_mdebug_frame_this_id (struct frame_info *next_frame, void **this_cache,
- struct frame_id *this_id)
-{
- struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
- this_cache);
- (*this_id) = frame_id_build (info->base, frame_func_unwind (next_frame));
-}
-
-static void
-mips_mdebug_frame_prev_register (struct frame_info *next_frame,
- void **this_cache,
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realnump, void *valuep)
-{
- struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
- this_cache);
- trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
- optimizedp, lvalp, addrp, realnump, valuep);
-}
-
-static const struct frame_unwind mips_mdebug_frame_unwind =
-{
- NORMAL_FRAME,
- mips_mdebug_frame_this_id,
- mips_mdebug_frame_prev_register
-};
-
-static const struct frame_unwind *
-mips_mdebug_frame_sniffer (struct frame_info *next_frame)
-{
- CORE_ADDR pc = frame_pc_unwind (next_frame);
- CORE_ADDR startaddr = 0;
- mips_extra_func_info_t proc_desc;
- int kernel_trap;
-
- /* Only use the mdebug frame unwinder on mdebug frames where all the
- registers have been saved. Leave hard cases such as no mdebug or
- in prologue for the heuristic unwinders. */
-
- proc_desc = non_heuristic_proc_desc (pc, &startaddr);
- if (proc_desc == NULL)
- return NULL;
-
- /* Not sure exactly what kernel_trap means, but if it means the
- kernel saves the registers without a prologue doing it, we better
- not examine the prologue to see whether registers have been saved
- yet. */
- kernel_trap = PROC_REG_MASK (proc_desc) & 1;
- if (kernel_trap)
- return &mips_mdebug_frame_unwind;
-
- /* In any frame other than the innermost or a frame interrupted by a
- signal, we assume that all registers have been saved. This
- assumes that all register saves in a function happen before the
- first function call. */
- if (!in_prologue (pc, PROC_LOW_ADDR (proc_desc)))
- return &mips_mdebug_frame_unwind;
-
- return NULL;
-}
-
-static CORE_ADDR
-mips_mdebug_frame_base_address (struct frame_info *next_frame,
- void **this_cache)
-{
- struct mips_frame_cache *info = mips_mdebug_frame_cache (next_frame,
- this_cache);
- return info->base;
-}
-
-static const struct frame_base mips_mdebug_frame_base = {
- &mips_mdebug_frame_unwind,
- mips_mdebug_frame_base_address,
- mips_mdebug_frame_base_address,
- mips_mdebug_frame_base_address
-};
-
-static const struct frame_base *
-mips_mdebug_frame_base_sniffer (struct frame_info *next_frame)
-{
- if (mips_mdebug_frame_sniffer (next_frame) != NULL)
- return &mips_mdebug_frame_base;
- else
- return NULL;
-}
-
/* Set a register's saved stack address in temp_saved_regs. If an
address has already been set for this register, do nothing; this
way we will only recognize the first save of a given register in a
char *contents;
};
-/* Global used to communicate between non_heuristic_proc_desc and
- compare_pdr_entries within qsort (). */
-static bfd *the_bfd;
-
-static int
-compare_pdr_entries (const void *a, const void *b)
-{
- CORE_ADDR lhs = bfd_get_32 (the_bfd, (bfd_byte *) a);
- CORE_ADDR rhs = bfd_get_32 (the_bfd, (bfd_byte *) b);
-
- if (lhs < rhs)
- return -1;
- else if (lhs == rhs)
- return 0;
- else
- return 1;
-}
-
-static mips_extra_func_info_t
-non_heuristic_proc_desc (CORE_ADDR pc, CORE_ADDR *addrptr)
-{
- CORE_ADDR startaddr;
- mips_extra_func_info_t proc_desc;
- struct block *b = block_for_pc (pc);
- struct symbol *sym;
- struct obj_section *sec;
- struct mips_objfile_private *priv;
-
- find_pc_partial_function (pc, NULL, &startaddr, NULL);
- if (addrptr)
- *addrptr = startaddr;
-
- priv = NULL;
-
- sec = find_pc_section (pc);
- if (sec != NULL)
- {
- priv = (struct mips_objfile_private *) objfile_data (sec->objfile, mips_pdr_data);
-
- /* Search the ".pdr" section generated by GAS. This includes most of
- the information normally found in ECOFF PDRs. */
-
- the_bfd = sec->objfile->obfd;
- if (priv == NULL
- && (the_bfd->format == bfd_object
- && bfd_get_flavour (the_bfd) == bfd_target_elf_flavour
- && elf_elfheader (the_bfd)->e_ident[EI_CLASS] == ELFCLASS64))
- {
- /* Right now GAS only outputs the address as a four-byte sequence.
- This means that we should not bother with this method on 64-bit
- targets (until that is fixed). */
-
- priv = obstack_alloc (&sec->objfile->objfile_obstack,
- sizeof (struct mips_objfile_private));
- priv->size = 0;
- set_objfile_data (sec->objfile, mips_pdr_data, priv);
- }
- else if (priv == NULL)
- {
- asection *bfdsec;
-
- priv = obstack_alloc (&sec->objfile->objfile_obstack,
- sizeof (struct mips_objfile_private));
-
- bfdsec = bfd_get_section_by_name (sec->objfile->obfd, ".pdr");
- if (bfdsec != NULL)
- {
- priv->size = bfd_section_size (sec->objfile->obfd, bfdsec);
- priv->contents = obstack_alloc (&sec->objfile->objfile_obstack,
- priv->size);
- bfd_get_section_contents (sec->objfile->obfd, bfdsec,
- priv->contents, 0, priv->size);
-
- /* In general, the .pdr section is sorted. However, in the
- presence of multiple code sections (and other corner cases)
- it can become unsorted. Sort it so that we can use a faster
- binary search. */
- qsort (priv->contents, priv->size / 32, 32,
- compare_pdr_entries);
- }
- else
- priv->size = 0;
-
- set_objfile_data (sec->objfile, mips_pdr_data, priv);
- }
- the_bfd = NULL;
-
- if (priv->size != 0)
- {
- int low, mid, high;
- char *ptr;
- CORE_ADDR pdr_pc;
-
- low = 0;
- high = priv->size / 32;
-
- /* We've found a .pdr section describing this objfile. We want to
- find the entry which describes this code address. The .pdr
- information is not very descriptive; we have only a function
- start address. We have to look for the closest entry, because
- the local symbol at the beginning of this function may have
- been stripped - so if we ask the symbol table for the start
- address we may get a preceding global function. */
-
- /* First, find the last .pdr entry starting at or before PC. */
- do
- {
- mid = (low + high) / 2;
-
- ptr = priv->contents + mid * 32;
- pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
- pdr_pc += ANOFFSET (sec->objfile->section_offsets,
- SECT_OFF_TEXT (sec->objfile));
-
- if (pdr_pc > pc)
- high = mid;
- else
- low = mid + 1;
- }
- while (low != high);
-
- /* Both low and high point one past the PDR of interest. If
- both are zero, that means this PC is before any region
- covered by a PDR, i.e. pdr_pc for the first PDR entry is
- greater than PC. */
- if (low > 0)
- {
- ptr = priv->contents + (low - 1) * 32;
- pdr_pc = bfd_get_signed_32 (sec->objfile->obfd, ptr);
- pdr_pc += ANOFFSET (sec->objfile->section_offsets,
- SECT_OFF_TEXT (sec->objfile));
- }
-
- /* We don't have a range, so we have no way to know for sure
- whether we're in the correct PDR or a PDR for a preceding
- function and the current function was a stripped local
- symbol. But if the PDR's PC is at least as great as the
- best guess from the symbol table, assume that it does cover
- the right area; if a .pdr section is present at all then
- nearly every function will have an entry. The biggest exception
- will be the dynamic linker stubs; conveniently these are
- placed before .text instead of after. */
-
- if (pc >= pdr_pc && pdr_pc >= startaddr)
- {
- struct symbol *sym = find_pc_function (pc);
-
- if (addrptr)
- *addrptr = pdr_pc;
-
- /* Fill in what we need of the proc_desc. */
- proc_desc = (mips_extra_func_info_t)
- obstack_alloc (&sec->objfile->objfile_obstack,
- sizeof (struct mips_extra_func_info));
- PROC_LOW_ADDR (proc_desc) = pdr_pc;
-
- /* Only used for dummy frames. */
- PROC_HIGH_ADDR (proc_desc) = 0;
-
- PROC_FRAME_OFFSET (proc_desc)
- = bfd_get_32 (sec->objfile->obfd, ptr + 20);
- PROC_FRAME_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
- ptr + 24);
- PROC_FRAME_ADJUST (proc_desc) = 0;
- PROC_REG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
- ptr + 4);
- PROC_FREG_MASK (proc_desc) = bfd_get_32 (sec->objfile->obfd,
- ptr + 12);
- PROC_REG_OFFSET (proc_desc) = bfd_get_32 (sec->objfile->obfd,
- ptr + 8);
- PROC_FREG_OFFSET (proc_desc)
- = bfd_get_32 (sec->objfile->obfd, ptr + 16);
- PROC_PC_REG (proc_desc) = bfd_get_32 (sec->objfile->obfd,
- ptr + 28);
- proc_desc->pdr.isym = (long) sym;
-
- return proc_desc;
- }
- }
- }
-
- if (b == NULL)
- return NULL;
-
- if (startaddr > BLOCK_START (b))
- {
- /* 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. */
- return NULL;
- }
-
- sym = lookup_symbol (MIPS_EFI_SYMBOL_NAME, b, LABEL_DOMAIN, 0, NULL);
-
- /* If we never found a PDR for this function in symbol reading, then
- examine prologues to find the information. */
- if (sym)
- {
- proc_desc = (mips_extra_func_info_t) SYMBOL_VALUE (sym);
- if (PROC_FRAME_REG (proc_desc) == -1)
- return NULL;
- else
- return proc_desc;
- }
- else
- return NULL;
-}
-
/* MIPS stack frames are almost impenetrable. When execution stops,
we basically have to look at symbol information for the function
that we stopped in, which tells us *which* register (if any) is
/* Unwind the frame. */
frame_unwind_append_sniffer (gdbarch, mips_stub_frame_sniffer);
- frame_unwind_append_sniffer (gdbarch, mips_mdebug_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, mips_insn16_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, mips_insn32_frame_sniffer);
frame_base_append_sniffer (gdbarch, mips_stub_frame_base_sniffer);
- frame_base_append_sniffer (gdbarch, mips_mdebug_frame_base_sniffer);
frame_base_append_sniffer (gdbarch, mips_insn16_frame_base_sniffer);
frame_base_append_sniffer (gdbarch, mips_insn32_frame_base_sniffer);