public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC v3 1/8] Convert substitute_path_component to C++
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
@ 2017-03-16 16:57 ` Philipp Rudo
  2017-04-20 20:02   ` Sergio Durigan Junior
  2017-03-16 16:58 ` [RFC v3 6/8] Seperate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:57 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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     | 50 ++++++++++++++------------------------------------
 gdb/utils.h     |  9 +++++++--
 3 files changed, 30 insertions(+), 47 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 56914c8..c84fee1 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 27021a1..bef619a 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -66,6 +66,8 @@
 #include "interps.h"
 #include "gdb_regex.h"
 
+#include <string>
+
 #if !HAVE_DECL_MALLOC
 extern PTR malloc ();		/* ARI: PTR */
 #endif
@@ -3158,49 +3160,25 @@ 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;
-
-      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))
+      char start, end;
+      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 f138702..d32114e 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);
 
 char *ldirname (const char *filename);
 
-- 
2.8.4

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

* [RFC v3 0/8] Support for Linux kernel debugging
@ 2017-03-16 16:57 Philipp Rudo
  2017-03-16 16:57 ` [RFC v3 1/8] Convert substitute_path_component to C++ Philipp Rudo
                   ` (7 more replies)
  0 siblings, 8 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:57 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

Hi everybody

here is v3 of the patch series.  Andreas also pushed it to a new branch
(users/arnez/lk3) on Sourceware.

To make colaboration easier and prevent merge conflicts we (Peter, Yao,
Andreas and I) decided to take v3 as the basis for further development.
That means future patches will be added on top of the branch and finally
merged to reasonable patches once the feature is ready to go upstream.  I
hope there are no objections against this procedure.

New in v3:
	* Rebase to current master.
	* configure.tgt: Fix build bug when not compiled with
	--enable-targets=all.
	* Makefile.in: Move all lk-*.o files to ALL_TARGET_OBS.
	* lk-low.c (lk_init_addr): Use lookup_minimal_symbol instead of parser.
	(lk_init_struct): Use lookup_symbol instead of parser.
	(lk_init_field): Adjust calling signature.
	(lk_fetch_registers): Use ptid from regcache instead of inferior_ptid.
	* lk-low.h: Adjust LK_*_FIELD macros.

Philipp Rudo (8):
  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
  Seperate common s390-tdep.* from s390-linux-tdep.*
  Add privileged registers for s390x
  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/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                          |  937 +++++++++
 gdb/lk-low.h                          |  334 ++++
 gdb/lk-modules.c                      |  412 ++++
 gdb/lk-modules.h                      |   29 +
 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                 | 3427 +--------------------------------
 gdb/s390-linux-tdep.h                 |  178 +-
 gdb/s390-lk-tdep.c                    |  390 ++++
 gdb/s390-lk-tdep.h                    |   36 +
 gdb/s390-tdep.c                       | 3408 ++++++++++++++++++++++++++++++++
 gdb/s390-tdep.h                       |  384 ++++
 gdb/solib.c                           |    8 +
 gdb/solib.h                           |    5 +
 gdb/typeprint.c                       |    8 +-
 gdb/typeprint.h                       |    2 +
 gdb/utils.c                           |   88 +-
 gdb/utils.h                           |   26 +-
 36 files changed, 7061 insertions(+), 3633 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.8.4

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

* [RFC v3 8/8] Add S390 support for linux-kernel target
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
  2017-03-16 16:57 ` [RFC v3 1/8] Convert substitute_path_component to C++ Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 6/8] Seperate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 5/8] Add commands " Philipp Rudo
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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 | 390 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-lk-tdep.h |  36 +++++
 gdb/s390-tdep.c    |  57 +++++++-
 gdb/s390-tdep.h    |  62 +++++++++
 6 files changed, 548 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 2933b08..d6ea773 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -863,6 +863,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 \
@@ -1421,6 +1422,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 \
@@ -2620,6 +2622,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 a6dc662..c89e9ee 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -482,8 +482,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 0000000..7ff71b8
--- /dev/null
+++ b/gdb/s390-lk-tdep.c
@@ -0,0 +1,390 @@
+/* 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 "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "lk-low.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
+};
+
+/* 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_ptr, lowcore;
+  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+
+  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
+  lowcore = lk_read_addr (lowcore_ptr);
+
+  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 get_module_text_offset hook.  */
+
+CORE_ADDR
+s390_lk_get_module_text_offset (CORE_ADDR mod)
+{
+  CORE_ADDR offset, mod_arch;
+
+  mod_arch = mod + LK_OFFSET (module, arch);
+  offset = lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, got_size));
+  offset += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
+
+  return offset;
+}
+
+/* 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 (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, current_pid);
+  LK_DECLARE_FIELD (lowcore, cpu_nr);
+
+  LK_DECLARE_FIELD (mod_arch_specific, got_size);
+  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->get_percpu_offset = s390_lk_get_percpu_offset;
+  LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
+  LK_HOOK->get_module_text_offset = s390_lk_get_module_text_offset;
+}
+
+/* Initialize Linux kernel specific gdbarch hooks.  */
+
+void
+s390_gdbarch_lk_init (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  /* 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 0000000..68bfc96
--- /dev/null
+++ b/gdb/s390-lk-tdep.h
@@ -0,0 +1,36 @@
+/* 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
+
+extern void s390_gdbarch_lk_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 78757fa..f4ebda8 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -40,6 +40,7 @@
 #include "reggroups.h"
 #include "regset.h"
 #include "s390-linux-tdep.h"
+#include "s390-lk-tdep.h"
 #include "s390-tdep.h"
 #include "target-descriptions.h"
 #include "trad-frame.h"
@@ -1032,7 +1033,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)
@@ -1044,6 +1045,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))
     {
@@ -1060,6 +1062,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 :
@@ -1116,6 +1120,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);
 }
 
 
@@ -2986,6 +3006,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[] = { "(",
@@ -3160,6 +3181,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);
@@ -3340,8 +3385,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_gdbarch_linux_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_gdbarch_lk_init (info, gdbarch);
+  else
+    s390_gdbarch_linux_init (info, gdbarch);
 
   return gdbarch;
 }
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
index 8d0da72..0df8a27 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -319,4 +319,66 @@ enum
 #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
+
 #endif /* S390_TDEP_H */
-- 
2.8.4

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

* [RFC v3 3/8] Add basic Linux kernel support
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
                   ` (3 preceding siblings ...)
  2017-03-16 16:58 ` [RFC v3 5/8] Add commands " Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-04-16 22:59   ` Omair Javaid
                     ` (2 more replies)
  2017-03-16 16:58 ` [RFC v3 4/8] Add kernel module support for linux-kernel target Philipp Rudo
                   ` (2 subsequent siblings)
  7 siblings, 3 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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..

Currently the target_ops is designed only to work with static targets, i.e.
dumps. Thus it lacks implementation for hooks like to_wait, to_resume or
to_store_registers. Furthermore the mapping between a CPU and the
task_struct of the running task is only be done once at initialization. See
cover letter for a detailed discussion.

Nevertheless i made some design decisions different to Peter [1] which are
worth discussing. Especially storing the private data in a htab (or
std::unordered_map if i had the time...) instead of global variables makes
the code much nicer and less memory consuming.

[1] https://sourceware.org/ml/gdb-patches/2016-12/msg00382.html

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      | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-low.h      | 310 ++++++++++++++++++++
 9 files changed, 1301 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 0818742..9387c66 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -817,6 +817,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 \
@@ -1103,6 +1105,8 @@ SFILES = \
 	jit.c \
 	language.c \
 	linespec.c \
+	lk-lists.c \
+	lk-low.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1350,6 +1354,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 \
@@ -2547,6 +2553,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 cb909e7..8d87fea 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -34,6 +34,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
@@ -479,7 +483,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 87eafb2..5509a6c 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -349,6 +349,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
@@ -1139,6 +1140,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,
@@ -5008,6 +5015,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 34f82a7..c03bf00 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 39b1f94..cad45d1 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1167,6 +1167,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 0000000..55d11bd
--- /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 0000000..f9c2a85
--- /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 0000000..768f228
--- /dev/null
+++ b/gdb/lk-low.c
@@ -0,0 +1,833 @@
+/* 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;
+      ptid_t new_ptid, old_ptid;
+
+      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;
+
+      new_ptid = ptid_build (inf_pid, pid, curr);
+      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;
+	  ptid_t ptid;
+	  struct thread_info *tp;
+
+	  pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
+	  ptid = ptid_build (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) ptid_get_tid (regcache_get_ptid (regcache));
+  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 char *
+lk_pid_to_str (struct target_ops *target, ptid_t ptid)
+{
+  static char buf[64];
+  long pid;
+  CORE_ADDR task;
+
+  pid = ptid_get_lwp (ptid);
+  task = (CORE_ADDR) ptid_get_tid (ptid);
+
+  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) ptid_get_tid (ti->ptid);
+  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 == ptid_get_lwp (ti->ptid))
+		{
+		  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)
+	  && ptid_get_pid (inferior_ptid) != 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)
+      && ptid_get_pid (inferior_ptid) != 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 (ptid_get_pid (inferior_ptid) == 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 0000000..292ef97
--- /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 determin 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 numinator N by demoniator 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.8.4

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

* [RFC v3 2/8] Add libiberty/concat styled concat_path function
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
                   ` (6 preceding siblings ...)
  2017-03-16 16:58 ` [RFC v3 7/8] Add privileged registers for s390x Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  7 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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 618d266..47fdeeb 100644
--- a/gdb/common/common-utils.h
+++ b/gdb/common/common-utils.h
@@ -83,6 +83,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 bef619a..9722d8d 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3181,6 +3181,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 d32114e..d9dbaec 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)
+
 char *ldirname (const char *filename);
 
 extern int count_path_elements (const char *path);
-- 
2.8.4

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

* [RFC v3 7/8] Add privileged registers for s390x
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
                   ` (5 preceding siblings ...)
  2017-03-16 16:58 ` [RFC v3 4/8] Add kernel module support for linux-kernel target Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 2/8] Add libiberty/concat styled concat_path function Philipp Rudo
  7 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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 3bc8b5a..ac34c4e 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 0000000..15ae4f9
--- /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 0000000..caf535b
--- /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 0000000..d38bc35
--- /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 0000000..36453d2
--- /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 0000000..30a1ceb
--- /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 0000000..e8a4dd7
--- /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 0000000..8c28fd1
--- /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 53f50fe..78757fa 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -915,9 +915,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 21dcb12..8d0da72 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -274,9 +274,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.8.4

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

* [RFC v3 6/8] Seperate common s390-tdep.* from s390-linux-tdep.*
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
  2017-03-16 16:57 ` [RFC v3 1/8] Convert substitute_path_component to C++ Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 8/8] Add S390 support for linux-kernel target Philipp Rudo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

The new linux-kernel target need some architecture dependant code.  To
prepare for this split up the existing s390 code into a general s390-tedep
and a GDNU/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_gdbarch_linux_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_gdbarch_linux_init): ...this and adjust.
	(_initialize_s390_tdep): Rename to...
	(_initialize_s390_linux_tdep): ...this and adjust.
	(s390_abi_kind, s390_vector_abi_kind): Move to s390-tdep.h
	(gdbarch_tdep, enum named opcodes): Move to s390-tdep.h
	(s390_readinstruction, is_ri, is_ril): Move to s390-tdep.c
	(is_rr, is_rre, is_rs, is_rsy, is_rsi, is_rie): Move to s390-tdep.c
	(is_rx, is_rxy, s390_break_insn): Move to s390-tdep.c
	(s390_breakpoint, s390_is_partial_instruction): Move to s390-tdep.c
	(s390_software_single_step, s390_prologue_data): Move to s390-tdep.c
	(s390_addr, s390_store, s390_load): Move to s390-tdep.c
	(s390_check_for_saved, s390_analyze_prologue): Move to s390-tdep.c
	(s390_skip_prologue, s390_register_call_saved): Move to s390-tdep.c
	(s390_register_name, s390_cannot_store_register): Move to s390-tdep.c
	(s390_write_pc, s390_dwarf_regmap): Move to s390-tdep.c
	(s390_dwarf_reg_to_regnum, regnum_is_gpr_full): Move to s390-tdep.c
	(regnum_is_vxr_full, s390_value_from_register): Move to s390-tdep.c
	(s390_core_read_description): Move to s390-tdep.c
	(s390_iterate_over_regset_sections): Move to s390-tdep.c
	(s390_pseudo_register_name): Move to s390-tdep.c
	(s390_pseudo_register_read): Move to s390-tdep.c
	(s390_pseudo_register_write): Move to s390-tdep.c
	(s390_pseudo_register_type): Move to s390-tdep.c
	(s390_pseudo_register_reggroup_p): Move to s390-tdep.c
	(s390_ax_pseudo_register_collect): Move to s390-tdep.c
	(s390_ax_pseudo_register_push_stack): Move to s390-tdep.c
	(s390_gen_return_address): Move to s390-tdep.c
	(s390_unwind_pseudo_register): Move to s390-tdep.c
	(s390_effective_inner_type): Move to s390-tdep.c
	(s390_function_arg_float): Move to s390-tdep.c
	(s390_function_arg_vector, is_power_of_two): Move to s390-tdep.c
	(s390_function_arg_integer, s390_arg_state): Move to s390-tdep.c
	(s390_handle_arg, s390_push_dummy_call): Move to s390-tdep.c
	(s390_dummy_id, s390_register_return_value): Move to s390-tdep.c
	(s390_return_value, s390_stack_frame_destroyed_p): Move to s390-tdep.c
	(s390_dwarf2_prev_register): Move to s390-tdep.c
	(s390_dwarf2_frame_init_reg): Move to s390-tdep.c
	(s390_adjust_frame_regnum, s390_unwind_cache): Move to s390-tdep.c
	(s390_prologue_frame_unwind_cache): Move to s390-tdep.c
	(s390_stub_unwind_cache): Move to s390-tdep.c
	(s390_stub_frame_unwind_cache): Move to s390-tdep.c
	(s390_stub_frame_this_id): Move to s390-tdep.c
	(s390_trad_frame_prev_register): Move to s390-tdep.c
	(s390_stub_frame_prev_register): Move to s390-tdep.c
	(s390_stub_frame_sniffer, s390_stub_frame_unwind): Move to s390-tdep.c
	(s390_sigtramp_unwind_cache): Move to s390-tdep.c
	(s390_sigtramp_frame_unwind_cache): Move to s390-tdep.c
	(s390_sigtramp_frame_this_id): Move to s390-tdep.c
	(s390_sigtramp_frame_prev_register): Move to s390-tdep.c
	(s390_sigtramp_frame_sniffer): Move to s390-tdep.c
	(s390_sigtramp_frame_unwind): Move to s390-tdep.c
	(s390_backchain_frame_unwind_cache): Move to s390-tdep.c
	(s390_frame_unwind_cache, s390_frame_this_id): Move to s390-tdep.c
	(s390_frame_prev_register, s390_frame_unwind): Move to s390-tdep.c
	(s390_frame_base_address): Move to s390-tdep.c
	(s390_local_base_address, s390_frame_base): Move to s390-tdep.c
	(s390_unwind_pc, s390_unwind_sp): Move to s390-tdep.c
	(is_non_branch_ril): Move to s390-tdep.c
	(s390_displaced_step_copy_insn): Move to s390-tdep.c
	(s390_displaced_step_fixup): Move to s390-tdep.c
	(s390_displaced_step_hw_singlestep): Move to s390-tdep.c
	(s390_addr_bits_remove): Move to s390-tdep.c
	(s390_address_class_type_flags): Move to s390-tdep.c
	(s390_address_class_type_flags_to_name): Move to s390-tdep.c
	(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 | 3427 +------------------------------------------------
 gdb/s390-linux-tdep.h |  178 +--
 gdb/s390-tdep.c       | 3355 +++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-tdep.h       |  301 +++++
 7 files changed, 3689 insertions(+), 3580 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 38651dd..2933b08 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -863,6 +863,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 \
@@ -1420,6 +1421,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 \
@@ -2618,6 +2620,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 60a488c..a6dc662 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -482,8 +482,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 2b205df..12feffc 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 abc9438..a850ada 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,19 @@
    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 "gdbcore.h"
-#include "gdbcmd.h"
-#include "objfiles.h"
-#include "floatformat.h"
+#include "linux-record.h"
+#include "linux-tdep.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 "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 +53,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,570 +88,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 VEC (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;
-  VEC (CORE_ADDR) *next_pcs = NULL;
-
-  /* Special handling only if recording.  */
-  if (!record_full_is_used ())
-    return NULL;
-
-  /* First, match a partial instruction.  */
-  if (!s390_is_partial_instruction (gdbarch, loc, &len))
-    return NULL;
-
-  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 NULL;
-
-  insn = read_memory_integer (loc + 2, 2, byte_order);
-  if (insn != (uint16_t) -(len / 2))
-    return NULL;
-
-  loc += 4;
-
-  /* Found it, step past the whole thing.  */
-  VEC_safe_push (CORE_ADDR, next_pcs, loc);
-
-  return next_pcs;
-}
-
-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[] =
@@ -866,7 +184,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
@@ -908,1750 +226,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_rsi (bfd_byte *insn, int op,
-	unsigned int *r1, unsigned int *r3, int *i2)
-{
-  if (insn[0] == op)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *r3 = insn[1] & 0xf;
-      /* i2 is a 16-bit signed quantity.  */
-      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
-      return 1;
-    }
-  else
-    return 0;
-}
-
-
-static int
-is_rie (bfd_byte *insn, int op1, int op2,
-	unsigned int *r1, unsigned int *r3, int *i2)
-{
-  if (insn[0] == op1
-      && insn[5] == op2)
-    {
-      *r1 = (insn[1] >> 4) & 0xf;
-      *r3 = insn[1] & 0xf;
-      /* i2 is a 16-bit signed quantity.  */
-      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
-      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 PC-relative branch instructions.  */
-  else if (is_ri (insn, op1_brc, op2_brc, &r1, &i2)
-	   || is_ril (insn, op1_brcl, op2_brcl, &r1, &i2)
-	   || is_ri (insn, op1_brct, op2_brct, &r1, &i2)
-	   || is_ri (insn, op1_brctg, op2_brctg, &r1, &i2)
-	   || is_rsi (insn, op_brxh, &r1, &r3, &i2)
-	   || is_rie (insn, op1_brxhg, op2_brxhg, &r1, &r3, &i2)
-	   || is_rsi (insn, op_brxle, &r1, &r3, &i2)
-	   || is_rie (insn, op1_brxlg, op2_brxlg, &r1, &r3, &i2))
-    {
-      /* Update PC.  */
-      regcache_write_pc (regs, pc - to + from);
-    }
-
-  /* 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, PC points right after the original instruction.  */
-  else
-    regcache_write_pc (regs, from + insnlen);
-
-  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. */
@@ -2979,622 +555,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
-      || code == TYPE_CODE_REF)
-    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".  */
@@ -3628,6 +588,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
@@ -7729,409 +4690,59 @@ 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;
+/* Initialize GNU/Linux specific gdbarch hooks.  */
 
-    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",
-      };
-      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;
-	}
-    }
+void
+s390_gdbarch_linux_init (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* 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;
-    }
+  linux_init_abi (info, 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);
-  /* 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)
-    {
-      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);
 
   /* Syscall handling.  */
   set_gdbarch_get_syscall_number (gdbarch, s390_linux_get_syscall_number);
 
