From 5ac213430b710e8aaed1f4cea6ff809783201df9 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 6 Aug 2015 18:23:01 +0100 Subject: S/390: displaced stepping and PC-relative RIL-b/RIL-c instructions This adds displaced stepping support for the General-Instruction Extension Facility instructions, which have a PC-relative displacement (RIL-b/RIL-c). We already handle RIL branches, but not others. Currently, displaced stepping a breakpoint put on any of these instructions results in the inferior crashing when or after the instruction is executed out-of-line in the scratch pad. This patch takes the easy route of patching the displacement in the copy of the instruction in the scratch pad. As the displacement is a signed 32-bit field, it's possible that the stratch pad ends too far that the needed displacement doesn't fit in the adjusted instruction, as e.g., if stepping over a breakpoint in a shared library (the scratch pad is around the main program's entry point). That case is detected and GDB falls back to stepping over the breakpoint in-line (which involves pausing all threads momentarily). (We could probably do something smarter, but I don't plan on doing it myself. This was already sufficient to get "maint set target-non-stop on" working regression free on S/390.) Tested on S/390 RHEL 7.1, where it fixes a few hundred FAILs when testing with displaced stepping force-enabled, with the end result being no regressions compared to a test run that doesn't force displaced stepping. Fixes the non-stop tests compared to mainline too; most are crashing due to this on the machine I run tests on. gdb/ChangeLog: 2015-08-07 Pedro Alves * s390-linux-tdep.c (is_non_branch_ril) (s390_displaced_step_copy_insn): New functions. (s390_displaced_step_fixup): Update comment. (s390_gdbarch_init): Install s390_displaced_step_copy_insn as gdbarch_displaced_step_copy_insn hook. --- gdb/s390-linux-tdep.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 3 deletions(-) (limited to 'gdb/s390-linux-tdep.c') diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c index 32a5ed6..1e20c92 100644 --- a/gdb/s390-linux-tdep.c +++ b/gdb/s390-linux-tdep.c @@ -1539,6 +1539,116 @@ s390_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) /* Displaced stepping. */ +/* Return true if INSN is a non-branch RIL-b or RIL-c format + instruction. */ + +static int +is_non_branch_ril (gdb_byte *insn) +{ + gdb_byte op1 = insn[0]; + + if (op1 == 0xc4) + { + gdb_byte op2 = insn[1] & 0x0f; + + switch (op2) + { + case 0x02: /* llhrl */ + case 0x04: /* lghrl */ + case 0x05: /* lhrl */ + case 0x06: /* llghrl */ + case 0x07: /* sthrl */ + case 0x08: /* lgrl */ + case 0x0b: /* stgrl */ + case 0x0c: /* lgfrl */ + case 0x0d: /* lrl */ + case 0x0e: /* llgfrl */ + case 0x0f: /* strl */ + return 1; + } + } + else if (op1 == 0xc6) + { + gdb_byte op2 = insn[1] & 0x0f; + + switch (op2) + { + case 0x00: /* exrl */ + case 0x02: /* pfdrl */ + case 0x04: /* cghrl */ + case 0x05: /* chrl */ + case 0x06: /* clghrl */ + case 0x07: /* clhrl */ + case 0x08: /* cgrl */ + case 0x0a: /* clgrl */ + case 0x0c: /* cgfrl */ + case 0x0d: /* crl */ + case 0x0e: /* clgfrl */ + case 0x0f: /* clrl */ + return 1; + } + } + + return 0; +} + +/* Implementation of gdbarch_displaced_step_copy_insn. */ + +static struct displaced_step_closure * +s390_displaced_step_copy_insn (struct gdbarch *gdbarch, + CORE_ADDR from, CORE_ADDR to, + struct regcache *regs) +{ + size_t len = gdbarch_max_insn_length (gdbarch); + gdb_byte *buf = xmalloc (len); + struct cleanup *old_chain = make_cleanup (xfree, buf); + + read_memory (from, buf, len); + + /* Adjust the displacement field of PC-relative RIL instructions, + except branches. The latter are handled in the fixup hook. */ + if (is_non_branch_ril (buf)) + { + LONGEST offset; + + offset = extract_signed_integer (buf + 2, 4, BFD_ENDIAN_BIG); + offset = (from - to + offset * 2) / 2; + + /* If the instruction is too far from the jump pad, punt. This + will usually happen with instructions in shared libraries. + We could probably support these by rewriting them to be + absolute or fully emulating them. */ + if (offset < INT32_MIN || offset > INT32_MAX) + { + /* Let the core fall back to stepping over the breakpoint + in-line. */ + if (debug_displaced) + { + fprintf_unfiltered (gdb_stdlog, + "displaced: can't displaced step " + "RIL instruction: offset %s out of range\n", + plongest (offset)); + } + do_cleanups (old_chain); + return NULL; + } + + store_signed_integer (buf + 2, 4, BFD_ENDIAN_BIG, offset); + } + + write_memory (to, buf, len); + + if (debug_displaced) + { + fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ", + paddress (gdbarch, from), paddress (gdbarch, to)); + displaced_step_dump_bytes (gdb_stdlog, buf, len); + } + + discard_cleanups (old_chain); + return (struct displaced_step_closure *) buf; +} + /* Fix up the state of registers and memory after having single-stepped a displaced instruction. */ static void @@ -1547,8 +1657,7 @@ s390_displaced_step_fixup (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs) { - /* Since we use simple_displaced_step_copy_insn, our closure is a - copy of the instruction. */ + /* Our closure is a copy of the instruction. */ gdb_byte *insn = (gdb_byte *) closure; static int s390_instrlen[] = { 2, 4, 4, 6 }; int insnlen = s390_instrlen[insn[0] >> 6]; @@ -3285,7 +3394,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Displaced stepping. */ set_gdbarch_displaced_step_copy_insn (gdbarch, - simple_displaced_step_copy_insn); + s390_displaced_step_copy_insn); set_gdbarch_displaced_step_fixup (gdbarch, s390_displaced_step_fixup); set_gdbarch_displaced_step_free_closure (gdbarch, simple_displaced_step_free_closure); -- cgit v1.1