gdb: or1k: add single step for linux native debugging

Needed for single stepping in Linux, this adds the or1k implementation
of or1k_software_single_step.  Most of the implementation is borrowed
from the bare metal single step code from or1k_single_step_through_delay
which has been extracted and shared in helper function
or1k_delay_slot_p.
This commit is contained in:
Stafford Horne 2020-10-23 13:39:53 +09:00
parent a45b1e66c5
commit 5729359001
3 changed files with 55 additions and 20 deletions

View file

@ -154,6 +154,8 @@ or1k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* GNU/Linux uses the dynamic linker included in the GNU C Library. */
set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
set_gdbarch_software_single_step (gdbarch, or1k_software_single_step);
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);

View file

@ -346,33 +346,16 @@ constexpr gdb_byte or1k_break_insn[] = {0x21, 0x00, 0x00, 0x01};
typedef BP_MANIPULATION (or1k_break_insn) or1k_breakpoint;
/* Implement the single_step_through_delay gdbarch method. */
static int
or1k_single_step_through_delay (struct gdbarch *gdbarch,
struct frame_info *this_frame)
or1k_delay_slot_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
ULONGEST val;
CORE_ADDR ppc;
CORE_ADDR npc;
CGEN_FIELDS tmp_fields;
const CGEN_INSN *insn;
struct regcache *regcache = get_current_regcache ();
CGEN_FIELDS tmp_fields;
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* Get the previous and current instruction addresses. If they are not
adjacent, we cannot be in a delay slot. */
regcache_cooked_read_unsigned (regcache, OR1K_PPC_REGNUM, &val);
ppc = (CORE_ADDR) val;
regcache_cooked_read_unsigned (regcache, OR1K_NPC_REGNUM, &val);
npc = (CORE_ADDR) val;
if (0x4 != (npc - ppc))
return 0;
insn = cgen_lookup_insn (tdep->gdb_cgen_cpu_desc,
NULL,
or1k_fetch_instruction (gdbarch, ppc),
or1k_fetch_instruction (gdbarch, pc),
NULL, 32, &tmp_fields, 0);
/* NULL here would mean the last instruction was not understood by cgen.
@ -390,6 +373,51 @@ or1k_single_step_through_delay (struct gdbarch *gdbarch,
|| (CGEN_INSN_NUM (insn) == OR1K_INSN_L_BF));
}
/* Implement the single_step_through_delay gdbarch method. */
static int
or1k_single_step_through_delay (struct gdbarch *gdbarch,
struct frame_info *this_frame)
{
ULONGEST val;
CORE_ADDR ppc;
CORE_ADDR npc;
struct regcache *regcache = get_current_regcache ();
/* Get the previous and current instruction addresses. If they are not
adjacent, we cannot be in a delay slot. */
regcache_cooked_read_unsigned (regcache, OR1K_PPC_REGNUM, &val);
ppc = (CORE_ADDR) val;
regcache_cooked_read_unsigned (regcache, OR1K_NPC_REGNUM, &val);
npc = (CORE_ADDR) val;
if (0x4 != (npc - ppc))
return 0;
return or1k_delay_slot_p (gdbarch, ppc);
}
/* or1k_software_single_step() is called just before we want to resume
the inferior, if we want to single-step it but there is no hardware
or kernel single-step support (OpenRISC on GNU/Linux for example). We
find the target of the coming instruction skipping over delay slots
and breakpoint it. */
std::vector<CORE_ADDR>
or1k_software_single_step (struct regcache *regcache)
{
struct gdbarch *gdbarch = regcache->arch ();
CORE_ADDR pc, next_pc;
pc = regcache_read_pc (regcache);
next_pc = pc + 4;
if (or1k_delay_slot_p (gdbarch, pc))
next_pc += 4;
return {next_pc};
}
/* Name for or1k general registers. */
static const char *const or1k_reg_names[OR1K_NUM_REGS] = {

View file

@ -52,4 +52,9 @@
#define OR1K_NUM_TAP_RECORDS 8
#define OR1K_FRAME_RED_ZONE_SIZE 2536
/* Single step based on where the current instruction will take us. */
extern std::vector<CORE_ADDR> or1k_software_single_step
(struct regcache *regcache);
#endif /* OR1K_TDEP_H */