-  /* 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_free_closure (gdbarch,
-					   simple_displaced_step_free_closure);
-  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);
-
   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;
     }
 
-  set_gdbarch_print_insn (gdbarch, print_insn_s390);
-
   set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
 
   /* Enable TLS support.  */
   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;
 }
 
-
-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 4f818b4..353b808 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_gdbarch_linux_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 0000000..53f50fe
--- /dev/null
+++ b/gdb/s390-tdep.c
@@ -0,0 +1,3355 @@
+/* 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.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "infrun.h"
+#include "linux-tdep.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "prologue-value.h"
+#include "record-full.h"
+#include "reggroups.h"
+#include "regset.h"
+#include "s390-linux-tdep.h"
+#include "s390-tdep.h"
+#include "target-descriptions.h"
+#include "trad-frame.h"
+#include "value.h"
+
+/* Decoding S/390 instructions.  */
+
+/* Read a single instruction from address AT.  */
+
+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_rsi (bfd_byte *insn, int op,
+	unsigned int *r1, unsigned int *r3, int *i2)
+{
+  if (insn[0] == op)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *r3 = insn[1] & 0xf;
+      /* i2 is a 16-bit signed quantity.  */
+      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+
+static int
+is_rie (bfd_byte *insn, int op1, int op2,
+	unsigned int *r1, unsigned int *r3, int *i2)
+{
+  if (insn[0] == op1
+      && insn[5] == op2)
+    {
+      *r1 = (insn[1] >> 4) & 0xf;
+      *r3 = insn[1] & 0xf;
+      /* i2 is a 16-bit signed quantity.  */
+      *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
+      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 VEC (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;
+  VEC (CORE_ADDR) *next_pcs = NULL;
+
+  /* Special handling only if recording.  */
+  if (!record_full_is_used ())
+    return NULL;
+
+  /* First, match a partial instruction.  */
+  if (!s390_is_partial_instruction (gdbarch, loc, &len))
+    return NULL;
+
+  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 NULL;
+
+  insn = read_memory_integer (loc + 2, 2, byte_order);
+  if (insn != (uint16_t) -(len / 2))
+    return NULL;
+
+  loc += 4;
+
+  /* Found it, step past the whole thing.  */
+  VEC_safe_push (CORE_ADDR, next_pcs, loc);
+
+  return next_pcs;
+}
+
+
+/* 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;
+};
+
+/* 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.  */
+
+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;
+}
+
+
+/* Registers.  */
+
+/* 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;
+}
+
+/* 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.  */
+
+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);
+}
+
+
+/* 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
+      || code == TYPE_CODE_REF)
+    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.  */
+
+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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+
+/* 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
+};
+
+/* 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 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)
+    {
+      /* 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
+};
+
+/* 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);
+}
+
+
+/* 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 PC-relative branch instructions.  */
+  else if (is_ri (insn, op1_brc, op2_brc, &r1, &i2)
+	   || is_ril (insn, op1_brcl, op2_brcl, &r1, &i2)
+	   || is_ri (insn, op1_brct, op2_brct, &r1, &i2)
+	   || is_ri (insn, op1_brctg, op2_brctg, &r1, &i2)
+	   || is_rsi (insn, op_brxh, &r1, &r3, &i2)
+	   || is_rie (insn, op1_brxhg, op2_brxhg, &r1, &r3, &i2)
+	   || is_rsi (insn, op_brxle, &r1, &r3, &i2)
+	   || is_rie (insn, op1_brxlg, op2_brxlg, &r1, &r3, &i2))
+    {
+      /* Update PC.  */
+      regcache_write_pc (regs, pc - to + from);
+    }
+
+  /* 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, PC points right after the original instruction.  */
+  else
+    regcache_write_pc (regs, from + insnlen);
+
+  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);
+  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_free_closure (gdbarch,
+					   simple_displaced_step_free_closure);
+  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;
+    }
+
+  set_gdbarch_print_insn (gdbarch, print_insn_s390);
+
+  /* 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_gdbarch_linux_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 0000000..21dcb12
--- /dev/null
+++ b/gdb/s390-tdep.h
@@ -0,0 +1,301 @@
+/* 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
+
+/* 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;
+};
+
+/* 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)
+
+#endif /* S390_TDEP_H */
-- 
2.8.4

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

* [RFC v3 5/8] Add commands for linux-kernel target
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
                   ` (2 preceding siblings ...)
  2017-03-16 16:58 ` [RFC v3 8/8] Add S390 support for linux-kernel target Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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 251143b..38651dd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -817,6 +817,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 \
@@ -1106,6 +1107,7 @@ SFILES = \
 	jit.c \
 	language.c \
 	linespec.c \
+	lk-cmds.c \
 	lk-lists.c \
 	lk-low.c \
 	lk-modules.c \
@@ -1356,6 +1358,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 \
@@ -2556,6 +2559,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 a16ccd7..60a488c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -36,7 +36,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 0000000..e63e074
--- /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 0000000..63e4246
--- /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 c10f884..b2e7334 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"
@@ -798,6 +799,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 a22c6fb..e07cfb8 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 72da7f4..4cbe189 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.8.4

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

