diff options
author | Pedro Alves <palves@redhat.com> | 2015-08-06 18:23:01 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-08-07 17:26:21 +0100 |
commit | 5ac213430b710e8aaed1f4cea6ff809783201df9 (patch) | |
tree | 302a197a20f1f30d0fc7f4edef4ea122b4bbe5fe | |
parent | 7f03bd92e389a32da490bb55037881cf374d0f69 (diff) | |
download | gdb-5ac213430b710e8aaed1f4cea6ff809783201df9.zip gdb-5ac213430b710e8aaed1f4cea6ff809783201df9.tar.gz gdb-5ac213430b710e8aaed1f4cea6ff809783201df9.tar.bz2 |
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 <palves@redhat.com>
* 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.
-rw-r--r-- | gdb/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/s390-linux-tdep.c | 115 |
2 files changed, 120 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index af26b58..4c95b27 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,13 @@ 2015-08-07 Pedro Alves <palves@redhat.com> + * 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. + +2015-08-07 Pedro Alves <palves@redhat.com> + * infrun.c (displaced_step_prepare_throw): Return -1 if gdbarch_displaced_step_copy_insn returns NULL. Update intro comment. 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); |