public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC v4 0/9] Support for Linux kernel debugging
@ 2017-06-12 17:08 Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 4/9] Add kernel module support for linux-kernel target Philipp Rudo
                   ` (9 more replies)
  0 siblings, 10 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

Hi everybody,

here is finally v4 of my patch set.

Despite the announcement made with v3, I turned away from the idea to only
add patches on top.  While the idea sounds good reality showed that
resolving all merge conflicts and splitting them up so they can easily be
squashed into the original commits is not that nice.

New in v4:
	* Rebase to current master (fd0219988d).
	* Add S390 kernel stack unwinder (requires new patch #8).
	* C++-ify module support.
	* Rewrite module relocation to mimic kernel better.
	* Adapt to new ptid_t API.
	* Update commit messages.
	* Fix some typos and coding style violations.

Philipp

Philipp Rudo (9):
  Convert substitute_path_component to C++
  Add libiberty/concat styled concat_path function
  Add basic Linux kernel support
  Add kernel module support for linux-kernel target
  Add commands for linux-kernel target
  Separate common s390-tdep.* from s390-linux-tdep.*
  Add privileged registers for s390x
  Link frame_info to thread_info
  Add S390 support for linux-kernel target

 gdb/Makefile.in                       |   22 +
 gdb/auto-load.c                       |   18 +-
 gdb/common/common-utils.h             |   11 +
 gdb/configure.tgt                     |    9 +-
 gdb/features/Makefile                 |   11 +-
 gdb/features/s390-cr.xml              |   26 +
 gdb/features/s390x-cr-linux64.c       |   99 +
 gdb/features/s390x-cr-linux64.xml     |   24 +
 gdb/features/s390x-vxcr-linux64.c     |  169 ++
 gdb/features/s390x-vxcr-linux64.xml   |   25 +
 gdb/frame.c                           |   12 +
 gdb/frame.h                           |    2 +
 gdb/gdbarch.c                         |   31 +
 gdb/gdbarch.h                         |    7 +
 gdb/gdbarch.sh                        |    4 +
 gdb/lk-cmds.c                         |  253 +++
 gdb/lk-cmds.h                         |   25 +
 gdb/lk-lists.c                        |   47 +
 gdb/lk-lists.h                        |   56 +
 gdb/lk-low.c                          |  944 ++++++++
 gdb/lk-low.h                          |  338 +++
 gdb/lk-modules.c                      |  498 +++++
 gdb/lk-modules.h                      |  149 ++
 gdb/regformats/s390x-cr-linux64.dat   |   76 +
 gdb/regformats/s390x-vxcr-linux64.dat |  108 +
 gdb/s390-linux-nat.c                  |    1 +
 gdb/s390-linux-tdep.c                 | 3934 +++++----------------------------
 gdb/s390-linux-tdep.h                 |  178 +-
 gdb/s390-lk-tdep.c                    |  887 ++++++++
 gdb/s390-lk-tdep.h                    |   54 +
 gdb/s390-tdep.c                       | 2716 +++++++++++++++++++++++
 gdb/s390-tdep.h                       |  437 ++++
 gdb/solib.c                           |    8 +
 gdb/solib.h                           |    5 +
 gdb/typeprint.c                       |    8 +-
 gdb/typeprint.h                       |    2 +
 gdb/utils.c                           |   87 +-
 gdb/utils.h                           |   26 +-
 38 files changed, 7759 insertions(+), 3548 deletions(-)
 create mode 100644 gdb/features/s390-cr.xml
 create mode 100644 gdb/features/s390x-cr-linux64.c
 create mode 100644 gdb/features/s390x-cr-linux64.xml
 create mode 100644 gdb/features/s390x-vxcr-linux64.c
 create mode 100644 gdb/features/s390x-vxcr-linux64.xml
 create mode 100644 gdb/lk-cmds.c
 create mode 100644 gdb/lk-cmds.h
 create mode 100644 gdb/lk-lists.c
 create mode 100644 gdb/lk-lists.h
 create mode 100644 gdb/lk-low.c
 create mode 100644 gdb/lk-low.h
 create mode 100644 gdb/lk-modules.c
 create mode 100644 gdb/lk-modules.h
 create mode 100644 gdb/regformats/s390x-cr-linux64.dat
 create mode 100644 gdb/regformats/s390x-vxcr-linux64.dat
 create mode 100644 gdb/s390-lk-tdep.c
 create mode 100644 gdb/s390-lk-tdep.h
 create mode 100644 gdb/s390-tdep.c
 create mode 100644 gdb/s390-tdep.h

-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 3/9] Add basic Linux kernel support
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (3 preceding siblings ...)
  2017-06-12 17:08 ` [RFC v4 7/9] Add privileged registers for s390x Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-15 15:23   ` Yao Qi
  2017-06-12 17:08 ` [RFC v4 1/9] Convert substitute_path_component to C++ Philipp Rudo
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

This patch implements a basic target_ops for Linux kernel support. In
particular it models Linux tasks as GDB threads such that you are able to
change to a given thread, get backtraces, disassemble the current frame
etc..

To simplify matters this patch only supports static targets, i.e. core
dumps.  Support for live targets will be provided in a separate patch.

gdb/ChangeLog:

    * gdbarch.sh (lk_init_private): New hook.
    * gdbarch.h: Regenerated.
    * gdbarch.c: Regenerated.
    * lk-low.h: New file.
    * lk-low.c: New file.
    * lk-lists.h: New file.
    * lk-lists.c: New file.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-low.c and lk-lists.c.
    (HFILES_NO_SRCDIR): Add lk-low.h and lk-lists.h.
    (ALL_TARGET_OBS): Add lk-low.o and lk-lists.o.
    * configure.tgt (lk_target_obs): New variable with object files for Linux
      kernel support.
      (s390*-*-linux*): Add lk_target_obs.
---
 gdb/Makefile.in   |   8 +
 gdb/configure.tgt |   6 +-
 gdb/gdbarch.c     |  31 ++
 gdb/gdbarch.h     |   7 +
 gdb/gdbarch.sh    |   4 +
 gdb/lk-lists.c    |  47 +++
 gdb/lk-lists.h    |  56 ++++
 gdb/lk-low.c      | 831 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-low.h      | 310 ++++++++++++++++++++
 9 files changed, 1299 insertions(+), 1 deletion(-)
 create mode 100644 gdb/lk-lists.c
 create mode 100644 gdb/lk-lists.h
 create mode 100644 gdb/lk-low.c
 create mode 100644 gdb/lk-low.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5e5fcaae7a..a73ca26a29 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -827,6 +827,8 @@ ALL_TARGET_OBS = \
 	iq2000-tdep.o \
 	linux-record.o \
 	linux-tdep.o \
+	lk-lists.o \
+	lk-low.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1127,6 +1129,8 @@ SFILES = \
 	jit.c \
 	language.c \
 	linespec.c \
+	lk-lists.c \
+	lk-low.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1376,6 +1380,8 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-lists.h \
+	lk-low.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2582,6 +2588,8 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.c \
+	lk-lists.c \
+	lk-low.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index fdcb7b1d69..35db698860 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -36,6 +36,10 @@ case $targ in
     ;;
 esac
 
+# List of objectfiles for Linux kernel support.  To be included into *-linux*
+# targets wich support Linux kernel debugging.
+lk_target_obs="lk-lists.o lk-low.o"
+
 # map target info into gdb names.
 
 case "${targ}" in
@@ -480,7 +484,7 @@ powerpc*-*-*)
 s390*-*-linux*)
 	# Target: S390 running Linux
 	gdb_target_obs="s390-linux-tdep.o solib-svr4.o linux-tdep.o \
-			linux-record.o"
+			linux-record.o ${lk_target_obs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index e5efdfbb26..cacbc3e740 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -351,6 +351,7 @@ struct gdbarch
   gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
   char ** disassembler_options;
   const disasm_options_t * valid_disassembler_options;
+  gdbarch_lk_init_private_ftype *lk_init_private;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -1145,6 +1146,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: iterate_over_regset_sections = <%s>\n",
                       host_address_to_string (gdbarch->iterate_over_regset_sections));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_lk_init_private_p() = %d\n",
+                      gdbarch_lk_init_private_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: lk_init_private = <%s>\n",
+                      host_address_to_string (gdbarch->lk_init_private));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: long_bit = %s\n",
                       plongest (gdbarch->long_bit));
   fprintf_unfiltered (file,
@@ -5055,6 +5062,30 @@ set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch,
   gdbarch->valid_disassembler_options = valid_disassembler_options;
 }
 
+int
+gdbarch_lk_init_private_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->lk_init_private != NULL;
+}
+
+void
+gdbarch_lk_init_private (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->lk_init_private != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_lk_init_private called\n");
+  gdbarch->lk_init_private (gdbarch);
+}
+
+void
+set_gdbarch_lk_init_private (struct gdbarch *gdbarch,
+                             gdbarch_lk_init_private_ftype lk_init_private)
+{
+  gdbarch->lk_init_private = lk_init_private;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index ab7561f851..bfff52ce13 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1553,6 +1553,13 @@ extern void set_gdbarch_disassembler_options (struct gdbarch *gdbarch, char ** d
 
 extern const disasm_options_t * gdbarch_valid_disassembler_options (struct gdbarch *gdbarch);
 extern void set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch, const disasm_options_t * valid_disassembler_options);
+/* Initiate architecture dependent private data for the linux-kernel target. */
+
+extern int gdbarch_lk_init_private_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_lk_init_private_ftype) (struct gdbarch *gdbarch);
+extern void gdbarch_lk_init_private (struct gdbarch *gdbarch);
+extern void set_gdbarch_lk_init_private (struct gdbarch *gdbarch, gdbarch_lk_init_private_ftype *lk_init_private);
 
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 22f5715037..8bc9456d3e 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1160,6 +1160,10 @@ m;int;addressable_memory_unit_size;void;;;default_addressable_memory_unit_size;;
 v;char **;disassembler_options;;;0;0;;0;pstring_ptr (gdbarch->disassembler_options)
 v;const disasm_options_t *;valid_disassembler_options;;;0;0;;0;host_address_to_string (gdbarch->valid_disassembler_options)
 
+# Initialize architecture dependent private data for the linux-kernel
+# target.
+M:void:lk_init_private:void:
+
 EOF
 }
 
diff --git a/gdb/lk-lists.c b/gdb/lk-lists.c
new file mode 100644
index 0000000000..55d11bd11d
--- /dev/null
+++ b/gdb/lk-lists.c
@@ -0,0 +1,47 @@
+/* Iterators for internal data structures of the Linux kernel.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "inferior.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+
+/* Returns next entry from struct list_head CURR while iterating field
+   SNAME->FNAME.  */
+
+CORE_ADDR
+lk_list_head_next (CORE_ADDR curr, const char *sname, const char *fname)
+{
+  CORE_ADDR next, next_prev;
+
+  /* We must always assume that the data we handle is corrupted.  Thus use
+     curr->next->prev == curr as sanity check.  */
+  next = lk_read_addr (curr + LK_OFFSET (list_head, next));
+  next_prev = lk_read_addr (next + LK_OFFSET (list_head, prev));
+
+  if (!curr || curr != next_prev)
+    {
+      error (_("Memory corruption detected while iterating list_head at "\
+	       "0x%s belonging to list %s->%s."),
+	     phex (curr, lk_builtin_type_size (unsigned_long)) , sname, fname);
+    }
+
+  return next;
+}
diff --git a/gdb/lk-lists.h b/gdb/lk-lists.h
new file mode 100644
index 0000000000..f9c2a856e9
--- /dev/null
+++ b/gdb/lk-lists.h
@@ -0,0 +1,56 @@
+/* Iterators for internal data structures of the Linux kernel.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_LISTS_H__
+#define __LK_LISTS_H__
+
+extern CORE_ADDR lk_list_head_next (CORE_ADDR curr, const char *sname,
+				    const char *fname);
+
+/* Iterator over field SNAME->FNAME of type struct list_head starting at
+   address START of type struct list_head.  This iterator is intended to be
+   used for lists initiated with macro LIST_HEAD (include/linux/list.h) in
+   the kernel, i.e. lists that START is a global variable of type struct
+   list_head and _not_ of type struct SNAME as the rest of the list.  Thus
+   START will not be iterated over but only be used to start/terminate the
+   iteration.  */
+
+#define lk_list_for_each(next, start, sname, fname)		\
+  for ((next) = lk_list_head_next ((start), #sname, #fname);	\
+       (next) != (start);					\
+       (next) = lk_list_head_next ((next), #sname, #fname))
+
+/* Iterator over struct SNAME linked together via field SNAME->FNAME of type
+   struct list_head starting at address START of type struct SNAME.  In
+   contrast to the iterator above, START is a "full" member of the list and
+   thus will be iterated over.  */
+
+#define lk_list_for_each_container(cont, start, sname, fname)	\
+  CORE_ADDR _next;						\
+  bool _first_loop = true;					\
+  for ((cont) = (start),					\
+       _next = (start) + LK_OFFSET (sname, fname);		\
+								\
+       (cont) != (start) || _first_loop;			\
+								\
+       _next = lk_list_head_next (_next, #sname, #fname),	\
+       (cont) = LK_CONTAINER_OF (_next, sname, fname),		\
+       _first_loop = false)
+
+#endif /* __LK_LISTS_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
new file mode 100644
index 0000000000..e482ad08e7
--- /dev/null
+++ b/gdb/lk-low.c
@@ -0,0 +1,831 @@
+/* Basic Linux kernel support, architecture independent.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "block.h"
+#include "exceptions.h"
+#include "frame.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "solib.h"
+#include "target.h"
+#include "value.h"
+
+#include <algorithm>
+
+struct target_ops *linux_kernel_ops = NULL;
+
+/* Initialize a private data entry for an address, where NAME is the name
+   of the symbol, i.e. variable name in Linux, ALIAS the name used to
+   retrieve the entry from hashtab, and SILENT a flag to determine if
+   errors should be ignored.
+
+   Returns a pointer to the new entry.  In case of an error, either returns
+   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If SILENT = TRUE
+   the caller is responsible to check for errors.
+
+   Do not use directly, use LK_DECLARE_* macros defined in lk-low.h instead.  */
+
+struct lk_private_data *
+lk_init_addr (const char *name, const char *alias, int silent)
+{
+  struct lk_private_data *data;
+  struct bound_minimal_symbol bmsym;
+  void **new_slot;
+  void *old_slot;
+
+  if ((old_slot = lk_find (alias)) != NULL)
+    return (struct lk_private_data *) old_slot;
+
+  bmsym = lookup_minimal_symbol (name, NULL, NULL);
+
+  if (bmsym.minsym == NULL)
+    {
+      if (!silent)
+	error (_("Could not find address %s.  Aborting."), alias);
+      return NULL;
+    }
+
+  data = XCNEW (struct lk_private_data);
+  data->alias = alias;
+  data->data.addr = BMSYMBOL_VALUE_ADDRESS (bmsym);
+
+  new_slot = lk_find_slot (alias);
+  *new_slot = data;
+
+  return data;
+}
+
+/* Same as lk_init_addr but for structs.  */
+
+struct lk_private_data *
+lk_init_struct (const char *name, const char *alias, int silent)
+{
+  struct lk_private_data *data;
+  const struct block *global;
+  const struct symbol *sym;
+  struct type *type;
+  void **new_slot;
+  void *old_slot;
+
+  if ((old_slot = lk_find (alias)) != NULL)
+    return (struct lk_private_data *) old_slot;
+
+  global = block_global_block(get_selected_block (0));
+  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;
+
+  if (sym != NULL)
+    {
+      type = SYMBOL_TYPE (sym);
+      goto out;
+    }
+
+  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
+  sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;
+  if (sym == NULL)
+    goto error;
+
+  type = check_typedef (SYMBOL_TYPE (sym));
+
+  if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+    goto out;
+
+error:
+  if (!silent)
+    error (_("Could not find %s.  Aborting."), alias);
+
+  return NULL;
+
+out:
+  data = XCNEW (struct lk_private_data);
+  data->alias = alias;
+  data->data.type = type;
+
+  new_slot = lk_find_slot (alias);
+  *new_slot = data;
+
+  return data;
+}
+
+/* Nearly the same as lk_init_addr, with the difference that two names are
+   needed, i.e. the struct name S_NAME containing the field with name
+   F_NAME.  */
+
+struct lk_private_data *
+lk_init_field (const char *s_name, const char *f_name,
+	       const char *s_alias, const char *f_alias,
+	       int silent)
+{
+  struct lk_private_data *data;
+  struct lk_private_data *parent;
+  struct field *first, *last, *field;
+  void **new_slot;
+  void *old_slot;
+
+  if ((old_slot = lk_find (f_alias)) != NULL)
+    return (struct lk_private_data *) old_slot;
+
+  parent = lk_find (s_alias);
+  if (parent == NULL)
+    {
+      parent = lk_init_struct (s_name, s_alias, silent);
+
+      /* Only SILENT == true needed, as otherwise lk_init_struct would throw
+	 an error.  */
+      if (parent == NULL)
+	return NULL;
+    }
+
+  first = TYPE_FIELDS (parent->data.type);
+  last = first + TYPE_NFIELDS (parent->data.type);
+  for (field = first; field < last; field ++)
+    {
+      if (streq (field->name, f_name))
+	break;
+    }
+
+  if (field == last)
+    {
+      if (!silent)
+	error (_("Could not find field %s->%s.  Aborting."), s_alias, f_name);
+      return NULL;
+    }
+
+  data = XCNEW (struct lk_private_data);
+  data->alias = f_alias;
+  data->data.field = field;
+
+  new_slot = lk_find_slot (f_alias);
+  *new_slot = data;
+
+  return data;
+}
+
+/* Map cpu number CPU to the original PTID from target beneath.  */
+
+static ptid_t
+lk_cpu_to_old_ptid (const int cpu)
+{
+  struct lk_ptid_map *ptid_map;
+
+  for (ptid_map = LK_PRIVATE->old_ptid; ptid_map;
+       ptid_map = ptid_map->next)
+    {
+      if (ptid_map->cpu == cpu)
+	return ptid_map->old_ptid;
+    }
+
+  error (_("Could not map CPU %d to original PTID.  Aborting."), cpu);
+}
+
+/* Helper functions to read and return basic types at a given ADDRess.  */
+
+/* Read and return the integer value at address ADDR.  */
+
+int
+lk_read_int (CORE_ADDR addr)
+{
+  size_t int_size = lk_builtin_type_size (int);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_integer (addr, int_size, endian);
+}
+
+/* Read and return the unsigned integer value at address ADDR.  */
+
+unsigned int
+lk_read_uint (CORE_ADDR addr)
+{
+  size_t uint_size = lk_builtin_type_size (unsigned_int);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_integer (addr, uint_size, endian);
+}
+
+/* Read and return the long integer value at address ADDR.  */
+
+LONGEST
+lk_read_long (CORE_ADDR addr)
+{
+  size_t long_size = lk_builtin_type_size (long);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_integer (addr, long_size, endian);
+}
+
+/* Read and return the unsigned long integer value at address ADDR.  */
+
+ULONGEST
+lk_read_ulong (CORE_ADDR addr)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+  return read_memory_unsigned_integer (addr, ulong_size, endian);
+}
+
+/* Read and return the address value at address ADDR.  */
+
+CORE_ADDR
+lk_read_addr (CORE_ADDR addr)
+{
+  return (CORE_ADDR) lk_read_ulong (addr);
+}
+
+/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
+   returns an array of ulongs.  The caller is responsible to free the array
+   after it is no longer needed.  */
+
+ULONGEST *
+lk_read_bitmap (CORE_ADDR addr, size_t size)
+{
+  ULONGEST *bitmap;
+  size_t ulong_size, len;
+
+  ulong_size = lk_builtin_type_size (unsigned_long);
+  len = LK_DIV_ROUND_UP (size, ulong_size * LK_BITS_PER_BYTE);
+  bitmap = XNEWVEC (ULONGEST, len);
+
+  for (size_t i = 0; i < len; i++)
+    bitmap[i] = lk_read_ulong (addr + i * ulong_size);
+
+  return bitmap;
+}
+
+/* Return the next set bit in bitmap BITMAP of size SIZE (in bits)
+   starting from bit (index) BIT.  Return SIZE when the end of the bitmap
+   was reached.  To iterate over all set bits use macro
+   LK_BITMAP_FOR_EACH_SET_BIT defined in lk-low.h.  */
+
+size_t
+lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t size, size_t bit)
+{
+  size_t ulong_size, bits_per_ulong, elt;
+
+  ulong_size = lk_builtin_type_size (unsigned_long);
+  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
+  elt = bit / bits_per_ulong;
+
+  while (bit < size)
+    {
+      /* FIXME: Explain why using lsb0 bit order.  */
+      if (bitmap[elt] & (1UL << (bit % bits_per_ulong)))
+	return bit;
+
+      bit++;
+      if (bit % bits_per_ulong == 0)
+	elt++;
+    }
+
+  return size;
+}
+
+/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP
+   with size SIZE (in bits).  */
+
+size_t
+lk_bitmap_hweight (ULONGEST *bitmap, size_t size)
+{
+  size_t ulong_size, bit, bits_per_ulong, elt, retval;
+
+  ulong_size = lk_builtin_type_size (unsigned_long);
+  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
+  elt = bit = 0;
+  retval = 0;
+
+  while (bit < size)
+    {
+      if (bitmap[elt] & (1 << bit % bits_per_ulong))
+	retval++;
+
+      bit++;
+      if (bit % bits_per_ulong == 0)
+	elt++;
+    }
+
+  return retval;
+}
+
+/* Provide the per_cpu_offset of cpu CPU.  See comment in lk-low.h for
+   details.  */
+
+CORE_ADDR
+lk_get_percpu_offset (unsigned int cpu)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR percpu_elt;
+
+  /* Give the architecture a chance to overwrite default behaviour.  */
+  if (LK_HOOK->get_percpu_offset)
+      return LK_HOOK->get_percpu_offset (cpu);
+
+  percpu_elt = LK_ADDR (__per_cpu_offset) + (ulong_size * cpu);
+  return lk_read_addr (percpu_elt);
+}
+
+
+/* Test if a given task TASK is running.  See comment in lk-low.h for
+   details.  */
+
+unsigned int
+lk_task_running (CORE_ADDR task)
+{
+  ULONGEST *cpu_online_mask;
+  size_t size;
+  unsigned int cpu;
+  struct cleanup *old_chain;
+
+  size = LK_BITMAP_SIZE (cpumask);
+  cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
+  old_chain = make_cleanup (xfree, cpu_online_mask);
+
+  LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
+    {
+      CORE_ADDR rq;
+      CORE_ADDR curr;
+
+      rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
+      curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
+
+      if (curr == task)
+	break;
+    }
+
+  if (cpu == size)
+    cpu = LK_CPU_INVAL;
+
+  do_cleanups (old_chain);
+  return cpu;
+}
+
+/* Update running tasks with information from struct rq->curr. */
+
+static void
+lk_update_running_tasks ()
+{
+  ULONGEST *cpu_online_mask;
+  size_t size;
+  unsigned int cpu;
+  struct cleanup *old_chain;
+
+  size = LK_BITMAP_SIZE (cpumask);
+  cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
+  old_chain = make_cleanup (xfree, cpu_online_mask);
+
+  LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
+    {
+      struct thread_info *tp;
+      CORE_ADDR rq, curr;
+      LONGEST pid, inf_pid;
+
+      rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
+      curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
+      pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
+      inf_pid = current_inferior ()->pid;
+
+      ptid_t new_ptid (inf_pid, pid, curr);
+      ptid_t old_ptid = lk_cpu_to_old_ptid (cpu); /* FIXME not suitable for
+						     running targets? */
+
+      tp = find_thread_ptid (old_ptid);
+      if (tp && tp->state != THREAD_EXITED)
+	thread_change_ptid (old_ptid, new_ptid);
+    }
+  do_cleanups (old_chain);
+}
+
+/* Update sleeping tasks by walking the task_structs starting from
+   init_task.  */
+
+static void
+lk_update_sleeping_tasks ()
+{
+  CORE_ADDR init_task, task, thread;
+  int inf_pid;
+
+  inf_pid = current_inferior ()->pid;
+  init_task = LK_ADDR (init_task);
+
+  lk_list_for_each_container (task, init_task, task_struct, tasks)
+    {
+      lk_list_for_each_container (thread, task, task_struct, thread_group)
+	{
+	  int pid;
+	  struct thread_info *tp;
+
+	  pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
+	  ptid_t ptid (inf_pid, pid, thread);
+
+	  tp = find_thread_ptid (ptid);
+	  if (tp == NULL || tp->state == THREAD_EXITED)
+	    add_thread (ptid);
+	}
+    }
+}
+
+/* Function for targets to_update_thread_list hook.  */
+
+static void
+lk_update_thread_list (struct target_ops *target)
+{
+  prune_threads ();
+  lk_update_running_tasks ();
+  lk_update_sleeping_tasks ();
+}
+
+/* Function for targets to_fetch_registers hook.  */
+
+static void
+lk_fetch_registers (struct target_ops *target,
+		    struct regcache *regcache, int regnum)
+{
+  CORE_ADDR task;
+  unsigned int cpu;
+
+  task = (CORE_ADDR) regcache_get_ptid (regcache).tid ();
+  cpu = lk_task_running (task);
+
+  /* Let the target beneath fetch registers of running tasks.  */
+  if (cpu != LK_CPU_INVAL)
+    {
+      struct cleanup *old_inferior_ptid;
+
+      old_inferior_ptid = save_inferior_ptid ();
+      inferior_ptid = lk_cpu_to_old_ptid (cpu);
+      linux_kernel_ops->beneath->to_fetch_registers (target, regcache, regnum);
+      do_cleanups (old_inferior_ptid);
+    }
+  else
+    {
+      struct gdbarch *gdbarch;
+      unsigned int i;
+
+      LK_HOOK->get_registers (task, target, regcache, regnum);
+
+      /* Mark all registers not found as unavailable.  */
+      gdbarch = get_regcache_arch (regcache);
+      for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
+	{
+	  if (regcache_register_status (regcache, i) == REG_UNKNOWN)
+	    regcache_raw_supply (regcache, i, NULL);
+	}
+    }
+}
+
+/* Function for targets to_pid_to_str hook.  Marks running tasks with an
+   asterisk "*".  */
+
+static const char *
+lk_pid_to_str (struct target_ops *target, ptid_t ptid)
+{
+  static char buf[64];
+  long pid;
+  CORE_ADDR task;
+
+  pid = ptid.lwp ();
+  task = (CORE_ADDR) ptid.tid ();
+
+  xsnprintf (buf, sizeof (buf), "PID: %5li%s, 0x%s",
+	     pid, ((lk_task_running (task) != LK_CPU_INVAL) ? "*" : ""),
+	     phex (task, lk_builtin_type_size (unsigned_long)));
+
+  return buf;
+}
+
+/* Function for targets to_thread_name hook.  */
+
+static const char *
+lk_thread_name (struct target_ops *target, struct thread_info *ti)
+{
+  static char buf[LK_TASK_COMM_LEN + 1];
+  char tmp[LK_TASK_COMM_LEN + 1];
+  CORE_ADDR task, comm;
+  size_t size;
+
+  size = std::min ((unsigned int) LK_TASK_COMM_LEN,
+		   LK_ARRAY_LEN(LK_FIELD (task_struct, comm)));
+
+  task = (CORE_ADDR) ti->ptid.tid ();
+  comm = task + LK_OFFSET (task_struct, comm);
+  read_memory (comm, (gdb_byte *) tmp, size);
+
+  xsnprintf (buf, sizeof (buf), "%-16s", tmp);
+
+  return buf;
+}
+
+/* Functions to initialize and free target_ops and its private data.  As well
+   as functions for targets to_open/close/detach hooks.  */
+
+/* Check if OBFFILE is a Linux kernel.  */
+
+static int
+lk_is_linux_kernel (struct objfile *objfile)
+{
+  int ok = 0;
+
+  if (objfile == NULL || !(objfile->flags & OBJF_MAINLINE))
+    return 0;
+
+  ok += lookup_minimal_symbol ("linux_banner", NULL, objfile).minsym != NULL;
+  ok += lookup_minimal_symbol ("_stext", NULL, objfile).minsym != NULL;
+  ok += lookup_minimal_symbol ("_etext", NULL, objfile).minsym != NULL;
+
+  return (ok > 2);
+}
+
+/* Initialize struct lk_private.  */
+
+static void
+lk_init_private ()
+{
+  linux_kernel_ops->to_data = XCNEW (struct lk_private);
+  LK_PRIVATE->hooks = XCNEW (struct lk_private_hooks);
+  LK_PRIVATE->data = htab_create_alloc (31, (htab_hash) lk_hash_private_data,
+					(htab_eq) lk_private_data_eq, NULL,
+					xcalloc, xfree);
+}
+
+/* Initialize architecture independent private data.  Must be called
+   _after_ symbol tables were initialized.  */
+
+static void
+lk_init_private_data ()
+{
+  if (LK_PRIVATE->data != NULL)
+    htab_empty (LK_PRIVATE->data);
+
+  LK_DECLARE_FIELD (task_struct, tasks);
+  LK_DECLARE_FIELD (task_struct, pid);
+  LK_DECLARE_FIELD (task_struct, tgid);
+  LK_DECLARE_FIELD (task_struct, thread_group);
+  LK_DECLARE_FIELD (task_struct, comm);
+  LK_DECLARE_FIELD (task_struct, thread);
+
+  LK_DECLARE_FIELD (list_head, next);
+  LK_DECLARE_FIELD (list_head, prev);
+
+  LK_DECLARE_FIELD (rq, curr);
+
+  LK_DECLARE_FIELD (cpumask, bits);
+
+  LK_DECLARE_ADDR (init_task);
+  LK_DECLARE_ADDR (runqueues);
+  LK_DECLARE_ADDR (__per_cpu_offset);
+  LK_DECLARE_ADDR (init_mm);
+
+  LK_DECLARE_ADDR_ALIAS (__cpu_online_mask, cpu_online_mask);	/* linux 4.5+ */
+  LK_DECLARE_ADDR_ALIAS (cpu_online_bits, cpu_online_mask);	/* linux -4.4 */
+  if (LK_ADDR (cpu_online_mask) == -1)
+    error (_("Could not find address cpu_online_mask.  Aborting."));
+}
+
+/* Frees the cpu to old ptid map.  */
+
+static void
+lk_free_ptid_map ()
+{
+  while (LK_PRIVATE->old_ptid)
+    {
+      struct lk_ptid_map *tmp;
+
+      tmp = LK_PRIVATE->old_ptid;
+      LK_PRIVATE->old_ptid = tmp->next;
+      XDELETE (tmp);
+    }
+}
+
+/* Initialize the cpu to old ptid map.  Prefer the arch dependent
+   map_running_task_to_cpu hook if provided, else assume that the PID used
+   by target beneath is the same as in task_struct PID task_struct.  See
+   comment on lk_ptid_map in lk-low.h for details.  */
+
+static void
+lk_init_ptid_map ()
+{
+  struct thread_info *ti;
+  ULONGEST *cpu_online_mask;
+  size_t size;
+  unsigned int cpu;
+  struct cleanup *old_chain;
+
+  if (LK_PRIVATE->old_ptid != NULL)
+    lk_free_ptid_map ();
+
+  size = LK_BITMAP_SIZE (cpumask);
+  cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
+  old_chain = make_cleanup (xfree, cpu_online_mask);
+
+  ALL_THREADS (ti)
+    {
+      struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
+      CORE_ADDR rq, curr;
+      int pid;
+
+      /* Give the architecture a chance to overwrite default behaviour.  */
+      if (LK_HOOK->map_running_task_to_cpu)
+	{
+	  ptid_map->cpu = LK_HOOK->map_running_task_to_cpu (ti);
+	}
+      else
+	{
+	  LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
+	    {
+	      rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
+	      curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
+	      pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
+
+	      if (pid == ti->ptid.lwp ())
+		{
+		  ptid_map->cpu = cpu;
+		  break;
+		}
+	    }
+	  if (cpu == size)
+	    error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
+		   ti->ptid.pid (), ti->ptid.lwp ());
+	}
+      ptid_map->old_ptid = ti->ptid;
+      ptid_map->next = LK_PRIVATE->old_ptid;
+      LK_PRIVATE->old_ptid = ptid_map;
+    }
+
+  do_cleanups (old_chain);
+}
+
+/* Initializes all private data and pushes the linux kernel target, if not
+   already done.  */
+
+static void
+lk_try_push_target ()
+{
+  struct gdbarch *gdbarch;
+
+  gdbarch = current_inferior ()->gdbarch;
+  if (!(gdbarch && gdbarch_lk_init_private_p (gdbarch)))
+    error (_("Linux kernel debugging not supported on %s."),
+	   gdbarch_bfd_arch_info (gdbarch)->printable_name);
+
+  lk_init_private ();
+  lk_init_private_data ();
+  gdbarch_lk_init_private (gdbarch);
+  /* Check for required arch hooks.  */
+  gdb_assert (LK_HOOK->get_registers);
+
+  lk_init_ptid_map ();
+  lk_update_thread_list (linux_kernel_ops);
+
+  if (!target_is_pushed (linux_kernel_ops))
+    push_target (linux_kernel_ops);
+}
+
+/* Function for targets to_open hook.  */
+
+static void
+lk_open (const char *args, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (target_is_pushed (linux_kernel_ops))
+    {
+      printf_unfiltered (_("Linux kernel target already pushed.  Aborting\n"));
+      return;
+    }
+
+  for (objfile = current_program_space->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile)
+	  && inferior_ptid.pid () != 0)
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+  printf_unfiltered (_("Could not find a valid Linux kernel object file.  "
+		       "Aborting.\n"));
+}
+
+/* Function for targets to_close hook.  Deletes all private data.  */
+
+static void
+lk_close (struct target_ops *ops)
+{
+  htab_delete (LK_PRIVATE->data);
+  lk_free_ptid_map ();
+  XDELETE (LK_PRIVATE->hooks);
+
+  XDELETE (LK_PRIVATE);
+  linux_kernel_ops->to_data = NULL;
+}
+
+/* Function for targets to_detach hook.  */
+
+static void
+lk_detach (struct target_ops *t, const char *args, int from_tty)
+{
+  struct target_ops *beneath = linux_kernel_ops->beneath;
+
+  unpush_target (linux_kernel_ops);
+  reinit_frame_cache ();
+  if (from_tty)
+    printf_filtered (_("Linux kernel target detached.\n"));
+
+  beneath->to_detach (beneath, args, from_tty);
+}
+
+/* Function for new objfile observer.  */
+
+static void
+lk_observer_new_objfile (struct objfile *objfile)
+{
+  if (lk_is_linux_kernel (objfile)
+      && inferior_ptid.pid () != 0)
+    lk_try_push_target ();
+}
+
+/* Function for inferior created observer.  */
+
+static void
+lk_observer_inferior_created (struct target_ops *ops, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (inferior_ptid.pid () == 0)
+    return;
+
+  for (objfile = current_inferior ()->pspace->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile))
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+}
+
+/* Initialize linux kernel target.  */
+
+static void
+init_linux_kernel_ops (void)
+{
+  struct target_ops *t;
+
+  if (linux_kernel_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_ops);
+  t->to_shortname = "linux-kernel";
+  t->to_longname = "linux kernel support";
+  t->to_doc = "Adds support to debug the Linux kernel";
+
+  /* set t->to_data = struct lk_private in lk_init_private.  */
+
+  t->to_open = lk_open;
+  t->to_close = lk_close;
+  t->to_detach = lk_detach;
+  t->to_fetch_registers = lk_fetch_registers;
+  t->to_update_thread_list = lk_update_thread_list;
+  t->to_pid_to_str = lk_pid_to_str;
+  t->to_thread_name = lk_thread_name;
+
+  t->to_stratum = thread_stratum;
+  t->to_magic = OPS_MAGIC;
+
+  linux_kernel_ops = t;
+
+  add_target (t);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_linux_kernel;
+
+void
+_initialize_linux_kernel (void)
+{
+  init_linux_kernel_ops ();
+
+  observer_attach_new_objfile (lk_observer_new_objfile);
+  observer_attach_inferior_created (lk_observer_inferior_created);
+}
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
new file mode 100644
index 0000000000..be8c5556df
--- /dev/null
+++ b/gdb/lk-low.h
@@ -0,0 +1,310 @@
+/* Basic Linux kernel support, architecture independent.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_LOW_H__
+#define __LK_LOW_H__
+
+#include "target.h"
+
+extern struct target_ops *linux_kernel_ops;
+
+/* Copy constants defined in Linux kernel.  */
+#define LK_TASK_COMM_LEN 16
+#define LK_BITS_PER_BYTE 8
+
+/* Definitions used in linux kernel target.  */
+#define LK_CPU_INVAL -1U
+
+/* Private data structs for this target.  */
+/* Forward declarations.  */
+struct lk_private_hooks;
+struct lk_ptid_map;
+
+/* Short hand access to private data.  */
+#define LK_PRIVATE ((struct lk_private *) linux_kernel_ops->to_data)
+#define LK_HOOK (LK_PRIVATE->hooks)
+
+struct lk_private
+{
+  /* Hashtab for needed addresses, structs and fields.  */
+  htab_t data;
+
+  /* Linked list to map between cpu number and original ptid from target
+     beneath.  */
+  struct lk_ptid_map *old_ptid;
+
+  /* Hooks for architecture dependent functions.  */
+  struct lk_private_hooks *hooks;
+};
+
+/* We use the following convention for PTIDs:
+
+   ptid->pid = inferiors PID
+   ptid->lwp = PID from task_stuct
+   ptid->tid = address of task_struct
+
+   The task_structs address as TID has two reasons.  First, we need it quite
+   often and there is no other reasonable way to pass it down.  Second, it
+   helps us to distinguish swapper tasks as they all have PID = 0.
+
+   Furthermore we cannot rely on the target beneath to use the same PID as the
+   task_struct. Thus we need a mapping between our PTID and the PTID of the
+   target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
+   registers of running tasks, to the target beneath.  */
+
+/* Private data struct to map between our and the target beneath PTID.  */
+
+struct lk_ptid_map
+{
+  struct lk_ptid_map *next;
+  unsigned int cpu;
+  ptid_t old_ptid;
+};
+
+/* Private data struct to be stored in hashtab.  */
+
+struct lk_private_data
+{
+  const char *alias;
+
+  union
+  {
+    CORE_ADDR addr;
+    struct type *type;
+    struct field *field;
+  } data;
+};
+
+/* Wrapper for htab_hash_string to work with our private data.  */
+
+static inline hashval_t
+lk_hash_private_data (const struct lk_private_data *entry)
+{
+  return htab_hash_string (entry->alias);
+}
+
+/* Function for htab_eq to work with our private data.  */
+
+static inline int
+lk_private_data_eq (const struct lk_private_data *entry,
+		    const struct lk_private_data *element)
+{
+  return streq (entry->alias, element->alias);
+}
+
+/* Wrapper for htab_find_slot to work with our private data.  Do not use
+   directly, use the macros below instead.  */
+
+static inline void **
+lk_find_slot (const char *alias)
+{
+  const struct lk_private_data dummy = { alias };
+  return htab_find_slot (LK_PRIVATE->data, &dummy, INSERT);
+}
+
+/* Wrapper for htab_find to work with our private data.  Do not use
+   directly, use the macros below instead.  */
+
+static inline struct lk_private_data *
+lk_find (const char *alias)
+{
+  const struct lk_private_data dummy = { alias };
+  return (struct lk_private_data *) htab_find (LK_PRIVATE->data, &dummy);
+}
+
+/* Functions to initialize private data.  Do not use directly, use the
+   macros below instead.  */
+
+extern struct lk_private_data *lk_init_addr (const char *name,
+					     const char *alias, int silent);
+extern struct lk_private_data *lk_init_struct (const char *name,
+					       const char *alias, int silent);
+extern struct lk_private_data *lk_init_field (const char *s_name,
+					      const char *f_name,
+					      const char *s_alias,
+					      const char *f_alias, int silent);
+
+/* The names we use to store our private data in the hashtab.  */
+
+#define LK_STRUCT_ALIAS(s_name) ("struct " #s_name)
+#define LK_FIELD_ALIAS(s_name, f_name) (#s_name " " #f_name)
+
+/* Macros to initiate addresses and fields, where (S_/F_)NAME is the variables
+   name as used in Linux.  LK_DECLARE_FIELD also initializes the corresponding
+   struct entry.  Throws an error, if no symbol with the given name is found.
+ */
+
+#define LK_DECLARE_ADDR(name) \
+  lk_init_addr (#name, #name, 0)
+#define LK_DECLARE_FIELD(s_name, f_name) \
+  lk_init_field (#s_name, #f_name, LK_STRUCT_ALIAS (s_name), \
+		 LK_FIELD_ALIAS (s_name, f_name), 0)
+
+/* Same as LK_DECLARE_*, but returns NULL instead of throwing an error if no
+   symbol was found.  The caller is responsible to check for possible errors.
+ */
+
+#define LK_DECLARE_ADDR_SILENT(name) \
+  lk_init_addr (#name, #name, 1)
+#define LK_DECLARE_FIELD_SILENT(s_name, f_name) \
+  lk_init_field (#s_name, #f_name, LK_STRUCT_ALIAS (s_name), \
+		 LK_FIELD_ALIAS (s_name, f_name), 1)
+
+/* Same as LK_DECLARE_*_SILENT, but allows you to give an ALIAS name.  If used
+   for a struct, the struct has to be declared explicitly _before_ any of its
+   fields.  They are ment to be used, when a variable in the kernel was simply
+   renamed (at least from our point of view).  The caller is responsible to
+   check for possible errors.  */
+
+#define LK_DECLARE_ADDR_ALIAS(name, alias) \
+  lk_init_addr (#name, #alias, 1)
+#define LK_DECLARE_STRUCT_ALIAS(s_name, alias) \
+  lk_init_struct (#s_name, LK_STRUCT_ALIAS (alias), 1)
+#define LK_DECLARE_FIELD_ALIAS(s_alias, f_name, f_alias) \
+  lk_init_field (NULL, #f_name, LK_STRUCT_ALIAS (s_alias), \
+		 LK_FIELD_ALIAS (s_alias, f_alias), 1)
+
+/* Macros to retrieve private data from hashtab. Returns NULL (-1) if no entry
+   with the given ALIAS exists. The caller only needs to check for possible
+   errors if not done so at initialization.  */
+
+#define LK_ADDR(alias) \
+  (lk_find (#alias) ? (lk_find (#alias))->data.addr : -1)
+#define LK_STRUCT(alias) \
+  (lk_find (LK_STRUCT_ALIAS (alias)) \
+   ? (lk_find (LK_STRUCT_ALIAS (alias)))->data.type \
+   : NULL)
+#define LK_FIELD(s_alias, f_alias) \
+  (lk_find (LK_FIELD_ALIAS (s_alias, f_alias)) \
+   ? (lk_find (LK_FIELD_ALIAS (s_alias, f_alias)))->data.field \
+   : NULL)
+
+
+/* Definitions for architecture dependent hooks.  */
+/* Hook to read registers from the target and supply their content
+   to the regcache.  */
+typedef void (*lk_hook_get_registers) (CORE_ADDR task,
+				       struct target_ops *target,
+				       struct regcache *regcache,
+				       int regnum);
+
+/* Hook to return the per_cpu_offset of cpu CPU.  Only architectures that
+   do not use the __per_cpu_offset array to determine the offset have to
+   supply this hook.  */
+typedef CORE_ADDR (*lk_hook_get_percpu_offset) (unsigned int cpu);
+
+/* Hook to map a running task to a logical CPU.  Required if the target
+   beneath uses a different PID as struct rq.  */
+typedef unsigned int (*lk_hook_map_running_task_to_cpu) (struct thread_info *ti);
+
+struct lk_private_hooks
+{
+  /* required */
+  lk_hook_get_registers get_registers;
+
+  /* optional, required if __per_cpu_offset array is not used to determine
+     offset.  */
+  lk_hook_get_percpu_offset get_percpu_offset;
+
+  /* optional, required if the target beneath uses a different PID as struct
+     rq.  */
+  lk_hook_map_running_task_to_cpu map_running_task_to_cpu;
+};
+
+/* Helper functions to read and return a value at a given ADDRess.  */
+extern int lk_read_int (CORE_ADDR addr);
+extern unsigned int lk_read_uint (CORE_ADDR addr);
+extern LONGEST lk_read_long (CORE_ADDR addr);
+extern ULONGEST lk_read_ulong (CORE_ADDR addr);
+extern CORE_ADDR lk_read_addr (CORE_ADDR addr);
+
+/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
+   returns an array of ulongs.  The caller is responsible to free the array
+   after it is no longer needed.  */
+extern ULONGEST *lk_read_bitmap (CORE_ADDR addr, size_t size);
+
+/* Walks the bitmap BITMAP of size SIZE from bit (index) BIT.
+   Returns the index of the next set bit or SIZE, when the end of the bitmap
+   was reached.  To iterate over all set bits use macro
+   LK_BITMAP_FOR_EACH_SET_BIT defined below.  */
+extern size_t lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t bit,
+				       size_t size);
+#define LK_BITMAP_FOR_EACH_SET_BIT(bitmap, size, bit)			\
+  for ((bit) = lk_bitmap_find_next_bit ((bitmap), (size), 0);		\
+       (bit) < (size);							\
+       (bit) = lk_bitmap_find_next_bit ((bitmap), (size), (bit) + 1))
+
+/* Returns the size of BITMAP in bits.  */
+#define LK_BITMAP_SIZE(bitmap) \
+  (FIELD_SIZE (LK_FIELD (bitmap, bits)) * LK_BITS_PER_BYTE)
+
+/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP with
+   size SIZE (in bits).  */
+extern size_t lk_bitmap_hweight (ULONGEST *bitmap, size_t size);
+
+
+/* Short hand access to current gdbarchs builtin types and their
+   size (in byte).  For TYPE replace spaces " " by underscore "_", e.g.
+   "unsigned int" => "unsigned_int".  */
+#define lk_builtin_type(type)					\
+  (builtin_type (current_inferior ()->gdbarch)->builtin_##type)
+#define lk_builtin_type_size(type)		\
+  (lk_builtin_type (type)->length)
+
+/* If field FIELD is an array returns its length (in #elements).  */
+#define LK_ARRAY_LEN(field)			\
+  (FIELD_SIZE (field) / FIELD_TARGET_SIZE (field))
+
+/* Short hand access to the offset of field F_NAME in struct S_NAME.  */
+#define LK_OFFSET(s_name, f_name)		\
+  (FIELD_OFFSET (LK_FIELD (s_name, f_name)))
+
+/* Returns the container of field FNAME of struct SNAME located at address
+   ADDR.  */
+#define LK_CONTAINER_OF(addr, sname, fname)		\
+  ((addr) - LK_OFFSET (sname, fname))
+
+/* Divides nominator N by denominator D and rounds up the result.  */
+#define LK_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+
+/* Additional access macros to fields in the style of gdbtypes.h */
+/* Returns the size of field FIELD (in bytes). If FIELD is an array returns
+   the size of the whole array.  */
+#define FIELD_SIZE(field)			\
+  TYPE_LENGTH (check_typedef (FIELD_TYPE (*field)))
+
+/* Returns the size of the target type of field FIELD (in bytes).  If FIELD is
+   an array returns the size of its elements.  */
+#define FIELD_TARGET_SIZE(field)		\
+  TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (FIELD_TYPE (*field))))
+
+/* Returns the offset of field FIELD (in bytes).  */
+#define FIELD_OFFSET(field)			\
+  (FIELD_BITPOS (*field) / TARGET_CHAR_BIT)
+
+/* Provides the per_cpu_offset of cpu CPU.  If the architecture
+   provides a get_percpu_offset hook, the call is passed to it.  Otherwise
+   returns the __per_cpu_offset[CPU] element.  */
+extern CORE_ADDR lk_get_percpu_offset (unsigned int cpu);
+
+/* Tests if a given task TASK is running. Returns either the cpu-id
+   if running or LK_CPU_INVAL if not.  */
+extern unsigned int lk_task_running (CORE_ADDR task);
+#endif /* __LK_LOW_H__ */
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 4/9] Add kernel module support for linux-kernel target
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

This patch implements module support for the new linux-kernel target by
adding a target_so_ops. In addition this patch adds handling for kernel
virtual addresses. This is necessary because kernel modules, unlike
task_structs, live in kernel virtual address space. Thus addresses need
to be translated before they can be used. We achieve this by adding
an implementation for the targets to_xfer_partial hook, which translates
the addresses before passing them down to the target beneath.

gdb/ChangeLog:

    * lk-modules.h: New file.
    * lk-modules.c: New file.
    * lk-low.h (lk_hook_is_kvaddr, lk_hook_vtop)
    (lk_hook_get_module_text_offset): New arch dependent hooks.
    (sturct lk_private_hooks): Add new hooks.
    (LK_MODULES_NAME_LEN, LK_UTS_NAME_LEN): New define.
    * lk-low.c (lk-modules.h): New include.
    (lk_kvtop, restore_current_target, lk_xfer_partial): New functions.
    (lk_init_private_data): Declare needed debug symbols.
    (lk_try_push_target): Assert for new hooks and set solib_ops.
    (init_linux_kernel_ops): Add implementation for to_xfer_partial.
    * solib.c (get_solib_search_path): New function.
    * solib.h (get_solib_search_path): New export.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-modules.c.
    (HFILES_NO_SRCDIR): Add lk-modules.h.
    (ALL_TARGET_OBS): Add lk-modules.o.
    * configure.tgt (lk_target_obs): Add lk-modules.o.
---
 gdb/Makefile.in   |   4 +
 gdb/configure.tgt |   2 +-
 gdb/lk-low.c      | 110 ++++++++++++
 gdb/lk-low.h      |  28 +++
 gdb/lk-modules.c  | 498 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-modules.h  | 149 ++++++++++++++++
 gdb/solib.c       |   8 +
 gdb/solib.h       |   5 +
 8 files changed, 803 insertions(+), 1 deletion(-)
 create mode 100644 gdb/lk-modules.c
 create mode 100644 gdb/lk-modules.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a73ca26a29..7c5d26dc6c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -829,6 +829,7 @@ ALL_TARGET_OBS = \
 	linux-tdep.o \
 	lk-lists.o \
 	lk-low.o \
+	lk-modules.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1131,6 +1132,7 @@ SFILES = \
 	linespec.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1382,6 +1384,7 @@ HFILES_NO_SRCDIR = \
 	linux-tdep.h \
 	lk-lists.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2590,6 +2593,7 @@ ALLDEPFILES = \
 	linux-tdep.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 35db698860..19c5311b23 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -38,7 +38,7 @@ esac
 
 # List of objectfiles for Linux kernel support.  To be included into *-linux*
 # targets wich support Linux kernel debugging.
-lk_target_obs="lk-lists.o lk-low.o"
+lk_target_obs="lk-lists.o lk-low.o lk-modules.o"
 
 # map target info into gdb names.
 
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index e482ad08e7..c4ca472ea4 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -29,6 +29,7 @@
 #include "inferior.h"
 #include "lk-lists.h"
 #include "lk-low.h"
+#include "lk-modules.h"
 #include "objfiles.h"
 #include "observer.h"
 #include "solib.h"
@@ -534,6 +535,46 @@ lk_thread_name (struct target_ops *target, struct thread_info *ti)
   return buf;
 }
 
+/* Translate a kernel virtual address ADDR to a physical address.  */
+
+CORE_ADDR
+lk_kvtop (CORE_ADDR addr)
+{
+  CORE_ADDR pgd = lk_read_addr (LK_ADDR (init_mm)
+				+ LK_OFFSET (mm_struct, pgd));
+  return LK_HOOK->vtop (pgd, addr);
+}
+
+/* Restore current_target to TARGET.  */
+static void
+restore_current_target (void *target)
+{
+  current_target.beneath = (struct target_ops *) target;
+}
+
+/* Function for targets to_xfer_partial hook.  */
+
+enum target_xfer_status
+lk_xfer_partial (struct target_ops *ops, enum target_object object,
+		 const char *annex, gdb_byte *readbuf,
+		 const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+		 ULONGEST *xfered_len)
+{
+  enum target_xfer_status ret_val;
+  struct cleanup *old_chain = make_cleanup (restore_current_target, ops);
+
+  current_target.beneath = ops->beneath;
+
+  if (LK_HOOK->is_kvaddr (offset))
+    offset = lk_kvtop (offset);
+
+  ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+					    readbuf, writebuf, offset, len,
+					    xfered_len);
+  do_cleanups (old_chain);
+  return ret_val;
+}
+
 /* Functions to initialize and free target_ops and its private data.  As well
    as functions for targets to_open/close/detach hooks.  */
 
@@ -569,6 +610,9 @@ lk_init_private ()
 /* Initialize architecture independent private data.  Must be called
    _after_ symbol tables were initialized.  */
 
+/* FIXME: throw error more fine-grained.  */
+/* FIXME: make independent of compile options.  */
+
 static void
 lk_init_private_data ()
 {
@@ -589,10 +633,70 @@ lk_init_private_data ()
 
   LK_DECLARE_FIELD (cpumask, bits);
 
+  LK_DECLARE_FIELD (mm_struct, pgd);
+
+  LK_DECLARE_FIELD (pgd_t, pgd);
+
+  LK_DECLARE_FIELD (module, state);
+  LK_DECLARE_FIELD (module, list);
+  LK_DECLARE_FIELD (module, name);
+  LK_DECLARE_FIELD (module, source_list);
+  LK_DECLARE_FIELD (module, arch);
+  // FIXME only exists if compiled with CONFIG_SMT
+  LK_DECLARE_FIELD (module, percpu);
+  LK_DECLARE_FIELD (module, percpu_size);
+
+  /* Module offset moved to new struct module_layout with linux 4.5.
+     It must be checked in code which of this fields exist.  */
+  if (LK_DECLARE_FIELD_SILENT (module_layout, base)) /* linux 4.5+ */
+    {
+      LK_DECLARE_FIELD (module, init_layout);
+      LK_DECLARE_FIELD (module, core_layout);
+
+      LK_DECLARE_FIELD (module_layout, size);
+      LK_DECLARE_FIELD (module_layout, text_size);
+      LK_DECLARE_FIELD (module_layout, ro_size);
+
+      /* Declare silent as existence needs to be checked in code.  */
+      LK_DECLARE_FIELD_SILENT (module_layout, ro_after_init_size); /* linux 4.8+ */
+    }
+  else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */
+    {
+      LK_DECLARE_FIELD (module, module_init);
+
+      LK_DECLARE_FIELD (module, init_size);
+      LK_DECLARE_FIELD (module, core_size);
+
+      LK_DECLARE_FIELD (module, init_text_size);
+      LK_DECLARE_FIELD (module, core_text_size);
+
+      LK_DECLARE_FIELD (module, init_ro_size);
+      LK_DECLARE_FIELD (module, core_ro_size);
+    }
+  else
+    {
+      error (_("Could not find module base.  Aborting."));
+    }
+
+  LK_DECLARE_FIELD (module_use, source_list);
+  LK_DECLARE_FIELD (module_use, source);
+
+  LK_DECLARE_FIELD (uts_namespace, name);
+
+  LK_DECLARE_STRUCT_ALIAS (new_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (old_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (oldold_utsname, utsname);
+  if (LK_STRUCT (utsname) == NULL)
+    error (_("Could not find struct utsname.  Aborting."));
+  LK_DECLARE_FIELD (utsname, version);
+  LK_DECLARE_FIELD (utsname, release);
+
   LK_DECLARE_ADDR (init_task);
   LK_DECLARE_ADDR (runqueues);
   LK_DECLARE_ADDR (__per_cpu_offset);
   LK_DECLARE_ADDR (init_mm);
+  LK_DECLARE_ADDR (modules);
+  LK_DECLARE_ADDR (init_uts_ns);
 
   LK_DECLARE_ADDR_ALIAS (__cpu_online_mask, cpu_online_mask);	/* linux 4.5+ */
   LK_DECLARE_ADDR_ALIAS (cpu_online_bits, cpu_online_mask);	/* linux -4.4 */
@@ -691,12 +795,17 @@ lk_try_push_target ()
   gdbarch_lk_init_private (gdbarch);
   /* Check for required arch hooks.  */
   gdb_assert (LK_HOOK->get_registers);
+  gdb_assert (LK_HOOK->is_kvaddr);
+  gdb_assert (LK_HOOK->vtop);
+  gdb_assert (LK_HOOK->adjust_module_layout);
 
   lk_init_ptid_map ();
   lk_update_thread_list (linux_kernel_ops);
 
   if (!target_is_pushed (linux_kernel_ops))
     push_target (linux_kernel_ops);
+
+  set_solib_ops (gdbarch, lk_modules_so_ops);
 }
 
 /* Function for targets to_open hook.  */
@@ -809,6 +918,7 @@ init_linux_kernel_ops (void)
   t->to_update_thread_list = lk_update_thread_list;
   t->to_pid_to_str = lk_pid_to_str;
   t->to_thread_name = lk_thread_name;
+  t->to_xfer_partial = lk_xfer_partial;
 
   t->to_stratum = thread_stratum;
   t->to_magic = OPS_MAGIC;
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
index be8c5556df..7a20db3aaf 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -20,6 +20,7 @@
 #ifndef __LK_LOW_H__
 #define __LK_LOW_H__
 
+#include "lk-modules.h"
 #include "target.h"
 
 extern struct target_ops *linux_kernel_ops;
@@ -27,6 +28,8 @@ extern struct target_ops *linux_kernel_ops;
 /* Copy constants defined in Linux kernel.  */
 #define LK_TASK_COMM_LEN 16
 #define LK_BITS_PER_BYTE 8
+#define LK_MODULE_NAME_LEN 56
+#define LK_UTS_NAME_LEN 64
 
 /* Definitions used in linux kernel target.  */
 #define LK_CPU_INVAL -1U
@@ -204,6 +207,22 @@ typedef void (*lk_hook_get_registers) (CORE_ADDR task,
 				       struct regcache *regcache,
 				       int regnum);
 
+/* Hook to check if address ADDR is a kernel virtual address.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef int (*lk_hook_is_kvaddr) (CORE_ADDR addr);
+
+/* Hook to translate virtual adress ADDR to a pysical address using page
+   table located at PGD.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef CORE_ADDR (*lk_hook_vtop) (CORE_ADDR addr, CORE_ADDR pgd);
+
+/* Hook to get the offset between a modules base and the start of its
+   first section.
+   Equivalent to <linux>/include/linux/moduleloader.h:module_frob_arch_sections
+ */
+typedef void (*lk_hook_adjust_module_layout) (CORE_ADDR mod,
+					      lk_module *module);
+
 /* Hook to return the per_cpu_offset of cpu CPU.  Only architectures that
    do not use the __per_cpu_offset array to determine the offset have to
    supply this hook.  */
@@ -218,6 +237,15 @@ struct lk_private_hooks
   /* required */
   lk_hook_get_registers get_registers;
 
+  /* required */
+  lk_hook_is_kvaddr is_kvaddr;
+
+  /* required */
+  lk_hook_vtop vtop;
+
+  /* reqired */
+  lk_hook_adjust_module_layout adjust_module_layout;
+
   /* optional, required if __per_cpu_offset array is not used to determine
      offset.  */
   lk_hook_get_percpu_offset get_percpu_offset;
diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
new file mode 100644
index 0000000000..ad5b22e834
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,498 @@
+/* Handle Linux kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "common/filestuff.h"
+#include "filenames.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_regex.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "readline/readline.h"
+#include "solib.h"
+#include "solist.h"
+#include "utils.h"
+
+#include <unordered_map>
+#include <string>
+
+struct target_so_ops *lk_modules_so_ops = NULL;
+
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_section_type::clear ()
+{
+  base = 0;
+  offset = 0;
+  size = 0;
+}
+
+/* Constructor for module_layout located at address LAYOUT.
+   Linux 4.5+ only! */
+
+lk_module_layout::lk_module_layout (CORE_ADDR layout)
+{
+  base = lk_read_addr (layout + LK_OFFSET (module_layout, base));
+  size = lk_read_uint (layout + LK_OFFSET (module_layout, size));
+
+  text.base = base;
+  text.size = lk_read_uint (layout + LK_OFFSET (module_layout, text_size));
+
+  ro_data.base = base + text.size;
+  ro_data.size = lk_read_uint (layout + LK_OFFSET (module_layout, ro_size));
+
+  if (LK_FIELD (module_layout, ro_after_init_size)) /* linux 4.8+ */
+    {
+      ro_after_init_data.base = base + ro_data.size;
+      ro_after_init_data.size
+	= lk_read_uint (layout + LK_OFFSET (module_layout, ro_after_init_size));
+
+      rw_data.base = base + ro_after_init_data.size;
+    }
+  else /* linux 4.5 - 4.8 */
+    {
+      rw_data.base = base + ro_data.size;
+    }
+
+  rw_data.size = size;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_layout::clear ()
+{
+  base = 0;
+  size = 0;
+
+  text.clear ();
+  ro_data.clear ();
+  rw_data.clear ();
+}
+
+/* Init MODULE from struct module located at address MOD.
+   Linux -4.4 only!  */
+
+void
+lk_module_init_module_from_struct_module (CORE_ADDR mod, lk_module *module)
+{
+  module->core = new lk_module_layout;
+  module->init = new lk_module_layout;
+
+  /* Short-hand access.  */
+  lk_module_layout *core = module->core;
+  lk_module_layout *init = module->init;
+
+  /* Core layout.  */
+  core->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
+  core->size = lk_read_uint (mod + LK_OFFSET (module, core_size));
+
+  core->text.base = core->base;
+  core->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size));
+
+  core->ro_data.base = core->base + core->text.size;
+  core->ro_data.size = lk_read_uint (mod + LK_OFFSET (module, core_ro_size));
+
+  core->rw_data.base = core->base + core->ro_data.size;
+  core->rw_data.size = core->size;
+
+  /* Init layout.  */
+  init->base = lk_read_addr (mod + LK_OFFSET (module, module_init));
+  init->size = lk_read_uint (mod + LK_OFFSET (module, init_size));
+
+  init->text.base = init->base;
+  init->text.size = lk_read_uint (mod + LK_OFFSET (module, init_text_size));
+
+  init->ro_data.base = init->base + init->text.size;
+  init->ro_data.size = lk_read_uint (mod + LK_OFFSET (module, init_ro_size));
+
+  init->rw_data.base = init->base + init->ro_data.size;
+  init->rw_data.size = init->size;
+}
+
+/* Constructor for kernel module located at address MOD.  */
+
+lk_module::lk_module (CORE_ADDR mod)
+{
+  state = (lk_module_state) lk_read_int (mod + LK_OFFSET (module, state));
+
+  if (LK_FIELD (module, module_core)) /* linux -4.4 */
+    {
+      lk_module_init_module_from_struct_module (mod, this);
+    }
+  else /* linux 4.5+ */
+    {
+      core = new lk_module_layout (mod + LK_OFFSET (module, core_layout));
+      init = new lk_module_layout (mod + LK_OFFSET (module, init_layout));
+    }
+
+  // FIXME only exists if compiled with CONFIG_SMT
+  percpu.base = lk_read_addr (mod + LK_OFFSET (module, percpu));
+  percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
+
+  /* Architectures can adjust the sections in the kernel (via
+     module_frob_arch_sections in <linux>/kernelmodule.c:layout_and_allocate).
+     So give them the chance to do the same in GDB, too.  */
+  LK_HOOK->adjust_module_layout (mod, this);
+}
+
+lk_module::~lk_module ()
+{
+  delete core;
+  delete init;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module::clear ()
+{
+  state = LK_MODULE_STATE_INVALID;
+
+  core->clear ();
+  init->clear ();
+
+  percpu.clear ();
+}
+
+/* Check if debug info for module NAME is loaded.  */
+
+bool
+lk_modules_debug_info_loaded (const std::string &name)
+{
+  struct so_list *so;
+
+  for (so = master_so_list (); so; so = so->next)
+    {
+      if (name == so->so_original_name)
+	return (so->symbols_loaded && objfile_has_symbols (so->objfile));
+    }
+
+  return false;
+}
+
+/* Replace tags, like '$release', with corresponding data in
+   solib_search_path.
+
+   Known tags:
+   $release	Linux kernel release, same as 'uname -r'
+
+   Returns the expanded path.  */
+
+static std::string
+lk_modules_expand_search_path ()
+{
+  char release[LK_UTS_NAME_LEN + 1];
+  CORE_ADDR utsname;
+
+  utsname = LK_ADDR (init_uts_ns) + LK_OFFSET (uts_namespace, name);
+  read_memory_string (utsname + LK_OFFSET (utsname, release),
+		      release, LK_UTS_NAME_LEN);
+  release[LK_UTS_NAME_LEN] = '\0';
+
+  std::string search_path = get_solib_search_path ();
+  substitute_path_component (search_path, "$release", release);
+
+  return search_path;
+}
+
+/* With kernel modules there is the problem that the kernel only stores
+   the modules name but not the path from wich it was loaded from.
+   Thus we need to map the name to a path GDB can read from.  We use file
+   modules.order to do so.  It is created by kbuild containing the order in
+   which the modules appear in the Makefile and is also used by modprobe.
+   The drawback of this method is that it needs the modules.order file and
+   all relative paths, starting from <solib-search-path>, must be exactly the
+   same as decribed in it.  */
+
+/* Open file <solib-search-path>/modules.order and return its file
+   pointer.  */
+
+FILE *
+lk_modules_open_mod_order ()
+{
+  FILE *mod_order;
+  std::string filename = concat_path (lk_modules_expand_search_path (),
+				      "modules.order");
+  mod_order = gdb_fopen_cloexec (filename.c_str (), "r");
+
+  if (!mod_order)
+    {
+      error (_("\
+Can not find file module.order at %s \
+to load module symbol files.\n\
+Please check if solib-search-path is set correctly."),
+	     filename.c_str ());
+    }
+
+  return mod_order;
+}
+
+/* Build map between module name and path to binary file by reading file
+   modules.order.  Returns unordered_map with module name as key and its
+   path as value.  */
+
+std::unordered_map<std::string, std::string>
+lk_modules_build_path_map ()
+{
+  std::unordered_map<std::string, std::string> umap;
+  FILE *mod_order;
+  struct cleanup *old_chain;
+  char line[SO_NAME_MAX_PATH_SIZE + 1];
+
+  mod_order = lk_modules_open_mod_order ();
+  old_chain = make_cleanup_fclose (mod_order);
+
+  line[SO_NAME_MAX_PATH_SIZE] = '\0';
+  std::string search_path = lk_modules_expand_search_path ();
+  while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order))
+    {
+      /* Remove trailing newline.  */
+      line[strlen (line) - 1] = '\0';
+
+      std::string name = lbasename (line);
+
+      /* 3 = strlen (".ko").  */
+      if (!endswith (name.c_str (), ".ko")
+	  || name.length () >= LK_MODULE_NAME_LEN + 3)
+	continue;
+
+      name = name.substr (0, name.length () - 3);
+
+      /* Kernel modules are named after the files they are stored in with
+	 all minus '-' replaced by underscore '_'.  Do the same to enable
+	 mapping.  */
+      for (size_t p = name.find('-'); p != std::string::npos;
+	   p = name.find ('-', p + 1))
+	name[p] = '_';
+
+      umap[name] = concat_path(search_path, line);
+    }
+
+  do_cleanups (old_chain);
+  return umap;
+}
+
+/* Function for current_sos hook.  */
+
+struct so_list *
+lk_modules_current_sos (void)
+{
+  CORE_ADDR modules, next;
+  FILE *mod_order;
+  struct so_list *list = NULL;
+  std::unordered_map<std::string, std::string> umap;
+
+  umap = lk_modules_build_path_map ();
+  modules = LK_ADDR (modules);
+  lk_list_for_each (next, modules, module, list)
+    {
+      char name[LK_MODULE_NAME_LEN];
+      CORE_ADDR mod, name_addr;
+
+      mod = LK_CONTAINER_OF (next, module, list);
+      name_addr = mod + LK_OFFSET (module, name);
+      read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
+
+      if (umap.count (name))
+	{
+	  struct so_list *newso = XCNEW (struct so_list);
+
+	  newso->next = list;
+	  list = newso;
+	  newso->lm_info = new lk_module (mod);
+	  strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE);
+	  strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE);
+	  newso->pspace = current_program_space;
+	}
+    }
+
+  return list;
+}
+
+/* Relocate target_section GDB_SEC using section type LK_SEC.  Helper
+   function for lk_modules_relocate_section_addresses.  */
+
+void
+lk_modules_relocate_sec (struct target_section *gdb_sec,
+			 lk_module_section_type &lk_sec)
+{
+  unsigned int alignment = 1 << gdb_sec->the_bfd_section->alignment_power;
+  CORE_ADDR base = lk_sec.base + lk_sec.offset;
+
+  /* Adjust offset to section alignment.  */
+  if (base % alignment != 0)
+    base += alignment - (base % alignment);
+
+  gdb_sec->addr += base;
+  gdb_sec->endaddr += base;
+  lk_sec.offset += gdb_sec->endaddr - gdb_sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  /* Remove unmapped sections.  */
+  if (streq (name, ".modinfo") || streq (name, "__versions")
+      || (startswith (name, ".init")
+	  && module->state != LK_MODULE_STATE_COMING))
+    {
+      // FIXME Not mapping a section != unmapping a section
+      //
+      // In particular this causes GDB to map the "unmapped" section at the
+      // same address as the last mapped section (e.g. init.text starts at
+      // the same address like .text).  Thus printing symbols definied in the
+      // unmapped section returnes random values instead of <opimized out>.
+      return;
+    }
+
+  /* module->percpu.base points to data of cpu0.  To access the data of
+     a cpu other than cpu0 the corresponding percpu offset needs to added
+     by hand.  */
+  if (endswith (name, ".percpu"))
+    {
+      lk_modules_relocate_sec (sec, module->percpu);
+      return;
+    }
+
+  lk_module_layout *layout;
+  if (startswith (name, ".init"))
+    layout = module->init;
+  else
+    layout = module->core;
+
+  if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, layout->text);
+  else if (endswith (name, ".ro_after_init"))
+    lk_modules_relocate_sec (sec, layout->ro_after_init_data);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, layout->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, layout->rw_data);
+
+  /* Set address range displayed with 'info shared' to match core_layout.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = module->core->base;
+      so->addr_high = module->core->base + module->core->size;
+    }
+}
+
+/* Function for free_so hook.  */
+
+void
+lk_modules_free_so (struct so_list *so)
+{
+  delete so->lm_info;
+}
+
+/* Function for clear_so hook.  */
+
+void
+lk_modules_clear_so (struct so_list *so)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  gdb_assert (module != NULL);
+
+  module->clear ();
+}
+
+/* Function for clear_solib hook.  */
+
+void
+lk_modules_clear_solib ()
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+void
+lk_modules_create_inferior_hook (int from_tty)
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+int
+lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+  return 0;
+}
+
+/* Function for same hook.  Assume the module to be different if its
+   state changed.  Otherwise there might be problems with pruning the .init
+   sections if the module was first seen during init.  */
+
+int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  lk_module_state gdb_state = ((lk_module *) gdb->lm_info)->state;
+  lk_module_state inf_state = ((lk_module *) inf->lm_info)->state;
+
+  return streq (gdb->so_name, inf->so_name) && gdb_state == inf_state;
+}
+
+/* Initialize linux modules solib target.  */
+
+void
+init_lk_modules_so_ops (void)
+{
+  struct target_so_ops *t;
+
+  if (lk_modules_so_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_so_ops);
+  t->relocate_section_addresses = lk_modules_relocate_section_addresses;
+  t->free_so = lk_modules_free_so;
+  t->clear_so = lk_modules_clear_so;
+  t->clear_solib = lk_modules_clear_solib;
+  t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
+  t->current_sos = lk_modules_current_sos;
+  t->bfd_open = solib_bfd_open;
+  t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
+  t->same = lk_modules_same;
+
+  lk_modules_so_ops = t;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_lk_modules;
+
+void
+_initialize_lk_modules (void)
+{
+  init_lk_modules_so_ops ();
+}
diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h
new file mode 100644
index 0000000000..ef54af4221
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,149 @@
+/* Handle kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_MODULES_H__
+#define __LK_MODULES_H__
+
+#include "solist.h"
+
+
+extern struct target_so_ops *lk_modules_so_ops;
+
+
+/* Copied from <linux>/include/linux/module.h.  */
+
+enum
+lk_module_state
+{
+  LK_MODULE_STATE_INVALID = -1, /* Invalid state, GDB only.  */
+  LK_MODULE_STATE_LIVE,		/* Normal state. */
+  LK_MODULE_STATE_COMING,	/* Full formed, running module_init. */
+  LK_MODULE_STATE_GOING,	/* Going away. */
+  LK_MODULE_STATE_UNFORMED,	/* Still setting it up. */
+};
+
+/* The kernel modules .ko files are only partially linked, i.e. they contain
+   section headers but no program headers.  When the module is loaded the
+   sections are relocated by the kernel (<linux>/kernel/module.c:load_module
+   & layout_and_allocate & layout_sections).  Here the kernel destinguishes
+   between different section types
+
+   * text (SHF_EXECINSTR | SHF_ALLOC)
+   * read-only data (SHF_ALLOC & !SHF_WRITE)
+   * read-only-after-init data (SHF_ALLOC | SHF_RO_AFTER_INIT)
+   * read-write data (SHF_WRITE | SHF_ALLOC)
+
+   combined in module layouts.
+
+   There are two module layouts, init and core.  The init module layout
+   contains all sections where the section name starts with ".init".  The
+   content of the init module layout gets unlinked once the module is
+   loaded.  The core module layout contains the "normal" text and data
+   needed during module runtime.
+
+   Furthermore there can be a
+
+   * data..percpu (<linux>/kernel/module.c:percpu_modalloc)
+
+   section which gets relocated to the other percpu data. */
+
+struct lk_module_section_type
+{
+  /* Lowest address of this section type.  */
+  CORE_ADDR base = 0;
+
+  /* Size of this section type.  Calculated by GDB during relocation.  */
+  CORE_ADDR offset = 0;
+
+  /* Accumulated size of all section types "below".  Read from kernel
+     (except for rw_data).  */
+  unsigned int size = 0;
+
+  /* Reset this module_section to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+};
+
+/* Copied from <linux>/include/linux/module.h: struct module_layout.  */
+
+struct lk_module_layout
+{
+  lk_module_layout () = default;
+  lk_module_layout (CORE_ADDR layout);
+
+  /* Reset this module_layout to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* Lowest address of this module layout.  */
+  CORE_ADDR base = 0;
+
+  /* Total size.  */
+  unsigned int size = 0;
+
+  /* Executable text.
+     text.size = text.  */
+  lk_module_section_type text;
+
+  /* Read-only data.
+     ro_data.size = text + ro_data.  */
+  lk_module_section_type ro_data;
+
+  /* Read-only-after-init data.
+     ro_after_init_data.size = text + ro_data + ro_after_init_data.  */
+  lk_module_section_type ro_after_init_data; /* linux 4.8+ */
+
+  /* Writable data.
+     rw_data.size = layout.size.  */
+  lk_module_section_type rw_data;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lk_module : public lm_info_base
+{
+  lk_module () = delete;
+  lk_module (CORE_ADDR mod);
+
+  ~lk_module ();
+
+  /* Reset this module to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* This modules state as seen from the kernel.  */
+  lk_module_state state = LK_MODULE_STATE_INVALID;
+
+  /* "core" layout, i.e. text + data after init.  */
+  lk_module_layout *core;
+
+  /* "init" layout, i.e. text + data only needed during init
+     (LK_MODULE_STATE_COMING).  It is unmapped after initialization.  */
+  lk_module_layout *init;
+
+  /* Per CPU data.  Mapped together with "ordinary" per cpu data.  */
+  lk_module_section_type percpu;
+};
+
+
+/* Check if debug info for module NAME are loaded.  Needed by lsmod command.  */
+
+extern bool lk_modules_debug_info_loaded (const std::string &name);
+
+#endif /*  __LK_MODULES_H__  */
diff --git a/gdb/solib.c b/gdb/solib.c
index 491c18a685..4e72c929ce 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -104,6 +104,14 @@ show_solib_search_path (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* see solib.h.  */
+
+const char *
+get_solib_search_path ()
+{
+  return solib_search_path ? solib_search_path : "";
+}
+
 /* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue.  */
 #if (HAVE_DOS_BASED_FILE_SYSTEM)
 #  define DOS_BASED_FILE_SYSTEM 1
diff --git a/gdb/solib.h b/gdb/solib.h
index e91fb75d3d..b7608d275f 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -31,6 +31,11 @@ struct program_space;
 /* List of known shared objects */
 #define so_list_head current_program_space->so_list
 
+/* Returns the solib_search_path.  The returned string is malloc'ed and must be
+   freed by the caller.  */
+
+extern const char *get_solib_search_path ();
+
 /* Called when we free all symtabs, to free the shared library information
    as well.  */
 
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 7/9] Add privileged registers for s390x
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (2 preceding siblings ...)
  2017-06-12 17:08 ` [RFC v4 5/9] Add commands for linux-kernel target Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-19 13:34   ` Yao Qi
  2017-06-12 17:08 ` [RFC v4 3/9] Add basic Linux kernel support Philipp Rudo
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

The Linux kernel and thus the linux-kernel target needs access to S390x's
privileged registers. Define new features and prepare s390-tdep.* to use
them in new Linux kernel code.

gdb/ChangeLog:

	* features/s390-cr.xml: New file.
	* features/s390x-cr-linux64.xml: New file
	* features/s390x-vxcr-linux64.xml: New file
	* features/Makefile: Add s390x-cr-linux64-expedite and
	s390x-vxcr-linux64-expedite
	(WICH): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
	(XMLTOC): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
	* features/s390x-cr-linux64.c: Generated.
	* features/s390x-vxcr-linux64.c: Generated.
	* regformats/s390x-cr-linux64.dat: Generated.
	* regformats/s390x-vxcr-linux64.dat: Generated.
	* s390-tdep.h: Define regnums for control registers.
	(S390_NUM_REGS): Adjust.
	* s390-tdep.c: (s390_dwarf_regmat): Add control registers.
---
 gdb/features/Makefile                 |  11 ++-
 gdb/features/s390-cr.xml              |  26 ++++++
 gdb/features/s390x-cr-linux64.c       |  99 ++++++++++++++++++++
 gdb/features/s390x-cr-linux64.xml     |  24 +++++
 gdb/features/s390x-vxcr-linux64.c     | 169 ++++++++++++++++++++++++++++++++++
 gdb/features/s390x-vxcr-linux64.xml   |  25 +++++
 gdb/regformats/s390x-cr-linux64.dat   |  76 +++++++++++++++
 gdb/regformats/s390x-vxcr-linux64.dat | 108 ++++++++++++++++++++++
 gdb/s390-tdep.c                       |   8 +-
 gdb/s390-tdep.h                       |  23 ++++-
 10 files changed, 562 insertions(+), 7 deletions(-)
 create mode 100644 gdb/features/s390-cr.xml
 create mode 100644 gdb/features/s390x-cr-linux64.c
 create mode 100644 gdb/features/s390x-cr-linux64.xml
 create mode 100644 gdb/features/s390x-vxcr-linux64.c
 create mode 100644 gdb/features/s390x-vxcr-linux64.xml
 create mode 100644 gdb/regformats/s390x-cr-linux64.dat
 create mode 100644 gdb/regformats/s390x-vxcr-linux64.dat

diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 3bc8b5ae85..ac34c4efe2 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -78,6 +78,7 @@ WHICH = aarch64 \
 	s390-linux32v2 s390-linux64v2 s390x-linux64v2 \
 	s390-te-linux64 s390x-te-linux64 s390-vx-linux64 s390x-vx-linux64 \
 	s390-tevx-linux64 s390x-tevx-linux64 \
+	s390x-cr-linux64 s390x-vxcr-linux64 \
 	tic6x-c64xp tic6x-c64x tic6x-c62x \
 	tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
 
@@ -111,6 +112,8 @@ s390x-linux64v2-expedite = r14,r15,pswa
 s390x-te-linux64-expedite = r14,r15,pswa
 s390x-vx-linux64-expedite = r14,r15,pswa
 s390x-tevx-linux64-expedite = r14,r15,pswa
+s390x-cr-linux64-expedite = r14,r15,pswa
+s390x-vxcr-linux64-expedite = r14,r15,pswa
 tic6x-c64xp-expedite = A15,PC
 tic6x-c64x-expedite = A15,PC
 tic6x-c62x-expedite = A15,PC
@@ -220,14 +223,16 @@ XMLTOC = \
 	s390-linux64v1.xml \
 	s390-linux64v2.xml \
 	s390-te-linux64.xml \
+	s390-vx-linux64.xml \
+	s390-tevx-linux64.xml \
 	s390x-linux64.xml \
 	s390x-linux64v1.xml \
 	s390x-linux64v2.xml \
 	s390x-te-linux64.xml \
-	s390-tevx-linux64.xml \
-	s390-vx-linux64.xml \
-	s390x-tevx-linux64.xml \
 	s390x-vx-linux64.xml \
+	s390x-cr-linux64.xml \
+	s390x-tevx-linux64.xml \
+	s390x-vxcr-linux64.xml \
 	tic6x-c62x-linux.xml \
 	tic6x-c62x.xml \
 	tic6x-c64x-linux.xml \
diff --git a/gdb/features/s390-cr.xml b/gdb/features/s390-cr.xml
new file mode 100644
index 0000000000..15ae4f9af7
--- /dev/null
+++ b/gdb/features/s390-cr.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.cr">
+  <reg name="cr0" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr1" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr2" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr3" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr4" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr5" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr6" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr7" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr8" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr9" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr10" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr11" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr12" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr13" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr14" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr15" bitsize="64" type="uint64" group="control"/>
+</feature>
diff --git a/gdb/features/s390x-cr-linux64.c b/gdb/features/s390x-cr-linux64.c
new file mode 100644
index 0000000000..caf535bb64
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.c
@@ -0,0 +1,99 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: s390x-cr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_cr_linux64;
+static void
+initialize_tdesc_s390x_cr_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+  tdesc_create_reg (feature, "cr0", 51, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr1", 52, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr2", 53, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr3", 54, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr4", 55, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr5", 56, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr6", 57, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr7", 58, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr8", 59, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr9", 60, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr10", 61, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr11", 62, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr12", 63, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr13", 64, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr14", 65, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr15", 66, 1, "control", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+  tdesc_create_reg (feature, "timer", 67, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todcmp", 68, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todpreg", 69, 1, "system", 32, "uint32");
+  tdesc_create_reg (feature, "prefix", 70, 1, "system", 32, "uint32");
+
+  tdesc_s390x_cr_linux64 = result;
+}
diff --git a/gdb/features/s390x-cr-linux64.xml b/gdb/features/s390x-cr-linux64.xml
new file mode 100644
index 0000000000..d38bc35030
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 64-bit kernel-level code.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:64-bit</architecture>
+  <xi:include href="s390x-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+  <xi:include href="s390-cr.xml"/>
+
+  <feature name="org.gnu.gdb.s390.privileged">
+    <reg name="timer" bitsize="64" type="uint64" group="system"/>
+    <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+    <reg name="todpreg" bitsize="32" type="uint32" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/features/s390x-vxcr-linux64.c b/gdb/features/s390x-vxcr-linux64.c
new file mode 100644
index 0000000000..36453d275c
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.c
@@ -0,0 +1,169 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: s390x-vxcr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_vxcr_linux64;
+static void
+initialize_tdesc_s390x_vxcr_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+  struct tdesc_type *field_type;
+  struct tdesc_type *type;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.vx");
+  field_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_create_vector (feature, "v4f", field_type, 4);
+
+  field_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_create_vector (feature, "v2d", field_type, 2);
+
+  field_type = tdesc_named_type (feature, "int8");
+  tdesc_create_vector (feature, "v16i8", field_type, 16);
+
+  field_type = tdesc_named_type (feature, "int16");
+  tdesc_create_vector (feature, "v8i16", field_type, 8);
+
+  field_type = tdesc_named_type (feature, "int32");
+  tdesc_create_vector (feature, "v4i32", field_type, 4);
+
+  field_type = tdesc_named_type (feature, "int64");
+  tdesc_create_vector (feature, "v2i64", field_type, 2);
+
+  type = tdesc_create_union (feature, "vec128");
+  field_type = tdesc_named_type (feature, "v4f");
+  tdesc_add_field (type, "v4_float", field_type);
+  field_type = tdesc_named_type (feature, "v2d");
+  tdesc_add_field (type, "v2_double", field_type);
+  field_type = tdesc_named_type (feature, "v16i8");
+  tdesc_add_field (type, "v16_int8", field_type);
+  field_type = tdesc_named_type (feature, "v8i16");
+  tdesc_add_field (type, "v8_int16", field_type);
+  field_type = tdesc_named_type (feature, "v4i32");
+  tdesc_add_field (type, "v4_int32", field_type);
+  field_type = tdesc_named_type (feature, "v2i64");
+  tdesc_add_field (type, "v2_int64", field_type);
+  field_type = tdesc_named_type (feature, "uint128");
+  tdesc_add_field (type, "uint128", field_type);
+
+  tdesc_create_reg (feature, "v0l", 51, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v1l", 52, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v2l", 53, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v3l", 54, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v4l", 55, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v5l", 56, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v6l", 57, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v7l", 58, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v8l", 59, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v9l", 60, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v10l", 61, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v11l", 62, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v12l", 63, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v13l", 64, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v14l", 65, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v15l", 66, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v16", 67, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v17", 68, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v18", 69, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v19", 70, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v20", 71, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v21", 72, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v22", 73, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v23", 74, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v24", 75, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v25", 76, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v26", 77, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v27", 78, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v28", 79, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v29", 80, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v30", 81, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v31", 82, 1, NULL, 128, "vec128");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+  tdesc_create_reg (feature, "cr0", 83, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr1", 84, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr2", 85, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr3", 86, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr4", 87, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr5", 88, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr6", 89, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr7", 90, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr8", 91, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr9", 92, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr10", 93, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr11", 94, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr12", 95, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr13", 96, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr14", 97, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr15", 98, 1, "control", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+  tdesc_create_reg (feature, "timer", 99, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todcmp", 100, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todpreg", 101, 1, "system", 32, "uint32");
+  tdesc_create_reg (feature, "prefix", 102, 1, "system", 32, "uint32");
+
+  tdesc_s390x_vxcr_linux64 = result;
+}
diff --git a/gdb/features/s390x-vxcr-linux64.xml b/gdb/features/s390x-vxcr-linux64.xml
new file mode 100644
index 0000000000..30a1cebfdc
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 64-bit kernel-level code.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:64-bit</architecture>
+  <xi:include href="s390x-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+  <xi:include href="s390-vx.xml"/>
+  <xi:include href="s390-cr.xml"/>
+
+  <feature name="org.gnu.gdb.s390.privileged">
+    <reg name="timer" bitsize="64" type="uint64" group="system"/>
+    <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+    <reg name="todpreg" bitsize="32" type="uint32" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/regformats/s390x-cr-linux64.dat b/gdb/regformats/s390x-cr-linux64.dat
new file mode 100644
index 0000000000..e8a4dd781e
--- /dev/null
+++ b/gdb/regformats/s390x-cr-linux64.dat
@@ -0,0 +1,76 @@
+# THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-cr-linux64.xml
+name:s390x_cr_linux64
+xmltarget:s390x-cr-linux64.xml
+expedite:r14,r15,pswa
+64:pswm
+64:pswa
+64:r0
+64:r1
+64:r2
+64:r3
+64:r4
+64:r5
+64:r6
+64:r7
+64:r8
+64:r9
+64:r10
+64:r11
+64:r12
+64:r13
+64:r14
+64:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+32:todpreg
+32:prefix
diff --git a/gdb/regformats/s390x-vxcr-linux64.dat b/gdb/regformats/s390x-vxcr-linux64.dat
new file mode 100644
index 0000000000..8c28fd1f69
--- /dev/null
+++ b/gdb/regformats/s390x-vxcr-linux64.dat
@@ -0,0 +1,108 @@
+# THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-vxcr-linux64.xml
+name:s390x_vxcr_linux64
+xmltarget:s390x-vxcr-linux64.xml
+expedite:r14,r15,pswa
+64:pswm
+64:pswa
+64:r0
+64:r1
+64:r2
+64:r3
+64:r4
+64:r5
+64:r6
+64:r7
+64:r8
+64:r9
+64:r10
+64:r11
+64:r12
+64:r13
+64:r14
+64:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
+64:v0l
+64:v1l
+64:v2l
+64:v3l
+64:v4l
+64:v5l
+64:v6l
+64:v7l
+64:v8l
+64:v9l
+64:v10l
+64:v11l
+64:v12l
+64:v13l
+64:v14l
+64:v15l
+128:v16
+128:v17
+128:v18
+128:v19
+128:v20
+128:v21
+128:v22
+128:v23
+128:v24
+128:v25
+128:v26
+128:v27
+128:v28
+128:v29
+128:v30
+128:v31
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+32:todpreg
+32:prefix
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 1a9557bb3f..7d2906f294 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -845,9 +845,11 @@ static const short s390_dwarf_regmap[] =
   S390_F8_REGNUM, S390_F10_REGNUM, S390_F12_REGNUM, S390_F14_REGNUM,
   S390_F9_REGNUM, S390_F11_REGNUM, S390_F13_REGNUM, S390_F15_REGNUM,
 
-  /* 32-47: Control Registers (not mapped).  */
-  -1, -1, -1, -1, -1, -1, -1, -1,
-  -1, -1, -1, -1, -1, -1, -1, -1,
+  /* 32-47: Control Registers.  */
+  S390_CR0_REGNUM, S390_CR1_REGNUM, S390_CR2_REGNUM, S390_CR3_REGNUM,
+  S390_CR4_REGNUM, S390_CR5_REGNUM, S390_CR6_REGNUM, S390_CR7_REGNUM,
+  S390_CR8_REGNUM, S390_CR9_REGNUM, S390_CR10_REGNUM, S390_CR11_REGNUM,
+  S390_CR12_REGNUM, S390_CR13_REGNUM, S390_CR14_REGNUM, S390_CR15_REGNUM,
 
   /* 48-63: Access Registers.  */
   S390_A0_REGNUM, S390_A1_REGNUM, S390_A2_REGNUM, S390_A3_REGNUM,
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
index d586237dc7..df9a3ac56d 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -273,9 +273,30 @@ enum
 #define S390_V29_REGNUM 119
 #define S390_V30_REGNUM 120
 #define S390_V31_REGNUM 121
+/* Control registers.  */
+#define S390_TIMER_REGNUM 122
+#define S390_TODCMP_REGNUM 123
+#define S390_TODPREG_REGNUM 124
+#define S390_CR0_REGNUM 125
+#define S390_CR1_REGNUM 126
+#define S390_CR2_REGNUM 127
+#define S390_CR3_REGNUM 128
+#define S390_CR4_REGNUM 129
+#define S390_CR5_REGNUM 130
+#define S390_CR6_REGNUM 131
+#define S390_CR7_REGNUM 132
+#define S390_CR8_REGNUM 133
+#define S390_CR9_REGNUM 134
+#define S390_CR10_REGNUM 135
+#define S390_CR11_REGNUM 136
+#define S390_CR12_REGNUM 137
+#define S390_CR13_REGNUM 138
+#define S390_CR14_REGNUM 139
+#define S390_CR15_REGNUM 140
+#define S390_PREFIX_REGNUM 141
 
 /* Total.  */
-#define S390_NUM_REGS 122
+#define S390_NUM_REGS 142
 #define S390_NUM_GPRS 16
 #define S390_NUM_FPRS 16
 
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 5/9] Add commands for linux-kernel target
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 4/9] Add kernel module support for linux-kernel target Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 7/9] Add privileged registers for s390x Philipp Rudo
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

This patch implements a "lsmod", "struct" and, "offset" command to work with
the new linux-kernel target. The commands are a handy byproduct from
development and crude hacks. I don't expect them to be accepted in the
current state.  Nevertheless there needs to be an discussion on how and
where (see gdb/python scrips in kernel sources) to implement them. So here
is the start for it.

gdb/Changelog:

    * lk-cmds.h: New file.
    * lk-cmds.c: New file.
    * lk-low.c: Include lk-cmds.h.
    (lk_try_push_target): Init commands.
    * typeprint.c: Remove unnecessary forward declarations.
    (whatis_exp): Remove static.
    * typeprint.h (whatis_exp): New export.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-cmds.c.
    (HFILES_NO_SRCDIR): Add lk-cmds.h.
    (ALL_TARGET_OBS): Add lk-cmds.o.
    * configure.tgt (lk_target_obs): Add lk-cmds.o.
---
 gdb/Makefile.in   |   4 +
 gdb/configure.tgt |   2 +-
 gdb/lk-cmds.c     | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-cmds.h     |  25 ++++++
 gdb/lk-low.c      |   3 +
 gdb/typeprint.c   |   8 +-
 gdb/typeprint.h   |   2 +
 7 files changed, 289 insertions(+), 8 deletions(-)
 create mode 100644 gdb/lk-cmds.c
 create mode 100644 gdb/lk-cmds.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7c5d26dc6c..ccc1db596e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -827,6 +827,7 @@ ALL_TARGET_OBS = \
 	iq2000-tdep.o \
 	linux-record.o \
 	linux-tdep.o \
+	lk-cmds.o \
 	lk-lists.o \
 	lk-low.o \
 	lk-modules.o \
@@ -1130,6 +1131,7 @@ SFILES = \
 	jit.c \
 	language.c \
 	linespec.c \
+	lk-cmds.c \
 	lk-lists.c \
 	lk-low.c \
 	lk-modules.c \
@@ -1382,6 +1384,7 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-cmds.h \
 	lk-lists.h \
 	lk-low.h \
 	lk-modules.h \
@@ -2591,6 +2594,7 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.c \
+	lk-cmds.c \
 	lk-lists.c \
 	lk-low.c \
 	lk-modules.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 19c5311b23..efc67b2166 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -38,7 +38,7 @@ esac
 
 # List of objectfiles for Linux kernel support.  To be included into *-linux*
 # targets wich support Linux kernel debugging.
-lk_target_obs="lk-lists.o lk-low.o lk-modules.o"
+lk_target_obs="lk-cmds.o lk-lists.o lk-low.o lk-modules.o"
 
 # map target info into gdb names.
 
diff --git a/gdb/lk-cmds.c b/gdb/lk-cmds.c
new file mode 100644
index 0000000000..e63e07492b
--- /dev/null
+++ b/gdb/lk-cmds.c
@@ -0,0 +1,253 @@
+/* Commands for Linux kernel target.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "cli/cli-decode.h"
+#include "gdbcore.h"
+#include "inferior.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "typeprint.h"
+#include "valprint.h"
+
+
+/* Print line for module MOD to UIOUT for lsmod command.  */
+
+static bool
+lk_lsmod_print_single_module (struct ui_out *uiout, CORE_ADDR mod)
+{
+  char *src_list, name[LK_MODULE_NAME_LEN + 2];
+  CORE_ADDR next, src_list_addr;
+  size_t src_num, src_size, list_len;
+  bool loaded;
+  struct cleanup *ui_chain;
+
+
+  /* Get name.  */
+  read_memory_string (mod + LK_OFFSET (module, name), name + 1,
+		      LK_MODULE_NAME_LEN);
+  loaded = lk_modules_debug_info_loaded (name + 1);
+  name[0] = loaded ? ' ' : '*' ;
+  name[LK_MODULE_NAME_LEN + 1] = '\0';
+
+  /* Get size.  */
+  if (LK_FIELD (module, module_core))
+    {
+      src_size = lk_read_uint (mod + LK_OFFSET (module, init_size));
+      src_size += lk_read_uint (mod + LK_OFFSET (module, core_size));
+    }
+  else
+    {
+      src_size = lk_read_uint (mod + LK_OFFSET (module, init_layout)
+			       + LK_OFFSET (module_layout, size));
+      src_size += lk_read_uint (mod + LK_OFFSET (module, core_layout)
+				+ LK_OFFSET (module_layout, size));
+    }
+
+  /* Get number of sources and list of their names.  */
+  src_num = 0;
+  src_list_addr = mod + LK_OFFSET (module, source_list);
+  src_list = xstrdup ("");
+  list_len = 0;
+
+  lk_list_for_each (next, src_list_addr, module, source_list)
+    {
+      char src_name[LK_MODULE_NAME_LEN + 1];
+      CORE_ADDR src_mod, src_addr;
+
+      src_addr = (LK_CONTAINER_OF (next, module_use, source_list)
+		  + LK_OFFSET (module_use, source));
+      src_mod = lk_read_addr (src_addr);
+      read_memory_string (src_mod + LK_OFFSET (module, name), src_name,
+			  LK_MODULE_NAME_LEN);
+
+      /* 2 = strlen (", ").  */
+      list_len += strlen (src_name) + 2;
+      src_list = reconcat (src_list, src_list, src_name, ", ", NULL);
+
+      src_num++;
+    }
+
+  make_cleanup (xfree, src_list);
+  /* Remove trailing comma.  */
+  if (strlen (src_list) >= 2)
+    src_list [list_len - 2] = '\0';
+
+  ui_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+  uiout->field_fmt ("addr", "0x%s",
+		    phex(mod, lk_builtin_type_size (unsigned_long)));
+  uiout->field_string ("module", name);
+  uiout->field_int ("size", src_size);
+  uiout->field_int ("src_num", src_num);
+  uiout->field_string ("src_list", src_list);
+  uiout->text ("\n");
+
+  do_cleanups (ui_chain);
+  return loaded;
+}
+
+/* Print information about loaded kernel modules.  Output equivalent to
+   lsmod, but also prints the address of the corrensponfing struct module.
+   Marks modules with missing debug info with an asterix '*'.  */
+
+void
+lk_lsmod (char *args, int from_tty)
+{
+  struct ui_out *uiout;
+  struct cleanup *ui_chain;
+  CORE_ADDR modules, next;
+  bool all_loaded = true;
+
+  uiout = current_uiout;
+  ui_chain = make_cleanup_ui_out_table_begin_end (uiout, 5, -1,
+						  "ModuleTable");
+  uiout->table_header (14, ui_left, "addr", "ADDR");
+  uiout->table_header (20, ui_left, "module", "Module");
+  uiout->table_header (7, ui_right, "size", "Size");
+  uiout->table_header (4, ui_right, "src_num", "");
+  uiout->table_header (40, ui_left, "src_list", "Used by");
+
+  uiout->table_body ();
+
+  modules = LK_ADDR (modules);
+  lk_list_for_each (next, modules, module, list)
+    {
+      CORE_ADDR mod;
+      mod = LK_CONTAINER_OF (next, module, list);
+      all_loaded &= lk_lsmod_print_single_module (uiout, mod);
+    }
+  if (!all_loaded)
+    uiout->text ("(*) Missing debug info for module.\n");
+
+  do_cleanups (ui_chain);
+}
+
+static void
+lk_print_struct (char *args_, int from_tty)
+{
+  struct format_data fmt;
+  size_t pos;
+  print_command_parse_format ((const char **) &args_, "print", &fmt);
+
+  if (!args_)
+    return;
+
+  std::string args (args_);
+  /* No address given default to behave like ptype.  */
+  if ((pos = args.find (" ")) == std::string::npos)
+    {
+      args = "struct " + args;
+      char *tmp = xstrdup (args.c_str ());
+      whatis_exp (tmp, 1);
+      xfree (tmp);
+      return;
+    }
+
+
+  std::string type = args.substr (0, pos);
+  std::string addr = args.substr (args.find_first_not_of (" ", pos));
+
+  if ((pos = type.find ("."))!= std::string::npos)
+    {
+      std::string field = type.substr (pos + 1);
+      type = type.substr (0, pos);
+      args = "((struct " + type + " *) " + addr + ")->" + field;
+    }
+  else if ((pos = type.find ("->"))!= std::string::npos)
+    {
+      std::string field = type.substr (pos + 2);
+      type = type.substr (0, pos);
+      args = "((struct " + type + " *) " + addr + ")->" + field;
+    }
+  else
+    args = "*(struct " + type + " *) " + addr;
+
+  expression_up expr = parse_expression (args.c_str ());
+  struct value *val = evaluate_expression (expr.get ());
+
+  print_value (val, &fmt);
+}
+
+#include "gdbtypes.h"
+void
+lk_print_offset (char *args_, int from_tty)
+{
+  std::string args (args_);
+  std::string type, field;
+  size_t pos;
+
+  if ((pos = args.find ('.')) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 1);
+    }
+  else if ((pos = args.find ("->")) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 2);
+    }
+  else
+    return;
+
+  expression_up expr = parse_expression (type.c_str ());
+  struct type *tp = value_type (evaluate_type (expr.get ()));
+
+  struct field *first = TYPE_FIELDS (tp);
+  struct field *last = first + TYPE_NFIELDS (tp);
+
+  for (; first != last; first++)
+    if (field.compare (first->name) == 0)
+      break;
+
+  if (first == last)
+    return;
+
+  size_t offset = FIELD_BITPOS (*first);
+
+  if (offset % TARGET_CHAR_BIT)
+    printf_unfiltered ("offset = %lu + %lu\n", offset / 8, offset % TARGET_CHAR_BIT);
+  else
+    printf_unfiltered ("offset = %lu\n", offset / 8);
+}
+
+void
+lk_init_cmds ()
+{
+  add_com ("lsmod", class_vars, lk_lsmod, "\n\
+	lsmod\n\n\
+List kernel modules as known by the kernel.  The address belongs to the \
+corresponding struct module.  \
+");
+
+  add_com ("struct", class_vars, lk_print_struct, "\n\
+	struct <struct>.<field> <address>\n\n\
+Print content of field <field> in structure <struct> for structure located\n\
+at address <address>.  If no field is given prints entire content of\n\
+<struct>.  If neither <field> nor <address> is given prints type definition\n\
+of <struct> (equivalent to ptype).\
+");
+
+  add_com ("offset", class_vars, lk_print_offset, "\n\
+	offset <struct>.<field>\n\n\
+Print offset of field <field> in structure <struct> in byte (+ bit for bit fields).\n\
+");
+}
diff --git a/gdb/lk-cmds.h b/gdb/lk-cmds.h
new file mode 100644
index 0000000000..63e424636d
--- /dev/null
+++ b/gdb/lk-cmds.h
@@ -0,0 +1,25 @@
+/* Commands for Linux kernel target.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_CMDS_H__
+#define __LK_CMDS_H__
+
+extern void lk_init_cmds ();
+
+#endif /* __LK_CMDS_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index c4ca472ea4..c2c31b91fc 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -27,6 +27,7 @@
 #include "gdbthread.h"
 #include "gdbtypes.h"
 #include "inferior.h"
+#include "lk-cmds.h"
 #include "lk-lists.h"
 #include "lk-low.h"
 #include "lk-modules.h"
@@ -805,6 +806,8 @@ lk_try_push_target ()
   if (!target_is_pushed (linux_kernel_ops))
     push_target (linux_kernel_ops);
 
+  lk_init_cmds ();
+
   set_solib_ops (gdbarch, lk_modules_so_ops);
 }
 
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index fc687d3fd5..6c5d45bf9d 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -39,12 +39,6 @@
 
 extern void _initialize_typeprint (void);
 
-static void ptype_command (char *, int);
-
-static void whatis_command (char *, int);
-
-static void whatis_exp (char *, int);
-
 const struct type_print_options type_print_raw_options =
 {
   1,				/* raw */
@@ -389,7 +383,7 @@ type_to_string (struct type *type)
 /* Print type of EXP, or last thing in value history if EXP == NULL.
    show is passed to type_print.  */
 
-static void
+void
 whatis_exp (char *exp, int show)
 {
   struct value *val;
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 72da7f485b..4cbe189b2e 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -78,4 +78,6 @@ extern void val_print_not_allocated (struct ui_file *stream);
 
 extern void val_print_not_associated (struct ui_file *stream);
 
+extern void whatis_exp (char *exp, int show);
+
 #endif
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 1/9] Convert substitute_path_component to C++
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (4 preceding siblings ...)
  2017-06-12 17:08 ` [RFC v4 3/9] Add basic Linux kernel support Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 8/9] Link frame_info to thread_info Philipp Rudo
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

Simplify the code of utils.c:substiute_path_component by converting it to C++.

gdb/ChangeLog:

	* utils.c (substitute_path_component): Convert to C++.
	* utils.h (substitute_path_componetn): Adjust declatation.
	* auto-load.c (auto_load_expand_dir_vars): Adjust.
---
 gdb/auto-load.c | 18 +++++++++---------
 gdb/utils.c     | 49 ++++++++++++++-----------------------------------
 gdb/utils.h     |  9 +++++++--
 3 files changed, 30 insertions(+), 46 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 37bf94286d..be950ac1bf 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -40,6 +40,7 @@
 #include "filestuff.h"
 #include "extension.h"
 #include "gdb/section-scripts.h"
+#include <string>
 
 /* The section to look in for auto-loaded scripts (in file formats that
    support sections).
@@ -175,21 +176,20 @@ static VEC (char_ptr) *auto_load_safe_path_vec;
    this vector must be freed by free_char_ptr_vec by the caller.  */
 
 static VEC (char_ptr) *
-auto_load_expand_dir_vars (const char *string)
+auto_load_expand_dir_vars (std::string orig)
 {
   VEC (char_ptr) *dir_vec;
-  char *s;
+  std::string str = orig;
 
-  s = xstrdup (string);
-  substitute_path_component (&s, "$datadir", gdb_datadir);
-  substitute_path_component (&s, "$debugdir", debug_file_directory);
+  substitute_path_component (str, "$datadir", gdb_datadir);
+  substitute_path_component (str, "$debugdir", debug_file_directory);
 
-  if (debug_auto_load && strcmp (s, string) != 0)
+  if (debug_auto_load && str.compare (orig) != 0)
     fprintf_unfiltered (gdb_stdlog,
-			_("auto-load: Expanded $-variables to \"%s\".\n"), s);
+			_("auto-load: Expanded $-variables to \"%s\".\n"),
+			str.c_str ());
 
-  dir_vec = dirnames_to_char_ptr_vec (s);
-  xfree(s);
+  dir_vec = dirnames_to_char_ptr_vec (str.c_str ());
 
   return dir_vec;
 }
diff --git a/gdb/utils.c b/gdb/utils.c
index 3d50ea4cc2..2a7d39801e 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -67,6 +67,8 @@
 #include "gdb_regex.h"
 #include "job-control.h"
 
+#include <string>
+
 #if !HAVE_DECL_MALLOC
 extern PTR malloc ();		/* ARI: PTR */
 #endif
@@ -3102,49 +3104,26 @@ make_cleanup_free_char_ptr_vec (VEC (char_ptr) *char_ptr_vec)
   return make_cleanup (do_free_char_ptr_vec, char_ptr_vec);
 }
 
-/* Substitute all occurences of string FROM by string TO in *STRINGP.  *STRINGP
-   must come from xrealloc-compatible allocator and it may be updated.  FROM
-   needs to be delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be
-   located at the start or end of *STRINGP.  */
+/* See utils.h.  */
 
 void
-substitute_path_component (char **stringp, const char *from, const char *to)
+substitute_path_component (std::string &str, const std::string &from,
+			   const std::string &to)
 {
-  char *string = *stringp, *s;
-  const size_t from_len = strlen (from);
-  const size_t to_len = strlen (to);
-
-  for (s = string;;)
+  for (size_t pos = str.find (from); pos != std::string::npos;
+       pos = str.find (from, pos + 1))
     {
-      s = strstr (s, from);
-      if (s == NULL)
-	break;
+      char start, end;
 
-      if ((s == string || IS_DIR_SEPARATOR (s[-1])
-	   || s[-1] == DIRNAME_SEPARATOR)
-          && (s[from_len] == '\0' || IS_DIR_SEPARATOR (s[from_len])
-	      || s[from_len] == DIRNAME_SEPARATOR))
+      start = str[pos - 1];
+      end = str[pos + from.length ()];
+      if ((pos == 0 || IS_DIR_SEPARATOR (start) || start == DIRNAME_SEPARATOR)
+	  && (end == '\0' || IS_DIR_SEPARATOR (end)
+	      || end == DIRNAME_SEPARATOR))
 	{
-	  char *string_new;
-
-	  string_new
-	    = (char *) xrealloc (string, (strlen (string) + to_len + 1));
-
-	  /* Relocate the current S pointer.  */
-	  s = s - string + string_new;
-	  string = string_new;
-
-	  /* Replace from by to.  */
-	  memmove (&s[to_len], &s[from_len], strlen (&s[from_len]) + 1);
-	  memcpy (s, to, to_len);
-
-	  s += to_len;
+	  str.replace (pos, from.length (), to);
 	}
-      else
-	s++;
     }
-
-  *stringp = string;
 }
 
 #ifdef HAVE_WAITPID
diff --git a/gdb/utils.h b/gdb/utils.h
index f3e800743c..9726025c12 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -132,8 +132,13 @@ extern char *gdb_abspath (const char *);
 extern int gdb_filename_fnmatch (const char *pattern, const char *string,
 				 int flags);
 
-extern void substitute_path_component (char **stringp, const char *from,
-				       const char *to);
+/* Substitute all occurences of string FROM by string TO in STR.  FROM
+   needs to be delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be
+   located at the start or end of STR).  */
+
+extern void substitute_path_component (std::string &str,
+				       const std::string &from,
+				       const std::string &to);
 
 std::string ldirname (const char *filename);
 
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 8/9] Link frame_info to thread_info
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (5 preceding siblings ...)
  2017-06-12 17:08 ` [RFC v4 1/9] Convert substitute_path_component to C++ Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-19 13:07   ` Yao Qi
  2017-06-12 17:09 ` [RFC v4 6/9] Separate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

Linux kernel stacks on S390 are spread over several memory locations.
These locations differ for every kernel task/thread.  Thus we need to know
to which task/thread a frame belongs to check whether a stack pointer is
valid and when to stop unwinding.  To do so add a pointer to corresponding
thread_info to frame_info.

This connection already exists implicitly by the fact that switch_to_thread
reinitializes the frame cache.

gdb/ChangeLog:

	* frame.h (frame_get_thread): New extern declaration.
	* frame.c (frame_info) <thread>: New field.
	(frame_get_thread): New function.
	(create_sentinel_frame, create_new_frame, get_prev_frame_raw): Set
	new field.
---
 gdb/frame.c | 12 ++++++++++++
 gdb/frame.h |  2 ++
 2 files changed, 14 insertions(+)

diff --git a/gdb/frame.c b/gdb/frame.c
index 30e4aeab7e..5a2fdda783 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -101,6 +101,9 @@ struct frame_info
   /* The frame's address space.  */
   struct address_space *aspace;
 
+  /* The thread this frame belongs to.  */
+  struct thread_info *thread;
+
   /* The frame's low-level unwinder and corresponding cache.  The
      low-level unwinder is responsible for unwinding register values
      for the previous frame.  The low-level unwind methods are
@@ -157,6 +160,12 @@ struct frame_info
   const char *stop_string;
 };
 
+struct thread_info *
+frame_get_thread (struct frame_info *frame)
+{
+  return frame->thread;
+}
+
 /* A frame stash used to speed up frame lookups.  Create a hash table
    to stash frames previously accessed from the frame cache for
    quicker subsequent retrieval.  The hash table is emptied whenever
@@ -1527,6 +1536,7 @@ create_sentinel_frame (struct program_space *pspace, struct regcache *regcache)
   frame->level = -1;
   frame->pspace = pspace;
   frame->aspace = get_regcache_aspace (regcache);
+  frame->thread = inferior_thread ();
   /* Explicitly initialize the sentinel frame's cache.  Provide it
      with the underlying regcache.  In the future additional
      information, such as the frame's thread will be added.  */
@@ -1768,6 +1778,7 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
   /* We currently assume that frame chain's can't cross spaces.  */
   fi->pspace = fi->next->pspace;
   fi->aspace = fi->next->aspace;
+  fi->thread = fi->next->thread;
 
   /* Select/initialize both the unwind function and the frame's type
      based on the PC.  */
@@ -2179,6 +2190,7 @@ get_prev_frame_raw (struct frame_info *this_frame)
      spaces.  */
   prev_frame->pspace = this_frame->pspace;
   prev_frame->aspace = this_frame->aspace;
+  prev_frame->thread = this_frame->thread;
 
   /* Don't yet compute ->unwind (and hence ->type).  It is computed
      on-demand in get_frame_type, frame_register_unwind, and
diff --git a/gdb/frame.h b/gdb/frame.h
index 56cbd4400b..26079e8215 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -103,6 +103,8 @@ enum frame_id_stack_status
 
 struct frame_info;
 
+extern struct thread_info *frame_get_thread (struct frame_info *frame);
+
 /* The frame object's ID.  This provides a per-frame unique identifier
    that can be used to relocate a `struct frame_info' after a target
    resume or a frame cache destruct.  It of course assumes that the
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 2/9] Add libiberty/concat styled concat_path function
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 4/9] Add kernel module support for linux-kernel target Philipp Rudo
@ 2017-06-12 17:08 ` Philipp Rudo
  2017-06-12 17:08 ` [RFC v4 5/9] Add commands for linux-kernel target Philipp Rudo
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

    This commit adds concat_path function to concatenate an arbitrary number of
    path elements.  The function automatically adds an directory separator between
    two elements as needed.

    gdb/ChangeLog:

	* common/common-utils.h (endswith): New function.
	* utils.c (_concat_path, approx_path_length): New function.
	* utils.h (_concat_path): New export.
	(concat_path): New define.
---
 gdb/common/common-utils.h | 11 +++++++++++
 gdb/utils.c               | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/utils.h               | 17 +++++++++++++++++
 3 files changed, 74 insertions(+)

diff --git a/gdb/common/common-utils.h b/gdb/common/common-utils.h
index 787bac9a03..465e1342af 100644
--- a/gdb/common/common-utils.h
+++ b/gdb/common/common-utils.h
@@ -84,6 +84,17 @@ startswith (const char *string, const char *pattern)
   return strncmp (string, pattern, strlen (pattern)) == 0;
 }
 
+/* Return non-zero if the end of STRING matches PATTERN, zero
+   otherwise.  */
+
+static inline int
+endswith (const char *string, const char *pattern)
+{
+  return (strlen (string) > strlen (pattern)
+	  && strncmp (string + strlen (string) - strlen (pattern), pattern,
+		      strlen (pattern)) == 0);
+}
+
 ULONGEST strtoulst (const char *num, const char **trailer, int base);
 
 /* Skip leading whitespace characters in INP, returning an updated
diff --git a/gdb/utils.c b/gdb/utils.c
index 2a7d39801e..53123f7a06 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3126,6 +3126,52 @@ substitute_path_component (std::string &str, const std::string &from,
     }
 }
 
+/* Approximate length of final path.  Helper for concat_path.  */
+
+static inline unsigned long
+approx_path_length (std::initializer_list<std::string> args,
+		   std::string dir_separator)
+{
+  size_t length = 0;
+
+  for (const std::string &arg: args)
+      length += arg.length () + dir_separator.length ();
+
+  return length;
+}
+
+/* See utils.h.  */
+
+std::string
+_concat_path (std::initializer_list<std::string> args,
+	      std::string dir_separator)
+{
+  std::string dst;
+  dst.reserve (approx_path_length (args, dir_separator));
+
+  for (const std::string &arg : args)
+    {
+      if (arg.empty ())
+	continue;
+
+      if (startswith (arg.c_str (), dir_separator.c_str ())
+	  && endswith (dst.c_str (), dir_separator.c_str ()))
+	dst.erase (dst.length () - dir_separator.length (),
+		   dir_separator.length ());
+
+      else if (!dst.empty ()
+	       && !startswith (arg.c_str (), dir_separator.c_str ())
+	       && !endswith (dst.c_str (), dir_separator.c_str ())
+	       && dst != TARGET_SYSROOT_PREFIX)
+	dst += dir_separator;
+
+      dst += arg;
+    }
+
+  dst.shrink_to_fit ();
+  return dst;
+}
+
 #ifdef HAVE_WAITPID
 
 #ifdef SIGALRM
diff --git a/gdb/utils.h b/gdb/utils.h
index 9726025c12..506e178d85 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -24,6 +24,7 @@
 #include "exceptions.h"
 #include "common/scoped_restore.h"
 #include <chrono>
+#include <string>
 
 extern void initialize_utils (void);
 
@@ -140,6 +141,22 @@ extern void substitute_path_component (std::string &str,
 				       const std::string &from,
 				       const std::string &to);
 
+/* Concatenate an arbitrary number of path elements.  Adds and removes
+   directory separators as needed.
+
+   concat_path (/first, second)		=> /first/second
+   concat_path (first, second)		=> first/second
+   concat_path (first/, second)		=> first/second
+   concat_path (first, /second)		=> first/second
+   concat_path (first/, /second)	=> first/second
+   concat_path (target:, second)	=> target:second
+   */
+
+extern std::string _concat_path (std::initializer_list<std::string> args,
+				 std::string dir_separator);
+
+#define concat_path(...) _concat_path ({__VA_ARGS__}, SLASH_STRING)
+
 std::string ldirname (const char *filename);
 
 extern int count_path_elements (const char *path);
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 6/9] Separate common s390-tdep.* from s390-linux-tdep.*
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (6 preceding siblings ...)
  2017-06-12 17:08 ` [RFC v4 8/9] Link frame_info to thread_info Philipp Rudo
@ 2017-06-12 17:09 ` Philipp Rudo
  2017-06-12 17:39 ` [RFC v4 9/9] Add S390 support for linux-kernel target Philipp Rudo
  2017-06-12 21:17 ` Philipp Rudo
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:09 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

The new linux-kernel target needs some architecture dependent code.  To
prepare for this we split up the existing s390 code into a general
s390-tedep and a GNU/Linux (user space) specific s390-linux-tdep.  This
keeps the files manageable and allows for kernel specific code e.g.
unwinder.

gdb/ChangeLog:

	* s390-tdep.h: New file.
	* s390-tdep.c: New file.
	* Makefile.in (ALL_TARGET_OBS): Add s390-tdep.o.
	(HFILES_NO_SRCDIR): Add s390-tdep.h.
	(ALLDEPFILES): Add s390-tdep.c.
	* configure.tgt (s390*-*-linux*): Add s390-tdep.o.
	* s390-linux-tdep.h: Move defines for hardware capabilities and
	register informations to s390-tdep.h.
	(s390_linux_gdbarch_init): New export.
	(s390_upper_registers): New export.
	* s390-linux-tdep.c: Remove unneeded includes and sort alphabetically.
	(s390-tdep.h): New include.
	(s390_upper_regset): Remove static.
	(s390_gdbarch_init): Rename to...
	(s390_linux_gdbarch_init): ...this and adjust.
	(_initialize_s390_tdep): Rename to...
	(_initialize_s390_linux_tdep): ...this and adjust.
	(s390_abi_kind, s390_vector_abi_kind, gdbarch_tdep, enum named
	(opcodes): Move to s390-tdep.h
	(s390_readinstruction, is_ri, is_ril, is_rr, is_rre, is_rs)
	(is_rsy, is_rx, is_rxy, s390_break_insn, s390_breakpoint)
	(s390_is_partial_instruction, s390_software_single_step)
	(s390_prologue_data, s390_addr, s390_store, s390_load)
	(s390_check_for_saved, s390_analyze_prologue, s390_skip_prologue)
	(s390_register_call_saved, s390_register_name)
	(s390_cannot_store_register, s390_write_pc, s390_dwarf_regmap)
	(s390_dwarf_reg_to_regnum, regnum_is_gpr_full, regnum_is_vxr_full)
	(s390_value_from_register, s390_core_read_description)
	(s390_iterate_over_regset_sections, s390_pseudo_register_name)
	(s390_pseudo_register_read, s390_pseudo_register_write)
	(s390_pseudo_register_type, s390_pseudo_register_reggroup_p)
	(s390_ax_pseudo_register_collect)
	(s390_ax_pseudo_register_push_stack, s390_gen_return_address)
	(s390_unwind_pseudo_register, s390_effective_inner_type)
	(s390_function_arg_float, s390_function_arg_vector)
	(is_power_of_two, s390_function_arg_integer, s390_arg_state)
	(s390_handle_arg, s390_push_dummy_call, s390_dummy_id)
	(s390_register_return_value, s390_return_value)
	(s390_stack_frame_destroyed_p, s390_dwarf2_prev_register)
	(s390_dwarf2_frame_init_reg, s390_adjust_frame_regnum)
	(s390_unwind_pc, s390_unwind_sp, is_non_branch_ril)
	(s390_displaced_step_copy_insn, s390_displaced_step_fixup)
	(s390_displaced_step_hw_singlestep, s390_addr_bits_remove)
	(s390_address_class_type_flags)
	(s390_address_class_type_flags_to_name)
	(s390_address_class_name_to_type_flags): Move to s390-tdep.c
---
 gdb/Makefile.in       |    3 +
 gdb/configure.tgt     |    4 +-
 gdb/s390-linux-nat.c  |    1 +
 gdb/s390-linux-tdep.c | 3934 ++++++++-----------------------------------------
 gdb/s390-linux-tdep.h |  178 +--
 gdb/s390-tdep.c       | 2663 +++++++++++++++++++++++++++++++++
 gdb/s390-tdep.h       |  416 ++++++
 7 files changed, 3703 insertions(+), 3496 deletions(-)
 create mode 100644 gdb/s390-tdep.c
 create mode 100644 gdb/s390-tdep.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ccc1db596e..9819b70f3d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -873,6 +873,7 @@ ALL_TARGET_OBS = \
 	rs6000-tdep.o \
 	rx-tdep.o \
 	s390-linux-tdep.o \
+	s390-tdep.o \
 	score-tdep.o \
 	sh-linux-tdep.o \
 	sh-nbsd-tdep.o \
@@ -1447,6 +1448,7 @@ HFILES_NO_SRCDIR = \
 	rs6000-aix-tdep.h \
 	rs6000-tdep.h \
 	s390-linux-tdep.h \
+	s390-tdep.h \
 	score-tdep.h \
 	selftest-arch.h \
 	sentinel-frame.h \
@@ -2653,6 +2655,7 @@ ALLDEPFILES = \
 	rx-tdep.c \
 	s390-linux-nat.c \
 	s390-linux-tdep.c \
+	s390-tdep.c \
 	score-tdep.c \
 	ser-go32.c \
 	ser-mingw.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index efc67b2166..b79a0bac4c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -483,8 +483,8 @@ powerpc*-*-*)
 
 s390*-*-linux*)
 	# Target: S390 running Linux
-	gdb_target_obs="s390-linux-tdep.o solib-svr4.o linux-tdep.o \
-			linux-record.o ${lk_target_obs}"
+	gdb_target_obs="s390-tdep.o s390-linux-tdep.o solib-svr4.o \
+			linux-tdep.o linux-record.o ${lk_target_obs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c
index 90c73c44cd..bc55673e14 100644
--- a/gdb/s390-linux-nat.c
+++ b/gdb/s390-linux-nat.c
@@ -31,6 +31,7 @@
 #include "gdbcmd.h"
 
 #include "s390-linux-tdep.h"
+#include "s390-tdep.h"
 #include "elf/common.h"
 
 #include <asm/ptrace.h>
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 8840c2f633..e3a7db5a43 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -1,4 +1,4 @@
-/* Target-dependent code for GDB, the GNU debugger.
+/* Target-dependent code for GNU/Linux on S390.
 
    Copyright (C) 2001-2017 Free Software Foundation, Inc.
 
@@ -21,45 +21,24 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "arch-utils.h"
-#include "frame.h"
-#include "inferior.h"
-#include "infrun.h"
-#include "symtab.h"
-#include "target.h"
+
+#include "frame-base.h"
+#include "frame-unwind.h"
 #include "gdbcore.h"
-#include "gdbcmd.h"
+#include "linux-record.h"
+#include "linux-tdep.h"
 #include "objfiles.h"
-#include "floatformat.h"
+#include "prologue-value.h"
+#include "record-full.h"
 #include "regcache.h"
-#include "trad-frame.h"
-#include "frame-base.h"
-#include "frame-unwind.h"
-#include "dwarf2-frame.h"
-#include "reggroups.h"
 #include "regset.h"
-#include "value.h"
-#include "dis-asm.h"
-#include "solib-svr4.h"
-#include "prologue-value.h"
-#include "linux-tdep.h"
 #include "s390-linux-tdep.h"
-#include "linux-record.h"
-#include "record-full.h"
-#include "auxv.h"
+#include "s390-tdep.h"
+#include "solib-svr4.h"
+#include "target.h"
+#include "trad-frame.h"
 #include "xml-syscall.h"
 
-#include "stap-probe.h"
-#include "ax.h"
-#include "ax-gdb.h"
-#include "user-regs.h"
-#include "cli/cli-utils.h"
-#include <ctype.h>
-#include "elf/common.h"
-#include "elf/s390.h"
-#include "elf-bfd.h"
-#include <algorithm>
-
 #include "features/s390-linux32.c"
 #include "features/s390-linux32v1.c"
 #include "features/s390-linux32v2.c"
@@ -79,98 +58,6 @@
 #define XML_SYSCALL_FILENAME_S390 "syscalls/s390-linux.xml"
 #define XML_SYSCALL_FILENAME_S390X "syscalls/s390x-linux.xml"
 
-/* Holds the current set of options to be passed to the disassembler.  */
-static char *s390_disassembler_options;
-
-enum s390_abi_kind
-{
-  ABI_LINUX_S390,
-  ABI_LINUX_ZSERIES
-};
-
-enum s390_vector_abi_kind
-{
-  S390_VECTOR_ABI_NONE,
-  S390_VECTOR_ABI_128
-};
-
-/* The tdep structure.  */
-
-struct gdbarch_tdep
-{
-  /* ABI version.  */
-  enum s390_abi_kind abi;
-
-  /* Vector ABI.  */
-  enum s390_vector_abi_kind vector_abi;
-
-  /* Pseudo register numbers.  */
-  int gpr_full_regnum;
-  int pc_regnum;
-  int cc_regnum;
-  int v0_full_regnum;
-
-  int have_linux_v1;
-  int have_linux_v2;
-  int have_tdb;
-};
-
-
-/* ABI call-saved register information.  */
-
-static int
-s390_register_call_saved (struct gdbarch *gdbarch, int regnum)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
-  switch (tdep->abi)
-    {
-    case ABI_LINUX_S390:
-      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
-	  || regnum == S390_F4_REGNUM || regnum == S390_F6_REGNUM
-	  || regnum == S390_A0_REGNUM)
-	return 1;
-
-      break;
-
-    case ABI_LINUX_ZSERIES:
-      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
-	  || (regnum >= S390_F8_REGNUM && regnum <= S390_F15_REGNUM)
-	  || (regnum >= S390_A0_REGNUM && regnum <= S390_A1_REGNUM))
-	return 1;
-
-      break;
-    }
-
-  return 0;
-}
-
-static int
-s390_cannot_store_register (struct gdbarch *gdbarch, int regnum)
-{
-  /* The last-break address is read-only.  */
-  return regnum == S390_LAST_BREAK_REGNUM;
-}
-
-static void
-s390_write_pc (struct regcache *regcache, CORE_ADDR pc)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
-  regcache_cooked_write_unsigned (regcache, tdep->pc_regnum, pc);
-
-  /* Set special SYSTEM_CALL register to 0 to prevent the kernel from
-     messing with the PC we just installed, if we happen to be within
-     an interrupted system call that the kernel wants to restart.
-
-     Note that after we return from the dummy call, the SYSTEM_CALL and
-     ORIG_R2 registers will be automatically restored, and the kernel
-     continues to restart the system call at this point.  */
-  if (register_size (gdbarch, S390_SYSTEM_CALL_REGNUM) > 0)
-    regcache_cooked_write_unsigned (regcache, S390_SYSTEM_CALL_REGNUM, 0);
-}
-
 /* The "guess_tracepoint_registers" gdbarch method.  */
 
 static void
@@ -206,567 +93,6 @@ s390_guess_tracepoint_registers (struct gdbarch *gdbarch,
 }
 
 
-/* DWARF Register Mapping.  */
-
-static const short s390_dwarf_regmap[] =
-{
-  /* 0-15: General Purpose Registers.  */
-  S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
-  S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
-  S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
-  S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
-
-  /* 16-31: Floating Point Registers / Vector Registers 0-15. */
-  S390_F0_REGNUM, S390_F2_REGNUM, S390_F4_REGNUM, S390_F6_REGNUM,
-  S390_F1_REGNUM, S390_F3_REGNUM, S390_F5_REGNUM, S390_F7_REGNUM,
-  S390_F8_REGNUM, S390_F10_REGNUM, S390_F12_REGNUM, S390_F14_REGNUM,
-  S390_F9_REGNUM, S390_F11_REGNUM, S390_F13_REGNUM, S390_F15_REGNUM,
-
-  /* 32-47: Control Registers (not mapped).  */
-  -1, -1, -1, -1, -1, -1, -1, -1,
-  -1, -1, -1, -1, -1, -1, -1, -1,
-
-  /* 48-63: Access Registers.  */
-  S390_A0_REGNUM, S390_A1_REGNUM, S390_A2_REGNUM, S390_A3_REGNUM,
-  S390_A4_REGNUM, S390_A5_REGNUM, S390_A6_REGNUM, S390_A7_REGNUM,
-  S390_A8_REGNUM, S390_A9_REGNUM, S390_A10_REGNUM, S390_A11_REGNUM,
-  S390_A12_REGNUM, S390_A13_REGNUM, S390_A14_REGNUM, S390_A15_REGNUM,
-
-  /* 64-65: Program Status Word.  */
-  S390_PSWM_REGNUM,
-  S390_PSWA_REGNUM,
-
-  /* 66-67: Reserved.  */
-  -1, -1,
-
-  /* 68-83: Vector Registers 16-31.  */
-  S390_V16_REGNUM, S390_V18_REGNUM, S390_V20_REGNUM, S390_V22_REGNUM,
-  S390_V17_REGNUM, S390_V19_REGNUM, S390_V21_REGNUM, S390_V23_REGNUM,
-  S390_V24_REGNUM, S390_V26_REGNUM, S390_V28_REGNUM, S390_V30_REGNUM,
-  S390_V25_REGNUM, S390_V27_REGNUM, S390_V29_REGNUM, S390_V31_REGNUM,
-
-  /* End of "official" DWARF registers.  The remainder of the map is
-     for GDB internal use only.  */
-
-  /* GPR Lower Half Access.  */
-  S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
-  S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
-  S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
-  S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
-};
-
-enum { s390_dwarf_reg_r0l = ARRAY_SIZE (s390_dwarf_regmap) - 16 };
-
-/* Convert DWARF register number REG to the appropriate register
-   number used by GDB.  */
-static int
-s390_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  int gdb_reg = -1;
-
-  /* In a 32-on-64 debug scenario, debug info refers to the full
-     64-bit GPRs.  Note that call frame information still refers to
-     the 32-bit lower halves, because s390_adjust_frame_regnum uses
-     special register numbers to access GPRs.  */
-  if (tdep->gpr_full_regnum != -1 && reg >= 0 && reg < 16)
-    return tdep->gpr_full_regnum + reg;
-
-  if (reg >= 0 && reg < ARRAY_SIZE (s390_dwarf_regmap))
-    gdb_reg = s390_dwarf_regmap[reg];
-
-  if (tdep->v0_full_regnum == -1)
-    {
-      if (gdb_reg >= S390_V16_REGNUM && gdb_reg <= S390_V31_REGNUM)
-	gdb_reg = -1;
-    }
-  else
-    {
-      if (gdb_reg >= S390_F0_REGNUM && gdb_reg <= S390_F15_REGNUM)
-	gdb_reg = gdb_reg - S390_F0_REGNUM + tdep->v0_full_regnum;
-    }
-
-  return gdb_reg;
-}
-
-/* Translate a .eh_frame register to DWARF register, or adjust a
-   .debug_frame register.  */
-static int
-s390_adjust_frame_regnum (struct gdbarch *gdbarch, int num, int eh_frame_p)
-{
-  /* See s390_dwarf_reg_to_regnum for comments.  */
-  return (num >= 0 && num < 16) ? num + s390_dwarf_reg_r0l : num;
-}
-
-
-/* Pseudo registers.  */
-
-static int
-regnum_is_gpr_full (struct gdbarch_tdep *tdep, int regnum)
-{
-  return (tdep->gpr_full_regnum != -1
-	  && regnum >= tdep->gpr_full_regnum
-	  && regnum <= tdep->gpr_full_regnum + 15);
-}
-
-/* Check whether REGNUM indicates a full vector register (v0-v15).
-   These pseudo-registers are composed of f0-f15 and v0l-v15l.  */
-
-static int
-regnum_is_vxr_full (struct gdbarch_tdep *tdep, int regnum)
-{
-  return (tdep->v0_full_regnum != -1
-	  && regnum >= tdep->v0_full_regnum
-	  && regnum <= tdep->v0_full_regnum + 15);
-}
-
-/* Return the name of register REGNO.  Return the empty string for
-   registers that shouldn't be visible.  */
-
-static const char *
-s390_register_name (struct gdbarch *gdbarch, int regnum)
-{
-  if (regnum >= S390_V0_LOWER_REGNUM
-      && regnum <= S390_V15_LOWER_REGNUM)
-    return "";
-  return tdesc_register_name (gdbarch, regnum);
-}
-
-static const char *
-s390_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
-  if (regnum == tdep->pc_regnum)
-    return "pc";
-
-  if (regnum == tdep->cc_regnum)
-    return "cc";
-
-  if (regnum_is_gpr_full (tdep, regnum))
-    {
-      static const char *full_name[] = {
-	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
-	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
-      };
-      return full_name[regnum - tdep->gpr_full_regnum];
-    }
-
-  if (regnum_is_vxr_full (tdep, regnum))
-    {
-      static const char *full_name[] = {
-	"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
-	"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15"
-      };
-      return full_name[regnum - tdep->v0_full_regnum];
-    }
-
-  internal_error (__FILE__, __LINE__, _("invalid regnum"));
-}
-
-static struct type *
-s390_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
-  if (regnum == tdep->pc_regnum)
-    return builtin_type (gdbarch)->builtin_func_ptr;
-
-  if (regnum == tdep->cc_regnum)
-    return builtin_type (gdbarch)->builtin_int;
-
-  if (regnum_is_gpr_full (tdep, regnum))
-    return builtin_type (gdbarch)->builtin_uint64;
-
-  if (regnum_is_vxr_full (tdep, regnum))
-    return tdesc_find_type (gdbarch, "vec128");
-
-  internal_error (__FILE__, __LINE__, _("invalid regnum"));
-}
-
-static enum register_status
-s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
-			   int regnum, gdb_byte *buf)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  int regsize = register_size (gdbarch, regnum);
-  ULONGEST val;
-
-  if (regnum == tdep->pc_regnum)
-    {
-      enum register_status status;
-
-      status = regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
-      if (status == REG_VALID)
-	{
-	  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	    val &= 0x7fffffff;
-	  store_unsigned_integer (buf, regsize, byte_order, val);
-	}
-      return status;
-    }
-
-  if (regnum == tdep->cc_regnum)
-    {
-      enum register_status status;
-
-      status = regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
-      if (status == REG_VALID)
-	{
-	  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	    val = (val >> 12) & 3;
-	  else
-	    val = (val >> 44) & 3;
-	  store_unsigned_integer (buf, regsize, byte_order, val);
-	}
-      return status;
-    }
-
-  if (regnum_is_gpr_full (tdep, regnum))
-    {
-      enum register_status status;
-      ULONGEST val_upper;
-
-      regnum -= tdep->gpr_full_regnum;
-
-      status = regcache_raw_read_unsigned (regcache, S390_R0_REGNUM + regnum, &val);
-      if (status == REG_VALID)
-	status = regcache_raw_read_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
-					     &val_upper);
-      if (status == REG_VALID)
-	{
-	  val |= val_upper << 32;
-	  store_unsigned_integer (buf, regsize, byte_order, val);
-	}
-      return status;
-    }
-
-  if (regnum_is_vxr_full (tdep, regnum))
-    {
-      enum register_status status;
-
-      regnum -= tdep->v0_full_regnum;
-
-      status = regcache_raw_read (regcache, S390_F0_REGNUM + regnum, buf);
-      if (status == REG_VALID)
-	status = regcache_raw_read (regcache,
-				    S390_V0_LOWER_REGNUM + regnum, buf + 8);
-      return status;
-    }
-
-  internal_error (__FILE__, __LINE__, _("invalid regnum"));
-}
-
-static void
-s390_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
-			    int regnum, const gdb_byte *buf)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  int regsize = register_size (gdbarch, regnum);
-  ULONGEST val, psw;
-
-  if (regnum == tdep->pc_regnum)
-    {
-      val = extract_unsigned_integer (buf, regsize, byte_order);
-      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	{
-	  regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &psw);
-	  val = (psw & 0x80000000) | (val & 0x7fffffff);
-	}
-      regcache_raw_write_unsigned (regcache, S390_PSWA_REGNUM, val);
-      return;
-    }
-
-  if (regnum == tdep->cc_regnum)
-    {
-      val = extract_unsigned_integer (buf, regsize, byte_order);
-      regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
-      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	val = (psw & ~((ULONGEST)3 << 12)) | ((val & 3) << 12);
-      else
-	val = (psw & ~((ULONGEST)3 << 44)) | ((val & 3) << 44);
-      regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, val);
-      return;
-    }
-
-  if (regnum_is_gpr_full (tdep, regnum))
-    {
-      regnum -= tdep->gpr_full_regnum;
-      val = extract_unsigned_integer (buf, regsize, byte_order);
-      regcache_raw_write_unsigned (regcache, S390_R0_REGNUM + regnum,
-				   val & 0xffffffff);
-      regcache_raw_write_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
-				   val >> 32);
-      return;
-    }
-
-  if (regnum_is_vxr_full (tdep, regnum))
-    {
-      regnum -= tdep->v0_full_regnum;
-      regcache_raw_write (regcache, S390_F0_REGNUM + regnum, buf);
-      regcache_raw_write (regcache, S390_V0_LOWER_REGNUM + regnum, buf + 8);
-      return;
-    }
-
-  internal_error (__FILE__, __LINE__, _("invalid regnum"));
-}
-
-/* 'float' values are stored in the upper half of floating-point
-   registers, even though we are otherwise a big-endian platform.  The
-   same applies to a 'float' value within a vector.  */
-
-static struct value *
-s390_value_from_register (struct gdbarch *gdbarch, struct type *type,
-			  int regnum, struct frame_id frame_id)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  struct value *value = default_value_from_register (gdbarch, type,
-						     regnum, frame_id);
-  check_typedef (type);
-
-  if ((regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM
-       && TYPE_LENGTH (type) < 8)
-      || regnum_is_vxr_full (tdep, regnum)
-      || (regnum >= S390_V16_REGNUM && regnum <= S390_V31_REGNUM))
-    set_value_offset (value, 0);
-
-  return value;
-}
-
-/* Register groups.  */
-
-static int
-s390_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
-				 struct reggroup *group)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
-  /* We usually save/restore the whole PSW, which includes PC and CC.
-     However, some older gdbservers may not support saving/restoring
-     the whole PSW yet, and will return an XML register description
-     excluding those from the save/restore register groups.  In those
-     cases, we still need to explicitly save/restore PC and CC in order
-     to push or pop frames.  Since this doesn't hurt anything if we
-     already save/restore the whole PSW (it's just redundant), we add
-     PC and CC at this point unconditionally.  */
-  if (group == save_reggroup || group == restore_reggroup)
-    return regnum == tdep->pc_regnum || regnum == tdep->cc_regnum;
-
-  if (group == vector_reggroup)
-    return regnum_is_vxr_full (tdep, regnum);
-
-  if (group == general_reggroup && regnum_is_vxr_full (tdep, regnum))
-    return 0;
-
-  return default_register_reggroup_p (gdbarch, regnum, group);
-}
-
-/* The "ax_pseudo_register_collect" gdbarch method.  */
-
-static int
-s390_ax_pseudo_register_collect (struct gdbarch *gdbarch,
-				 struct agent_expr *ax, int regnum)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  if (regnum == tdep->pc_regnum)
-    {
-      ax_reg_mask (ax, S390_PSWA_REGNUM);
-    }
-  else if (regnum == tdep->cc_regnum)
-    {
-      ax_reg_mask (ax, S390_PSWM_REGNUM);
-    }
-  else if (regnum_is_gpr_full (tdep, regnum))
-    {
-      regnum -= tdep->gpr_full_regnum;
-      ax_reg_mask (ax, S390_R0_REGNUM + regnum);
-      ax_reg_mask (ax, S390_R0_UPPER_REGNUM + regnum);
-    }
-  else if (regnum_is_vxr_full (tdep, regnum))
-    {
-      regnum -= tdep->v0_full_regnum;
-      ax_reg_mask (ax, S390_F0_REGNUM + regnum);
-      ax_reg_mask (ax, S390_V0_LOWER_REGNUM + regnum);
-    }
-  else
-    {
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
-    }
-  return 0;
-}
-
-/* The "ax_pseudo_register_push_stack" gdbarch method.  */
-
-static int
-s390_ax_pseudo_register_push_stack (struct gdbarch *gdbarch,
-				    struct agent_expr *ax, int regnum)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  if (regnum == tdep->pc_regnum)
-    {
-      ax_reg (ax, S390_PSWA_REGNUM);
-      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	{
-	  ax_zero_ext (ax, 31);
-	}
-    }
-  else if (regnum == tdep->cc_regnum)
-    {
-      ax_reg (ax, S390_PSWM_REGNUM);
-      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-	ax_const_l (ax, 12);
-      else
-	ax_const_l (ax, 44);
-      ax_simple (ax, aop_rsh_unsigned);
-      ax_zero_ext (ax, 2);
-    }
-  else if (regnum_is_gpr_full (tdep, regnum))
-    {
-      regnum -= tdep->gpr_full_regnum;
-      ax_reg (ax, S390_R0_REGNUM + regnum);
-      ax_reg (ax, S390_R0_UPPER_REGNUM + regnum);
-      ax_const_l (ax, 32);
-      ax_simple (ax, aop_lsh);
-      ax_simple (ax, aop_bit_or);
-    }
-  else if (regnum_is_vxr_full (tdep, regnum))
-    {
-      /* Too large to stuff on the stack.  */
-      return 1;
-    }
-  else
-    {
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
-    }
-  return 0;
-}
-
-/* The "gen_return_address" gdbarch method.  Since this is supposed to be
-   just a best-effort method, and we don't really have the means to run
-   the full unwinder here, just collect the link register.  */
-
-static void
-s390_gen_return_address (struct gdbarch *gdbarch,
-			 struct agent_expr *ax, struct axs_value *value,
-			 CORE_ADDR scope)
-{
-  value->type = register_type (gdbarch, S390_R14_REGNUM);
-  value->kind = axs_lvalue_register;
-  value->u.reg = S390_R14_REGNUM;
-}
-
-
-/* A helper for s390_software_single_step, decides if an instruction
-   is a partial-execution instruction that needs to be executed until
-   completion when in record mode.  If it is, returns 1 and writes
-   instruction length to a pointer.  */
-
-static int
-s390_is_partial_instruction (struct gdbarch *gdbarch, CORE_ADDR loc, int *len)
-{
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  uint16_t insn;
-
-  insn = read_memory_integer (loc, 2, byte_order);
-
-  switch (insn >> 8)
-    {
-    case 0xa8: /* MVCLE */
-      *len = 4;
-      return 1;
-
-    case 0xeb:
-      {
-        insn = read_memory_integer (loc + 4, 2, byte_order);
-        if ((insn & 0xff) == 0x8e)
-          {
-            /* MVCLU */
-            *len = 6;
-            return 1;
-          }
-      }
-      break;
-    }
-
-  switch (insn)
-    {
-    case 0xb255: /* MVST */
-    case 0xb263: /* CMPSC */
-    case 0xb2a5: /* TRE */
-    case 0xb2a6: /* CU21 */
-    case 0xb2a7: /* CU12 */
-    case 0xb9b0: /* CU14 */
-    case 0xb9b1: /* CU24 */
-    case 0xb9b2: /* CU41 */
-    case 0xb9b3: /* CU42 */
-    case 0xb92a: /* KMF */
-    case 0xb92b: /* KMO */
-    case 0xb92f: /* KMC */
-    case 0xb92d: /* KMCTR */
-    case 0xb92e: /* KM */
-    case 0xb93c: /* PPNO */
-    case 0xb990: /* TRTT */
-    case 0xb991: /* TRTO */
-    case 0xb992: /* TROT */
-    case 0xb993: /* TROO */
-      *len = 4;
-      return 1;
-    }
-
-  return 0;
-}
-
-/* Implement the "software_single_step" gdbarch method, needed to single step
-   through instructions like MVCLE in record mode, to make sure they are
-   executed to completion.  Without that, record will save the full length
-   of destination buffer on every iteration, even though the CPU will only
-   process about 4kiB of it each time, leading to O(n**2) memory and time
-   complexity.  */
-
-static std::vector<CORE_ADDR>
-s390_software_single_step (struct regcache *regcache)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  CORE_ADDR loc = regcache_read_pc (regcache);
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  int len;
-  uint16_t insn;
-
-  /* Special handling only if recording.  */
-  if (!record_full_is_used ())
-    return {};
-
-  /* First, match a partial instruction.  */
-  if (!s390_is_partial_instruction (gdbarch, loc, &len))
-    return {};
-
-  loc += len;
-
-  /* Second, look for a branch back to it.  */
-  insn = read_memory_integer (loc, 2, byte_order);
-  if (insn != 0xa714) /* BRC with mask 1 */
-    return {};
-
-  insn = read_memory_integer (loc + 2, 2, byte_order);
-  if (insn != (uint16_t) -(len / 2))
-    return {};
-
-  loc += 4;
-
-  /* Found it, step past the whole thing.  */
-  return {loc};
-}
-
-static int
-s390_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
-				   struct displaced_step_closure *closure)
-{
-  return 1;
-}
-
-
 /* Maps for register sets.  */
 
 static const struct regcache_map_entry s390_gregmap[] =
@@ -863,7 +189,7 @@ const struct regset s390_fpregset = {
   regcache_collect_regset
 };
 
-static const struct regset s390_upper_regset = {
+const struct regset s390_upper_regset = {
   s390_regmap_upper,
   regcache_supply_regset,
   regcache_collect_regset
@@ -905,1703 +231,8 @@ const struct regset s390_vxrs_high_regset = {
   regcache_collect_regset
 };
 
-/* Iterate over supported core file register note sections. */
-
-static void
-s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
-				   iterate_over_regset_sections_cb *cb,
-				   void *cb_data,
-				   const struct regcache *regcache)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  const int gregset_size = (tdep->abi == ABI_LINUX_S390 ?
-			    s390_sizeof_gregset : s390x_sizeof_gregset);
-
-  cb (".reg", gregset_size, &s390_gregset, NULL, cb_data);
-  cb (".reg2", s390_sizeof_fpregset, &s390_fpregset, NULL, cb_data);
-
-  if (tdep->abi == ABI_LINUX_S390 && tdep->gpr_full_regnum != -1)
-    cb (".reg-s390-high-gprs", 16 * 4, &s390_upper_regset,
-	"s390 GPR upper halves", cb_data);
-
-  if (tdep->have_linux_v1)
-    cb (".reg-s390-last-break", 8,
-	(gdbarch_ptr_bit (gdbarch) == 32
-	 ? &s390_last_break_regset : &s390x_last_break_regset),
-	"s390 last-break address", cb_data);
-
-  if (tdep->have_linux_v2)
-    cb (".reg-s390-system-call", 4, &s390_system_call_regset,
-	"s390 system-call", cb_data);
-
-  /* If regcache is set, we are in "write" (gcore) mode.  In this
-     case, don't iterate over the TDB unless its registers are
-     available.  */
-  if (tdep->have_tdb
-      && (regcache == NULL
-	  || REG_VALID == regcache_register_status (regcache,
-						    S390_TDB_DWORD0_REGNUM)))
-    cb (".reg-s390-tdb", s390_sizeof_tdbregset, &s390_tdb_regset,
-	"s390 TDB", cb_data);
-
-  if (tdep->v0_full_regnum != -1)
-    {
-      cb (".reg-s390-vxrs-low", 16 * 8, &s390_vxrs_low_regset,
-	  "s390 vector registers 0-15 lower half", cb_data);
-      cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
-	  "s390 vector registers 16-31", cb_data);
-    }
-}
-
-static const struct target_desc *
-s390_core_read_description (struct gdbarch *gdbarch,
-			    struct target_ops *target, bfd *abfd)
-{
-  asection *section = bfd_get_section_by_name (abfd, ".reg");
-  CORE_ADDR hwcap = 0;
-  int high_gprs, v1, v2, te, vx;
-
-  target_auxv_search (target, AT_HWCAP, &hwcap);
-  if (!section)
-    return NULL;
-
-  high_gprs = (bfd_get_section_by_name (abfd, ".reg-s390-high-gprs")
-	       != NULL);
-  v1 = (bfd_get_section_by_name (abfd, ".reg-s390-last-break") != NULL);
-  v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL);
-  vx = (hwcap & HWCAP_S390_VX);
-  te = (hwcap & HWCAP_S390_TE);
-
-  switch (bfd_section_size (abfd, section))
-    {
-    case s390_sizeof_gregset:
-      if (high_gprs)
-	return (te && vx ? tdesc_s390_tevx_linux64 :
-		vx ? tdesc_s390_vx_linux64 :
-		te ? tdesc_s390_te_linux64 :
-		v2 ? tdesc_s390_linux64v2 :
-		v1 ? tdesc_s390_linux64v1 : tdesc_s390_linux64);
-      else
-	return (v2 ? tdesc_s390_linux32v2 :
-		v1 ? tdesc_s390_linux32v1 : tdesc_s390_linux32);
-
-    case s390x_sizeof_gregset:
-      return (te && vx ? tdesc_s390x_tevx_linux64 :
-	      vx ? tdesc_s390x_vx_linux64 :
-	      te ? tdesc_s390x_te_linux64 :
-	      v2 ? tdesc_s390x_linux64v2 :
-	      v1 ? tdesc_s390x_linux64v1 : tdesc_s390x_linux64);
-
-    default:
-      return NULL;
-    }
-}
-
-
-/* Decoding S/390 instructions.  */
-
-/* Named opcode values for the S/390 instructions we recognize.  Some
-   instructions have their opcode split across two fields; those are the
-   op1_* and op2_* enums.  */
-enum
-  {
-    op1_lhi  = 0xa7,   op2_lhi  = 0x08,
-    op1_lghi = 0xa7,   op2_lghi = 0x09,
-    op1_lgfi = 0xc0,   op2_lgfi = 0x01,
-    op_lr    = 0x18,
-    op_lgr   = 0xb904,
-    op_l     = 0x58,
-    op1_ly   = 0xe3,   op2_ly   = 0x58,
-    op1_lg   = 0xe3,   op2_lg   = 0x04,
-    op_lm    = 0x98,
-    op1_lmy  = 0xeb,   op2_lmy  = 0x98,
-    op1_lmg  = 0xeb,   op2_lmg  = 0x04,
-    op_st    = 0x50,
-    op1_sty  = 0xe3,   op2_sty  = 0x50,
-    op1_stg  = 0xe3,   op2_stg  = 0x24,
-    op_std   = 0x60,
-    op_stm   = 0x90,
-    op1_stmy = 0xeb,   op2_stmy = 0x90,
-    op1_stmg = 0xeb,   op2_stmg = 0x24,
-    op1_aghi = 0xa7,   op2_aghi = 0x0b,
-    op1_ahi  = 0xa7,   op2_ahi  = 0x0a,
-    op1_agfi = 0xc2,   op2_agfi = 0x08,
-    op1_afi  = 0xc2,   op2_afi  = 0x09,
-    op1_algfi= 0xc2,   op2_algfi= 0x0a,
-    op1_alfi = 0xc2,   op2_alfi = 0x0b,
-    op_ar    = 0x1a,
-    op_agr   = 0xb908,
-    op_a     = 0x5a,
-    op1_ay   = 0xe3,   op2_ay   = 0x5a,
-    op1_ag   = 0xe3,   op2_ag   = 0x08,
-    op1_slgfi= 0xc2,   op2_slgfi= 0x04,
-    op1_slfi = 0xc2,   op2_slfi = 0x05,
-    op_sr    = 0x1b,
-    op_sgr   = 0xb909,
-    op_s     = 0x5b,
-    op1_sy   = 0xe3,   op2_sy   = 0x5b,
-    op1_sg   = 0xe3,   op2_sg   = 0x09,
-    op_nr    = 0x14,
-    op_ngr   = 0xb980,
-    op_la    = 0x41,
-    op1_lay  = 0xe3,   op2_lay  = 0x71,
-    op1_larl = 0xc0,   op2_larl = 0x00,
-    op_basr  = 0x0d,
-    op_bas   = 0x4d,
-    op_bcr   = 0x07,
-    op_bc    = 0x0d,
-    op_bctr  = 0x06,
-    op_bctgr = 0xb946,
-    op_bct   = 0x46,
-    op1_bctg = 0xe3,   op2_bctg = 0x46,
-    op_bxh   = 0x86,
-    op1_bxhg = 0xeb,   op2_bxhg = 0x44,
-    op_bxle  = 0x87,
-    op1_bxleg= 0xeb,   op2_bxleg= 0x45,
-    op1_bras = 0xa7,   op2_bras = 0x05,
-    op1_brasl= 0xc0,   op2_brasl= 0x05,
-    op1_brc  = 0xa7,   op2_brc  = 0x04,
-    op1_brcl = 0xc0,   op2_brcl = 0x04,
-    op1_brct = 0xa7,   op2_brct = 0x06,
-    op1_brctg= 0xa7,   op2_brctg= 0x07,
-    op_brxh  = 0x84,
-    op1_brxhg= 0xec,   op2_brxhg= 0x44,
-    op_brxle = 0x85,
-    op1_brxlg= 0xec,   op2_brxlg= 0x45,
-    op_svc   = 0x0a,
-  };
-
-
-/* Read a single instruction from address AT.  */
-
-#define S390_MAX_INSTR_SIZE 6
-static int
-s390_readinstruction (bfd_byte instr[], CORE_ADDR at)
-{
-  static int s390_instrlen[] = { 2, 4, 4, 6 };
-  int instrlen;
-
-  if (target_read_memory (at, &instr[0], 2))
-    return -1;
-  instrlen = s390_instrlen[instr[0] >> 6];
-  if (instrlen > 2)
-    {
-      if (target_read_memory (at + 2, &instr[2], instrlen - 2))
-	return -1;
-    }
-  return instrlen;
-}
-
-
-/* The functions below are for recognizing and decoding S/390
-   instructions of various formats.  Each of them checks whether INSN
-   is an instruction of the given format, with the specified opcodes.
-   If it is, it sets the remaining arguments to the values of the
-   instruction's fields, and returns a non-zero value; otherwise, it
-   returns zero.
-
-   These functions' arguments appear in the order they appear in the
-   instruction, not in the machine-language form.  So, opcodes always
-   come first, even though they're sometimes scattered around the
-   instructions.  And displacements appear before base and extension
-   registers, as they do in the assembly syntax, not at the end, as
-   they do in the machine language.  */
-static int
-is_ri (bfd_byte *insn, int op1, int op2, unsigned int *r1, int *i2)
-{
-  if (insn[0] == op1 && (insn[1] & 0xf) == op2)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      /* i2 is a 16-bit signed quantity.  */
-      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_ril (bfd_byte *insn, int op1, int op2,
-	unsigned int *r1, int *i2)
-{
-  if (insn[0] == op1 && (insn[1] & 0xf) == op2)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      /* i2 is a signed quantity.  If the host 'int' is 32 bits long,
-	 no sign extension is necessary, but we don't want to assume
-	 that.  */
-      *i2 = (((insn[2] << 24)
-	      | (insn[3] << 16)
-	      | (insn[4] << 8)
-	      | (insn[5])) ^ 0x80000000) - 0x80000000;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rr (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
-{
-  if (insn[0] == op)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *r2 = insn[1] & 0xf;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rre (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
-{
-  if (((insn[0] << 8) | insn[1]) == op)
-    {
-      /* Yes, insn[3].  insn[2] is unused in RRE format.  */
-      *r1 = (insn[3] >> 4) & 0xf;
-      *r2 = insn[3] & 0xf;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rs (bfd_byte *insn, int op,
-       unsigned int *r1, unsigned int *r3, int *d2, unsigned int *b2)
-{
-  if (insn[0] == op)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *r3 = insn[1] & 0xf;
-      *b2 = (insn[2] >> 4) & 0xf;
-      *d2 = ((insn[2] & 0xf) << 8) | insn[3];
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rsy (bfd_byte *insn, int op1, int op2,
-	unsigned int *r1, unsigned int *r3, int *d2, unsigned int *b2)
-{
-  if (insn[0] == op1
-      && insn[5] == op2)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *r3 = insn[1] & 0xf;
-      *b2 = (insn[2] >> 4) & 0xf;
-      /* The 'long displacement' is a 20-bit signed integer.  */
-      *d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
-		^ 0x80000) - 0x80000;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rx (bfd_byte *insn, int op,
-       unsigned int *r1, int *d2, unsigned int *x2, unsigned int *b2)
-{
-  if (insn[0] == op)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *x2 = insn[1] & 0xf;
-      *b2 = (insn[2] >> 4) & 0xf;
-      *d2 = ((insn[2] & 0xf) << 8) | insn[3];
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rxy (bfd_byte *insn, int op1, int op2,
-	unsigned int *r1, int *d2, unsigned int *x2, unsigned int *b2)
-{
-  if (insn[0] == op1
-      && insn[5] == op2)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *x2 = insn[1] & 0xf;
-      *b2 = (insn[2] >> 4) & 0xf;
-      /* The 'long displacement' is a 20-bit signed integer.  */
-      *d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
-		^ 0x80000) - 0x80000;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-/* Prologue analysis.  */
-
-#define S390_NUM_GPRS 16
-#define S390_NUM_FPRS 16
-
-struct s390_prologue_data {
-
-  /* The stack.  */
-  struct pv_area *stack;
-
-  /* The size and byte-order of a GPR or FPR.  */
-  int gpr_size;
-  int fpr_size;
-  enum bfd_endian byte_order;
-
-  /* The general-purpose registers.  */
-  pv_t gpr[S390_NUM_GPRS];
-
-  /* The floating-point registers.  */
-  pv_t fpr[S390_NUM_FPRS];
-
-  /* The offset relative to the CFA where the incoming GPR N was saved
-     by the function prologue.  0 if not saved or unknown.  */
-  int gpr_slot[S390_NUM_GPRS];
-
-  /* Likewise for FPRs.  */
-  int fpr_slot[S390_NUM_FPRS];
-
-  /* Nonzero if the backchain was saved.  This is assumed to be the
-     case when the incoming SP is saved at the current SP location.  */
-  int back_chain_saved_p;
-};
-
-/* Return the effective address for an X-style instruction, like:
-
-	L R1, D2(X2, B2)
-
-   Here, X2 and B2 are registers, and D2 is a signed 20-bit
-   constant; the effective address is the sum of all three.  If either
-   X2 or B2 are zero, then it doesn't contribute to the sum --- this
-   means that r0 can't be used as either X2 or B2.  */
-static pv_t
-s390_addr (struct s390_prologue_data *data,
-	   int d2, unsigned int x2, unsigned int b2)
-{
-  pv_t result;
-
-  result = pv_constant (d2);
-  if (x2)
-    result = pv_add (result, data->gpr[x2]);
-  if (b2)
-    result = pv_add (result, data->gpr[b2]);
-
-  return result;
-}
-
-/* Do a SIZE-byte store of VALUE to D2(X2,B2).  */
-static void
-s390_store (struct s390_prologue_data *data,
-	    int d2, unsigned int x2, unsigned int b2, CORE_ADDR size,
-	    pv_t value)
-{
-  pv_t addr = s390_addr (data, d2, x2, b2);
-  pv_t offset;
-
-  /* Check whether we are storing the backchain.  */
-  offset = pv_subtract (data->gpr[S390_SP_REGNUM - S390_R0_REGNUM], addr);
-
-  if (pv_is_constant (offset) && offset.k == 0)
-    if (size == data->gpr_size
-	&& pv_is_register_k (value, S390_SP_REGNUM, 0))
-      {
-	data->back_chain_saved_p = 1;
-	return;
-      }
-
-
-  /* Check whether we are storing a register into the stack.  */
-  if (!pv_area_store_would_trash (data->stack, addr))
-    pv_area_store (data->stack, addr, size, value);
-
-
-  /* Note: If this is some store we cannot identify, you might think we
-     should forget our cached values, as any of those might have been hit.
-
-     However, we make the assumption that the register save areas are only
-     ever stored to once in any given function, and we do recognize these
-     stores.  Thus every store we cannot recognize does not hit our data.  */
-}
-
-/* Do a SIZE-byte load from D2(X2,B2).  */
-static pv_t
-s390_load (struct s390_prologue_data *data,
-	   int d2, unsigned int x2, unsigned int b2, CORE_ADDR size)
-
-{
-  pv_t addr = s390_addr (data, d2, x2, b2);
-
-  /* If it's a load from an in-line constant pool, then we can
-     simulate that, under the assumption that the code isn't
-     going to change between the time the processor actually
-     executed it creating the current frame, and the time when
-     we're analyzing the code to unwind past that frame.  */
-  if (pv_is_constant (addr))
-    {
-      struct target_section *secp;
-      secp = target_section_by_addr (&current_target, addr.k);
-      if (secp != NULL
-	  && (bfd_get_section_flags (secp->the_bfd_section->owner,
-				     secp->the_bfd_section)
-	      & SEC_READONLY))
-	return pv_constant (read_memory_integer (addr.k, size,
-						 data->byte_order));
-    }
-
-  /* Check whether we are accessing one of our save slots.  */
-  return pv_area_fetch (data->stack, addr, size);
-}
-
-/* Function for finding saved registers in a 'struct pv_area'; we pass
-   this to pv_area_scan.
-
-   If VALUE is a saved register, ADDR says it was saved at a constant
-   offset from the frame base, and SIZE indicates that the whole
-   register was saved, record its offset in the reg_offset table in
-   PROLOGUE_UNTYPED.  */
-static void
-s390_check_for_saved (void *data_untyped, pv_t addr,
-		      CORE_ADDR size, pv_t value)
-{
-  struct s390_prologue_data *data = (struct s390_prologue_data *) data_untyped;
-  int i, offset;
-
-  if (!pv_is_register (addr, S390_SP_REGNUM))
-    return;
-
-  offset = 16 * data->gpr_size + 32 - addr.k;
-
-  /* If we are storing the original value of a register, we want to
-     record the CFA offset.  If the same register is stored multiple
-     times, the stack slot with the highest address counts.  */
-
-  for (i = 0; i < S390_NUM_GPRS; i++)
-    if (size == data->gpr_size
-	&& pv_is_register_k (value, S390_R0_REGNUM + i, 0))
-      if (data->gpr_slot[i] == 0
-	  || data->gpr_slot[i] > offset)
-	{
-	  data->gpr_slot[i] = offset;
-	  return;
-	}
-
-  for (i = 0; i < S390_NUM_FPRS; i++)
-    if (size == data->fpr_size
-	&& pv_is_register_k (value, S390_F0_REGNUM + i, 0))
-      if (data->fpr_slot[i] == 0
-	  || data->fpr_slot[i] > offset)
-	{
-	  data->fpr_slot[i] = offset;
-	  return;
-	}
-}
-
-/* Analyze the prologue of the function starting at START_PC,
-   continuing at most until CURRENT_PC.  Initialize DATA to
-   hold all information we find out about the state of the registers
-   and stack slots.  Return the address of the instruction after
-   the last one that changed the SP, FP, or back chain; or zero
-   on error.  */
-static CORE_ADDR
-s390_analyze_prologue (struct gdbarch *gdbarch,
-		       CORE_ADDR start_pc,
-		       CORE_ADDR current_pc,
-		       struct s390_prologue_data *data)
-{
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-
-  /* Our return value:
-     The address of the instruction after the last one that changed
-     the SP, FP, or back chain;  zero if we got an error trying to
-     read memory.  */
-  CORE_ADDR result = start_pc;
-
-  /* The current PC for our abstract interpretation.  */
-  CORE_ADDR pc;
-
-  /* The address of the next instruction after that.  */
-  CORE_ADDR next_pc;
-
-  /* Set up everything's initial value.  */
-  {
-    int i;
-
-    data->stack = make_pv_area (S390_SP_REGNUM, gdbarch_addr_bit (gdbarch));
-
-    /* For the purpose of prologue tracking, we consider the GPR size to
-       be equal to the ABI word size, even if it is actually larger
-       (i.e. when running a 32-bit binary under a 64-bit kernel).  */
-    data->gpr_size = word_size;
-    data->fpr_size = 8;
-    data->byte_order = gdbarch_byte_order (gdbarch);
-
-    for (i = 0; i < S390_NUM_GPRS; i++)
-      data->gpr[i] = pv_register (S390_R0_REGNUM + i, 0);
-
-    for (i = 0; i < S390_NUM_FPRS; i++)
-      data->fpr[i] = pv_register (S390_F0_REGNUM + i, 0);
-
-    for (i = 0; i < S390_NUM_GPRS; i++)
-      data->gpr_slot[i]  = 0;
-
-    for (i = 0; i < S390_NUM_FPRS; i++)
-      data->fpr_slot[i]  = 0;
-
-    data->back_chain_saved_p = 0;
-  }
-
-  /* Start interpreting instructions, until we hit the frame's
-     current PC or the first branch instruction.  */
-  for (pc = start_pc; pc > 0 && pc < current_pc; pc = next_pc)
-    {
-      bfd_byte insn[S390_MAX_INSTR_SIZE];
-      int insn_len = s390_readinstruction (insn, pc);
-
-      bfd_byte dummy[S390_MAX_INSTR_SIZE] = { 0 };
-      bfd_byte *insn32 = word_size == 4 ? insn : dummy;
-      bfd_byte *insn64 = word_size == 8 ? insn : dummy;
-
-      /* Fields for various kinds of instructions.  */
-      unsigned int b2, r1, r2, x2, r3;
-      int i2, d2;
-
-      /* The values of SP and FP before this instruction,
-	 for detecting instructions that change them.  */
-      pv_t pre_insn_sp, pre_insn_fp;
-      /* Likewise for the flag whether the back chain was saved.  */
-      int pre_insn_back_chain_saved_p;
-
-      /* If we got an error trying to read the instruction, report it.  */
-      if (insn_len < 0)
-	{
-	  result = 0;
-	  break;
-	}
-
-      next_pc = pc + insn_len;
-
-      pre_insn_sp = data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
-      pre_insn_fp = data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
-      pre_insn_back_chain_saved_p = data->back_chain_saved_p;
-
-
-      /* LHI r1, i2 --- load halfword immediate.  */
-      /* LGHI r1, i2 --- load halfword immediate (64-bit version).  */
-      /* LGFI r1, i2 --- load fullword immediate.  */
-      if (is_ri (insn32, op1_lhi, op2_lhi, &r1, &i2)
-	  || is_ri (insn64, op1_lghi, op2_lghi, &r1, &i2)
-	  || is_ril (insn, op1_lgfi, op2_lgfi, &r1, &i2))
-	data->gpr[r1] = pv_constant (i2);
-
-      /* LR r1, r2 --- load from register.  */
-      /* LGR r1, r2 --- load from register (64-bit version).  */
-      else if (is_rr (insn32, op_lr, &r1, &r2)
-	       || is_rre (insn64, op_lgr, &r1, &r2))
-	data->gpr[r1] = data->gpr[r2];
-
-      /* L r1, d2(x2, b2) --- load.  */
-      /* LY r1, d2(x2, b2) --- load (long-displacement version).  */
-      /* LG r1, d2(x2, b2) --- load (64-bit version).  */
-      else if (is_rx (insn32, op_l, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn32, op1_ly, op2_ly, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn64, op1_lg, op2_lg, &r1, &d2, &x2, &b2))
-	data->gpr[r1] = s390_load (data, d2, x2, b2, data->gpr_size);
-
-      /* ST r1, d2(x2, b2) --- store.  */
-      /* STY r1, d2(x2, b2) --- store (long-displacement version).  */
-      /* STG r1, d2(x2, b2) --- store (64-bit version).  */
-      else if (is_rx (insn32, op_st, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn32, op1_sty, op2_sty, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn64, op1_stg, op2_stg, &r1, &d2, &x2, &b2))
-	s390_store (data, d2, x2, b2, data->gpr_size, data->gpr[r1]);
-
-      /* STD r1, d2(x2,b2) --- store floating-point register.  */
-      else if (is_rx (insn, op_std, &r1, &d2, &x2, &b2))
-	s390_store (data, d2, x2, b2, data->fpr_size, data->fpr[r1]);
-
-      /* STM r1, r3, d2(b2) --- store multiple.  */
-      /* STMY r1, r3, d2(b2) --- store multiple (long-displacement
-	 version).  */
-      /* STMG r1, r3, d2(b2) --- store multiple (64-bit version).  */
-      else if (is_rs (insn32, op_stm, &r1, &r3, &d2, &b2)
-	       || is_rsy (insn32, op1_stmy, op2_stmy, &r1, &r3, &d2, &b2)
-	       || is_rsy (insn64, op1_stmg, op2_stmg, &r1, &r3, &d2, &b2))
-	{
-	  for (; r1 <= r3; r1++, d2 += data->gpr_size)
-	    s390_store (data, d2, 0, b2, data->gpr_size, data->gpr[r1]);
-	}
-
-      /* AHI r1, i2 --- add halfword immediate.  */
-      /* AGHI r1, i2 --- add halfword immediate (64-bit version).  */
-      /* AFI r1, i2 --- add fullword immediate.  */
-      /* AGFI r1, i2 --- add fullword immediate (64-bit version).  */
-      else if (is_ri (insn32, op1_ahi, op2_ahi, &r1, &i2)
-	       || is_ri (insn64, op1_aghi, op2_aghi, &r1, &i2)
-	       || is_ril (insn32, op1_afi, op2_afi, &r1, &i2)
-	       || is_ril (insn64, op1_agfi, op2_agfi, &r1, &i2))
-	data->gpr[r1] = pv_add_constant (data->gpr[r1], i2);
-
-      /* ALFI r1, i2 --- add logical immediate.  */
-      /* ALGFI r1, i2 --- add logical immediate (64-bit version).  */
-      else if (is_ril (insn32, op1_alfi, op2_alfi, &r1, &i2)
-	       || is_ril (insn64, op1_algfi, op2_algfi, &r1, &i2))
-	data->gpr[r1] = pv_add_constant (data->gpr[r1],
-					 (CORE_ADDR)i2 & 0xffffffff);
-
-      /* AR r1, r2 -- add register.  */
-      /* AGR r1, r2 -- add register (64-bit version).  */
-      else if (is_rr (insn32, op_ar, &r1, &r2)
-	       || is_rre (insn64, op_agr, &r1, &r2))
-	data->gpr[r1] = pv_add (data->gpr[r1], data->gpr[r2]);
-
-      /* A r1, d2(x2, b2) -- add.  */
-      /* AY r1, d2(x2, b2) -- add (long-displacement version).  */
-      /* AG r1, d2(x2, b2) -- add (64-bit version).  */
-      else if (is_rx (insn32, op_a, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn32, op1_ay, op2_ay, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn64, op1_ag, op2_ag, &r1, &d2, &x2, &b2))
-	data->gpr[r1] = pv_add (data->gpr[r1],
-				s390_load (data, d2, x2, b2, data->gpr_size));
-
-      /* SLFI r1, i2 --- subtract logical immediate.  */
-      /* SLGFI r1, i2 --- subtract logical immediate (64-bit version).  */
-      else if (is_ril (insn32, op1_slfi, op2_slfi, &r1, &i2)
-	       || is_ril (insn64, op1_slgfi, op2_slgfi, &r1, &i2))
-	data->gpr[r1] = pv_add_constant (data->gpr[r1],
-					 -((CORE_ADDR)i2 & 0xffffffff));
-
-      /* SR r1, r2 -- subtract register.  */
-      /* SGR r1, r2 -- subtract register (64-bit version).  */
-      else if (is_rr (insn32, op_sr, &r1, &r2)
-	       || is_rre (insn64, op_sgr, &r1, &r2))
-	data->gpr[r1] = pv_subtract (data->gpr[r1], data->gpr[r2]);
-
-      /* S r1, d2(x2, b2) -- subtract.  */
-      /* SY r1, d2(x2, b2) -- subtract (long-displacement version).  */
-      /* SG r1, d2(x2, b2) -- subtract (64-bit version).  */
-      else if (is_rx (insn32, op_s, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn32, op1_sy, op2_sy, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn64, op1_sg, op2_sg, &r1, &d2, &x2, &b2))
-	data->gpr[r1] = pv_subtract (data->gpr[r1],
-				s390_load (data, d2, x2, b2, data->gpr_size));
-
-      /* LA r1, d2(x2, b2) --- load address.  */
-      /* LAY r1, d2(x2, b2) --- load address (long-displacement version).  */
-      else if (is_rx (insn, op_la, &r1, &d2, &x2, &b2)
-	       || is_rxy (insn, op1_lay, op2_lay, &r1, &d2, &x2, &b2))
-	data->gpr[r1] = s390_addr (data, d2, x2, b2);
-
-      /* LARL r1, i2 --- load address relative long.  */
-      else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
-	data->gpr[r1] = pv_constant (pc + i2 * 2);
-
-      /* BASR r1, 0 --- branch and save.
-	 Since r2 is zero, this saves the PC in r1, but doesn't branch.  */
-      else if (is_rr (insn, op_basr, &r1, &r2)
-	       && r2 == 0)
-	data->gpr[r1] = pv_constant (next_pc);
-
-      /* BRAS r1, i2 --- branch relative and save.  */
-      else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2))
-	{
-	  data->gpr[r1] = pv_constant (next_pc);
-	  next_pc = pc + i2 * 2;
-
-	  /* We'd better not interpret any backward branches.  We'll
-	     never terminate.  */
-	  if (next_pc <= pc)
-	    break;
-	}
-
-      /* BRC/BRCL -- branch relative on condition.  Ignore "branch
-	 never", branch to following instruction, and "conditional
-	 trap" (BRC +2).  Otherwise terminate search.  */
-      else if (is_ri (insn, op1_brc, op2_brc, &r1, &i2))
-	{
-	  if (r1 != 0 && i2 != 1 && i2 != 2)
-	    break;
-	}
-      else if (is_ril (insn, op1_brcl, op2_brcl, &r1, &i2))
-	{
-	  if (r1 != 0 && i2 != 3)
-	    break;
-	}
-
-      /* Terminate search when hitting any other branch instruction.  */
-      else if (is_rr (insn, op_basr, &r1, &r2)
-	       || is_rx (insn, op_bas, &r1, &d2, &x2, &b2)
-	       || is_rr (insn, op_bcr, &r1, &r2)
-	       || is_rx (insn, op_bc, &r1, &d2, &x2, &b2)
-	       || is_ril (insn, op1_brasl, op2_brasl, &r2, &i2))
-	break;
-
-      else
-	{
-	  /* An instruction we don't know how to simulate.  The only
-	     safe thing to do would be to set every value we're tracking
-	     to 'unknown'.  Instead, we'll be optimistic: we assume that
-	     we *can* interpret every instruction that the compiler uses
-	     to manipulate any of the data we're interested in here --
-	     then we can just ignore anything else.  */
-	}
-
-      /* Record the address after the last instruction that changed
-	 the FP, SP, or backlink.  Ignore instructions that changed
-	 them back to their original values --- those are probably
-	 restore instructions.  (The back chain is never restored,
-	 just popped.)  */
-      {
-	pv_t sp = data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
-	pv_t fp = data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
-
-	if ((! pv_is_identical (pre_insn_sp, sp)
-	     && ! pv_is_register_k (sp, S390_SP_REGNUM, 0)
-	     && sp.kind != pvk_unknown)
-	    || (! pv_is_identical (pre_insn_fp, fp)
-		&& ! pv_is_register_k (fp, S390_FRAME_REGNUM, 0)
-		&& fp.kind != pvk_unknown)
-	    || pre_insn_back_chain_saved_p != data->back_chain_saved_p)
-	  result = next_pc;
-      }
-    }
-
-  /* Record where all the registers were saved.  */
-  pv_area_scan (data->stack, s390_check_for_saved, data);
-
-  free_pv_area (data->stack);
-  data->stack = NULL;
-
-  return result;
-}
-
-/* Advance PC across any function entry prologue instructions to reach
-   some "real" code.  */
-static CORE_ADDR
-s390_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
-{
-  struct s390_prologue_data data;
-  CORE_ADDR skip_pc, func_addr;
-
-  if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
-    {
-      CORE_ADDR post_prologue_pc
-	= skip_prologue_using_sal (gdbarch, func_addr);
-      if (post_prologue_pc != 0)
-	return std::max (pc, post_prologue_pc);
-    }
-
-  skip_pc = s390_analyze_prologue (gdbarch, pc, (CORE_ADDR)-1, &data);
-  return skip_pc ? skip_pc : pc;
-}
-
-/* Implmement the stack_frame_destroyed_p gdbarch method.  */
-static int
-s390_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
-{
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-
-  /* In frameless functions, there's not frame to destroy and thus
-     we don't care about the epilogue.
-
-     In functions with frame, the epilogue sequence is a pair of
-     a LM-type instruction that restores (amongst others) the
-     return register %r14 and the stack pointer %r15, followed
-     by a branch 'br %r14' --or equivalent-- that effects the
-     actual return.
-
-     In that situation, this function needs to return 'true' in
-     exactly one case: when pc points to that branch instruction.
-
-     Thus we try to disassemble the one instructions immediately
-     preceding pc and check whether it is an LM-type instruction
-     modifying the stack pointer.
-
-     Note that disassembling backwards is not reliable, so there
-     is a slight chance of false positives here ...  */
-
-  bfd_byte insn[6];
-  unsigned int r1, r3, b2;
-  int d2;
-
-  if (word_size == 4
-      && !target_read_memory (pc - 4, insn, 4)
-      && is_rs (insn, op_lm, &r1, &r3, &d2, &b2)
-      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
-    return 1;
-
-  if (word_size == 4
-      && !target_read_memory (pc - 6, insn, 6)
-      && is_rsy (insn, op1_lmy, op2_lmy, &r1, &r3, &d2, &b2)
-      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
-    return 1;
-
-  if (word_size == 8
-      && !target_read_memory (pc - 6, insn, 6)
-      && is_rsy (insn, op1_lmg, op2_lmg, &r1, &r3, &d2, &b2)
-      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
-    return 1;
-
-  return 0;
-}
-
-/* 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 = (gdb_byte *) 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
-s390_displaced_step_fixup (struct gdbarch *gdbarch,
-			   struct displaced_step_closure *closure,
-			   CORE_ADDR from, CORE_ADDR to,
-			   struct regcache *regs)
-{
-  /* 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];
-
-  /* Fields for various kinds of instructions.  */
-  unsigned int b2, r1, r2, x2, r3;
-  int i2, d2;
-
-  /* Get current PC and addressing mode bit.  */
-  CORE_ADDR pc = regcache_read_pc (regs);
-  ULONGEST amode = 0;
-
-  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
-    {
-      regcache_cooked_read_unsigned (regs, S390_PSWA_REGNUM, &amode);
-      amode &= 0x80000000;
-    }
-
-  if (debug_displaced)
-    fprintf_unfiltered (gdb_stdlog,
-			"displaced: (s390) fixup (%s, %s) pc %s len %d amode 0x%x\n",
-			paddress (gdbarch, from), paddress (gdbarch, to),
-			paddress (gdbarch, pc), insnlen, (int) amode);
-
-  /* Handle absolute branch and save instructions.  */
-  if (is_rr (insn, op_basr, &r1, &r2)
-      || is_rx (insn, op_bas, &r1, &d2, &x2, &b2))
-    {
-      /* Recompute saved return address in R1.  */
-      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
-				      amode | (from + insnlen));
-    }
-
-  /* Handle absolute branch instructions.  */
-  else if (is_rr (insn, op_bcr, &r1, &r2)
-	   || is_rx (insn, op_bc, &r1, &d2, &x2, &b2)
-	   || is_rr (insn, op_bctr, &r1, &r2)
-	   || is_rre (insn, op_bctgr, &r1, &r2)
-	   || is_rx (insn, op_bct, &r1, &d2, &x2, &b2)
-	   || is_rxy (insn, op1_bctg, op2_brctg, &r1, &d2, &x2, &b2)
-	   || is_rs (insn, op_bxh, &r1, &r3, &d2, &b2)
-	   || is_rsy (insn, op1_bxhg, op2_bxhg, &r1, &r3, &d2, &b2)
-	   || is_rs (insn, op_bxle, &r1, &r3, &d2, &b2)
-	   || is_rsy (insn, op1_bxleg, op2_bxleg, &r1, &r3, &d2, &b2))
-    {
-      /* Update PC iff branch was *not* taken.  */
-      if (pc == to + insnlen)
-	regcache_write_pc (regs, from + insnlen);
-    }
-
-  /* Handle PC-relative branch and save instructions.  */
-  else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2)
-	   || is_ril (insn, op1_brasl, op2_brasl, &r1, &i2))
-    {
-      /* Update PC.  */
-      regcache_write_pc (regs, pc - to + from);
-      /* Recompute saved return address in R1.  */
-      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
-				      amode | (from + insnlen));
-    }
-
-  /* Handle LOAD ADDRESS RELATIVE LONG.  */
-  else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
-    {
-      /* Update PC.  */
-      regcache_write_pc (regs, from + insnlen);
-      /* Recompute output address in R1.  */
-      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
-				      amode | (from + i2 * 2));
-    }
-
-  /* If we executed a breakpoint instruction, point PC right back at it.  */
-  else if (insn[0] == 0x0 && insn[1] == 0x1)
-    regcache_write_pc (regs, from);
-
-  /* For any other insn, adjust PC by negated displacement.  PC then
-     points right after the original instruction, except for PC-relative
-     branches, where it points to the adjusted branch target.  */
-  else
-    regcache_write_pc (regs, pc - to + from);
-
-  if (debug_displaced)
-    fprintf_unfiltered (gdb_stdlog,
-			"displaced: (s390) pc is now %s\n",
-			paddress (gdbarch, regcache_read_pc (regs)));
-}
-
-
-/* Helper routine to unwind pseudo registers.  */
-
-static struct value *
-s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  struct type *type = register_type (gdbarch, regnum);
-
-  /* Unwind PC via PSW address.  */
-  if (regnum == tdep->pc_regnum)
-    {
-      struct value *val;
-
-      val = frame_unwind_register_value (this_frame, S390_PSWA_REGNUM);
-      if (!value_optimized_out (val))
-	{
-	  LONGEST pswa = value_as_long (val);
-
-	  if (TYPE_LENGTH (type) == 4)
-	    return value_from_pointer (type, pswa & 0x7fffffff);
-	  else
-	    return value_from_pointer (type, pswa);
-	}
-    }
-
-  /* Unwind CC via PSW mask.  */
-  if (regnum == tdep->cc_regnum)
-    {
-      struct value *val;
-
-      val = frame_unwind_register_value (this_frame, S390_PSWM_REGNUM);
-      if (!value_optimized_out (val))
-	{
-	  LONGEST pswm = value_as_long (val);
-
-	  if (TYPE_LENGTH (type) == 4)
-	    return value_from_longest (type, (pswm >> 12) & 3);
-	  else
-	    return value_from_longest (type, (pswm >> 44) & 3);
-	}
-    }
-
-  /* Unwind full GPRs to show at least the lower halves (as the
-     upper halves are undefined).  */
-  if (regnum_is_gpr_full (tdep, regnum))
-    {
-      int reg = regnum - tdep->gpr_full_regnum;
-      struct value *val;
-
-      val = frame_unwind_register_value (this_frame, S390_R0_REGNUM + reg);
-      if (!value_optimized_out (val))
-	return value_cast (type, val);
-    }
-
-  return allocate_optimized_out_value (type);
-}
-
-static struct value *
-s390_trad_frame_prev_register (struct frame_info *this_frame,
-			       struct trad_frame_saved_reg saved_regs[],
-			       int regnum)
-{
-  if (regnum < S390_NUM_REGS)
-    return trad_frame_get_prev_register (this_frame, saved_regs, regnum);
-  else
-    return s390_unwind_pseudo_register (this_frame, regnum);
-}
-
-
-/* Normal stack frames.  */
-
-struct s390_unwind_cache {
-
-  CORE_ADDR func;
-  CORE_ADDR frame_base;
-  CORE_ADDR local_base;
-
-  struct trad_frame_saved_reg *saved_regs;
-};
-
-static int
-s390_prologue_frame_unwind_cache (struct frame_info *this_frame,
-				  struct s390_unwind_cache *info)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  struct s390_prologue_data data;
-  pv_t *fp = &data.gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
-  pv_t *sp = &data.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
-  int i;
-  CORE_ADDR cfa;
-  CORE_ADDR func;
-  CORE_ADDR result;
-  ULONGEST reg;
-  CORE_ADDR prev_sp;
-  int frame_pointer;
-  int size;
-  struct frame_info *next_frame;
-
-  /* Try to find the function start address.  If we can't find it, we don't
-     bother searching for it -- with modern compilers this would be mostly
-     pointless anyway.  Trust that we'll either have valid DWARF-2 CFI data
-     or else a valid backchain ...  */
-  if (!get_frame_func_if_available (this_frame, &info->func))
-    {
-      info->func = -1;
-      return 0;
-    }
-  func = info->func;
-
-  /* Try to analyze the prologue.  */
-  result = s390_analyze_prologue (gdbarch, func,
-				  get_frame_pc (this_frame), &data);
-  if (!result)
-    return 0;
 
-  /* If this was successful, we should have found the instruction that
-     sets the stack pointer register to the previous value of the stack
-     pointer minus the frame size.  */
-  if (!pv_is_register (*sp, S390_SP_REGNUM))
-    return 0;
-
-  /* A frame size of zero at this point can mean either a real
-     frameless function, or else a failure to find the prologue.
-     Perform some sanity checks to verify we really have a
-     frameless function.  */
-  if (sp->k == 0)
-    {
-      /* If the next frame is a NORMAL_FRAME, this frame *cannot* have frame
-	 size zero.  This is only possible if the next frame is a sentinel
-	 frame, a dummy frame, or a signal trampoline frame.  */
-      /* FIXME: cagney/2004-05-01: This sanity check shouldn't be
-	 needed, instead the code should simpliy rely on its
-	 analysis.  */
-      next_frame = get_next_frame (this_frame);
-      while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
-	next_frame = get_next_frame (next_frame);
-      if (next_frame
-	  && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME)
-	return 0;
-
-      /* If we really have a frameless function, %r14 must be valid
-	 -- in particular, it must point to a different function.  */
-      reg = get_frame_register_unsigned (this_frame, S390_RETADDR_REGNUM);
-      reg = gdbarch_addr_bits_remove (gdbarch, reg) - 1;
-      if (get_pc_function_start (reg) == func)
-	{
-	  /* However, there is one case where it *is* valid for %r14
-	     to point to the same function -- if this is a recursive
-	     call, and we have stopped in the prologue *before* the
-	     stack frame was allocated.
-
-	     Recognize this case by looking ahead a bit ...  */
-
-	  struct s390_prologue_data data2;
-	  pv_t *sp = &data2.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
-
-	  if (!(s390_analyze_prologue (gdbarch, func, (CORE_ADDR)-1, &data2)
-		&& pv_is_register (*sp, S390_SP_REGNUM)
-		&& sp->k != 0))
-	    return 0;
-	}
-    }
-
-
-  /* OK, we've found valid prologue data.  */
-  size = -sp->k;
-
-  /* If the frame pointer originally also holds the same value
-     as the stack pointer, we're probably using it.  If it holds
-     some other value -- even a constant offset -- it is most
-     likely used as temp register.  */
-  if (pv_is_identical (*sp, *fp))
-    frame_pointer = S390_FRAME_REGNUM;
-  else
-    frame_pointer = S390_SP_REGNUM;
-
-  /* If we've detected a function with stack frame, we'll still have to
-     treat it as frameless if we're currently within the function epilog
-     code at a point where the frame pointer has already been restored.
-     This can only happen in an innermost frame.  */
-  /* FIXME: cagney/2004-05-01: This sanity check shouldn't be needed,
-     instead the code should simpliy rely on its analysis.  */
-  next_frame = get_next_frame (this_frame);
-  while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
-    next_frame = get_next_frame (next_frame);
-  if (size > 0
-      && (next_frame == NULL
-	  || get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME))
-    {
-      /* See the comment in s390_stack_frame_destroyed_p on why this is
-	 not completely reliable ...  */
-      if (s390_stack_frame_destroyed_p (gdbarch, get_frame_pc (this_frame)))
-	{
-	  memset (&data, 0, sizeof (data));
-	  size = 0;
-	  frame_pointer = S390_SP_REGNUM;
-	}
-    }
-
-  /* Once we know the frame register and the frame size, we can unwind
-     the current value of the frame register from the next frame, and
-     add back the frame size to arrive that the previous frame's
-     stack pointer value.  */
-  prev_sp = get_frame_register_unsigned (this_frame, frame_pointer) + size;
-  cfa = prev_sp + 16*word_size + 32;
-
-  /* Set up ABI call-saved/call-clobbered registers.  */
-  for (i = 0; i < S390_NUM_REGS; i++)
-    if (!s390_register_call_saved (gdbarch, i))
-      trad_frame_set_unknown (info->saved_regs, i);
-
-  /* CC is always call-clobbered.  */
-  trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
-
-  /* Record the addresses of all register spill slots the prologue parser
-     has recognized.  Consider only registers defined as call-saved by the
-     ABI; for call-clobbered registers the parser may have recognized
-     spurious stores.  */
-
-  for (i = 0; i < 16; i++)
-    if (s390_register_call_saved (gdbarch, S390_R0_REGNUM + i)
-	&& data.gpr_slot[i] != 0)
-      info->saved_regs[S390_R0_REGNUM + i].addr = cfa - data.gpr_slot[i];
-
-  for (i = 0; i < 16; i++)
-    if (s390_register_call_saved (gdbarch, S390_F0_REGNUM + i)
-	&& data.fpr_slot[i] != 0)
-      info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
-
-  /* Function return will set PC to %r14.  */
-  info->saved_regs[S390_PSWA_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
-
-  /* In frameless functions, we unwind simply by moving the return
-     address to the PC.  However, if we actually stored to the
-     save area, use that -- we might only think the function frameless
-     because we're in the middle of the prologue ...  */
-  if (size == 0
-      && !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
-    {
-      info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
-    }
-
-  /* Another sanity check: unless this is a frameless function,
-     we should have found spill slots for SP and PC.
-     If not, we cannot unwind further -- this happens e.g. in
-     libc's thread_start routine.  */
-  if (size > 0)
-    {
-      if (!trad_frame_addr_p (info->saved_regs, S390_SP_REGNUM)
-	  || !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
-	prev_sp = -1;
-    }
-
-  /* We use the current value of the frame register as local_base,
-     and the top of the register save area as frame_base.  */
-  if (prev_sp != -1)
-    {
-      info->frame_base = prev_sp + 16*word_size + 32;
-      info->local_base = prev_sp - size;
-    }
-
-  return 1;
-}
-
-static void
-s390_backchain_frame_unwind_cache (struct frame_info *this_frame,
-				   struct s390_unwind_cache *info)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  CORE_ADDR backchain;
-  ULONGEST reg;
-  LONGEST sp, tmp;
-  int i;
-
-  /* Set up ABI call-saved/call-clobbered registers.  */
-  for (i = 0; i < S390_NUM_REGS; i++)
-    if (!s390_register_call_saved (gdbarch, i))
-      trad_frame_set_unknown (info->saved_regs, i);
-
-  /* CC is always call-clobbered.  */
-  trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
-
-  /* Get the backchain.  */
-  reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
-  if (!safe_read_memory_integer (reg, word_size, byte_order, &tmp))
-    tmp = 0;
-  backchain = (CORE_ADDR) tmp;
-
-  /* A zero backchain terminates the frame chain.  As additional
-     sanity check, let's verify that the spill slot for SP in the
-     save area pointed to by the backchain in fact links back to
-     the save area.  */
-  if (backchain != 0
-      && safe_read_memory_integer (backchain + 15*word_size,
-				   word_size, byte_order, &sp)
-      && (CORE_ADDR)sp == backchain)
-    {
-      /* We don't know which registers were saved, but it will have
-	 to be at least %r14 and %r15.  This will allow us to continue
-	 unwinding, but other prev-frame registers may be incorrect ...  */
-      info->saved_regs[S390_SP_REGNUM].addr = backchain + 15*word_size;
-      info->saved_regs[S390_RETADDR_REGNUM].addr = backchain + 14*word_size;
-
-      /* Function return will set PC to %r14.  */
-      info->saved_regs[S390_PSWA_REGNUM]
-	= info->saved_regs[S390_RETADDR_REGNUM];
-
-      /* We use the current value of the frame register as local_base,
-	 and the top of the register save area as frame_base.  */
-      info->frame_base = backchain + 16*word_size + 32;
-      info->local_base = reg;
-    }
-
-  info->func = get_frame_pc (this_frame);
-}
-
-static struct s390_unwind_cache *
-s390_frame_unwind_cache (struct frame_info *this_frame,
-			 void **this_prologue_cache)
-{
-  struct s390_unwind_cache *info;
-
-  if (*this_prologue_cache)
-    return (struct s390_unwind_cache *) *this_prologue_cache;
-
-  info = FRAME_OBSTACK_ZALLOC (struct s390_unwind_cache);
-  *this_prologue_cache = info;
-  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
-  info->func = -1;
-  info->frame_base = -1;
-  info->local_base = -1;
-
-  TRY
-    {
-      /* Try to use prologue analysis to fill the unwind cache.
-	 If this fails, fall back to reading the stack backchain.  */
-      if (!s390_prologue_frame_unwind_cache (this_frame, info))
-	s390_backchain_frame_unwind_cache (this_frame, info);
-    }
-  CATCH (ex, RETURN_MASK_ERROR)
-    {
-      if (ex.error != NOT_AVAILABLE_ERROR)
-	throw_exception (ex);
-    }
-  END_CATCH
-
-  return info;
-}
-
-static void
-s390_frame_this_id (struct frame_info *this_frame,
-		    void **this_prologue_cache,
-		    struct frame_id *this_id)
-{
-  struct s390_unwind_cache *info
-    = s390_frame_unwind_cache (this_frame, this_prologue_cache);
-
-  if (info->frame_base == -1)
-    {
-      if (info->func != -1)
-	*this_id = frame_id_build_unavailable_stack (info->func);
-      return;
-    }
-
-  *this_id = frame_id_build (info->frame_base, info->func);
-}
-
-static struct value *
-s390_frame_prev_register (struct frame_info *this_frame,
-			  void **this_prologue_cache, int regnum)
-{
-  struct s390_unwind_cache *info
-    = s390_frame_unwind_cache (this_frame, this_prologue_cache);
-
-  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
-}
-
-static const struct frame_unwind s390_frame_unwind = {
-  NORMAL_FRAME,
-  default_frame_unwind_stop_reason,
-  s390_frame_this_id,
-  s390_frame_prev_register,
-  NULL,
-  default_frame_sniffer
-};
-
-
-/* Code stubs and their stack frames.  For things like PLTs and NULL
-   function calls (where there is no true frame and the return address
-   is in the RETADDR register).  */
-
-struct s390_stub_unwind_cache
-{
-  CORE_ADDR frame_base;
-  struct trad_frame_saved_reg *saved_regs;
-};
-
-static struct s390_stub_unwind_cache *
-s390_stub_frame_unwind_cache (struct frame_info *this_frame,
-			      void **this_prologue_cache)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  struct s390_stub_unwind_cache *info;
-  ULONGEST reg;
-
-  if (*this_prologue_cache)
-    return (struct s390_stub_unwind_cache *) *this_prologue_cache;
-
-  info = FRAME_OBSTACK_ZALLOC (struct s390_stub_unwind_cache);
-  *this_prologue_cache = info;
-  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
-
-  /* The return address is in register %r14.  */
-  info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
-
-  /* Retrieve stack pointer and determine our frame base.  */
-  reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
-  info->frame_base = reg + 16*word_size + 32;
-
-  return info;
-}
-
-static void
-s390_stub_frame_this_id (struct frame_info *this_frame,
-			 void **this_prologue_cache,
-			 struct frame_id *this_id)
-{
-  struct s390_stub_unwind_cache *info
-    = s390_stub_frame_unwind_cache (this_frame, this_prologue_cache);
-  *this_id = frame_id_build (info->frame_base, get_frame_pc (this_frame));
-}
-
-static struct value *
-s390_stub_frame_prev_register (struct frame_info *this_frame,
-			       void **this_prologue_cache, int regnum)
-{
-  struct s390_stub_unwind_cache *info
-    = s390_stub_frame_unwind_cache (this_frame, this_prologue_cache);
-  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
-}
-
-static int
-s390_stub_frame_sniffer (const struct frame_unwind *self,
-			 struct frame_info *this_frame,
-			 void **this_prologue_cache)
-{
-  CORE_ADDR addr_in_block;
-  bfd_byte insn[S390_MAX_INSTR_SIZE];
-
-  /* If the current PC points to non-readable memory, we assume we
-     have trapped due to an invalid function pointer call.  We handle
-     the non-existing current function like a PLT stub.  */
-  addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block)
-      || s390_readinstruction (insn, get_frame_pc (this_frame)) < 0)
-    return 1;
-  return 0;
-}
-
-static const struct frame_unwind s390_stub_frame_unwind = {
-  NORMAL_FRAME,
-  default_frame_unwind_stop_reason,
-  s390_stub_frame_this_id,
-  s390_stub_frame_prev_register,
-  NULL,
-  s390_stub_frame_sniffer
-};
-
-
-/* Signal trampoline stack frames.  */
-
-struct s390_sigtramp_unwind_cache {
-  CORE_ADDR frame_base;
-  struct trad_frame_saved_reg *saved_regs;
-};
-
-static struct s390_sigtramp_unwind_cache *
-s390_sigtramp_frame_unwind_cache (struct frame_info *this_frame,
-				  void **this_prologue_cache)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  struct s390_sigtramp_unwind_cache *info;
-  ULONGEST this_sp, prev_sp;
-  CORE_ADDR next_ra, next_cfa, sigreg_ptr, sigreg_high_off;
-  int i;
-
-  if (*this_prologue_cache)
-    return (struct s390_sigtramp_unwind_cache *) *this_prologue_cache;
-
-  info = FRAME_OBSTACK_ZALLOC (struct s390_sigtramp_unwind_cache);
-  *this_prologue_cache = info;
-  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
-
-  this_sp = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
-  next_ra = get_frame_pc (this_frame);
-  next_cfa = this_sp + 16*word_size + 32;
-
-  /* New-style RT frame:
-	retcode + alignment (8 bytes)
-	siginfo (128 bytes)
-	ucontext (contains sigregs at offset 5 words).  */
-  if (next_ra == next_cfa)
-    {
-      sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8);
-      /* sigregs are followed by uc_sigmask (8 bytes), then by the
-	 upper GPR halves if present.  */
-      sigreg_high_off = 8;
-    }
-
-  /* Old-style RT frame and all non-RT frames:
-	old signal mask (8 bytes)
-	pointer to sigregs.  */
-  else
-    {
-      sigreg_ptr = read_memory_unsigned_integer (next_cfa + 8,
-						 word_size, byte_order);
-      /* sigregs are followed by signo (4 bytes), then by the
-	 upper GPR halves if present.  */
-      sigreg_high_off = 4;
-    }
-
-  /* The sigregs structure looks like this:
-	    long   psw_mask;
-	    long   psw_addr;
-	    long   gprs[16];
-	    int    acrs[16];
-	    int    fpc;
-	    int    __pad;
-	    double fprs[16];  */
-
-  /* PSW mask and address.  */
-  info->saved_regs[S390_PSWM_REGNUM].addr = sigreg_ptr;
-  sigreg_ptr += word_size;
-  info->saved_regs[S390_PSWA_REGNUM].addr = sigreg_ptr;
-  sigreg_ptr += word_size;
-
-  /* Then the GPRs.  */
-  for (i = 0; i < 16; i++)
-    {
-      info->saved_regs[S390_R0_REGNUM + i].addr = sigreg_ptr;
-      sigreg_ptr += word_size;
-    }
-
-  /* Then the ACRs.  */
-  for (i = 0; i < 16; i++)
-    {
-      info->saved_regs[S390_A0_REGNUM + i].addr = sigreg_ptr;
-      sigreg_ptr += 4;
-    }
-
-  /* The floating-point control word.  */
-  info->saved_regs[S390_FPC_REGNUM].addr = sigreg_ptr;
-  sigreg_ptr += 8;
-
-  /* And finally the FPRs.  */
-  for (i = 0; i < 16; i++)
-    {
-      info->saved_regs[S390_F0_REGNUM + i].addr = sigreg_ptr;
-      sigreg_ptr += 8;
-    }
-
-  /* If we have them, the GPR upper halves are appended at the end.  */
-  sigreg_ptr += sigreg_high_off;
-  if (tdep->gpr_full_regnum != -1)
-    for (i = 0; i < 16; i++)
-      {
-	info->saved_regs[S390_R0_UPPER_REGNUM + i].addr = sigreg_ptr;
-	sigreg_ptr += 4;
-      }
-
-  /* Restore the previous frame's SP.  */
-  prev_sp = read_memory_unsigned_integer (
-			info->saved_regs[S390_SP_REGNUM].addr,
-			word_size, byte_order);
-
-  /* Determine our frame base.  */
-  info->frame_base = prev_sp + 16*word_size + 32;
-
-  return info;
-}
-
-static void
-s390_sigtramp_frame_this_id (struct frame_info *this_frame,
-			     void **this_prologue_cache,
-			     struct frame_id *this_id)
-{
-  struct s390_sigtramp_unwind_cache *info
-    = s390_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
-  *this_id = frame_id_build (info->frame_base, get_frame_pc (this_frame));
-}
-
-static struct value *
-s390_sigtramp_frame_prev_register (struct frame_info *this_frame,
-				   void **this_prologue_cache, int regnum)
-{
-  struct s390_sigtramp_unwind_cache *info
-    = s390_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
-  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
-}
-
-static int
-s390_sigtramp_frame_sniffer (const struct frame_unwind *self,
-			     struct frame_info *this_frame,
-			     void **this_prologue_cache)
-{
-  CORE_ADDR pc = get_frame_pc (this_frame);
-  bfd_byte sigreturn[2];
-
-  if (target_read_memory (pc, sigreturn, 2))
-    return 0;
-
-  if (sigreturn[0] != op_svc)
-    return 0;
-
-  if (sigreturn[1] != 119 /* sigreturn */
-      && sigreturn[1] != 173 /* rt_sigreturn */)
-    return 0;
-
-  return 1;
-}
-
-static const struct frame_unwind s390_sigtramp_frame_unwind = {
-  SIGTRAMP_FRAME,
-  default_frame_unwind_stop_reason,
-  s390_sigtramp_frame_this_id,
-  s390_sigtramp_frame_prev_register,
-  NULL,
-  s390_sigtramp_frame_sniffer
-};
+/* Syscall handling.  */
 
 /* Retrieve the syscall number at a ptrace syscall-stop.  Return -1
    upon error. */
@@ -2929,622 +560,6 @@ s390_linux_record_signal (struct gdbarch *gdbarch, struct regcache *regcache,
   return 0;
 }
 
-/* Frame base handling.  */
-
-static CORE_ADDR
-s390_frame_base_address (struct frame_info *this_frame, void **this_cache)
-{
-  struct s390_unwind_cache *info
-    = s390_frame_unwind_cache (this_frame, this_cache);
-  return info->frame_base;
-}
-
-static CORE_ADDR
-s390_local_base_address (struct frame_info *this_frame, void **this_cache)
-{
-  struct s390_unwind_cache *info
-    = s390_frame_unwind_cache (this_frame, this_cache);
-  return info->local_base;
-}
-
-static const struct frame_base s390_frame_base = {
-  &s390_frame_unwind,
-  s390_frame_base_address,
-  s390_local_base_address,
-  s390_local_base_address
-};
-
-static CORE_ADDR
-s390_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  ULONGEST pc;
-  pc = frame_unwind_register_unsigned (next_frame, tdep->pc_regnum);
-  return gdbarch_addr_bits_remove (gdbarch, pc);
-}
-
-static CORE_ADDR
-s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
-  ULONGEST sp;
-  sp = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
-  return gdbarch_addr_bits_remove (gdbarch, sp);
-}
-
-
-/* DWARF-2 frame support.  */
-
-static struct value *
-s390_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
-			   int regnum)
-{
-  return s390_unwind_pseudo_register (this_frame, regnum);
-}
-
-static void
-s390_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
-			    struct dwarf2_frame_state_reg *reg,
-			    struct frame_info *this_frame)
-{
-  /* The condition code (and thus PSW mask) is call-clobbered.  */
-  if (regnum == S390_PSWM_REGNUM)
-    reg->how = DWARF2_FRAME_REG_UNDEFINED;
-
-  /* The PSW address unwinds to the return address.  */
-  else if (regnum == S390_PSWA_REGNUM)
-    reg->how = DWARF2_FRAME_REG_RA;
-
-  /* Fixed registers are call-saved or call-clobbered
-     depending on the ABI in use.  */
-  else if (regnum < S390_NUM_REGS)
-    {
-      if (s390_register_call_saved (gdbarch, regnum))
-	reg->how = DWARF2_FRAME_REG_SAME_VALUE;
-      else
-	reg->how = DWARF2_FRAME_REG_UNDEFINED;
-    }
-
-  /* We install a special function to unwind pseudos.  */
-  else
-    {
-      reg->how = DWARF2_FRAME_REG_FN;
-      reg->loc.fn = s390_dwarf2_prev_register;
-    }
-}
-
-
-/* Dummy function calls.  */
-
-/* Unwrap any single-field structs in TYPE and return the effective
-   "inner" type.  E.g., yield "float" for all these cases:
-
-     float x;
-     struct { float x };
-     struct { struct { float x; } x; };
-     struct { struct { struct { float x; } x; } x; };
-
-   However, if an inner type is smaller than MIN_SIZE, abort the
-   unwrapping.  */
-
-static struct type *
-s390_effective_inner_type (struct type *type, unsigned int min_size)
-{
-  while (TYPE_CODE (type) == TYPE_CODE_STRUCT
-	 && TYPE_NFIELDS (type) == 1)
-    {
-      struct type *inner = check_typedef (TYPE_FIELD_TYPE (type, 0));
-
-      if (TYPE_LENGTH (inner) < min_size)
-	break;
-      type = inner;
-    }
-
-  return type;
-}
-
-/* Return non-zero if TYPE should be passed like "float" or
-   "double".  */
-
-static int
-s390_function_arg_float (struct type *type)
-{
-  /* Note that long double as well as complex types are intentionally
-     excluded. */
-  if (TYPE_LENGTH (type) > 8)
-    return 0;
-
-  /* A struct containing just a float or double is passed like a float
-     or double.  */
-  type = s390_effective_inner_type (type, 0);
-
-  return (TYPE_CODE (type) == TYPE_CODE_FLT
-	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT);
-}
-
-/* Return non-zero if TYPE should be passed like a vector.  */
-
-static int
-s390_function_arg_vector (struct type *type)
-{
-  if (TYPE_LENGTH (type) > 16)
-    return 0;
-
-  /* Structs containing just a vector are passed like a vector.  */
-  type = s390_effective_inner_type (type, TYPE_LENGTH (type));
-
-  return TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type);
-}
-
-/* Determine whether N is a power of two.  */
-
-static int
-is_power_of_two (unsigned int n)
-{
-  return n && ((n & (n - 1)) == 0);
-}
-
-/* For an argument whose type is TYPE and which is not passed like a
-   float or vector, return non-zero if it should be passed like "int"
-   or "long long".  */
-
-static int
-s390_function_arg_integer (struct type *type)
-{
-  enum type_code code = TYPE_CODE (type);
-
-  if (TYPE_LENGTH (type) > 8)
-    return 0;
-
-  if (code == TYPE_CODE_INT
-      || code == TYPE_CODE_ENUM
-      || code == TYPE_CODE_RANGE
-      || code == TYPE_CODE_CHAR
-      || code == TYPE_CODE_BOOL
-      || code == TYPE_CODE_PTR
-      || TYPE_IS_REFERENCE (type))
-    return 1;
-
-  return ((code == TYPE_CODE_UNION || code == TYPE_CODE_STRUCT)
-	  && is_power_of_two (TYPE_LENGTH (type)));
-}
-
-/* Argument passing state: Internal data structure passed to helper
-   routines of s390_push_dummy_call.  */
-
-struct s390_arg_state
-  {
-    /* Register cache, or NULL, if we are in "preparation mode".  */
-    struct regcache *regcache;
-    /* Next available general/floating-point/vector register for
-       argument passing.  */
-    int gr, fr, vr;
-    /* Current pointer to copy area (grows downwards).  */
-    CORE_ADDR copy;
-    /* Current pointer to parameter area (grows upwards).  */
-    CORE_ADDR argp;
-  };
-
-/* Prepare one argument ARG for a dummy call and update the argument
-   passing state AS accordingly.  If the regcache field in AS is set,
-   operate in "write mode" and write ARG into the inferior.  Otherwise
-   run "preparation mode" and skip all updates to the inferior.  */
-
-static void
-s390_handle_arg (struct s390_arg_state *as, struct value *arg,
-		 struct gdbarch_tdep *tdep, int word_size,
-		 enum bfd_endian byte_order, int is_unnamed)
-{
-  struct type *type = check_typedef (value_type (arg));
-  unsigned int length = TYPE_LENGTH (type);
-  int write_mode = as->regcache != NULL;
-
-  if (s390_function_arg_float (type))
-    {
-      /* The GNU/Linux for S/390 ABI uses FPRs 0 and 2 to pass
-	 arguments.  The GNU/Linux for zSeries ABI uses 0, 2, 4, and
-	 6.  */
-      if (as->fr <= (tdep->abi == ABI_LINUX_S390 ? 2 : 6))
-	{
-	  /* When we store a single-precision value in an FP register,
-	     it occupies the leftmost bits.  */
-	  if (write_mode)
-	    regcache_cooked_write_part (as->regcache,
-					S390_F0_REGNUM + as->fr,
-					0, length,
-					value_contents (arg));
-	  as->fr += 2;
-	}
-      else
-	{
-	  /* When we store a single-precision value in a stack slot,
-	     it occupies the rightmost bits.  */
-	  as->argp = align_up (as->argp + length, word_size);
-	  if (write_mode)
-	    write_memory (as->argp - length, value_contents (arg),
-			  length);
-	}
-    }
-  else if (tdep->vector_abi == S390_VECTOR_ABI_128
-	   && s390_function_arg_vector (type))
-    {
-      static const char use_vr[] = {24, 26, 28, 30, 25, 27, 29, 31};
-
-      if (!is_unnamed && as->vr < ARRAY_SIZE (use_vr))
-	{
-	  int regnum = S390_V24_REGNUM + use_vr[as->vr] - 24;
-
-	  if (write_mode)
-	    regcache_cooked_write_part (as->regcache, regnum,
-					0, length,
-					value_contents (arg));
-	  as->vr++;
-	}
-      else
-	{
-	  if (write_mode)
-	    write_memory (as->argp, value_contents (arg), length);
-	  as->argp = align_up (as->argp + length, word_size);
-	}
-    }
-  else if (s390_function_arg_integer (type) && length <= word_size)
-    {
-      /* Initialize it just to avoid a GCC false warning.  */
-      ULONGEST val = 0;
-
-      if (write_mode)
-	{
-	  /* Place value in least significant bits of the register or
-	     memory word and sign- or zero-extend to full word size.
-	     This also applies to a struct or union.  */
-	  val = TYPE_UNSIGNED (type)
-	    ? extract_unsigned_integer (value_contents (arg),
-					length, byte_order)
-	    : extract_signed_integer (value_contents (arg),
-				      length, byte_order);
-	}
-
-      if (as->gr <= 6)
-	{
-	  if (write_mode)
-	    regcache_cooked_write_unsigned (as->regcache,
-					    S390_R0_REGNUM + as->gr,
-					    val);
-	  as->gr++;
-	}
-      else
-	{
-	  if (write_mode)
-	    write_memory_unsigned_integer (as->argp, word_size,
-					   byte_order, val);
-	  as->argp += word_size;
-	}
-    }
-  else if (s390_function_arg_integer (type) && length == 8)
-    {
-      if (as->gr <= 5)
-	{
-	  if (write_mode)
-	    {
-	      regcache_cooked_write (as->regcache,
-				     S390_R0_REGNUM + as->gr,
-				     value_contents (arg));
-	      regcache_cooked_write (as->regcache,
-				     S390_R0_REGNUM + as->gr + 1,
-				     value_contents (arg) + word_size);
-	    }
-	  as->gr += 2;
-	}
-      else
-	{
-	  /* If we skipped r6 because we couldn't fit a DOUBLE_ARG
-	     in it, then don't go back and use it again later.  */
-	  as->gr = 7;
-
-	  if (write_mode)
-	    write_memory (as->argp, value_contents (arg), length);
-	  as->argp += length;
-	}
-    }
-  else
-    {
-      /* This argument type is never passed in registers.  Place the
-	 value in the copy area and pass a pointer to it.  Use 8-byte
-	 alignment as a conservative assumption.  */
-      as->copy = align_down (as->copy - length, 8);
-      if (write_mode)
-	write_memory (as->copy, value_contents (arg), length);
-
-      if (as->gr <= 6)
-	{
-	  if (write_mode)
-	    regcache_cooked_write_unsigned (as->regcache,
-					    S390_R0_REGNUM + as->gr,
-					    as->copy);
-	  as->gr++;
-	}
-      else
-	{
-	  if (write_mode)
-	    write_memory_unsigned_integer (as->argp, word_size,
-					   byte_order, as->copy);
-	  as->argp += word_size;
-	}
-    }
-}
-
-/* Put the actual parameter values pointed to by ARGS[0..NARGS-1] in
-   place to be passed to a function, as specified by the "GNU/Linux
-   for S/390 ELF Application Binary Interface Supplement".
-
-   SP is the current stack pointer.  We must put arguments, links,
-   padding, etc. whereever they belong, and return the new stack
-   pointer value.
-
-   If STRUCT_RETURN is non-zero, then the function we're calling is
-   going to return a structure by value; STRUCT_ADDR is the address of
-   a block we've allocated for it on the stack.
-
-   Our caller has taken care of any type promotions needed to satisfy
-   prototypes or the old K&R argument-passing rules.  */
-
-static CORE_ADDR
-s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
-		      struct regcache *regcache, CORE_ADDR bp_addr,
-		      int nargs, struct value **args, CORE_ADDR sp,
-		      int struct_return, CORE_ADDR struct_addr)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  int i;
-  struct s390_arg_state arg_state, arg_prep;
-  CORE_ADDR param_area_start, new_sp;
-  struct type *ftype = check_typedef (value_type (function));
-
-  if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
-    ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
-
-  arg_prep.copy = sp;
-  arg_prep.gr = struct_return ? 3 : 2;
-  arg_prep.fr = 0;
-  arg_prep.vr = 0;
-  arg_prep.argp = 0;
-  arg_prep.regcache = NULL;
-
-  /* Initialize arg_state for "preparation mode".  */
-  arg_state = arg_prep;
-
-  /* Update arg_state.copy with the start of the reference-to-copy area
-     and arg_state.argp with the size of the parameter area.  */
-  for (i = 0; i < nargs; i++)
-    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
-		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
-
-  param_area_start = align_down (arg_state.copy - arg_state.argp, 8);
-
-  /* Allocate the standard frame areas: the register save area, the
-     word reserved for the compiler, and the back chain pointer.  */
-  new_sp = param_area_start - (16 * word_size + 32);
-
-  /* Now we have the final stack pointer.  Make sure we didn't
-     underflow; on 31-bit, this would result in addresses with the
-     high bit set, which causes confusion elsewhere.  Note that if we
-     error out here, stack and registers remain untouched.  */
-  if (gdbarch_addr_bits_remove (gdbarch, new_sp) != new_sp)
-    error (_("Stack overflow"));
-
-  /* Pass the structure return address in general register 2.  */
-  if (struct_return)
-    regcache_cooked_write_unsigned (regcache, S390_R2_REGNUM, struct_addr);
-
-  /* Initialize arg_state for "write mode".  */
-  arg_state = arg_prep;
-  arg_state.argp = param_area_start;
-  arg_state.regcache = regcache;
-
-  /* Write all parameters.  */
-  for (i = 0; i < nargs; i++)
-    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
-		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
-
-  /* Store return PSWA.  In 31-bit mode, keep addressing mode bit.  */
-  if (word_size == 4)
-    {
-      ULONGEST pswa;
-      regcache_cooked_read_unsigned (regcache, S390_PSWA_REGNUM, &pswa);
-      bp_addr = (bp_addr & 0x7fffffff) | (pswa & 0x80000000);
-    }
-  regcache_cooked_write_unsigned (regcache, S390_RETADDR_REGNUM, bp_addr);
-
-  /* Store updated stack pointer.  */
-  regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, new_sp);
-
-  /* We need to return the 'stack part' of the frame ID,
-     which is actually the top of the register save area.  */
-  return param_area_start;
-}
-
-/* Assuming THIS_FRAME is a dummy, return the frame ID of that
-   dummy frame.  The frame ID's base needs to match the TOS value
-   returned by push_dummy_call, and the PC match the dummy frame's
-   breakpoint.  */
-static struct frame_id
-s390_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
-{
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  CORE_ADDR sp = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
-  sp = gdbarch_addr_bits_remove (gdbarch, sp);
-
-  return frame_id_build (sp + 16*word_size + 32,
-			 get_frame_pc (this_frame));
-}
-
-static CORE_ADDR
-s390_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
-{
-  /* Both the 32- and 64-bit ABI's say that the stack pointer should
-     always be aligned on an eight-byte boundary.  */
-  return (addr & -8);
-}
-
-
-/* Helper for s390_return_value: Set or retrieve a function return
-   value if it resides in a register.  */
-
-static void
-s390_register_return_value (struct gdbarch *gdbarch, struct type *type,
-			    struct regcache *regcache,
-			    gdb_byte *out, const gdb_byte *in)
-{
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
-  int length = TYPE_LENGTH (type);
-  int code = TYPE_CODE (type);
-
-  if (code == TYPE_CODE_FLT || code == TYPE_CODE_DECFLOAT)
-    {
-      /* Float-like value: left-aligned in f0.  */
-      if (in != NULL)
-	regcache_cooked_write_part (regcache, S390_F0_REGNUM,
-				    0, length, in);
-      else
-	regcache_cooked_read_part (regcache, S390_F0_REGNUM,
-				   0, length, out);
-    }
-  else if (code == TYPE_CODE_ARRAY)
-    {
-      /* Vector: left-aligned in v24.  */
-      if (in != NULL)
-	regcache_cooked_write_part (regcache, S390_V24_REGNUM,
-				    0, length, in);
-      else
-	regcache_cooked_read_part (regcache, S390_V24_REGNUM,
-				   0, length, out);
-    }
-  else if (length <= word_size)
-    {
-      /* Integer: zero- or sign-extended in r2.  */
-      if (out != NULL)
-	regcache_cooked_read_part (regcache, S390_R2_REGNUM,
-				   word_size - length, length, out);
-      else if (TYPE_UNSIGNED (type))
-	regcache_cooked_write_unsigned
-	  (regcache, S390_R2_REGNUM,
-	   extract_unsigned_integer (in, length, byte_order));
-      else
-	regcache_cooked_write_signed
-	  (regcache, S390_R2_REGNUM,
-	   extract_signed_integer (in, length, byte_order));
-    }
-  else if (length == 2 * word_size)
-    {
-      /* Double word: in r2 and r3.  */
-      if (in != NULL)
-	{
-	  regcache_cooked_write (regcache, S390_R2_REGNUM, in);
-	  regcache_cooked_write (regcache, S390_R3_REGNUM,
-				 in + word_size);
-	}
-      else
-	{
-	  regcache_cooked_read (regcache, S390_R2_REGNUM, out);
-	  regcache_cooked_read (regcache, S390_R3_REGNUM,
-				out + word_size);
-	}
-    }
-  else
-    internal_error (__FILE__, __LINE__, _("invalid return type"));
-}
-
-
-/* Implement the 'return_value' gdbarch method.  */
-
-static enum return_value_convention
-s390_return_value (struct gdbarch *gdbarch, struct value *function,
-		   struct type *type, struct regcache *regcache,
-		   gdb_byte *out, const gdb_byte *in)
-{
-  enum return_value_convention rvc;
-
-  type = check_typedef (type);
-
-  switch (TYPE_CODE (type))
-    {
-    case TYPE_CODE_STRUCT:
-    case TYPE_CODE_UNION:
-    case TYPE_CODE_COMPLEX:
-      rvc = RETURN_VALUE_STRUCT_CONVENTION;
-      break;
-    case TYPE_CODE_ARRAY:
-      rvc = (gdbarch_tdep (gdbarch)->vector_abi == S390_VECTOR_ABI_128
-	     && TYPE_LENGTH (type) <= 16 && TYPE_VECTOR (type))
-	? RETURN_VALUE_REGISTER_CONVENTION
-	: RETURN_VALUE_STRUCT_CONVENTION;
-      break;
-    default:
-      rvc = TYPE_LENGTH (type) <= 8
-	? RETURN_VALUE_REGISTER_CONVENTION
-	: RETURN_VALUE_STRUCT_CONVENTION;
-    }
-
-  if (in != NULL || out != NULL)
-    {
-      if (rvc == RETURN_VALUE_REGISTER_CONVENTION)
-	s390_register_return_value (gdbarch, type, regcache, out, in);
-      else if (in != NULL)
-	error (_("Cannot set function return value."));
-      else
-	error (_("Function return value unknown."));
-    }
-
-  return rvc;
-}
-
-
-/* Breakpoints.  */
-constexpr gdb_byte s390_break_insn[] = { 0x0, 0x1 };
-
-typedef BP_MANIPULATION (s390_break_insn) s390_breakpoint;
-
-/* Address handling.  */
-
-static CORE_ADDR
-s390_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
-{
-  return addr & 0x7fffffff;
-}
-
-static int
-s390_address_class_type_flags (int byte_size, int dwarf2_addr_class)
-{
-  if (byte_size == 4)
-    return TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
-  else
-    return 0;
-}
-
-static const char *
-s390_address_class_type_flags_to_name (struct gdbarch *gdbarch, int type_flags)
-{
-  if (type_flags & TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1)
-    return "mode32";
-  else
-    return NULL;
-}
-
-static int
-s390_address_class_name_to_type_flags (struct gdbarch *gdbarch,
-				       const char *name,
-				       int *type_flags_ptr)
-{
-  if (strcmp (name, "mode32") == 0)
-    {
-      *type_flags_ptr = TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
-      return 1;
-    }
-  else
-    return 0;
-}
 
 /* Implement gdbarch_gcc_target_options.  GCC does not know "-m32" or
    "-mcmodel=large".  */
@@ -3578,6 +593,7 @@ s390_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
 	  || isdigit (*s)); /* Literal number.  */
 }
 
+
 /* Process record and replay helpers.  */
 
 /* Takes the intermediate sum of address calculations and masks off upper
@@ -7679,361 +4695,642 @@ s390_init_linux_record_tdep (struct linux_record_tdep *record_tdep,
   record_tdep->ioctl_FIOQSIZE = 0x545e;
 }
 
-/* Set up gdbarch struct.  */
 
-static struct gdbarch *
-s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
-{
-  const struct target_desc *tdesc = info.target_desc;
-  struct tdesc_arch_data *tdesc_data = NULL;
-  struct gdbarch *gdbarch;
-  struct gdbarch_tdep *tdep;
-  enum s390_abi_kind tdep_abi;
-  enum s390_vector_abi_kind vector_abi;
-  int have_upper = 0;
-  int have_linux_v1 = 0;
-  int have_linux_v2 = 0;
-  int have_tdb = 0;
-  int have_vx = 0;
-  int first_pseudo_reg, last_pseudo_reg;
-  static const char *const stap_register_prefixes[] = { "%", NULL };
-  static const char *const stap_register_indirection_prefixes[] = { "(",
-								    NULL };
-  static const char *const stap_register_indirection_suffixes[] = { ")",
-								    NULL };
-
-  /* Default ABI and register size.  */
-  switch (info.bfd_arch_info->mach)
-    {
-    case bfd_mach_s390_31:
-      tdep_abi = ABI_LINUX_S390;
-      break;
+/* Frame handling.  */
+/* Normal stack frames.  */
 
-    case bfd_mach_s390_64:
-      tdep_abi = ABI_LINUX_ZSERIES;
-      break;
+struct s390_unwind_cache {
 
-    default:
-      return NULL;
-    }
+  CORE_ADDR func;
+  CORE_ADDR frame_base;
+  CORE_ADDR local_base;
 
-  /* Use default target description if none provided by the target.  */
-  if (!tdesc_has_registers (tdesc))
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static int
+s390_prologue_frame_unwind_cache (struct frame_info *this_frame,
+				  struct s390_unwind_cache *info)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  struct s390_prologue_data data;
+  pv_t *fp = &data.gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
+  pv_t *sp = &data.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
+  int i;
+  CORE_ADDR cfa;
+  CORE_ADDR func;
+  CORE_ADDR result;
+  ULONGEST reg;
+  CORE_ADDR prev_sp;
+  int frame_pointer;
+  int size;
+  struct frame_info *next_frame;
+
+  /* Try to find the function start address.  If we can't find it, we don't
+     bother searching for it -- with modern compilers this would be mostly
+     pointless anyway.  Trust that we'll either have valid DWARF-2 CFI data
+     or else a valid backchain ...  */
+  if (!get_frame_func_if_available (this_frame, &info->func))
     {
-      if (tdep_abi == ABI_LINUX_S390)
-	tdesc = tdesc_s390_linux32;
-      else
-	tdesc = tdesc_s390x_linux64;
+      info->func = -1;
+      return 0;
     }
+  func = info->func;
+
+  /* Try to analyze the prologue.  */
+  result = s390_analyze_prologue (gdbarch, func,
+				  get_frame_pc (this_frame), &data);
+  if (!result)
+    return 0;
+
+  /* If this was successful, we should have found the instruction that
+     sets the stack pointer register to the previous value of the stack
+     pointer minus the frame size.  */
+  if (!pv_is_register (*sp, S390_SP_REGNUM))
+    return 0;
 
-  /* Check any target description for validity.  */
-  if (tdesc_has_registers (tdesc))
+  /* A frame size of zero at this point can mean either a real
+     frameless function, or else a failure to find the prologue.
+     Perform some sanity checks to verify we really have a
+     frameless function.  */
+  if (sp->k == 0)
     {
-      static const char *const gprs[] = {
-	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
-	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
-      };
-      static const char *const fprs[] = {
-	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
-	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15"
-      };
-      static const char *const acrs[] = {
-	"acr0", "acr1", "acr2", "acr3", "acr4", "acr5", "acr6", "acr7",
-	"acr8", "acr9", "acr10", "acr11", "acr12", "acr13", "acr14", "acr15"
-      };
-      static const char *const gprs_lower[] = {
-	"r0l", "r1l", "r2l", "r3l", "r4l", "r5l", "r6l", "r7l",
-	"r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
-      };
-      static const char *const gprs_upper[] = {
-	"r0h", "r1h", "r2h", "r3h", "r4h", "r5h", "r6h", "r7h",
-	"r8h", "r9h", "r10h", "r11h", "r12h", "r13h", "r14h", "r15h"
-      };
-      static const char *const tdb_regs[] = {
-	"tdb0", "tac", "tct", "atia",
-	"tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7",
-	"tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15"
-      };
-      static const char *const vxrs_low[] = {
-	"v0l", "v1l", "v2l", "v3l", "v4l", "v5l", "v6l", "v7l", "v8l",
-	"v9l", "v10l", "v11l", "v12l", "v13l", "v14l", "v15l",
-      };
-      static const char *const vxrs_high[] = {
-	"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24",
-	"v25", "v26", "v27", "v28", "v29", "v30", "v31",
-      };
-      const struct tdesc_feature *feature;
-      int i, valid_p = 1;
-
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.core");
-      if (feature == NULL)
-	return NULL;
-
-      tdesc_data = tdesc_data_alloc ();
-
-      valid_p &= tdesc_numbered_register (feature, tdesc_data,
-					  S390_PSWM_REGNUM, "pswm");
-      valid_p &= tdesc_numbered_register (feature, tdesc_data,
-					  S390_PSWA_REGNUM, "pswa");
-
-      if (tdesc_unnumbered_register (feature, "r0"))
-	{
-	  for (i = 0; i < 16; i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_R0_REGNUM + i, gprs[i]);
-	}
-      else
-	{
-	  have_upper = 1;
-
-	  for (i = 0; i < 16; i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_R0_REGNUM + i,
-						gprs_lower[i]);
-	  for (i = 0; i < 16; i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_R0_UPPER_REGNUM + i,
-						gprs_upper[i]);
-	}
+      /* If the next frame is a NORMAL_FRAME, this frame *cannot* have frame
+	 size zero.  This is only possible if the next frame is a sentinel
+	 frame, a dummy frame, or a signal trampoline frame.  */
+      /* FIXME: cagney/2004-05-01: This sanity check shouldn't be
+	 needed, instead the code should simpliy rely on its
+	 analysis.  */
+      next_frame = get_next_frame (this_frame);
+      while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
+	next_frame = get_next_frame (next_frame);
+      if (next_frame
+	  && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME)
+	return 0;
 
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.fpr");
-      if (feature == NULL)
+      /* If we really have a frameless function, %r14 must be valid
+	 -- in particular, it must point to a different function.  */
+      reg = get_frame_register_unsigned (this_frame, S390_RETADDR_REGNUM);
+      reg = gdbarch_addr_bits_remove (gdbarch, reg) - 1;
+      if (get_pc_function_start (reg) == func)
 	{
-	  tdesc_data_cleanup (tdesc_data);
-	  return NULL;
+	  /* However, there is one case where it *is* valid for %r14
+	     to point to the same function -- if this is a recursive
+	     call, and we have stopped in the prologue *before* the
+	     stack frame was allocated.
+
+	     Recognize this case by looking ahead a bit ...  */
+
+	  struct s390_prologue_data data2;
+	  pv_t *sp = &data2.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
+
+	  if (!(s390_analyze_prologue (gdbarch, func, (CORE_ADDR)-1, &data2)
+		&& pv_is_register (*sp, S390_SP_REGNUM)
+		&& sp->k != 0))
+	    return 0;
 	}
+    }
+
 
-      valid_p &= tdesc_numbered_register (feature, tdesc_data,
-					  S390_FPC_REGNUM, "fpc");
-      for (i = 0; i < 16; i++)
-	valid_p &= tdesc_numbered_register (feature, tdesc_data,
-					    S390_F0_REGNUM + i, fprs[i]);
+  /* OK, we've found valid prologue data.  */
+  size = -sp->k;
 
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.acr");
-      if (feature == NULL)
+  /* If the frame pointer originally also holds the same value
+     as the stack pointer, we're probably using it.  If it holds
+     some other value -- even a constant offset -- it is most
+     likely used as temp register.  */
+  if (pv_is_identical (*sp, *fp))
+    frame_pointer = S390_FRAME_REGNUM;
+  else
+    frame_pointer = S390_SP_REGNUM;
+
+  /* If we've detected a function with stack frame, we'll still have to
+     treat it as frameless if we're currently within the function epilog
+     code at a point where the frame pointer has already been restored.
+     This can only happen in an innermost frame.  */
+  /* FIXME: cagney/2004-05-01: This sanity check shouldn't be needed,
+     instead the code should simpliy rely on its analysis.  */
+  next_frame = get_next_frame (this_frame);
+  while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
+    next_frame = get_next_frame (next_frame);
+  if (size > 0
+      && (next_frame == NULL
+	  || get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME))
+    {
+      /* See the comment in s390_stack_frame_destroyed_p on why this is
+	 not completely reliable ...  */
+      if (s390_stack_frame_destroyed_p (gdbarch, get_frame_pc (this_frame)))
 	{
-	  tdesc_data_cleanup (tdesc_data);
-	  return NULL;
+	  memset (&data, 0, sizeof (data));
+	  size = 0;
+	  frame_pointer = S390_SP_REGNUM;
 	}
+    }
 
-      for (i = 0; i < 16; i++)
-	valid_p &= tdesc_numbered_register (feature, tdesc_data,
-					    S390_A0_REGNUM + i, acrs[i]);
+  /* Once we know the frame register and the frame size, we can unwind
+     the current value of the frame register from the next frame, and
+     add back the frame size to arrive that the previous frame's
+     stack pointer value.  */
+  prev_sp = get_frame_register_unsigned (this_frame, frame_pointer) + size;
+  cfa = prev_sp + 16*word_size + 32;
 
-      /* Optional GNU/Linux-specific "registers".  */
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.linux");
-      if (feature)
-	{
-	  tdesc_numbered_register (feature, tdesc_data,
-				   S390_ORIG_R2_REGNUM, "orig_r2");
+  /* Set up ABI call-saved/call-clobbered registers.  */
+  for (i = 0; i < S390_NUM_REGS; i++)
+    if (!s390_register_call_saved (gdbarch, i))
+      trad_frame_set_unknown (info->saved_regs, i);
 
-	  if (tdesc_numbered_register (feature, tdesc_data,
-				       S390_LAST_BREAK_REGNUM, "last_break"))
-	    have_linux_v1 = 1;
+  /* CC is always call-clobbered.  */
+  trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
 
-	  if (tdesc_numbered_register (feature, tdesc_data,
-				       S390_SYSTEM_CALL_REGNUM, "system_call"))
-	    have_linux_v2 = 1;
+  /* Record the addresses of all register spill slots the prologue parser
+     has recognized.  Consider only registers defined as call-saved by the
+     ABI; for call-clobbered registers the parser may have recognized
+     spurious stores.  */
 
-	  if (have_linux_v2 > have_linux_v1)
-	    valid_p = 0;
-	}
+  for (i = 0; i < 16; i++)
+    if (s390_register_call_saved (gdbarch, S390_R0_REGNUM + i)
+	&& data.gpr_slot[i] != 0)
+      info->saved_regs[S390_R0_REGNUM + i].addr = cfa - data.gpr_slot[i];
 
-      /* Transaction diagnostic block.  */
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.tdb");
-      if (feature)
-	{
-	  for (i = 0; i < ARRAY_SIZE (tdb_regs); i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_TDB_DWORD0_REGNUM + i,
-						tdb_regs[i]);
-	  have_tdb = 1;
-	}
+  for (i = 0; i < 16; i++)
+    if (s390_register_call_saved (gdbarch, S390_F0_REGNUM + i)
+	&& data.fpr_slot[i] != 0)
+      info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
 
-      /* Vector registers.  */
-      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.vx");
-      if (feature)
-	{
-	  for (i = 0; i < 16; i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_V0_LOWER_REGNUM + i,
-						vxrs_low[i]);
-	  for (i = 0; i < 16; i++)
-	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
-						S390_V16_REGNUM + i,
-						vxrs_high[i]);
-	  have_vx = 1;
-	}
+  /* Function return will set PC to %r14.  */
+  info->saved_regs[S390_PSWA_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
 
-      if (!valid_p)
-	{
-	  tdesc_data_cleanup (tdesc_data);
-	  return NULL;
-	}
+  /* In frameless functions, we unwind simply by moving the return
+     address to the PC.  However, if we actually stored to the
+     save area, use that -- we might only think the function frameless
+     because we're in the middle of the prologue ...  */
+  if (size == 0
+      && !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
+    {
+      info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
     }
 
-  /* Determine vector ABI.  */
-  vector_abi = S390_VECTOR_ABI_NONE;
-#ifdef HAVE_ELF
-  if (have_vx
-      && info.abfd != NULL
-      && info.abfd->format == bfd_object
-      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour
-      && bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
-				   Tag_GNU_S390_ABI_Vector) == 2)
-    vector_abi = S390_VECTOR_ABI_128;
-#endif
-
-  /* Find a candidate among extant architectures.  */
-  for (arches = gdbarch_list_lookup_by_info (arches, &info);
-       arches != NULL;
-       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+  /* Another sanity check: unless this is a frameless function,
+     we should have found spill slots for SP and PC.
+     If not, we cannot unwind further -- this happens e.g. in
+     libc's thread_start routine.  */
+  if (size > 0)
     {
-      tdep = gdbarch_tdep (arches->gdbarch);
-      if (!tdep)
-	continue;
-      if (tdep->abi != tdep_abi)
-	continue;
-      if (tdep->vector_abi != vector_abi)
-	continue;
-      if ((tdep->gpr_full_regnum != -1) != have_upper)
-	continue;
-      if (tdesc_data != NULL)
-	tdesc_data_cleanup (tdesc_data);
-      return arches->gdbarch;
+      if (!trad_frame_addr_p (info->saved_regs, S390_SP_REGNUM)
+	  || !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
+	prev_sp = -1;
     }
 
-  /* Otherwise create a new gdbarch for the specified machine type.  */
-  tdep = XCNEW (struct gdbarch_tdep);
-  tdep->abi = tdep_abi;
-  tdep->vector_abi = vector_abi;
-  tdep->have_linux_v1 = have_linux_v1;
-  tdep->have_linux_v2 = have_linux_v2;
-  tdep->have_tdb = have_tdb;
-  gdbarch = gdbarch_alloc (&info, tdep);
-
-  set_gdbarch_believe_pcc_promotion (gdbarch, 0);
-  set_gdbarch_char_signed (gdbarch, 0);
-
-  /* S/390 GNU/Linux uses either 64-bit or 128-bit long doubles.
-     We can safely let them default to 128-bit, since the debug info
-     will give the size of type actually used in each case.  */
-  set_gdbarch_long_double_bit (gdbarch, 128);
-  set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
-
-  /* Amount PC must be decremented by after a breakpoint.  This is
-     often the number of bytes returned by gdbarch_breakpoint_from_pc but not
-     always.  */
-  set_gdbarch_decr_pc_after_break (gdbarch, 2);
-  /* Stack grows downward.  */
-  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
-  set_gdbarch_breakpoint_kind_from_pc (gdbarch, s390_breakpoint::kind_from_pc);
-  set_gdbarch_sw_breakpoint_from_kind (gdbarch, s390_breakpoint::bp_from_kind);
-  set_gdbarch_software_single_step (gdbarch, s390_software_single_step);
-  set_gdbarch_displaced_step_hw_singlestep (gdbarch, s390_displaced_step_hw_singlestep);
-  set_gdbarch_skip_prologue (gdbarch, s390_skip_prologue);
-  set_gdbarch_stack_frame_destroyed_p (gdbarch, s390_stack_frame_destroyed_p);
-
-  set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
-  set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
-  set_gdbarch_fp0_regnum (gdbarch, S390_F0_REGNUM);
-  set_gdbarch_stab_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
-  set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
-  set_gdbarch_value_from_register (gdbarch, s390_value_from_register);
-  set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
-  set_gdbarch_iterate_over_regset_sections (gdbarch,
-					    s390_iterate_over_regset_sections);
-  set_gdbarch_cannot_store_register (gdbarch, s390_cannot_store_register);
-  set_gdbarch_write_pc (gdbarch, s390_write_pc);
-  set_gdbarch_guess_tracepoint_registers (gdbarch, s390_guess_tracepoint_registers);
-  set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
-  set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
-  set_tdesc_pseudo_register_type (gdbarch, s390_pseudo_register_type);
-  set_tdesc_pseudo_register_reggroup_p (gdbarch,
-					s390_pseudo_register_reggroup_p);
-  set_gdbarch_ax_pseudo_register_collect (gdbarch,
-					  s390_ax_pseudo_register_collect);
-  set_gdbarch_ax_pseudo_register_push_stack
-      (gdbarch, s390_ax_pseudo_register_push_stack);
-  set_gdbarch_gen_return_address (gdbarch, s390_gen_return_address);
-  tdesc_use_registers (gdbarch, tdesc, tdesc_data);
-  set_gdbarch_register_name (gdbarch, s390_register_name);
-
-  /* Assign pseudo register numbers.  */
-  first_pseudo_reg = gdbarch_num_regs (gdbarch);
-  last_pseudo_reg = first_pseudo_reg;
-  tdep->gpr_full_regnum = -1;
-  if (have_upper)
+  /* We use the current value of the frame register as local_base,
+     and the top of the register save area as frame_base.  */
+  if (prev_sp != -1)
     {
-      tdep->gpr_full_regnum = last_pseudo_reg;
-      last_pseudo_reg += 16;
+      info->frame_base = prev_sp + 16*word_size + 32;
+      info->local_base = prev_sp - size;
     }
-  tdep->v0_full_regnum = -1;
-  if (have_vx)
+
+  return 1;
+}
+
+
+/* Code stubs and their stack frames.  For things like PLTs and NULL
+   function calls (where there is no true frame and the return address
+   is in the RETADDR register).  */
+
+struct s390_stub_unwind_cache
+{
+  CORE_ADDR frame_base;
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct s390_stub_unwind_cache *
+s390_stub_frame_unwind_cache (struct frame_info *this_frame,
+			      void **this_prologue_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  struct s390_stub_unwind_cache *info;
+  ULONGEST reg;
+
+  if (*this_prologue_cache)
+    return (struct s390_stub_unwind_cache *) *this_prologue_cache;
+
+  info = FRAME_OBSTACK_ZALLOC (struct s390_stub_unwind_cache);
+  *this_prologue_cache = info;
+  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+
+  /* The return address is in register %r14.  */
+  info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
+
+  /* Retrieve stack pointer and determine our frame base.  */
+  reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
+  info->frame_base = reg + 16*word_size + 32;
+
+  return info;
+}
+
+static void
+s390_stub_frame_this_id (struct frame_info *this_frame,
+			 void **this_prologue_cache,
+			 struct frame_id *this_id)
+{
+  struct s390_stub_unwind_cache *info
+    = s390_stub_frame_unwind_cache (this_frame, this_prologue_cache);
+  *this_id = frame_id_build (info->frame_base, get_frame_pc (this_frame));
+}
+
+static struct value *
+s390_trad_frame_prev_register (struct frame_info *this_frame,
+			       struct trad_frame_saved_reg saved_regs[],
+			       int regnum)
+{
+  if (regnum < S390_NUM_REGS)
+    return trad_frame_get_prev_register (this_frame, saved_regs, regnum);
+  else
+    return s390_unwind_pseudo_register (this_frame, regnum);
+}
+
+static struct value *
+s390_stub_frame_prev_register (struct frame_info *this_frame,
+			       void **this_prologue_cache, int regnum)
+{
+  struct s390_stub_unwind_cache *info
+    = s390_stub_frame_unwind_cache (this_frame, this_prologue_cache);
+  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
+}
+
+static int
+s390_stub_frame_sniffer (const struct frame_unwind *self,
+			 struct frame_info *this_frame,
+			 void **this_prologue_cache)
+{
+  CORE_ADDR addr_in_block;
+  bfd_byte insn[S390_MAX_INSTR_SIZE];
+
+  /* If the current PC points to non-readable memory, we assume we
+     have trapped due to an invalid function pointer call.  We handle
+     the non-existing current function like a PLT stub.  */
+  addr_in_block = get_frame_address_in_block (this_frame);
+  if (in_plt_section (addr_in_block)
+      || s390_readinstruction (insn, get_frame_pc (this_frame)) < 0)
+    return 1;
+  return 0;
+}
+
+static const struct frame_unwind s390_stub_frame_unwind = {
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_stub_frame_this_id,
+  s390_stub_frame_prev_register,
+  NULL,
+  s390_stub_frame_sniffer
+};
+
+/* Frame unwind.  */
+
+static void
+s390_backchain_frame_unwind_cache (struct frame_info *this_frame,
+				   struct s390_unwind_cache *info)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR backchain;
+  ULONGEST reg;
+  LONGEST sp, tmp;
+  int i;
+
+  /* Set up ABI call-saved/call-clobbered registers.  */
+  for (i = 0; i < S390_NUM_REGS; i++)
+    if (!s390_register_call_saved (gdbarch, i))
+      trad_frame_set_unknown (info->saved_regs, i);
+
+  /* CC is always call-clobbered.  */
+  trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
+
+  /* Get the backchain.  */
+  reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
+  if (!safe_read_memory_integer (reg, word_size, byte_order, &tmp))
+    tmp = 0;
+  backchain = (CORE_ADDR) tmp;
+
+  /* A zero backchain terminates the frame chain.  As additional
+     sanity check, let's verify that the spill slot for SP in the
+     save area pointed to by the backchain in fact links back to
+     the save area.  */
+  if (backchain != 0
+      && safe_read_memory_integer (backchain + 15*word_size,
+				   word_size, byte_order, &sp)
+      && (CORE_ADDR)sp == backchain)
     {
-      tdep->v0_full_regnum = last_pseudo_reg;
-      last_pseudo_reg += 16;
+      /* We don't know which registers were saved, but it will have
+	 to be at least %r14 and %r15.  This will allow us to continue
+	 unwinding, but other prev-frame registers may be incorrect ...  */
+      info->saved_regs[S390_SP_REGNUM].addr = backchain + 15*word_size;
+      info->saved_regs[S390_RETADDR_REGNUM].addr = backchain + 14*word_size;
+
+      /* Function return will set PC to %r14.  */
+      info->saved_regs[S390_PSWA_REGNUM]
+	= info->saved_regs[S390_RETADDR_REGNUM];
+
+      /* We use the current value of the frame register as local_base,
+	 and the top of the register save area as frame_base.  */
+      info->frame_base = backchain + 16*word_size + 32;
+      info->local_base = reg;
     }
-  tdep->pc_regnum = last_pseudo_reg++;
-  tdep->cc_regnum = last_pseudo_reg++;
-  set_gdbarch_pc_regnum (gdbarch, tdep->pc_regnum);
-  set_gdbarch_num_pseudo_regs (gdbarch, last_pseudo_reg - first_pseudo_reg);
 
-  /* Inferior function calls.  */
-  set_gdbarch_push_dummy_call (gdbarch, s390_push_dummy_call);
-  set_gdbarch_dummy_id (gdbarch, s390_dummy_id);
-  set_gdbarch_frame_align (gdbarch, s390_frame_align);
-  set_gdbarch_return_value (gdbarch, s390_return_value);
+  info->func = get_frame_pc (this_frame);
+}
+
+static struct s390_unwind_cache *
+s390_frame_unwind_cache (struct frame_info *this_frame,
+			 void **this_prologue_cache)
+{
+  struct s390_unwind_cache *info;
 
-  /* Syscall handling.  */
-  set_gdbarch_get_syscall_number (gdbarch, s390_linux_get_syscall_number);
+  if (*this_prologue_cache)
+    return (struct s390_unwind_cache *) *this_prologue_cache;
+
+  info = FRAME_OBSTACK_ZALLOC (struct s390_unwind_cache);
+  *this_prologue_cache = info;
+  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+  info->func = -1;
+  info->frame_base = -1;
+  info->local_base = -1;
+
+  TRY
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+      /* Try to use prologue analysis to fill the unwind cache.
+	 If this fails, fall back to reading the stack backchain.  */
+      if (!s390_prologue_frame_unwind_cache (this_frame, info))
+	s390_backchain_frame_unwind_cache (this_frame, info);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      if (ex.error != NOT_AVAILABLE_ERROR)
+	throw_exception (ex);
+    }
+  END_CATCH
+
+  return info;
+}
+
+static void
+s390_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache,
+		    struct frame_id *this_id)
+{
+  struct s390_unwind_cache *info
+    = s390_frame_unwind_cache (this_frame, this_prologue_cache);
+
+  if (info->frame_base == -1)
+    {
+      if (info->func != -1)
+	*this_id = frame_id_build_unavailable_stack (info->func);
+      return;
+    }
+
+  *this_id = frame_id_build (info->frame_base, info->func);
+}
+
+static struct value *
+s390_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct s390_unwind_cache *info
+    = s390_frame_unwind_cache (this_frame, this_prologue_cache);
+
+  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
+}
+
+static const struct frame_unwind s390_frame_unwind = {
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_frame_this_id,
+  s390_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+
+/* Signal trampoline stack frames.  */
+
+struct s390_sigtramp_unwind_cache {
+  CORE_ADDR frame_base;
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct s390_sigtramp_unwind_cache *
+s390_sigtramp_frame_unwind_cache (struct frame_info *this_frame,
+				  void **this_prologue_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct s390_sigtramp_unwind_cache *info;
+  ULONGEST this_sp, prev_sp;
+  CORE_ADDR next_ra, next_cfa, sigreg_ptr, sigreg_high_off;
+  int i;
+
+  if (*this_prologue_cache)
+    return (struct s390_sigtramp_unwind_cache *) *this_prologue_cache;
+
+  info = FRAME_OBSTACK_ZALLOC (struct s390_sigtramp_unwind_cache);
+  *this_prologue_cache = info;
+  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+
+  this_sp = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
+  next_ra = get_frame_pc (this_frame);
+  next_cfa = this_sp + 16*word_size + 32;
+
+  /* New-style RT frame:
+	retcode + alignment (8 bytes)
+	siginfo (128 bytes)
+	ucontext (contains sigregs at offset 5 words).  */
+  if (next_ra == next_cfa)
+    {
+      sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8);
+      /* sigregs are followed by uc_sigmask (8 bytes), then by the
+	 upper GPR halves if present.  */
+      sigreg_high_off = 8;
+    }
+
+  /* Old-style RT frame and all non-RT frames:
+	old signal mask (8 bytes)
+	pointer to sigregs.  */
+  else
+    {
+      sigreg_ptr = read_memory_unsigned_integer (next_cfa + 8,
+						 word_size, byte_order);
+      /* sigregs are followed by signo (4 bytes), then by the
+	 upper GPR halves if present.  */
+      sigreg_high_off = 4;
+    }
+
+  /* The sigregs structure looks like this:
+	    long   psw_mask;
+	    long   psw_addr;
+	    long   gprs[16];
+	    int    acrs[16];
+	    int    fpc;
+	    int    __pad;
+	    double fprs[16];  */
+
+  /* PSW mask and address.  */
+  info->saved_regs[S390_PSWM_REGNUM].addr = sigreg_ptr;
+  sigreg_ptr += word_size;
+  info->saved_regs[S390_PSWA_REGNUM].addr = sigreg_ptr;
+  sigreg_ptr += word_size;
+
+  /* Then the GPRs.  */
+  for (i = 0; i < 16; i++)
+    {
+      info->saved_regs[S390_R0_REGNUM + i].addr = sigreg_ptr;
+      sigreg_ptr += word_size;
+    }
+
+  /* Then the ACRs.  */
+  for (i = 0; i < 16; i++)
+    {
+      info->saved_regs[S390_A0_REGNUM + i].addr = sigreg_ptr;
+      sigreg_ptr += 4;
+    }
+
+  /* The floating-point control word.  */
+  info->saved_regs[S390_FPC_REGNUM].addr = sigreg_ptr;
+  sigreg_ptr += 8;
+
+  /* And finally the FPRs.  */
+  for (i = 0; i < 16; i++)
+    {
+      info->saved_regs[S390_F0_REGNUM + i].addr = sigreg_ptr;
+      sigreg_ptr += 8;
+    }
+
+  /* If we have them, the GPR upper halves are appended at the end.  */
+  sigreg_ptr += sigreg_high_off;
+  if (tdep->gpr_full_regnum != -1)
+    for (i = 0; i < 16; i++)
+      {
+	info->saved_regs[S390_R0_UPPER_REGNUM + i].addr = sigreg_ptr;
+	sigreg_ptr += 4;
+      }
+
+  /* Restore the previous frame's SP.  */
+  prev_sp = read_memory_unsigned_integer (
+			info->saved_regs[S390_SP_REGNUM].addr,
+			word_size, byte_order);
+
+  /* Determine our frame base.  */
+  info->frame_base = prev_sp + 16*word_size + 32;
+
+  return info;
+}
+
+static void
+s390_sigtramp_frame_this_id (struct frame_info *this_frame,
+			     void **this_prologue_cache,
+			     struct frame_id *this_id)
+{
+  struct s390_sigtramp_unwind_cache *info
+    = s390_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
+  *this_id = frame_id_build (info->frame_base, get_frame_pc (this_frame));
+}
+
+static struct value *
+s390_sigtramp_frame_prev_register (struct frame_info *this_frame,
+				   void **this_prologue_cache, int regnum)
+{
+  struct s390_sigtramp_unwind_cache *info
+    = s390_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
+  return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
+}
+
+static int
+s390_sigtramp_frame_sniffer (const struct frame_unwind *self,
+			     struct frame_info *this_frame,
+			     void **this_prologue_cache)
+{
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  bfd_byte sigreturn[2];
+
+  if (target_read_memory (pc, sigreturn, 2))
+    return 0;
+
+  if (sigreturn[0] != op_svc)
+    return 0;
+
+  if (sigreturn[1] != 119 /* sigreturn */
+      && sigreturn[1] != 173 /* rt_sigreturn */)
+    return 0;
+
+  return 1;
+}
+
+static const struct frame_unwind s390_sigtramp_frame_unwind = {
+  SIGTRAMP_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_sigtramp_frame_this_id,
+  s390_sigtramp_frame_prev_register,
+  NULL,
+  s390_sigtramp_frame_sniffer
+};
+
+/* Frame base handling.  */
+
+static CORE_ADDR
+s390_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct s390_unwind_cache *info
+    = s390_frame_unwind_cache (this_frame, this_cache);
+  return info->frame_base;
+}
+
+static CORE_ADDR
+s390_local_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct s390_unwind_cache *info
+    = s390_frame_unwind_cache (this_frame, this_cache);
+  return info->local_base;
+}
+
+static const struct frame_base s390_frame_base = {
+  &s390_frame_unwind,
+  s390_frame_base_address,
+  s390_local_base_address,
+  s390_local_base_address
+};
+
+
+/* Initialize GNU/Linux specific gdbarch hooks.  */
+
+void
+s390_linux_gdbarch_init (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* Frame handling.  */
-  dwarf2_frame_set_init_reg (gdbarch, s390_dwarf2_frame_init_reg);
-  dwarf2_frame_set_adjust_regnum (gdbarch, s390_adjust_frame_regnum);
-  dwarf2_append_unwinders (gdbarch);
-  frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
-  frame_unwind_append_unwinder (gdbarch, &s390_stub_frame_unwind);
-  frame_unwind_append_unwinder (gdbarch, &s390_sigtramp_frame_unwind);
-  frame_unwind_append_unwinder (gdbarch, &s390_frame_unwind);
-  frame_base_set_default (gdbarch, &s390_frame_base);
-  set_gdbarch_unwind_pc (gdbarch, s390_unwind_pc);
-  set_gdbarch_unwind_sp (gdbarch, s390_unwind_sp);
-
-  /* Displaced stepping.  */
-  set_gdbarch_displaced_step_copy_insn (gdbarch,
-					s390_displaced_step_copy_insn);
-  set_gdbarch_displaced_step_fixup (gdbarch, s390_displaced_step_fixup);
-  set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
-  set_gdbarch_max_insn_length (gdbarch, S390_MAX_INSTR_SIZE);
-
-  /* Note that GNU/Linux is the only OS supported on this
-     platform.  */
   linux_init_abi (info, gdbarch);
 
+  set_gdbarch_guess_tracepoint_registers (gdbarch, s390_guess_tracepoint_registers);
+
+  /* Syscall handling.  */
+  set_gdbarch_get_syscall_number (gdbarch, s390_linux_get_syscall_number);
+
   switch (tdep->abi)
     {
     case ABI_LINUX_S390:
-      set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
       set_solib_svr4_fetch_link_map_offsets
 	(gdbarch, svr4_ilp32_fetch_link_map_offsets);
-
       set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390);
       break;
 
     case ABI_LINUX_ZSERIES:
-      set_gdbarch_long_bit (gdbarch, 64);
-      set_gdbarch_long_long_bit (gdbarch, 64);
-      set_gdbarch_ptr_bit (gdbarch, 64);
       set_solib_svr4_fetch_link_map_offsets
 	(gdbarch, svr4_lp64_fetch_link_map_offsets);
-      set_gdbarch_address_class_type_flags (gdbarch,
-					    s390_address_class_type_flags);
-      set_gdbarch_address_class_type_flags_to_name (gdbarch,
-						    s390_address_class_type_flags_to_name);
-      set_gdbarch_address_class_name_to_type_flags (gdbarch,
-						    s390_address_class_name_to_type_flags);
       set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390X);
       break;
     }
@@ -8044,40 +5341,29 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_fetch_tls_load_module_address (gdbarch,
 					     svr4_fetch_objfile_link_map);
 
-  /* SystemTap functions.  */
-  set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes);
-  set_gdbarch_stap_register_indirection_prefixes (gdbarch,
-					  stap_register_indirection_prefixes);
-  set_gdbarch_stap_register_indirection_suffixes (gdbarch,
-					  stap_register_indirection_suffixes);
   set_gdbarch_stap_is_single_operand (gdbarch, s390_stap_is_single_operand);
   set_gdbarch_gcc_target_options (gdbarch, s390_gcc_target_options);
   set_gdbarch_gnu_triplet_regexp (gdbarch, s390_gnu_triplet_regexp);
 
   /* Support reverse debugging.  */
-
   set_gdbarch_process_record (gdbarch, s390_process_record);
   set_gdbarch_process_record_signal (gdbarch, s390_linux_record_signal);
 
   s390_init_linux_record_tdep (&s390_linux_record_tdep, ABI_LINUX_S390);
   s390_init_linux_record_tdep (&s390x_linux_record_tdep, ABI_LINUX_ZSERIES);
 
-  set_gdbarch_disassembler_options (gdbarch, &s390_disassembler_options);
-  set_gdbarch_valid_disassembler_options (gdbarch,
-					  disassembler_options_s390 ());
-
-  return gdbarch;
+  /* Frame handling.  */
+  frame_unwind_append_unwinder (gdbarch, &s390_stub_frame_unwind);
+  frame_unwind_append_unwinder (gdbarch, &s390_sigtramp_frame_unwind);
+  frame_unwind_append_unwinder (gdbarch, &s390_frame_unwind);
+  frame_base_set_default (gdbarch, &s390_frame_base);
 }
 
-
-extern initialize_file_ftype _initialize_s390_tdep; /* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_s390_linux_tdep; /* -Wmissing-prototypes */
 
 void
-_initialize_s390_tdep (void)
+_initialize_s390_linux_tdep (void)
 {
-  /* Hook us into the gdbarch mechanism.  */
-  register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
-
   /* Initialize the GNU/Linux target descriptions.  */
   initialize_tdesc_s390_linux32 ();
   initialize_tdesc_s390_linux32v1 ();
diff --git a/gdb/s390-linux-tdep.h b/gdb/s390-linux-tdep.h
index 4f818b4319..b2ef641e1c 100644
--- a/gdb/s390-linux-tdep.h
+++ b/gdb/s390-linux-tdep.h
@@ -1,4 +1,4 @@
-/* Target-dependent code for GDB, the GNU debugger.
+/* Target-dependent code for GNU/Linux on S390.
    Copyright (C) 2003-2017 Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -16,181 +16,19 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef S390_TDEP_H
-#define S390_TDEP_H
+#ifndef S390_LINUX_TDEP_H
+#define S390_LINUX_TDEP_H
 
-/* Hardware capabilities. */
+extern void s390_linux_gdbarch_init (struct gdbarch_info info,
+				     struct gdbarch *gdbarch);
 
-#ifndef HWCAP_S390_HIGH_GPRS
-#define HWCAP_S390_HIGH_GPRS 512
-#endif
-
-#ifndef HWCAP_S390_TE
-#define HWCAP_S390_TE 1024
-#endif
-
-#ifndef HWCAP_S390_VX
-#define HWCAP_S390_VX 2048
-#endif
-
-/* Register information.  */
-
-/* Program Status Word.  */
-#define S390_PSWM_REGNUM 0
-#define S390_PSWA_REGNUM 1
-/* General Purpose Registers.  */
-#define S390_R0_REGNUM 2
-#define S390_R1_REGNUM 3
-#define S390_R2_REGNUM 4
-#define S390_R3_REGNUM 5
-#define S390_R4_REGNUM 6
-#define S390_R5_REGNUM 7
-#define S390_R6_REGNUM 8
-#define S390_R7_REGNUM 9
-#define S390_R8_REGNUM 10
-#define S390_R9_REGNUM 11
-#define S390_R10_REGNUM 12
-#define S390_R11_REGNUM 13
-#define S390_R12_REGNUM 14
-#define S390_R13_REGNUM 15
-#define S390_R14_REGNUM 16
-#define S390_R15_REGNUM 17
-/* Access Registers.  */
-#define S390_A0_REGNUM 18
-#define S390_A1_REGNUM 19
-#define S390_A2_REGNUM 20
-#define S390_A3_REGNUM 21
-#define S390_A4_REGNUM 22
-#define S390_A5_REGNUM 23
-#define S390_A6_REGNUM 24
-#define S390_A7_REGNUM 25
-#define S390_A8_REGNUM 26
-#define S390_A9_REGNUM 27
-#define S390_A10_REGNUM 28
-#define S390_A11_REGNUM 29
-#define S390_A12_REGNUM 30
-#define S390_A13_REGNUM 31
-#define S390_A14_REGNUM 32
-#define S390_A15_REGNUM 33
-/* Floating Point Control Word.  */
-#define S390_FPC_REGNUM 34
-/* Floating Point Registers.  */
-#define S390_F0_REGNUM 35
-#define S390_F1_REGNUM 36
-#define S390_F2_REGNUM 37
-#define S390_F3_REGNUM 38
-#define S390_F4_REGNUM 39
-#define S390_F5_REGNUM 40
-#define S390_F6_REGNUM 41
-#define S390_F7_REGNUM 42
-#define S390_F8_REGNUM 43
-#define S390_F9_REGNUM 44
-#define S390_F10_REGNUM 45
-#define S390_F11_REGNUM 46
-#define S390_F12_REGNUM 47
-#define S390_F13_REGNUM 48
-#define S390_F14_REGNUM 49
-#define S390_F15_REGNUM 50
-/* General Purpose Register Upper Halves.  */
-#define S390_R0_UPPER_REGNUM 51
-#define S390_R1_UPPER_REGNUM 52
-#define S390_R2_UPPER_REGNUM 53
-#define S390_R3_UPPER_REGNUM 54
-#define S390_R4_UPPER_REGNUM 55
-#define S390_R5_UPPER_REGNUM 56
-#define S390_R6_UPPER_REGNUM 57
-#define S390_R7_UPPER_REGNUM 58
-#define S390_R8_UPPER_REGNUM 59
-#define S390_R9_UPPER_REGNUM 60
-#define S390_R10_UPPER_REGNUM 61
-#define S390_R11_UPPER_REGNUM 62
-#define S390_R12_UPPER_REGNUM 63
-#define S390_R13_UPPER_REGNUM 64
-#define S390_R14_UPPER_REGNUM 65
-#define S390_R15_UPPER_REGNUM 66
-/* GNU/Linux-specific optional registers.  */
-#define S390_ORIG_R2_REGNUM 67
-#define S390_LAST_BREAK_REGNUM 68
-#define S390_SYSTEM_CALL_REGNUM 69
-/* Transaction diagnostic block.  */
-#define S390_TDB_DWORD0_REGNUM 70
-#define S390_TDB_ABORT_CODE_REGNUM 71
-#define S390_TDB_CONFLICT_TOKEN_REGNUM 72
-#define S390_TDB_ATIA_REGNUM 73
-#define S390_TDB_R0_REGNUM 74
-#define S390_TDB_R1_REGNUM 75
-#define S390_TDB_R2_REGNUM 76
-#define S390_TDB_R3_REGNUM 77
-#define S390_TDB_R4_REGNUM 78
-#define S390_TDB_R5_REGNUM 79
-#define S390_TDB_R6_REGNUM 80
-#define S390_TDB_R7_REGNUM 81
-#define S390_TDB_R8_REGNUM 82
-#define S390_TDB_R9_REGNUM 83
-#define S390_TDB_R10_REGNUM 84
-#define S390_TDB_R11_REGNUM 85
-#define S390_TDB_R12_REGNUM 86
-#define S390_TDB_R13_REGNUM 87
-#define S390_TDB_R14_REGNUM 88
-#define S390_TDB_R15_REGNUM 89
-/* Vector registers.  */
-#define S390_V0_LOWER_REGNUM 90
-#define S390_V1_LOWER_REGNUM 91
-#define S390_V2_LOWER_REGNUM 92
-#define S390_V3_LOWER_REGNUM 93
-#define S390_V4_LOWER_REGNUM 94
-#define S390_V5_LOWER_REGNUM 95
-#define S390_V6_LOWER_REGNUM 96
-#define S390_V7_LOWER_REGNUM 97
-#define S390_V8_LOWER_REGNUM 98
-#define S390_V9_LOWER_REGNUM 99
-#define S390_V10_LOWER_REGNUM 100
-#define S390_V11_LOWER_REGNUM 101
-#define S390_V12_LOWER_REGNUM 102
-#define S390_V13_LOWER_REGNUM 103
-#define S390_V14_LOWER_REGNUM 104
-#define S390_V15_LOWER_REGNUM 105
-#define S390_V16_REGNUM 106
-#define S390_V17_REGNUM 107
-#define S390_V18_REGNUM 108
-#define S390_V19_REGNUM 109
-#define S390_V20_REGNUM 110
-#define S390_V21_REGNUM 111
-#define S390_V22_REGNUM 112
-#define S390_V23_REGNUM 113
-#define S390_V24_REGNUM 114
-#define S390_V25_REGNUM 115
-#define S390_V26_REGNUM 116
-#define S390_V27_REGNUM 117
-#define S390_V28_REGNUM 118
-#define S390_V29_REGNUM 119
-#define S390_V30_REGNUM 120
-#define S390_V31_REGNUM 121
-/* Total.  */
-#define S390_NUM_REGS 122
-
-/* Special register usage.  */
-#define S390_SP_REGNUM S390_R15_REGNUM
-#define S390_RETADDR_REGNUM S390_R14_REGNUM
-#define S390_FRAME_REGNUM S390_R11_REGNUM
-
-#define S390_IS_GREGSET_REGNUM(i)					\
-  (((i) >= S390_PSWM_REGNUM && (i) <= S390_A15_REGNUM)			\
-   || ((i) >= S390_R0_UPPER_REGNUM && (i) <= S390_R15_UPPER_REGNUM)	\
-   || (i) == S390_ORIG_R2_REGNUM)
-
-#define S390_IS_FPREGSET_REGNUM(i)			\
-  ((i) >= S390_FPC_REGNUM && (i) <= S390_F15_REGNUM)
-
-#define S390_IS_TDBREGSET_REGNUM(i)				\
-  ((i) >= S390_TDB_DWORD0_REGNUM && (i) <= S390_TDB_R15_REGNUM)
-
-/* Core file register sets, defined in s390-tdep.c.  */
+/* Core file register sets, defined in s390-linux-tdep.c.  */
 #define s390_sizeof_gregset 0x90
 #define s390x_sizeof_gregset 0xd8
 extern const struct regset s390_gregset;
 #define s390_sizeof_fpregset 0x88
 extern const struct regset s390_fpregset;
+extern const struct regset s390_upper_regset;
 extern const struct regset s390_last_break_regset;
 extern const struct regset s390x_last_break_regset;
 extern const struct regset s390_system_call_regset;
@@ -216,4 +54,4 @@ extern struct target_desc *tdesc_s390x_te_linux64;
 extern struct target_desc *tdesc_s390x_vx_linux64;
 extern struct target_desc *tdesc_s390x_tevx_linux64;
 
-#endif
+#endif /* S390_LINUX_TDEP_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
new file mode 100644
index 0000000000..1a9557bb3f
--- /dev/null
+++ b/gdb/s390-tdep.c
@@ -0,0 +1,2663 @@
+/* Target-dependent code for S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "arch-utils.h"
+#include "auxv.h"
+#include "ax-gdb.h"
+#include "dis-asm.h"
+#include "dwarf2-frame.h"
+#include "elf/common.h"
+#include "elf/s390.h"
+#include "elf-bfd.h"
+#include "frame-base.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "infrun.h"
+#include "linux-tdep.h"
+#include "prologue-value.h"
+#include "record-full.h"
+#include "reggroups.h"
+#include "s390-linux-tdep.h"
+#include "s390-tdep.h"
+#include "target-descriptions.h"
+#include "value.h"
+
+/* Holds the current set of options to be passed to the disassembler.  */
+static char *s390_disassembler_options;
+
+/* Decoding S/390 instructions.  */
+
+/* Read a single instruction from address AT.  */
+
+int
+s390_readinstruction (bfd_byte instr[], CORE_ADDR at)
+{
+  static int s390_instrlen[] = { 2, 4, 4, 6 };
+  int instrlen;
+
+  if (target_read_memory (at, &instr[0], 2))
+    return -1;
+  instrlen = s390_instrlen[instr[0] >> 6];
+  if (instrlen > 2)
+    {
+      if (target_read_memory (at + 2, &instr[2], instrlen - 2))
+	return -1;
+    }
+  return instrlen;
+}
+
+/* The functions below are for recognizing and decoding S/390
+   instructions of various formats.  Each of them checks whether INSN
+   is an instruction of the given format, with the specified opcodes.
+   If it is, it sets the remaining arguments to the values of the
+   instruction's fields, and returns a non-zero value; otherwise, it
+   returns zero.
+
+   These functions' arguments appear in the order they appear in the
+   instruction, not in the machine-language form.  So, opcodes always
+   come first, even though they're sometimes scattered around the
+   instructions.  And displacements appear before base and extension
+   registers, as they do in the assembly syntax, not at the end, as
+   they do in the machine language.  */
+
+static int
+is_ri (bfd_byte *insn, int op1, int op2, unsigned int *r1, int *i2)
+{
+  if (insn[0] == op1 && (insn[1] & 0xf) == op2)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      /* i2 is a 16-bit signed quantity.  */
+      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_ril (bfd_byte *insn, int op1, int op2,
+	unsigned int *r1, int *i2)
+{
+  if (insn[0] == op1 && (insn[1] & 0xf) == op2)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      /* i2 is a signed quantity.  If the host 'int' is 32 bits long,
+	 no sign extension is necessary, but we don't want to assume
+	 that.  */
+      *i2 = (((insn[2] << 24)
+	      | (insn[3] << 16)
+	      | (insn[4] << 8)
+	      | (insn[5])) ^ 0x80000000) - 0x80000000;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rr (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
+{
+  if (insn[0] == op)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *r2 = insn[1] & 0xf;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rre (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
+{
+  if (((insn[0] << 8) | insn[1]) == op)
+    {
+      /* Yes, insn[3].  insn[2] is unused in RRE format.  */
+      *r1 = (insn[3] >> 4) & 0xf;
+      *r2 = insn[3] & 0xf;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rs (bfd_byte *insn, int op,
+       unsigned int *r1, unsigned int *r3, int *d2, unsigned int *b2)
+{
+  if (insn[0] == op)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *r3 = insn[1] & 0xf;
+      *b2 = (insn[2] >> 4) & 0xf;
+      *d2 = ((insn[2] & 0xf) << 8) | insn[3];
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rsy (bfd_byte *insn, int op1, int op2,
+	unsigned int *r1, unsigned int *r3, int *d2, unsigned int *b2)
+{
+  if (insn[0] == op1
+      && insn[5] == op2)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *r3 = insn[1] & 0xf;
+      *b2 = (insn[2] >> 4) & 0xf;
+      /* The 'long displacement' is a 20-bit signed integer.  */
+      *d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
+		^ 0x80000) - 0x80000;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rx (bfd_byte *insn, int op,
+       unsigned int *r1, int *d2, unsigned int *x2, unsigned int *b2)
+{
+  if (insn[0] == op)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *x2 = insn[1] & 0xf;
+      *b2 = (insn[2] >> 4) & 0xf;
+      *d2 = ((insn[2] & 0xf) << 8) | insn[3];
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rxy (bfd_byte *insn, int op1, int op2,
+	unsigned int *r1, int *d2, unsigned int *x2, unsigned int *b2)
+{
+  if (insn[0] == op1
+      && insn[5] == op2)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *x2 = insn[1] & 0xf;
+      *b2 = (insn[2] >> 4) & 0xf;
+      /* The 'long displacement' is a 20-bit signed integer.  */
+      *d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
+		^ 0x80000) - 0x80000;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+/* Breakpoints.  */
+
+constexpr gdb_byte s390_break_insn[] = { 0x0, 0x1 };
+
+typedef BP_MANIPULATION (s390_break_insn) s390_breakpoint;
+
+/* A helper for s390_software_single_step, decides if an instruction
+   is a partial-execution instruction that needs to be executed until
+   completion when in record mode.  If it is, returns 1 and writes
+   instruction length to a pointer.  */
+
+static int
+s390_is_partial_instruction (struct gdbarch *gdbarch, CORE_ADDR loc, int *len)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  uint16_t insn;
+
+  insn = read_memory_integer (loc, 2, byte_order);
+
+  switch (insn >> 8)
+    {
+    case 0xa8: /* MVCLE */
+      *len = 4;
+      return 1;
+
+    case 0xeb:
+      {
+	insn = read_memory_integer (loc + 4, 2, byte_order);
+	if ((insn & 0xff) == 0x8e)
+	  {
+	    /* MVCLU */
+	    *len = 6;
+	    return 1;
+	  }
+      }
+      break;
+    }
+
+  switch (insn)
+    {
+    case 0xb255: /* MVST */
+    case 0xb263: /* CMPSC */
+    case 0xb2a5: /* TRE */
+    case 0xb2a6: /* CU21 */
+    case 0xb2a7: /* CU12 */
+    case 0xb9b0: /* CU14 */
+    case 0xb9b1: /* CU24 */
+    case 0xb9b2: /* CU41 */
+    case 0xb9b3: /* CU42 */
+    case 0xb92a: /* KMF */
+    case 0xb92b: /* KMO */
+    case 0xb92f: /* KMC */
+    case 0xb92d: /* KMCTR */
+    case 0xb92e: /* KM */
+    case 0xb93c: /* PPNO */
+    case 0xb990: /* TRTT */
+    case 0xb991: /* TRTO */
+    case 0xb992: /* TROT */
+    case 0xb993: /* TROO */
+      *len = 4;
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Implement the "software_single_step" gdbarch method, needed to single step
+   through instructions like MVCLE in record mode, to make sure they are
+   executed to completion.  Without that, record will save the full length
+   of destination buffer on every iteration, even though the CPU will only
+   process about 4kiB of it each time, leading to O(n**2) memory and time
+   complexity.  */
+
+static std::vector<CORE_ADDR>
+s390_software_single_step (struct regcache *regcache)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR loc = regcache_read_pc (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int len;
+  uint16_t insn;
+
+  /* Special handling only if recording.  */
+  if (!record_full_is_used ())
+    return {};
+
+  /* First, match a partial instruction.  */
+  if (!s390_is_partial_instruction (gdbarch, loc, &len))
+    return {};
+
+  loc += len;
+
+  /* Second, look for a branch back to it.  */
+  insn = read_memory_integer (loc, 2, byte_order);
+  if (insn != 0xa714) /* BRC with mask 1 */
+    return {};
+
+  insn = read_memory_integer (loc + 2, 2, byte_order);
+  if (insn != (uint16_t) -(len / 2))
+    return {};
+
+  loc += 4;
+
+  return {loc};
+}
+
+
+/* Prologue analysis.  */
+
+/* Return the effective address for an X-style instruction, like:
+
+	L R1, D2(X2, B2)
+
+   Here, X2 and B2 are registers, and D2 is a signed 20-bit
+   constant; the effective address is the sum of all three.  If either
+   X2 or B2 are zero, then it doesn't contribute to the sum --- this
+   means that r0 can't be used as either X2 or B2.  */
+
+static pv_t
+s390_addr (struct s390_prologue_data *data,
+	   int d2, unsigned int x2, unsigned int b2)
+{
+  pv_t result;
+
+  result = pv_constant (d2);
+  if (x2)
+    result = pv_add (result, data->gpr[x2]);
+  if (b2)
+    result = pv_add (result, data->gpr[b2]);
+
+  return result;
+}
+
+/* Do a SIZE-byte store of VALUE to D2(X2,B2).  Helper for
+   s390_analyze_prologue.  */
+
+static void
+s390_store (struct s390_prologue_data *data,
+	    int d2, unsigned int x2, unsigned int b2, CORE_ADDR size,
+	    pv_t value)
+{
+  pv_t addr = s390_addr (data, d2, x2, b2);
+  pv_t offset;
+
+  /* Check whether we are storing the backchain.  */
+  offset = pv_subtract (data->gpr[S390_SP_REGNUM - S390_R0_REGNUM], addr);
+
+  if (pv_is_constant (offset) && offset.k == 0)
+    if (size == data->gpr_size
+	&& pv_is_register_k (value, S390_SP_REGNUM, 0))
+      {
+	data->back_chain_saved_p = 1;
+	return;
+      }
+
+
+  /* Check whether we are storing a register into the stack.  */
+  if (!pv_area_store_would_trash (data->stack, addr))
+    pv_area_store (data->stack, addr, size, value);
+
+
+  /* Note: If this is some store we cannot identify, you might think we
+     should forget our cached values, as any of those might have been hit.
+
+     However, we make the assumption that the register save areas are only
+     ever stored to once in any given function, and we do recognize these
+     stores.  Thus every store we cannot recognize does not hit our data.  */
+}
+
+/* Do a SIZE-byte load from D2(X2,B2).  Helper for s390_analyze_prologue.  */
+
+static pv_t
+s390_load (struct s390_prologue_data *data,
+	   int d2, unsigned int x2, unsigned int b2, CORE_ADDR size)
+
+{
+  pv_t addr = s390_addr (data, d2, x2, b2);
+
+  /* If it's a load from an in-line constant pool, then we can
+     simulate that, under the assumption that the code isn't
+     going to change between the time the processor actually
+     executed it creating the current frame, and the time when
+     we're analyzing the code to unwind past that frame.  */
+  if (pv_is_constant (addr))
+    {
+      struct target_section *secp;
+      secp = target_section_by_addr (&current_target, addr.k);
+      if (secp != NULL
+	  && (bfd_get_section_flags (secp->the_bfd_section->owner,
+				     secp->the_bfd_section)
+	      & SEC_READONLY))
+	return pv_constant (read_memory_integer (addr.k, size,
+						 data->byte_order));
+    }
+
+  /* Check whether we are accessing one of our save slots.  */
+  return pv_area_fetch (data->stack, addr, size);
+}
+
+/* Function for finding saved registers in a 'struct pv_area'; we pass
+   this to pv_area_scan.
+
+   If VALUE is a saved register, ADDR says it was saved at a constant
+   offset from the frame base, and SIZE indicates that the whole
+   register was saved, record its offset in the reg_offset table in
+   PROLOGUE_UNTYPED.  */
+
+static void
+s390_check_for_saved (void *data_untyped, pv_t addr,
+		      CORE_ADDR size, pv_t value)
+{
+  struct s390_prologue_data *data = (struct s390_prologue_data *) data_untyped;
+  int i, offset;
+
+  if (!pv_is_register (addr, S390_SP_REGNUM))
+    return;
+
+  offset = 16 * data->gpr_size + 32 - addr.k;
+
+  /* If we are storing the original value of a register, we want to
+     record the CFA offset.  If the same register is stored multiple
+     times, the stack slot with the highest address counts.  */
+
+  for (i = 0; i < S390_NUM_GPRS; i++)
+    if (size == data->gpr_size
+	&& pv_is_register_k (value, S390_R0_REGNUM + i, 0))
+      if (data->gpr_slot[i] == 0
+	  || data->gpr_slot[i] > offset)
+	{
+	  data->gpr_slot[i] = offset;
+	  return;
+	}
+
+  for (i = 0; i < S390_NUM_FPRS; i++)
+    if (size == data->fpr_size
+	&& pv_is_register_k (value, S390_F0_REGNUM + i, 0))
+      if (data->fpr_slot[i] == 0
+	  || data->fpr_slot[i] > offset)
+	{
+	  data->fpr_slot[i] = offset;
+	  return;
+	}
+}
+
+/* Analyze the prologue of the function starting at START_PC,
+   continuing at most until CURRENT_PC.  Initialize DATA to
+   hold all information we find out about the state of the registers
+   and stack slots.  Return the address of the instruction after
+   the last one that changed the SP, FP, or back chain; or zero
+   on error.  */
+
+CORE_ADDR
+s390_analyze_prologue (struct gdbarch *gdbarch,
+		       CORE_ADDR start_pc,
+		       CORE_ADDR current_pc,
+		       struct s390_prologue_data *data)
+{
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+
+  /* Our return value:
+     The address of the instruction after the last one that changed
+     the SP, FP, or back chain;  zero if we got an error trying to
+     read memory.  */
+  CORE_ADDR result = start_pc;
+
+  /* The current PC for our abstract interpretation.  */
+  CORE_ADDR pc;
+
+  /* The address of the next instruction after that.  */
+  CORE_ADDR next_pc;
+
+  /* Set up everything's initial value.  */
+  {
+    int i;
+
+    data->stack = make_pv_area (S390_SP_REGNUM, gdbarch_addr_bit (gdbarch));
+
+    /* For the purpose of prologue tracking, we consider the GPR size to
+       be equal to the ABI word size, even if it is actually larger
+       (i.e. when running a 32-bit binary under a 64-bit kernel).  */
+    data->gpr_size = word_size;
+    data->fpr_size = 8;
+    data->byte_order = gdbarch_byte_order (gdbarch);
+
+    for (i = 0; i < S390_NUM_GPRS; i++)
+      data->gpr[i] = pv_register (S390_R0_REGNUM + i, 0);
+
+    for (i = 0; i < S390_NUM_FPRS; i++)
+      data->fpr[i] = pv_register (S390_F0_REGNUM + i, 0);
+
+    for (i = 0; i < S390_NUM_GPRS; i++)
+      data->gpr_slot[i]  = 0;
+
+    for (i = 0; i < S390_NUM_FPRS; i++)
+      data->fpr_slot[i]  = 0;
+
+    data->back_chain_saved_p = 0;
+  }
+
+  /* Start interpreting instructions, until we hit the frame's
+     current PC or the first branch instruction.  */
+  for (pc = start_pc; pc > 0 && pc < current_pc; pc = next_pc)
+    {
+      bfd_byte insn[S390_MAX_INSTR_SIZE];
+      int insn_len = s390_readinstruction (insn, pc);
+
+      bfd_byte dummy[S390_MAX_INSTR_SIZE] = { 0 };
+      bfd_byte *insn32 = word_size == 4 ? insn : dummy;
+      bfd_byte *insn64 = word_size == 8 ? insn : dummy;
+
+      /* Fields for various kinds of instructions.  */
+      unsigned int b2, r1, r2, x2, r3;
+      int i2, d2;
+
+      /* The values of SP and FP before this instruction,
+	 for detecting instructions that change them.  */
+      pv_t pre_insn_sp, pre_insn_fp;
+      /* Likewise for the flag whether the back chain was saved.  */
+      int pre_insn_back_chain_saved_p;
+
+      /* If we got an error trying to read the instruction, report it.  */
+      if (insn_len < 0)
+	{
+	  result = 0;
+	  break;
+	}
+
+      next_pc = pc + insn_len;
+
+      pre_insn_sp = data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
+      pre_insn_fp = data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
+      pre_insn_back_chain_saved_p = data->back_chain_saved_p;
+
+
+      /* LHI r1, i2 --- load halfword immediate.  */
+      /* LGHI r1, i2 --- load halfword immediate (64-bit version).  */
+      /* LGFI r1, i2 --- load fullword immediate.  */
+      if (is_ri (insn32, op1_lhi, op2_lhi, &r1, &i2)
+	  || is_ri (insn64, op1_lghi, op2_lghi, &r1, &i2)
+	  || is_ril (insn, op1_lgfi, op2_lgfi, &r1, &i2))
+	data->gpr[r1] = pv_constant (i2);
+
+      /* LR r1, r2 --- load from register.  */
+      /* LGR r1, r2 --- load from register (64-bit version).  */
+      else if (is_rr (insn32, op_lr, &r1, &r2)
+	       || is_rre (insn64, op_lgr, &r1, &r2))
+	data->gpr[r1] = data->gpr[r2];
+
+      /* L r1, d2(x2, b2) --- load.  */
+      /* LY r1, d2(x2, b2) --- load (long-displacement version).  */
+      /* LG r1, d2(x2, b2) --- load (64-bit version).  */
+      else if (is_rx (insn32, op_l, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn32, op1_ly, op2_ly, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn64, op1_lg, op2_lg, &r1, &d2, &x2, &b2))
+	data->gpr[r1] = s390_load (data, d2, x2, b2, data->gpr_size);
+
+      /* ST r1, d2(x2, b2) --- store.  */
+      /* STY r1, d2(x2, b2) --- store (long-displacement version).  */
+      /* STG r1, d2(x2, b2) --- store (64-bit version).  */
+      else if (is_rx (insn32, op_st, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn32, op1_sty, op2_sty, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn64, op1_stg, op2_stg, &r1, &d2, &x2, &b2))
+	s390_store (data, d2, x2, b2, data->gpr_size, data->gpr[r1]);
+
+      /* STD r1, d2(x2,b2) --- store floating-point register.  */
+      else if (is_rx (insn, op_std, &r1, &d2, &x2, &b2))
+	s390_store (data, d2, x2, b2, data->fpr_size, data->fpr[r1]);
+
+      /* STM r1, r3, d2(b2) --- store multiple.  */
+      /* STMY r1, r3, d2(b2) --- store multiple (long-displacement
+	 version).  */
+      /* STMG r1, r3, d2(b2) --- store multiple (64-bit version).  */
+      else if (is_rs (insn32, op_stm, &r1, &r3, &d2, &b2)
+	       || is_rsy (insn32, op1_stmy, op2_stmy, &r1, &r3, &d2, &b2)
+	       || is_rsy (insn64, op1_stmg, op2_stmg, &r1, &r3, &d2, &b2))
+	{
+	  for (; r1 <= r3; r1++, d2 += data->gpr_size)
+	    s390_store (data, d2, 0, b2, data->gpr_size, data->gpr[r1]);
+	}
+
+      /* AHI r1, i2 --- add halfword immediate.  */
+      /* AGHI r1, i2 --- add halfword immediate (64-bit version).  */
+      /* AFI r1, i2 --- add fullword immediate.  */
+      /* AGFI r1, i2 --- add fullword immediate (64-bit version).  */
+      else if (is_ri (insn32, op1_ahi, op2_ahi, &r1, &i2)
+	       || is_ri (insn64, op1_aghi, op2_aghi, &r1, &i2)
+	       || is_ril (insn32, op1_afi, op2_afi, &r1, &i2)
+	       || is_ril (insn64, op1_agfi, op2_agfi, &r1, &i2))
+	data->gpr[r1] = pv_add_constant (data->gpr[r1], i2);
+
+      /* ALFI r1, i2 --- add logical immediate.  */
+      /* ALGFI r1, i2 --- add logical immediate (64-bit version).  */
+      else if (is_ril (insn32, op1_alfi, op2_alfi, &r1, &i2)
+	       || is_ril (insn64, op1_algfi, op2_algfi, &r1, &i2))
+	data->gpr[r1] = pv_add_constant (data->gpr[r1],
+					 (CORE_ADDR)i2 & 0xffffffff);
+
+      /* AR r1, r2 -- add register.  */
+      /* AGR r1, r2 -- add register (64-bit version).  */
+      else if (is_rr (insn32, op_ar, &r1, &r2)
+	       || is_rre (insn64, op_agr, &r1, &r2))
+	data->gpr[r1] = pv_add (data->gpr[r1], data->gpr[r2]);
+
+      /* A r1, d2(x2, b2) -- add.  */
+      /* AY r1, d2(x2, b2) -- add (long-displacement version).  */
+      /* AG r1, d2(x2, b2) -- add (64-bit version).  */
+      else if (is_rx (insn32, op_a, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn32, op1_ay, op2_ay, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn64, op1_ag, op2_ag, &r1, &d2, &x2, &b2))
+	data->gpr[r1] = pv_add (data->gpr[r1],
+				s390_load (data, d2, x2, b2, data->gpr_size));
+
+      /* SLFI r1, i2 --- subtract logical immediate.  */
+      /* SLGFI r1, i2 --- subtract logical immediate (64-bit version).  */
+      else if (is_ril (insn32, op1_slfi, op2_slfi, &r1, &i2)
+	       || is_ril (insn64, op1_slgfi, op2_slgfi, &r1, &i2))
+	data->gpr[r1] = pv_add_constant (data->gpr[r1],
+					 -((CORE_ADDR)i2 & 0xffffffff));
+
+      /* SR r1, r2 -- subtract register.  */
+      /* SGR r1, r2 -- subtract register (64-bit version).  */
+      else if (is_rr (insn32, op_sr, &r1, &r2)
+	       || is_rre (insn64, op_sgr, &r1, &r2))
+	data->gpr[r1] = pv_subtract (data->gpr[r1], data->gpr[r2]);
+
+      /* S r1, d2(x2, b2) -- subtract.  */
+      /* SY r1, d2(x2, b2) -- subtract (long-displacement version).  */
+      /* SG r1, d2(x2, b2) -- subtract (64-bit version).  */
+      else if (is_rx (insn32, op_s, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn32, op1_sy, op2_sy, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn64, op1_sg, op2_sg, &r1, &d2, &x2, &b2))
+	data->gpr[r1] = pv_subtract (data->gpr[r1],
+				s390_load (data, d2, x2, b2, data->gpr_size));
+
+      /* LA r1, d2(x2, b2) --- load address.  */
+      /* LAY r1, d2(x2, b2) --- load address (long-displacement version).  */
+      else if (is_rx (insn, op_la, &r1, &d2, &x2, &b2)
+	       || is_rxy (insn, op1_lay, op2_lay, &r1, &d2, &x2, &b2))
+	data->gpr[r1] = s390_addr (data, d2, x2, b2);
+
+      /* LARL r1, i2 --- load address relative long.  */
+      else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
+	data->gpr[r1] = pv_constant (pc + i2 * 2);
+
+      /* BASR r1, 0 --- branch and save.
+	 Since r2 is zero, this saves the PC in r1, but doesn't branch.  */
+      else if (is_rr (insn, op_basr, &r1, &r2)
+	       && r2 == 0)
+	data->gpr[r1] = pv_constant (next_pc);
+
+      /* BRAS r1, i2 --- branch relative and save.  */
+      else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2))
+	{
+	  data->gpr[r1] = pv_constant (next_pc);
+	  next_pc = pc + i2 * 2;
+
+	  /* We'd better not interpret any backward branches.  We'll
+	     never terminate.  */
+	  if (next_pc <= pc)
+	    break;
+	}
+
+      /* BRC/BRCL -- branch relative on condition.  Ignore "branch
+	 never", branch to following instruction, and "conditional
+	 trap" (BRC +2).  Otherwise terminate search.  */
+      else if (is_ri (insn, op1_brc, op2_brc, &r1, &i2))
+	{
+	  if (r1 != 0 && i2 != 1 && i2 != 2)
+	    break;
+	}
+      else if (is_ril (insn, op1_brcl, op2_brcl, &r1, &i2))
+	{
+	  if (r1 != 0 && i2 != 3)
+	    break;
+	}
+
+      /* Terminate search when hitting any other branch instruction.  */
+      else if (is_rr (insn, op_basr, &r1, &r2)
+	       || is_rx (insn, op_bas, &r1, &d2, &x2, &b2)
+	       || is_rr (insn, op_bcr, &r1, &r2)
+	       || is_rx (insn, op_bc, &r1, &d2, &x2, &b2)
+	       || is_ril (insn, op1_brasl, op2_brasl, &r2, &i2))
+	break;
+
+      else
+	{
+	  /* An instruction we don't know how to simulate.  The only
+	     safe thing to do would be to set every value we're tracking
+	     to 'unknown'.  Instead, we'll be optimistic: we assume that
+	     we *can* interpret every instruction that the compiler uses
+	     to manipulate any of the data we're interested in here --
+	     then we can just ignore anything else.  */
+	}
+
+      /* Record the address after the last instruction that changed
+	 the FP, SP, or backlink.  Ignore instructions that changed
+	 them back to their original values --- those are probably
+	 restore instructions.  (The back chain is never restored,
+	 just popped.)  */
+      {
+	pv_t sp = data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
+	pv_t fp = data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
+
+	if ((! pv_is_identical (pre_insn_sp, sp)
+	     && ! pv_is_register_k (sp, S390_SP_REGNUM, 0)
+	     && sp.kind != pvk_unknown)
+	    || (! pv_is_identical (pre_insn_fp, fp)
+		&& ! pv_is_register_k (fp, S390_FRAME_REGNUM, 0)
+		&& fp.kind != pvk_unknown)
+	    || pre_insn_back_chain_saved_p != data->back_chain_saved_p)
+	  result = next_pc;
+      }
+    }
+
+  /* Record where all the registers were saved.  */
+  pv_area_scan (data->stack, s390_check_for_saved, data);
+
+  free_pv_area (data->stack);
+  data->stack = NULL;
+
+  return result;
+}
+
+/* Advance PC across any function entry prologue instructions to reach
+   some "real" code.  */
+
+static CORE_ADDR
+s390_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  struct s390_prologue_data data;
+  CORE_ADDR skip_pc, func_addr;
+
+  if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return std::max (pc, post_prologue_pc);
+    }
+
+  skip_pc = s390_analyze_prologue (gdbarch, pc, (CORE_ADDR)-1, &data);
+  return skip_pc ? skip_pc : pc;
+}
+
+
+/* Registers.  */
+
+/* ABI call-saved register information.  */
+
+int
+s390_register_call_saved (struct gdbarch *gdbarch, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  switch (tdep->abi)
+    {
+    case ABI_LINUX_S390:
+      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
+	  || regnum == S390_F4_REGNUM || regnum == S390_F6_REGNUM
+	  || regnum == S390_A0_REGNUM)
+	return 1;
+
+      break;
+
+    case ABI_LINUX_ZSERIES:
+      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
+	  || (regnum >= S390_F8_REGNUM && regnum <= S390_F15_REGNUM)
+	  || (regnum >= S390_A0_REGNUM && regnum <= S390_A1_REGNUM))
+	return 1;
+
+      break;
+    }
+
+  return 0;
+}
+
+/* Return the name of register REGNO.  Return the empty string for
+   registers that shouldn't be visible.  */
+
+static const char *
+s390_register_name (struct gdbarch *gdbarch, int regnum)
+{
+  if (regnum >= S390_V0_LOWER_REGNUM
+      && regnum <= S390_V15_LOWER_REGNUM)
+    return "";
+  return tdesc_register_name (gdbarch, regnum);
+}
+
+static int
+s390_cannot_store_register (struct gdbarch *gdbarch, int regnum)
+{
+  /* The last-break address is read-only.  */
+  return regnum == S390_LAST_BREAK_REGNUM;
+}
+
+static void
+s390_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  regcache_cooked_write_unsigned (regcache, tdep->pc_regnum, pc);
+
+  /* Set special SYSTEM_CALL register to 0 to prevent the kernel from
+     messing with the PC we just installed, if we happen to be within
+     an interrupted system call that the kernel wants to restart.
+
+     Note that after we return from the dummy call, the SYSTEM_CALL and
+     ORIG_R2 registers will be automatically restored, and the kernel
+     continues to restart the system call at this point.  */
+  if (register_size (gdbarch, S390_SYSTEM_CALL_REGNUM) > 0)
+    regcache_cooked_write_unsigned (regcache, S390_SYSTEM_CALL_REGNUM, 0);
+}
+
+/* DWARF Register Mapping.  */
+
+static const short s390_dwarf_regmap[] =
+{
+  /* 0-15: General Purpose Registers.  */
+  S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
+  S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
+  S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
+  S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
+
+  /* 16-31: Floating Point Registers / Vector Registers 0-15. */
+  S390_F0_REGNUM, S390_F2_REGNUM, S390_F4_REGNUM, S390_F6_REGNUM,
+  S390_F1_REGNUM, S390_F3_REGNUM, S390_F5_REGNUM, S390_F7_REGNUM,
+  S390_F8_REGNUM, S390_F10_REGNUM, S390_F12_REGNUM, S390_F14_REGNUM,
+  S390_F9_REGNUM, S390_F11_REGNUM, S390_F13_REGNUM, S390_F15_REGNUM,
+
+  /* 32-47: Control Registers (not mapped).  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+
+  /* 48-63: Access Registers.  */
+  S390_A0_REGNUM, S390_A1_REGNUM, S390_A2_REGNUM, S390_A3_REGNUM,
+  S390_A4_REGNUM, S390_A5_REGNUM, S390_A6_REGNUM, S390_A7_REGNUM,
+  S390_A8_REGNUM, S390_A9_REGNUM, S390_A10_REGNUM, S390_A11_REGNUM,
+  S390_A12_REGNUM, S390_A13_REGNUM, S390_A14_REGNUM, S390_A15_REGNUM,
+
+  /* 64-65: Program Status Word.  */
+  S390_PSWM_REGNUM,
+  S390_PSWA_REGNUM,
+
+  /* 66-67: Reserved.  */
+  -1, -1,
+
+  /* 68-83: Vector Registers 16-31.  */
+  S390_V16_REGNUM, S390_V18_REGNUM, S390_V20_REGNUM, S390_V22_REGNUM,
+  S390_V17_REGNUM, S390_V19_REGNUM, S390_V21_REGNUM, S390_V23_REGNUM,
+  S390_V24_REGNUM, S390_V26_REGNUM, S390_V28_REGNUM, S390_V30_REGNUM,
+  S390_V25_REGNUM, S390_V27_REGNUM, S390_V29_REGNUM, S390_V31_REGNUM,
+
+  /* End of "official" DWARF registers.  The remainder of the map is
+     for GDB internal use only.  */
+
+  /* GPR Lower Half Access.  */
+  S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
+  S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
+  S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
+  S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
+};
+
+enum { s390_dwarf_reg_r0l = ARRAY_SIZE (s390_dwarf_regmap) - 16 };
+
+/* Convert DWARF register number REG to the appropriate register
+   number used by GDB.  */
+
+static int
+s390_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int gdb_reg = -1;
+
+  /* In a 32-on-64 debug scenario, debug info refers to the full
+     64-bit GPRs.  Note that call frame information still refers to
+     the 32-bit lower halves, because s390_adjust_frame_regnum uses
+     special register numbers to access GPRs.  */
+  if (tdep->gpr_full_regnum != -1 && reg >= 0 && reg < 16)
+    return tdep->gpr_full_regnum + reg;
+
+  if (reg >= 0 && reg < ARRAY_SIZE (s390_dwarf_regmap))
+    gdb_reg = s390_dwarf_regmap[reg];
+
+  if (tdep->v0_full_regnum == -1)
+    {
+      if (gdb_reg >= S390_V16_REGNUM && gdb_reg <= S390_V31_REGNUM)
+	gdb_reg = -1;
+    }
+  else
+    {
+      if (gdb_reg >= S390_F0_REGNUM && gdb_reg <= S390_F15_REGNUM)
+	gdb_reg = gdb_reg - S390_F0_REGNUM + tdep->v0_full_regnum;
+    }
+
+  return gdb_reg;
+}
+
+static int
+regnum_is_gpr_full (struct gdbarch_tdep *tdep, int regnum)
+{
+  return (tdep->gpr_full_regnum != -1
+	  && regnum >= tdep->gpr_full_regnum
+	  && regnum <= tdep->gpr_full_regnum + 15);
+}
+
+/* Check whether REGNUM indicates a full vector register (v0-v15).
+   These pseudo-registers are composed of f0-f15 and v0l-v15l.  */
+
+static int
+regnum_is_vxr_full (struct gdbarch_tdep *tdep, int regnum)
+{
+  return (tdep->v0_full_regnum != -1
+	  && regnum >= tdep->v0_full_regnum
+	  && regnum <= tdep->v0_full_regnum + 15);
+}
+
+/* 'float' values are stored in the upper half of floating-point
+   registers, even though we are otherwise a big-endian platform.  The
+   same applies to a 'float' value within a vector.  */
+
+static struct value *
+s390_value_from_register (struct gdbarch *gdbarch, struct type *type,
+			  int regnum, struct frame_id frame_id)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct value *value = default_value_from_register (gdbarch, type,
+						     regnum, frame_id);
+  check_typedef (type);
+
+  if ((regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM
+       && TYPE_LENGTH (type) < 8)
+      || regnum_is_vxr_full (tdep, regnum)
+      || (regnum >= S390_V16_REGNUM && regnum <= S390_V31_REGNUM))
+    set_value_offset (value, 0);
+
+  return value;
+}
+
+static const struct target_desc *
+s390_core_read_description (struct gdbarch *gdbarch,
+			    struct target_ops *target, bfd *abfd)
+{
+  asection *section = bfd_get_section_by_name (abfd, ".reg");
+  CORE_ADDR hwcap = 0;
+  int high_gprs, v1, v2, te, vx;
+
+  target_auxv_search (target, AT_HWCAP, &hwcap);
+  if (!section)
+    return NULL;
+
+  high_gprs = (bfd_get_section_by_name (abfd, ".reg-s390-high-gprs")
+	       != NULL);
+  v1 = (bfd_get_section_by_name (abfd, ".reg-s390-last-break") != NULL);
+  v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL);
+  vx = (hwcap & HWCAP_S390_VX);
+  te = (hwcap & HWCAP_S390_TE);
+
+  switch (bfd_section_size (abfd, section))
+    {
+    case s390_sizeof_gregset:
+      if (high_gprs)
+	return (te && vx ? tdesc_s390_tevx_linux64 :
+		vx ? tdesc_s390_vx_linux64 :
+		te ? tdesc_s390_te_linux64 :
+		v2 ? tdesc_s390_linux64v2 :
+		v1 ? tdesc_s390_linux64v1 : tdesc_s390_linux64);
+      else
+	return (v2 ? tdesc_s390_linux32v2 :
+		v1 ? tdesc_s390_linux32v1 : tdesc_s390_linux32);
+
+    case s390x_sizeof_gregset:
+      return (te && vx ? tdesc_s390x_tevx_linux64 :
+	      vx ? tdesc_s390x_vx_linux64 :
+	      te ? tdesc_s390x_te_linux64 :
+	      v2 ? tdesc_s390x_linux64v2 :
+	      v1 ? tdesc_s390x_linux64v1 : tdesc_s390x_linux64);
+
+    default:
+      return NULL;
+    }
+}
+
+/* Iterate over supported core file register note sections. */
+
+static void
+s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
+				   iterate_over_regset_sections_cb *cb,
+				   void *cb_data,
+				   const struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  const int gregset_size = (tdep->abi == ABI_LINUX_S390 ?
+			    s390_sizeof_gregset : s390x_sizeof_gregset);
+
+  cb (".reg", gregset_size, &s390_gregset, NULL, cb_data);
+  cb (".reg2", s390_sizeof_fpregset, &s390_fpregset, NULL, cb_data);
+
+  if (tdep->abi == ABI_LINUX_S390 && tdep->gpr_full_regnum != -1)
+    cb (".reg-s390-high-gprs", 16 * 4, &s390_upper_regset,
+	"s390 GPR upper halves", cb_data);
+
+  if (tdep->have_linux_v1)
+    cb (".reg-s390-last-break", 8,
+	(gdbarch_ptr_bit (gdbarch) == 32
+	 ? &s390_last_break_regset : &s390x_last_break_regset),
+	"s390 last-break address", cb_data);
+
+  if (tdep->have_linux_v2)
+    cb (".reg-s390-system-call", 4, &s390_system_call_regset,
+	"s390 system-call", cb_data);
+
+  /* If regcache is set, we are in "write" (gcore) mode.  In this
+     case, don't iterate over the TDB unless its registers are
+     available.  */
+  if (tdep->have_tdb
+      && (regcache == NULL
+	  || REG_VALID == regcache_register_status (regcache,
+						    S390_TDB_DWORD0_REGNUM)))
+    cb (".reg-s390-tdb", s390_sizeof_tdbregset, &s390_tdb_regset,
+	"s390 TDB", cb_data);
+
+  if (tdep->v0_full_regnum != -1)
+    {
+      cb (".reg-s390-vxrs-low", 16 * 8, &s390_vxrs_low_regset,
+	  "s390 vector registers 0-15 lower half", cb_data);
+      cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
+	  "s390 vector registers 16-31", cb_data);
+    }
+}
+
+
+/* Pseudo registers.  */
+
+static const char *
+s390_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (regnum == tdep->pc_regnum)
+    return "pc";
+
+  if (regnum == tdep->cc_regnum)
+    return "cc";
+
+  if (regnum_is_gpr_full (tdep, regnum))
+    {
+      static const char *full_name[] = {
+	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+      };
+      return full_name[regnum - tdep->gpr_full_regnum];
+    }
+
+  if (regnum_is_vxr_full (tdep, regnum))
+    {
+      static const char *full_name[] = {
+	"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+	"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15"
+      };
+      return full_name[regnum - tdep->v0_full_regnum];
+    }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
+}
+
+static enum register_status
+s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
+			   int regnum, gdb_byte *buf)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int regsize = register_size (gdbarch, regnum);
+  ULONGEST val;
+
+  if (regnum == tdep->pc_regnum)
+    {
+      enum register_status status;
+
+      status = regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
+      if (status == REG_VALID)
+	{
+	  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	    val &= 0x7fffffff;
+	  store_unsigned_integer (buf, regsize, byte_order, val);
+	}
+      return status;
+    }
+
+  if (regnum == tdep->cc_regnum)
+    {
+      enum register_status status;
+
+      status = regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
+      if (status == REG_VALID)
+	{
+	  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	    val = (val >> 12) & 3;
+	  else
+	    val = (val >> 44) & 3;
+	  store_unsigned_integer (buf, regsize, byte_order, val);
+	}
+      return status;
+    }
+
+  if (regnum_is_gpr_full (tdep, regnum))
+    {
+      enum register_status status;
+      ULONGEST val_upper;
+
+      regnum -= tdep->gpr_full_regnum;
+
+      status = regcache_raw_read_unsigned (regcache,
+					   S390_R0_REGNUM + regnum, &val);
+      if (status == REG_VALID)
+	status = regcache_raw_read_unsigned (regcache,
+					     S390_R0_UPPER_REGNUM + regnum,
+					     &val_upper);
+      if (status == REG_VALID)
+	{
+	  val |= val_upper << 32;
+	  store_unsigned_integer (buf, regsize, byte_order, val);
+	}
+      return status;
+    }
+
+  if (regnum_is_vxr_full (tdep, regnum))
+    {
+      enum register_status status;
+
+      regnum -= tdep->v0_full_regnum;
+
+      status = regcache_raw_read (regcache, S390_F0_REGNUM + regnum, buf);
+      if (status == REG_VALID)
+	status = regcache_raw_read (regcache,
+				    S390_V0_LOWER_REGNUM + regnum, buf + 8);
+      return status;
+    }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
+}
+
+static void
+s390_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
+			    int regnum, const gdb_byte *buf)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int regsize = register_size (gdbarch, regnum);
+  ULONGEST val, psw;
+
+  if (regnum == tdep->pc_regnum)
+    {
+      val = extract_unsigned_integer (buf, regsize, byte_order);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	{
+	  regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &psw);
+	  val = (psw & 0x80000000) | (val & 0x7fffffff);
+	}
+      regcache_raw_write_unsigned (regcache, S390_PSWA_REGNUM, val);
+      return;
+    }
+
+  if (regnum == tdep->cc_regnum)
+    {
+      val = extract_unsigned_integer (buf, regsize, byte_order);
+      regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	val = (psw & ~((ULONGEST)3 << 12)) | ((val & 3) << 12);
+      else
+	val = (psw & ~((ULONGEST)3 << 44)) | ((val & 3) << 44);
+      regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, val);
+      return;
+    }
+
+  if (regnum_is_gpr_full (tdep, regnum))
+    {
+      regnum -= tdep->gpr_full_regnum;
+      val = extract_unsigned_integer (buf, regsize, byte_order);
+      regcache_raw_write_unsigned (regcache, S390_R0_REGNUM + regnum,
+				   val & 0xffffffff);
+      regcache_raw_write_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
+				   val >> 32);
+      return;
+    }
+
+  if (regnum_is_vxr_full (tdep, regnum))
+    {
+      regnum -= tdep->v0_full_regnum;
+      regcache_raw_write (regcache, S390_F0_REGNUM + regnum, buf);
+      regcache_raw_write (regcache, S390_V0_LOWER_REGNUM + regnum, buf + 8);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
+}
+
+static struct type *
+s390_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (regnum == tdep->pc_regnum)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+
+  if (regnum == tdep->cc_regnum)
+    return builtin_type (gdbarch)->builtin_int;
+
+  if (regnum_is_gpr_full (tdep, regnum))
+    return builtin_type (gdbarch)->builtin_uint64;
+
+  if (regnum_is_vxr_full (tdep, regnum))
+    return tdesc_find_type (gdbarch, "vec128");
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
+}
+
+/* Register groups.  */
+
+static int
+s390_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+				 struct reggroup *group)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  /* We usually save/restore the whole PSW, which includes PC and CC.
+     However, some older gdbservers may not support saving/restoring
+     the whole PSW yet, and will return an XML register description
+     excluding those from the save/restore register groups.  In those
+     cases, we still need to explicitly save/restore PC and CC in order
+     to push or pop frames.  Since this doesn't hurt anything if we
+     already save/restore the whole PSW (it's just redundant), we add
+     PC and CC at this point unconditionally.  */
+  if (group == save_reggroup || group == restore_reggroup)
+    return regnum == tdep->pc_regnum || regnum == tdep->cc_regnum;
+
+  if (group == vector_reggroup)
+    return regnum_is_vxr_full (tdep, regnum);
+
+  if (group == general_reggroup && regnum_is_vxr_full (tdep, regnum))
+    return 0;
+
+  return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+/* The "ax_pseudo_register_collect" gdbarch method.  */
+
+static int
+s390_ax_pseudo_register_collect (struct gdbarch *gdbarch,
+				 struct agent_expr *ax, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  if (regnum == tdep->pc_regnum)
+    {
+      ax_reg_mask (ax, S390_PSWA_REGNUM);
+    }
+  else if (regnum == tdep->cc_regnum)
+    {
+      ax_reg_mask (ax, S390_PSWM_REGNUM);
+    }
+  else if (regnum_is_gpr_full (tdep, regnum))
+    {
+      regnum -= tdep->gpr_full_regnum;
+      ax_reg_mask (ax, S390_R0_REGNUM + regnum);
+      ax_reg_mask (ax, S390_R0_UPPER_REGNUM + regnum);
+    }
+  else if (regnum_is_vxr_full (tdep, regnum))
+    {
+      regnum -= tdep->v0_full_regnum;
+      ax_reg_mask (ax, S390_F0_REGNUM + regnum);
+      ax_reg_mask (ax, S390_V0_LOWER_REGNUM + regnum);
+    }
+  else
+    {
+      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+    }
+  return 0;
+}
+
+/* The "ax_pseudo_register_push_stack" gdbarch method.  */
+
+static int
+s390_ax_pseudo_register_push_stack (struct gdbarch *gdbarch,
+				    struct agent_expr *ax, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  if (regnum == tdep->pc_regnum)
+    {
+      ax_reg (ax, S390_PSWA_REGNUM);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	{
+	  ax_zero_ext (ax, 31);
+	}
+    }
+  else if (regnum == tdep->cc_regnum)
+    {
+      ax_reg (ax, S390_PSWM_REGNUM);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	ax_const_l (ax, 12);
+      else
+	ax_const_l (ax, 44);
+      ax_simple (ax, aop_rsh_unsigned);
+      ax_zero_ext (ax, 2);
+    }
+  else if (regnum_is_gpr_full (tdep, regnum))
+    {
+      regnum -= tdep->gpr_full_regnum;
+      ax_reg (ax, S390_R0_REGNUM + regnum);
+      ax_reg (ax, S390_R0_UPPER_REGNUM + regnum);
+      ax_const_l (ax, 32);
+      ax_simple (ax, aop_lsh);
+      ax_simple (ax, aop_bit_or);
+    }
+  else if (regnum_is_vxr_full (tdep, regnum))
+    {
+      /* Too large to stuff on the stack.  */
+      return 1;
+    }
+  else
+    {
+      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+    }
+  return 0;
+}
+
+/* The "gen_return_address" gdbarch method.  Since this is supposed to be
+   just a best-effort method, and we don't really have the means to run
+   the full unwinder here, just collect the link register.  */
+
+static void
+s390_gen_return_address (struct gdbarch *gdbarch,
+			 struct agent_expr *ax, struct axs_value *value,
+			 CORE_ADDR scope)
+{
+  value->type = register_type (gdbarch, S390_R14_REGNUM);
+  value->kind = axs_lvalue_register;
+  value->u.reg = S390_R14_REGNUM;
+}
+
+/* Helper routine to unwind pseudo registers.  */
+
+struct value *
+s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct type *type = register_type (gdbarch, regnum);
+
+  /* Unwind PC via PSW address.  */
+  if (regnum == tdep->pc_regnum)
+    {
+      struct value *val;
+
+      val = frame_unwind_register_value (this_frame, S390_PSWA_REGNUM);
+      if (!value_optimized_out (val))
+	{
+	  LONGEST pswa = value_as_long (val);
+
+	  if (TYPE_LENGTH (type) == 4)
+	    return value_from_pointer (type, pswa & 0x7fffffff);
+	  else
+	    return value_from_pointer (type, pswa);
+	}
+    }
+
+  /* Unwind CC via PSW mask.  */
+  if (regnum == tdep->cc_regnum)
+    {
+      struct value *val;
+
+      val = frame_unwind_register_value (this_frame, S390_PSWM_REGNUM);
+      if (!value_optimized_out (val))
+	{
+	  LONGEST pswm = value_as_long (val);
+
+	  if (TYPE_LENGTH (type) == 4)
+	    return value_from_longest (type, (pswm >> 12) & 3);
+	  else
+	    return value_from_longest (type, (pswm >> 44) & 3);
+	}
+    }
+
+  /* Unwind full GPRs to show at least the lower halves (as the
+     upper halves are undefined).  */
+  if (regnum_is_gpr_full (tdep, regnum))
+    {
+      int reg = regnum - tdep->gpr_full_regnum;
+      struct value *val;
+
+      val = frame_unwind_register_value (this_frame, S390_R0_REGNUM + reg);
+      if (!value_optimized_out (val))
+	return value_cast (type, val);
+    }
+
+  return allocate_optimized_out_value (type);
+}
+
+
+/* Inferior function calls.  */
+
+/* Dummy function calls.  */
+
+/* Unwrap any single-field structs in TYPE and return the effective
+   "inner" type.  E.g., yield "float" for all these cases:
+
+     float x;
+     struct { float x };
+     struct { struct { float x; } x; };
+     struct { struct { struct { float x; } x; } x; };
+
+   However, if an inner type is smaller than MIN_SIZE, abort the
+   unwrapping.  */
+
+static struct type *
+s390_effective_inner_type (struct type *type, unsigned int min_size)
+{
+  while (TYPE_CODE (type) == TYPE_CODE_STRUCT
+	 && TYPE_NFIELDS (type) == 1)
+    {
+      struct type *inner = check_typedef (TYPE_FIELD_TYPE (type, 0));
+
+      if (TYPE_LENGTH (inner) < min_size)
+	break;
+      type = inner;
+    }
+
+  return type;
+}
+
+/* Return non-zero if TYPE should be passed like "float" or
+   "double".  */
+
+static int
+s390_function_arg_float (struct type *type)
+{
+  /* Note that long double as well as complex types are intentionally
+     excluded. */
+  if (TYPE_LENGTH (type) > 8)
+    return 0;
+
+  /* A struct containing just a float or double is passed like a float
+     or double.  */
+  type = s390_effective_inner_type (type, 0);
+
+  return (TYPE_CODE (type) == TYPE_CODE_FLT
+	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT);
+}
+
+/* Return non-zero if TYPE should be passed like a vector.  */
+
+static int
+s390_function_arg_vector (struct type *type)
+{
+  if (TYPE_LENGTH (type) > 16)
+    return 0;
+
+  /* Structs containing just a vector are passed like a vector.  */
+  type = s390_effective_inner_type (type, TYPE_LENGTH (type));
+
+  return TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type);
+}
+
+/* Determine whether N is a power of two.  */
+
+static int
+is_power_of_two (unsigned int n)
+{
+  return n && ((n & (n - 1)) == 0);
+}
+
+/* For an argument whose type is TYPE and which is not passed like a
+   float or vector, return non-zero if it should be passed like "int"
+   or "long long".  */
+
+static int
+s390_function_arg_integer (struct type *type)
+{
+  enum type_code code = TYPE_CODE (type);
+
+  if (TYPE_LENGTH (type) > 8)
+    return 0;
+
+  if (code == TYPE_CODE_INT
+      || code == TYPE_CODE_ENUM
+      || code == TYPE_CODE_RANGE
+      || code == TYPE_CODE_CHAR
+      || code == TYPE_CODE_BOOL
+      || code == TYPE_CODE_PTR
+      || TYPE_IS_REFERENCE (type))
+
+    return 1;
+
+  return ((code == TYPE_CODE_UNION || code == TYPE_CODE_STRUCT)
+	  && is_power_of_two (TYPE_LENGTH (type)));
+}
+
+/* Argument passing state: Internal data structure passed to helper
+   routines of s390_push_dummy_call.  */
+
+struct s390_arg_state
+  {
+    /* Register cache, or NULL, if we are in "preparation mode".  */
+    struct regcache *regcache;
+    /* Next available general/floating-point/vector register for
+       argument passing.  */
+    int gr, fr, vr;
+    /* Current pointer to copy area (grows downwards).  */
+    CORE_ADDR copy;
+    /* Current pointer to parameter area (grows upwards).  */
+    CORE_ADDR argp;
+  };
+
+/* Prepare one argument ARG for a dummy call and update the argument
+   passing state AS accordingly.  If the regcache field in AS is set,
+   operate in "write mode" and write ARG into the inferior.  Otherwise
+   run "preparation mode" and skip all updates to the inferior.  */
+
+static void
+s390_handle_arg (struct s390_arg_state *as, struct value *arg,
+		 struct gdbarch_tdep *tdep, int word_size,
+		 enum bfd_endian byte_order, int is_unnamed)
+{
+  struct type *type = check_typedef (value_type (arg));
+  unsigned int length = TYPE_LENGTH (type);
+  int write_mode = as->regcache != NULL;
+
+  if (s390_function_arg_float (type))
+    {
+      /* The GNU/Linux for S/390 ABI uses FPRs 0 and 2 to pass
+	 arguments.  The GNU/Linux for zSeries ABI uses 0, 2, 4, and
+	 6.  */
+      if (as->fr <= (tdep->abi == ABI_LINUX_S390 ? 2 : 6))
+	{
+	  /* When we store a single-precision value in an FP register,
+	     it occupies the leftmost bits.  */
+	  if (write_mode)
+	    regcache_cooked_write_part (as->regcache,
+					S390_F0_REGNUM + as->fr,
+					0, length,
+					value_contents (arg));
+	  as->fr += 2;
+	}
+      else
+	{
+	  /* When we store a single-precision value in a stack slot,
+	     it occupies the rightmost bits.  */
+	  as->argp = align_up (as->argp + length, word_size);
+	  if (write_mode)
+	    write_memory (as->argp - length, value_contents (arg),
+			  length);
+	}
+    }
+  else if (tdep->vector_abi == S390_VECTOR_ABI_128
+	   && s390_function_arg_vector (type))
+    {
+      static const char use_vr[] = {24, 26, 28, 30, 25, 27, 29, 31};
+
+      if (!is_unnamed && as->vr < ARRAY_SIZE (use_vr))
+	{
+	  int regnum = S390_V24_REGNUM + use_vr[as->vr] - 24;
+
+	  if (write_mode)
+	    regcache_cooked_write_part (as->regcache, regnum,
+					0, length,
+					value_contents (arg));
+	  as->vr++;
+	}
+      else
+	{
+	  if (write_mode)
+	    write_memory (as->argp, value_contents (arg), length);
+	  as->argp = align_up (as->argp + length, word_size);
+	}
+    }
+  else if (s390_function_arg_integer (type) && length <= word_size)
+    {
+      /* Initialize it just to avoid a GCC false warning.  */
+      ULONGEST val = 0;
+
+      if (write_mode)
+	{
+	  /* Place value in least significant bits of the register or
+	     memory word and sign- or zero-extend to full word size.
+	     This also applies to a struct or union.  */
+	  val = TYPE_UNSIGNED (type)
+	    ? extract_unsigned_integer (value_contents (arg),
+					length, byte_order)
+	    : extract_signed_integer (value_contents (arg),
+				      length, byte_order);
+	}
+
+      if (as->gr <= 6)
+	{
+	  if (write_mode)
+	    regcache_cooked_write_unsigned (as->regcache,
+					    S390_R0_REGNUM + as->gr,
+					    val);
+	  as->gr++;
+	}
+      else
+	{
+	  if (write_mode)
+	    write_memory_unsigned_integer (as->argp, word_size,
+					   byte_order, val);
+	  as->argp += word_size;
+	}
+    }
+  else if (s390_function_arg_integer (type) && length == 8)
+    {
+      if (as->gr <= 5)
+	{
+	  if (write_mode)
+	    {
+	      regcache_cooked_write (as->regcache,
+				     S390_R0_REGNUM + as->gr,
+				     value_contents (arg));
+	      regcache_cooked_write (as->regcache,
+				     S390_R0_REGNUM + as->gr + 1,
+				     value_contents (arg) + word_size);
+	    }
+	  as->gr += 2;
+	}
+      else
+	{
+	  /* If we skipped r6 because we couldn't fit a DOUBLE_ARG
+	     in it, then don't go back and use it again later.  */
+	  as->gr = 7;
+
+	  if (write_mode)
+	    write_memory (as->argp, value_contents (arg), length);
+	  as->argp += length;
+	}
+    }
+  else
+    {
+      /* This argument type is never passed in registers.  Place the
+	 value in the copy area and pass a pointer to it.  Use 8-byte
+	 alignment as a conservative assumption.  */
+      as->copy = align_down (as->copy - length, 8);
+      if (write_mode)
+	write_memory (as->copy, value_contents (arg), length);
+
+      if (as->gr <= 6)
+	{
+	  if (write_mode)
+	    regcache_cooked_write_unsigned (as->regcache,
+					    S390_R0_REGNUM + as->gr,
+					    as->copy);
+	  as->gr++;
+	}
+      else
+	{
+	  if (write_mode)
+	    write_memory_unsigned_integer (as->argp, word_size,
+					   byte_order, as->copy);
+	  as->argp += word_size;
+	}
+    }
+}
+
+/* Put the actual parameter values pointed to by ARGS[0..NARGS-1] in
+   place to be passed to a function, as specified by the "GNU/Linux
+   for S/390 ELF Application Binary Interface Supplement".
+
+   SP is the current stack pointer.  We must put arguments, links,
+   padding, etc. whereever they belong, and return the new stack
+   pointer value.
+
+   If STRUCT_RETURN is non-zero, then the function we're calling is
+   going to return a structure by value; STRUCT_ADDR is the address of
+   a block we've allocated for it on the stack.
+
+   Our caller has taken care of any type promotions needed to satisfy
+   prototypes or the old K&R argument-passing rules.  */
+
+static CORE_ADDR
+s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
+		      struct regcache *regcache, CORE_ADDR bp_addr,
+		      int nargs, struct value **args, CORE_ADDR sp,
+		      int struct_return, CORE_ADDR struct_addr)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int i;
+  struct s390_arg_state arg_state, arg_prep;
+  CORE_ADDR param_area_start, new_sp;
+  struct type *ftype = check_typedef (value_type (function));
+
+  if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
+    ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
+
+  arg_prep.copy = sp;
+  arg_prep.gr = struct_return ? 3 : 2;
+  arg_prep.fr = 0;
+  arg_prep.vr = 0;
+  arg_prep.argp = 0;
+  arg_prep.regcache = NULL;
+
+  /* Initialize arg_state for "preparation mode".  */
+  arg_state = arg_prep;
+
+  /* Update arg_state.copy with the start of the reference-to-copy area
+     and arg_state.argp with the size of the parameter area.  */
+  for (i = 0; i < nargs; i++)
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
+
+  param_area_start = align_down (arg_state.copy - arg_state.argp, 8);
+
+  /* Allocate the standard frame areas: the register save area, the
+     word reserved for the compiler, and the back chain pointer.  */
+  new_sp = param_area_start - (16 * word_size + 32);
+
+  /* Now we have the final stack pointer.  Make sure we didn't
+     underflow; on 31-bit, this would result in addresses with the
+     high bit set, which causes confusion elsewhere.  Note that if we
+     error out here, stack and registers remain untouched.  */
+  if (gdbarch_addr_bits_remove (gdbarch, new_sp) != new_sp)
+    error (_("Stack overflow"));
+
+  /* Pass the structure return address in general register 2.  */
+  if (struct_return)
+    regcache_cooked_write_unsigned (regcache, S390_R2_REGNUM, struct_addr);
+
+  /* Initialize arg_state for "write mode".  */
+  arg_state = arg_prep;
+  arg_state.argp = param_area_start;
+  arg_state.regcache = regcache;
+
+  /* Write all parameters.  */
+  for (i = 0; i < nargs; i++)
+    s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+		     TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
+
+  /* Store return PSWA.  In 31-bit mode, keep addressing mode bit.  */
+  if (word_size == 4)
+    {
+      ULONGEST pswa;
+      regcache_cooked_read_unsigned (regcache, S390_PSWA_REGNUM, &pswa);
+      bp_addr = (bp_addr & 0x7fffffff) | (pswa & 0x80000000);
+    }
+  regcache_cooked_write_unsigned (regcache, S390_RETADDR_REGNUM, bp_addr);
+
+  /* Store updated stack pointer.  */
+  regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, new_sp);
+
+  /* We need to return the 'stack part' of the frame ID,
+     which is actually the top of the register save area.  */
+  return param_area_start;
+}
+
+/* Assuming THIS_FRAME is a dummy, return the frame ID of that
+   dummy frame.  The frame ID's base needs to match the TOS value
+   returned by push_dummy_call, and the PC match the dummy frame's
+   breakpoint.  */
+
+static struct frame_id
+s390_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
+  sp = gdbarch_addr_bits_remove (gdbarch, sp);
+
+  return frame_id_build (sp + 16*word_size + 32,
+			 get_frame_pc (this_frame));
+}
+
+static CORE_ADDR
+s390_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  /* Both the 32- and 64-bit ABI's say that the stack pointer should
+     always be aligned on an eight-byte boundary.  */
+  return (addr & -8);
+}
+
+/* Helper for s390_return_value: Set or retrieve a function return
+   value if it resides in a register.  */
+
+static void
+s390_register_return_value (struct gdbarch *gdbarch, struct type *type,
+			    struct regcache *regcache,
+			    gdb_byte *out, const gdb_byte *in)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  int length = TYPE_LENGTH (type);
+  int code = TYPE_CODE (type);
+
+  if (code == TYPE_CODE_FLT || code == TYPE_CODE_DECFLOAT)
+    {
+      /* Float-like value: left-aligned in f0.  */
+      if (in != NULL)
+	regcache_cooked_write_part (regcache, S390_F0_REGNUM,
+				    0, length, in);
+      else
+	regcache_cooked_read_part (regcache, S390_F0_REGNUM,
+				   0, length, out);
+    }
+  else if (code == TYPE_CODE_ARRAY)
+    {
+      /* Vector: left-aligned in v24.  */
+      if (in != NULL)
+	regcache_cooked_write_part (regcache, S390_V24_REGNUM,
+				    0, length, in);
+      else
+	regcache_cooked_read_part (regcache, S390_V24_REGNUM,
+				   0, length, out);
+    }
+  else if (length <= word_size)
+    {
+      /* Integer: zero- or sign-extended in r2.  */
+      if (out != NULL)
+	regcache_cooked_read_part (regcache, S390_R2_REGNUM,
+				   word_size - length, length, out);
+      else if (TYPE_UNSIGNED (type))
+	regcache_cooked_write_unsigned
+	  (regcache, S390_R2_REGNUM,
+	   extract_unsigned_integer (in, length, byte_order));
+      else
+	regcache_cooked_write_signed
+	  (regcache, S390_R2_REGNUM,
+	   extract_signed_integer (in, length, byte_order));
+    }
+  else if (length == 2 * word_size)
+    {
+      /* Double word: in r2 and r3.  */
+      if (in != NULL)
+	{
+	  regcache_cooked_write (regcache, S390_R2_REGNUM, in);
+	  regcache_cooked_write (regcache, S390_R3_REGNUM,
+				 in + word_size);
+	}
+      else
+	{
+	  regcache_cooked_read (regcache, S390_R2_REGNUM, out);
+	  regcache_cooked_read (regcache, S390_R3_REGNUM,
+				out + word_size);
+	}
+    }
+  else
+    internal_error (__FILE__, __LINE__, _("invalid return type"));
+}
+
+/* Implement the 'return_value' gdbarch method.  */
+
+static enum return_value_convention
+s390_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *type, struct regcache *regcache,
+		   gdb_byte *out, const gdb_byte *in)
+{
+  enum return_value_convention rvc;
+
+  type = check_typedef (type);
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+    case TYPE_CODE_COMPLEX:
+      rvc = RETURN_VALUE_STRUCT_CONVENTION;
+      break;
+    case TYPE_CODE_ARRAY:
+      rvc = (gdbarch_tdep (gdbarch)->vector_abi == S390_VECTOR_ABI_128
+	     && TYPE_LENGTH (type) <= 16 && TYPE_VECTOR (type))
+	? RETURN_VALUE_REGISTER_CONVENTION
+	: RETURN_VALUE_STRUCT_CONVENTION;
+      break;
+    default:
+      rvc = TYPE_LENGTH (type) <= 8
+	? RETURN_VALUE_REGISTER_CONVENTION
+	: RETURN_VALUE_STRUCT_CONVENTION;
+    }
+
+  if (in != NULL || out != NULL)
+    {
+      if (rvc == RETURN_VALUE_REGISTER_CONVENTION)
+	s390_register_return_value (gdbarch, type, regcache, out, in);
+      else if (in != NULL)
+	error (_("Cannot set function return value."));
+      else
+	error (_("Function return value unknown."));
+    }
+
+  return rvc;
+}
+
+
+/* Frame handling.  */
+
+/* Implmement the stack_frame_destroyed_p gdbarch method.  */
+
+int
+s390_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+
+  /* In frameless functions, there's not frame to destroy and thus
+     we don't care about the epilogue.
+
+     In functions with frame, the epilogue sequence is a pair of
+     a LM-type instruction that restores (amongst others) the
+     return register %r14 and the stack pointer %r15, followed
+     by a branch 'br %r14' --or equivalent-- that effects the
+     actual return.
+
+     In that situation, this function needs to return 'true' in
+     exactly one case: when pc points to that branch instruction.
+
+     Thus we try to disassemble the one instructions immediately
+     preceding pc and check whether it is an LM-type instruction
+     modifying the stack pointer.
+
+     Note that disassembling backwards is not reliable, so there
+     is a slight chance of false positives here ...  */
+
+  bfd_byte insn[6];
+  unsigned int r1, r3, b2;
+  int d2;
+
+  if (word_size == 4
+      && !target_read_memory (pc - 4, insn, 4)
+      && is_rs (insn, op_lm, &r1, &r3, &d2, &b2)
+      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
+    return 1;
+
+  if (word_size == 4
+      && !target_read_memory (pc - 6, insn, 6)
+      && is_rsy (insn, op1_lmy, op2_lmy, &r1, &r3, &d2, &b2)
+      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
+    return 1;
+
+  if (word_size == 8
+      && !target_read_memory (pc - 6, insn, 6)
+      && is_rsy (insn, op1_lmg, op2_lmg, &r1, &r3, &d2, &b2)
+      && r3 == S390_SP_REGNUM - S390_R0_REGNUM)
+    return 1;
+
+  return 0;
+}
+
+/* DWARF-2 frame support.  */
+
+static struct value *
+s390_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
+			   int regnum)
+{
+  return s390_unwind_pseudo_register (this_frame, regnum);
+}
+
+static void
+s390_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+			    struct dwarf2_frame_state_reg *reg,
+			    struct frame_info *this_frame)
+{
+  /* The condition code (and thus PSW mask) is call-clobbered.  */
+  if (regnum == S390_PSWM_REGNUM)
+    reg->how = DWARF2_FRAME_REG_UNDEFINED;
+
+  /* The PSW address unwinds to the return address.  */
+  else if (regnum == S390_PSWA_REGNUM)
+    reg->how = DWARF2_FRAME_REG_RA;
+
+  /* Fixed registers are call-saved or call-clobbered
+     depending on the ABI in use.  */
+  else if (regnum < S390_NUM_REGS)
+    {
+      if (s390_register_call_saved (gdbarch, regnum))
+	reg->how = DWARF2_FRAME_REG_SAME_VALUE;
+      else
+	reg->how = DWARF2_FRAME_REG_UNDEFINED;
+    }
+
+  /* We install a special function to unwind pseudos.  */
+  else
+    {
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = s390_dwarf2_prev_register;
+    }
+}
+
+/* Translate a .eh_frame register to DWARF register, or adjust a
+   .debug_frame register.  */
+
+static int
+s390_adjust_frame_regnum (struct gdbarch *gdbarch, int num, int eh_frame_p)
+{
+  /* See s390_dwarf_reg_to_regnum for comments.  */
+  return (num >= 0 && num < 16) ? num + s390_dwarf_reg_r0l : num;
+}
+
+static CORE_ADDR
+s390_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  ULONGEST pc;
+  pc = frame_unwind_register_unsigned (next_frame, tdep->pc_regnum);
+  return gdbarch_addr_bits_remove (gdbarch, pc);
+}
+
+static CORE_ADDR
+s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  ULONGEST sp;
+  sp = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
+  return gdbarch_addr_bits_remove (gdbarch, sp);
+}
+
+
+/* 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 = (gdb_byte *) 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
+s390_displaced_step_fixup (struct gdbarch *gdbarch,
+			   struct displaced_step_closure *closure,
+			   CORE_ADDR from, CORE_ADDR to,
+			   struct regcache *regs)
+{
+  /* 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];
+
+  /* Fields for various kinds of instructions.  */
+  unsigned int b2, r1, r2, x2, r3;
+  int i2, d2;
+
+  /* Get current PC and addressing mode bit.  */
+  CORE_ADDR pc = regcache_read_pc (regs);
+  ULONGEST amode = 0;
+
+  if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+    {
+      regcache_cooked_read_unsigned (regs, S390_PSWA_REGNUM, &amode);
+      amode &= 0x80000000;
+    }
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: (s390) fixup (%s, %s) pc %s len %d amode 0x%x\n",
+			paddress (gdbarch, from), paddress (gdbarch, to),
+			paddress (gdbarch, pc), insnlen, (int) amode);
+
+  /* Handle absolute branch and save instructions.  */
+  if (is_rr (insn, op_basr, &r1, &r2)
+      || is_rx (insn, op_bas, &r1, &d2, &x2, &b2))
+    {
+      /* Recompute saved return address in R1.  */
+      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
+				      amode | (from + insnlen));
+    }
+
+  /* Handle absolute branch instructions.  */
+  else if (is_rr (insn, op_bcr, &r1, &r2)
+	   || is_rx (insn, op_bc, &r1, &d2, &x2, &b2)
+	   || is_rr (insn, op_bctr, &r1, &r2)
+	   || is_rre (insn, op_bctgr, &r1, &r2)
+	   || is_rx (insn, op_bct, &r1, &d2, &x2, &b2)
+	   || is_rxy (insn, op1_bctg, op2_brctg, &r1, &d2, &x2, &b2)
+	   || is_rs (insn, op_bxh, &r1, &r3, &d2, &b2)
+	   || is_rsy (insn, op1_bxhg, op2_bxhg, &r1, &r3, &d2, &b2)
+	   || is_rs (insn, op_bxle, &r1, &r3, &d2, &b2)
+	   || is_rsy (insn, op1_bxleg, op2_bxleg, &r1, &r3, &d2, &b2))
+    {
+      /* Update PC iff branch was *not* taken.  */
+      if (pc == to + insnlen)
+	regcache_write_pc (regs, from + insnlen);
+    }
+
+  /* Handle PC-relative branch and save instructions.  */
+  else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2)
+	   || is_ril (insn, op1_brasl, op2_brasl, &r1, &i2))
+    {
+      /* Update PC.  */
+      regcache_write_pc (regs, pc - to + from);
+      /* Recompute saved return address in R1.  */
+      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
+				      amode | (from + insnlen));
+    }
+
+  /* Handle LOAD ADDRESS RELATIVE LONG.  */
+  else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
+    {
+      /* Update PC.  */
+      regcache_write_pc (regs, from + insnlen);
+      /* Recompute output address in R1.  */
+      regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
+				      amode | (from + i2 * 2));
+    }
+
+  /* If we executed a breakpoint instruction, point PC right back at it.  */
+  else if (insn[0] == 0x0 && insn[1] == 0x1)
+    regcache_write_pc (regs, from);
+
+  /* For any other insn, adjust PC by negated displacement.  PC then
+     points right after the original instruction, except for PC-relative
+     branches, where it points to the adjusted branch target.  */
+
+  else
+    regcache_write_pc (regs, pc - to + from);
+
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+			"displaced: (s390) pc is now %s\n",
+			paddress (gdbarch, regcache_read_pc (regs)));
+}
+
+static int
+s390_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
+				   struct displaced_step_closure *closure)
+{
+  return 1;
+}
+
+
+/* Address handling.  */
+
+static CORE_ADDR
+s390_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return addr & 0x7fffffff;
+}
+
+static int
+s390_address_class_type_flags (int byte_size, int dwarf2_addr_class)
+{
+  if (byte_size == 4)
+    return TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
+  else
+    return 0;
+}
+
+static const char *
+s390_address_class_type_flags_to_name (struct gdbarch *gdbarch, int type_flags)
+{
+  if (type_flags & TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1)
+    return "mode32";
+  else
+    return NULL;
+}
+
+static int
+s390_address_class_name_to_type_flags (struct gdbarch *gdbarch,
+				       const char *name,
+				       int *type_flags_ptr)
+{
+  if (strcmp (name, "mode32") == 0)
+    {
+      *type_flags_ptr = TYPE_INSTANCE_FLAG_ADDRESS_CLASS_1;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+/* Set up gdbarch struct.  */
+
+static struct gdbarch *
+s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  const struct target_desc *tdesc = info.target_desc;
+  struct tdesc_arch_data *tdesc_data = NULL;
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+  enum s390_abi_kind tdep_abi;
+  enum s390_vector_abi_kind vector_abi;
+  int have_upper = 0;
+  int have_linux_v1 = 0;
+  int have_linux_v2 = 0;
+  int have_tdb = 0;
+  int have_vx = 0;
+  int first_pseudo_reg, last_pseudo_reg;
+  static const char *const stap_register_prefixes[] = { "%", NULL };
+  static const char *const stap_register_indirection_prefixes[] = { "(",
+								    NULL };
+  static const char *const stap_register_indirection_suffixes[] = { ")",
+								    NULL };
+
+  /* Default ABI and register size.  */
+  switch (info.bfd_arch_info->mach)
+    {
+    case bfd_mach_s390_31:
+      tdep_abi = ABI_LINUX_S390;
+      break;
+
+    case bfd_mach_s390_64:
+      tdep_abi = ABI_LINUX_ZSERIES;
+      break;
+
+    default:
+      return NULL;
+    }
+
+  /* Use default target description if none provided by the target.  */
+  if (!tdesc_has_registers (tdesc))
+    {
+      if (tdep_abi == ABI_LINUX_S390)
+	tdesc = tdesc_s390_linux32;
+      else
+	tdesc = tdesc_s390x_linux64;
+    }
+
+  /* Check any target description for validity.  */
+  if (tdesc_has_registers (tdesc))
+    {
+      static const char *const gprs[] = {
+	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+      };
+      static const char *const fprs[] = {
+	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15"
+      };
+      static const char *const acrs[] = {
+	"acr0", "acr1", "acr2", "acr3", "acr4", "acr5", "acr6", "acr7",
+	"acr8", "acr9", "acr10", "acr11", "acr12", "acr13", "acr14", "acr15"
+      };
+      static const char *const gprs_lower[] = {
+	"r0l", "r1l", "r2l", "r3l", "r4l", "r5l", "r6l", "r7l",
+	"r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
+      };
+      static const char *const gprs_upper[] = {
+	"r0h", "r1h", "r2h", "r3h", "r4h", "r5h", "r6h", "r7h",
+	"r8h", "r9h", "r10h", "r11h", "r12h", "r13h", "r14h", "r15h"
+      };
+      static const char *const tdb_regs[] = {
+	"tdb0", "tac", "tct", "atia",
+	"tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7",
+	"tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15"
+      };
+      static const char *const vxrs_low[] = {
+	"v0l", "v1l", "v2l", "v3l", "v4l", "v5l", "v6l", "v7l", "v8l",
+	"v9l", "v10l", "v11l", "v12l", "v13l", "v14l", "v15l",
+      };
+      static const char *const vxrs_high[] = {
+	"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24",
+	"v25", "v26", "v27", "v28", "v29", "v30", "v31",
+      };
+      static const char *const cr[] = {
+	"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+	"cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15"
+      };
+      const struct tdesc_feature *feature;
+      int i, valid_p = 1;
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.core");
+      if (feature == NULL)
+	return NULL;
+
+      tdesc_data = tdesc_data_alloc ();
+
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_PSWM_REGNUM, "pswm");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_PSWA_REGNUM, "pswa");
+
+      if (tdesc_unnumbered_register (feature, "r0"))
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_REGNUM + i, gprs[i]);
+	}
+      else
+	{
+	  have_upper = 1;
+
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_REGNUM + i,
+						gprs_lower[i]);
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_UPPER_REGNUM + i,
+						gprs_upper[i]);
+	}
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.fpr");
+      if (feature == NULL)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
+
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_FPC_REGNUM, "fpc");
+      for (i = 0; i < 16; i++)
+	valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					    S390_F0_REGNUM + i, fprs[i]);
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.acr");
+      if (feature == NULL)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
+
+      for (i = 0; i < 16; i++)
+	valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					    S390_A0_REGNUM + i, acrs[i]);
+
+      /* Optional GNU/Linux-specific "registers".  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.linux");
+      if (feature)
+	{
+	  tdesc_numbered_register (feature, tdesc_data,
+				   S390_ORIG_R2_REGNUM, "orig_r2");
+
+	  if (tdesc_numbered_register (feature, tdesc_data,
+				       S390_LAST_BREAK_REGNUM, "last_break"))
+	    have_linux_v1 = 1;
+
+	  if (tdesc_numbered_register (feature, tdesc_data,
+				       S390_SYSTEM_CALL_REGNUM, "system_call"))
+	    have_linux_v2 = 1;
+
+	  if (have_linux_v2 > have_linux_v1)
+	    valid_p = 0;
+	}
+
+      /* Transaction diagnostic block.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.tdb");
+      if (feature)
+	{
+	  for (i = 0; i < ARRAY_SIZE (tdb_regs); i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_TDB_DWORD0_REGNUM + i,
+						tdb_regs[i]);
+	  have_tdb = 1;
+	}
+
+      /* Vector registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.vx");
+      if (feature)
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_V0_LOWER_REGNUM + i,
+						vxrs_low[i]);
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_V16_REGNUM + i,
+						vxrs_high[i]);
+	  have_vx = 1;
+	}
+
+      if (!valid_p)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
+    }
+
+  /* Determine vector ABI.  */
+  vector_abi = S390_VECTOR_ABI_NONE;
+#ifdef HAVE_ELF
+  if (have_vx
+      && info.abfd != NULL
+      && info.abfd->format == bfd_object
+      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour
+      && bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
+				   Tag_GNU_S390_ABI_Vector) == 2)
+    vector_abi = S390_VECTOR_ABI_128;
+#endif
+
+  /* Find a candidate among extant architectures.  */
+  for (arches = gdbarch_list_lookup_by_info (arches, &info);
+       arches != NULL;
+       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+    {
+      tdep = gdbarch_tdep (arches->gdbarch);
+      if (!tdep)
+	continue;
+      if (tdep->abi != tdep_abi)
+	continue;
+      if (tdep->vector_abi != vector_abi)
+	continue;
+      if ((tdep->gpr_full_regnum != -1) != have_upper)
+	continue;
+      if (tdesc_data != NULL)
+	tdesc_data_cleanup (tdesc_data);
+
+      return arches->gdbarch;
+    }
+
+  /* Otherwise create a new gdbarch for the specified machine type.  */
+  tdep = XCNEW (struct gdbarch_tdep);
+  tdep->abi = tdep_abi;
+  tdep->vector_abi = vector_abi;
+  tdep->have_linux_v1 = have_linux_v1;
+  tdep->have_linux_v2 = have_linux_v2;
+  tdep->have_tdb = have_tdb;
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_believe_pcc_promotion (gdbarch, 0);
+  set_gdbarch_char_signed (gdbarch, 0);
+
+  /* S/390 GNU/Linux uses either 64-bit or 128-bit long doubles.
+     We can safely let them default to 128-bit, since the debug info
+     will give the size of type actually used in each case.  */
+  set_gdbarch_long_double_bit (gdbarch, 128);
+  set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
+
+  /* Amount PC must be decremented by after a breakpoint.  This is
+     often the number of bytes returned by gdbarch_breakpoint_from_pc but not
+     always.  */
+  set_gdbarch_decr_pc_after_break (gdbarch, 2);
+
+  /* Breakpoints.  */
+  set_gdbarch_breakpoint_kind_from_pc (gdbarch, s390_breakpoint::kind_from_pc);
+  set_gdbarch_sw_breakpoint_from_kind (gdbarch, s390_breakpoint::bp_from_kind);
+  set_gdbarch_software_single_step (gdbarch, s390_software_single_step);
+  set_gdbarch_skip_prologue (gdbarch, s390_skip_prologue);
+
+  /* Registers.  */
+  set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
+  set_gdbarch_fp0_regnum (gdbarch, S390_F0_REGNUM);
+  set_gdbarch_register_name (gdbarch, s390_register_name);
+  set_gdbarch_cannot_store_register (gdbarch, s390_cannot_store_register);
+  set_gdbarch_write_pc (gdbarch, s390_write_pc);
+  set_gdbarch_stab_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
+  set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
+  set_gdbarch_value_from_register (gdbarch, s390_value_from_register);
+  set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
+  set_gdbarch_iterate_over_regset_sections (gdbarch,
+					    s390_iterate_over_regset_sections);
+
+  /* Pseudo registers.  */
+  set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
+  set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
+  set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
+  set_tdesc_pseudo_register_type (gdbarch, s390_pseudo_register_type);
+  set_tdesc_pseudo_register_reggroup_p (gdbarch,
+					s390_pseudo_register_reggroup_p);
+  set_gdbarch_ax_pseudo_register_collect (gdbarch,
+					  s390_ax_pseudo_register_collect);
+  set_gdbarch_ax_pseudo_register_push_stack (gdbarch,
+					     s390_ax_pseudo_register_push_stack);
+  set_gdbarch_gen_return_address (gdbarch, s390_gen_return_address);
+  tdesc_use_registers (gdbarch, tdesc, tdesc_data);
+
+  /* Assign pseudo register numbers.  */
+  first_pseudo_reg = gdbarch_num_regs (gdbarch);
+  last_pseudo_reg = first_pseudo_reg;
+  tdep->gpr_full_regnum = -1;
+  if (have_upper)
+    {
+      tdep->gpr_full_regnum = last_pseudo_reg;
+      last_pseudo_reg += 16;
+    }
+  tdep->v0_full_regnum = -1;
+  if (have_vx)
+    {
+      tdep->v0_full_regnum = last_pseudo_reg;
+      last_pseudo_reg += 16;
+    }
+  tdep->pc_regnum = last_pseudo_reg++;
+  tdep->cc_regnum = last_pseudo_reg++;
+  set_gdbarch_pc_regnum (gdbarch, tdep->pc_regnum);
+  set_gdbarch_num_pseudo_regs (gdbarch, last_pseudo_reg - first_pseudo_reg);
+
+  /* Inferior function calls.  */
+  set_gdbarch_push_dummy_call (gdbarch, s390_push_dummy_call);
+  set_gdbarch_dummy_id (gdbarch, s390_dummy_id);
+  set_gdbarch_frame_align (gdbarch, s390_frame_align);
+  set_gdbarch_return_value (gdbarch, s390_return_value);
+
+  /* Frame handling.  */
+  /* Stack grows downward.  */
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_stack_frame_destroyed_p (gdbarch, s390_stack_frame_destroyed_p);
+  dwarf2_frame_set_init_reg (gdbarch, s390_dwarf2_frame_init_reg);
+  dwarf2_frame_set_adjust_regnum (gdbarch, s390_adjust_frame_regnum);
+  dwarf2_append_unwinders (gdbarch);
+  frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
+  set_gdbarch_unwind_pc (gdbarch, s390_unwind_pc);
+  set_gdbarch_unwind_sp (gdbarch, s390_unwind_sp);
+
+  /* Displaced stepping.  */
+  set_gdbarch_displaced_step_copy_insn (gdbarch,
+					s390_displaced_step_copy_insn);
+  set_gdbarch_displaced_step_fixup (gdbarch, s390_displaced_step_fixup);
+  set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
+  set_gdbarch_displaced_step_hw_singlestep (gdbarch,
+					    s390_displaced_step_hw_singlestep);
+  set_gdbarch_max_insn_length (gdbarch, S390_MAX_INSTR_SIZE);
+
+  switch (tdep->abi)
+    {
+    case ABI_LINUX_S390:
+      set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
+      break;
+
+    case ABI_LINUX_ZSERIES:
+      set_gdbarch_long_bit (gdbarch, 64);
+      set_gdbarch_long_long_bit (gdbarch, 64);
+      set_gdbarch_ptr_bit (gdbarch, 64);
+      set_gdbarch_address_class_type_flags (gdbarch,
+					    s390_address_class_type_flags);
+      set_gdbarch_address_class_type_flags_to_name (gdbarch,
+						    s390_address_class_type_flags_to_name);
+      set_gdbarch_address_class_name_to_type_flags (gdbarch,
+						    s390_address_class_name_to_type_flags);
+      break;
+    }
+
+  /* SystemTap functions.  */
+  set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes);
+  set_gdbarch_stap_register_indirection_prefixes (gdbarch,
+						  stap_register_indirection_prefixes);
+  set_gdbarch_stap_register_indirection_suffixes (gdbarch,
+						  stap_register_indirection_suffixes);
+
+  set_gdbarch_disassembler_options (gdbarch, &s390_disassembler_options);
+  set_gdbarch_valid_disassembler_options (gdbarch,
+					  disassembler_options_s390 ());
+
+  /* Note that GNU/Linux is the only OS supported on this platform.  */
+  s390_linux_gdbarch_init (info, gdbarch);
+
+  return gdbarch;
+}
+
+
+extern initialize_file_ftype _initialize_s390_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_s390_tdep (void)
+{
+  /* Hook us into the gdbarch mechanism.  */
+  register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
+}
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
new file mode 100644
index 0000000000..d586237dc7
--- /dev/null
+++ b/gdb/s390-tdep.h
@@ -0,0 +1,416 @@
+/* Target-dependent code for S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef S390_TDEP_H
+#define S390_TDEP_H
+
+#include "prologue-value.h"
+
+enum s390_abi_kind
+{
+  ABI_LINUX_S390,
+  ABI_LINUX_ZSERIES
+};
+
+enum s390_vector_abi_kind
+{
+  S390_VECTOR_ABI_NONE,
+  S390_VECTOR_ABI_128
+};
+
+/* The tdep structure.  */
+
+struct gdbarch_tdep
+{
+  /* ABI version.  */
+  enum s390_abi_kind abi;
+
+  /* Vector ABI.  */
+  enum s390_vector_abi_kind vector_abi;
+
+  /* Pseudo register numbers.  */
+  int gpr_full_regnum;
+  int pc_regnum;
+  int cc_regnum;
+  int v0_full_regnum;
+
+  int have_linux_v1;
+  int have_linux_v2;
+  int have_tdb;
+};
+
+/* Named opcode values for the S/390 instructions we recognize.  Some
+   instructions have their opcode split across two fields; those are the
+   op1_* and op2_* enums.  */
+
+enum
+  {
+    op1_lhi  = 0xa7,   op2_lhi  = 0x08,
+    op1_lghi = 0xa7,   op2_lghi = 0x09,
+    op1_lgfi = 0xc0,   op2_lgfi = 0x01,
+    op_lr    = 0x18,
+    op_lgr   = 0xb904,
+    op_l     = 0x58,
+    op1_ly   = 0xe3,   op2_ly   = 0x58,
+    op1_lg   = 0xe3,   op2_lg   = 0x04,
+    op_lm    = 0x98,
+    op1_lmy  = 0xeb,   op2_lmy  = 0x98,
+    op1_lmg  = 0xeb,   op2_lmg  = 0x04,
+    op_st    = 0x50,
+    op1_sty  = 0xe3,   op2_sty  = 0x50,
+    op1_stg  = 0xe3,   op2_stg  = 0x24,
+    op_std   = 0x60,
+    op_stm   = 0x90,
+    op1_stmy = 0xeb,   op2_stmy = 0x90,
+    op1_stmg = 0xeb,   op2_stmg = 0x24,
+    op1_aghi = 0xa7,   op2_aghi = 0x0b,
+    op1_ahi  = 0xa7,   op2_ahi  = 0x0a,
+    op1_agfi = 0xc2,   op2_agfi = 0x08,
+    op1_afi  = 0xc2,   op2_afi  = 0x09,
+    op1_algfi= 0xc2,   op2_algfi= 0x0a,
+    op1_alfi = 0xc2,   op2_alfi = 0x0b,
+    op_ar    = 0x1a,
+    op_agr   = 0xb908,
+    op_a     = 0x5a,
+    op1_ay   = 0xe3,   op2_ay   = 0x5a,
+    op1_ag   = 0xe3,   op2_ag   = 0x08,
+    op1_slgfi= 0xc2,   op2_slgfi= 0x04,
+    op1_slfi = 0xc2,   op2_slfi = 0x05,
+    op_sr    = 0x1b,
+    op_sgr   = 0xb909,
+    op_s     = 0x5b,
+    op1_sy   = 0xe3,   op2_sy   = 0x5b,
+    op1_sg   = 0xe3,   op2_sg   = 0x09,
+    op_nr    = 0x14,
+    op_ngr   = 0xb980,
+    op_la    = 0x41,
+    op1_lay  = 0xe3,   op2_lay  = 0x71,
+    op1_larl = 0xc0,   op2_larl = 0x00,
+    op_basr  = 0x0d,
+    op_bas   = 0x4d,
+    op_bcr   = 0x07,
+    op_bc    = 0x0d,
+    op_bctr  = 0x06,
+    op_bctgr = 0xb946,
+    op_bct   = 0x46,
+    op1_bctg = 0xe3,   op2_bctg = 0x46,
+    op_bxh   = 0x86,
+    op1_bxhg = 0xeb,   op2_bxhg = 0x44,
+    op_bxle  = 0x87,
+    op1_bxleg= 0xeb,   op2_bxleg= 0x45,
+    op1_bras = 0xa7,   op2_bras = 0x05,
+    op1_brasl= 0xc0,   op2_brasl= 0x05,
+    op1_brc  = 0xa7,   op2_brc  = 0x04,
+    op1_brcl = 0xc0,   op2_brcl = 0x04,
+    op1_brct = 0xa7,   op2_brct = 0x06,
+    op1_brctg= 0xa7,   op2_brctg= 0x07,
+    op_brxh  = 0x84,
+    op1_brxhg= 0xec,   op2_brxhg= 0x44,
+    op_brxle = 0x85,
+    op1_brxlg= 0xec,   op2_brxlg= 0x45,
+    op_svc   = 0x0a,
+  };
+
+/* Hardware capabilities. */
+
+#ifndef HWCAP_S390_HIGH_GPRS
+#define HWCAP_S390_HIGH_GPRS 512
+#endif
+
+#ifndef HWCAP_S390_TE
+#define HWCAP_S390_TE 1024
+#endif
+
+#ifndef HWCAP_S390_VX
+#define HWCAP_S390_VX 2048
+#endif
+
+/* Register information.  */
+
+/* Program Status Word.  */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers.  */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers.  */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word.  */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers.  */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* General Purpose Register Upper Halves.  */
+#define S390_R0_UPPER_REGNUM 51
+#define S390_R1_UPPER_REGNUM 52
+#define S390_R2_UPPER_REGNUM 53
+#define S390_R3_UPPER_REGNUM 54
+#define S390_R4_UPPER_REGNUM 55
+#define S390_R5_UPPER_REGNUM 56
+#define S390_R6_UPPER_REGNUM 57
+#define S390_R7_UPPER_REGNUM 58
+#define S390_R8_UPPER_REGNUM 59
+#define S390_R9_UPPER_REGNUM 60
+#define S390_R10_UPPER_REGNUM 61
+#define S390_R11_UPPER_REGNUM 62
+#define S390_R12_UPPER_REGNUM 63
+#define S390_R13_UPPER_REGNUM 64
+#define S390_R14_UPPER_REGNUM 65
+#define S390_R15_UPPER_REGNUM 66
+/* GNU/Linux-specific optional registers.  */
+#define S390_ORIG_R2_REGNUM 67
+#define S390_LAST_BREAK_REGNUM 68
+#define S390_SYSTEM_CALL_REGNUM 69
+/* Transaction diagnostic block.  */
+#define S390_TDB_DWORD0_REGNUM 70
+#define S390_TDB_ABORT_CODE_REGNUM 71
+#define S390_TDB_CONFLICT_TOKEN_REGNUM 72
+#define S390_TDB_ATIA_REGNUM 73
+#define S390_TDB_R0_REGNUM 74
+#define S390_TDB_R1_REGNUM 75
+#define S390_TDB_R2_REGNUM 76
+#define S390_TDB_R3_REGNUM 77
+#define S390_TDB_R4_REGNUM 78
+#define S390_TDB_R5_REGNUM 79
+#define S390_TDB_R6_REGNUM 80
+#define S390_TDB_R7_REGNUM 81
+#define S390_TDB_R8_REGNUM 82
+#define S390_TDB_R9_REGNUM 83
+#define S390_TDB_R10_REGNUM 84
+#define S390_TDB_R11_REGNUM 85
+#define S390_TDB_R12_REGNUM 86
+#define S390_TDB_R13_REGNUM 87
+#define S390_TDB_R14_REGNUM 88
+#define S390_TDB_R15_REGNUM 89
+/* Vector registers.  */
+#define S390_V0_LOWER_REGNUM 90
+#define S390_V1_LOWER_REGNUM 91
+#define S390_V2_LOWER_REGNUM 92
+#define S390_V3_LOWER_REGNUM 93
+#define S390_V4_LOWER_REGNUM 94
+#define S390_V5_LOWER_REGNUM 95
+#define S390_V6_LOWER_REGNUM 96
+#define S390_V7_LOWER_REGNUM 97
+#define S390_V8_LOWER_REGNUM 98
+#define S390_V9_LOWER_REGNUM 99
+#define S390_V10_LOWER_REGNUM 100
+#define S390_V11_LOWER_REGNUM 101
+#define S390_V12_LOWER_REGNUM 102
+#define S390_V13_LOWER_REGNUM 103
+#define S390_V14_LOWER_REGNUM 104
+#define S390_V15_LOWER_REGNUM 105
+#define S390_V16_REGNUM 106
+#define S390_V17_REGNUM 107
+#define S390_V18_REGNUM 108
+#define S390_V19_REGNUM 109
+#define S390_V20_REGNUM 110
+#define S390_V21_REGNUM 111
+#define S390_V22_REGNUM 112
+#define S390_V23_REGNUM 113
+#define S390_V24_REGNUM 114
+#define S390_V25_REGNUM 115
+#define S390_V26_REGNUM 116
+#define S390_V27_REGNUM 117
+#define S390_V28_REGNUM 118
+#define S390_V29_REGNUM 119
+#define S390_V30_REGNUM 120
+#define S390_V31_REGNUM 121
+
+/* Total.  */
+#define S390_NUM_REGS 122
+#define S390_NUM_GPRS 16
+#define S390_NUM_FPRS 16
+
+#define S390_MAX_INSTR_SIZE 6
+
+/* Special register usage.  */
+#define S390_SP_REGNUM S390_R15_REGNUM
+#define S390_RETADDR_REGNUM S390_R14_REGNUM
+#define S390_FRAME_REGNUM S390_R11_REGNUM
+
+#define S390_IS_GREGSET_REGNUM(i)					\
+  (((i) >= S390_PSWM_REGNUM && (i) <= S390_A15_REGNUM)			\
+   || ((i) >= S390_R0_UPPER_REGNUM && (i) <= S390_R15_UPPER_REGNUM)	\
+   || (i) == S390_ORIG_R2_REGNUM)
+
+#define S390_IS_FPREGSET_REGNUM(i)			\
+  ((i) >= S390_FPC_REGNUM && (i) <= S390_F15_REGNUM)
+
+#define S390_IS_TDBREGSET_REGNUM(i)				\
+  ((i) >= S390_TDB_DWORD0_REGNUM && (i) <= S390_TDB_R15_REGNUM)
+
+/* Definitions for address translation.  */
+/* DAT Table types.  */
+#define S390_LK_DAT_TT_REGION1  3
+#define S390_LK_DAT_TT_REGION2  2
+#define S390_LK_DAT_TT_REGION3  1
+#define S390_LK_DAT_TT_SEGMENT  0
+
+/* Region-First-Table */
+#define S390_LK_RFTE_TL  0x3ULL		/* Table-Length */
+#define S390_LK_RFTE_TT  0xcULL		/* Table-Type */
+#define S390_LK_RFTE_I   0x20ULL	/* Region-Invalid Bit */
+#define S390_LK_RFTE_TF  0xc0ULL	/* Table Offset */
+#define S390_LK_RFTE_P   0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_RFTE_O  ~0xfffULL	/* Region-Second-Table Origin */
+
+/* Region-Second-Table flags.  */
+#define S390_LK_RSTE_TL  0x3ULL		/* Table-Length */
+#define S390_LK_RSTE_TT  0xcULL		/* Table-Type */
+#define S390_LK_RSTE_I   0x20ULL	/* Region-Invalid Bit */
+#define S390_LK_RSTE_TF  0xc0ULL	/* Table Offset */
+#define S390_LK_RSTE_P   0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_RSTE_O  ~0xfffULL	/* Region-Third-Table Origin */
+
+/* Region-Third-Table flags.  */
+#define S390_LK_RTTE_TL    0x3ULL	/* Table-Length */
+#define S390_LK_RTTE_TT    0xcULL	/* Table-Type */
+#define S390_LK_RTTE_CR    0x10ULL	/* Common-Region Bit */
+#define S390_LK_RTTE_I     0x20ULL	/* Region-Invalid Bit */
+#define S390_LK_RTTE_TF    0xc0ULL	/* Table Offset */
+#define S390_LK_RTTE_P     0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_RTTE_FC    0x400ULL	/* Format-Control Bit */
+#define S390_LK_RTTE_F     0x800ULL	/* Fetch-Protection Bit */
+#define S390_LK_RTTE_ACC   0xf000ULL	/* Access-Control Bits */
+#define S390_LK_RTTE_AV    0x10000ULL	/* ACCF-Validity Control */
+#define S390_LK_RTTE_O    ~0xfffULL	/* Segment-Table Origin */
+#define S390_LK_RTTE_RFAA ~0x7fffffffULL /* Region-Frame Absolute Address */
+
+/* Segment-Table flags.  */
+#define S390_LK_STE_TT    0xcULL	/* Table-Type */
+#define S390_LK_STE_I     0x20ULL	/* Segment-Invalid Bit */
+#define S390_LK_STE_TF    0xc0ULL	/* Table Offset */
+#define S390_LK_STE_P     0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_STE_FC    0x400ULL	/* Format-Control Bit */
+#define S390_LK_STE_F     0x800ULL	/* Fetch-Protection Bit */
+#define S390_LK_STE_ACC   0xf000ULL	/* Access-Control Bits */
+#define S390_LK_STE_AV    0x10000ULL	/* ACCF-Validity Control */
+#define S390_LK_STE_O    ~0x7ffULL	/* Page-Table Origin */
+#define S390_LK_STE_SFAA ~0xfffffULL	/* Segment-Frame Absolute Address */
+
+/* Page-Table flags.  */
+#define S390_LK_PTE_P     0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_PTE_I     0x400ULL	/* Page-Invalid Bit */
+#define S390_LK_PTE_PFAA ~0xfffULL	/* Page-Frame Absolute Address */
+
+/* Virtual Address Fields.  */
+#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
+#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
+#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
+#define S390_LK_VADDR_SX  0x000000007ff00000ULL
+#define S390_LK_VADDR_PX  0x00000000000ff000ULL
+#define S390_LK_VADDR_BX  0x0000000000000fffULL
+
+
+/* Decoding S/390 instructions.  */
+
+/* Read a single instruction from address AT.  */
+extern int s390_readinstruction (bfd_byte instr[], CORE_ADDR at);
+
+
+/* Prologue analysis.  */
+
+struct s390_prologue_data {
+
+  /* The stack.  */
+  struct pv_area *stack;
+
+  /* The size and byte-order of a GPR or FPR.  */
+  int gpr_size;
+  int fpr_size;
+  enum bfd_endian byte_order;
+
+  /* The general-purpose registers.  */
+  pv_t gpr[S390_NUM_GPRS];
+
+  /* The floating-point registers.  */
+  pv_t fpr[S390_NUM_FPRS];
+
+  /* The offset relative to the CFA where the incoming GPR N was saved
+     by the function prologue.  0 if not saved or unknown.  */
+  int gpr_slot[S390_NUM_GPRS];
+
+  /* Likewise for FPRs.  */
+  int fpr_slot[S390_NUM_FPRS];
+
+  /* Nonzero if the backchain was saved.  This is assumed to be the
+     case when the incoming SP is saved at the current SP location.  */
+  int back_chain_saved_p;
+};
+
+/* Analyze the prologue of the function starting at START_PC, continuing at
+   most until CURRENT_PC.  Initialize DATA to hold all information we find out
+   about the state of the registers and stack slots.  Return the address of the
+   instruction after the last one that changed the SP, FP, or back chain; or
+   zero on error.  */
+extern CORE_ADDR s390_analyze_prologue (struct gdbarch *gdbarch,
+					CORE_ADDR start_pc,
+					CORE_ADDR current_pc,
+					struct s390_prologue_data *data);
+
+/* Helper routine to unwind pseudo registers.  */
+extern struct value *s390_unwind_pseudo_register (struct frame_info *this_frame,
+						  int regnum);
+
+extern int s390_register_call_saved (struct gdbarch *gdbarch, int regnum);
+extern int s390_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc);
+
+#endif /* S390_TDEP_H */
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 9/9] Add S390 support for linux-kernel target
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (7 preceding siblings ...)
  2017-06-12 17:09 ` [RFC v4 6/9] Separate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
@ 2017-06-12 17:39 ` Philipp Rudo
  2017-06-19 13:20   ` Yao Qi
  2017-06-12 21:17 ` Philipp Rudo
  9 siblings, 1 reply; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 17:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

Hi

apparently the last mail got lost...

So let's try it this way.

---

After implementing the new linux-kernel target and preparing s390-tdep.
It is now time to get everything to work.  Thus implement the hooks
required by the linux-kernel target and enable s390's privileged
registers.

gdb/ChangeLog:

	* s390-lk-tdep.h: New file.
	* s390-lk-tdep.c: New file.
	* Makefile.in (ALL_TARGET_OBS): Add s390-lk-tdep.o.
	(HFILES_NO_SRCDIR): Add s390-lk-tdep.h.
	(ALLDEPFILES): Add s390-lk-tdep.c.
	* configure.tgt (s390*-*-linux*): Add s390-lk-tdep.o.
	* s390-tdep.h: Define macros for address translation.
	* s390-tdep.c (s390-lk-tdep.h): New include.
	(s390_iterate_over_regset_sections): Enable privileged registers.
	(s390_core_read_description): Enable privileged registers.
	(s390_gdbarch_init): Enable privileged registers and adjust.
---
 gdb/Makefile.in    |   3 +
 gdb/configure.tgt  |   5 +-
 gdb/s390-lk-tdep.c | 887 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-lk-tdep.h |  54 ++++
 gdb/s390-tdep.c    |  57 +++-
 5 files changed, 1001 insertions(+), 5 deletions(-)
 create mode 100644 gdb/s390-lk-tdep.c
 create mode 100644 gdb/s390-lk-tdep.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9819b70f3d..cb31b65b5e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -873,6 +873,7 @@ ALL_TARGET_OBS = \
 	rs6000-tdep.o \
 	rx-tdep.o \
 	s390-linux-tdep.o \
+	s390-lk-tdep.o \
 	s390-tdep.o \
 	score-tdep.o \
 	sh-linux-tdep.o \
@@ -1448,6 +1449,7 @@ HFILES_NO_SRCDIR = \
 	rs6000-aix-tdep.h \
 	rs6000-tdep.h \
 	s390-linux-tdep.h \
+	s390-lk-tdep.h \
 	s390-tdep.h \
 	score-tdep.h \
 	selftest-arch.h \
@@ -2655,6 +2657,7 @@ ALLDEPFILES = \
 	rx-tdep.c \
 	s390-linux-nat.c \
 	s390-linux-tdep.c \
+	s390-lk-tdep.c \
 	s390-tdep.c \
 	score-tdep.c \
 	ser-go32.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index b79a0bac4c..2788368c17 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -483,8 +483,9 @@ powerpc*-*-*)
 
 s390*-*-linux*)
 	# Target: S390 running Linux
-	gdb_target_obs="s390-tdep.o s390-linux-tdep.o solib-svr4.o \
-			linux-tdep.o linux-record.o ${lk_target_obs}"
+	gdb_target_obs="s390-tdep.o s390-linux-tdep.o s390-lk-tdep.o \
+			solib-svr4.o linux-tdep.o linux-record.o \
+			${lk_target_obs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c
new file mode 100644
index 0000000000..06da7795f0
--- /dev/null
+++ b/gdb/s390-lk-tdep.c
@@ -0,0 +1,887 @@
+/* Target-dependent code for linux-kernel target on S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "dwarf2-frame.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "osabi.h"
+#include "regcache.h"
+#include "regset.h"
+#include "s390-tdep.h"
+#include "s390-lk-tdep.h"
+
+#include "features/s390x-cr-linux64.c"
+#include "features/s390x-vxcr-linux64.c"
+
+/* Register maps and sets.  */
+
+static const struct regcache_map_entry s390x_gregmap_lk[] =
+  {
+    { 10, S390_R6_REGNUM }, /* r0-r5 volatile */
+    { -2, REGCACHE_MAP_SKIP, 8 },
+    { 1, S390_PSWA_REGNUM }, /* Use r14 for PSWA.  */
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_cr [] =
+  {
+    { 16, S390_CR0_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_timer [] =
+  {
+    { 1, S390_TIMER_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_todcmp [] =
+  {
+    { 1, S390_TODCMP_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_todpreg [] =
+  {
+    { 1, S390_TODPREG_REGNUM, 4 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_prefix [] =
+  {
+    { 1, S390_PREFIX_REGNUM, 4 },
+    { 0 }
+  };
+
+const struct regset s390x_gregset_lk = {
+  s390x_gregmap_lk,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_cr_regset = {
+  s390x_regmap_cr,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_timer_regset = {
+  s390x_regmap_timer,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_todcmp_regset = {
+  s390x_regmap_todcmp,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_todpreg_regset = {
+  s390x_regmap_todpreg,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_prefix_regset = {
+  s390x_regmap_prefix,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+
+/* General Helper functions.  */
+
+/* Get the lowcore of CPU.  */
+
+CORE_ADDR
+s390_lk_get_lowcore (unsigned int cpu)
+{
+  CORE_ADDR lowcore_ptr, lowcore;
+  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+
+  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
+  return lk_read_addr (lowcore_ptr);
+}
+
+
+/* Linux Kernel unwinder.  */
+
+/* Helper functions and definitions.  */
+
+struct s390_lk_unwind_cache
+{
+  /* The frame this frame_cache belongs to and its ... */
+  struct frame_info *frame;
+
+  /* ... frame base, ... */
+  CORE_ADDR frame_base;
+  /* ... stack pointer, ... */
+  CORE_ADDR sp;
+  /* ... process counter, ... */
+  CORE_ADDR pc;
+  /* ... PSW mask, ... */
+  CORE_ADDR pswm;
+  /* ... PSW address and ... */
+  CORE_ADDR pswa;
+  /* ... saved general purpose registers.  */
+  CORE_ADDR gprs[S390_NUM_GPRS];
+
+  /* Mask to store which gprs where saved.  */
+  unsigned long gprs_p:S390_NUM_GPRS;
+  /* Does this frame contain a zero backchain?  */
+  bool zero_backchain;
+};
+
+/* S390s kernel stack is split up on several memory locations:
+
+   - kernel stack (per task)
+   - async aka. interrupt stack (per cpu)
+   - panic stack (per cpu)
+   - restart stack (unique, global)
+
+   Each memory location is page aligned and has a size of four consecutive
+   pages (except the panic stack with only one page).  */
+
+enum s390_lk_stack_location
+{
+  S390_LK_STACK_INVALID = -1,
+  S390_LK_STACK_USER,
+  S390_LK_STACK_KERNEL,
+  S390_LK_STACK_ASYNC,
+  S390_LK_STACK_PANIC,
+  S390_LK_STACK_RESTART
+};
+
+/* Get value of gpr REGNUM stored in CACHE.  */
+
+static inline struct value *
+s390_lk_unwind_get_gpr (struct s390_lk_unwind_cache *cache, int regnum)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  if (!(cache->gprs_p & (1 << gpr)))
+    return frame_unwind_got_optimized (cache->frame, regnum);
+
+  /* When we hit a zero_backchain R15 is the (computed) lowest address of
+     pt_regs.  */
+  if (regnum == S390_R15_REGNUM && cache->zero_backchain)
+    return frame_unwind_got_address (cache->frame, regnum, cache->gprs[gpr]);
+
+  return frame_unwind_got_memory (cache->frame, regnum, cache->gprs[gpr]);
+}
+
+/* Store ADDR of gpr REGNUM in CACHE.  */
+
+static inline void
+s390_lk_unwind_set_gpr (struct s390_lk_unwind_cache *cache, int regnum,
+			CORE_ADDR addr)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  cache->gprs[gpr] = addr;
+  cache->gprs_p |= (1 << gpr);
+}
+
+/* Allocate and initialize a new s390_lk_unwind_cache.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_alloc_unwind_cache ()
+{
+  struct s390_lk_unwind_cache *cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct s390_lk_unwind_cache);
+
+  cache->frame = NULL;
+  cache->frame_base = -1;
+  cache->zero_backchain = false;
+
+  cache->sp = -1;
+  cache->pc = -1;
+  cache->pswm = -1;
+  cache->pswa = -1;
+  for (CORE_ADDR *gpr = cache->gprs; gpr < cache->gprs + S390_NUM_GPRS; gpr++)
+    *gpr = -1;
+  cache->gprs_p = 0;
+
+  return cache;
+}
+
+/* Helper macro for s390_lk_get_stack_location to check for stacks which
+   locations are stored in the lowcore.
+
+      _addr	address to be checked for
+      _lc	address of the corresponding lowcore
+      _stack	field name of stack in lowcore
+      _type	type to be returned if _addr is on _stack
+      _size	size of _stack
+*/
+
+#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size)	\
+  do									\
+    {									\
+      CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom;		\
+      _##_stack = lk_read_addr ((_lc) + LK_OFFSET (lowcore, _stack));	\
+      _##_stack##_top = S390_LK_ROUNDUP (_##_stack, S390_LK_PAGESIZE);	\
+      _##_stack##_bottom = _##_stack##_top - (_size);			\
+      if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom)	\
+	return (_type);							\
+    }									\
+  while (0)
+
+/* Find and return the stack location of ADDR belonging to TASK.  */
+
+static enum s390_lk_stack_location
+s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
+{
+  CORE_ADDR lowcore, top, bottom;
+  unsigned int cpu = lk_task_running (task);
+
+
+  /* Kernel stack.  */
+  bottom = lk_read_addr (task + LK_OFFSET (task_struct, stack));
+  top = bottom + S390_LK_STACKSIZE;
+  if (addr <= top && addr >= bottom)
+    return S390_LK_STACK_KERNEL;
+
+  /* A sleeping task only has the kernel stack.  If a sleeping task reaches
+     this point ADDR isn't on the stack.  */
+  if (cpu == LK_CPU_INVAL)
+    return S390_LK_STACK_INVALID;
+
+  lowcore = s390_lk_get_lowcore (cpu);
+
+  /* Async aka. interrupt stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
+			       S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
+
+  /* Panic stack.
+     Note: The panic stack only has a size of one page.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
+			       S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
+
+  /* Restart stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
+			       S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
+
+  return S390_LK_STACK_INVALID;
+}
+
+
+/* Unwind a single NORMAL_FRAME, i.e. a struct stack_frame.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_frame_unwind_cache (struct frame_info *this_frame,
+			    void **this_cache)
+{
+  struct s390_lk_unwind_cache *cache;
+  CORE_ADDR backchain, backchain_addr, prev_gpr15;
+
+  if (*this_cache)
+    return (struct s390_lk_unwind_cache *) *this_cache;
+
+  cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For NORMAL_FRAMES, cache->sp == lowest address of struct stack_frame.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = get_frame_pc (this_frame);
+  /* Choose the frame's base to be gpr15 + sizeof (stack_frame) to be
+     complient with DWARF's DW_CFA_def_cfa.  Otherwise gdbarch_inner_than
+     can trigger false stack corruption asserts, as
+
+     next_dwarf_frame_base = next_backchain_frame_base + sizeof (stack_frame)
+			   > this_backchain_frame_base
+  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (LK_STRUCT (stack_frame));
+
+  backchain_addr = cache->sp + LK_OFFSET (stack_frame, back_chain);
+  backchain = lk_read_addr (backchain_addr);
+
+  /* A zero backchain marks the end of this stack.  This can lead to two
+     actions (handled by SIGTRAMP_FRAME unwinder):
+       * stop unwinding (if sp at the end of the kernel stack)
+       * jump to next stack location (otherwise).
+  */
+  if (backchain == 0)
+    {
+      CORE_ADDR pt_regs;
+
+      pt_regs = cache->sp + TYPE_LENGTH (LK_STRUCT (stack_frame));
+
+      {
+	/* A bug in the kernel (linux 3.10+) adds the stack overhead twice
+	   for swapper tasks (!= swapper/0) with the information written in
+	   the first (higher address) pt_regs.  Compensate this by updating
+	   the stack pointer.  */
+	enum s390_lk_stack_location stack;
+
+	CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+	long pid = frame_get_thread (this_frame)->ptid.lwp ();
+	stack = s390_lk_get_stack_location (task, cache->sp);
+	if (pid == 0 && task != LK_ADDR (init_task)
+	    && stack == S390_LK_STACK_KERNEL)
+	  pt_regs += (TYPE_LENGTH (LK_STRUCT (stack_frame))
+		      + TYPE_LENGTH (LK_STRUCT (pt_regs)));
+      }
+
+      cache->zero_backchain = true;
+      s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, pt_regs);
+
+      /* Use pc = 0 to mark end of stack.  */
+      s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, backchain_addr);
+      return cache;
+    }
+
+  /* Do sanity check.  */
+  prev_gpr15 = lk_read_addr (backchain
+			     + s390_lk_stack_frame_gpr (S390_R15_REGNUM));
+  if (backchain != prev_gpr15)
+    {
+      size_t ptr_len = lk_builtin_type_size (unsigned_long);
+      CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+      error (_("Stack corruption for task 0x%s at stack frame 0x%s with "
+	       "back_chain 0x%s detected %s.  back_chain->gpr15 != back_chain."),
+	     phex (task, ptr_len), phex (cache->sp, ptr_len),
+	     phex (backchain, ptr_len), phex (prev_gpr15, ptr_len));
+    }
+
+  s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, backchain_addr);
+  s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM,
+			  backchain + s390_lk_stack_frame_gpr (S390_R14_REGNUM));
+
+  return cache;
+}
+
+/* Function for frame_unwind->this_id hook for NORMAL_FRAMEs.  */
+
+static void
+s390_lk_frame_this_id (struct frame_info *this_frame,
+		       void **this_cache,
+		       struct frame_id *this_id)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* If we don't have a stack frame...  */
+  if (cache->sp == -1)
+    {
+      /* ...but a PC.  We have a inline frame.  */
+      if (cache->pc != -1)
+	*this_id = frame_id_build_unavailable_stack (cache->pc);
+
+      return;
+    }
+
+  *this_id = frame_id_build (cache->frame_base, cache->pc);
+}
+
+/* Function for frame_unwind->prev_register hook for NORMAL_ and
+   SIGTRAMP_FRAMEs.  */
+
+static struct value *
+s390_lk_frame_prev_register (struct frame_info *this_frame,
+			     void **this_cache, int regnum)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* Also handles S390_SP_REGNUM and S390_RETADDR_REGNUM as they are mapped to
+     R15 and R14 respectfully in s390-tdep.h.  */
+  if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
+    return s390_lk_unwind_get_gpr (cache, regnum);
+
+  /* Use PSWM stored in pt_regs if available.  */
+  if (regnum == S390_PSWM_REGNUM)
+    return (cache->pswm != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswm)
+	    : frame_unwind_got_optimized (cache->frame, regnum));
+
+  /* Use PSWA stored in pt_regs if available, R14/PC otherwise.  */
+  if (regnum == S390_PSWA_REGNUM)
+    return (cache->pswa != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswa)
+	    : s390_lk_unwind_get_gpr (cache, S390_RETADDR_REGNUM));
+
+  if (regnum >= S390_NUM_REGS)
+    return s390_unwind_pseudo_register (cache->frame, regnum);
+
+  /* Default is not saved.  */
+  return frame_unwind_got_optimized (cache->frame, regnum);
+}
+
+/* Simple backchain unwinder for Linux kernel on S390.  Its main purpose is
+   to handle stack frames created in Assembler code without debug
+   infromation.  */
+
+static const struct frame_unwind s390_lk_frame_unwind = {
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_frame_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+
+/* Unwind a single SIGTRAMP_FRAME, i.e. a struct pt_regs.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_sigtramp_unwind_cache (struct frame_info *this_frame,
+			       void **this_cache)
+{
+  struct frame_info *next_frame;
+  struct s390_lk_unwind_cache *cache;
+  CORE_ADDR psw;
+
+  if (*this_cache)
+    return (struct s390_lk_unwind_cache *) *this_cache;
+
+  cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For SIGTRAMP_FRAMES, cache->sp = lowest address of struct pt_regs.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = cache->sp + s390_lk_pt_regs_gpr (S390_R14_REGNUM);
+  /* Choose the frame base to be SP + sizeof (pt_regs) aka. the end of this
+     stack.  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (LK_STRUCT (pt_regs));
+
+  psw = cache->sp + LK_OFFSET (pt_regs, psw);
+  cache->pswm = psw + LK_OFFSET (psw_t, mask);
+  cache->pswa = psw + LK_OFFSET (psw_t, addr);
+
+  for (int i = S390_R0_REGNUM; i <= S390_R15_REGNUM; i++)
+    s390_lk_unwind_set_gpr (cache, i, cache->sp + s390_lk_pt_regs_gpr (i));
+
+  return cache;
+}
+
+/* Function for frame_unwind->this_id hook for SIGTRAMP_FRAMEs.  */
+
+static void
+s390_lk_sigtramp_this_id (struct frame_info *this_frame,
+			  void **this_cache,
+			  struct frame_id *this_id)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_sigtramp_unwind_cache (this_frame, this_cache);
+
+  CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+
+  switch (s390_lk_get_stack_location (task, cache->sp))
+    {
+      /* If we are on an interrupt stack keep on unwinding.  */
+      case S390_LK_STACK_ASYNC:
+      case S390_LK_STACK_PANIC:
+      case S390_LK_STACK_RESTART:
+	*this_id = frame_id_build (cache->frame_base, cache->pc);
+	return;
+
+      /* If we are on the kernel stack check whether we are at the end of
+	 the stack.  If so stop unwinding otherwise continue.  */
+      case S390_LK_STACK_KERNEL:
+	{
+	  CORE_ADDR top;
+
+	  top = lk_read_addr (task + LK_OFFSET (task_struct, stack));
+	  top += S390_LK_STACKSIZE;
+
+	  /* swapper/0 doesn't have a pt_regs at the top of its stack, so in
+	     this case cache->sp pointing at the top of the last stack_frame
+	     points at the end of the stack.  */
+	  if (task == LK_ADDR (init_task) && top <= cache->sp)
+	    *this_id = outer_frame_id;
+
+	  /* For all other tasks cache->frame_base pointing at the top of
+	     pt_regs points at the end of stack.  */
+	  else if (top <= cache->frame_base)
+	    *this_id = outer_frame_id;
+
+	  /* The program check handler appends his stack to the kernel stack.
+	     If that is the case simply keep on unwinding.  */
+	  else
+	    *this_id = frame_id_build (cache->frame_base, cache->pc);
+
+	  return;
+	}
+
+      /* Otherwise somewhere something went wrong.  */
+      default:
+	{
+	  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+	  error (_("Couldn't find find valid stack for task 0x%s with stack "
+		   "pointer 0x%s.  Stack corruption?"),
+		   phex (task, ptr_len), phex (cache->sp, ptr_len));
+	}
+    }
+}
+
+/* Function for frame_unwind->sniffer hook for SIGTRAMP_FRAMEs.  */
+
+static int
+s390_lk_sigtramp_sniffer (const struct frame_unwind *self,
+			  struct frame_info *this_frame,
+			  void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+  if (!gdbarch_lk_init_private_p (gdbarch))
+    return 0;
+
+  CORE_ADDR pc = get_frame_pc (this_frame);
+
+  return pc == 0;
+}
+
+/* Sigtramp unwinder for Linux kernel on S390.  It handles situations when
+   the end of a stack, i.e. a zero backchain, was detected.  */
+
+static const struct frame_unwind s390_lk_sigtramp_unwind = {
+  SIGTRAMP_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_sigtramp_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  s390_lk_sigtramp_sniffer
+};
+
+
+/* Frame base handling.  */
+
+static CORE_ADDR
+s390_lk_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+  return cache->frame_base;
+}
+
+static const struct frame_base s390_lk_frame_base = {
+  &s390_lk_frame_unwind,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address
+};
+
+
+/* Hooks for linux-kernel target.  */
+
+/* Function for Linux kernel target get_registers hook.  Supplies gprs for
+   task TASK to REGCACHE.  Uses r14 (back jump address) as current pswa.  */
+
+void
+s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
+		       struct regcache *regcache, int regnum)
+{
+  const struct regset *regset;
+  CORE_ADDR ksp, gprs, pswa;
+  gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */
+  size_t size;
+
+  regset = &s390x_gregset_lk;
+
+  ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
+		      + LK_OFFSET (thread_struct, ksp));
+  gprs = ksp + LK_OFFSET (stack_frame, gprs);
+  size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
+  gdb_assert (size <= sizeof (buf));
+
+  read_memory (gprs, buf, size);
+  regset->supply_regset (regset, regcache, -1, buf, size);
+}
+
+/* Function for Linux kernel target get_percpu_offset hook.  Returns the
+   percpu_offset from lowcore for cpu CPU.  */
+
+CORE_ADDR
+s390_lk_get_percpu_offset (unsigned int cpu)
+{
+  CORE_ADDR lowcore = s390_lk_get_lowcore (cpu);
+  return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset));
+}
+
+/* Function for Linux kernel target map_running_task_to_cpu hook.  */
+
+unsigned int
+s390_lk_map_running_task_to_cpu (struct thread_info *ti)
+{
+  struct regcache *regcache;
+  enum register_status reg_status;
+  CORE_ADDR lowcore;
+  unsigned int cpu;
+
+  regcache = get_thread_regcache (ti->ptid);
+  reg_status = regcache_raw_read_unsigned (regcache, S390_PREFIX_REGNUM,
+					   (ULONGEST *) &lowcore);
+  if (reg_status != REG_VALID)
+    error (_("Could not find prefix register for thread with pid %d, lwp %li."),
+	   ti->ptid.pid (), ti->ptid.lwp ());
+
+  cpu = lk_read_uint (lowcore + LK_OFFSET (lowcore, cpu_nr));
+
+  return cpu;
+}
+
+/* Function for Linux kernel target is_kvaddr hook.  */
+
+int
+s390_lk_is_kvaddr (CORE_ADDR addr)
+{
+  return addr >= LK_ADDR (high_memory);
+}
+
+/* Read table entry from TABLE at offset OFFSET.  Helper for s390_lk_vtop.  */
+
+static inline ULONGEST
+s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
+{
+  return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
+}
+
+/* Function for Linux kernel target vtop hook.  Assume 64 bit addresses.  */
+
+CORE_ADDR
+s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
+{
+  ULONGEST entry, offset;
+  CORE_ADDR paddr;
+  unsigned int table_type;
+  size_t addr_size = lk_builtin_type_size (unsigned_long);
+
+  /* Read first entry in table to get its type.  As the Table-Type bits are
+     the same in every table assume Region1-Table.  */
+  entry = s390_lk_read_table_entry (table, 0);
+  table_type = (entry & S390_LK_RFTE_TT) >> 2;
+
+  switch (table_type)
+    {
+      case S390_LK_DAT_TT_REGION1:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RFX) >> 53;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-first-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-first-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RFTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-first-table entry."),
+		     phex (vaddr, addr_size));
+
+	  table = entry & S390_LK_RFTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_REGION2:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RSX) >> 42;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-second-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-second-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RSTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-second-table entry."),
+		     phex (vaddr, addr_size));
+
+	  table = entry & S390_LK_RSTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_REGION3:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RTX) >> 31;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-third-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-third-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RTTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-third-table entry."),
+		     phex (vaddr, addr_size));
+
+	  /* Check for huge page.  */
+	  if (entry & S390_LK_RTTE_FC)
+	    {
+	      paddr = ((entry & S390_LK_RTTE_RFAA)
+		       + (vaddr & ~S390_LK_RTTE_RFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_RTTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_SEGMENT:
+	{
+	  offset = (vaddr & S390_LK_VADDR_SX) >> 20;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "segment-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in segment-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_STE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "segment-table entry."),
+		     phex (vaddr, addr_size));
+
+	  /* Check for large page.  */
+	  if (entry & S390_LK_STE_FC)
+	    {
+	      paddr = ((entry & S390_LK_STE_SFAA)
+		       + (vaddr & ~S390_LK_STE_SFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_STE_O;
+	  break;
+	}
+    } /* switch (table_type) */
+
+  offset = (vaddr & S390_LK_VADDR_PX) >> 12;
+  entry = s390_lk_read_table_entry (table, offset);
+
+  /* Do sanity checks.  */
+  if (!entry)
+    warning (_("Trying to translate address 0x%s with empty page-table "\
+	       "entry."),
+	     phex (vaddr, addr_size));
+  else if (entry & S390_LK_PTE_I)
+    warning (_("Translating address 0x%s with invalid bit set at page-table "\
+	       "entry."),
+	     phex (vaddr, addr_size));
+
+  paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA));
+
+  return paddr;
+}
+
+/* Function for Linux kernel target adjust_module_layout hook.  */
+
+void
+s390_lk_adjust_module_layout (CORE_ADDR mod, lk_module *module)
+{
+  CORE_ADDR mod_arch;
+
+  mod_arch = mod + LK_OFFSET (module, arch);
+
+  module->core->text.base
+    += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_offset));
+  module->core->text.base
+    += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
+}
+
+/* Initialize s390 dependent private data for linux kernel target.  */
+
+static void
+s390_lk_init_private (struct gdbarch *gdbarch)
+{
+  LK_DECLARE_FIELD (stack_frame, gprs);
+  LK_DECLARE_FIELD (stack_frame, back_chain);
+
+  LK_DECLARE_FIELD (pt_regs, psw);
+  LK_DECLARE_FIELD (pt_regs, gprs);
+
+  LK_DECLARE_FIELD (task_struct, stack);
+
+  LK_DECLARE_FIELD (thread_struct, ksp);
+
+  LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
+  LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
+  if (LK_STRUCT (lowcore) == NULL)
+    error (_("Could not find struct lowcore.  Aborting."));
+  LK_DECLARE_FIELD (lowcore, percpu_offset);
+  LK_DECLARE_FIELD (lowcore, cpu_nr);
+  LK_DECLARE_FIELD (lowcore, async_stack);
+  LK_DECLARE_FIELD (lowcore, panic_stack);
+  LK_DECLARE_FIELD (lowcore, restart_stack);
+
+  LK_DECLARE_FIELD (psw_t, mask);
+  LK_DECLARE_FIELD (psw_t, addr);
+
+  LK_DECLARE_FIELD (mod_arch_specific, plt_offset);
+  LK_DECLARE_FIELD (mod_arch_specific, plt_size);
+
+  LK_DECLARE_ADDR (lowcore_ptr);
+  LK_DECLARE_ADDR (high_memory);
+
+  LK_HOOK->get_registers = s390_lk_get_registers;
+  LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
+  LK_HOOK->vtop = s390_lk_vtop;
+  LK_HOOK->adjust_module_layout = s390_lk_adjust_module_layout;
+  LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
+  LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
+}
+
+/* Initialize Linux kernel specific gdbarch hooks.  */
+
+void
+s390_lk_gdbarch_init (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  /* Frame handling.  */
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_sigtramp_unwind);
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_frame_unwind);
+
+  /* Support linux kernel debugging.  */
+  set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
+}
+
+extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_s390_lk_tdep (void)
+{
+  /* Initialize the Linux kernel target descriptions.  */
+  initialize_tdesc_s390x_cr_linux64 ();
+  initialize_tdesc_s390x_vxcr_linux64 ();
+}
diff --git a/gdb/s390-lk-tdep.h b/gdb/s390-lk-tdep.h
new file mode 100644
index 0000000000..4e54e7d236
--- /dev/null
+++ b/gdb/s390-lk-tdep.h
@@ -0,0 +1,54 @@
+/* Target-dependent code for linux-kernel target on S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef S390_LK_TDEP_H
+#define S390_LK_TDEP_H
+
+/* Constants copied from kernel sources.  */
+#define S390_LK_PAGESIZE (1UL << 12)
+#define S390_LK_STACKSIZE (S390_LK_PAGESIZE << 2)
+#define S390_LK_ASYNCSIZE (S390_LK_STACKSIZE)
+#define S390_LK_ROUNDUP(val, base) ((val) + (base) - ((val) % (base)))
+
+/* Short hand access to gprs stored in struct pt_regs.  */
+#define s390_lk_pt_regs_gpr(regnum)					\
+  (LK_OFFSET (pt_regs, gprs)						\
+   + (FIELD_TARGET_SIZE (LK_FIELD (pt_regs, gprs))			\
+      * ((regnum) - S390_R0_REGNUM)))					\
+
+/* Short hand access to gprs stored in struct stack_frame.  */
+#define s390_lk_stack_frame_gpr(regnum)					\
+  (LK_OFFSET (stack_frame, gprs)					\
+   + (FIELD_TARGET_SIZE (LK_FIELD (stack_frame, gprs))			\
+      * ((regnum) - S390_R6_REGNUM)))					\
+
+extern void s390_lk_gdbarch_init (struct gdbarch_info info,
+				  struct gdbarch *gdbarch);
+
+/* Core file privileged register sets, defined in s390-lk-tdep.c.  */
+extern const struct regset s390x_gregset_lk;
+extern const struct regset s390x_cr_regset;
+extern const struct regset s390x_timer_regset;
+extern const struct regset s390x_todcmp_regset;
+extern const struct regset s390x_todpreg_regset;
+extern const struct regset s390x_prefix_regset;
+
+extern struct target_desc *tdesc_s390x_cr_linux64;
+extern struct target_desc *tdesc_s390x_vxcr_linux64;
+
+#endif /* S390_LK_TDEP_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 7d2906f294..e40fea120a 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -35,6 +35,7 @@
 #include "record-full.h"
 #include "reggroups.h"
 #include "s390-linux-tdep.h"
+#include "s390-lk-tdep.h"
 #include "s390-tdep.h"
 #include "target-descriptions.h"
 #include "value.h"
@@ -962,7 +963,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
 {
   asection *section = bfd_get_section_by_name (abfd, ".reg");
   CORE_ADDR hwcap = 0;
-  int high_gprs, v1, v2, te, vx;
+  int high_gprs, v1, v2, te, vx, cr;
 
   target_auxv_search (target, AT_HWCAP, &hwcap);
   if (!section)
@@ -974,6 +975,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
   v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL);
   vx = (hwcap & HWCAP_S390_VX);
   te = (hwcap & HWCAP_S390_TE);
+  cr = (bfd_get_section_by_name (abfd, ".reg-s390-ctrs") != NULL);
 
   switch (bfd_section_size (abfd, section))
     {
@@ -990,6 +992,8 @@ s390_core_read_description (struct gdbarch *gdbarch,
 
     case s390x_sizeof_gregset:
       return (te && vx ? tdesc_s390x_tevx_linux64 :
+	      vx && cr ? tdesc_s390x_vxcr_linux64 :
+	      cr ? tdesc_s390x_cr_linux64 :
 	      vx ? tdesc_s390x_vx_linux64 :
 	      te ? tdesc_s390x_te_linux64 :
 	      v2 ? tdesc_s390x_linux64v2 :
@@ -1046,6 +1050,22 @@ s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
       cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
 	  "s390 vector registers 16-31", cb_data);
     }
+
+  /* Privileged registers for kernel debugging.  */
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-timer"))
+    cb (".reg-s390-timer", 8, &s390x_timer_regset, "s390 timer", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todcmp"))
+    cb (".reg-s390-todcmp", 8, &s390x_todcmp_regset,
+	"s390 clock comperator", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todpreg"))
+    cb (".reg-s390-todpreg", 4, &s390x_todpreg_regset,
+	"s390 TOD programable register", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-ctrs"))
+    cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset,
+	"s390 control registers", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-prefix"))
+    cb (".reg-s390-prefix", 4, &s390x_prefix_regset,
+	"s390 prefix area", cb_data);
 }
 
 
@@ -2302,6 +2322,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   int have_linux_v2 = 0;
   int have_tdb = 0;
   int have_vx = 0;
+  int have_privileged = 0;
   int first_pseudo_reg, last_pseudo_reg;
   static const char *const stap_register_prefixes[] = { "%", NULL };
   static const char *const stap_register_indirection_prefixes[] = { "(",
@@ -2476,6 +2497,30 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	  have_vx = 1;
 	}
 
+      /* Control registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr");
+      if (feature)
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_CR0_REGNUM + i, cr[i]);
+	}
+
+      /* Privileged registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged");
+      if (feature)
+	{
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TIMER_REGNUM, "timer");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODCMP_REGNUM, "todcmp");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODPREG_REGNUM, "todpreg");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_PREFIX_REGNUM, "prefix");
+	  have_privileged = 1;
+	}
+
       if (!valid_p)
 	{
 	  tdesc_data_cleanup (tdesc_data);
@@ -2648,8 +2693,14 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_valid_disassembler_options (gdbarch,
 					  disassembler_options_s390 ());
 
-  /* Note that GNU/Linux is the only OS supported on this platform.  */
-  s390_linux_gdbarch_init (info, gdbarch);
+  /* Note that GNU/Linux is the only OS supported on this platform.
+     Nevertheless we need to distinguish between a kernel- and user-space
+     gdbarch.  GDBs osabi support is not sufficient for our case as for both
+     cases the ELF bits are for Linux.  Thus fallback to this method.  */
+  if (have_privileged)
+    s390_lk_gdbarch_init (info, gdbarch);
+  else
+    s390_linux_gdbarch_init (info, gdbarch);
 
   return gdbarch;
 }
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC v4 9/9] Add S390 support for linux-kernel target
  2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
                   ` (8 preceding siblings ...)
  2017-06-12 17:39 ` [RFC v4 9/9] Add S390 support for linux-kernel target Philipp Rudo
@ 2017-06-12 21:17 ` Philipp Rudo
  9 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-12 21:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: omair.javaid, yao.qi, peter.griffin, arnez

After implementing the new linux-kernel target and preparing s390-tdep.
It is now time to get everything to work.  Thus implement the hooks
required by the linux-kernel target and enable s390's privileged
registers.

gdb/ChangeLog:

	* s390-lk-tdep.h: New file.
	* s390-lk-tdep.c: New file.
	* Makefile.in (ALL_TARGET_OBS): Add s390-lk-tdep.o.
	(HFILES_NO_SRCDIR): Add s390-lk-tdep.h.
	(ALLDEPFILES): Add s390-lk-tdep.c.
	* configure.tgt (s390*-*-linux*): Add s390-lk-tdep.o.
	* s390-tdep.h: Define macros for address translation.
	* s390-tdep.c (s390-lk-tdep.h): New include.
	(s390_iterate_over_regset_sections): Enable privileged registers.
	(s390_core_read_description): Enable privileged registers.
	(s390_gdbarch_init): Enable privileged registers and adjust.
---
 gdb/Makefile.in    |   3 +
 gdb/configure.tgt  |   5 +-
 gdb/s390-lk-tdep.c | 887 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-lk-tdep.h |  54 ++++
 gdb/s390-tdep.c    |  57 +++-
 5 files changed, 1001 insertions(+), 5 deletions(-)
 create mode 100644 gdb/s390-lk-tdep.c
 create mode 100644 gdb/s390-lk-tdep.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9819b70f3d..cb31b65b5e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -873,6 +873,7 @@ ALL_TARGET_OBS = \
 	rs6000-tdep.o \
 	rx-tdep.o \
 	s390-linux-tdep.o \
+	s390-lk-tdep.o \
 	s390-tdep.o \
 	score-tdep.o \
 	sh-linux-tdep.o \
@@ -1448,6 +1449,7 @@ HFILES_NO_SRCDIR = \
 	rs6000-aix-tdep.h \
 	rs6000-tdep.h \
 	s390-linux-tdep.h \
+	s390-lk-tdep.h \
 	s390-tdep.h \
 	score-tdep.h \
 	selftest-arch.h \
@@ -2655,6 +2657,7 @@ ALLDEPFILES = \
 	rx-tdep.c \
 	s390-linux-nat.c \
 	s390-linux-tdep.c \
+	s390-lk-tdep.c \
 	s390-tdep.c \
 	score-tdep.c \
 	ser-go32.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index b79a0bac4c..2788368c17 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -483,8 +483,9 @@ powerpc*-*-*)
 
 s390*-*-linux*)
 	# Target: S390 running Linux
-	gdb_target_obs="s390-tdep.o s390-linux-tdep.o solib-svr4.o \
-			linux-tdep.o linux-record.o ${lk_target_obs}"
+	gdb_target_obs="s390-tdep.o s390-linux-tdep.o s390-lk-tdep.o \
+			solib-svr4.o linux-tdep.o linux-record.o \
+			${lk_target_obs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c
new file mode 100644
index 0000000000..06da7795f0
--- /dev/null
+++ b/gdb/s390-lk-tdep.c
@@ -0,0 +1,887 @@
+/* Target-dependent code for linux-kernel target on S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "dwarf2-frame.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "osabi.h"
+#include "regcache.h"
+#include "regset.h"
+#include "s390-tdep.h"
+#include "s390-lk-tdep.h"
+
+#include "features/s390x-cr-linux64.c"
+#include "features/s390x-vxcr-linux64.c"
+
+/* Register maps and sets.  */
+
+static const struct regcache_map_entry s390x_gregmap_lk[] =
+  {
+    { 10, S390_R6_REGNUM }, /* r0-r5 volatile */
+    { -2, REGCACHE_MAP_SKIP, 8 },
+    { 1, S390_PSWA_REGNUM }, /* Use r14 for PSWA.  */
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_cr [] =
+  {
+    { 16, S390_CR0_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_timer [] =
+  {
+    { 1, S390_TIMER_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_todcmp [] =
+  {
+    { 1, S390_TODCMP_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_todpreg [] =
+  {
+    { 1, S390_TODPREG_REGNUM, 4 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_prefix [] =
+  {
+    { 1, S390_PREFIX_REGNUM, 4 },
+    { 0 }
+  };
+
+const struct regset s390x_gregset_lk = {
+  s390x_gregmap_lk,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_cr_regset = {
+  s390x_regmap_cr,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_timer_regset = {
+  s390x_regmap_timer,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_todcmp_regset = {
+  s390x_regmap_todcmp,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_todpreg_regset = {
+  s390x_regmap_todpreg,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390x_prefix_regset = {
+  s390x_regmap_prefix,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+
+/* General Helper functions.  */
+
+/* Get the lowcore of CPU.  */
+
+CORE_ADDR
+s390_lk_get_lowcore (unsigned int cpu)
+{
+  CORE_ADDR lowcore_ptr, lowcore;
+  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+
+  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
+  return lk_read_addr (lowcore_ptr);
+}
+
+
+/* Linux Kernel unwinder.  */
+
+/* Helper functions and definitions.  */
+
+struct s390_lk_unwind_cache
+{
+  /* The frame this frame_cache belongs to and its ... */
+  struct frame_info *frame;
+
+  /* ... frame base, ... */
+  CORE_ADDR frame_base;
+  /* ... stack pointer, ... */
+  CORE_ADDR sp;
+  /* ... process counter, ... */
+  CORE_ADDR pc;
+  /* ... PSW mask, ... */
+  CORE_ADDR pswm;
+  /* ... PSW address and ... */
+  CORE_ADDR pswa;
+  /* ... saved general purpose registers.  */
+  CORE_ADDR gprs[S390_NUM_GPRS];
+
+  /* Mask to store which gprs where saved.  */
+  unsigned long gprs_p:S390_NUM_GPRS;
+  /* Does this frame contain a zero backchain?  */
+  bool zero_backchain;
+};
+
+/* S390s kernel stack is split up on several memory locations:
+
+   - kernel stack (per task)
+   - async aka. interrupt stack (per cpu)
+   - panic stack (per cpu)
+   - restart stack (unique, global)
+
+   Each memory location is page aligned and has a size of four consecutive
+   pages (except the panic stack with only one page).  */
+
+enum s390_lk_stack_location
+{
+  S390_LK_STACK_INVALID = -1,
+  S390_LK_STACK_USER,
+  S390_LK_STACK_KERNEL,
+  S390_LK_STACK_ASYNC,
+  S390_LK_STACK_PANIC,
+  S390_LK_STACK_RESTART
+};
+
+/* Get value of gpr REGNUM stored in CACHE.  */
+
+static inline struct value *
+s390_lk_unwind_get_gpr (struct s390_lk_unwind_cache *cache, int regnum)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  if (!(cache->gprs_p & (1 << gpr)))
+    return frame_unwind_got_optimized (cache->frame, regnum);
+
+  /* When we hit a zero_backchain R15 is the (computed) lowest address of
+     pt_regs.  */
+  if (regnum == S390_R15_REGNUM && cache->zero_backchain)
+    return frame_unwind_got_address (cache->frame, regnum, cache->gprs[gpr]);
+
+  return frame_unwind_got_memory (cache->frame, regnum, cache->gprs[gpr]);
+}
+
+/* Store ADDR of gpr REGNUM in CACHE.  */
+
+static inline void
+s390_lk_unwind_set_gpr (struct s390_lk_unwind_cache *cache, int regnum,
+			CORE_ADDR addr)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  cache->gprs[gpr] = addr;
+  cache->gprs_p |= (1 << gpr);
+}
+
+/* Allocate and initialize a new s390_lk_unwind_cache.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_alloc_unwind_cache ()
+{
+  struct s390_lk_unwind_cache *cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct s390_lk_unwind_cache);
+
+  cache->frame = NULL;
+  cache->frame_base = -1;
+  cache->zero_backchain = false;
+
+  cache->sp = -1;
+  cache->pc = -1;
+  cache->pswm = -1;
+  cache->pswa = -1;
+  for (CORE_ADDR *gpr = cache->gprs; gpr < cache->gprs + S390_NUM_GPRS; gpr++)
+    *gpr = -1;
+  cache->gprs_p = 0;
+
+  return cache;
+}
+
+/* Helper macro for s390_lk_get_stack_location to check for stacks which
+   locations are stored in the lowcore.
+
+      _addr	address to be checked for
+      _lc	address of the corresponding lowcore
+      _stack	field name of stack in lowcore
+      _type	type to be returned if _addr is on _stack
+      _size	size of _stack
+*/
+
+#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size)	\
+  do									\
+    {									\
+      CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom;		\
+      _##_stack = lk_read_addr ((_lc) + LK_OFFSET (lowcore, _stack));	\
+      _##_stack##_top = S390_LK_ROUNDUP (_##_stack, S390_LK_PAGESIZE);	\
+      _##_stack##_bottom = _##_stack##_top - (_size);			\
+      if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom)	\
+	return (_type);							\
+    }									\
+  while (0)
+
+/* Find and return the stack location of ADDR belonging to TASK.  */
+
+static enum s390_lk_stack_location
+s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
+{
+  CORE_ADDR lowcore, top, bottom;
+  unsigned int cpu = lk_task_running (task);
+
+
+  /* Kernel stack.  */
+  bottom = lk_read_addr (task + LK_OFFSET (task_struct, stack));
+  top = bottom + S390_LK_STACKSIZE;
+  if (addr <= top && addr >= bottom)
+    return S390_LK_STACK_KERNEL;
+
+  /* A sleeping task only has the kernel stack.  If a sleeping task reaches
+     this point ADDR isn't on the stack.  */
+  if (cpu == LK_CPU_INVAL)
+    return S390_LK_STACK_INVALID;
+
+  lowcore = s390_lk_get_lowcore (cpu);
+
+  /* Async aka. interrupt stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
+			       S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
+
+  /* Panic stack.
+     Note: The panic stack only has a size of one page.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
+			       S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
+
+  /* Restart stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
+			       S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
+
+  return S390_LK_STACK_INVALID;
+}
+
+
+/* Unwind a single NORMAL_FRAME, i.e. a struct stack_frame.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_frame_unwind_cache (struct frame_info *this_frame,
+			    void **this_cache)
+{
+  struct s390_lk_unwind_cache *cache;
+  CORE_ADDR backchain, backchain_addr, prev_gpr15;
+
+  if (*this_cache)
+    return (struct s390_lk_unwind_cache *) *this_cache;
+
+  cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For NORMAL_FRAMES, cache->sp == lowest address of struct stack_frame.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = get_frame_pc (this_frame);
+  /* Choose the frame's base to be gpr15 + sizeof (stack_frame) to be
+     complient with DWARF's DW_CFA_def_cfa.  Otherwise gdbarch_inner_than
+     can trigger false stack corruption asserts, as
+
+     next_dwarf_frame_base = next_backchain_frame_base + sizeof (stack_frame)
+			   > this_backchain_frame_base
+  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (LK_STRUCT (stack_frame));
+
+  backchain_addr = cache->sp + LK_OFFSET (stack_frame, back_chain);
+  backchain = lk_read_addr (backchain_addr);
+
+  /* A zero backchain marks the end of this stack.  This can lead to two
+     actions (handled by SIGTRAMP_FRAME unwinder):
+       * stop unwinding (if sp at the end of the kernel stack)
+       * jump to next stack location (otherwise).
+  */
+  if (backchain == 0)
+    {
+      CORE_ADDR pt_regs;
+
+      pt_regs = cache->sp + TYPE_LENGTH (LK_STRUCT (stack_frame));
+
+      {
+	/* A bug in the kernel (linux 3.10+) adds the stack overhead twice
+	   for swapper tasks (!= swapper/0) with the information written in
+	   the first (higher address) pt_regs.  Compensate this by updating
+	   the stack pointer.  */
+	enum s390_lk_stack_location stack;
+
+	CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+	long pid = frame_get_thread (this_frame)->ptid.lwp ();
+	stack = s390_lk_get_stack_location (task, cache->sp);
+	if (pid == 0 && task != LK_ADDR (init_task)
+	    && stack == S390_LK_STACK_KERNEL)
+	  pt_regs += (TYPE_LENGTH (LK_STRUCT (stack_frame))
+		      + TYPE_LENGTH (LK_STRUCT (pt_regs)));
+      }
+
+      cache->zero_backchain = true;
+      s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, pt_regs);
+
+      /* Use pc = 0 to mark end of stack.  */
+      s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, backchain_addr);
+      return cache;
+    }
+
+  /* Do sanity check.  */
+  prev_gpr15 = lk_read_addr (backchain
+			     + s390_lk_stack_frame_gpr (S390_R15_REGNUM));
+  if (backchain != prev_gpr15)
+    {
+      size_t ptr_len = lk_builtin_type_size (unsigned_long);
+      CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+      error (_("Stack corruption for task 0x%s at stack frame 0x%s with "
+	       "back_chain 0x%s detected %s.  back_chain->gpr15 != back_chain."),
+	     phex (task, ptr_len), phex (cache->sp, ptr_len),
+	     phex (backchain, ptr_len), phex (prev_gpr15, ptr_len));
+    }
+
+  s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, backchain_addr);
+  s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM,
+			  backchain + s390_lk_stack_frame_gpr (S390_R14_REGNUM));
+
+  return cache;
+}
+
+/* Function for frame_unwind->this_id hook for NORMAL_FRAMEs.  */
+
+static void
+s390_lk_frame_this_id (struct frame_info *this_frame,
+		       void **this_cache,
+		       struct frame_id *this_id)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* If we don't have a stack frame...  */
+  if (cache->sp == -1)
+    {
+      /* ...but a PC.  We have a inline frame.  */
+      if (cache->pc != -1)
+	*this_id = frame_id_build_unavailable_stack (cache->pc);
+
+      return;
+    }
+
+  *this_id = frame_id_build (cache->frame_base, cache->pc);
+}
+
+/* Function for frame_unwind->prev_register hook for NORMAL_ and
+   SIGTRAMP_FRAMEs.  */
+
+static struct value *
+s390_lk_frame_prev_register (struct frame_info *this_frame,
+			     void **this_cache, int regnum)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* Also handles S390_SP_REGNUM and S390_RETADDR_REGNUM as they are mapped to
+     R15 and R14 respectfully in s390-tdep.h.  */
+  if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
+    return s390_lk_unwind_get_gpr (cache, regnum);
+
+  /* Use PSWM stored in pt_regs if available.  */
+  if (regnum == S390_PSWM_REGNUM)
+    return (cache->pswm != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswm)
+	    : frame_unwind_got_optimized (cache->frame, regnum));
+
+  /* Use PSWA stored in pt_regs if available, R14/PC otherwise.  */
+  if (regnum == S390_PSWA_REGNUM)
+    return (cache->pswa != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswa)
+	    : s390_lk_unwind_get_gpr (cache, S390_RETADDR_REGNUM));
+
+  if (regnum >= S390_NUM_REGS)
+    return s390_unwind_pseudo_register (cache->frame, regnum);
+
+  /* Default is not saved.  */
+  return frame_unwind_got_optimized (cache->frame, regnum);
+}
+
+/* Simple backchain unwinder for Linux kernel on S390.  Its main purpose is
+   to handle stack frames created in Assembler code without debug
+   infromation.  */
+
+static const struct frame_unwind s390_lk_frame_unwind = {
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_frame_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+
+/* Unwind a single SIGTRAMP_FRAME, i.e. a struct pt_regs.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_sigtramp_unwind_cache (struct frame_info *this_frame,
+			       void **this_cache)
+{
+  struct frame_info *next_frame;
+  struct s390_lk_unwind_cache *cache;
+  CORE_ADDR psw;
+
+  if (*this_cache)
+    return (struct s390_lk_unwind_cache *) *this_cache;
+
+  cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For SIGTRAMP_FRAMES, cache->sp = lowest address of struct pt_regs.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = cache->sp + s390_lk_pt_regs_gpr (S390_R14_REGNUM);
+  /* Choose the frame base to be SP + sizeof (pt_regs) aka. the end of this
+     stack.  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (LK_STRUCT (pt_regs));
+
+  psw = cache->sp + LK_OFFSET (pt_regs, psw);
+  cache->pswm = psw + LK_OFFSET (psw_t, mask);
+  cache->pswa = psw + LK_OFFSET (psw_t, addr);
+
+  for (int i = S390_R0_REGNUM; i <= S390_R15_REGNUM; i++)
+    s390_lk_unwind_set_gpr (cache, i, cache->sp + s390_lk_pt_regs_gpr (i));
+
+  return cache;
+}
+
+/* Function for frame_unwind->this_id hook for SIGTRAMP_FRAMEs.  */
+
+static void
+s390_lk_sigtramp_this_id (struct frame_info *this_frame,
+			  void **this_cache,
+			  struct frame_id *this_id)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_sigtramp_unwind_cache (this_frame, this_cache);
+
+  CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+
+  switch (s390_lk_get_stack_location (task, cache->sp))
+    {
+      /* If we are on an interrupt stack keep on unwinding.  */
+      case S390_LK_STACK_ASYNC:
+      case S390_LK_STACK_PANIC:
+      case S390_LK_STACK_RESTART:
+	*this_id = frame_id_build (cache->frame_base, cache->pc);
+	return;
+
+      /* If we are on the kernel stack check whether we are at the end of
+	 the stack.  If so stop unwinding otherwise continue.  */
+      case S390_LK_STACK_KERNEL:
+	{
+	  CORE_ADDR top;
+
+	  top = lk_read_addr (task + LK_OFFSET (task_struct, stack));
+	  top += S390_LK_STACKSIZE;
+
+	  /* swapper/0 doesn't have a pt_regs at the top of its stack, so in
+	     this case cache->sp pointing at the top of the last stack_frame
+	     points at the end of the stack.  */
+	  if (task == LK_ADDR (init_task) && top <= cache->sp)
+	    *this_id = outer_frame_id;
+
+	  /* For all other tasks cache->frame_base pointing at the top of
+	     pt_regs points at the end of stack.  */
+	  else if (top <= cache->frame_base)
+	    *this_id = outer_frame_id;
+
+	  /* The program check handler appends his stack to the kernel stack.
+	     If that is the case simply keep on unwinding.  */
+	  else
+	    *this_id = frame_id_build (cache->frame_base, cache->pc);
+
+	  return;
+	}
+
+      /* Otherwise somewhere something went wrong.  */
+      default:
+	{
+	  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+	  error (_("Couldn't find find valid stack for task 0x%s with stack "
+		   "pointer 0x%s.  Stack corruption?"),
+		   phex (task, ptr_len), phex (cache->sp, ptr_len));
+	}
+    }
+}
+
+/* Function for frame_unwind->sniffer hook for SIGTRAMP_FRAMEs.  */
+
+static int
+s390_lk_sigtramp_sniffer (const struct frame_unwind *self,
+			  struct frame_info *this_frame,
+			  void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+  if (!gdbarch_lk_init_private_p (gdbarch))
+    return 0;
+
+  CORE_ADDR pc = get_frame_pc (this_frame);
+
+  return pc == 0;
+}
+
+/* Sigtramp unwinder for Linux kernel on S390.  It handles situations when
+   the end of a stack, i.e. a zero backchain, was detected.  */
+
+static const struct frame_unwind s390_lk_sigtramp_unwind = {
+  SIGTRAMP_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_sigtramp_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  s390_lk_sigtramp_sniffer
+};
+
+
+/* Frame base handling.  */
+
+static CORE_ADDR
+s390_lk_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+  return cache->frame_base;
+}
+
+static const struct frame_base s390_lk_frame_base = {
+  &s390_lk_frame_unwind,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address
+};
+
+
+/* Hooks for linux-kernel target.  */
+
+/* Function for Linux kernel target get_registers hook.  Supplies gprs for
+   task TASK to REGCACHE.  Uses r14 (back jump address) as current pswa.  */
+
+void
+s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
+		       struct regcache *regcache, int regnum)
+{
+  const struct regset *regset;
+  CORE_ADDR ksp, gprs, pswa;
+  gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */
+  size_t size;
+
+  regset = &s390x_gregset_lk;
+
+  ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
+		      + LK_OFFSET (thread_struct, ksp));
+  gprs = ksp + LK_OFFSET (stack_frame, gprs);
+  size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
+  gdb_assert (size <= sizeof (buf));
+
+  read_memory (gprs, buf, size);
+  regset->supply_regset (regset, regcache, -1, buf, size);
+}
+
+/* Function for Linux kernel target get_percpu_offset hook.  Returns the
+   percpu_offset from lowcore for cpu CPU.  */
+
+CORE_ADDR
+s390_lk_get_percpu_offset (unsigned int cpu)
+{
+  CORE_ADDR lowcore = s390_lk_get_lowcore (cpu);
+  return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset));
+}
+
+/* Function for Linux kernel target map_running_task_to_cpu hook.  */
+
+unsigned int
+s390_lk_map_running_task_to_cpu (struct thread_info *ti)
+{
+  struct regcache *regcache;
+  enum register_status reg_status;
+  CORE_ADDR lowcore;
+  unsigned int cpu;
+
+  regcache = get_thread_regcache (ti->ptid);
+  reg_status = regcache_raw_read_unsigned (regcache, S390_PREFIX_REGNUM,
+					   (ULONGEST *) &lowcore);
+  if (reg_status != REG_VALID)
+    error (_("Could not find prefix register for thread with pid %d, lwp %li."),
+	   ti->ptid.pid (), ti->ptid.lwp ());
+
+  cpu = lk_read_uint (lowcore + LK_OFFSET (lowcore, cpu_nr));
+
+  return cpu;
+}
+
+/* Function for Linux kernel target is_kvaddr hook.  */
+
+int
+s390_lk_is_kvaddr (CORE_ADDR addr)
+{
+  return addr >= LK_ADDR (high_memory);
+}
+
+/* Read table entry from TABLE at offset OFFSET.  Helper for s390_lk_vtop.  */
+
+static inline ULONGEST
+s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
+{
+  return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
+}
+
+/* Function for Linux kernel target vtop hook.  Assume 64 bit addresses.  */
+
+CORE_ADDR
+s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
+{
+  ULONGEST entry, offset;
+  CORE_ADDR paddr;
+  unsigned int table_type;
+  size_t addr_size = lk_builtin_type_size (unsigned_long);
+
+  /* Read first entry in table to get its type.  As the Table-Type bits are
+     the same in every table assume Region1-Table.  */
+  entry = s390_lk_read_table_entry (table, 0);
+  table_type = (entry & S390_LK_RFTE_TT) >> 2;
+
+  switch (table_type)
+    {
+      case S390_LK_DAT_TT_REGION1:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RFX) >> 53;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-first-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-first-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RFTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-first-table entry."),
+		     phex (vaddr, addr_size));
+
+	  table = entry & S390_LK_RFTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_REGION2:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RSX) >> 42;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-second-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-second-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RSTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-second-table entry."),
+		     phex (vaddr, addr_size));
+
+	  table = entry & S390_LK_RSTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_REGION3:
+	{
+	  offset = (vaddr & S390_LK_VADDR_RTX) >> 31;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "region-third-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in region-third-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_RTTE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "region-third-table entry."),
+		     phex (vaddr, addr_size));
+
+	  /* Check for huge page.  */
+	  if (entry & S390_LK_RTTE_FC)
+	    {
+	      paddr = ((entry & S390_LK_RTTE_RFAA)
+		       + (vaddr & ~S390_LK_RTTE_RFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_RTTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_SEGMENT:
+	{
+	  offset = (vaddr & S390_LK_VADDR_SX) >> 20;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address 0x%s with empty "\
+		       "segment-table entry."),
+		   phex (vaddr, addr_size));
+	  else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
+	    warning (_("Trying to translate address 0x%s with corrupt "\
+		       "table type in segment-table entry."),
+		     phex (vaddr, addr_size));
+	  else if (entry & S390_LK_STE_I)
+	    warning (_("Translating address 0x%s with invalid bit set at "\
+		       "segment-table entry."),
+		     phex (vaddr, addr_size));
+
+	  /* Check for large page.  */
+	  if (entry & S390_LK_STE_FC)
+	    {
+	      paddr = ((entry & S390_LK_STE_SFAA)
+		       + (vaddr & ~S390_LK_STE_SFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_STE_O;
+	  break;
+	}
+    } /* switch (table_type) */
+
+  offset = (vaddr & S390_LK_VADDR_PX) >> 12;
+  entry = s390_lk_read_table_entry (table, offset);
+
+  /* Do sanity checks.  */
+  if (!entry)
+    warning (_("Trying to translate address 0x%s with empty page-table "\
+	       "entry."),
+	     phex (vaddr, addr_size));
+  else if (entry & S390_LK_PTE_I)
+    warning (_("Translating address 0x%s with invalid bit set at page-table "\
+	       "entry."),
+	     phex (vaddr, addr_size));
+
+  paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA));
+
+  return paddr;
+}
+
+/* Function for Linux kernel target adjust_module_layout hook.  */
+
+void
+s390_lk_adjust_module_layout (CORE_ADDR mod, lk_module *module)
+{
+  CORE_ADDR mod_arch;
+
+  mod_arch = mod + LK_OFFSET (module, arch);
+
+  module->core->text.base
+    += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_offset));
+  module->core->text.base
+    += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
+}
+
+/* Initialize s390 dependent private data for linux kernel target.  */
+
+static void
+s390_lk_init_private (struct gdbarch *gdbarch)
+{
+  LK_DECLARE_FIELD (stack_frame, gprs);
+  LK_DECLARE_FIELD (stack_frame, back_chain);
+
+  LK_DECLARE_FIELD (pt_regs, psw);
+  LK_DECLARE_FIELD (pt_regs, gprs);
+
+  LK_DECLARE_FIELD (task_struct, stack);
+
+  LK_DECLARE_FIELD (thread_struct, ksp);
+
+  LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
+  LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
+  if (LK_STRUCT (lowcore) == NULL)
+    error (_("Could not find struct lowcore.  Aborting."));
+  LK_DECLARE_FIELD (lowcore, percpu_offset);
+  LK_DECLARE_FIELD (lowcore, cpu_nr);
+  LK_DECLARE_FIELD (lowcore, async_stack);
+  LK_DECLARE_FIELD (lowcore, panic_stack);
+  LK_DECLARE_FIELD (lowcore, restart_stack);
+
+  LK_DECLARE_FIELD (psw_t, mask);
+  LK_DECLARE_FIELD (psw_t, addr);
+
+  LK_DECLARE_FIELD (mod_arch_specific, plt_offset);
+  LK_DECLARE_FIELD (mod_arch_specific, plt_size);
+
+  LK_DECLARE_ADDR (lowcore_ptr);
+  LK_DECLARE_ADDR (high_memory);
+
+  LK_HOOK->get_registers = s390_lk_get_registers;
+  LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
+  LK_HOOK->vtop = s390_lk_vtop;
+  LK_HOOK->adjust_module_layout = s390_lk_adjust_module_layout;
+  LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
+  LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
+}
+
+/* Initialize Linux kernel specific gdbarch hooks.  */
+
+void
+s390_lk_gdbarch_init (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  /* Frame handling.  */
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_sigtramp_unwind);
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_frame_unwind);
+
+  /* Support linux kernel debugging.  */
+  set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
+}
+
+extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_s390_lk_tdep (void)
+{
+  /* Initialize the Linux kernel target descriptions.  */
+  initialize_tdesc_s390x_cr_linux64 ();
+  initialize_tdesc_s390x_vxcr_linux64 ();
+}
diff --git a/gdb/s390-lk-tdep.h b/gdb/s390-lk-tdep.h
new file mode 100644
index 0000000000..4e54e7d236
--- /dev/null
+++ b/gdb/s390-lk-tdep.h
@@ -0,0 +1,54 @@
+/* Target-dependent code for linux-kernel target on S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef S390_LK_TDEP_H
+#define S390_LK_TDEP_H
+
+/* Constants copied from kernel sources.  */
+#define S390_LK_PAGESIZE (1UL << 12)
+#define S390_LK_STACKSIZE (S390_LK_PAGESIZE << 2)
+#define S390_LK_ASYNCSIZE (S390_LK_STACKSIZE)
+#define S390_LK_ROUNDUP(val, base) ((val) + (base) - ((val) % (base)))
+
+/* Short hand access to gprs stored in struct pt_regs.  */
+#define s390_lk_pt_regs_gpr(regnum)					\
+  (LK_OFFSET (pt_regs, gprs)						\
+   + (FIELD_TARGET_SIZE (LK_FIELD (pt_regs, gprs))			\
+      * ((regnum) - S390_R0_REGNUM)))					\
+
+/* Short hand access to gprs stored in struct stack_frame.  */
+#define s390_lk_stack_frame_gpr(regnum)					\
+  (LK_OFFSET (stack_frame, gprs)					\
+   + (FIELD_TARGET_SIZE (LK_FIELD (stack_frame, gprs))			\
+      * ((regnum) - S390_R6_REGNUM)))					\
+
+extern void s390_lk_gdbarch_init (struct gdbarch_info info,
+				  struct gdbarch *gdbarch);
+
+/* Core file privileged register sets, defined in s390-lk-tdep.c.  */
+extern const struct regset s390x_gregset_lk;
+extern const struct regset s390x_cr_regset;
+extern const struct regset s390x_timer_regset;
+extern const struct regset s390x_todcmp_regset;
+extern const struct regset s390x_todpreg_regset;
+extern const struct regset s390x_prefix_regset;
+
+extern struct target_desc *tdesc_s390x_cr_linux64;
+extern struct target_desc *tdesc_s390x_vxcr_linux64;
+
+#endif /* S390_LK_TDEP_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 7d2906f294..e40fea120a 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -35,6 +35,7 @@
 #include "record-full.h"
 #include "reggroups.h"
 #include "s390-linux-tdep.h"
+#include "s390-lk-tdep.h"
 #include "s390-tdep.h"
 #include "target-descriptions.h"
 #include "value.h"
@@ -962,7 +963,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
 {
   asection *section = bfd_get_section_by_name (abfd, ".reg");
   CORE_ADDR hwcap = 0;
-  int high_gprs, v1, v2, te, vx;
+  int high_gprs, v1, v2, te, vx, cr;
 
   target_auxv_search (target, AT_HWCAP, &hwcap);
   if (!section)
@@ -974,6 +975,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
   v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL);
   vx = (hwcap & HWCAP_S390_VX);
   te = (hwcap & HWCAP_S390_TE);
+  cr = (bfd_get_section_by_name (abfd, ".reg-s390-ctrs") != NULL);
 
   switch (bfd_section_size (abfd, section))
     {
@@ -990,6 +992,8 @@ s390_core_read_description (struct gdbarch *gdbarch,
 
     case s390x_sizeof_gregset:
       return (te && vx ? tdesc_s390x_tevx_linux64 :
+	      vx && cr ? tdesc_s390x_vxcr_linux64 :
+	      cr ? tdesc_s390x_cr_linux64 :
 	      vx ? tdesc_s390x_vx_linux64 :
 	      te ? tdesc_s390x_te_linux64 :
 	      v2 ? tdesc_s390x_linux64v2 :
@@ -1046,6 +1050,22 @@ s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
       cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
 	  "s390 vector registers 16-31", cb_data);
     }
+
+  /* Privileged registers for kernel debugging.  */
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-timer"))
+    cb (".reg-s390-timer", 8, &s390x_timer_regset, "s390 timer", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todcmp"))
+    cb (".reg-s390-todcmp", 8, &s390x_todcmp_regset,
+	"s390 clock comperator", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todpreg"))
+    cb (".reg-s390-todpreg", 4, &s390x_todpreg_regset,
+	"s390 TOD programable register", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-ctrs"))
+    cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset,
+	"s390 control registers", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-prefix"))
+    cb (".reg-s390-prefix", 4, &s390x_prefix_regset,
+	"s390 prefix area", cb_data);
 }
 
 
@@ -2302,6 +2322,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   int have_linux_v2 = 0;
   int have_tdb = 0;
   int have_vx = 0;
+  int have_privileged = 0;
   int first_pseudo_reg, last_pseudo_reg;
   static const char *const stap_register_prefixes[] = { "%", NULL };
   static const char *const stap_register_indirection_prefixes[] = { "(",
@@ -2476,6 +2497,30 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	  have_vx = 1;
 	}
 
+      /* Control registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr");
+      if (feature)
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_CR0_REGNUM + i, cr[i]);
+	}
+
+      /* Privileged registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged");
+      if (feature)
+	{
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TIMER_REGNUM, "timer");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODCMP_REGNUM, "todcmp");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODPREG_REGNUM, "todpreg");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_PREFIX_REGNUM, "prefix");
+	  have_privileged = 1;
+	}
+
       if (!valid_p)
 	{
 	  tdesc_data_cleanup (tdesc_data);
@@ -2648,8 +2693,14 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_valid_disassembler_options (gdbarch,
 					  disassembler_options_s390 ());
 
-  /* Note that GNU/Linux is the only OS supported on this platform.  */
-  s390_linux_gdbarch_init (info, gdbarch);
+  /* Note that GNU/Linux is the only OS supported on this platform.
+     Nevertheless we need to distinguish between a kernel- and user-space
+     gdbarch.  GDBs osabi support is not sufficient for our case as for both
+     cases the ELF bits are for Linux.  Thus fallback to this method.  */
+  if (have_privileged)
+    s390_lk_gdbarch_init (info, gdbarch);
+  else
+    s390_linux_gdbarch_init (info, gdbarch);
 
   return gdbarch;
 }
-- 
2.11.2

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-12 17:08 ` [RFC v4 3/9] Add basic Linux kernel support Philipp Rudo
@ 2017-06-15 15:23   ` Yao Qi
  2017-06-16 10:10     ` Philipp Rudo
  0 siblings, 1 reply; 24+ messages in thread
From: Yao Qi @ 2017-06-15 15:23 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

> /* Initialize a private data entry for an address, where NAME is the name
> +   of the symbol, i.e. variable name in Linux, ALIAS the name used to
> +   retrieve the entry from hashtab, and SILENT a flag to determine if
> +   errors should be ignored.
> +
> +   Returns a pointer to the new entry.  In case of an error, either returns
> +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If SILENT = TRUE
> +   the caller is responsible to check for errors.
> +
> +   Do not use directly, use LK_DECLARE_* macros defined in lk-low.h instead.  */
> +
> +struct lk_private_data *
> +lk_init_addr (const char *name, const char *alias, int silent)

s/bool/int/

> +{
> +  struct lk_private_data *data;
> +  struct bound_minimal_symbol bmsym;
> +  void **new_slot;
> +  void *old_slot;
> +
> +  if ((old_slot = lk_find (alias)) != NULL)
> +    return (struct lk_private_data *) old_slot;
> +
> +  bmsym = lookup_minimal_symbol (name, NULL, NULL);
> +
> +  if (bmsym.minsym == NULL)
> +    {
> +      if (!silent)
> +	error (_("Could not find address %s.  Aborting."), alias);
> +      return NULL;
> +    }
> +
> +  data = XCNEW (struct lk_private_data);
> +  data->alias = alias;
> +  data->data.addr = BMSYMBOL_VALUE_ADDRESS (bmsym);
> +
> +  new_slot = lk_find_slot (alias);
> +  *new_slot = data;
> +
> +  return data;
> +}
> +
> +/* Same as lk_init_addr but for structs.  */
> +
> +struct lk_private_data *
> +lk_init_struct (const char *name, const char *alias, int silent)

Likewise, bool silent.

> +{
> +  struct lk_private_data *data;
> +  const struct block *global;
> +  const struct symbol *sym;
> +  struct type *type;
> +  void **new_slot;
> +  void *old_slot;
> +
> +  if ((old_slot = lk_find (alias)) != NULL)
> +    return (struct lk_private_data *) old_slot;
> +
> +  global = block_global_block(get_selected_block (0));
> +  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;
> +
> +  if (sym != NULL)
> +    {
> +      type = SYMBOL_TYPE (sym);
> +      goto out;
> +    }
> +
> +  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
> +  sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;
> +  if (sym == NULL)
> +    goto error;
> +
> +  type = check_typedef (SYMBOL_TYPE (sym));
> +
> +  if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
> +    goto out;

These two "goto" can be removed.

> +
> +error:
> +  if (!silent)
> +    error (_("Could not find %s.  Aborting."), alias);
> +
> +  return NULL;
> +
> +out:
> +  data = XCNEW (struct lk_private_data);
> +  data->alias = alias;
> +  data->data.type = type;
> +
> +  new_slot = lk_find_slot (alias);
> +  *new_slot = data;
> +
> +  return data;
> +}
> +
> +
> +/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
> +   returns an array of ulongs.  The caller is responsible to free the array
> +   after it is no longer needed.  */
> +
> +ULONGEST *
> +lk_read_bitmap (CORE_ADDR addr, size_t size)
> +{
> +  ULONGEST *bitmap;
> +  size_t ulong_size, len;
> +
> +  ulong_size = lk_builtin_type_size (unsigned_long);
> +  len = LK_DIV_ROUND_UP (size, ulong_size * LK_BITS_PER_BYTE);
> +  bitmap = XNEWVEC (ULONGEST, len);
> +
> +  for (size_t i = 0; i < len; i++)
> +    bitmap[i] = lk_read_ulong (addr + i * ulong_size);
> +
> +  return bitmap;
> +}
> +
> +/* Return the next set bit in bitmap BITMAP of size SIZE (in bits)
> +   starting from bit (index) BIT.  Return SIZE when the end of the bitmap
> +   was reached.  To iterate over all set bits use macro
> +   LK_BITMAP_FOR_EACH_SET_BIT defined in lk-low.h.  */
> +
> +size_t
> +lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t size, size_t bit)
> +{
> +  size_t ulong_size, bits_per_ulong, elt;
> +
> +  ulong_size = lk_builtin_type_size (unsigned_long);
> +  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
> +  elt = bit / bits_per_ulong;
> +
> +  while (bit < size)
> +    {
> +      /* FIXME: Explain why using lsb0 bit order.  */
> +      if (bitmap[elt] & (1UL << (bit % bits_per_ulong)))
> +	return bit;
> +
> +      bit++;
> +      if (bit % bits_per_ulong == 0)
> +	elt++;
> +    }
> +
> +  return size;
> +}
> +
> +/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP
> +   with size SIZE (in bits).  */
> +
> +size_t
> +lk_bitmap_hweight (ULONGEST *bitmap, size_t size)
> +{
> +  size_t ulong_size, bit, bits_per_ulong, elt, retval;
> +
> +  ulong_size = lk_builtin_type_size (unsigned_long);
> +  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
> +  elt = bit = 0;
> +  retval = 0;
> +
> +  while (bit < size)
> +    {
> +      if (bitmap[elt] & (1 << bit % bits_per_ulong))
> +	retval++;
> +
> +      bit++;
> +      if (bit % bits_per_ulong == 0)
> +	elt++;
> +    }
> +
> +  return retval;
> +}
> +

Could you add some unit tests to these operations to bitmap?  I am
thinking that we need a class BitMap or BitVector later.

> +/* Provide the per_cpu_offset of cpu CPU.  See comment in lk-low.h for
> +   details.  */
> +
> +CORE_ADDR
> +lk_get_percpu_offset (unsigned int cpu)
> +{
> +  size_t ulong_size = lk_builtin_type_size (unsigned_long);
> +  CORE_ADDR percpu_elt;
> +
> +  /* Give the architecture a chance to overwrite default behaviour.  */
> +  if (LK_HOOK->get_percpu_offset)
> +      return LK_HOOK->get_percpu_offset (cpu);
> +
> +  percpu_elt = LK_ADDR (__per_cpu_offset) + (ulong_size * cpu);
> +  return lk_read_addr (percpu_elt);
> +}
> +
> +


> +
> +/* Function for targets to_update_thread_list hook.  */
> +
> +static void
> +lk_update_thread_list (struct target_ops *target)
> +{
> +  prune_threads ();
> +  lk_update_running_tasks ();
> +  lk_update_sleeping_tasks ();
> +}
> +
> +/* Function for targets to_fetch_registers hook.  */
> +
> +static void
> +lk_fetch_registers (struct target_ops *target,
> +		    struct regcache *regcache, int regnum)
> +{
> +  CORE_ADDR task;
> +  unsigned int cpu;
> +
> +  task = (CORE_ADDR) regcache_get_ptid (regcache).tid ();
> +  cpu = lk_task_running (task);
> +
> +  /* Let the target beneath fetch registers of running tasks.  */
> +  if (cpu != LK_CPU_INVAL)
> +    {
> +      struct cleanup *old_inferior_ptid;
> +
> +      old_inferior_ptid = save_inferior_ptid ();
> +      inferior_ptid = lk_cpu_to_old_ptid (cpu);

Use make_scoped_restore (&inferior_ptid, lk_cpu_to_old_ptid (cpu));?

> +      linux_kernel_ops->beneath->to_fetch_registers (target, regcache, regnum);
> +      do_cleanups (old_inferior_ptid);
> +    }
> +  else
> +    {
> +      struct gdbarch *gdbarch;
> +      unsigned int i;
> +
> +      LK_HOOK->get_registers (task, target, regcache, regnum);
> +
> +      /* Mark all registers not found as unavailable.  */
> +      gdbarch = get_regcache_arch (regcache);
> +      for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
> +	{
> +	  if (regcache_register_status (regcache, i) == REG_UNKNOWN)
> +	    regcache_raw_supply (regcache, i, NULL);
> +	}
> +    }
> +}
> +

> +
> +/* Functions to initialize and free target_ops and its private data.  As well

This line is too long.

> +   as functions for targets to_open/close/detach hooks.  */
> +
> +/* Check if OBFFILE is a Linux kernel.  */
> +


> +
> +/* Initialize the cpu to old ptid map.  Prefer the arch dependent
> +   map_running_task_to_cpu hook if provided, else assume that the PID used
> +   by target beneath is the same as in task_struct PID task_struct.  See
> +   comment on lk_ptid_map in lk-low.h for details.  */
> +
> +static void
> +lk_init_ptid_map ()
> +{
> +  struct thread_info *ti;
> +  ULONGEST *cpu_online_mask;
> +  size_t size;
> +  unsigned int cpu;
> +  struct cleanup *old_chain;
> +
> +  if (LK_PRIVATE->old_ptid != NULL)
> +    lk_free_ptid_map ();
> +
> +  size = LK_BITMAP_SIZE (cpumask);
> +  cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
> +  old_chain = make_cleanup (xfree, cpu_online_mask);
> +
> +  ALL_THREADS (ti)
> +    {
> +      struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
> +      CORE_ADDR rq, curr;
> +      int pid;
> +
> +      /* Give the architecture a chance to overwrite default behaviour.  */
> +      if (LK_HOOK->map_running_task_to_cpu)
> +	{
> +	  ptid_map->cpu = LK_HOOK->map_running_task_to_cpu (ti);
> +	}
> +      else
> +	{
> +	  LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
> +	    {
> +	      rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
> +	      curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
> +	      pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
> +
> +	      if (pid == ti->ptid.lwp ())
> +		{
> +		  ptid_map->cpu = cpu;
> +		  break;
> +		}
> +	    }
> +	  if (cpu == size)
> +	    error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
> +		   ti->ptid.pid (), ti->ptid.lwp ());
> +	}
> +      ptid_map->old_ptid = ti->ptid;
> +      ptid_map->next = LK_PRIVATE->old_ptid;
> +      LK_PRIVATE->old_ptid = ptid_map;
> +    }
> +
> +  do_cleanups (old_chain);
> +}
> +

> diff --git a/gdb/lk-low.h b/gdb/lk-low.h
> new file mode 100644
> index 0000000000..be8c5556df
> --- /dev/null
> +++ b/gdb/lk-low.h
> @@ -0,0 +1,310 @@
> +/* Basic Linux kernel support, architecture independent.
> +
> +   Copyright (C) 2016 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef __LK_LOW_H__
> +#define __LK_LOW_H__
> +
> +#include "target.h"
> +
> +extern struct target_ops *linux_kernel_ops;
> +
> +/* Copy constants defined in Linux kernel.  */
> +#define LK_TASK_COMM_LEN 16
> +#define LK_BITS_PER_BYTE 8
> +
> +/* Definitions used in linux kernel target.  */
> +#define LK_CPU_INVAL -1U
> +
> +/* Private data structs for this target.  */
> +/* Forward declarations.  */
> +struct lk_private_hooks;
> +struct lk_ptid_map;
> +
> +/* Short hand access to private data.  */
> +#define LK_PRIVATE ((struct lk_private *) linux_kernel_ops->to_data)
> +#define LK_HOOK (LK_PRIVATE->hooks)
> +
> +struct lk_private
> +{
> +  /* Hashtab for needed addresses, structs and fields.  */
> +  htab_t data;
> +
> +  /* Linked list to map between cpu number and original ptid from target
> +     beneath.  */
> +  struct lk_ptid_map *old_ptid;
> +
> +  /* Hooks for architecture dependent functions.  */
> +  struct lk_private_hooks *hooks;
> +};
> +
> +/* We use the following convention for PTIDs:
> +
> +   ptid->pid = inferiors PID
> +   ptid->lwp = PID from task_stuct
> +   ptid->tid = address of task_struct
> +
> +   The task_structs address as TID has two reasons.  First, we need it quite
> +   often and there is no other reasonable way to pass it down.  Second, it
> +   helps us to distinguish swapper tasks as they all have PID = 0.
> +
> +   Furthermore we cannot rely on the target beneath to use the same PID as the
> +   task_struct. Thus we need a mapping between our PTID and the PTID of the
> +   target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
> +   registers of running tasks, to the target beneath.  */

Sorry, I don't understand this design.  Can you elaborate?

> +
> +/* Private data struct to map between our and the target beneath PTID.  */
> +
> +struct lk_ptid_map
> +{
> +  struct lk_ptid_map *next;

Can you use C++ stl list instead?

> +  unsigned int cpu;
> +  ptid_t old_ptid;
> +};
> +
> +/* Private data struct to be stored in hashtab.  */
> +
> +struct lk_private_data
> +{
> +  const char *alias;
> +
> +  union
> +  {
> +    CORE_ADDR addr;
> +    struct type *type;
> +    struct field *field;
> +  } data;
> +};
> +
> +/* Wrapper for htab_hash_string to work with our private data.  */
> +
> +static inline hashval_t
> +lk_hash_private_data (const struct lk_private_data *entry)
> +{
> +  return htab_hash_string (entry->alias);
> +}
> +
> +/* Function for htab_eq to work with our private data.  */
> +
> +static inline int
> +lk_private_data_eq (const struct lk_private_data *entry,
> +		    const struct lk_private_data *element)
> +{
> +  return streq (entry->alias, element->alias);
> +}
> +
> +/* Wrapper for htab_find_slot to work with our private data.  Do not use
> +   directly, use the macros below instead.  */
> +
> +static inline void **
> +lk_find_slot (const char *alias)
> +{
> +  const struct lk_private_data dummy = { alias };
> +  return htab_find_slot (LK_PRIVATE->data, &dummy, INSERT);
> +}
> +
> +/* Wrapper for htab_find to work with our private data.  Do not use
> +   directly, use the macros below instead.  */
> +
> +static inline struct lk_private_data *
> +lk_find (const char *alias)
> +{
> +  const struct lk_private_data dummy = { alias };
> +  return (struct lk_private_data *) htab_find (LK_PRIVATE->data, &dummy);
> +}
> +

> +
> +struct lk_private_hooks
> +{
> +  /* required */
> +  lk_hook_get_registers get_registers;
> +
> +  /* optional, required if __per_cpu_offset array is not used to determine
> +     offset.  */
> +  lk_hook_get_percpu_offset get_percpu_offset;
> +
> +  /* optional, required if the target beneath uses a different PID as struct
> +     rq.  */
> +  lk_hook_map_running_task_to_cpu map_running_task_to_cpu;
> +};

The lk_private_hooks is still not a class.  I raised this in v3 review,
https://sourceware.org/ml/gdb-patches/2017-05/msg00004.html

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-15 15:23   ` Yao Qi
@ 2017-06-16 10:10     ` Philipp Rudo
  2017-06-16 11:43       ` Pedro Alves
  2017-06-19  9:52       ` Yao Qi
  0 siblings, 2 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-16 10:10 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Hi Yao


On Thu, 15 Jun 2017 16:23:39 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > /* Initialize a private data entry for an address, where NAME is the name
> > +   of the symbol, i.e. variable name in Linux, ALIAS the name used to
> > +   retrieve the entry from hashtab, and SILENT a flag to determine if
> > +   errors should be ignored.
> > +
> > +   Returns a pointer to the new entry.  In case of an error, either returns
> > +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If SILENT =
> > TRUE
> > +   the caller is responsible to check for errors.
> > +
> > +   Do not use directly, use LK_DECLARE_* macros defined in lk-low.h
> > instead.  */ +
> > +struct lk_private_data *
> > +lk_init_addr (const char *name, const char *alias, int silent)  
> 
> s/bool/int/

you are right with that.

The reason why I haven't changed it yet was that I wanted to c++-ify the whole
file in one go.  My problem is that I'm currently also working on an other
project, which needs to be finished soon. That's why I can't spend all my time
on GDB at the moment.  That's why I decided it is better to give Omair a
working v4, even when it does not contain all changes.

For the same reason I also deferred changes like classifying lk_private, htab
-> std::unordered_map, cleanup -> scoped_resore etc.

> > +{
> > +  struct lk_private_data *data;
> > +  struct bound_minimal_symbol bmsym;
> > +  void **new_slot;
> > +  void *old_slot;
> > +
> > +  if ((old_slot = lk_find (alias)) != NULL)
> > +    return (struct lk_private_data *) old_slot;
> > +
> > +  bmsym = lookup_minimal_symbol (name, NULL, NULL);
> > +
> > +  if (bmsym.minsym == NULL)
> > +    {
> > +      if (!silent)
> > +	error (_("Could not find address %s.  Aborting."), alias);
> > +      return NULL;
> > +    }
> > +
> > +  data = XCNEW (struct lk_private_data);
> > +  data->alias = alias;
> > +  data->data.addr = BMSYMBOL_VALUE_ADDRESS (bmsym);
> > +
> > +  new_slot = lk_find_slot (alias);
> > +  *new_slot = data;
> > +
> > +  return data;
> > +}
> > +
> > +/* Same as lk_init_addr but for structs.  */
> > +
> > +struct lk_private_data *
> > +lk_init_struct (const char *name, const char *alias, int silent)  
> 
> Likewise, bool silent.
> 
> > +{
> > +  struct lk_private_data *data;
> > +  const struct block *global;
> > +  const struct symbol *sym;
> > +  struct type *type;
> > +  void **new_slot;
> > +  void *old_slot;
> > +
> > +  if ((old_slot = lk_find (alias)) != NULL)
> > +    return (struct lk_private_data *) old_slot;
> > +
> > +  global = block_global_block(get_selected_block (0));
> > +  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;
> > +
> > +  if (sym != NULL)
> > +    {
> > +      type = SYMBOL_TYPE (sym);
> > +      goto out;
> > +    }
> > +
> > +  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
> > +  sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;
> > +  if (sym == NULL)
> > +    goto error;
> > +
> > +  type = check_typedef (SYMBOL_TYPE (sym));
> > +
> > +  if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
> > +    goto out;  
> 
> These two "goto" can be removed.

True, but that would lead to code duplication.  That's why I chose the way
using the two goto.

Without goto the function reads like this

struct lk_private_data *                                                        
lk_init_struct (const char *name, const char *alias, int silent)                
{                                                                               
  struct lk_private_data *data;                                                 
  const struct block *global;                                                   
  const struct symbol *sym;                                                     
  struct type *type;                                                            
  void **new_slot;                                                              
  void *old_slot;                                                               
                                                                                
  if ((old_slot = lk_find (alias)) != NULL)                                     
    return (struct lk_private_data *) old_slot;                                 
                                                                                
  global = block_global_block(get_selected_block (0));                          
  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;               
                                                                                
  if (sym != NULL)                                                              
    {                                                                           
      type = SYMBOL_TYPE (sym);                                                 
    }                                                                           
  else                                                                          
    {                                                                           
      /*  Chek for "typedef struct { ... } name;"-like definitions.  */         
      sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;              
      if (sym == NULL)                                                          
        {                                                                       
          if (!silent)                                                          
            error (_("Could not find %s.  Aborting."), alias);                  
                                                                                
          return NULL;                                                          
        }                                                                       
                                                                                
      type = check_typedef (SYMBOL_TYPE (sym));                                 
      if (TYPE_CODE (type) != TYPE_CODE_STRUCT)                                 
        {                                                                       
          if (!silent)                                                          
            error (_("Could not find %s.  Aborting."), alias);                  
                                                                                
          return NULL;                                                          
        }                                                                       
    }                                                                           
                                                                                
  data = XCNEW (struct lk_private_data);                                        
  data->alias = alias;                                                          
  data->data.type = type;                                                       
                                                                                
  new_slot = lk_find_slot (alias);                                              
  *new_slot = data;                                                             
                                                                                
  return data;                                                                  
}

> > +
> > +error:
> > +  if (!silent)
> > +    error (_("Could not find %s.  Aborting."), alias);
> > +
> > +  return NULL;
> > +
> > +out:
> > +  data = XCNEW (struct lk_private_data);
> > +  data->alias = alias;
> > +  data->data.type = type;
> > +
> > +  new_slot = lk_find_slot (alias);
> > +  *new_slot = data;
> > +
> > +  return data;
> > +}
> > +
> > +
> > +/* Reads a bitmap at a given ADDRess of size SIZE (in bits). Allocates and
> > +   returns an array of ulongs.  The caller is responsible to free the array
> > +   after it is no longer needed.  */
> > +
> > +ULONGEST *
> > +lk_read_bitmap (CORE_ADDR addr, size_t size)
> > +{
> > +  ULONGEST *bitmap;
> > +  size_t ulong_size, len;
> > +
> > +  ulong_size = lk_builtin_type_size (unsigned_long);
> > +  len = LK_DIV_ROUND_UP (size, ulong_size * LK_BITS_PER_BYTE);
> > +  bitmap = XNEWVEC (ULONGEST, len);
> > +
> > +  for (size_t i = 0; i < len; i++)
> > +    bitmap[i] = lk_read_ulong (addr + i * ulong_size);
> > +
> > +  return bitmap;
> > +}
> > +
> > +/* Return the next set bit in bitmap BITMAP of size SIZE (in bits)
> > +   starting from bit (index) BIT.  Return SIZE when the end of the bitmap
> > +   was reached.  To iterate over all set bits use macro
> > +   LK_BITMAP_FOR_EACH_SET_BIT defined in lk-low.h.  */
> > +
> > +size_t
> > +lk_bitmap_find_next_bit (ULONGEST *bitmap, size_t size, size_t bit)
> > +{
> > +  size_t ulong_size, bits_per_ulong, elt;
> > +
> > +  ulong_size = lk_builtin_type_size (unsigned_long);
> > +  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
> > +  elt = bit / bits_per_ulong;
> > +
> > +  while (bit < size)
> > +    {
> > +      /* FIXME: Explain why using lsb0 bit order.  */
> > +      if (bitmap[elt] & (1UL << (bit % bits_per_ulong)))
> > +	return bit;
> > +
> > +      bit++;
> > +      if (bit % bits_per_ulong == 0)
> > +	elt++;
> > +    }
> > +
> > +  return size;
> > +}
> > +
> > +/* Returns the Hamming weight, i.e. number of set bits, of bitmap BITMAP
> > +   with size SIZE (in bits).  */
> > +
> > +size_t
> > +lk_bitmap_hweight (ULONGEST *bitmap, size_t size)
> > +{
> > +  size_t ulong_size, bit, bits_per_ulong, elt, retval;
> > +
> > +  ulong_size = lk_builtin_type_size (unsigned_long);
> > +  bits_per_ulong = ulong_size * LK_BITS_PER_BYTE;
> > +  elt = bit = 0;
> > +  retval = 0;
> > +
> > +  while (bit < size)
> > +    {
> > +      if (bitmap[elt] & (1 << bit % bits_per_ulong))
> > +	retval++;
> > +
> > +      bit++;
> > +      if (bit % bits_per_ulong == 0)
> > +	elt++;
> > +    }
> > +
> > +  return retval;
> > +}
> > +  
> 
> Could you add some unit tests to these operations to bitmap?  I am
> thinking that we need a class BitMap or BitVector later.

You are right on both (unittests and class BitMap).  I will see into it when I
c++-ify lk-low.  

> > +/* Provide the per_cpu_offset of cpu CPU.  See comment in lk-low.h for
> > +   details.  */
> > +
> > +CORE_ADDR
> > +lk_get_percpu_offset (unsigned int cpu)
> > +{
> > +  size_t ulong_size = lk_builtin_type_size (unsigned_long);
> > +  CORE_ADDR percpu_elt;
> > +
> > +  /* Give the architecture a chance to overwrite default behaviour.  */
> > +  if (LK_HOOK->get_percpu_offset)
> > +      return LK_HOOK->get_percpu_offset (cpu);
> > +
> > +  percpu_elt = LK_ADDR (__per_cpu_offset) + (ulong_size * cpu);
> > +  return lk_read_addr (percpu_elt);
> > +}
> > +
> > +  
> 
> 
> > +
> > +/* Function for targets to_update_thread_list hook.  */
> > +
> > +static void
> > +lk_update_thread_list (struct target_ops *target)
> > +{
> > +  prune_threads ();
> > +  lk_update_running_tasks ();
> > +  lk_update_sleeping_tasks ();
> > +}
> > +
> > +/* Function for targets to_fetch_registers hook.  */
> > +
> > +static void
> > +lk_fetch_registers (struct target_ops *target,
> > +		    struct regcache *regcache, int regnum)
> > +{
> > +  CORE_ADDR task;
> > +  unsigned int cpu;
> > +
> > +  task = (CORE_ADDR) regcache_get_ptid (regcache).tid ();
> > +  cpu = lk_task_running (task);
> > +
> > +  /* Let the target beneath fetch registers of running tasks.  */
> > +  if (cpu != LK_CPU_INVAL)
> > +    {
> > +      struct cleanup *old_inferior_ptid;
> > +
> > +      old_inferior_ptid = save_inferior_ptid ();
> > +      inferior_ptid = lk_cpu_to_old_ptid (cpu);  
> 
> Use make_scoped_restore (&inferior_ptid, lk_cpu_to_old_ptid (cpu));?
> 
> > +      linux_kernel_ops->beneath->to_fetch_registers (target, regcache,
> > regnum);
> > +      do_cleanups (old_inferior_ptid);
> > +    }
> > +  else
> > +    {
> > +      struct gdbarch *gdbarch;
> > +      unsigned int i;
> > +
> > +      LK_HOOK->get_registers (task, target, regcache, regnum);
> > +
> > +      /* Mark all registers not found as unavailable.  */
> > +      gdbarch = get_regcache_arch (regcache);
> > +      for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
> > +	{
> > +	  if (regcache_register_status (regcache, i) == REG_UNKNOWN)
> > +	    regcache_raw_supply (regcache, i, NULL);
> > +	}
> > +    }
> > +}
> > +  
> 
> > +
> > +/* Functions to initialize and free target_ops and its private data.  As
> > well  
> 
> This line is too long.

Oops. Thanks for the hint.
 
> > +   as functions for targets to_open/close/detach hooks.  */
> > +
> > +/* Check if OBFFILE is a Linux kernel.  */
> > +  
> 
> 
> > +
> > +/* Initialize the cpu to old ptid map.  Prefer the arch dependent
> > +   map_running_task_to_cpu hook if provided, else assume that the PID used
> > +   by target beneath is the same as in task_struct PID task_struct.  See
> > +   comment on lk_ptid_map in lk-low.h for details.  */
> > +
> > +static void
> > +lk_init_ptid_map ()
> > +{
> > +  struct thread_info *ti;
> > +  ULONGEST *cpu_online_mask;
> > +  size_t size;
> > +  unsigned int cpu;
> > +  struct cleanup *old_chain;
> > +
> > +  if (LK_PRIVATE->old_ptid != NULL)
> > +    lk_free_ptid_map ();
> > +
> > +  size = LK_BITMAP_SIZE (cpumask);
> > +  cpu_online_mask = lk_read_bitmap (LK_ADDR (cpu_online_mask), size);
> > +  old_chain = make_cleanup (xfree, cpu_online_mask);
> > +
> > +  ALL_THREADS (ti)
> > +    {
> > +      struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
> > +      CORE_ADDR rq, curr;
> > +      int pid;
> > +
> > +      /* Give the architecture a chance to overwrite default behaviour.  */
> > +      if (LK_HOOK->map_running_task_to_cpu)
> > +	{
> > +	  ptid_map->cpu = LK_HOOK->map_running_task_to_cpu (ti);
> > +	}
> > +      else
> > +	{
> > +	  LK_BITMAP_FOR_EACH_SET_BIT (cpu_online_mask, size, cpu)
> > +	    {
> > +	      rq = LK_ADDR (runqueues) + lk_get_percpu_offset (cpu);
> > +	      curr = lk_read_addr (rq + LK_OFFSET (rq, curr));
> > +	      pid = lk_read_int (curr + LK_OFFSET (task_struct, pid));
> > +
> > +	      if (pid == ti->ptid.lwp ())
> > +		{
> > +		  ptid_map->cpu = cpu;
> > +		  break;
> > +		}
> > +	    }
> > +	  if (cpu == size)
> > +	    error (_("Could not map thread with pid %d, lwp %lu to a
> > cpu."),
> > +		   ti->ptid.pid (), ti->ptid.lwp ());
> > +	}
> > +      ptid_map->old_ptid = ti->ptid;
> > +      ptid_map->next = LK_PRIVATE->old_ptid;
> > +      LK_PRIVATE->old_ptid = ptid_map;
> > +    }
> > +
> > +  do_cleanups (old_chain);
> > +}
> > +  
> 
> > diff --git a/gdb/lk-low.h b/gdb/lk-low.h
> > new file mode 100644
> > index 0000000000..be8c5556df
> > --- /dev/null
> > +++ b/gdb/lk-low.h
> > @@ -0,0 +1,310 @@
> > +/* Basic Linux kernel support, architecture independent.
> > +
> > +   Copyright (C) 2016 Free Software Foundation, Inc.
> > +
> > +   This file is part of GDB.
> > +
> > +   This program is free software; you can redistribute it and/or modify
> > +   it under the terms of the GNU General Public License as published by
> > +   the Free Software Foundation; either version 3 of the License, or
> > +   (at your option) any later version.
> > +
> > +   This program is distributed in the hope that it will be useful,
> > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +   GNU General Public License for more details.
> > +
> > +   You should have received a copy of the GNU General Public License
> > +   along with this program.  If not, see <http://www.gnu.org/licenses/>.
> > */ +
> > +#ifndef __LK_LOW_H__
> > +#define __LK_LOW_H__
> > +
> > +#include "target.h"
> > +
> > +extern struct target_ops *linux_kernel_ops;
> > +
> > +/* Copy constants defined in Linux kernel.  */
> > +#define LK_TASK_COMM_LEN 16
> > +#define LK_BITS_PER_BYTE 8
> > +
> > +/* Definitions used in linux kernel target.  */
> > +#define LK_CPU_INVAL -1U
> > +
> > +/* Private data structs for this target.  */
> > +/* Forward declarations.  */
> > +struct lk_private_hooks;
> > +struct lk_ptid_map;
> > +
> > +/* Short hand access to private data.  */
> > +#define LK_PRIVATE ((struct lk_private *) linux_kernel_ops->to_data)
> > +#define LK_HOOK (LK_PRIVATE->hooks)
> > +
> > +struct lk_private
> > +{
> > +  /* Hashtab for needed addresses, structs and fields.  */
> > +  htab_t data;
> > +
> > +  /* Linked list to map between cpu number and original ptid from target
> > +     beneath.  */
> > +  struct lk_ptid_map *old_ptid;
> > +
> > +  /* Hooks for architecture dependent functions.  */
> > +  struct lk_private_hooks *hooks;
> > +};
> > +
> > +/* We use the following convention for PTIDs:
> > +
> > +   ptid->pid = inferiors PID
> > +   ptid->lwp = PID from task_stuct
> > +   ptid->tid = address of task_struct
> > +
> > +   The task_structs address as TID has two reasons.  First, we need it
> > quite
> > +   often and there is no other reasonable way to pass it down.  Second, it
> > +   helps us to distinguish swapper tasks as they all have PID = 0.
> > +
> > +   Furthermore we cannot rely on the target beneath to use the same PID as
> > the
> > +   task_struct. Thus we need a mapping between our PTID and the PTID of
> >
> > +   target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
> > +   registers of running tasks, to the target beneath.  */  
> 
> Sorry, I don't understand this design.  Can you elaborate?

The target beneath reports us a "pid" it thinks is right. For example in a core
file the register sections are usually named

.reg/XXX

where XXX names the thread these registers belong to.  In user space this is
typically the pid of the thread but for kernel dumps it usually is a cpu-id
(this needn't be the logical cpu-id used in the kernel).  Because of that the
kernel ptid, we generate from task_struct, usually has a different lwp than the
same thread reported from the target beneath.  Nevertheless we need the target
beneath to give us information for running tasks.  That's why we need to
introduce a mapping between the ptid of the target beneath and the kernel ptid.

In short, this represents two different thread views (hardware vs. software
threads).

Did that answer your question? Or did you mean something different?

> > +
> > +/* Private data struct to map between our and the target beneath PTID.  */
> > +
> > +struct lk_ptid_map
> > +{
> > +  struct lk_ptid_map *next;  
> 
> Can you use C++ stl list instead?

As I already wrote Omair, my ptid_map was only meant to "somehow work" but
never to be permanent.  Managing the ptid map will be the main task for live
debugging.  That's why I think it is best when Omair changes this bit to
whatever he needs.


Philipp


> > +  unsigned int cpu;
> > +  ptid_t old_ptid;
> > +};
> > +
> > +/* Private data struct to be stored in hashtab.  */
> > +
> > +struct lk_private_data
> > +{
> > +  const char *alias;
> > +
> > +  union
> > +  {
> > +    CORE_ADDR addr;
> > +    struct type *type;
> > +    struct field *field;
> > +  } data;
> > +};
> > +
> > +/* Wrapper for htab_hash_string to work with our private data.  */
> > +
> > +static inline hashval_t
> > +lk_hash_private_data (const struct lk_private_data *entry)
> > +{
> > +  return htab_hash_string (entry->alias);
> > +}
> > +
> > +/* Function for htab_eq to work with our private data.  */
> > +
> > +static inline int
> > +lk_private_data_eq (const struct lk_private_data *entry,
> > +		    const struct lk_private_data *element)
> > +{
> > +  return streq (entry->alias, element->alias);
> > +}
> > +
> > +/* Wrapper for htab_find_slot to work with our private data.  Do not use
> > +   directly, use the macros below instead.  */
> > +
> > +static inline void **
> > +lk_find_slot (const char *alias)
> > +{
> > +  const struct lk_private_data dummy = { alias };
> > +  return htab_find_slot (LK_PRIVATE->data, &dummy, INSERT);
> > +}
> > +
> > +/* Wrapper for htab_find to work with our private data.  Do not use
> > +   directly, use the macros below instead.  */
> > +
> > +static inline struct lk_private_data *
> > +lk_find (const char *alias)
> > +{
> > +  const struct lk_private_data dummy = { alias };
> > +  return (struct lk_private_data *) htab_find (LK_PRIVATE->data, &dummy);
> > +}
> > +  
> 
> > +
> > +struct lk_private_hooks
> > +{
> > +  /* required */
> > +  lk_hook_get_registers get_registers;
> > +
> > +  /* optional, required if __per_cpu_offset array is not used to determine
> > +     offset.  */
> > +  lk_hook_get_percpu_offset get_percpu_offset;
> > +
> > +  /* optional, required if the target beneath uses a different PID as
> > struct
> > +     rq.  */
> > +  lk_hook_map_running_task_to_cpu map_running_task_to_cpu;
> > +};  
> 
> The lk_private_hooks is still not a class.  I raised this in v3 review,
> https://sourceware.org/ml/gdb-patches/2017-05/msg00004.html
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-16 10:10     ` Philipp Rudo
@ 2017-06-16 11:43       ` Pedro Alves
  2017-06-19  7:56         ` Philipp Rudo
  2017-06-19  9:52       ` Yao Qi
  1 sibling, 1 reply; 24+ messages in thread
From: Pedro Alves @ 2017-06-16 11:43 UTC (permalink / raw)
  To: Philipp Rudo, Yao Qi
  Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

On 06/16/2017 11:10 AM, Philipp Rudo wrote:
> True, but that would lead to code duplication.  That's why I chose the way
> using the two goto.
> 
> Without goto the function reads like this
> 
> struct lk_private_data *                                                        
> lk_init_struct (const char *name, const char *alias, int silent)                
> {                                         

                    
>     {                                                                           
>       /*  Chek for "typedef struct { ... } name;"-like definitions.  */         
>       sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;              
>       if (sym == NULL)                                                          
>         {                                                                       
>           if (!silent)                                                          
>             error (_("Could not find %s.  Aborting."), alias);                  
>                                                                                 
>           return NULL;                                                          
>         }                                                                       
>                                                                                 
>       type = check_typedef (SYMBOL_TYPE (sym));                                 
>       if (TYPE_CODE (type) != TYPE_CODE_STRUCT)                                 
>         {                                                                       
>           if (!silent)                                                          
>             error (_("Could not find %s.  Aborting."), alias);                  
>                                                                                 
>           return NULL;                                                          
>         }                                                                       
>     }                                                             

You could add a helper function or lambda that handles the
error return.  E.g., with a lambda it'd look something like:

lk_init_struct (const char *name, const char *alias, bool silent)                
{                                                                               
...
  auto not_found = [=] ()
  {
     if (!silent)                                                          
       error (_("Could not find %s.  Aborting."), alias);                  
     return NULL;                                                          
  };
...
       sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;              
       if (sym == NULL)
         return not_found ();
                                                                                 
       type = check_typedef (SYMBOL_TYPE (sym));                                 
       if (TYPE_CODE (type) != TYPE_CODE_STRUCT)                                 
         return not_found ();


Alternatively, since NULL return is always an error
indication, split into two functions, one that returns
NULL if not found, and another that throws on error.
The latter calls the former.  I.e., something like this:

/* Returns NULL if not found.  */
lk_private_data *
lk_init_struct_nothrow (const char *name, const char *alias)
{
...
       sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;              
       if (sym == NULL)
         return NULL;
...
  return data;
}

/* Either returns non-NULL, or throws an error.  */

lk_private_data *
lk_init_struct (const char *name, const char *alias)
{
  lk_private_data *data = lk_init_struct_nothrow (name, alias);
  if (data == NULL)
    error (_("Could not find %s.  Aborting."), alias);
  return data; 
}

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-16 11:43       ` Pedro Alves
@ 2017-06-19  7:56         ` Philipp Rudo
  0 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-19  7:56 UTC (permalink / raw)
  To: Pedro Alves
  Cc: Yao Qi, gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Hi Pedro

On Fri, 16 Jun 2017 12:43:02 +0100
Pedro Alves <palves@redhat.com> wrote:

> On 06/16/2017 11:10 AM, Philipp Rudo wrote:
> > True, but that would lead to code duplication.  That's why I chose the way
> > using the two goto.
> > 
> > Without goto the function reads like this
> > 
> > struct lk_private_data
> > * lk_init_struct (const char *name, const char *alias, int
> > silent) {                                           
> 
>                     
> >     {                                                                           
> >       /*  Chek for "typedef struct { ... } name;"-like definitions.
> > */ sym = lookup_symbol (name, global, VAR_DOMAIN,
> > NULL).symbol; if (sym ==
> > NULL)
> > { if (!silent)                                                          
> >             error (_("Could not find %s.  Aborting."),
> > alias); 
> >           return
> > NULL; }                                                                       
> >                                                                                 
> >       type = check_typedef (SYMBOL_TYPE
> > (sym)); if (TYPE_CODE (type) !=
> > TYPE_CODE_STRUCT)
> > { if (!silent)                                                          
> >             error (_("Could not find %s.  Aborting."),
> > alias); 
> >           return
> > NULL; }                                                                       
> >     }                                                               
> 
> You could add a helper function or lambda that handles the
> error return.  E.g., with a lambda it'd look something like:
> 
> lk_init_struct (const char *name, const char *alias, bool
> silent)
> { ...
>   auto not_found = [=] ()
>   {
>      if (!silent)                                                          
>        error (_("Could not find %s.  Aborting."), alias);                  
>      return NULL;                                                          
>   };
> ...
>        sym = lookup_symbol (name, global, VAR_DOMAIN,
> NULL).symbol; if (sym == NULL)
>          return not_found ();
>                                                                                  
>        type = check_typedef (SYMBOL_TYPE
> (sym)); if (TYPE_CODE (type) !=
> TYPE_CODE_STRUCT) return not_found ();
> 
> 
> Alternatively, since NULL return is always an error
> indication, split into two functions, one that returns
> NULL if not found, and another that throws on error.
> The latter calls the former.  I.e., something like this:
> 
> /* Returns NULL if not found.  */
> lk_private_data *
> lk_init_struct_nothrow (const char *name, const char *alias)
> {
> ...
>        sym = lookup_symbol (name, global, VAR_DOMAIN,
> NULL).symbol; if (sym == NULL)
>          return NULL;
> ...
>   return data;
> }
> 
> /* Either returns non-NULL, or throws an error.  */
> 
> lk_private_data *
> lk_init_struct (const char *name, const char *alias)
> {
>   lk_private_data *data = lk_init_struct_nothrow (name, alias);
>   if (data == NULL)
>     error (_("Could not find %s.  Aborting."), alias);
>   return data; 
> }

Thanks for the tip.  As I still live in a C world, I prefer your second
approach.  The code now look like this

/* Helper function for lk_init_struct.  Returns NULL if symbol could not be     
   found.  Doesn't throw an error.  */                                          
                                                                                
struct lk_private_data *                                                        
lk_init_struct_silent (const char *name, const char *alias)                     
{                                                                               
  struct lk_private_data *data;                                                 
  const struct block *global;                                                   
  const struct symbol *sym;                                                     
  struct type *type;                                                            
  void **new_slot;                                                              
  void *old_slot;                                                               
                                                                                
  if ((old_slot = lk_find (alias)) != NULL)                                     
    return (struct lk_private_data *) old_slot;                                 
                                                                                
  global = block_global_block(get_selected_block (0));                          
  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;               
                                                                                
  if (sym != NULL)                                                              
    {                                                                           
      type = SYMBOL_TYPE (sym);                                                 
    }                                                                           
  else                                                                          
    {                                                                           
      /*  Chek for "typedef struct { ... } name;"-like definitions.  */         
      sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;              
      if (sym == NULL)                                                          
        return NULL;                                                            
                                                                                
      type = check_typedef (SYMBOL_TYPE (sym));                                 
      if (TYPE_CODE (type) != TYPE_CODE_STRUCT)                                 
        return NULL;                                                            
    }                                                                           
                                                                                
  data = XCNEW (struct lk_private_data);                                        
  data->alias = alias;                                                          
  data->data.type = type;                                                       
                                                                                
  new_slot = lk_find_slot (alias);                                              
  *new_slot = data;                                                             
                                                                                
  return data;                                                                  
}                                                                               
                                                                                
/* Same as lk_init_addr but for structs.  */                                    
                                                                                
struct lk_private_data *                                                        
lk_init_struct (const char *name, const char *alias, bool silent)               
{                                                                               
  struct lk_private_data *data;                                                 
  data = lk_init_struct_silent (name, alias);                                   
                                                                                
  if (data == NULL && !silent)                                                  
    error (_("Could not find %s.  Aborting."), alias);                          
                                                                                
  return data;                                                                  
}

Philipp

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-16 10:10     ` Philipp Rudo
  2017-06-16 11:43       ` Pedro Alves
@ 2017-06-19  9:52       ` Yao Qi
  2017-06-19 13:35         ` Omair Javaid
  2017-06-19 15:44         ` Philipp Rudo
  1 sibling, 2 replies; 24+ messages in thread
From: Yao Qi @ 2017-06-19  9:52 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

>> Sorry, I don't understand this design.  Can you elaborate?
>
> The target beneath reports us a "pid" it thinks is right. For example in a core
> file the register sections are usually named
>
> .reg/XXX
>
> where XXX names the thread these registers belong to.  In user space this is
> typically the pid of the thread but for kernel dumps it usually is a cpu-id
> (this needn't be the logical cpu-id used in the kernel).  Because of that the
> kernel ptid, we generate from task_struct, usually has a different lwp than the
> same thread reported from the target beneath.  Nevertheless we need the target
> beneath to give us information for running tasks.  That's why we need to
> introduce a mapping between the ptid of the target beneath and the kernel ptid.
>
> In short, this represents two different thread views (hardware vs. software
> threads).
>
> Did that answer your question? Or did you mean something different?
>

Can we use thread_info.priv to store these information rather than using
ptid_t?

>> > +
>> > +/* Private data struct to map between our and the target beneath PTID.  */
>> > +
>> > +struct lk_ptid_map
>> > +{
>> > +  struct lk_ptid_map *next;  
>> 
>> Can you use C++ stl list instead?
>
> As I already wrote Omair, my ptid_map was only meant to "somehow work" but
> never to be permanent.  Managing the ptid map will be the main task for live
> debugging.  That's why I think it is best when Omair changes this bit to
> whatever he needs.

The lk_ptid_map is a list, and this patch still iterates it.  We need to
use C++ stl list, rather than leave it to someone else who does the
related work later.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 8/9] Link frame_info to thread_info
  2017-06-12 17:08 ` [RFC v4 8/9] Link frame_info to thread_info Philipp Rudo
@ 2017-06-19 13:07   ` Yao Qi
  2017-06-19 15:46     ` Philipp Rudo
  0 siblings, 1 reply; 24+ messages in thread
From: Yao Qi @ 2017-06-19 13:07 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

> Linux kernel stacks on S390 are spread over several memory locations.
> These locations differ for every kernel task/thread.  Thus we need to know
> to which task/thread a frame belongs to check whether a stack pointer is
> valid and when to stop unwinding.  To do so add a pointer to corresponding
> thread_info to frame_info.
>
> This connection already exists implicitly by the fact that switch_to_thread
> reinitializes the frame cache.

The whole frame cache is created for current thread, from sentinel
frame.  When the current thread is changed, the frame cache will be
re-created.  If we see different frame_info objects are about different
threads, it must be a bug somewhere.  I think frame_info.thread always
points to the current thread, no?

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 9/9] Add S390 support for linux-kernel target
  2017-06-12 17:39 ` [RFC v4 9/9] Add S390 support for linux-kernel target Philipp Rudo
@ 2017-06-19 13:20   ` Yao Qi
  2017-06-19 15:45     ` Philipp Rudo
  0 siblings, 1 reply; 24+ messages in thread
From: Yao Qi @ 2017-06-19 13:20 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

> +/* S390s kernel stack is split up on several memory locations:
> +
> +   - kernel stack (per task)
> +   - async aka. interrupt stack (per cpu)
> +   - panic stack (per cpu)
> +   - restart stack (unique, global)
> +
> +   Each memory location is page aligned and has a size of four consecutive
> +   pages (except the panic stack with only one page).  */
> +
> +enum s390_lk_stack_location
> +{
> +  S390_LK_STACK_INVALID = -1,
> +  S390_LK_STACK_USER,
> +  S390_LK_STACK_KERNEL,
> +  S390_LK_STACK_ASYNC,
> +  S390_LK_STACK_PANIC,
> +  S390_LK_STACK_RESTART
> +};
> +

Did you consider adding different frame unwinders for frames on
different memory locations?  Your patches only adds two, but IMO, we
need more unwinders for different frames,

> +
> +/* Helper macro for s390_lk_get_stack_location to check for stacks which
> +   locations are stored in the lowcore.
> +
> +      _addr	address to be checked for
> +      _lc	address of the corresponding lowcore
> +      _stack	field name of stack in lowcore
> +      _type	type to be returned if _addr is on _stack
> +      _size	size of _stack
> +*/
> +
> +#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size)	\
> +  do									\
> +    {									\
> +      CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom;		\
> +      _##_stack = lk_read_addr ((_lc) + LK_OFFSET (lowcore, _stack));	\
> +      _##_stack##_top = S390_LK_ROUNDUP (_##_stack, S390_LK_PAGESIZE);	\
> +      _##_stack##_bottom = _##_stack##_top - (_size);			\
> +      if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom)	\
> +	return (_type);							\
> +    }									\
> +  while (0)
> +
> +/* Find and return the stack location of ADDR belonging to TASK.  */
> +
> +static enum s390_lk_stack_location
> +s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
> +{
> +  CORE_ADDR lowcore, top, bottom;
> +  unsigned int cpu = lk_task_running (task);
> +
> +
> +  /* Kernel stack.  */
> +  bottom = lk_read_addr (task + LK_OFFSET (task_struct, stack));
> +  top = bottom + S390_LK_STACKSIZE;
> +  if (addr <= top && addr >= bottom)
> +    return S390_LK_STACK_KERNEL;
> +
> +  /* A sleeping task only has the kernel stack.  If a sleeping task reaches
> +     this point ADDR isn't on the stack.  */
> +  if (cpu == LK_CPU_INVAL)
> +    return S390_LK_STACK_INVALID;
> +
> +  lowcore = s390_lk_get_lowcore (cpu);
> +
> +  /* Async aka. interrupt stack.  */
> +  s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
> +			       S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
> +
> +  /* Panic stack.
> +     Note: The panic stack only has a size of one page.  */
> +  s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
> +			       S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
> +
> +  /* Restart stack.  */
> +  s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
> +			       S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
> +
> +  return S390_LK_STACK_INVALID;
> +}

The unwinders for different frames should be chained in this order.

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 7/9] Add privileged registers for s390x
  2017-06-12 17:08 ` [RFC v4 7/9] Add privileged registers for s390x Philipp Rudo
@ 2017-06-19 13:34   ` Yao Qi
  2017-06-19 15:46     ` Philipp Rudo
  0 siblings, 1 reply; 24+ messages in thread
From: Yao Qi @ 2017-06-19 13:34 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

> @@ -220,14 +223,16 @@ XMLTOC = \
>  	s390-linux64v1.xml \
>  	s390-linux64v2.xml \
>  	s390-te-linux64.xml \
> +	s390-vx-linux64.xml \
> +	s390-tevx-linux64.xml \

The list should be in alphabetical order.

>  	s390x-linux64.xml \
>  	s390x-linux64v1.xml \
>  	s390x-linux64v2.xml \
>  	s390x-te-linux64.xml \
> -	s390-tevx-linux64.xml \
> -	s390-vx-linux64.xml \

This change is not related to this patch, so can be posted separately.

> -	s390x-tevx-linux64.xml \
>  	s390x-vx-linux64.xml \
> +	s390x-cr-linux64.xml \
> +	s390x-tevx-linux64.xml \
> +	s390x-vxcr-linux64.xml \
>  	tic6x-c62x-linux.xml \
>  	tic6x-c62x.xml \
>  	tic6x-c64x-linux.xml \

-- 
Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-19  9:52       ` Yao Qi
@ 2017-06-19 13:35         ` Omair Javaid
  2017-06-19 15:44         ` Philipp Rudo
  1 sibling, 0 replies; 24+ messages in thread
From: Omair Javaid @ 2017-06-19 13:35 UTC (permalink / raw)
  To: Yao Qi; +Cc: Philipp Rudo, gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez

On 19 June 2017 at 14:52, Yao Qi <qiyaoltc@gmail.com> wrote:
> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
>
>>> Sorry, I don't understand this design.  Can you elaborate?
>>
>> The target beneath reports us a "pid" it thinks is right. For example in a core
>> file the register sections are usually named
>>
>> .reg/XXX
>>
>> where XXX names the thread these registers belong to.  In user space this is
>> typically the pid of the thread but for kernel dumps it usually is a cpu-id
>> (this needn't be the logical cpu-id used in the kernel).  Because of that the
>> kernel ptid, we generate from task_struct, usually has a different lwp than the
>> same thread reported from the target beneath.  Nevertheless we need the target
>> beneath to give us information for running tasks.  That's why we need to
>> introduce a mapping between the ptid of the target beneath and the kernel ptid.
>>
>> In short, this represents two different thread views (hardware vs. software
>> threads).
>>
>> Did that answer your question? Or did you mean something different?
>>
>
> Can we use thread_info.priv to store these information rather than using
> ptid_t?
>
>>> > +
>>> > +/* Private data struct to map between our and the target beneath PTID.  */
>>> > +
>>> > +struct lk_ptid_map
>>> > +{
>>> > +  struct lk_ptid_map *next;
>>>
>>> Can you use C++ stl list instead?
>>
>> As I already wrote Omair, my ptid_map was only meant to "somehow work" but
>> never to be permanent.  Managing the ptid map will be the main task for live
>> debugging.  That's why I think it is best when Omair changes this bit to
>> whatever he needs.

Hi Phillip,

I have sent you my changes where I have also changed lk_ptid_map to a
c++ map data type.

Please have a look at those before making any further changes to your patches.

Thanks!


>
> The lk_ptid_map is a list, and this patch still iterates it.  We need to
> use C++ stl list, rather than leave it to someone else who does the
> related work later.
>
> --
> Yao (齐尧)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 3/9] Add basic Linux kernel support
  2017-06-19  9:52       ` Yao Qi
  2017-06-19 13:35         ` Omair Javaid
@ 2017-06-19 15:44         ` Philipp Rudo
  1 sibling, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-19 15:44 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

On Mon, 19 Jun 2017 10:52:05 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> >> Sorry, I don't understand this design.  Can you elaborate?  
> >
> > The target beneath reports us a "pid" it thinks is right. For example in a
> > core file the register sections are usually named
> >
> > .reg/XXX
> >
> > where XXX names the thread these registers belong to.  In user space this is
> > typically the pid of the thread but for kernel dumps it usually is a cpu-id
> > (this needn't be the logical cpu-id used in the kernel).  Because of that
> > the kernel ptid, we generate from task_struct, usually has a different lwp
> > than the same thread reported from the target beneath.  Nevertheless we
> > need the target beneath to give us information for running tasks.  That's
> > why we need to introduce a mapping between the ptid of the target beneath
> > and the kernel ptid.
> >
> > In short, this represents two different thread views (hardware vs. software
> > threads).
> >
> > Did that answer your question? Or did you mean something different?
> >  
> 
> Can we use thread_info.priv to store these information rather than using
> ptid_t?


We cannot use thread_info.priv because the remote target already uses it.  This was the
main reason why Peter abandoned his patch.

> >> > +
> >> > +/* Private data struct to map between our and the target beneath PTID.
> >> > */ +
> >> > +struct lk_ptid_map
> >> > +{
> >> > +  struct lk_ptid_map *next;    
> >> 
> >> Can you use C++ stl list instead?  
> >
> > As I already wrote Omair, my ptid_map was only meant to "somehow work" but
> > never to be permanent.  Managing the ptid map will be the main task for live
> > debugging.  That's why I think it is best when Omair changes this bit to
> > whatever he needs.  
> 
> The lk_ptid_map is a list, and this patch still iterates it.  We need to
> use C++ stl list, rather than leave it to someone else who does the
> related work later.

With respect to Omairs mail I think this is obsolete.

Philipp

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 9/9] Add S390 support for linux-kernel target
  2017-06-19 13:20   ` Yao Qi
@ 2017-06-19 15:45     ` Philipp Rudo
  0 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-19 15:45 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Hi Yao

On Mon, 19 Jun 2017 14:20:34 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > +/* S390s kernel stack is split up on several memory locations:
> > +
> > +   - kernel stack (per task)
> > +   - async aka. interrupt stack (per cpu)
> > +   - panic stack (per cpu)
> > +   - restart stack (unique, global)
> > +
> > +   Each memory location is page aligned and has a size of four consecutive
> > +   pages (except the panic stack with only one page).  */
> > +
> > +enum s390_lk_stack_location
> > +{
> > +  S390_LK_STACK_INVALID = -1,
> > +  S390_LK_STACK_USER,
> > +  S390_LK_STACK_KERNEL,
> > +  S390_LK_STACK_ASYNC,
> > +  S390_LK_STACK_PANIC,
> > +  S390_LK_STACK_RESTART
> > +};
> > +  
> 
> Did you consider adding different frame unwinders for frames on
> different memory locations?  Your patches only adds two, but IMO, we
> need more unwinders for different frames,

All those stacks (except user space) are working the same way.  That's why it
is sufficient to only add one unwinder.  In theory it is sufficient to only
distinguish three cases (KERNEL, INVALID and "OTHER") but for the sake of
completeness I also added the other possible cases.  This makes the code only a
little longer but makes extending it (e.g. to compensate a bug as been done for
the kernel stack) much easier.

Philipp

> > +
> > +/* Helper macro for s390_lk_get_stack_location to check for stacks which
> > +   locations are stored in the lowcore.
> > +
> > +      _addr	address to be checked for
> > +      _lc	address of the corresponding lowcore
> > +      _stack	field name of stack in lowcore
> > +      _type	type to be returned if _addr is on _stack
> > +      _size	size of _stack
> > +*/
> > +
> > +#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type,
> > _size)	\
> > +
> > do									\
> > +
> > {									\
> > +      CORE_ADDR _##_stack, _##_stack##_top,
> > _##_stack##_bottom;		\
> > +      _##_stack = lk_read_addr ((_lc) + LK_OFFSET (lowcore,
> > _stack));	\
> > +      _##_stack##_top = S390_LK_ROUNDUP (_##_stack,
> > S390_LK_PAGESIZE);	\
> > +      _##_stack##_bottom = _##_stack##_top -
> > (_size);			\
> > +      if ((_addr) <= _##_stack##_top && (_addr) >=
> > _##_stack##_bottom)	\
> > +	return
> > (_type);							\
> > +    }
> > \
> > +  while (0)
> > +
> > +/* Find and return the stack location of ADDR belonging to TASK.  */
> > +
> > +static enum s390_lk_stack_location
> > +s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
> > +{
> > +  CORE_ADDR lowcore, top, bottom;
> > +  unsigned int cpu = lk_task_running (task);
> > +
> > +
> > +  /* Kernel stack.  */
> > +  bottom = lk_read_addr (task + LK_OFFSET (task_struct, stack));
> > +  top = bottom + S390_LK_STACKSIZE;
> > +  if (addr <= top && addr >= bottom)
> > +    return S390_LK_STACK_KERNEL;
> > +
> > +  /* A sleeping task only has the kernel stack.  If a sleeping task reaches
> > +     this point ADDR isn't on the stack.  */
> > +  if (cpu == LK_CPU_INVAL)
> > +    return S390_LK_STACK_INVALID;
> > +
> > +  lowcore = s390_lk_get_lowcore (cpu);
> > +
> > +  /* Async aka. interrupt stack.  */
> > +  s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
> > +			       S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
> > +
> > +  /* Panic stack.
> > +     Note: The panic stack only has a size of one page.  */
> > +  s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
> > +			       S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
> > +
> > +  /* Restart stack.  */
> > +  s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
> > +			       S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
> > +
> > +  return S390_LK_STACK_INVALID;
> > +}  
> 
> The unwinders for different frames should be chained in this order.
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 8/9] Link frame_info to thread_info
  2017-06-19 13:07   ` Yao Qi
@ 2017-06-19 15:46     ` Philipp Rudo
  0 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-19 15:46 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Hi Yao

On Mon, 19 Jun 2017 14:07:11 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > Linux kernel stacks on S390 are spread over several memory locations.
> > These locations differ for every kernel task/thread.  Thus we need to know
> > to which task/thread a frame belongs to check whether a stack pointer is
> > valid and when to stop unwinding.  To do so add a pointer to corresponding
> > thread_info to frame_info.
> >
> > This connection already exists implicitly by the fact that switch_to_thread
> > reinitializes the frame cache.  
> 
> The whole frame cache is created for current thread, from sentinel
> frame.  When the current thread is changed, the frame cache will be
> re-created.  If we see different frame_info objects are about different
> threads, it must be a bug somewhere.  I think frame_info.thread always
> points to the current thread, no?

That's also the way I understand it how it should work.

My problem with it is that such implicit connections via global variables are
extremely error prone and hard to debug.  For example look at
linux-tdep.c:linux_corefile_thread, here the code looks like this

static void                                                                     
linux_corefile_thread (struct thread_info *info,
                       struct linux_corefile_thread_data *args)
{
  [...]

  old_chain = save_inferior_ptid ();
  inferior_ptid = info->ptid;
  target_fetch_registers (regcache, -1);

  [...]
}

At this point the inferior_ptid is changed without re-creating the frame
cache.  Thus the assumption that a existing frame_info always belongs to
current_thread is wrong at this point.  What makes it worse is that right after
a target hook is called.  So suddenly you have a connection between a
target_obs and a gdbarch you'd never expect which can lead to a bug...

For me the easiest way to prevent such long range bugs is not to use global
variables but explicit connections between the different subsystems.

That's why I made this patch.

Philipp

P.S. I know that the example is bad and you shouldn't use the frame cache to
fetch registers but I hope you get the point I was trying to make.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC v4 7/9] Add privileged registers for s390x
  2017-06-19 13:34   ` Yao Qi
@ 2017-06-19 15:46     ` Philipp Rudo
  0 siblings, 0 replies; 24+ messages in thread
From: Philipp Rudo @ 2017-06-19 15:46 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, omair.javaid, yao.qi, peter.griffin, arnez

Hi Yao

On Mon, 19 Jun 2017 14:34:08 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > @@ -220,14 +223,16 @@ XMLTOC = \
> >  	s390-linux64v1.xml \
> >  	s390-linux64v2.xml \
> >  	s390-te-linux64.xml \
> > +	s390-vx-linux64.xml \
> > +	s390-tevx-linux64.xml \  
> 
> The list should be in alphabetical order.

You are right.  I will fix it ...

> >  	s390x-linux64.xml \
> >  	s390x-linux64v1.xml \
> >  	s390x-linux64v2.xml \
> >  	s390x-te-linux64.xml \
> > -	s390-tevx-linux64.xml \
> > -	s390-vx-linux64.xml \  
> 
> This change is not related to this patch, so can be posted separately.

... and post it in a separate patch.

Philipp

> > -	s390x-tevx-linux64.xml \
> >  	s390x-vx-linux64.xml \
> > +	s390x-cr-linux64.xml \
> > +	s390x-tevx-linux64.xml \
> > +	s390x-vxcr-linux64.xml \
> >  	tic6x-c62x-linux.xml \
> >  	tic6x-c62x.xml \
> >  	tic6x-c64x-linux.xml \  
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2017-06-19 15:46 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-12 17:08 [RFC v4 0/9] Support for Linux kernel debugging Philipp Rudo
2017-06-12 17:08 ` [RFC v4 4/9] Add kernel module support for linux-kernel target Philipp Rudo
2017-06-12 17:08 ` [RFC v4 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
2017-06-12 17:08 ` [RFC v4 5/9] Add commands for linux-kernel target Philipp Rudo
2017-06-12 17:08 ` [RFC v4 7/9] Add privileged registers for s390x Philipp Rudo
2017-06-19 13:34   ` Yao Qi
2017-06-19 15:46     ` Philipp Rudo
2017-06-12 17:08 ` [RFC v4 3/9] Add basic Linux kernel support Philipp Rudo
2017-06-15 15:23   ` Yao Qi
2017-06-16 10:10     ` Philipp Rudo
2017-06-16 11:43       ` Pedro Alves
2017-06-19  7:56         ` Philipp Rudo
2017-06-19  9:52       ` Yao Qi
2017-06-19 13:35         ` Omair Javaid
2017-06-19 15:44         ` Philipp Rudo
2017-06-12 17:08 ` [RFC v4 1/9] Convert substitute_path_component to C++ Philipp Rudo
2017-06-12 17:08 ` [RFC v4 8/9] Link frame_info to thread_info Philipp Rudo
2017-06-19 13:07   ` Yao Qi
2017-06-19 15:46     ` Philipp Rudo
2017-06-12 17:09 ` [RFC v4 6/9] Separate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
2017-06-12 17:39 ` [RFC v4 9/9] Add S390 support for linux-kernel target Philipp Rudo
2017-06-19 13:20   ` Yao Qi
2017-06-19 15:45     ` Philipp Rudo
2017-06-12 21:17 ` Philipp Rudo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).