* [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
                   ` (4 preceding siblings ...)
  2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
@ 2017-03-16 16:58 ` Philipp Rudo
  2017-05-02 13:15   ` Yao Qi
  2017-03-16 16:58 ` [RFC v3 7/8] Add privileged registers for s390x Philipp Rudo
  2017-03-16 16:58 ` [RFC v3 2/8] Add libiberty/concat styled concat_path function Philipp Rudo
  7 siblings, 1 reply; 35+ messages in thread
From: Philipp Rudo @ 2017-03-16 16:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Yao Qi, Peter Griffin, Omair Javaid, Andreas 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 read from. 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      | 101 +++++++++++++
 gdb/lk-low.h      |  24 ++++
 gdb/lk-modules.c  | 412 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-modules.h  |  29 ++++
 gdb/solib.c       |   8 ++
 gdb/solib.h       |   5 +
 8 files changed, 584 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 9387c66..251143b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -819,6 +819,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 \
@@ -1107,6 +1108,7 @@ SFILES = \
 	linespec.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1356,6 +1358,7 @@ HFILES_NO_SRCDIR = \
 	linux-tdep.h \
 	lk-lists.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2555,6 +2558,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 8d87fea..a16ccd7 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -36,7 +36,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 768f228..c10f884 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"
@@ -536,6 +537,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.  */
 
@@ -571,6 +612,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 ()
 {
@@ -591,10 +635,61 @@ 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, list);
+  LK_DECLARE_FIELD (module, name);
+  LK_DECLARE_FIELD (module, source_list);
+  LK_DECLARE_FIELD (module, arch);
+  LK_DECLARE_FIELD (module, init);
+  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);
+    }
+  else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */
+    {
+      LK_DECLARE_FIELD (module, init_size);
+      LK_DECLARE_FIELD (module, core_size);
+
+      LK_DECLARE_FIELD (module, core_text_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 */
@@ -693,12 +788,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->get_module_text_offset);
 
   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.  */
@@ -811,6 +911,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 292ef97..1d6e053 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -27,6 +27,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 +206,19 @@ 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
+   .text section.  */
+typedef CORE_ADDR (*lk_hook_get_module_text_offset) (CORE_ADDR mod);
+
 /* Hook to return the per_cpu_offset of cpu CPU.  Only architectures that
    do not use the __per_cpu_offset array to determin the offset have to
    supply this hook.  */
@@ -218,6 +233,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_get_module_text_offset get_module_text_offset;
+
   /* 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 0000000..f3c559d
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,412 @@
+/* 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;
+
+/* Info for single section type.  */
+
+struct lm_info_sec
+{
+  CORE_ADDR start;
+  CORE_ADDR offset;
+  unsigned int size;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lm_info
+{
+  CORE_ADDR base;
+  unsigned int size;
+
+  struct lm_info_sec text;
+  struct lm_info_sec init_text;
+  struct lm_info_sec ro_data;
+  struct lm_info_sec rw_data;
+  struct lm_info_sec percpu;
+};
+
+/* Check if debug info for module NAME are 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;
+}
+
+/* Allocate and fill a copy of struct lm_info for module at address MOD.  */
+
+struct lm_info *
+lk_modules_read_lm_info (CORE_ADDR mod)
+{
+  struct lm_info *lmi = XNEW (struct lm_info);
+  struct cleanup *old_chain = make_cleanup (xfree, lmi);
+
+  if (LK_FIELD (module, module_core)) /* linux -4.4 */
+    {
+      lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
+      lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size));
+
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod + LK_OFFSET (module,
+							  core_ro_size));
+    }
+  else /* linux 4.5+ */
+    {
+      CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
+
+      lmi->base = lk_read_addr (mod_core
+				+ LK_OFFSET (module_layout, base));
+      lmi->size = lk_read_uint (mod_core
+				+ LK_OFFSET (module_layout, size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod_core
+				     + LK_OFFSET (module_layout, text_size));
+
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod_core
+					 + LK_OFFSET (module_layout, ro_size));
+    }
+
+  lmi->rw_data.start = lmi->base + lmi->ro_data.size;
+  lmi->rw_data.offset = 0;
+  lmi->rw_data.size = lmi->size - lmi->ro_data.size;
+
+  lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
+  lmi->init_text.offset = 0;
+
+  lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
+  lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
+  lmi->percpu.offset = 0;
+
+  discard_cleanups (old_chain);
+  return lmi;
+}
+
+/* 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 = lk_modules_read_lm_info (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 SEC to section type LMI_SEC.  Helper function for
+   lk_modules_relocate_section_addresses.  */
+
+void
+lk_modules_relocate_sec (struct target_section *sec,
+			 struct lm_info_sec *lmi_sec)
+{
+  unsigned int alignment = 1;
+
+ alignment = 1 << sec->the_bfd_section->alignment_power;
+
+  /* Adjust offset to section alignment.  */
+  if (lmi_sec->offset % alignment != 0)
+    lmi_sec->offset += alignment - (lmi_sec->offset % alignment);
+
+  sec->addr += lmi_sec->start + lmi_sec->offset;
+  sec->endaddr += lmi_sec->start + lmi_sec->offset;
+  lmi_sec->offset += sec->endaddr - sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  struct lm_info *lmi = so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  if (streq (name, ".modinfo") || streq (name, "__versions"))
+      return;
+
+  /* FIXME: Make dependent on module state, i.e. only map .init sections if
+   * state is MODULE_STATE_COMING.  */
+  if (startswith (name, ".init"))
+    lk_modules_relocate_sec (sec, &lmi->init_text);
+  else if (endswith (name, ".percpu"))
+    lk_modules_relocate_sec (sec, &lmi->percpu);
+  else if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, &lmi->text);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, &lmi->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, &lmi->rw_data);
+
+  /* Set address range to be displayed with info shared.
+     size = text + (ro + rw) data without .init sections.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = lmi->base;
+      so->addr_high = lmi->base + lmi->size;
+    }
+}
+
+/* Function for free_so hook.  */
+
+void
+lk_modules_free_so (struct so_list *so)
+{
+  xfree (so->lm_info);
+}
+
+/* Function for clear_so hook.  */
+
+void
+lk_modules_clear_so (struct so_list *so)
+{
+  if (so->lm_info != NULL)
+    memset (so->lm_info, 0, sizeof (struct lm_info));
+}
+
+/* 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.  */
+
+int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  return streq (gdb->so_name, inf->so_name);
+}
+
+/* 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 0000000..47e6dde
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,29 @@
+/* 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__
+
+extern struct target_so_ops *lk_modules_so_ops;
+
+/* 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 fc45133..595828a 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -107,6 +107,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 dd07636..99b5cda 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -28,6 +28,11 @@ struct program_space;
 
 #include "symfile-add-flags.h"
 
+/* 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.8.4

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
@ 2017-04-16 22:59   ` Omair Javaid
  2017-05-03 14:38     ` Philipp Rudo
  2017-04-20 11:09   ` Omair Javaid
  2017-05-02 11:14   ` Yao Qi
  2 siblings, 1 reply; 35+ messages in thread
From: Omair Javaid @ 2017-04-16 22:59 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez, Lee Jones,
	Russell Wayman

Hi Philip,

I like your handling of linux kernel data structures though I havent
been able to get your code working on arm.

There are some challenges with regards to live debugging support which
I am trying to figure out. There is no reliable way to tell between a
kernel direct mapped address, vmalloc address and module address when
we also have user address available.

Also there is no way to switch between stratums if we need to do so in
case we try to support switching between userspace and kernel space.

As far as this patch is concerned there are no major issues that can
block me from further progressing towards live debugging support.

I have compiled this patch with arm support on top and overall this
looks good. See some minor inline comments.

Yao: Kindly check if there are any coding convention or styling issues here.

PS: I have not looked at module support or s390 target specific code in detail.

Thanks!

--
Omair


On 16 March 2017 at 21:57, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:
> 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..
>
> Currently the target_ops is designed only to work with static targets, i.e.
> dumps. Thus it lacks implementation for hooks like to_wait, to_resume or
> to_store_registers. Furthermore the mapping between a CPU and the
> task_struct of the running task is only be done once at initialization. See
> cover letter for a detailed discussion.
>
> Nevertheless i made some design decisions different to Peter [1] which are
> worth discussing. Especially storing the private data in a htab (or
> std::unordered_map if i had the time...) instead of global variables makes
> the code much nicer and less memory consuming.
>
> [1] https://sourceware.org/ml/gdb-patches/2016-12/msg00382.html
>
> 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      | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  gdb/lk-low.h      | 310 ++++++++++++++++++++
>  9 files changed, 1301 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 0818742..9387c66 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -817,6 +817,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 \
> @@ -1103,6 +1105,8 @@ SFILES = \
>         jit.c \
>         language.c \
>         linespec.c \
> +       lk-lists.c \
> +       lk-low.c \
>         location.c \
>         m2-exp.y \
>         m2-lang.c \
> @@ -1350,6 +1354,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 \
> @@ -2547,6 +2553,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 cb909e7..8d87fea 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -34,6 +34,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
> @@ -479,7 +483,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 87eafb2..5509a6c 100644
> --- a/gdb/gdbarch.c
> +++ b/gdb/gdbarch.c
> @@ -349,6 +349,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
> @@ -1139,6 +1140,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,
> @@ -5008,6 +5015,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 34f82a7..c03bf00 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 39b1f94..cad45d1 100755
> --- a/gdb/gdbarch.sh
> +++ b/gdb/gdbarch.sh
> @@ -1167,6 +1167,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 0000000..55d11bd
> --- /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 0000000..f9c2a85
> --- /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 0000000..768f228
> --- /dev/null
> +++ b/gdb/lk-low.c
> @@ -0,0 +1,833 @@
> +/* 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)
> +    {

Will this be portable across endianess?

> +      /* 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;
> +}
> +

lk_bitmap_hweight seems un-used.
I wonder if there is generic implementation available for this
function somewhere in binutils-gdb sources.
Can we use something like __builtin_popcount from GCC intrinsic?

> +/* 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;
> +      ptid_t new_ptid, old_ptid;
> +
> +      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;
> +
> +      new_ptid = ptid_build (inf_pid, pid, curr);
> +      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;
> +         ptid_t ptid;
> +         struct thread_info *tp;
> +
> +         pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
> +         ptid = ptid_build (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) ptid_get_tid (regcache_get_ptid (regcache));
> +  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);
> +       }
> +    }
> +}
> +

This function throws an error while compiling for arm-linux target on
x86_64 host.

lk-low.c: In function ‘void init_linux_kernel_ops()’:
lk-low.c:812:20: error: invalid conversion from ‘char*
(*)(target_ops*, ptid_t)’ to ‘const char* (*)(target_ops*, ptid_t)’
[-fpermissive]
   t->to_pid_to_str = lk_pid_to_str;


> +/* Function for targets to_pid_to_str hook.  Marks running tasks with an
> +   asterisk "*".  */
> +
> +static char *
> +lk_pid_to_str (struct target_ops *target, ptid_t ptid)
> +{
> +  static char buf[64];
> +  long pid;
> +  CORE_ADDR task;
> +
> +  pid = ptid_get_lwp (ptid);
> +  task = (CORE_ADDR) ptid_get_tid (ptid);
> +
> +  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) ptid_get_tid (ti->ptid);
> +  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);
> +

Nice to have comments for all structs/fields below, a kernel tree
reference may be?

> +  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 == ptid_get_lwp (ti->ptid))
> +               {
> +                 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);

Accessing pid and lwp fields directly is not recommended. May be use
something like
         error (_("Could not map thread with pid %d, lwp %ld to a cpu."),
               ptid_get_pid (ti->ptid), ptid_get_lwp (ti->ptid));


> +       }
> +      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)
> +         && ptid_get_pid (inferior_ptid) != 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)
> +      && ptid_get_pid (inferior_ptid) != 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 (ptid_get_pid (inferior_ptid) == 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 0000000..292ef97
> --- /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 determin the offset have to
> +   supply this hook.  */

^Typo in comment.
Also if its not too much trouble can you kindly put Linux kernel
source tree references like
__per_cpu_offset: Linux/include/asm-generic/percpu.h 4.10. in comments.

> +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 numinator N by demoniator D and rounds up the result.  */

^ Spell check above.

> +#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.8.4
>

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
  2017-04-16 22:59   ` Omair Javaid
@ 2017-04-20 11:09   ` Omair Javaid
  2017-04-24 15:24     ` Andreas Arnez
  2017-05-03 14:38     ` Philipp Rudo
  2017-05-02 11:14   ` Yao Qi
  2 siblings, 2 replies; 35+ messages in thread
From: Omair Javaid @ 2017-04-20 11:09 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez, Lee Jones,
	Russell Wayman

Hi Philipp and Andreas,

I have some further comments on this patch specifically about copying
task_struct->pid into ptid->lwp and using task_struct address as tid.

I see that we are overriding lwp, tid which any target beneath might
be using differently.

So suggestion about storing task_struct->pid or task_struct address is
to use private_thread_info in binutils-gdb/gdb/gdbthread.h for this
information.

I also have reservation about use of old_ptid naming in struct
lk_private and > +struct lk_ptid_map.

old_ptid naming is a little confusing kindly choose a distinguishable
name for old_ptid varibles in both lk_private and lk_ptid_map.

Further Here's an implementation of bitmap_weight function from linux
kernel. Kindly see if your implementation can be improved and moved to
a generic area in gdb.

 10 int __bitmap_weight(const unsigned long *bitmap, int bits)
 11 {
 12         int k, w = 0, lim = bits/BITS_PER_LONG;
 13
 14         for (k = 0; k < lim; k++)
 15                 w += hweight_long(bitmap[k]);
 16
 17         if (bits % BITS_PER_LONG)
 18                 w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
 19
 20         return w;
 21 }

Thanks!

--
Omair.

On 16 March 2017 at 21:57, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:
> 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..
>
> Currently the target_ops is designed only to work with static targets, i.e.
> dumps. Thus it lacks implementation for hooks like to_wait, to_resume or
> to_store_registers. Furthermore the mapping between a CPU and the
> task_struct of the running task is only be done once at initialization. See
> cover letter for a detailed discussion.
>
> Nevertheless i made some design decisions different to Peter [1] which are
> worth discussing. Especially storing the private data in a htab (or
> std::unordered_map if i had the time...) instead of global variables makes
> the code much nicer and less memory consuming.
>
> [1] https://sourceware.org/ml/gdb-patches/2016-12/msg00382.html
>
> 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      | 833 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  gdb/lk-low.h      | 310 ++++++++++++++++++++
>  9 files changed, 1301 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 0818742..9387c66 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -817,6 +817,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 \
> @@ -1103,6 +1105,8 @@ SFILES = \
>         jit.c \
>         language.c \
>         linespec.c \
> +       lk-lists.c \
> +       lk-low.c \
>         location.c \
>         m2-exp.y \
>         m2-lang.c \
> @@ -1350,6 +1354,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 \
> @@ -2547,6 +2553,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 cb909e7..8d87fea 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -34,6 +34,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
> @@ -479,7 +483,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 87eafb2..5509a6c 100644
> --- a/gdb/gdbarch.c
> +++ b/gdb/gdbarch.c
> @@ -349,6 +349,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
> @@ -1139,6 +1140,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,
> @@ -5008,6 +5015,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 34f82a7..c03bf00 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 39b1f94..cad45d1 100755
> --- a/gdb/gdbarch.sh
> +++ b/gdb/gdbarch.sh
> @@ -1167,6 +1167,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 0000000..55d11bd
> --- /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 0000000..f9c2a85
> --- /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 0000000..768f228
> --- /dev/null
> +++ b/gdb/lk-low.c
> @@ -0,0 +1,833 @@
> +/* 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;
> +      ptid_t new_ptid, old_ptid;
> +
> +      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;
> +
> +      new_ptid = ptid_build (inf_pid, pid, curr);
> +      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;
> +         ptid_t ptid;
> +         struct thread_info *tp;
> +
> +         pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
> +         ptid = ptid_build (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) ptid_get_tid (regcache_get_ptid (regcache));
> +  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 char *
> +lk_pid_to_str (struct target_ops *target, ptid_t ptid)
> +{
> +  static char buf[64];
> +  long pid;
> +  CORE_ADDR task;
> +
> +  pid = ptid_get_lwp (ptid);
> +  task = (CORE_ADDR) ptid_get_tid (ptid);
> +
> +  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) ptid_get_tid (ti->ptid);
> +  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 == ptid_get_lwp (ti->ptid))
> +               {
> +                 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)
> +         && ptid_get_pid (inferior_ptid) != 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)
> +      && ptid_get_pid (inferior_ptid) != 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 (ptid_get_pid (inferior_ptid) == 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 0000000..292ef97
> --- /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 determin 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 numinator N by demoniator 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.8.4
>

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

* Re: [RFC v3 1/8] Convert substitute_path_component to C++
  2017-03-16 16:57 ` [RFC v3 1/8] Convert substitute_path_component to C++ Philipp Rudo
@ 2017-04-20 20:02   ` Sergio Durigan Junior
  2017-05-03 16:20     ` Philipp Rudo
  0 siblings, 1 reply; 35+ messages in thread
From: Sergio Durigan Junior @ 2017-04-20 20:02 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

On Thursday, March 16 2017, Philipp Rudo wrote:

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

Thanks for the patch, Philipp.  Just a minor nit.

> 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     | 50 ++++++++++++++------------------------------------
>  gdb/utils.h     |  9 +++++++--
>  3 files changed, 30 insertions(+), 47 deletions(-)
>
> diff --git a/gdb/auto-load.c b/gdb/auto-load.c
> index 56914c8..c84fee1 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 27021a1..bef619a 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -66,6 +66,8 @@
>  #include "interps.h"
>  #include "gdb_regex.h"
>  
> +#include <string>
> +
>  #if !HAVE_DECL_MALLOC
>  extern PTR malloc ();		/* ARI: PTR */
>  #endif
> @@ -3158,49 +3160,25 @@ 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;
> -
> -      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))
> +      char start, end;

Missing blank like between the declaration of the variables and the
assignment.

> +      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 f138702..d32114e 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);
>  
>  char *ldirname (const char *filename);
>  
> -- 
> 2.8.4

Other than that, LGTM.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-04-20 11:09   ` Omair Javaid
@ 2017-04-24 15:24     ` Andreas Arnez
  2017-05-03 14:13       ` Omair Javaid
  2017-05-03 14:38     ` Philipp Rudo
  1 sibling, 1 reply; 35+ messages in thread
From: Andreas Arnez @ 2017-04-24 15:24 UTC (permalink / raw)
  To: Omair Javaid
  Cc: Philipp Rudo, gdb-patches, Yao Qi, Peter Griffin, Lee Jones,
	Russell Wayman

On Thu, Apr 20 2017, Omair Javaid wrote:

> Hi Philipp and Andreas,
>
> I have some further comments on this patch specifically about copying
> task_struct->pid into ptid->lwp and using task_struct address as tid.
>
> I see that we are overriding lwp, tid which any target beneath might
> be using differently.
>
> So suggestion about storing task_struct->pid or task_struct address is
> to use private_thread_info in binutils-gdb/gdb/gdbthread.h for this
> information.

The current version of the patch series is mainly focused on dump
targets.  Remote targets require some additional changes.  We've
discussed the use of private_thread_info before, and the last I've seen
is that it is not suitable either, because remote.c uses it already:

  https://sourceware.org/ml/gdb-patches/2017-02/msg00543.html

In my view, the private_thread_info field really is a hack, and we are
now facing its limits.  It provides some space for a single thread layer
to store information into, but not for multiple such layers.  In the
case of the Linux kernel we at least have two different thread layers:
the CPU layer (each "thread" is a CPU), and the kernel task layer on top
of that.

I think we need to allow a target to maintain its *own* thread list.
The CPU "thread list" would be maintained by the target beneath
(remote/dump), and the kernel task list would be maintained by the LK
target.  The ptid namespaces could be completely separate.

> I also have reservation about use of old_ptid naming in struct
> lk_private and struct lk_ptid_map.
>
> old_ptid naming is a little confusing kindly choose a distinguishable
> name for old_ptid varibles in both lk_private and lk_ptid_map.
>
> Further Here's an implementation of bitmap_weight function from linux
> kernel. Kindly see if your implementation can be improved and moved to
> a generic area in gdb.
>
>  10 int __bitmap_weight(const unsigned long *bitmap, int bits)
>  11 {
>  12         int k, w = 0, lim = bits/BITS_PER_LONG;
>  13
>  14         for (k = 0; k < lim; k++)
>  15                 w += hweight_long(bitmap[k]);
>  16
>  17         if (bits % BITS_PER_LONG)
>  18                 w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
>  19
>  20         return w;
>  21 }

The __bitmap_weight function is specific to Linux, so I'm not sure we
want to move it to a generic area.  For big-endian targets the function
depends on the width of Linux' "unsigned long" type, because
BITMAP_LAST_WORD_MASK builds a mask for the *least significant* bits
instead of the *lowest-addressed* ones.

It's probably true that the performance of lk_bitmap_hweight could be
improved.  For instance, with some care a function like popcount_hwi()
in GCC's hwint.h could be exploited, even if the target's word width and
byte order may not match the GDB client's.  This would not make the
function simpler, though.

--
Andreas

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
  2017-04-16 22:59   ` Omair Javaid
  2017-04-20 11:09   ` Omair Javaid
@ 2017-05-02 11:14   ` Yao Qi
  2017-05-03 15:36     ` Philipp Rudo
  2 siblings, 1 reply; 35+ messages in thread
From: Yao Qi @ 2017-05-02 11:14 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

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

Hi Philipp,

