/* Traditional frame unwind support, for GDB the GNU Debugger.
- Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2003-2020 Free Software Foundation, Inc.
This file is part of GDB.
#include "trad-frame.h"
#include "regcache.h"
#include "frame-unwind.h"
+#include "target.h"
#include "value.h"
+#include "gdbarch.h"
struct trad_frame_cache
{
return this_trad_cache;
}
+/* See trad-frame.h. */
+
+void
+trad_frame_reset_saved_regs (struct gdbarch *gdbarch,
+ struct trad_frame_saved_reg *regs)
+{
+ int numregs = gdbarch_num_cooked_regs (gdbarch);
+ for (int regnum = 0; regnum < numregs; regnum++)
+ {
+ regs[regnum].realreg = regnum;
+ regs[regnum].addr = -1;
+ }
+}
+
+struct trad_frame_saved_reg *
+trad_frame_alloc_saved_regs (struct gdbarch *gdbarch)
+{
+ int numregs = gdbarch_num_cooked_regs (gdbarch);
+ struct trad_frame_saved_reg *this_saved_regs
+ = FRAME_OBSTACK_CALLOC (numregs, struct trad_frame_saved_reg);
+
+ trad_frame_reset_saved_regs (gdbarch, this_saved_regs);
+ return this_saved_regs;
+}
+
/* A traditional frame is unwound by analysing the function prologue
and using the information gathered to track registers. For
non-optimized frames, the technique is reliable (just need to check
struct trad_frame_saved_reg *
trad_frame_alloc_saved_regs (struct frame_info *this_frame)
{
- int regnum;
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- int numregs = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
- struct trad_frame_saved_reg *this_saved_regs
- = FRAME_OBSTACK_CALLOC (numregs, struct trad_frame_saved_reg);
- for (regnum = 0; regnum < numregs; regnum++)
- {
- this_saved_regs[regnum].realreg = regnum;
- this_saved_regs[regnum].addr = -1;
- }
- return this_saved_regs;
+ return trad_frame_alloc_saved_regs (gdbarch);
}
-enum { REG_VALUE = -1, REG_UNKNOWN = -2 };
+enum { TF_REG_VALUE = -1, TF_REG_UNKNOWN = -2 };
int
trad_frame_value_p (struct trad_frame_saved_reg this_saved_regs[], int regnum)
{
- return (this_saved_regs[regnum].realreg == REG_VALUE);
+ return (this_saved_regs[regnum].realreg == TF_REG_VALUE);
}
int
{
/* Make the REALREG invalid, indicating that the ADDR contains the
register's value. */
- this_saved_regs[regnum].realreg = REG_VALUE;
+ this_saved_regs[regnum].realreg = TF_REG_VALUE;
this_saved_regs[regnum].addr = val;
}
+/* See trad-frame.h. */
+
+void
+trad_frame_set_realreg (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum, int realreg)
+{
+ this_saved_regs[regnum].realreg = realreg;
+ this_saved_regs[regnum].addr = -1;
+}
+
+/* See trad-frame.h. */
+
+void
+trad_frame_set_addr (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum, CORE_ADDR addr)
+{
+ this_saved_regs[regnum].realreg = regnum;
+ this_saved_regs[regnum].addr = addr;
+}
+
void
trad_frame_set_reg_value (struct trad_frame_cache *this_trad_cache,
int regnum, LONGEST val)
trad_frame_set_reg_realreg (struct trad_frame_cache *this_trad_cache,
int regnum, int realreg)
{
- this_trad_cache->prev_regs[regnum].realreg = realreg;
- this_trad_cache->prev_regs[regnum].addr = -1;
+ trad_frame_set_realreg (this_trad_cache->prev_regs, regnum, realreg);
}
void
trad_frame_set_reg_addr (struct trad_frame_cache *this_trad_cache,
int regnum, CORE_ADDR addr)
{
- this_trad_cache->prev_regs[regnum].addr = addr;
+ trad_frame_set_addr (this_trad_cache->prev_regs, regnum, addr);
+}
+
+void
+trad_frame_set_reg_regmap (struct trad_frame_cache *this_trad_cache,
+ const struct regcache_map_entry *regmap,
+ CORE_ADDR addr, size_t size)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_trad_cache->this_frame);
+ int offs = 0, count;
+
+ for (; (count = regmap->count) != 0; regmap++)
+ {
+ int regno = regmap->regno;
+ int slot_size = regmap->size;
+
+ if (slot_size == 0 && regno != REGCACHE_MAP_SKIP)
+ slot_size = register_size (gdbarch, regno);
+
+ if (offs + slot_size > size)
+ break;
+
+ if (regno == REGCACHE_MAP_SKIP)
+ offs += count * slot_size;
+ else
+ for (; count--; regno++, offs += slot_size)
+ {
+ /* Mimic the semantics of regcache::transfer_regset if a
+ register slot's size does not match the size of a
+ register.
+
+ If a register slot is larger than a register, assume
+ the register's value is stored in the first N bytes of
+ the slot and ignore the remaining bytes.
+
+ If the register slot is smaller than the register,
+ assume that the slot contains the low N bytes of the
+ register's value. Since trad_frame assumes that
+ registers stored by address are sized according to the
+ register, read the low N bytes and zero-extend them to
+ generate a register value. */
+ if (slot_size >= register_size (gdbarch, regno))
+ trad_frame_set_reg_addr (this_trad_cache, regno, addr + offs);
+ else
+ {
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ gdb_byte buf[slot_size];
+
+ if (target_read_memory (addr + offs, buf, sizeof buf) == 0)
+ {
+ LONGEST val
+ = extract_unsigned_integer (buf, sizeof buf, byte_order);
+ trad_frame_set_reg_value (this_trad_cache, regno, val);
+ }
+ }
+ }
+ }
}
void
int regnum)
{
/* Make the REALREG invalid, indicating that the value is not known. */
- this_saved_regs[regnum].realreg = REG_UNKNOWN;
+ this_saved_regs[regnum].realreg = TF_REG_UNKNOWN;
this_saved_regs[regnum].addr = -1;
}