/* Traditional frame unwind support, for GDB the GNU Debugger.
- Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2003-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include "frame.h"
#include "trad-frame.h"
#include "regcache.h"
+#include "frame-unwind.h"
+#include "target.h"
+#include "value.h"
+#include "gdbarch.h"
+#include "gdbsupport/traits.h"
struct trad_frame_cache
{
- struct frame_info *next_frame;
+ struct frame_info *this_frame;
CORE_ADDR this_base;
- struct trad_frame_saved_reg *prev_regs;
+ trad_frame_saved_reg *prev_regs;
struct frame_id this_id;
};
struct trad_frame_cache *
-trad_frame_cache_zalloc (struct frame_info *next_frame)
+trad_frame_cache_zalloc (struct frame_info *this_frame)
{
struct trad_frame_cache *this_trad_cache;
this_trad_cache = FRAME_OBSTACK_ZALLOC (struct trad_frame_cache);
- this_trad_cache->prev_regs = trad_frame_alloc_saved_regs (next_frame);
- this_trad_cache->next_frame = next_frame;
+ this_trad_cache->prev_regs = trad_frame_alloc_saved_regs (this_frame);
+ this_trad_cache->this_frame = this_frame;
return this_trad_cache;
}
-/* 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
- for all potential instruction sequences). */
+/* See trad-frame.h. */
-struct trad_frame_saved_reg *
-trad_frame_alloc_saved_regs (struct frame_info *next_frame)
+void
+trad_frame_reset_saved_regs (struct gdbarch *gdbarch,
+ trad_frame_saved_reg *regs)
{
- int regnum;
- struct gdbarch *gdbarch = get_frame_arch (next_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;
-}
+ int numregs = gdbarch_num_cooked_regs (gdbarch);
-enum { REG_VALUE = -1, 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);
+ for (int regnum = 0; regnum < numregs; regnum++)
+ regs[regnum].set_realreg (regnum);
}
-int
-trad_frame_addr_p (struct trad_frame_saved_reg this_saved_regs[], int regnum)
+trad_frame_saved_reg *
+trad_frame_alloc_saved_regs (struct gdbarch *gdbarch)
{
- return (this_saved_regs[regnum].realreg >= 0
- && this_saved_regs[regnum].addr != -1);
-}
+#ifdef HAVE_IS_TRIVIALLY_CONSTRUCTIBLE
+ gdb_static_assert (std::is_trivially_constructible<trad_frame_saved_reg>::value);
+#endif
-int
-trad_frame_realreg_p (struct trad_frame_saved_reg this_saved_regs[],
- int regnum)
-{
- return (this_saved_regs[regnum].realreg >= 0
- && this_saved_regs[regnum].addr == -1);
+ int numregs = gdbarch_num_cooked_regs (gdbarch);
+ trad_frame_saved_reg *this_saved_regs
+ = FRAME_OBSTACK_CALLOC (numregs, trad_frame_saved_reg);
+
+ /* For backwards compatibility, initialize all the register values to
+ REALREG, with register 0 stored in 0, register 1 stored in 1 and so
+ on. */
+ trad_frame_reset_saved_regs (gdbarch, this_saved_regs);
+
+ return this_saved_regs;
}
-void
-trad_frame_set_value (struct trad_frame_saved_reg this_saved_regs[],
- int regnum, LONGEST val)
+/* 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
+ for all potential instruction sequences). */
+
+trad_frame_saved_reg *
+trad_frame_alloc_saved_regs (struct frame_info *this_frame)
{
- /* Make the REALREG invalid, indicating that the ADDR contains the
- register's value. */
- this_saved_regs[regnum].realreg = REG_VALUE;
- this_saved_regs[regnum].addr = val;
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+ return trad_frame_alloc_saved_regs (gdbarch);
}
void
{
/* External interface for users of trad_frame_cache
(who cannot access the prev_regs object directly). */
- trad_frame_set_value (this_trad_cache->prev_regs, regnum, val);
+ this_trad_cache->prev_regs[regnum].set_value (val);
}
void
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;
+ this_trad_cache->prev_regs[regnum].set_realreg (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;
+ this_trad_cache->prev_regs[regnum].set_addr (addr);
}
void
-trad_frame_set_unknown (struct trad_frame_saved_reg this_saved_regs[],
- int regnum)
+trad_frame_set_reg_regmap (struct trad_frame_cache *this_trad_cache,
+ const struct regcache_map_entry *regmap,
+ CORE_ADDR addr, size_t size)
{
- /* Make the REALREG invalid, indicating that the value is not known. */
- this_saved_regs[regnum].realreg = REG_UNKNOWN;
- this_saved_regs[regnum].addr = -1;
-}
+ struct gdbarch *gdbarch = get_frame_arch (this_trad_cache->this_frame);
+ int offs = 0, count;
-void
-trad_frame_get_prev_register (struct frame_info *next_frame,
- struct trad_frame_saved_reg this_saved_regs[],
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realregp, gdb_byte *bufferp)
-{
- struct gdbarch *gdbarch = get_frame_arch (next_frame);
- if (trad_frame_addr_p (this_saved_regs, regnum))
- {
- /* The register was saved in memory. */
- *optimizedp = 0;
- *lvalp = lval_memory;
- *addrp = this_saved_regs[regnum].addr;
- *realregp = -1;
- if (bufferp != NULL)
- {
- /* Read the value in from memory. */
- get_frame_memory (next_frame, this_saved_regs[regnum].addr, bufferp,
- register_size (gdbarch, regnum));
- }
- }
- else if (trad_frame_realreg_p (this_saved_regs, regnum))
- {
- *optimizedp = 0;
- *lvalp = lval_register;
- *addrp = 0;
- *realregp = this_saved_regs[regnum].realreg;
- /* Ask the next frame to return the value of the register. */
- if (bufferp)
- frame_unwind_register (next_frame, (*realregp), bufferp);
- }
- else if (trad_frame_value_p (this_saved_regs, regnum))
+ for (; (count = regmap->count) != 0; regmap++)
{
- /* The register's value is available. */
- *optimizedp = 0;
- *lvalp = not_lval;
- *addrp = 0;
- *realregp = -1;
- if (bufferp != NULL)
- store_unsigned_integer (bufferp, register_size (gdbarch, regnum),
- this_saved_regs[regnum].addr);
- }
- else
- {
- error (_("Register %s not available"),
- gdbarch_register_name (gdbarch, regnum));
+ 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);
+ }
+ }
+ }
}
}
+/* See trad-frame.h. */
+
void
+trad_frame_set_reg_value_bytes (struct trad_frame_cache *this_trad_cache,
+ int regnum,
+ gdb::array_view<const gdb_byte> bytes)
+{
+ /* External interface for users of trad_frame_cache
+ (who cannot access the prev_regs object directly). */
+ this_trad_cache->prev_regs[regnum].set_value_bytes (bytes);
+}
+
+
+
+struct value *
+trad_frame_get_prev_register (struct frame_info *this_frame,
+ trad_frame_saved_reg this_saved_regs[],
+ int regnum)
+{
+ if (this_saved_regs[regnum].is_addr ())
+ /* The register was saved in memory. */
+ return frame_unwind_got_memory (this_frame, regnum,
+ this_saved_regs[regnum].addr ());
+ else if (this_saved_regs[regnum].is_realreg ())
+ return frame_unwind_got_register (this_frame, regnum,
+ this_saved_regs[regnum].realreg ());
+ else if (this_saved_regs[regnum].is_value ())
+ /* The register's value is available. */
+ return frame_unwind_got_constant (this_frame, regnum,
+ this_saved_regs[regnum].value ());
+ else if (this_saved_regs[regnum].is_value_bytes ())
+ /* The register's value is available as a sequence of bytes. */
+ return frame_unwind_got_bytes (this_frame, regnum,
+ this_saved_regs[regnum].value_bytes ());
+ else
+ return frame_unwind_got_optimized (this_frame, regnum);
+}
+
+struct value *
trad_frame_get_register (struct trad_frame_cache *this_trad_cache,
- struct frame_info *next_frame,
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realregp, gdb_byte *bufferp)
-{
- trad_frame_get_prev_register (next_frame, this_trad_cache->prev_regs,
- regnum, optimizedp, lvalp, addrp, realregp,
- bufferp);
+ struct frame_info *this_frame,
+ int regnum)
+{
+ return trad_frame_get_prev_register (this_frame, this_trad_cache->prev_regs,
+ regnum);
}
void