> +/* 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."));
> +}
> +

> +
> +/* 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 0000000..292ef97
> --- /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

"private" here is a little confusing.  How about rename it to "linux_kernel"?

> +{
> +  /* 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;
> +};
> +

Secondly, can we change it to a class and function pointers in
lk_private_hooks become virtual functions.  gdbarch_lk_init_private
returns a pointer to an instance of sub-class of "linux_kernel".

lk_init_private_data can be put the constructor of base class, to add
entries to "data", and sub-class (in each gdbarch) can add their own
specific stuff.

> +
> +/* 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);

> +
> +/* 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 determin 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;
> +};

-- 
Yao (齐尧)

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

* Re: [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-03-16 16:58 ` [RFC v3 4/8] Add kernel module support for linux-kernel target Philipp Rudo
@ 2017-05-02 13:15   ` Yao Qi
  2017-05-03 16:16     ` Philipp Rudo
  0 siblings, 1 reply; 35+ messages in thread
From: Yao Qi @ 2017-05-02 13:15 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

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

> +/* Translate a kernel virtual address ADDR to a physical address.  */
> +
> +CORE_ADDR
> +lk_kvtop (CORE_ADDR addr)

How about lk_kernel_vir_to_phy_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);

Use make_scoped_restore instead of make_cleanup?

> +
> +  current_target.beneath = ops->beneath;
> +

Any reasons you switch current_target.beneath temporarily?

> +  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);

Two spaces after "=".

> diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
> new file mode 100644
> index 0000000..f3c559d
> --- /dev/null
> +++ b/gdb/lk-modules.c
> @@ -0,0 +1,412 @@
> +/* 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;
> +
> +/* Info for single section type.  */
> +
> +struct lm_info_sec
> +{
> +  CORE_ADDR start;
> +  CORE_ADDR offset;
> +  unsigned int size;
> +};
> +
> +/* Link map info to include in an allocated so_list entry.  */
> +
> +struct lm_info
> +{
> +  CORE_ADDR base;
> +  unsigned int size;
> +
> +  struct lm_info_sec text;
> +  struct lm_info_sec init_text;
> +  struct lm_info_sec ro_data;
> +  struct lm_info_sec rw_data;
> +  struct lm_info_sec percpu;
> +};

Comments to these fields are needed.

> +
> +/* 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;
> +}
> +
> +/* Allocate and fill a copy of struct lm_info for module at address MOD.  */
> +
> +struct lm_info *
> +lk_modules_read_lm_info (CORE_ADDR mod)
> +{
> +  struct lm_info *lmi = XNEW (struct lm_info);
> +  struct cleanup *old_chain = make_cleanup (xfree, lmi);
> +

use std::unique_ptr to avoid cleanup.

sdt::unique_ptr<lm_info> lmi (new lm_info ());

> +  if (LK_FIELD (module, module_core)) /* linux -4.4 */
> +    {
> +      lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
> +      lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
> +
> +      lmi->text.start = lmi->base;
> +      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
> +      lmi->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size));
> +
> +      lmi->ro_data.start = lmi->base + lmi->text.size;
> +      lmi->ro_data.offset = 0;
> +      lmi->ro_data.size =  lk_read_uint (mod + LK_OFFSET (module,
> +							  core_ro_size));
> +    }
> +  else /* linux 4.5+ */
> +    {
> +      CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
> +
> +      lmi->base = lk_read_addr (mod_core
> +				+ LK_OFFSET (module_layout, base));
> +      lmi->size = lk_read_uint (mod_core
> +				+ LK_OFFSET (module_layout, size));
> +
> +      lmi->text.start = lmi->base;
> +      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
> +      lmi->text.size = lk_read_uint (mod_core
> +				     + LK_OFFSET (module_layout, text_size));
> +
> +      lmi->ro_data.start = lmi->base + lmi->text.size;
> +      lmi->ro_data.offset = 0;
> +      lmi->ro_data.size =  lk_read_uint (mod_core
> +					 + LK_OFFSET (module_layout, ro_size));
> +    }
> +
> +  lmi->rw_data.start = lmi->base + lmi->ro_data.size;
> +  lmi->rw_data.offset = 0;
> +  lmi->rw_data.size = lmi->size - lmi->ro_data.size;
> +
> +  lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
> +  lmi->init_text.offset = 0;
> +
> +  lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
> +  lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
> +  lmi->percpu.offset = 0;
> +
> +  discard_cleanups (old_chain);
> +  return lmi;
> +}
> +

-- 
Yao (齐尧)

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-04-24 15:24     ` Andreas Arnez
@ 2017-05-03 14:13       ` Omair Javaid
  2017-05-03 15:20         ` Philipp Rudo
  0 siblings, 1 reply; 35+ messages in thread
From: Omair Javaid @ 2017-05-03 14:13 UTC (permalink / raw)
  To: Andreas Arnez
  Cc: Philipp Rudo, gdb-patches, Yao Qi, Peter Griffin, Lee Jones,
	Russell Wayman

On 24 April 2017 at 20:24, Andreas Arnez <arnez@linux.vnet.ibm.com> wrote:
> On Thu, Apr 20 2017, Omair Javaid wrote:
>
>> Hi Philipp and Andreas,
>>
>> I have some further comments on this patch specifically about copying
>> task_struct->pid into ptid->lwp and using task_struct address as tid.
>>
>> I see that we are overriding lwp, tid which any target beneath might
>> be using differently.
>>
>> So suggestion about storing task_struct->pid or task_struct address is
>> to use private_thread_info in binutils-gdb/gdb/gdbthread.h for this
>> information.
>
> The current version of the patch series is mainly focused on dump
> targets.  Remote targets require some additional changes.  We've
> discussed the use of private_thread_info before, and the last I've seen
> is that it is not suitable either, because remote.c uses it already:
>
>   https://sourceware.org/ml/gdb-patches/2017-02/msg00543.html
>
> In my view, the private_thread_info field really is a hack, and we are
> now facing its limits.  It provides some space for a single thread layer
> to store information into, but not for multiple such layers.  In the
> case of the Linux kernel we at least have two different thread layers:
> the CPU layer (each "thread" is a CPU), and the kernel task layer on top
> of that.
>
> I think we need to allow a target to maintain its *own* thread list.
> The CPU "thread list" would be maintained by the target beneath
> (remote/dump), and the kernel task list would be maintained by the LK
> target.  The ptid namespaces could be completely separate.

Hi Philip and Andreas,

Further more on this topic, remote stub assigns a common pid to all
CPU threads and uses LWP as CPU number.
While tid is left zero. I think we ll have to rework the old to new
ptid mapping mechanism a little bit in order to adjust all types of
targets beneath.

In your implementation of lk_init_ptid_map() you are testing pid from
task_struct against lwp of set by target target beneath. In case of
remote this will never be equal to pid as it is marked as the cpu
number.

Also in your implementation of lk_update_running_tasks lwp is being
updated with pid read from task_struct and tid is the task struct
address.
We are doing this not only for linux thread layer tasks but also for
CPU tasks in lk_update_running_tasks. This causes some sync issues
while on live targets as every time we halt a new thread is reported
by remote.

I think linux thread layer should only update tasks it has created
itself i-e tasks created by parsing task_struct. We can devise a
mechanism to map CPU tasks to curr task_struct and leave CPU tasks as
they were created by target beneath with same ptids.

Whats your take on this?


>
>> I also have reservation about use of old_ptid naming in struct
>> lk_private and struct lk_ptid_map.
>>
>> old_ptid naming is a little confusing kindly choose a distinguishable
>> name for old_ptid varibles in both lk_private and lk_ptid_map.
>>
>> Further Here's an implementation of bitmap_weight function from linux
>> kernel. Kindly see if your implementation can be improved and moved to
>> a generic area in gdb.
>>
>>  10 int __bitmap_weight(const unsigned long *bitmap, int bits)
>>  11 {
>>  12         int k, w = 0, lim = bits/BITS_PER_LONG;
>>  13
>>  14         for (k = 0; k < lim; k++)
>>  15                 w += hweight_long(bitmap[k]);
>>  16
>>  17         if (bits % BITS_PER_LONG)
>>  18                 w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
>>  19
>>  20         return w;
>>  21 }
>
> The __bitmap_weight function is specific to Linux, so I'm not sure we
> want to move it to a generic area.  For big-endian targets the function
> depends on the width of Linux' "unsigned long" type, because
> BITMAP_LAST_WORD_MASK builds a mask for the *least significant* bits
> instead of the *lowest-addressed* ones.
>
> It's probably true that the performance of lk_bitmap_hweight could be
> improved.  For instance, with some care a function like popcount_hwi()
> in GCC's hwint.h could be exploited, even if the target's word width and
> byte order may not match the GDB client's.  This would not make the
> function simpler, though.
>
> --
> Andreas
>

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-04-20 11:09   ` Omair Javaid
  2017-04-24 15:24     ` Andreas Arnez
@ 2017-05-03 14:38     ` Philipp Rudo
  1 sibling, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 14:38 UTC (permalink / raw)
  To: Omair Javaid
  Cc: gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez, Lee Jones,
	Russell Wayman

Hi Omair,


On Thu, 20 Apr 2017 16:08:57 +0500
Omair Javaid <omair.javaid@linaro.org> wrote:

> Hi Philipp and Andreas,
> 
> I have some further comments on this patch specifically about copying
> task_struct->pid into ptid->lwp and using task_struct address as tid.
> 
> I see that we are overriding lwp, tid which any target beneath might
> be using differently.
> 
> So suggestion about storing task_struct->pid or task_struct address is
> to use private_thread_info in binutils-gdb/gdb/gdbthread.h for this
> information.

You are right that other targets might overwrite ptid->lwp/tid fields.  But the
same they can do (and remote.c does) with the private_tread_info.  As Andreas
already pointed out this is a limitation in GDB we are facing.  The only proper
solution I see is to allow every target to manage its own thread_list.
Otherwise there is always the possibility that an other target interferes with
you.
 
> I also have reservation about use of old_ptid naming in struct
> lk_private and > +struct lk_ptid_map.
> 
> old_ptid naming is a little confusing kindly choose a distinguishable
> name for old_ptid varibles in both lk_private and lk_ptid_map.

The old_ptid/lk_ptid_map is a crude hack to show that kernel debugging is
possible at all.  It was never meant to be permanent.  So feel free to change
anything you like.
 
> Further Here's an implementation of bitmap_weight function from linux
> kernel. Kindly see if your implementation can be improved and moved to
> a generic area in gdb.
> 
>  10 int __bitmap_weight(const unsigned long *bitmap, int bits)
>  11 {
>  12         int k, w = 0, lim = bits/BITS_PER_LONG;
>  13
>  14         for (k = 0; k < lim; k++)
>  15                 w += hweight_long(bitmap[k]);
>  16
>  17         if (bits % BITS_PER_LONG)
>  18                 w += hweight_long(bitmap[k] &
> BITMAP_LAST_WORD_MASK(bits)); 19
>  20         return w;
>  21 }

You could possibly improve performance in my implementation.  Although I don't
think it will be very much.  When you look at the function above in more detail
you'll see that hweight_long is a macro checking each bit on its own (see
include/asm-generic/bitops/const_weight.h).  The problem is that GDB doesn't
know the length of a long from the target system during compile time.  Thus it
has to be determined in a loop during run time.

Nevertheless we could improve performance by going though the array byte-
instead of bit-wise.  I think assuming a byte with 8 bits is pretty safe, even
in the future.
An other possible "solution" is to remove the function completely.  As you said
in your previous mail lk_bitmap_hweight is currently not used.  I implemented
it because I thought it would be handy if we e.g. need to allocate memory
for percpu data.  But (at least currently) this is not the case.

Thanks

Philipp

 
> Thanks!
> 
> --
> Omair.
> 
> On 16 March 2017 at 21:57, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:
> > 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..
> >
> > Currently the target_ops is designed only to work with static targets, i.e.
> > dumps. Thus it lacks implementation for hooks like to_wait, to_resume or
> > to_store_registers. Furthermore the mapping between a CPU and the
> > task_struct of the running task is only be done once at initialization. See
> > cover letter for a detailed discussion.
> >
> > Nevertheless i made some design decisions different to Peter [1] which are
> > worth discussing. Especially storing the private data in a htab (or
> > std::unordered_map if i had the time...) instead of global variables makes
> > the code much nicer and less memory consuming.
> >
> > [1] https://sourceware.org/ml/gdb-patches/2016-12/msg00382.html
> >
> > 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      | 833
> > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/lk-low.h      |
> > 310 ++++++++++++++++++++ 9 files changed, 1301 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 0818742..9387c66 100644
> > --- a/gdb/Makefile.in
> > +++ b/gdb/Makefile.in
> > @@ -817,6 +817,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 \
> > @@ -1103,6 +1105,8 @@ SFILES = \
> >         jit.c \
> >         language.c \
> >         linespec.c \
> > +       lk-lists.c \
> > +       lk-low.c \
> >         location.c \
> >         m2-exp.y \
> >         m2-lang.c \
> > @@ -1350,6 +1354,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 \
> > @@ -2547,6 +2553,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 cb909e7..8d87fea 100644
> > --- a/gdb/configure.tgt
> > +++ b/gdb/configure.tgt
> > @@ -34,6 +34,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
> > @@ -479,7 +483,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 87eafb2..5509a6c 100644
> > --- a/gdb/gdbarch.c
> > +++ b/gdb/gdbarch.c
> > @@ -349,6 +349,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
> > @@ -1139,6 +1140,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,
> > @@ -5008,6 +5015,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 34f82a7..c03bf00 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 39b1f94..cad45d1 100755
> > --- a/gdb/gdbarch.sh
> > +++ b/gdb/gdbarch.sh
> > @@ -1167,6 +1167,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 0000000..55d11bd
> > --- /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 0000000..f9c2a85
> > --- /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 0000000..768f228
> > --- /dev/null
> > +++ b/gdb/lk-low.c
> > @@ -0,0 +1,833 @@
> > +/* 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;
> > +      ptid_t new_ptid, old_ptid;
> > +
> > +      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;
> > +
> > +      new_ptid = ptid_build (inf_pid, pid, curr);
> > +      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;
> > +         ptid_t ptid;
> > +         struct thread_info *tp;
> > +
> > +         pid = lk_read_int (thread + LK_OFFSET (task_struct, pid));
> > +         ptid = ptid_build (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) ptid_get_tid (regcache_get_ptid (regcache));
> > +  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 char *
> > +lk_pid_to_str (struct target_ops *target, ptid_t ptid)
> > +{
> > +  static char buf[64];
> > +  long pid;
> > +  CORE_ADDR task;
> > +
> > +  pid = ptid_get_lwp (ptid);
> > +  task = (CORE_ADDR) ptid_get_tid (ptid);
> > +
> > +  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) ptid_get_tid (ti->ptid);
> > +  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 == ptid_get_lwp (ti->ptid))
> > +               {
> > +                 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)
> > +         && ptid_get_pid (inferior_ptid) != 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)
> > +      && ptid_get_pid (inferior_ptid) != 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 (ptid_get_pid (inferior_ptid) == 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 0000000..292ef97
> > --- /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 determin 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 numinator N by demoniator 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.8.4
> >  
> 

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-04-16 22:59   ` Omair Javaid
@ 2017-05-03 14:38     ` Philipp Rudo
  0 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 14:38 UTC (permalink / raw)
  To: Omair Javaid
  Cc: gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez, Lee Jones,
	Russell Wayman

Hi Omair,

sorry for the late reply but I was sick the last two weeks...

On Mon, 17 Apr 2017 03:58:35 +0500
Omair Javaid <omair.javaid@linaro.org> wrote:

> Hi Philip,
> 
> I like your handling of linux kernel data structures though I havent
> been able to get your code working on arm.

I'm glad to hear it.

> There are some challenges with regards to live debugging support which
> I am trying to figure out. There is no reliable way to tell between a
> kernel direct mapped address, vmalloc address and module address when
> we also have user address available.

I'm afraid you will find more challenges like these.  Although I tried to be as
general as possible I most likely missed some things you will need for live
debugging ...

I don't think there is a reliable way to tell what kind of address you have
when you only have its unsigned long value.  On s390 (at least in theory) it
should be possible to find out via lowcore ("percpu state").  There the
currently running task is stored.  So you could try something like
lowcore->current_task->(mm|active_mm).

I actually never needed to find out what kind of address I have as for dumps the
user space is usually stripped off.  This makes live a lot easier as both
(direct mapped and vmalloc'ed addresses (including modules)) can be accessed
via the kernels page table (init_mm->pgd).  So you don't have to
care about what kind of address you have.  Furthermore they never get swapped
out.

In theory this method could also be used to access user space memory if you
know the corresponding page table and the memory isn't swapped out.  But here
GDB has the problem, that to_xfer_partial (in gdb/target.h) only gets the
address as unsigned long but has absolutely no information about the address
space it lives in ...

 
> Also there is no way to switch between stratums if we need to do so in
> case we try to support switching between userspace and kernel space.

No, GDB currently doesn't allow switching between straums.  This is one point
on my long ToDo-list...

> As far as this patch is concerned there are no major issues that can
> block me from further progressing towards live debugging support.
> 
> I have compiled this patch with arm support on top and overall this
> looks good. See some minor inline comments.
> 
> Yao: Kindly check if there are any coding convention or styling issues here.
> 
> PS: I have not looked at module support or s390 target specific code in
> detail.

No Problem just do one step after the other and don't waste too much time on
s390.  The architecture has some quirks ;)

> Thanks!
> 
> --
> Omair
> 
> 

[...]

> > +
> > +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)
> > +    {  
> 
> Will this be portable across endianess?

The generic implementation for the bitmap functions in the kernel rely on
BITMAP_LAST_WORD_MASK.  As Andreas mentioned earlier this macro creates a mask
for the least significant bits.  So this implementation should be portable. At
least it works on s390, which is big-endian.
 
> > +      /* 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;
> > +}
> > +  
> 
> lk_bitmap_hweight seems un-used.
> I wonder if there is generic implementation available for this
> function somewhere in binutils-gdb sources.
> Can we use something like __builtin_popcount from GCC intrinsic?

See reply to your next mail.
 
> > +/* 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;
> > +}
> > +

[...]

> This function throws an error while compiling for arm-linux target on
> x86_64 host.
> 
> lk-low.c: In function ‘void init_linux_kernel_ops()’:
> lk-low.c:812:20: error: invalid conversion from ‘char*
> (*)(target_ops*, ptid_t)’ to ‘const char* (*)(target_ops*, ptid_t)’
> [-fpermissive]
>    t->to_pid_to_str = lk_pid_to_str;

Could it be that you applied the patch on a master with Pedros "Enable
-Wwrite-strings" series?

https://sourceware.org/ml/gdb-patches/2017-04/msg00028.html

In this series the hook got "const-ified" (7a1149643).  It isn't considered in
my patches yet (aka. need to rebase to the current master, but that will be a
lot of work with all the C++-ification ...).
 
> > +/* Function for targets to_pid_to_str hook.  Marks running tasks with an
> > +   asterisk "*".  */
> > +
> > +static char *
> > +lk_pid_to_str (struct target_ops *target, ptid_t ptid)
> > +{
> > +  static char buf[64];
> > +  long pid;
> > +  CORE_ADDR task;
> > +
> > +  pid = ptid_get_lwp (ptid);
> > +  task = (CORE_ADDR) ptid_get_tid (ptid);
> > +
> > +  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;
> > +}

[...]

> Nice to have comments for all structs/fields below, a kernel tree
> reference may be?

I'm not 100% sure what you mean.  Is it a comment like
"/* Defined in <linux>/include/linux/sched.h.  */"?

If so I don't think its necessary as you could quickly grep for the
definition.  In particular I am afraid that after a while, when fields get
renamed and moved from one file to an other, the comments will confuse more
than they help.  That's why I haven't added any comments here (besides in what
linux version a field is valid).

> > +  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."));
> > +}

[...]

> > +/* 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 == ptid_get_lwp (ti->ptid))
> > +               {
> > +                 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);  
> 
> Accessing pid and lwp fields directly is not recommended. May be use
> something like
>          error (_("Could not map thread with pid %d, lwp %ld to a cpu."),
>                ptid_get_pid (ti->ptid), ptid_get_lwp (ti->ptid));

Yes, you are right.  I did the quick and dirty solution here because I already
knew that this ptid_map "solution" would only be temporary.  Besides ptid_t got
classified and it's API changed.  So this needs to be adjusted anyway when I
rebase to the current master...

[...]

> > +/* Hook to return the per_cpu_offset of cpu CPU.  Only architectures that
> > +   do not use the __per_cpu_offset array to determin the offset have to
> > +   supply this hook.  */  
> 
> ^Typo in comment.

Thanks, fixed.

> Also if its not too much trouble can you kindly put Linux kernel
> source tree references like
> __per_cpu_offset: Linux/include/asm-generic/percpu.h 4.10. in comments.

Like above.  I'm not sure if it is worth the effort and if outdated comments
confuse more than help.
 
[...]

> > +/* Divides numinator N by demoniator D and rounds up the result.  */  
> 
> ^ Spell check above.

What the hell did I do here?  Thanks for the hint, fixed.
 
Hope I didn't miss anything.

Thanks
Philipp

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-03 14:13       ` Omair Javaid
@ 2017-05-03 15:20         ` Philipp Rudo
  0 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 15:20 UTC (permalink / raw)
  To: Omair Javaid
  Cc: Andreas Arnez, gdb-patches, Yao Qi, Peter Griffin, Lee Jones,
	Russell Wayman

Hi Omair,

and now the third mail.

On Wed, 3 May 2017 19:12:36 +0500
Omair Javaid <omair.javaid@linaro.org> wrote:

> On 24 April 2017 at 20:24, Andreas Arnez <arnez@linux.vnet.ibm.com> wrote:
> > On Thu, Apr 20 2017, Omair Javaid wrote:
> >  
> >> Hi Philipp and Andreas,
> >>
> >> I have some further comments on this patch specifically about copying
> >> task_struct->pid into ptid->lwp and using task_struct address as tid.
> >>
> >> I see that we are overriding lwp, tid which any target beneath might
> >> be using differently.
> >>
> >> So suggestion about storing task_struct->pid or task_struct address is
> >> to use private_thread_info in binutils-gdb/gdb/gdbthread.h for this
> >> information.  
> >
> > The current version of the patch series is mainly focused on dump
> > targets.  Remote targets require some additional changes.  We've
> > discussed the use of private_thread_info before, and the last I've seen
> > is that it is not suitable either, because remote.c uses it already:
> >
> >   https://sourceware.org/ml/gdb-patches/2017-02/msg00543.html
> >
> > In my view, the private_thread_info field really is a hack, and we are
> > now facing its limits.  It provides some space for a single thread layer
> > to store information into, but not for multiple such layers.  In the
> > case of the Linux kernel we at least have two different thread layers:
> > the CPU layer (each "thread" is a CPU), and the kernel task layer on top
> > of that.
> >
> > I think we need to allow a target to maintain its *own* thread list.
> > The CPU "thread list" would be maintained by the target beneath
> > (remote/dump), and the kernel task list would be maintained by the LK
> > target.  The ptid namespaces could be completely separate.  
> 
> Hi Philip and Andreas,
> 
> Further more on this topic, remote stub assigns a common pid to all
> CPU threads and uses LWP as CPU number.
> While tid is left zero. I think we ll have to rework the old to new
> ptid mapping mechanism a little bit in order to adjust all types of
> targets beneath.

As mentioned in the mail before, the mapping between old and new ptid is a
hack.  Feel free to change it.  Although I think that the only proper solution
is to allow every target to manage its own thread_list.

> In your implementation of lk_init_ptid_map() you are testing pid from
> task_struct against lwp of set by target target beneath. In case of
> remote this will never be equal to pid as it is marked as the cpu
> number.

For dumps it depends on what was written in the dump.  Some architectures
write there the pid some the cpu (e.g. s390).  Thats why I made it possible
for the architecture to overwrite the default behavior.

I think the cleanest solution is to classify lk_private and make the hooks
virtual class methods (as Yao suggested).  What do you think?
 
> Also in your implementation of lk_update_running_tasks lwp is being
> updated with pid read from task_struct and tid is the task struct
> address.
> We are doing this not only for linux thread layer tasks but also for
> CPU tasks in lk_update_running_tasks. This causes some sync issues
> while on live targets as every time we halt a new thread is reported
> by remote.
> 
> I think linux thread layer should only update tasks it has created
> itself i-e tasks created by parsing task_struct. We can devise a
> mechanism to map CPU tasks to curr task_struct and leave CPU tasks as
> they were created by target beneath with same ptids.
> 
> Whats your take on this?

I'm against this.  When we only update the threads we created ourself there
will be multiple problems.  For example you will see running tasks twice with
"info thread", once its task_struct version and once its remote version.
Furthermore there will be no mapping between both versions.  Thus the
task_struct version will show the outdated state when the task was last
"unscheduled".  While the remote version shows the tasks actual state but has
no information about kernel internals, e.g. the linux pid.  This would be
extremely confusing!

In addition there is a chance that you have multiple threads with the same
ptid, e.g. cpu1 colliding with the init process with pid = 1.  GDB is not able
to handle two threads with the same ptid.  This could (most likely) be
prevented by using the task_struct address as tid.  But there is nothing
preventing the target beneath to do the same.

Long story short.  GDBs current thread implementation is a hack with
limitations we are now reaching.  The only clean solution is for every target
to have its own thread_list.

Philipp

> >> I also have reservation about use of old_ptid naming in struct
> >> lk_private and struct lk_ptid_map.
> >>
> >> old_ptid naming is a little confusing kindly choose a distinguishable
> >> name for old_ptid varibles in both lk_private and lk_ptid_map.
> >>
> >> Further Here's an implementation of bitmap_weight function from linux
> >> kernel. Kindly see if your implementation can be improved and moved to
> >> a generic area in gdb.
> >>
> >>  10 int __bitmap_weight(const unsigned long *bitmap, int bits)
> >>  11 {
> >>  12         int k, w = 0, lim = bits/BITS_PER_LONG;
> >>  13
> >>  14         for (k = 0; k < lim; k++)
> >>  15                 w += hweight_long(bitmap[k]);
> >>  16
> >>  17         if (bits % BITS_PER_LONG)
> >>  18                 w += hweight_long(bitmap[k] &
> >> BITMAP_LAST_WORD_MASK(bits)); 19
> >>  20         return w;
> >>  21 }  
> >
> > The __bitmap_weight function is specific to Linux, so I'm not sure we
> > want to move it to a generic area.  For big-endian targets the function
> > depends on the width of Linux' "unsigned long" type, because
> > BITMAP_LAST_WORD_MASK builds a mask for the *least significant* bits
> > instead of the *lowest-addressed* ones.
> >
> > It's probably true that the performance of lk_bitmap_hweight could be
> > improved.  For instance, with some care a function like popcount_hwi()
> > in GCC's hwint.h could be exploited, even if the target's word width and
> > byte order may not match the GDB client's.  This would not make the
> > function simpler, though.
> >
> > --
> > Andreas
> >  
> 

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-02 11:14   ` Yao Qi
@ 2017-05-03 15:36     ` Philipp Rudo
  2017-05-07 23:54       ` Omair Javaid
  0 siblings, 1 reply; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 15:36 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

Hi Yao,


On Tue, 02 May 2017 12:14:40 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> Hi Philipp,
> 
> > +/* 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."));
> > +}
> > +  
> 
> > +
> > +/* 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 0000000..292ef97
> > --- /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  
> 
> "private" here is a little confusing.  How about rename it to "linux_kernel"?

I called it "private" as it is the targets private data stored in its to_data
hook.  But I don't mind renaming it.  Especially ...
 
> > +{
> > +  /* 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;
> > +};
> > +  
> 
> Secondly, can we change it to a class and function pointers in
> lk_private_hooks become virtual functions.  gdbarch_lk_init_private
> returns a pointer to an instance of sub-class of "linux_kernel".
> 
> lk_init_private_data can be put the constructor of base class, to add
> entries to "data", and sub-class (in each gdbarch) can add their own
> specific stuff.

... when classifying the struct, which already is on my long ToDo-list.  This
struct is a left over from when I started working on the project shortly before
gdb-7.12 was released.  I didn't think that the C++-yfication would kick off
that fast and started with plain C ...

Thanks
Philipp

> > +
> > +/* 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);  
> 
> > +
> > +/* 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 determin 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;
> > +};  
> 

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

* Re: [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-05-02 13:15   ` Yao Qi
@ 2017-05-03 16:16     ` Philipp Rudo
  2017-05-05 21:33       ` Yao Qi
  0 siblings, 1 reply; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 16:16 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

Hi,

On Tue, 02 May 2017 14:15:43 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > +/* Translate a kernel virtual address ADDR to a physical address.  */
> > +
> > +CORE_ADDR
> > +lk_kvtop (CORE_ADDR addr)  
> 
> How about lk_kernel_vir_to_phy_addr?

I prefer kvtop.  It's much shorter and (for my taste) is just as readable.  But
I don't insist on keeping the name.  Are there other opinions?

> 
> > +{
> > +  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);  
> 
> Use make_scoped_restore instead of make_cleanup?

Using a scoped_restore probably makes sense.  Although I don't see the
advantage over old style cleanups other than having marginally shorter code ...

> > +
> > +  current_target.beneath = ops->beneath;
> > +  
> 
> Any reasons you switch current_target.beneath temporarily?

Yes.  lk_kvtop (at least for s390) reads memory if the address is not
physical.  Thus reading a virtual address calls xfer_partial twice.  Once for
the actual address and a second time for the data lk_kvtop needs.  This can
lead to an endless recursion if there is a bug or memory corruption.  Switching
to the target beneath prevents this.

> > +  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);  
> 
> Two spaces after "=".

Fixed, thanks.

> > diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
> > new file mode 100644
> > index 0000000..f3c559d
> > --- /dev/null
> > +++ b/gdb/lk-modules.c
> > @@ -0,0 +1,412 @@
> > +/* 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;
> > +
> > +/* Info for single section type.  */
> > +
> > +struct lm_info_sec
> > +{
> > +  CORE_ADDR start;
> > +  CORE_ADDR offset;
> > +  unsigned int size;
> > +};
> > +
> > +/* Link map info to include in an allocated so_list entry.  */
> > +
> > +struct lm_info
> > +{
> > +  CORE_ADDR base;
> > +  unsigned int size;
> > +
> > +  struct lm_info_sec text;
> > +  struct lm_info_sec init_text;
> > +  struct lm_info_sec ro_data;
> > +  struct lm_info_sec rw_data;
> > +  struct lm_info_sec percpu;
> > +};  
> 
> Comments to these fields are needed.

Yes, you are right. I will fix it when I adjust to the classification of
lm_info.
 
> > +
> > +/* 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;
> > +}
> > +
> > +/* Allocate and fill a copy of struct lm_info for module at address MOD.
> > */ +
> > +struct lm_info *
> > +lk_modules_read_lm_info (CORE_ADDR mod)
> > +{
> > +  struct lm_info *lmi = XNEW (struct lm_info);
> > +  struct cleanup *old_chain = make_cleanup (xfree, lmi);
> > +  
> 
> use std::unique_ptr to avoid cleanup.
> 
> sdt::unique_ptr<lm_info> lmi (new lm_info ());

Yes, a unique_ptr is better.  There are probably many cleanups I can get rid
off with the latest C++-yfication.

The only problem I currently have is that the C++-yfication rate is too high I
can barely follow and add new features to have a reliable kernel debugger.
Thanks for the hint anyway.

> > +  if (LK_FIELD (module, module_core)) /* linux -4.4 */
> > +    {
> > +      lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
> > +      lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
> > +
> > +      lmi->text.start = lmi->base;
> > +      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
> > +      lmi->text.size = lk_read_uint (mod + LK_OFFSET (module,
> > core_text_size)); +
> > +      lmi->ro_data.start = lmi->base + lmi->text.size;
> > +      lmi->ro_data.offset = 0;
> > +      lmi->ro_data.size =  lk_read_uint (mod + LK_OFFSET (module,
> > +							  core_ro_size));
> > +    }
> > +  else /* linux 4.5+ */
> > +    {
> > +      CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
> > +
> > +      lmi->base = lk_read_addr (mod_core
> > +				+ LK_OFFSET (module_layout, base));
> > +      lmi->size = lk_read_uint (mod_core
> > +				+ LK_OFFSET (module_layout, size));
> > +
> > +      lmi->text.start = lmi->base;
> > +      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
> > +      lmi->text.size = lk_read_uint (mod_core
> > +				     + LK_OFFSET (module_layout,
> > text_size)); +
> > +      lmi->ro_data.start = lmi->base + lmi->text.size;
> > +      lmi->ro_data.offset = 0;
> > +      lmi->ro_data.size =  lk_read_uint (mod_core
> > +					 + LK_OFFSET (module_layout,
> > ro_size));
> > +    }
> > +
> > +  lmi->rw_data.start = lmi->base + lmi->ro_data.size;
> > +  lmi->rw_data.offset = 0;
> > +  lmi->rw_data.size = lmi->size - lmi->ro_data.size;
> > +
> > +  lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
> > +  lmi->init_text.offset = 0;
> > +
> > +  lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
> > +  lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
> > +  lmi->percpu.offset = 0;
> > +
> > +  discard_cleanups (old_chain);
> > +  return lmi;
> > +}
> > +  
> 

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

* Re: [RFC v3 1/8] Convert substitute_path_component to C++
  2017-04-20 20:02   ` Sergio Durigan Junior
@ 2017-05-03 16:20     ` Philipp Rudo
  0 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-03 16:20 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

Hi Sergio,

sorry for the late answer but I was sick the last two weeks.

Thanks for the hint.  I fixed it locally.

Thanks
Philipp

On Thu, 20 Apr 2017 16:02:11 -0400
Sergio Durigan Junior <sergiodj@redhat.com> wrote:

> On Thursday, March 16 2017, Philipp Rudo wrote:
> 
> > Simplify the code of utils.c:substiute_path_component by converting it to
> > C++.  
> 
> Thanks for the patch, Philipp.  Just a minor nit.
> 
> > 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     | 50 ++++++++++++++------------------------------------
> >  gdb/utils.h     |  9 +++++++--
> >  3 files changed, 30 insertions(+), 47 deletions(-)
> >
> > diff --git a/gdb/auto-load.c b/gdb/auto-load.c
> > index 56914c8..c84fee1 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 27021a1..bef619a 100644
> > --- a/gdb/utils.c
> > +++ b/gdb/utils.c
> > @@ -66,6 +66,8 @@
> >  #include "interps.h"
> >  #include "gdb_regex.h"
> >  
> > +#include <string>
> > +
> >  #if !HAVE_DECL_MALLOC
> >  extern PTR malloc ();		/* ARI: PTR */
> >  #endif
> > @@ -3158,49 +3160,25 @@ 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;
> > -
> > -      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))
> > +      char start, end;  
> 
> Missing blank like between the declaration of the variables and the
> assignment.
> 
> > +      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 f138702..d32114e 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);
> >  
> >  char *ldirname (const char *filename);
> >  
> > -- 
> > 2.8.4  
> 
> Other than that, LGTM.
> 
> Thanks,
> 

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

* Re: [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-05-03 16:16     ` Philipp Rudo
@ 2017-05-05 21:33       ` Yao Qi
  2017-05-08  9:18         ` Philipp Rudo
  0 siblings, 1 reply; 35+ messages in thread
From: Yao Qi @ 2017-05-05 21:33 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

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

>> > +/* Translate a kernel virtual address ADDR to a physical address.  */
>> > +
>> > +CORE_ADDR
>> > +lk_kvtop (CORE_ADDR addr)  
>> 
>> How about lk_kernel_vir_to_phy_addr?
>
> I prefer kvtop.  It's much shorter and (for my taste) is just as readable.  But
> I don't insist on keeping the name.  Are there other opinions?
>

or maybe lk_vir_to_phy?

>> 
>> > +{
>> > +  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);  
>> 
>> Use make_scoped_restore instead of make_cleanup?
>
> Using a scoped_restore probably makes sense.  Although I don't see the
> advantage over old style cleanups other than having marginally shorter code ...
>

We want to reduce the usages of cleanup, and even completely remove it
ultimately, so we should avoid using it in new code.

>> > +
>> > +  current_target.beneath = ops->beneath;
>> > +  
>> 
>> Any reasons you switch current_target.beneath temporarily?
>
> Yes.  lk_kvtop (at least for s390) reads memory if the address is not
> physical.  Thus reading a virtual address calls xfer_partial twice.  Once for
> the actual address and a second time for the data lk_kvtop needs.  This can
> lead to an endless recursion if there is a bug or memory corruption.  Switching
> to the target beneath prevents this.
>

Does it work if you pass ops->beneath to lk_kvtop and all lk_read_XXX
apis, so that we can use ops->beneath there instead of current_target.

-- 
Yao (齐尧)

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-03 15:36     ` Philipp Rudo
@ 2017-05-07 23:54       ` Omair Javaid
  2017-05-08 11:22         ` Philipp Rudo
  0 siblings, 1 reply; 35+ messages in thread
From: Omair Javaid @ 2017-05-07 23:54 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: Yao Qi, gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez

Hi Phillip,

Thanks for writing back. I hope you are feeling better now.

I am trying to manage our basic live thread implementation within the
limits you have set out in your patches.

However I am interested in knowing what are your plans for immediate
future like next couple of weeks.

If you are not planning on making any particular design changes to the
current version of your patches then probably I will continue working
using your patches as base.

Otherwise if you plan to make any further changes like going for a
separate thread list implementation for all layers of targets then i
can also divert away from your patches for a while untill next update
is posted.

I am already diverting away from Peter's original implementation
because of some basic limitations pointed out during previous reviews.
I dont have reliable solution right now but trying to find one lets
see if i can manage to upgrade this current hack for live threads as
well.

--
Omair.

On 3 May 2017 at 20:36, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:r
> Hi Yao,
>
>
> On Tue, 02 May 2017 12:14:40 +0100
> Yao Qi <qiyaoltc@gmail.com> wrote:
>
>> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
>>
>> Hi Philipp,
>>
>> > +/* 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."));
>> > +}
>> > +
>>
>> > +
>> > +/* 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 0000000..292ef97
>> > --- /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
>>
>> "private" here is a little confusing.  How about rename it to "linux_kernel"?
>
> I called it "private" as it is the targets private data stored in its to_data
> hook.  But I don't mind renaming it.  Especially ...
>
>> > +{
>> > +  /* 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;
>> > +};
>> > +
>>
>> Secondly, can we change it to a class and function pointers in
>> lk_private_hooks become virtual functions.  gdbarch_lk_init_private
>> returns a pointer to an instance of sub-class of "linux_kernel".
>>
>> lk_init_private_data can be put the constructor of base class, to add
>> entries to "data", and sub-class (in each gdbarch) can add their own
>> specific stuff.
>
> ... when classifying the struct, which already is on my long ToDo-list.  This
> struct is a left over from when I started working on the project shortly before
> gdb-7.12 was released.  I didn't think that the C++-yfication would kick off
> that fast and started with plain C ...
>
> Thanks
> Philipp
>
>> > +
>> > +/* 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);
>>
>> > +
>> > +/* 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 determin 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;
>> > +};
>>
>

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

* Re: [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-05-05 21:33       ` Yao Qi
@ 2017-05-08  9:18         ` Philipp Rudo
  2017-05-08 13:05           ` Yao Qi via gdb-patches
  0 siblings, 1 reply; 35+ messages in thread
From: Philipp Rudo @ 2017-05-08  9:18 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

Hi

On Fri, 05 May 2017 22:33:51 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> >> > +/* Translate a kernel virtual address ADDR to a physical address.  */
> >> > +
> >> > +CORE_ADDR
> >> > +lk_kvtop (CORE_ADDR addr)    
> >> 
> >> How about lk_kernel_vir_to_phy_addr?  
> >
> > I prefer kvtop.  It's much shorter and (for my taste) is just as readable.
> > But I don't insist on keeping the name.  Are there other opinions?
> >  
> 
> or maybe lk_vir_to_phy?

What about lk_virt_to_phys and lk_kvirt_to_phys?  For the general virtual to
physical translation and the special case when the kernel page tables are used.

> 
> >>   
> >> > +{
> >> > +  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);    
> >> 
> >> Use make_scoped_restore instead of make_cleanup?  
> >
> > Using a scoped_restore probably makes sense.  Although I don't see the
> > advantage over old style cleanups other than having marginally shorter
> > code ... 
> 
> We want to reduce the usages of cleanup, and even completely remove it
> ultimately, so we should avoid using it in new code.

Yes, I understand.  It's just that you replace one mechanism to restore the
global variables with an other instead of removing the root cause, global
variables...
 
> >> > +
> >> > +  current_target.beneath = ops->beneath;
> >> > +    
> >> 
> >> Any reasons you switch current_target.beneath temporarily?  
> >
> > Yes.  lk_kvtop (at least for s390) reads memory if the address is not
> > physical.  Thus reading a virtual address calls xfer_partial twice.  Once
> > for the actual address and a second time for the data lk_kvtop needs.  This
> > can lead to an endless recursion if there is a bug or memory corruption.
> > Switching to the target beneath prevents this.
> >  
> 
> Does it work if you pass ops->beneath to lk_kvtop and all lk_read_XXX
> apis, so that we can use ops->beneath there instead of current_target.

Well switching the target is 'just' a backup.  Only a few lines below in
lk_xfer_partial I do this

   ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object, annex,        
                                             readbuf, writebuf, offset, len,     
                                             xfered_len);

Switching the target assures that, if ops->beneath->to_xfer_partial calls other
target methods via the global current_target those methods also belong to
ops->beneath.  Moving this to lk_kvtop or lk_read_* wouldn't prevent the
problem but would lead to code duplication.

Thanks
Philipp

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-07 23:54       ` Omair Javaid
@ 2017-05-08 11:22         ` Philipp Rudo
       [not found]           ` <CADrjBPqijRQFH4jthAedFzOzMLchpyvM53aXc9grOCjS2YUNCw@mail.gmail.com>
                             ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-08 11:22 UTC (permalink / raw)
  To: Omair Javaid; +Cc: Yao Qi, gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez

Hi Omair,

On Mon, 8 May 2017 04:54:16 +0500
Omair Javaid <omair.javaid@linaro.org> wrote:

> Hi Phillip,
> 
> Thanks for writing back. I hope you are feeling better now.

Thanks. It will take some more time for me to get 100% fit again but at least
the worst is over ...

> I am trying to manage our basic live thread implementation within the
> limits you have set out in your patches.
> 
> However I am interested in knowing what are your plans for immediate
> future like next couple of weeks.
>
> If you are not planning on making any particular design changes to the
> current version of your patches then probably I will continue working
> using your patches as base.

My current plan is to finish off the work that has piled up during the two
weeks I was sick.  After that I will clean up my kernel stack unwinder for
s390 so I have that finally gone (it already took way too much time).

From then I don't have a fixed plan.  On my bucket list there are some items
without particular order and different impact to the interfaces.  They are

* Rebase to current master.
  With all the C++-yfication this will most likely lead to some minor changes.

* C++-fy the target itself.
  As Yao mentioned in his mail [1] it would be better to classify
  struct lk_private to better fit the direction GDB is currently going to.
  In this process I would also get rid of some cleanups and further adept the
  new C++ features.  Overall this will change some (but hopefully not
  many) interfaces.  The biggest change will most likely be from function
  hooks (in struct lk_private_hooks) to virtual class methods (in lk_private).

* Make LK_DECLARE_* macros more flexible.
  Currently lk_init_private_data aborts once any declared symbol cannot be
  found.  This also makes the whole target unusable if e.g. the kernel is
  compiled without CONFIG_MODULES as then some symbols needed for module
  support cannot be found.  My idea is to assign symbols to GDB-features e.g.
  module support and only turn off those features if a symbol could not be
  found.

* Design a proper CLI (i.e. functions like dmesg etc.).
  This will be needed if we want others to actually use the feature.  Shouldn't
  have any impact on you.

* Implement separate thread_lists.
  Allow every target to manage its own thread_list.  Heavy impact for you and a
  lot work for me...

* Implement different target views.
  Allow the user to switch between different target views (e.g. linux_kernel
  and core/remote) and thus define the wanted level of abstraction.  Even worse
  then the separate thread_lists...

Long story short you don't have to divert away from my patches.  Even if I
start working on the separate thread_lists next it will definitely take quite a
lot of time to implement.  So no matter what you will most likely have a working
patch before me ;)

I hope I answered all your questions.

Philipp

[1] https://sourceware.org/ml/gdb-patches/2017-05/msg00004.html

> Otherwise if you plan to make any further changes like going for a
> separate thread list implementation for all layers of targets then i
> can also divert away from your patches for a while untill next update
> is posted.
> 
> I am already diverting away from Peter's original implementation
> because of some basic limitations pointed out during previous reviews.
> I dont have reliable solution right now but trying to find one lets
> see if i can manage to upgrade this current hack for live threads as
> well.
> 
> --
> Omair.
> 
> On 3 May 2017 at 20:36, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:r
> > Hi Yao,
> >
> >
> > On Tue, 02 May 2017 12:14:40 +0100
> > Yao Qi <qiyaoltc@gmail.com> wrote:
> >  
> >> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> >>
> >> Hi Philipp,
> >>  
> >> > +/* 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."));
> >> > +}
> >> > +  
> >>  
> >> > +
> >> > +/* 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 0000000..292ef97
> >> > --- /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  
> >>
> >> "private" here is a little confusing.  How about rename it to
> >> "linux_kernel"?  
> >
> > I called it "private" as it is the targets private data stored in its
> > to_data hook.  But I don't mind renaming it.  Especially ...
> >  
> >> > +{
> >> > +  /* 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;
> >> > +};
> >> > +  
> >>
> >> Secondly, can we change it to a class and function pointers in
> >> lk_private_hooks become virtual functions.  gdbarch_lk_init_private
> >> returns a pointer to an instance of sub-class of "linux_kernel".
> >>
> >> lk_init_private_data can be put the constructor of base class, to add
> >> entries to "data", and sub-class (in each gdbarch) can add their own
> >> specific stuff.  
> >
> > ... when classifying the struct, which already is on my long ToDo-list.
> > This struct is a left over from when I started working on the project
> > shortly before gdb-7.12 was released.  I didn't think that the
> > C++-yfication would kick off that fast and started with plain C ...
> >
> > Thanks
> > Philipp
> >  
> >> > +
> >> > +/* 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);  
> >>  
> >> > +
> >> > +/* 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 determin 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;
> >> > +};  
> >>  
> >  
> 

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

* Re: [RFC v3 4/8] Add kernel module support for linux-kernel target
  2017-05-08  9:18         ` Philipp Rudo
@ 2017-05-08 13:05           ` Yao Qi via gdb-patches
  0 siblings, 0 replies; 35+ messages in thread
From: Yao Qi via gdb-patches @ 2017-05-08 13:05 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: gdb-patches, Yao Qi, Peter Griffin, Omair Javaid, Andreas Arnez

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

>> or maybe lk_vir_to_phy?
>
> What about lk_virt_to_phys and lk_kvirt_to_phys?  For the general virtual to
> physical translation and the special case when the kernel page tables are used.
>

lk_virt_to_phys is good to me.

-- 
Yao (齐尧)

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
       [not found]           ` <CADrjBPqijRQFH4jthAedFzOzMLchpyvM53aXc9grOCjS2YUNCw@mail.gmail.com>
@ 2017-05-10  9:03             ` Philipp Rudo
  0 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-10  9:03 UTC (permalink / raw)
  To: Peter Griffin; +Cc: Omair Javaid, Yao Qi, gdb-patches, Yao Qi, Andreas Arnez

Hi Peter,

On Tue, 9 May 2017 09:38:03 +0100
Peter Griffin <peter.griffin@linaro.org> wrote:

> Hi Philipp,
> 
> I think as Omair mentioned previously I've moved teams in Linaro so not
> working on GDB Linux awareness directly anymore. But I'm still following

yes, Omair already mentioned and I was said to hear it.  I wish you good
luck and hope you enjoy your new challenges.

> along in the background, and would love to see this feature merged.

Me too.

> On 8 May 2017 at 12:22, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:
> 
> > Hi Omair,
> >
> > On Mon, 8 May 2017 04:54:16 +0500
> > Omair Javaid <omair.javaid@linaro.org> wrote:
> >  
> > > Hi Phillip,
> > >
> > > Thanks for writing back. I hope you are feeling better now.  
> >
> > Thanks. It will take some more time for me to get 100% fit again but at
> > least
> > the worst is over ...
> >  
> 
> Good to hear.

Thanks a lot!

> >  
> > > I am trying to manage our basic live thread implementation within the
> > > limits you have set out in your patches.
> > >
> > > However I am interested in knowing what are your plans for immediate
> > > future like next couple of weeks.
> > >
> > > If you are not planning on making any particular design changes to the
> > > current version of your patches then probably I will continue working
> > > using your patches as base.  
> >
> > My current plan is to finish off the work that has piled up during the two
> > weeks I was sick.  After that I will clean up my kernel stack unwinder for
> > s390 so I have that finally gone (it already took way too much time).
> >
> > From then I don't have a fixed plan.  On my bucket list there are some
> > items
> > without particular order and different impact to the interfaces.  They are
> >
> > * Rebase to current master.
> >   With all the C++-yfication this will most likely lead to some minor
> > changes.
> >
> > * C++-fy the target itself.
> >   As Yao mentioned in his mail [1] it would be better to classify
> >   struct lk_private to better fit the direction GDB is currently going to.
> >   In this process I would also get rid of some cleanups and further adept
> > the
> >   new C++ features.  Overall this will change some (but hopefully not
> >   many) interfaces.  The biggest change will most likely be from function
> >   hooks (in struct lk_private_hooks) to virtual class methods (in
> > lk_private).
> >
> > * Make LK_DECLARE_* macros more flexible.
> >   Currently lk_init_private_data aborts once any declared symbol cannot be
> >   found.  This also makes the whole target unusable if e.g. the kernel is
> >   compiled without CONFIG_MODULES as then some symbols needed for module
> >   support cannot be found.  My idea is to assign symbols to GDB-features
> > e.g.
> >   module support and only turn off those features if a symbol could not be
> >   found.
> >
> > * Design a proper CLI (i.e. functions like dmesg etc.).
> >   This will be needed if we want others to actually use the feature.
> > Shouldn't
> >   have any impact on you.
> >  
> 
> For dmesg and other OS helpers, don't you just want to rely on the GDB
> python
> implementations already in the kernel source?
> 
> The idea being that you reduce the cross dependencies between the kernel and
> GDB by having this code live in the kernel source tree. Obviously there are
> still
> quite a few dependencies for the thread parsing and kernel modules support
> in the Linux kernel thread layer already, but having what you can in python
> still
> reduces the number of dependencies and on-going maintennce I think.
> 
> Also in theory the python and kernel data structures should move in
> lockstep for
> a given release.

Yes, this is one possibility.  Although I must admit that I would prefer to
have at least the "core helpers" implemented in GDB.  This would guarantee a
core functionality even when you don't have the corresponding kernel sources
(when a customer sends a dump of an older distro it can sometimes be hard to
get the correct sources, especially when the distro patches the kernel...).

Furthermore an implementation within GDB can easier access GDBs internal
state.  For example my dummy implementation for lsmod also shows if GDB
couldn't find debug information for a module.  This also reduces code
duplication as the commands can access the infrastructure we need anyway to get
kernel awareness going.  Of course this could also be achieved by extending GDBs
python interface.  Finding out the "best" way to have a consistent user
interface I meant with "design" .

> >
> > * Implement separate thread_lists.
> >   Allow every target to manage its own thread_list.  Heavy impact for you
> > and a
> >   lot work for me...
> >  
> 
> That would be very neat!

For me this is not only neat but the only clean solution.  Otherwise it would
be just an other workaround for the global variables GDB uses.

Philipp

> >
> > * Implement different target views.
> >   Allow the user to switch between different target views (e.g.
> > linux_kernel
> >   and core/remote) and thus define the wanted level of abstraction.  Even
> > worse
> >   then the separate thread_lists...
> >  
> 
> as would this :)
> 
> regards,
> 
> Peter.
> 
> 
> >
> > Long story short you don't have to divert away from my patches.  Even if I
> > start working on the separate thread_lists next it will definitely take
> > quite a
> > lot of time to implement.  So no matter what you will most likely have a
> > working
> > patch before me ;)
> >
> > I hope I answered all your questions.
> >
> > Philipp
> >
> > [1] https://sourceware.org/ml/gdb-patches/2017-05/msg00004.html
> >  
> > > Otherwise if you plan to make any further changes like going for a
> > > separate thread list implementation for all layers of targets then i
> > > can also divert away from your patches for a while untill next update
> > > is posted.
> > >
> > > I am already diverting away from Peter's original implementation
> > > because of some basic limitations pointed out during previous reviews.
> > > I dont have reliable solution right now but trying to find one lets
> > > see if i can manage to upgrade this current hack for live threads as
> > > well.
> > >
> > > --
> > > Omair.
> > >
> > > On 3 May 2017 at 20:36, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:r  
> > > > Hi Yao,
> > > >
> > > >
> > > > On Tue, 02 May 2017 12:14:40 +0100
> > > > Yao Qi <qiyaoltc@gmail.com> wrote:
> > > >  
> > > >> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> > > >>
> > > >> Hi Philipp,
> > > >>  
> > > >> > +/* 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."));  
> > > >> > +}
> > > >> > +  
> > > >>  
> > > >> > +
> > > >> > +/* 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 0000000..292ef97
> > > >> > --- /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  
> > > >>
> > > >> "private" here is a little confusing.  How about rename it to
> > > >> "linux_kernel"?  
> > > >
> > > > I called it "private" as it is the targets private data stored in its
> > > > to_data hook.  But I don't mind renaming it.  Especially ...
> > > >  
> > > >> > +{
> > > >> > +  /* 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;
> > > >> > +};
> > > >> > +  
> > > >>
> > > >> Secondly, can we change it to a class and function pointers in
> > > >> lk_private_hooks become virtual functions.  gdbarch_lk_init_private
> > > >> returns a pointer to an instance of sub-class of "linux_kernel".
> > > >>
> > > >> lk_init_private_data can be put the constructor of base class, to add
> > > >> entries to "data", and sub-class (in each gdbarch) can add their own
> > > >> specific stuff.  
> > > >
> > > > ... when classifying the struct, which already is on my long ToDo-list.
> > > > This struct is a left over from when I started working on the project
> > > > shortly before gdb-7.12 was released.  I didn't think that the
> > > > C++-yfication would kick off that fast and started with plain C ...
> > > >
> > > > Thanks
> > > > Philipp
> > > >  
> > > >> > +
> > > >> > +/* 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);  
> > > >>  
> > > >> > +
> > > >> > +/* 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 determin 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;
> > > >> > +};  
> > > >>  
> > > >  
> > >  
> >
> >  

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-08 11:22         ` Philipp Rudo
       [not found]           ` <CADrjBPqijRQFH4jthAedFzOzMLchpyvM53aXc9grOCjS2YUNCw@mail.gmail.com>
@ 2017-05-10  9:36           ` Philipp Rudo
  2017-05-19  8:45           ` Yao Qi
  2 siblings, 0 replies; 35+ messages in thread
From: Philipp Rudo @ 2017-05-10  9:36 UTC (permalink / raw)
  To: Omair Javaid; +Cc: Yao Qi, gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez

Hi Omair,

I forgot one thin on my bucket list.

* The way the module support maps module names to paths to .ko files should be
  improved.
  Currently I parse <solib-search-path>/modules.order (usually
  <solib-search-path> = /lib/modules/$(uname -r)) to do so and load the path 
  relative <solib-search-path>. The problem is that for ubuntu the files
  in /lib/modules/... are stripped off their debuginfo.  And thus GDB
  complains that it cannot load the modules symbols.  The full files (including
  debuginfo) can be found under /usr/lib/debug/lib/modules/$(uname -r)/ but this
  directory doesn't contain the modules.order file ...
  There is a simple workaround by copying modules.order to /usr/lib/debug/...,
  nevertheless it would be nicer if the mapping would be more robust.

Philipp

On Mon, 8 May 2017 13:22:04 +0200
Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:

> Hi Omair,
> 
> On Mon, 8 May 2017 04:54:16 +0500
> Omair Javaid <omair.javaid@linaro.org> wrote:
> 
> > Hi Phillip,
> > 
> > Thanks for writing back. I hope you are feeling better now.  
> 
> Thanks. It will take some more time for me to get 100% fit again but at least
> the worst is over ...
> 
> > I am trying to manage our basic live thread implementation within the
> > limits you have set out in your patches.
> > 
> > However I am interested in knowing what are your plans for immediate
> > future like next couple of weeks.
> >
> > If you are not planning on making any particular design changes to the
> > current version of your patches then probably I will continue working
> > using your patches as base.  
> 
> My current plan is to finish off the work that has piled up during the two
> weeks I was sick.  After that I will clean up my kernel stack unwinder for
> s390 so I have that finally gone (it already took way too much time).
> 
> From then I don't have a fixed plan.  On my bucket list there are some items
> without particular order and different impact to the interfaces.  They are
> 
> * Rebase to current master.
>   With all the C++-yfication this will most likely lead to some minor changes.
> 
> * C++-fy the target itself.
>   As Yao mentioned in his mail [1] it would be better to classify
>   struct lk_private to better fit the direction GDB is currently going to.
>   In this process I would also get rid of some cleanups and further adept the
>   new C++ features.  Overall this will change some (but hopefully not
>   many) interfaces.  The biggest change will most likely be from function
>   hooks (in struct lk_private_hooks) to virtual class methods (in lk_private).
> 
> * Make LK_DECLARE_* macros more flexible.
>   Currently lk_init_private_data aborts once any declared symbol cannot be
>   found.  This also makes the whole target unusable if e.g. the kernel is
>   compiled without CONFIG_MODULES as then some symbols needed for module
>   support cannot be found.  My idea is to assign symbols to GDB-features e.g.
>   module support and only turn off those features if a symbol could not be
>   found.
> 
> * Design a proper CLI (i.e. functions like dmesg etc.).
>   This will be needed if we want others to actually use the feature.
> Shouldn't have any impact on you.
> 
> * Implement separate thread_lists.
>   Allow every target to manage its own thread_list.  Heavy impact for you and
> a lot work for me...
> 
> * Implement different target views.
>   Allow the user to switch between different target views (e.g. linux_kernel
>   and core/remote) and thus define the wanted level of abstraction.  Even
> worse then the separate thread_lists...
> 
> Long story short you don't have to divert away from my patches.  Even if I
> start working on the separate thread_lists next it will definitely take quite
> a lot of time to implement.  So no matter what you will most likely have a
> working patch before me ;)
> 
> I hope I answered all your questions.
> 
> Philipp
> 
> [1] https://sourceware.org/ml/gdb-patches/2017-05/msg00004.html
> 
> > Otherwise if you plan to make any further changes like going for a
> > separate thread list implementation for all layers of targets then i
> > can also divert away from your patches for a while untill next update
> > is posted.
> > 
> > I am already diverting away from Peter's original implementation
> > because of some basic limitations pointed out during previous reviews.
> > I dont have reliable solution right now but trying to find one lets
> > see if i can manage to upgrade this current hack for live threads as
> > well.
> > 
> > --
> > Omair.
> > 
> > On 3 May 2017 at 20:36, Philipp Rudo <prudo@linux.vnet.ibm.com> wrote:r  
> > > Hi Yao,
> > >
> > >
> > > On Tue, 02 May 2017 12:14:40 +0100
> > > Yao Qi <qiyaoltc@gmail.com> wrote:
> > >    
> > >> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> > >>
> > >> Hi Philipp,
> > >>    
> > >> > +/* 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."));
> > >> > +}
> > >> > +    
> > >>    
> > >> > +
> > >> > +/* 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 0000000..292ef97
> > >> > --- /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    
> > >>
> > >> "private" here is a little confusing.  How about rename it to
> > >> "linux_kernel"?    
> > >
> > > I called it "private" as it is the targets private data stored in its
> > > to_data hook.  But I don't mind renaming it.  Especially ...
> > >    
> > >> > +{
> > >> > +  /* 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;
> > >> > +};
> > >> > +    
> > >>
> > >> Secondly, can we change it to a class and function pointers in
> > >> lk_private_hooks become virtual functions.  gdbarch_lk_init_private
> > >> returns a pointer to an instance of sub-class of "linux_kernel".
> > >>
> > >> lk_init_private_data can be put the constructor of base class, to add
> > >> entries to "data", and sub-class (in each gdbarch) can add their own
> > >> specific stuff.    
> > >
> > > ... when classifying the struct, which already is on my long ToDo-list.
> > > This struct is a left over from when I started working on the project
> > > shortly before gdb-7.12 was released.  I didn't think that the
> > > C++-yfication would kick off that fast and started with plain C ...
> > >
> > > Thanks
> > > Philipp
> > >    
> > >> > +
> > >> > +/* 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);    
> > >>    
> > >> > +
> > >> > +/* 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 determin 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;
> > >> > +};    
> > >>    
> > >    
> >   
> 

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-08 11:22         ` Philipp Rudo
       [not found]           ` <CADrjBPqijRQFH4jthAedFzOzMLchpyvM53aXc9grOCjS2YUNCw@mail.gmail.com>
  2017-05-10  9:36           ` Philipp Rudo
@ 2017-05-19  8:45           ` Yao Qi
  2017-05-19 15:24             ` Andreas Arnez
  2 siblings, 1 reply; 35+ messages in thread
From: Yao Qi @ 2017-05-19  8:45 UTC (permalink / raw)
  To: Philipp Rudo
  Cc: Omair Javaid, gdb-patches, Yao Qi, Peter Griffin, Andreas Arnez

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

> * Implement separate thread_lists.
>   Allow every target to manage its own thread_list.  Heavy impact for you and a
>   lot work for me...

Hi Philipp,
before you spend a lot of time implementing this, it is better to start
an RFC discussion on an appropriate time, so that people can well
understand why do we need this change.

-- 
Yao (齐尧)

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-19  8:45           ` Yao Qi
@ 2017-05-19 15:24             ` Andreas Arnez
  2017-05-19 16:28               ` John Baldwin
  0 siblings, 1 reply; 35+ messages in thread
From: Andreas Arnez @ 2017-05-19 15:24 UTC (permalink / raw)
  To: Yao Qi; +Cc: Philipp Rudo, Omair Javaid, gdb-patches, Yao Qi, Peter Griffin

On Fri, May 19 2017, Yao Qi wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
>
>> * Implement separate thread_lists.
>>   Allow every target to manage its own thread_list.  Heavy impact for you and a
>>   lot work for me...
>
> Hi Philipp,
> before you spend a lot of time implementing this, it is better to start
> an RFC discussion on an appropriate time, so that people can well
> understand why do we need this change.

FYI, I have just started investigating this a bit.

The reason for multiple thread lists has been covered in some of the
discussions already, but let me give my few cents.

In the kernel live debug scenario we conceptually have two different
thread models layered on top of each other:

* LK target: Thread == Linux kernel thread
* Remote target: Thread == CPU

If we represent CPUs and Linux threads in a single thread list, then it
becomes difficult to maintain consistency between the LK target and the
remote target: Who owns which parts of the thread_info?  How to
guarantee unique ptids across the board, etc.?  Not to speak of the
confusing "info threads" output if CPUs and threads are munged
together.

Unfortunately many places in GDB assume that there is just one thread
list, one active target and one current inferior/thread.  In order to
maintain multiple thread lists cleanly, we probably have to lift these
restrictions and get rid of the global variables current_target,
thread_list, inferior_ptid, etc., or most of their uses.  That's my
preliminary conclusion, anyway.  Alternate suggestions are welcome.

--
Andreas

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-19 15:24             ` Andreas Arnez
@ 2017-05-19 16:28               ` John Baldwin
  2017-05-19 17:05                 ` Andreas Arnez
  0 siblings, 1 reply; 35+ messages in thread
From: John Baldwin @ 2017-05-19 16:28 UTC (permalink / raw)
  To: gdb-patches
  Cc: Andreas Arnez, Yao Qi, Philipp Rudo, Omair Javaid, Yao Qi, Peter Griffin

On Friday, May 19, 2017 05:24:09 PM Andreas Arnez wrote:
> On Fri, May 19 2017, Yao Qi wrote:
> 
> > Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> >
> >> * Implement separate thread_lists.
> >>   Allow every target to manage its own thread_list.  Heavy impact for you and a
> >>   lot work for me...
> >
> > Hi Philipp,
> > before you spend a lot of time implementing this, it is better to start
> > an RFC discussion on an appropriate time, so that people can well
> > understand why do we need this change.
> 
> FYI, I have just started investigating this a bit.
> 
> The reason for multiple thread lists has been covered in some of the
> discussions already, but let me give my few cents.
> 
> In the kernel live debug scenario we conceptually have two different
> thread models layered on top of each other:
> 
> * LK target: Thread == Linux kernel thread
> * Remote target: Thread == CPU
> 
> If we represent CPUs and Linux threads in a single thread list, then it
> becomes difficult to maintain consistency between the LK target and the
> remote target: Who owns which parts of the thread_info?  How to
> guarantee unique ptids across the board, etc.?  Not to speak of the
> confusing "info threads" output if CPUs and threads are munged
> together.
> 
> Unfortunately many places in GDB assume that there is just one thread
> list, one active target and one current inferior/thread.  In order to
> maintain multiple thread lists cleanly, we probably have to lift these
> restrictions and get rid of the global variables current_target,
> thread_list, inferior_ptid, etc., or most of their uses.  That's my
> preliminary conclusion, anyway.  Alternate suggestions are welcome.

FreeBSD's kernel GDB bits (which I maintain) have a similar issue, though for
now we only export kernel threads as threads in GDB and don't support CPUs as
a GDB-visible thing.  In some ways the model I would personally like would be
to have conceptual "layers" that you can bounce up and down between kind of
like a stack, but in this case a stack of thread targets, so that I could do
a kind of 'thread_down' and now 'info threads' would only show me CPUs, allow
me to select CPUs, etc. but then have a 'thread_up' to pop back up to the
kernel thread layer.  The best model I can think of is that this is similar
to M:N user-thread implementations where you have user threads multiplexed
onto LWPs.  In such a world (which I'm not sure many OS's use these days) it
would also be nice to kind of bounce between the worlds.  (In fact, the
model I have been toying with but have not yet implemented for adapting
FreeBSD's current kernel target support to qemu or the GDB stub I'm hacking
on for FreeBSD's native bhyve hypervisor would be to treat vCPUs as LWPs
so their ptid would have lwp == vcpu, and kernel-level threads as "threads",
so their ptid would have tid == kernel thread id).

-- 
John Baldwin

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-19 16:28               ` John Baldwin
@ 2017-05-19 17:05                 ` Andreas Arnez
  2017-05-19 17:40                   ` John Baldwin
  0 siblings, 1 reply; 35+ messages in thread
From: Andreas Arnez @ 2017-05-19 17:05 UTC (permalink / raw)
  To: John Baldwin
  Cc: gdb-patches, Yao Qi, Philipp Rudo, Omair Javaid, Yao Qi, Peter Griffin

On Fri, May 19 2017, John Baldwin wrote:

> FreeBSD's kernel GDB bits (which I maintain) have a similar issue, though for
> now we only export kernel threads as threads in GDB and don't support CPUs as
> a GDB-visible thing.  In some ways the model I would personally like would be
> to have conceptual "layers" that you can bounce up and down between kind of
> like a stack, but in this case a stack of thread targets, so that I could do
> a kind of 'thread_down' and now 'info threads' would only show me CPUs, allow
> me to select CPUs, etc. but then have a 'thread_up' to pop back up to the
> kernel thread layer.

Exactly!  Note that GDB already has a stack of "layers" -- the target
stack.  Thus I'm considering commands like "target up/down" for this
purpose.  Of course this requires per-target thread lists.

> The best model I can think of is that this is similar to M:N
> user-thread implementations where you have user threads multiplexed
> onto LWPs.  In such a world (which I'm not sure many OS's use these
> days) it would also be nice to kind of bounce between the worlds.

M:N user-thread implementations have probably become more popular with
Go.  In that scenario we have the following layers:

* Threads == Goroutines (user-thread implementation)
* Threads == OS threads

> (In fact, the model I have been toying with but have not yet
> implemented for adapting FreeBSD's current kernel target support to
> qemu or the GDB stub I'm hacking on for FreeBSD's native bhyve
> hypervisor would be to treat vCPUs as LWPs so their ptid would have
> lwp == vcpu, and kernel-level threads as "threads", so their ptid
> would have tid == kernel thread id).

So kernel-level threads can not be rescheduled on a different vCPU?

--
Andreas

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-19 17:05                 ` Andreas Arnez
@ 2017-05-19 17:40                   ` John Baldwin
  2017-05-22 10:18                     ` Andreas Arnez
  0 siblings, 1 reply; 35+ messages in thread
From: John Baldwin @ 2017-05-19 17:40 UTC (permalink / raw)
  To: gdb-patches
  Cc: Andreas Arnez, Yao Qi, Philipp Rudo, Omair Javaid, Yao Qi, Peter Griffin

On Friday, May 19, 2017 07:05:47 PM Andreas Arnez wrote:
> On Fri, May 19 2017, John Baldwin wrote:
> 
> > FreeBSD's kernel GDB bits (which I maintain) have a similar issue, though for
> > now we only export kernel threads as threads in GDB and don't support CPUs as
> > a GDB-visible thing.  In some ways the model I would personally like would be
> > to have conceptual "layers" that you can bounce up and down between kind of
> > like a stack, but in this case a stack of thread targets, so that I could do
> > a kind of 'thread_down' and now 'info threads' would only show me CPUs, allow
> > me to select CPUs, etc. but then have a 'thread_up' to pop back up to the
> > kernel thread layer.
> 
> Exactly!  Note that GDB already has a stack of "layers" -- the target
> stack.  Thus I'm considering commands like "target up/down" for this
> purpose.  Of course this requires per-target thread lists.

Yes, a target up/down might work.  Right now you can push/pop targets so in
theory you can do this today with "target push kthread" and then "target pop".
I hadn't played with this enough to know if that would be sufficient or not
or if we wanted the targets to be more persistent to avoid having to recreate
the thread list during each push.  One thing I wanted to look at in more
detail is how this interaction worked for the older M:N threading targets.
FreeBSD used to use M:N threading in userland but abandoned that a while ago.
The old thread target for that used libthread_db and you only had the one
thread list, never a way to pop back down to the LWP view.

> > The best model I can think of is that this is similar to M:N
> > user-thread implementations where you have user threads multiplexed
> > onto LWPs.  In such a world (which I'm not sure many OS's use these
> > days) it would also be nice to kind of bounce between the worlds.
> 
> M:N user-thread implementations have probably become more popular with
> Go.  In that scenario we have the following layers:
> 
> * Threads == Goroutines (user-thread implementation)
> * Threads == OS threads

Hmm.

> > (In fact, the model I have been toying with but have not yet
> > implemented for adapting FreeBSD's current kernel target support to
> > qemu or the GDB stub I'm hacking on for FreeBSD's native bhyve
> > hypervisor would be to treat vCPUs as LWPs so their ptid would have
> > lwp == vcpu, and kernel-level threads as "threads", so their ptid
> > would have tid == kernel thread id).
> 
> So kernel-level threads can not be rescheduled on a different vCPU?

They definitely can.  The same is true for user-level thread with LWPs
on systems with M:N threading (e.g. scheduler activations on Solaris
or FreeBSD's old KSE M:N threading model).

-- 
John Baldwin

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

* Re: [RFC v3 3/8] Add basic Linux kernel support
  2017-05-19 17:40                   ` John Baldwin
@ 2017-05-22 10:18                     ` Andreas Arnez
  0 siblings, 0 replies; 35+ messages in thread
From: Andreas Arnez @ 2017-05-22 10:18 UTC (permalink / raw)
  To: John Baldwin
  Cc: gdb-patches, Yao Qi, Philipp Rudo, Omair Javaid, Yao Qi, Peter Griffin

On Fri, May 19 2017, John Baldwin wrote:

> On Friday, May 19, 2017 07:05:47 PM Andreas Arnez wrote:
>> On Fri, May 19 2017, John Baldwin wrote:
>> 
>> > FreeBSD's kernel GDB bits (which I maintain) have a similar issue, though for
>> > now we only export kernel threads as threads in GDB and don't support CPUs as
>> > a GDB-visible thing.  In some ways the model I would personally like would be
>> > to have conceptual "layers" that you can bounce up and down between kind of
>> > like a stack, but in this case a stack of thread targets, so that I could do
>> > a kind of 'thread_down' and now 'info threads' would only show me CPUs, allow
>> > me to select CPUs, etc. but then have a 'thread_up' to pop back up to the
>> > kernel thread layer.
>> 
>> Exactly!  Note that GDB already has a stack of "layers" -- the target
>> stack.  Thus I'm considering commands like "target up/down" for this
>> purpose.  Of course this requires per-target thread lists.
>
> Yes, a target up/down might work.  Right now you can push/pop targets so in
> theory you can do this today with "target push kthread" and then "target pop".
> I hadn't played with this enough to know if that would be sufficient or not
> or if we wanted the targets to be more persistent to avoid having to recreate
> the thread list during each push.  One thing I wanted to look at in more
> detail is how this interaction worked for the older M:N threading targets.
> FreeBSD used to use M:N threading in userland but abandoned that a while ago.
> The old thread target for that used libthread_db and you only had the one
> thread list, never a way to pop back down to the LWP view.

Right, it might be interesting how that interaction worked.  If you gain
any insight, please share.  From a quick glance at these targets I had
the impression that they didn't work at all with the remote target
beneath them.

--
Andreas

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

end of thread, other threads:[~2017-05-22 10:18 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-16 16:57 [RFC v3 0/8] Support for Linux kernel debugging Philipp Rudo
2017-03-16 16:57 ` [RFC v3 1/8] Convert substitute_path_component to C++ Philipp Rudo
2017-04-20 20:02   ` Sergio Durigan Junior
2017-05-03 16:20     ` Philipp Rudo
2017-03-16 16:58 ` [RFC v3 6/8] Seperate common s390-tdep.* from s390-linux-tdep.* Philipp Rudo
2017-03-16 16:58 ` [RFC v3 8/8] Add S390 support for linux-kernel target Philipp Rudo
2017-03-16 16:58 ` [RFC v3 5/8] Add commands " Philipp Rudo
2017-03-16 16:58 ` [RFC v3 3/8] Add basic Linux kernel support Philipp Rudo
2017-04-16 22:59   ` Omair Javaid
2017-05-03 14:38     ` Philipp Rudo
2017-04-20 11:09   ` Omair Javaid
2017-04-24 15:24     ` Andreas Arnez
2017-05-03 14:13       ` Omair Javaid
2017-05-03 15:20         ` Philipp Rudo
2017-05-03 14:38     ` Philipp Rudo
2017-05-02 11:14   ` Yao Qi
2017-05-03 15:36     ` Philipp Rudo
2017-05-07 23:54       ` Omair Javaid
2017-05-08 11:22         ` Philipp Rudo
     [not found]           ` <CADrjBPqijRQFH4jthAedFzOzMLchpyvM53aXc9grOCjS2YUNCw@mail.gmail.com>
2017-05-10  9:03             ` Philipp Rudo
2017-05-10  9:36           ` Philipp Rudo
2017-05-19  8:45           ` Yao Qi
2017-05-19 15:24             ` Andreas Arnez
2017-05-19 16:28               ` John Baldwin
2017-05-19 17:05                 ` Andreas Arnez
2017-05-19 17:40                   ` John Baldwin
2017-05-22 10:18                     ` Andreas Arnez
2017-03-16 16:58 ` [RFC v3 4/8] Add kernel module support for linux-kernel target Philipp Rudo
2017-05-02 13:15   ` Yao Qi
2017-05-03 16:16     ` Philipp Rudo
2017-05-05 21:33       ` Yao Qi
2017-05-08  9:18         ` Philipp Rudo
2017-05-08 13:05           ` Yao Qi via gdb-patches
2017-03-16 16:58 ` [RFC v3 7/8] Add privileged registers for s390x Philipp Rudo
2017-03-16 16:58 ` [RFC v3 2/8] Add libiberty/concat styled concat_path function 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).