public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC PATCH v5 5/9] Add kernel module support for linux-kernel target
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (4 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 9/9] Add S390 support " Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 1/9] Convert substitute_path_component to C++ Philipp Rudo
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

Implement module support for the new linux-kernel target by handling them
as shared libraries.

Unlike everything before, modules live in a kernel virtual address space.
So the linux-kernel target has to be extended to support address
translation.  The approach to do so is to hook into to_xfer_partial and
translate the address before it is passed down to the target beneath.
Unfortunately this approach has a huge drawback, as to_xfer_partial only
gets the address as ULONGEST without any information on the address space.
So all addresses are treated as kernel virtual addresses, even when they are
from other address spaces, causing mistranslations.

gdb/ChangeLog:

    * lk-modules.h: New file.
    * lk-modules.c: New file.
    * lk-low.h <linux_kernel_ops> (is_kvaddr, vtop, kvtop)
    (adjust_module_layout): New virtual methods.
    (LK_MODULES_NAME_LEN, LK_UTS_NAME_LEN): New define.
    * lk-low.c (lk-modules.h): New include.
    (linux_kernel_ops::read_symbols): Declare needed symbols.
    (lk_xfer_partial): New functions.
    (lk_try_push_target): Set solib_ops.
    (init_linux_kernel_ops): Set to_xfer_partial.
    * solib.c (get_solib_search_path): New function.
    * solib.h (get_solib_search_path): New export.
    * Makefile.in (ALLDEPFILES): Add lk-modules.c.
    (HFILES_NO_SRCDIR): Add lk-modules.h.
    (ALL_TARGET_OBS): Add lk-modules.o.
    * configure.tgt (lk_tobjs): Add lk-modules.o.
---
 gdb/Makefile.in   |   3 +
 gdb/configure.tgt |   2 +-
 gdb/lk-low.c      |  86 ++++++++++
 gdb/lk-low.h      |  27 +++
 gdb/lk-modules.c  | 501 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-modules.h  | 148 ++++++++++++++++
 gdb/solib.c       |   8 +
 gdb/solib.h       |   5 +
 8 files changed, 779 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 056333e2cd..afa8039a95 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -716,6 +716,7 @@ ALL_TARGET_OBS = \
 	linux-record.o \
 	linux-tdep.o \
 	lk-low.o \
+	lk-modules.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1278,6 +1279,7 @@ HFILES_NO_SRCDIR = \
 	linux-record.h \
 	linux-tdep.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2259,6 +2261,7 @@ ALLDEPFILES = \
 	linux-record.c \
 	linux-tdep.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 be68ac50fc..39b2144e00 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -42,7 +42,7 @@ amd64_tobjs="amd64-tdep.o arch/amd64.o"
 
 # List of objectfiles for Linux kernel support.  To be included into *-linux*
 # targets wich support Linux kernel debugging.
-lk_tobjs="lk-low.o"
+lk_tobjs="lk-low.o lk-modules.o"
 
 # Here are three sections to get a list of target specific object
 # files according to target triplet $TARG.
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index 193619e6f5..54d3b6272d 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -30,6 +30,7 @@
 #include "lk-bitmap.h"
 #include "lk-list.h"
 #include "lk-low.h"
+#include "lk-modules.h"
 #include "objfiles.h"
 #include "observer.h"
 #include "solib.h"
@@ -386,9 +387,68 @@ linux_kernel_ops::read_symbols ()
 
   declare_field ("cpumask->bits", LK_CONFIG_ALWAYS);
 
+  declare_field ("mm_struct->pgd", LK_CONFIG_ALWAYS);
+
+  declare_field ("pgd_t->pgd", LK_CONFIG_ALWAYS);
+
+  declare_field ("module->state", LK_CONFIG_MODULES);
+  declare_field ("module->list", LK_CONFIG_MODULES);
+  declare_field ("module->name", LK_CONFIG_MODULES);
+  declare_field ("module->source_list", LK_CONFIG_MODULES);
+  declare_field ("module->arch", LK_CONFIG_MODULES);
+  declare_field ("module->percpu", LK_CONFIG_SMT);
+  declare_field ("module->percpu_size", LK_CONFIG_SMT);
+
+  /* Module offset moved to new struct module_layout with linux 4.5.
+     It must be checked in code which of these fields exist.  */
+  if (try_declare_field ("module_layout->base")) /* linux 4.5+ */
+    {
+      declare_field ("module->init_layout", LK_CONFIG_MODULES);
+      declare_field ("module->core_layout", LK_CONFIG_MODULES);
+
+      declare_field ("module_layout->size", LK_CONFIG_MODULES);
+      declare_field ("module_layout->text_size", LK_CONFIG_MODULES);
+      declare_field ("module_layout->ro_size", LK_CONFIG_MODULES);
+
+      /* Only try to declare -> existence needs to be checked in code.  */
+      try_declare_field ("module_layout->ro_after_init_size"); /* linux 4.8+ */
+    }
+  else if (try_declare_field ("module->module_core")) /* linux -4.4 */
+    {
+      declare_field ("module->module_init", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_size", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_text_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_text_size", LK_CONFIG_MODULES);
+
+      declare_field ("module->init_ro_size", LK_CONFIG_MODULES);
+      declare_field ("module->core_ro_size", LK_CONFIG_MODULES);
+    }
+  else
+    {
+      m_kconfig |= LK_CONFIG_MODULES;
+      warning (_("No symbols for the module layout found."));
+    }
+
+  declare_field ("module_use->source_list", LK_CONFIG_MODULES);
+  declare_field ("module_use->source", LK_CONFIG_MODULES);
+
+  declare_field ("uts_namespace->name", LK_CONFIG_ALWAYS);
+
+  declare_type ("utsname",
+		{"new_utsname", "old_utsname", "oldold_utsname"},
+		LK_CONFIG_ALWAYS);
+  declare_field ("utsname->version", LK_CONFIG_ALWAYS);
+  declare_field ("utsname->release", LK_CONFIG_ALWAYS);
+
   declare_address ("init_task", LK_CONFIG_ALWAYS);
   declare_address ("runqueues", LK_CONFIG_ALWAYS);
   declare_address ("__per_cpu_offset", LK_CONFIG_ALWAYS);
+  declare_address ("init_mm", LK_CONFIG_ALWAYS);
+  declare_address ("modules", LK_CONFIG_MODULES);
+  declare_address ("init_uts_ns", LK_CONFIG_ALWAYS);
 
   declare_address ("cpu_online_mask", {"__cpu_online_mask", /* linux 4.5+ */
 				       "cpu_online_bits"},  /* linux -4.4 */
@@ -657,6 +717,25 @@ lk_thread_name (struct target_ops *target, struct thread_info *ti)
   return str.c_str ();
 }
 
+/* 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;
+  scoped_restore restore_target = make_scoped_restore (&current_target.beneath,
+						       ops->beneath);
+  if (lk_ops->is_kvaddr (offset))
+    offset = lk_ops->kvtop (offset);
+
+  ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+					    readbuf, writebuf, offset, len,
+					    xfered_len);
+  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.  */
@@ -736,6 +815,12 @@ lk_try_push_target ()
 
   if (!target_is_pushed (lk_target_ops))
     push_target (lk_ops->target ());
+
+  if (lk_ifdef (LK_CONFIG_MODULES))
+    lk_init_modules (gdbarch);
+  else
+    warning (_("Could not find all symbols for module support.  "
+	       "Module support turned off."));
 }
 
 /* Function for targets to_open hook.  */
@@ -842,6 +927,7 @@ init_lk_target_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 39c0d88f43..31ae4d7966 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -23,11 +23,14 @@
 #include <unordered_map>
 
 #include "gdbtypes.h"
+#include "lk-modules.h"
 #include "target.h"
 
 /* Copied 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
@@ -104,6 +107,30 @@ public:
   virtual void get_registers (CORE_ADDR task, struct target_ops *target,
 			      struct regcache *regcache, int regnum) = 0;
 
+  /* Check if address ADDR is a kernel virtual address.
+     NOTE: This hook is called in the context of target beneath.  */
+  virtual bool is_kvaddr (CORE_ADDR addr) = 0;
+
+  /* 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.  */
+  virtual CORE_ADDR vtop (CORE_ADDR addr, CORE_ADDR pgd) = 0;
+
+  /* Translate a kernel virtual address ADDR to a physical address.
+     NOTE: This hook is called in the context of target beneath.  */
+  virtual CORE_ADDR kvtop (CORE_ADDR addr)
+  {
+    CORE_ADDR pgd = lk_read_addr (address ("init_mm")
+				  + offset ("mm_struct->pgd"));
+    return vtop (addr, pgd);
+  }
+
+  /* Add offset between a modules base and the start of its first section.
+     Equivalent to
+     <linux>/include/linux/moduleloader.h:module_frob_arch_sections  */
+  virtual void adjust_module_layout (CORE_ADDR mod, lk_module *module)
+  {}
+
   /* Return the per_cpu_offset of cpu CPU.  Default uses __per_cpu_offset
      array to determine the offset.  */
   virtual CORE_ADDR percpu_offset (unsigned int cpu);
diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
new file mode 100644
index 0000000000..18e31aadda
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,501 @@
+/* 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-list.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>
+
+static struct target_so_ops *lk_modules_so_ops = NULL;
+
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_section_type::clear ()
+{
+  base = 0;
+  offset = 0;
+  size = 0;
+}
+
+/* Constructor for module_layout located at address LAYOUT.
+   Linux 4.5+ only! */
+
+lk_module_layout::lk_module_layout (CORE_ADDR layout)
+{
+  base = lk_read_addr (layout + lk_offset ("module_layout->base"));
+  size = lk_read_uint (layout + lk_offset ("module_layout->size"));
+
+  text.base = base;
+  text.size = lk_read_uint (layout
+			    + lk_offset ("module_layout->text_size"));
+
+  ro_data.base = base + text.size;
+  ro_data.size = lk_read_uint (layout
+			       + lk_offset ("module_layout->ro_size"));
+
+  if (lk_ops->has_field ("module_layout->ro_after_init_size")) /* linux 4.8+ */
+    {
+      ro_after_init_data.base = base + ro_data.size;
+      ro_after_init_data.size
+	= lk_read_uint (layout
+			+ lk_offset ("module_layout->ro_after_init_size"));
+
+      rw_data.base = base + ro_after_init_data.size;
+    }
+  else /* linux 4.5 - 4.8 */
+    {
+      rw_data.base = base + ro_data.size;
+    }
+
+  rw_data.size = size;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module_layout::clear ()
+{
+  base = 0;
+  size = 0;
+
+  text.clear ();
+  ro_data.clear ();
+  rw_data.clear ();
+}
+
+/* Init MODULE from struct module located at address MOD.
+   Linux -4.4 only!  */
+
+static void
+lk_module_init_module_from_struct_module (CORE_ADDR mod, lk_module *module)
+{
+  module->core = new lk_module_layout;
+  module->init = new lk_module_layout;
+
+  /* Short-hand access.  */
+  lk_module_layout *core = module->core;
+  lk_module_layout *init = module->init;
+
+  /* Core layout.  */
+  core->base = lk_read_addr (mod + lk_offset ("module->module_core"));
+  core->size = lk_read_uint (mod + lk_offset ("module->core_size"));
+
+  core->text.base = core->base;
+  core->text.size = lk_read_uint (mod
+				  + lk_offset ("module->core_text_size"));
+
+  core->ro_data.base = core->base + core->text.size;
+  core->ro_data.size = lk_read_uint (mod
+				     + lk_offset ("module->core_ro_size"));
+
+  core->rw_data.base = core->base + core->ro_data.size;
+  core->rw_data.size = core->size;
+
+  /* Init layout.  */
+  init->base = lk_read_addr (mod + lk_offset ("module->module_init"));
+  init->size = lk_read_uint (mod + lk_offset ("module->init_size"));
+
+  init->text.base = init->base;
+  init->text.size = lk_read_uint (mod
+				  + lk_offset ("module->init_text_size"));
+
+  init->ro_data.base = init->base + init->text.size;
+  init->ro_data.size = lk_read_uint (mod
+				     + lk_offset ("module->init_ro_size"));
+
+  init->rw_data.base = init->base + init->ro_data.size;
+  init->rw_data.size = init->size;
+}
+
+/* Constructor for kernel module from struct module located at MOD.  */
+
+lk_module::lk_module (CORE_ADDR mod)
+{
+  state = (lk_module_state) lk_read_int (mod + lk_offset ("module->state"));
+
+  if (lk_ops->has_field ("module->module_core")) /* linux -4.4 */
+    {
+      lk_module_init_module_from_struct_module (mod, this);
+    }
+  else /* linux 4.5+ */
+    {
+      core = new lk_module_layout (mod + lk_offset ("module->core_layout"));
+      init = new lk_module_layout (mod + lk_offset ("module->init_layout"));
+    }
+
+  if (lk_ifdef (LK_CONFIG_SMT))
+    {
+      percpu.base = lk_read_addr (mod + lk_offset ("module->percpu"));
+      percpu.size = lk_read_uint (mod + lk_offset ("module->percpu_size"));
+    }
+
+  /* Architectures can adjust the sections in the kernel (via
+     module_frob_arch_sections in <linux>/kernel/module.c:layout_and_allocate).
+     Give them a chance to do the same in GDB.  */
+  lk_ops->adjust_module_layout (mod, this);
+}
+
+lk_module::~lk_module ()
+{
+  delete core;
+  delete init;
+}
+
+/* See comment in lk-modules.h.  */
+
+void
+lk_module::clear ()
+{
+  state = LK_MODULE_STATE_INVALID;
+
+  core->clear ();
+  init->clear ();
+
+  percpu.clear ();
+}
+
+/* Check if debug info for module NAME is loaded.  */
+
+bool
+lk_modules_debug_info_loaded (const std::string &name)
+{
+  for (so_list *so = master_so_list (); so != NULL; 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_address ("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.  */
+
+static gdb_file_up
+lk_modules_open_mod_order ()
+{
+  std::string filename = concat_path (lk_modules_expand_search_path (),
+				      "modules.order");
+  gdb_file_up 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.  */
+
+static std::unordered_map<std::string, std::string>
+lk_modules_build_path_map ()
+{
+  std::unordered_map<std::string, std::string> umap;
+  char line[SO_NAME_MAX_PATH_SIZE + 1];
+
+  gdb_file_up mod_order = lk_modules_open_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.get ()))
+    {
+      /* 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);
+    }
+
+  return umap;
+}
+
+/* Implement current_sos target_so_ops method.  */
+
+static struct so_list *
+lk_modules_current_sos (void)
+{
+  struct so_list *list = NULL;
+  std::unordered_map<std::string, std::string> umap;
+
+  umap = lk_modules_build_path_map ();
+  for (CORE_ADDR mod : lk_list ("modules", "module->list"))
+    {
+      char name[LK_MODULE_NAME_LEN];
+      CORE_ADDR name_addr;
+
+      name_addr = mod + lk_offset ("module->name");
+      read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
+
+      if (!umap.count (name))
+	continue;
+
+      struct so_list *newso = XCNEW (struct so_list);
+
+      newso->next = list;
+      list = newso;
+      newso->lm_info = new lk_module (mod);
+      strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE);
+      strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE);
+      newso->pspace = current_program_space;
+    }
+
+  return list;
+}
+
+/* Relocate target_section GDB_SEC using section type LK_SEC.  Helper
+   function for lk_modules_relocate_section_addresses.  */
+
+static void
+lk_modules_relocate_sec (struct target_section *gdb_sec,
+			 lk_module_section_type &lk_sec)
+{
+  unsigned int alignment = 1 << gdb_sec->the_bfd_section->alignment_power;
+  CORE_ADDR base = lk_sec.base + lk_sec.offset;
+
+  /* Adjust offset to section alignment.  */
+  if (base % alignment != 0)
+    base += alignment - (base % alignment);
+
+  gdb_sec->addr += base;
+  gdb_sec->endaddr += base;
+  lk_sec.offset += gdb_sec->endaddr - gdb_sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+static void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  /* Remove unmapped sections.  */
+  if (streq (name, ".modinfo") || streq (name, "__versions")
+      || (startswith (name, ".init")
+	  && module->state != LK_MODULE_STATE_COMING))
+    {
+      // FIXME Not mapping a section != unmapping a section
+      //
+      // In particular this causes GDB to map the "unmapped" section at the
+      // same address as the last mapped section (e.g. init.text starts at
+      // the same address like .text).  Thus printing symbols definied in the
+      // unmapped section returnes random values instead of <opimized out>.
+      return;
+    }
+
+  /* module->percpu.base points to data of cpu0.  To access the data of
+     a cpu other than cpu0 the corresponding percpu offset needs to added
+     by hand.  */
+  if (lk_ifdef (LK_CONFIG_MODULES) && endswith (name, ".percpu"))
+    {
+      lk_modules_relocate_sec (sec, module->percpu);
+      return;
+    }
+
+  lk_module_layout *layout;
+  if (startswith (name, ".init"))
+    layout = module->init;
+  else
+    layout = module->core;
+
+  if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, layout->text);
+  else if (endswith (name, ".ro_after_init"))
+    lk_modules_relocate_sec (sec, layout->ro_after_init_data);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, layout->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, layout->rw_data);
+
+  /* Set address range displayed with 'info shared' to match core_layout.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = module->core->base;
+      so->addr_high = module->core->base + module->core->size;
+    }
+}
+
+/* Implement free_so target_so_ops method.  */
+
+static void
+lk_modules_free_so (struct so_list *so)
+{
+  delete so->lm_info;
+}
+
+/* Implement clear_so target_so_ops method.  */
+
+static void
+lk_modules_clear_so (struct so_list *so)
+{
+  lk_module *module = (lk_module *) so->lm_info;
+  gdb_assert (module != NULL);
+
+  module->clear ();
+}
+
+/* Implement clear_solib target_so_ops method.  */
+
+static void
+lk_modules_clear_solib ()
+{
+  /* Nothing to do.  */
+}
+
+/* Implement clear_create_inferior_hook target_so_ops method.  */
+
+static void
+lk_modules_create_inferior_hook (int from_tty)
+{
+  /* Nothing to do.  */
+}
+
+/* Implement in_dynsym_resolve_code target_so_ops method.  */
+
+static int
+lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+  return 0;
+}
+
+/* Implement same target_so_ops method.  Assume the module to be different
+   if its state changed.  Otherwise there might be problems with pruning the
+   .init sections if the module was first seen during init.  */
+
+static int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  lk_module_state gdb_state = ((lk_module *) gdb->lm_info)->state;
+  lk_module_state inf_state = ((lk_module *) inf->lm_info)->state;
+
+  return streq (gdb->so_name, inf->so_name) && gdb_state == inf_state;
+}
+
+/* See lk-modules.h.  */
+
+void
+lk_init_modules (struct gdbarch *gdbarch)
+{
+  set_solib_ops (gdbarch, lk_modules_so_ops);
+}
+
+/* Initialize linux modules solib target.  */
+
+static void
+init_lk_modules_so_ops (void)
+{
+  struct target_so_ops *t;
+
+  if (lk_modules_so_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_so_ops);
+  t->relocate_section_addresses = lk_modules_relocate_section_addresses;
+  t->free_so = lk_modules_free_so;
+  t->clear_so = lk_modules_clear_so;
+  t->clear_solib = lk_modules_clear_solib;
+  t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
+  t->current_sos = lk_modules_current_sos;
+  t->bfd_open = solib_bfd_open;
+  t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
+  t->same = lk_modules_same;
+
+  lk_modules_so_ops = t;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_lk_modules;
+
+void
+_initialize_lk_modules (void)
+{
+  init_lk_modules_so_ops ();
+}
diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h
new file mode 100644
index 0000000000..44d86a2a95
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,148 @@
+/* Handle kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_MODULES_H__
+#define __LK_MODULES_H__
+
+#include "solist.h"
+
+/* Copied from <linux>/include/linux/module.h.  */
+
+enum
+lk_module_state
+{
+  LK_MODULE_STATE_INVALID = -1, /* Invalid state, GDB only.  */
+  LK_MODULE_STATE_LIVE,		/* Normal state. */
+  LK_MODULE_STATE_COMING,	/* Full formed, running module_init. */
+  LK_MODULE_STATE_GOING,	/* Going away. */
+  LK_MODULE_STATE_UNFORMED,	/* Still setting it up. */
+};
+
+/* The kernel modules .ko files are only partially linked, i.e. they contain
+   section headers but no program headers.  When the module is loaded the
+   sections are relocated by the kernel (<linux>/kernel/module.c:load_module
+   & layout_and_allocate & layout_sections).  Here the kernel destinguishes
+   between different section types
+
+   * text (SHF_EXECINSTR | SHF_ALLOC)
+   * read-only data (SHF_ALLOC & !SHF_WRITE)
+   * read-only-after-init data (SHF_ALLOC | SHF_RO_AFTER_INIT)
+   * read-write data (SHF_WRITE | SHF_ALLOC)
+
+   combined in module layouts.
+
+   There are two module layouts, init and core.  The init module layout
+   contains all sections where the section name starts with ".init".  The
+   content of the init module layout gets unlinked once the module is
+   loaded.  The core module layout contains the "normal" text and data
+   needed during module runtime.
+
+   Furthermore there can be a
+
+   * data..percpu (<linux>/kernel/module.c:percpu_modalloc)
+
+   section which gets relocated to the other percpu data. */
+
+struct lk_module_section_type
+{
+  /* Lowest address of this section type.  */
+  CORE_ADDR base = 0;
+
+  /* Size of this section type.  Calculated by GDB during relocation.  */
+  CORE_ADDR offset = 0;
+
+  /* Accumulated size of all section types "below".  Read from kernel
+     (except for rw_data).  */
+  unsigned int size = 0;
+
+  /* Reset this module_section to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+};
+
+/* Copied from <linux>/include/linux/module.h: struct module_layout.  */
+
+struct lk_module_layout
+{
+  lk_module_layout () = default;
+  lk_module_layout (CORE_ADDR layout);
+
+  /* Reset this module_layout to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* Lowest address of this module layout.  */
+  CORE_ADDR base = 0;
+
+  /* Total size.  */
+  unsigned int size = 0;
+
+  /* Executable text.
+     text.size = text.  */
+  lk_module_section_type text;
+
+  /* Read-only data.
+     ro_data.size = text + ro_data.  */
+  lk_module_section_type ro_data;
+
+  /* Read-only-after-init data.
+     ro_after_init_data.size = text + ro_data + ro_after_init_data.  */
+  lk_module_section_type ro_after_init_data; /* linux 4.8+ */
+
+  /* Writable data.
+     rw_data.size = layout.size.  */
+  lk_module_section_type rw_data;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lk_module : public lm_info_base
+{
+  lk_module () = delete;
+  lk_module (CORE_ADDR mod);
+
+  ~lk_module ();
+
+  /* Reset this module to default (all zero).  Needed for
+     target_so_ops->clear_so hook.  */
+  void clear ();
+
+  /* This modules state as seen from the kernel.  */
+  lk_module_state state = LK_MODULE_STATE_INVALID;
+
+  /* "core" layout, i.e. text + data after init.  */
+  lk_module_layout *core;
+
+  /* "init" layout, i.e. text + data only needed during init
+     (LK_MODULE_STATE_COMING).  It is unmapped after initialization.  */
+  lk_module_layout *init;
+
+  /* Per CPU data.  Mapped together with "ordinary" per cpu data.  */
+  lk_module_section_type percpu;
+};
+
+
+/* Check if debug info for module NAME are loaded.  Needed by lsmod command.  */
+
+extern bool lk_modules_debug_info_loaded (const std::string &name);
+
+/* Initialize module support for lk target.  */
+extern void lk_init_modules (struct gdbarch *gdbarch);
+
+#endif /*  __LK_MODULES_H__  */
diff --git a/gdb/solib.c b/gdb/solib.c
index 1c78845938..c411cc6124 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -105,6 +105,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 f6f02ad77a..8d58cc0675 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -31,6 +31,11 @@ struct program_space;
 /* List of known shared objects */
 #define so_list_head current_program_space->so_list
 
+/* Returns the solib_search_path.  The returned string is malloc'ed and must be
+   freed by the caller.  */
+
+extern const char *get_solib_search_path ();
+
 /* Called when we free all symtabs, to free the shared library information
    as well.  */
 
-- 
2.13.5

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

* [RFC PATCH v5 7/9] Add privileged registers for s390x
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 6/9] Add commands for linux-kernel target Philipp Rudo
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, 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:
	(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     | 170 ++++++++++++++++++++++++++++++++++
 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                       |  24 ++++-
 10 files changed, 562 insertions(+), 9 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 82609f5862..7924a64d1e 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -79,6 +79,7 @@ WHICH = aarch64 \
 	s390-te-linux64 s390x-te-linux64 s390-vx-linux64 s390x-vx-linux64 \
 	s390-tevx-linux64 s390x-tevx-linux64 \
 	s390-gs-linux64 s390x-gs-linux64 \
+	s390x-cr-linux64 s390x-vxcr-linux64 \
 	tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
 
 # Record which registers should be sent to GDB by default after stop.
@@ -179,16 +180,18 @@ 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 \
 	s390-gs-linux64.xml \
-	s390x-gs-linux64.xml
+	s390x-gs-linux64.xml \
+	s390x-cr-linux64.xml \
+	s390x-tevx-linux64.xml \
+	s390x-vxcr-linux64.xml
 
 TDESC_CFILES = $(patsubst %.xml,%.c,$(XMLTOC))
 GDB = false
diff --git a/gdb/features/s390-cr.xml b/gdb/features/s390-cr.xml
new file mode 100644
index 0000000000..15ae4f9af7
--- /dev/null
+++ b/gdb/features/s390-cr.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.cr">
+  <reg name="cr0" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr1" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr2" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr3" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr4" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr5" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr6" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr7" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr8" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr9" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr10" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr11" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr12" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr13" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr14" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr15" bitsize="64" type="uint64" group="control"/>
+</feature>
diff --git a/gdb/features/s390x-cr-linux64.c b/gdb/features/s390x-cr-linux64.c
new file mode 100644
index 0000000000..5f75aefe26
--- /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 ();
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  struct tdesc_feature *feature;
+
+  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", 64, "uint64");
+  tdesc_create_reg (feature, "prefix", 70, 1, "system", 32, "uint32");
+
+  tdesc_s390x_cr_linux64 = result;
+}
diff --git a/gdb/features/s390x-cr-linux64.xml b/gdb/features/s390x-cr-linux64.xml
new file mode 100644
index 0000000000..75c5d611df
--- /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="64" type="uint64" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/features/s390x-vxcr-linux64.c b/gdb/features/s390x-vxcr-linux64.c
new file mode 100644
index 0000000000..0ae2f93629
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.c
@@ -0,0 +1,170 @@
+/* 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 ();
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  struct tdesc_feature *feature;
+
+  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");
+  tdesc_type *element_type;
+  element_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_create_vector (feature, "v4f", element_type, 4);
+
+  element_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_create_vector (feature, "v2d", element_type, 2);
+
+  element_type = tdesc_named_type (feature, "int8");
+  tdesc_create_vector (feature, "v16i8", element_type, 16);
+
+  element_type = tdesc_named_type (feature, "int16");
+  tdesc_create_vector (feature, "v8i16", element_type, 8);
+
+  element_type = tdesc_named_type (feature, "int32");
+  tdesc_create_vector (feature, "v4i32", element_type, 4);
+
+  element_type = tdesc_named_type (feature, "int64");
+  tdesc_create_vector (feature, "v2i64", element_type, 2);
+
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_union (feature, "vec128");
+  tdesc_type *field_type;
+  field_type = tdesc_named_type (feature, "v4f");
+  tdesc_add_field (type_with_fields, "v4_float", field_type);
+  field_type = tdesc_named_type (feature, "v2d");
+  tdesc_add_field (type_with_fields, "v2_double", field_type);
+  field_type = tdesc_named_type (feature, "v16i8");
+  tdesc_add_field (type_with_fields, "v16_int8", field_type);
+  field_type = tdesc_named_type (feature, "v8i16");
+  tdesc_add_field (type_with_fields, "v8_int16", field_type);
+  field_type = tdesc_named_type (feature, "v4i32");
+  tdesc_add_field (type_with_fields, "v4_int32", field_type);
+  field_type = tdesc_named_type (feature, "v2i64");
+  tdesc_add_field (type_with_fields, "v2_int64", field_type);
+  field_type = tdesc_named_type (feature, "uint128");
+  tdesc_add_field (type_with_fields, "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", 64, "uint64");
+  tdesc_create_reg (feature, "prefix", 102, 1, "system", 32, "uint32");
+
+  tdesc_s390x_vxcr_linux64 = result;
+}
diff --git a/gdb/features/s390x-vxcr-linux64.xml b/gdb/features/s390x-vxcr-linux64.xml
new file mode 100644
index 0000000000..703b1c360c
--- /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="64" type="uint64" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/regformats/s390x-cr-linux64.dat b/gdb/regformats/s390x-cr-linux64.dat
new file mode 100644
index 0000000000..091d55a816
--- /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
+64:todpreg
+32:prefix
diff --git a/gdb/regformats/s390x-vxcr-linux64.dat b/gdb/regformats/s390x-vxcr-linux64.dat
new file mode 100644
index 0000000000..4b3bf71adb
--- /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
+64:todpreg
+32:prefix
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 408bb875d2..07b02afe3d 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -1108,9 +1108,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 14530cef31..a565f72d9c 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -296,9 +296,29 @@ enum
 #define S390_BC_GSD_REGNUM 125
 #define S390_BC_GSSM_REGNUM 126
 #define S390_BC_GSEPLA_REGNUM 127
+/* Control registers.  */
+#define S390_TIMER_REGNUM 128
+#define S390_TODCMP_REGNUM 129
+#define S390_TODPREG_REGNUM 130
+#define S390_CR0_REGNUM 131
+#define S390_CR1_REGNUM 132
+#define S390_CR2_REGNUM 133
+#define S390_CR3_REGNUM 134
+#define S390_CR4_REGNUM 135
+#define S390_CR5_REGNUM 136
+#define S390_CR6_REGNUM 137
+#define S390_CR7_REGNUM 138
+#define S390_CR8_REGNUM 139
+#define S390_CR9_REGNUM 140
+#define S390_CR10_REGNUM 141
+#define S390_CR11_REGNUM 142
+#define S390_CR12_REGNUM 143
+#define S390_CR13_REGNUM 144
+#define S390_CR14_REGNUM 145
+#define S390_CR15_REGNUM 146
+#define S390_PREFIX_REGNUM 147
 /* Total.  */
-#define S390_NUM_REGS 128
-
+#define S390_NUM_REGS 148
 #define S390_NUM_GPRS 16
 #define S390_NUM_FPRS 16
 
-- 
2.13.5

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

* [RFC PATCH v5 8/9] Link frame_info to thread_info
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (7 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

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

This connection already exists implicitly, via inferior_ptid, due to the
fact that switch_to_thread reinitializes the frame cache.

gdb/ChangeLog:

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

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

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

* [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (6 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 1/9] Convert substitute_path_component to C++ Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-16  2:47   ` Simon Marchi
  2018-03-12 15:31 ` [RFC PATCH v5 8/9] Link frame_info to thread_info Philipp Rudo
  8 siblings, 1 reply; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, 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               | 16 ++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/gdb/common/common-utils.h b/gdb/common/common-utils.h
index 5408c35469..8e6eb05c2d 100644
--- a/gdb/common/common-utils.h
+++ b/gdb/common/common-utils.h
@@ -109,6 +109,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 d4f1398d14..4ce3909fca 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3072,6 +3072,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 (const std::initializer_list<const std::string> args,
+		    const 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 (const std::initializer_list<const std::string> args,
+	      const std::string &dir_separator)
+{
+  std::string ret;
+  ret.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 (ret.c_str (), dir_separator.c_str ()))
+	ret.erase (ret.length () - dir_separator.length (),
+		   dir_separator.length ());
+
+      else if (!ret.empty ()
+	       && !startswith (arg.c_str (), dir_separator.c_str ())
+	       && !endswith (ret.c_str (), dir_separator.c_str ())
+	       && ret != TARGET_SYSROOT_PREFIX)
+	ret += dir_separator;
+
+      ret += arg;
+    }
+
+  ret.shrink_to_fit ();
+  return ret;
+}
+
 #ifdef HAVE_WAITPID
 
 #ifdef SIGALRM
diff --git a/gdb/utils.h b/gdb/utils.h
index 7e6a39ee82..aec9c6194d 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -307,6 +307,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 (const std::initializer_list<const std::string> args,
+				 const std::string &dir_separator);
+
+#define concat_path(...) _concat_path ({__VA_ARGS__}, SLASH_STRING)
+
 std::string ldirname (const char *filename);
 
 extern int count_path_elements (const char *path);
-- 
2.13.5

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

* [RFC v5 0/9] Add support for Linux kernel debugging
@ 2018-03-12 15:31 Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
                   ` (8 more replies)
  0 siblings, 9 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

Hi everybody,

after a long time here is a sign of life from kernel awareness.

Originally i planed to bring the patches upstream one by one.  However i'm
currently stuck on an other project.  But I should find time soon to
continue here.

The main change between v4 and v5 is the C++ification of lk_low, which
required big parts of the file to be rewritten.  Along with that smaller
improvements and bug fixes where made.

Changes v4 -> v5:
	* Rebase to current master.

	* C++-ify lk-low, i.e. replace:
		- struct lk_private -> class linux_kernel_ops,
		- hooks -> virtual member functions in linux_kernel_ops,
		- macros -> member function of linux_kernel_ops,
		- htab_t -> std::unordered_map for cached symbols,
		- gdbarch_lk_init_private -> gdbarch_get_new_lk_ops,
		- lk_list & lk_bitmap -> iterator class,
		- const char * -> const std::string & (where possible),
		- whatever I forgot, lk-low was basically rewritten.

	* Add class s390_linux_kernel_ops inherited from linux_kernel_ops.

	* Add GDB_OSABI_LINUX_KERNEL (patch #4).  Architectures must use a
	sniffer to distinguish between user- and kernel-space as in ELF both
	have type Linux.

	* Introduce ENUM_FLAG lk_kconfig to track config options used in
	kernel and turn off features not supported by the kernel (e.g.
	module support).

	* Fix handling of fields defined in anonymous structs.

	* Fix call to target beneath->to_fetch_registers. Adjust that
	to_fetch_registers uses regcache->ptid() instead of inferior_ptid
	nowadays (bcc0c096d5b et al.).  Requires new patch #3.

	* Fix typo in size of (s390) topdpreg.

	* Fix bug in S390_LK_ROUNDUP and rename to LK_ALIGN.

	* Move s390 split up to separate patch set (already pushed) and
	adjust s390 code accordingly.

Thanks
Philipp

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

 gdb/Makefile.in                       |   12 +
 gdb/auto-load.c                       |   19 +-
 gdb/common/common-utils.h             |   11 +
 gdb/configure.tgt                     |    9 +-
 gdb/defs.h                            |    1 +
 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     |  170 ++++++
 gdb/features/s390x-vxcr-linux64.xml   |   25 +
 gdb/frame.c                           |   12 +
 gdb/frame.h                           |    2 +
 gdb/gdbarch.c                         |   32 ++
 gdb/gdbarch.h                         |    9 +
 gdb/gdbarch.sh                        |    4 +
 gdb/lk-bitmap.h                       |  226 ++++++++
 gdb/lk-cmds.c                         |  254 +++++++++
 gdb/lk-cmds.h                         |   25 +
 gdb/lk-list.h                         |  201 +++++++
 gdb/lk-low.c                          |  953 +++++++++++++++++++++++++++++++
 gdb/lk-low.h                          |  362 ++++++++++++
 gdb/lk-modules.c                      |  501 ++++++++++++++++
 gdb/lk-modules.h                      |  148 +++++
 gdb/osabi.c                           |    1 +
 gdb/regcache.h                        |   21 +
 gdb/regformats/s390x-cr-linux64.dat   |   76 +++
 gdb/regformats/s390x-vxcr-linux64.dat |  108 ++++
 gdb/s390-lk-tdep.c                    | 1015 +++++++++++++++++++++++++++++++++
 gdb/s390-lk-tdep.h                    |   39 ++
 gdb/s390-tdep.c                       |   44 +-
 gdb/s390-tdep.h                       |   90 ++-
 gdb/solib.c                           |    8 +
 gdb/solib.h                           |    5 +
 gdb/typeprint.c                       |    2 +-
 gdb/typeprint.h                       |    2 +
 gdb/utils.c                           |   85 +--
 gdb/utils.h                           |   26 +-
 38 files changed, 4599 insertions(+), 59 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-bitmap.h
 create mode 100644 gdb/lk-cmds.c
 create mode 100644 gdb/lk-cmds.h
 create mode 100644 gdb/lk-list.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

-- 
2.13.5

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

* [RFC PATCH v5 9/9] Add S390 support for linux-kernel target
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (3 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 6/9] Add commands for linux-kernel target Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 5/9] Add kernel module " Philipp Rudo
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

Add s390 architecture support for the new linux-kernel target. In
particular implement the required virtual methods, enable priviledged
registers and add a kernel stack unwinder.

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.
	(gdbarch_tdep) <have_privileged>: New field.
	(s390_unwind_pseudo_register): New export.
	* s390-tdep.c (s390_unwind_pseudo_register): Remove static.
	(s390_tdesc_valid): Add control registers and check for
	priviledeged feature in tdesc.
	(s390_gdbarch_tdep_alloc): Initialize new field have_priviledged.
---
 gdb/Makefile.in    |    3 +
 gdb/configure.tgt  |    6 +-
 gdb/s390-lk-tdep.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-lk-tdep.h |   39 ++
 gdb/s390-tdep.c    |   36 +-
 gdb/s390-tdep.h    |   66 ++++
 6 files changed, 1160 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 2029c933af..1970b7f734 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -760,6 +760,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 \
@@ -1342,6 +1343,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 \
@@ -2321,6 +2323,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 680b3b2e42..a9116b3b7b 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -519,9 +519,9 @@ powerpc*-*-*)
 
 s390*-*-linux*)
 	# Target: S390 running Linux
-	gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
-			linux-tdep.o linux-record.o symfile-mem.o \
-			${lk_tobjs}"
+	gdb_target_obs="s390-linux-tdep.o s390-tdep.o s390-lk-tdep.o \
+			solib-svr4.o linux-tdep.o linux-record.o \
+			symfile-mem.o ${lk_tobjs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c
new file mode 100644
index 0000000000..d1582f2474
--- /dev/null
+++ b/gdb/s390-lk-tdep.c
@@ -0,0 +1,1015 @@
+/* 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 "auxv.h"
+#include "dwarf2-frame.h"
+#include "elf/common.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "osabi.h"
+#include "regcache.h"
+#include "regset.h"
+#include "s390-tdep.h"
+#include "s390-lk-tdep.h"
+
+#include "features/s390x-cr-linux64.c"
+#include "features/s390x-vxcr-linux64.c"
+
+
+/* Register maps and sets.  */
+
+static const struct regcache_map_entry s390_gregmap[] =
+  {
+    { 1, S390_PSWM_REGNUM },
+    { 1, S390_PSWA_REGNUM },
+    { 16, S390_R0_REGNUM },
+    { 16, S390_A0_REGNUM },
+    { 1, REGCACHE_MAP_SKIP, 8 }, /* ORIG_R2_REG, exists but unused.  */
+    { 0 }
+  };
+
+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 s390_fpregmap[] =
+  {
+    { 1, S390_FPC_REGNUM, 8 },
+    { 16, S390_F0_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_vxrs_low[] =
+  {
+    { 16, S390_V0_LOWER_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_vxrs_high[] =
+  {
+    { 16, S390_V16_REGNUM, 16 },
+    { 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, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390x_regmap_prefix [] =
+  {
+    { 1, S390_PREFIX_REGNUM, 4 },
+    { 0 }
+  };
+
+/* Gregset stored in dump section.  Used for running tasks.  */
+
+#define s390x_sizeof_gregset 0xd8
+static const struct regset s390_gregset = {
+  s390_gregmap,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+/* Gregset set stored in stack_frame.  Used for sleeping tasks.  */
+
+static const struct regset s390x_gregset_lk = {
+  s390x_gregmap_lk,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+#define s390_sizeof_fpregset 0x88
+static const struct regset s390_fpregset = {
+  s390_fpregmap,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390_vxrs_low_regset = {
+  s390_regmap_vxrs_low,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390_vxrs_high_regset = {
+  s390_regmap_vxrs_high,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390x_cr_regset = {
+  s390x_regmap_cr,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390x_timer_regset = {
+  s390x_regmap_timer,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390x_todcmp_regset = {
+  s390x_regmap_todcmp,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390x_todpreg_regset = {
+  s390x_regmap_todpreg,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+static const struct regset s390x_prefix_regset = {
+  s390x_regmap_prefix,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+/* S390 specific implementation for linux_kernel_ops.  */
+
+class s390_linux_kernel_ops : public linux_kernel_ops
+{
+public:
+  s390_linux_kernel_ops (struct target_ops *target)
+    : linux_kernel_ops::linux_kernel_ops (target)
+  {}
+
+  /* See lk-low.h.  Uses r14 as current pswa.  */
+  void get_registers (CORE_ADDR task, struct target_ops *target,
+		      struct regcache *regcache, int regnum) override;
+
+  /* See lk-low.h.  */
+  bool is_kvaddr (CORE_ADDR addr) override
+  { return addr >= lk_address ("high_memory"); }
+
+  /* See lk-low.h.  */
+  CORE_ADDR vtop (CORE_ADDR vaddr, CORE_ADDR pgd) override;
+
+  /* See lk-low.h.  */
+  void adjust_module_layout (CORE_ADDR mod, lk_module *module) override;
+
+  /* See lk-low.h.  */
+  CORE_ADDR percpu_offset (unsigned int cpu) override;
+
+  /* See lk-low.h.  */
+  unsigned int beneath_thread_to_cpu (thread_info *ti) override;
+
+protected:
+  /* See lk-low.h.  */
+  void arch_read_symbols () override;
+};
+
+/* Returns address of lowcore of given cpu.  */
+
+static CORE_ADDR
+s390_lk_get_lowcore (unsigned int cpu)
+{
+  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR lowcore_ptr = lk_ops->address ("lowcore_ptr") + (ptr_len * cpu);
+  return lk_read_addr (lowcore_ptr);
+}
+
+/* Implement linux_kernel_ops->arch_read_symbols virtual method.  */
+
+void
+s390_linux_kernel_ops::arch_read_symbols ()
+{
+  declare_field ("stack_frame->gprs", LK_CONFIG_ALWAYS);
+  declare_field ("stack_frame->back_chain", LK_CONFIG_ALWAYS);
+
+  declare_field ("pt_regs->psw", LK_CONFIG_ALWAYS);
+  declare_field ("pt_regs->gprs", LK_CONFIG_ALWAYS);
+
+  declare_field ("task_struct->stack", LK_CONFIG_ALWAYS);
+
+  declare_field ("thread_struct->ksp", LK_CONFIG_ALWAYS);
+
+  declare_type ("lowcore", {"lowcore",   /* linux  4.5+ */
+			    "_lowcore"}, /* linux -4.4 */
+		LK_CONFIG_ALWAYS);
+  declare_field ("lowcore->percpu_offset", LK_CONFIG_ALWAYS);
+  declare_field ("lowcore->cpu_nr", LK_CONFIG_ALWAYS);
+  declare_field ("lowcore->async_stack", LK_CONFIG_ALWAYS);
+  declare_field ("lowcore->panic_stack", LK_CONFIG_ALWAYS);
+  declare_field ("lowcore->restart_stack", LK_CONFIG_ALWAYS);
+
+  declare_field ("psw_t->mask", LK_CONFIG_ALWAYS);
+  declare_field ("psw_t->addr", LK_CONFIG_ALWAYS);
+
+  declare_field ("mod_arch_specific->plt_offset", LK_CONFIG_ALWAYS);
+  declare_field ("mod_arch_specific->plt_size", LK_CONFIG_ALWAYS);
+
+  declare_address ("lowcore_ptr", LK_CONFIG_ALWAYS);
+  declare_address ("high_memory", LK_CONFIG_ALWAYS);
+}
+
+/* Implement linux_kernel_ops->get_registers virtual method.  */
+
+void
+s390_linux_kernel_ops::get_registers (CORE_ADDR task,
+				      struct target_ops *target,
+				      struct regcache *regcache,
+				      int regnum)
+{
+  CORE_ADDR ksp, gprs;
+  gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */
+  size_t size;
+
+  ksp = lk_read_addr (task + offset ("task_struct->thread->ksp"));
+  gprs = ksp + offset ("stack_frame->gprs");
+  size = FIELD_SIZE (field ("stack_frame->gprs"));
+  gdb_assert (size <= sizeof (buf));
+
+  read_memory (gprs, buf, size);
+  regcache->supply_regset (&s390x_gregset_lk, -1, buf, size);
+}
+
+/* Read table entry from TABLE at offset OFFSET.  Helper for
+   s390_linux_kernel_ops::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));
+}
+
+/* Implement linux_kernel_ops->vtop virtual method.  */
+
+CORE_ADDR
+s390_linux_kernel_ops::vtop (CORE_ADDR vaddr, CORE_ADDR table)
+{
+  size_t addr_size = lk_builtin_type_size (unsigned_long);
+  unsigned int table_type;
+  ULONGEST entry, offset;
+
+  /* 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)
+	return (entry & S390_LK_RTTE_RFAA) + (vaddr & ~S390_LK_RTTE_RFAA);
+
+      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)
+	return ((entry & S390_LK_STE_SFAA) + (vaddr & ~S390_LK_STE_SFAA));
+
+      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));
+
+  return (entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA);
+}
+
+/* Implement linux_kernel_ops->adjust_module_layout virtual method.  */
+
+void
+s390_linux_kernel_ops::adjust_module_layout (CORE_ADDR mod,
+					     lk_module *module)
+{
+  CORE_ADDR mod_arch = mod + offset ("module->arch");
+
+  module->core->text.base
+    += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_offset"));
+  module->core->text.base
+    += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_size"));
+}
+
+/* Implement linux_kernel_ops->percpu_offset virtual method.  */
+
+CORE_ADDR
+s390_linux_kernel_ops::percpu_offset (unsigned int cpu)
+{
+  CORE_ADDR lowcore = s390_lk_get_lowcore (cpu);
+  return lk_read_addr (lowcore + offset ("lowcore->percpu_offset"));
+}
+
+/* Implement linux_kernel_ops->beneath_thread_to_cpu virtual method.  */
+
+unsigned int
+s390_linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti)
+{
+  CORE_ADDR lowcore;
+
+  regcache *regcache = get_thread_regcache (ti->ptid);
+  register_status reg_status = regcache->raw_read (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 ());
+
+  return lk_read_uint (lowcore + offset ("lowcore->cpu_nr"));
+}
+
+/* Linux kernel stack unwinder.  */
+
+/* Helper functions and definitions.  */
+
+struct s390_lk_unwind_cache
+{
+  /* The frame this frame_cache belongs to and its ... */
+  struct frame_info *frame;
+
+  /* ... frame base, ... */
+  CORE_ADDR frame_base;
+  /* ... stack pointer, ... */
+  CORE_ADDR sp;
+  /* ... process counter, ... */
+  CORE_ADDR pc;
+  /* ... PSW mask, ... */
+  CORE_ADDR pswm;
+  /* ... PSW address and ... */
+  CORE_ADDR pswa;
+  /* ... saved general purpose registers.  */
+  CORE_ADDR gprs[S390_NUM_GPRS];
+
+  /* Mask to store which gprs where saved.  */
+  unsigned long gprs_p:S390_NUM_GPRS;
+  /* Does this frame contain a zero backchain?  */
+  bool zero_backchain;
+};
+
+/* S390s kernel stack is split up on several memory locations:
+
+   - kernel stack (per task)
+   - async aka. interrupt stack (per cpu)
+   - panic stack (per cpu)
+   - restart stack (unique, global)
+
+   Each memory location is page aligned and has a size of four consecutive
+   pages (exception is the panic stack with size of one page).  */
+
+enum s390_lk_stack_location
+{
+  S390_LK_STACK_INVALID = -1,
+  S390_LK_STACK_USER,
+  S390_LK_STACK_KERNEL,
+  S390_LK_STACK_ASYNC,
+  S390_LK_STACK_PANIC,
+  S390_LK_STACK_RESTART
+};
+
+/* Get value of gpr REGNUM stored in CACHE.  */
+
+static inline struct value *
+s390_lk_unwind_get_gpr (struct s390_lk_unwind_cache *cache, int regnum)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  if (!(cache->gprs_p & (1 << gpr)))
+    return frame_unwind_got_optimized (cache->frame, regnum);
+
+  /* When we hit a zero_backchain R15 is the (computed) lowest address of
+     pt_regs.  */
+  if (regnum == S390_R15_REGNUM && cache->zero_backchain)
+    return frame_unwind_got_address (cache->frame, regnum, cache->gprs[gpr]);
+
+  return frame_unwind_got_memory (cache->frame, regnum, cache->gprs[gpr]);
+}
+
+/* Store ADDR of gpr REGNUM in CACHE.  */
+
+static inline void
+s390_lk_unwind_set_gpr (struct s390_lk_unwind_cache *cache, int regnum,
+			CORE_ADDR addr)
+{
+  gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+  unsigned int gpr = regnum - S390_R0_REGNUM;
+  cache->gprs[gpr] = addr;
+  cache->gprs_p |= (1 << gpr);
+}
+
+/* Allocate and initialize a new s390_lk_unwind_cache.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_alloc_unwind_cache ()
+{
+  struct s390_lk_unwind_cache *cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct s390_lk_unwind_cache);
+
+  cache->frame = NULL;
+  cache->frame_base = -1;
+  cache->zero_backchain = false;
+
+  cache->sp = -1;
+  cache->pc = -1;
+  cache->pswm = -1;
+  cache->pswa = -1;
+  for (CORE_ADDR *gpr = cache->gprs; gpr < cache->gprs + S390_NUM_GPRS; gpr++)
+    *gpr = -1;
+  cache->gprs_p = 0;
+
+  return cache;
+}
+
+/* Helper macro for s390_lk_get_stack_location to check for stacks which
+   locations are stored in the lowcore.
+
+      _addr	address to be checked for
+      _lc	address of the corresponding lowcore
+      _stack	field name of stack in lowcore
+      _type	type to be returned if _addr is on _stack
+      _size	size of _stack
+*/
+
+#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size)	\
+  do									\
+    {									\
+      CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom;		\
+      _##_stack = lk_read_addr ((_lc) + lk_offset ("lowcore->"#_stack));\
+      _##_stack##_top = LK_ALIGN (_##_stack, S390_LK_PAGESIZE);	\
+      _##_stack##_bottom = _##_stack##_top - (_size);			\
+      if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom)	\
+	return (_type);							\
+    }									\
+  while (0)
+
+/* Find and return the stack location of ADDR belonging to TASK.  */
+
+static enum s390_lk_stack_location
+s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
+{
+  CORE_ADDR lowcore, top, bottom;
+  unsigned int cpu = lk_task_running (task);
+
+  /* Kernel stack.  */
+  bottom = lk_read_addr (task + lk_offset ("task_struct->stack"));
+  top = bottom + S390_LK_STACKSIZE;
+  if (addr <= top && addr >= bottom)
+    return S390_LK_STACK_KERNEL;
+
+  /* A sleeping task only has the kernel stack.  If a sleeping task reaches
+     this point ADDR isn't on the stack.  */
+  if (cpu == LK_CPU_INVAL)
+    return S390_LK_STACK_INVALID;
+
+  lowcore = s390_lk_get_lowcore (cpu);
+
+  /* Async aka. interrupt stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
+			       S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
+
+  /* Panic stack.
+     Note: The panic stack only has a size of one page.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
+			       S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
+
+  /* Restart stack.  */
+  s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
+			       S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
+
+  return S390_LK_STACK_INVALID;
+}
+
+/* Unwind a single NORMAL_FRAME, i.e. a struct stack_frame.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_frame_unwind_cache (struct frame_info *this_frame,
+			    void **this_cache)
+{
+  CORE_ADDR backchain, backchain_addr, prev_gpr15;
+
+  if (*this_cache)
+    return (struct s390_lk_unwind_cache *) *this_cache;
+
+  s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For NORMAL_FRAMES, cache->sp == lowest address of struct stack_frame.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = get_frame_pc (this_frame);
+  /* Choose the frame's base to be gpr15 + sizeof (stack_frame) to be
+     complient with DWARF's DW_CFA_def_cfa.  Otherwise gdbarch_inner_than
+     can trigger false stack corruption asserts, as
+
+     next_dwarf_frame_base = next_backchain_frame_base + sizeof (stack_frame)
+			   > this_backchain_frame_base
+  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("stack_frame"));
+
+  backchain_addr = cache->sp + lk_offset ("stack_frame->back_chain");
+  backchain = lk_read_addr (backchain_addr);
+
+  /* A zero backchain marks the end of this stack.  This can lead to two
+     actions (handled by SIGTRAMP_FRAME unwinder):
+       * stop unwinding (if sp at the end of the kernel stack)
+       * jump to next stack location (otherwise).
+  */
+  if (backchain == 0)
+    {
+      CORE_ADDR pt_regs = cache->sp + TYPE_LENGTH (lk_type ("stack_frame"));
+
+      {
+	/* A bug in the kernel (linux 3.10+) adds the stack overhead twice
+	   for swapper tasks (!= swapper/0) with the information written in
+	   the first (higher address) pt_regs.  Compensate this by updating
+	   the stack pointer.  */
+	enum s390_lk_stack_location stack;
+
+	CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+	long pid = frame_get_thread (this_frame)->ptid.lwp ();
+	stack = s390_lk_get_stack_location (task, cache->sp);
+	if (pid == 0 && task != lk_address ("init_task")
+	    && stack == S390_LK_STACK_KERNEL)
+	  pt_regs += (TYPE_LENGTH (lk_type ("stack_frame"))
+		      + TYPE_LENGTH (lk_type ("pt_regs")));
+      }
+
+      cache->zero_backchain = true;
+      s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, pt_regs);
+
+      /* Use pc = 0 to mark end of stack.  */
+      s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, backchain_addr);
+      return cache;
+    }
+
+  /* Do sanity check.  */
+  prev_gpr15 = lk_read_addr (backchain
+			     + s390_lk_stack_frame_gpr (S390_R15_REGNUM));
+  if (backchain != prev_gpr15)
+    {
+      size_t ptr_len = lk_builtin_type_size (unsigned_long);
+      CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+      error (_("Stack corruption for task 0x%s at stack frame 0x%s with "
+	       "back_chain 0x%s detected %s.  back_chain->gpr15 != back_chain."),
+	     phex (task, ptr_len), phex (cache->sp, ptr_len),
+	     phex (backchain, ptr_len), phex (prev_gpr15, ptr_len));
+    }
+
+  s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, backchain_addr);
+  s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM,
+			  backchain + s390_lk_stack_frame_gpr (S390_R14_REGNUM));
+
+  return cache;
+}
+
+/* Implement frame_unwind->this_id for NORMAL_FRAMEs.  */
+
+static void
+s390_lk_frame_this_id (struct frame_info *this_frame,
+		       void **this_cache,
+		       struct frame_id *this_id)
+{
+  s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* If we don't have a stack frame...  */
+  if (cache->sp == -1)
+    {
+      /* ...but a PC.  We have a inline frame.  */
+      if (cache->pc != -1)
+	*this_id = frame_id_build_unavailable_stack (cache->pc);
+      return;
+    }
+  *this_id = frame_id_build (cache->frame_base, cache->pc);
+}
+
+/* Implement frame_unwind->prev_register for NORMAL and SIGTRAMP_FRAMEs.  */
+
+static struct value *
+s390_lk_frame_prev_register (struct frame_info *this_frame,
+			     void **this_cache, int regnum)
+{
+  s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+  /* Also handles S390_SP_REGNUM and S390_RETADDR_REGNUM as they are mapped to
+     R15 and R14 respectfully in s390-tdep.h.  */
+  if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
+    return s390_lk_unwind_get_gpr (cache, regnum);
+
+  /* Use PSWM stored in pt_regs if available.  */
+  if (regnum == S390_PSWM_REGNUM)
+    return (cache->pswm != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswm)
+	    : frame_unwind_got_optimized (cache->frame, regnum));
+
+  /* Use PSWA stored in pt_regs if available, R14/PC otherwise.  */
+  if (regnum == S390_PSWA_REGNUM)
+    return (cache->pswa != -1
+	    ? frame_unwind_got_memory (cache->frame, regnum, cache->pswa)
+	    : s390_lk_unwind_get_gpr (cache, S390_RETADDR_REGNUM));
+
+  if (regnum >= S390_NUM_REGS)
+    return s390_unwind_pseudo_register (cache->frame, regnum);
+
+  /* Default is not saved.  */
+  return frame_unwind_got_optimized (cache->frame, regnum);
+}
+
+/* Implement frame_unwind->sniffer for NORMAL_FRAMEs.  */
+
+static int
+s390_lk_frame_sniffer (const struct frame_unwind *self,
+		       struct frame_info *this_frame,
+		       void **this_cache)
+{
+  if (!target_is_pushed (lk_target_ops))
+    return 0;
+
+  return 1;
+}
+
+/* Simple backchain unwinder for Linux kernel on S390.  Its main purpose is
+   to handle stack frames created in Assembler code without debug
+   infromation.  */
+
+static const struct frame_unwind s390_lk_frame_unwind = {
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_frame_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  s390_lk_frame_sniffer
+};
+
+/* Unwind a single SIGTRAMP_FRAME, i.e. a struct pt_regs.  */
+
+static struct s390_lk_unwind_cache *
+s390_lk_sigtramp_unwind_cache (struct frame_info *this_frame,
+			       void **this_cache)
+{
+  if (*this_cache)
+    return (s390_lk_unwind_cache *) *this_cache;
+
+  s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache ();
+  *this_cache = cache;
+
+  cache->frame = this_frame;
+  /* For SIGTRAMP_FRAMES, cache->sp = lowest address of struct pt_regs.  */
+  cache->sp = get_frame_sp (this_frame);
+  cache->pc = cache->sp + s390_lk_pt_regs_gpr (S390_R14_REGNUM);
+  /* Choose the frame base to be SP + sizeof (pt_regs) aka. the end of this
+     stack.  */
+  cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("pt_regs"));
+
+  cache->pswm = cache->sp + lk_offset ("pt_regs->psw->mask");
+  cache->pswa = cache->sp + lk_offset ("pt_regs->psw->addr");
+
+  for (int i = S390_R0_REGNUM; i <= S390_R15_REGNUM; i++)
+    s390_lk_unwind_set_gpr (cache, i, cache->sp + s390_lk_pt_regs_gpr (i));
+
+  return cache;
+}
+
+/* Implement frame_unwind->this_id for SIGTRAMP_FRAMEs.  */
+
+static void
+s390_lk_sigtramp_this_id (struct frame_info *this_frame,
+			  void **this_cache,
+			  struct frame_id *this_id)
+{
+  s390_lk_unwind_cache *cache
+    = s390_lk_sigtramp_unwind_cache (this_frame, this_cache);
+
+  CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+
+  switch (s390_lk_get_stack_location (task, cache->sp))
+    {
+      /* If we are on an interrupt stack keep on unwinding.  */
+      case S390_LK_STACK_ASYNC:
+      case S390_LK_STACK_PANIC:
+      case S390_LK_STACK_RESTART:
+	*this_id = frame_id_build (cache->frame_base, cache->pc);
+	return;
+
+      /* If we are on the kernel stack check whether we are at the end of
+	 the stack.  If so stop unwinding otherwise continue.  */
+      case S390_LK_STACK_KERNEL:
+	{
+	  CORE_ADDR top;
+
+	  top = lk_read_addr (task + lk_offset ("task_struct->stack"));
+	  top += S390_LK_STACKSIZE;
+
+	  /* swapper/0 doesn't have a pt_regs at the top of its stack, so in
+	     this case cache->sp pointing at the top of the last stack_frame
+	     points at the end of the stack.  */
+	  if (task == lk_address ("init_task") && top <= cache->sp)
+	    *this_id = outer_frame_id;
+
+	  /* For all other tasks cache->frame_base pointing at the top of
+	     pt_regs points at the end of stack.  */
+	  else if (top <= cache->frame_base)
+	    *this_id = outer_frame_id;
+
+	  /* The program check handler appends his stack to the kernel stack.
+	     If that is the case simply keep on unwinding.  */
+	  else
+	    *this_id = frame_id_build (cache->frame_base, cache->pc);
+
+	  return;
+	}
+
+      /* Otherwise somewhere something went wrong.  */
+      default:
+	{
+	  size_t ptr_len = lk_builtin_type_size (unsigned_long);
+	  error (_("Couldn't find find valid stack for task 0x%s with stack "
+		   "pointer 0x%s.  Stack corruption?"),
+		   phex (task, ptr_len), phex (cache->sp, ptr_len));
+	}
+    }
+}
+
+/* Implement frame_unwind->sniffer for SIGTRAMP_FRAMEs.  */
+
+static int
+s390_lk_sigtramp_sniffer (const struct frame_unwind *self,
+			  struct frame_info *this_frame,
+			  void **this_cache)
+{
+  if (!target_is_pushed (lk_target_ops))
+    return 0;
+
+  return get_frame_pc (this_frame) == 0;
+}
+
+/* Sigtramp unwinder for Linux kernel on S390.  It handles situations when
+   the end of a stack, i.e. a zero backchain, was detected.  */
+
+static const struct frame_unwind s390_lk_sigtramp_unwind = {
+  SIGTRAMP_FRAME,
+  default_frame_unwind_stop_reason,
+  s390_lk_sigtramp_this_id,
+  s390_lk_frame_prev_register,
+  NULL,
+  s390_lk_sigtramp_sniffer
+};
+
+/* Frame base handling.  */
+
+static CORE_ADDR
+s390_lk_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct s390_lk_unwind_cache *cache
+    = s390_lk_frame_unwind_cache (this_frame, this_cache);
+  return cache->frame_base;
+}
+
+static const struct frame_base s390_lk_frame_base = {
+  &s390_lk_frame_unwind,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address,
+  s390_lk_frame_base_address
+};
+
+/* Implement gdbarch get_new_lk_ops hook.  */
+
+static linux_kernel_ops *
+s390_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target)
+{
+  return new s390_linux_kernel_ops (target);
+}
+
+/* Implement gdbarch iterate_over_regset_sections hook.  */
+
+static void
+s390_lk_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);
+
+  cb (".reg", s390x_sizeof_gregset, &s390_gregset, NULL, cb_data);
+  cb (".reg2", s390_sizeof_fpregset, &s390_fpregset, NULL, cb_data);
+  cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset, "s390 control registers",
+      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);
+    }
+
+  /* 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", 8, &s390x_todpreg_regset,
+	"s390 TOD programable register", 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);
+}
+
+/* Implement gdbarch core_read_description hook.  */
+
+static const struct target_desc *
+s390_lk_core_read_description (struct gdbarch *gdbarch,
+			       struct target_ops *target, bfd *abfd)
+{
+  CORE_ADDR hwcap = 0;
+
+  target_auxv_search (target, AT_HWCAP, &hwcap);
+
+  if (hwcap & HWCAP_S390_VX)
+    return tdesc_s390x_vxcr_linux64;
+  else
+    return tdesc_s390x_cr_linux64;
+}
+
+/* Initialize Linux kernel specific gdbarch hooks.  */
+
+static void
+s390_lk_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  const struct target_desc *tdesc = info.target_desc;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  tdep->abi = ABI_LINUX_ZSERIES;
+  if (!tdesc_has_registers (tdesc))
+    tdesc = tdesc_s390x_cr_linux64;
+  tdep->tdesc = tdesc;
+
+  /* Register handling.  */
+  set_gdbarch_core_read_description (gdbarch, s390_lk_core_read_description);
+  set_gdbarch_iterate_over_regset_sections (gdbarch,
+					    s390_lk_iterate_over_regset_sections);
+  /* Frame handling.  */
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_sigtramp_unwind);
+  frame_unwind_append_unwinder (gdbarch, &s390_lk_frame_unwind);
+
+  /* Support linux kernel debugging.  */
+  set_gdbarch_get_new_lk_ops (gdbarch, s390_get_new_lk_ops);
+}
+
+/* From the ELF-headers point of view Linux kernel- and user-space binaries
+   are the same.  So we have to rely on some heuristics to distinguish them.
+
+   For the heuristic we check for:
+       * the prefix register section in dumps.
+       * the __ksymtab section in the vmlinux and modules .ko files.
+ */
+
+static enum gdb_osabi
+s390_lk_osabi_sniffer (bfd *abfd)
+{
+  if (bfd_get_section_by_name (abfd, ".reg-s390-prefix")
+      || bfd_get_section_by_name (abfd, "__ksymtab"))
+    return GDB_OSABI_LINUX_KERNEL;
+
+  return GDB_OSABI_UNKNOWN;
+}
+
+extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_s390_lk_tdep (void)
+{
+  /* Hook us into the OSABI mechanism.  */
+  gdbarch_register_osabi (bfd_arch_s390, bfd_mach_s390_64, GDB_OSABI_LINUX_KERNEL,
+			  s390_lk_init_abi_64);
+  /* Add OSABI sniffer.  */
+  gdbarch_register_osabi_sniffer (bfd_arch_s390, bfd_target_elf_flavour,
+				  s390_lk_osabi_sniffer);
+
+  /* Initialize the Linux kernel target descriptions.  */
+  initialize_tdesc_s390x_cr_linux64 ();
+  initialize_tdesc_s390x_vxcr_linux64 ();
+}
diff --git a/gdb/s390-lk-tdep.h b/gdb/s390-lk-tdep.h
new file mode 100644
index 0000000000..e972d50167
--- /dev/null
+++ b/gdb/s390-lk-tdep.h
@@ -0,0 +1,39 @@
+/* Target-dependent code for linux-kernel target on S390.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef S390_LK_TDEP_H
+#define S390_LK_TDEP_H
+
+/* Constants copied from kernel sources.  */
+#define S390_LK_PAGESIZE (1UL << 12)
+#define S390_LK_STACKSIZE (S390_LK_PAGESIZE << 2)
+#define S390_LK_ASYNCSIZE (S390_LK_STACKSIZE)
+
+/* Short hand access to gprs stored in struct pt_regs.  */
+#define s390_lk_pt_regs_gpr(regnum)					\
+  (lk_offset ("pt_regs->gprs")					\
+   + (FIELD_TARGET_SIZE (lk_field ("pt_regs->gprs"))			\
+      * ((regnum) - S390_R0_REGNUM)))					\
+
+/* Short hand access to gprs stored in struct stack_frame.  */
+#define s390_lk_stack_frame_gpr(regnum)					\
+  (lk_offset ("stack_frame->gprs")					\
+   + (FIELD_TARGET_SIZE (lk_field ("stack_frame->gprs"))		\
+      * ((regnum) - S390_R6_REGNUM)))					\
+
+#endif /* S390_LK_TDEP_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 07b02afe3d..fc27a26702 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -2154,7 +2154,7 @@ s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
 
 /* Helper routine to unwind pseudo registers.  */
 
-static struct value *
+struct value *
 s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum)
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
@@ -2280,7 +2280,6 @@ s390_trad_frame_prev_register (struct frame_info *this_frame,
   else
     return s390_unwind_pseudo_register (this_frame, regnum);
 }
-
 /* Normal stack frames.  */
 
 struct s390_unwind_cache {
@@ -6790,6 +6789,10 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep,
   static const char *const gs_bc[] = {
     "bc_gsd", "bc_gssm", "bc_gsepla",
   };
+  static const char *const cr[] = {
+    "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+    "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15"
+  };
 
   const struct target_desc *tdesc = tdep->tdesc;
   const struct tdesc_feature *feature;
@@ -6891,6 +6894,34 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep,
 			       gs_bc);
     }
 
+  /* Control registers.  */
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr");
+  if (feature)
+    s390_validate_reg_range (feature, tdesc_data, S390_CR0_REGNUM, cr);
+
+  /* Privileged registers.  */
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged");
+  if (feature)
+    {
+      if (!tdesc_numbered_register (feature, tdesc_data,
+				    S390_TIMER_REGNUM, "timer"))
+	return false;
+
+      if (!tdesc_numbered_register (feature, tdesc_data,
+				    S390_TODCMP_REGNUM, "todcmp"))
+	return false;
+
+      if (!tdesc_numbered_register (feature, tdesc_data,
+				    S390_TODPREG_REGNUM, "todpreg"))
+	return false;
+
+      if (!tdesc_numbered_register (feature, tdesc_data,
+				    S390_PREFIX_REGNUM, "prefix"))
+	return false;
+
+      tdep->have_privileged = true;
+    }
+
   return true;
 }
 
@@ -6918,6 +6949,7 @@ s390_gdbarch_tdep_alloc ()
   tdep->have_tdb = false;
   tdep->have_vx = false;
   tdep->have_gs = false;
+  tdep->have_privileged = false;
 
   tdep->s390_syscall_record = NULL;
 
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
index a565f72d9c..63f2481010 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -60,6 +60,7 @@ struct gdbarch_tdep
   bool have_tdb;
   bool have_vx;
   bool have_gs;
+  bool have_privileged;
 
   /* Hook to record OS specific systemcall.  */
   int (*s390_syscall_record) (struct regcache *regcache, LONGEST svc_number);
@@ -329,12 +330,77 @@ enum
 #define S390_RETADDR_REGNUM S390_R14_REGNUM
 #define S390_FRAME_REGNUM S390_R11_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
+
 /* Frame unwinding.  */
 
 extern struct value *s390_trad_frame_prev_register
     (struct frame_info *this_frame, struct trad_frame_saved_reg saved_regs[],
      int regnum);
 
+extern struct value *s390_unwind_pseudo_register (struct frame_info *this_frame,
+						  int regnum);
+
 extern struct target_desc *tdesc_s390_linux32;
 extern struct target_desc *tdesc_s390x_linux64;
 
-- 
2.13.5

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

* [RFC PATCH v5 1/9] Convert substitute_path_component to C++
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (5 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 5/9] Add kernel module " Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-16  2:15   ` Simon Marchi
  2018-03-12 15:31 ` [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 8/9] Link frame_info to thread_info Philipp Rudo
  8 siblings, 1 reply; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, 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 | 19 ++++++++-----------
 gdb/utils.c     | 47 +++++++++++------------------------------------
 gdb/utils.h     | 10 ++++++++--
 3 files changed, 27 insertions(+), 49 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 70bddbc862..a7f9635252 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -175,21 +175,18 @@ std::vector<gdb::unique_xmalloc_ptr<char>> auto_load_safe_path_vec;
    substitute_path_component.  */
 
 static std::vector<gdb::unique_xmalloc_ptr<char>>
-auto_load_expand_dir_vars (const char *string)
+auto_load_expand_dir_vars (const std::string &string)
 {
-  char *s = xstrdup (string);
-  substitute_path_component (&s, "$datadir", gdb_datadir);
-  substitute_path_component (&s, "$debugdir", debug_file_directory);
+  std::string s (string);
+  substitute_path_component (s, "$datadir", gdb_datadir);
+  substitute_path_component (s, "$debugdir", debug_file_directory);
 
-  if (debug_auto_load && strcmp (s, string) != 0)
+  if (debug_auto_load && s.compare (string) != 0)
     fprintf_unfiltered (gdb_stdlog,
-			_("auto-load: Expanded $-variables to \"%s\".\n"), s);
+			_("auto-load: Expanded $-variables to \"%s\".\n"),
+			s.c_str ());
 
-  std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
-    = dirnames_to_char_ptr_vec (s);
-  xfree(s);
-
-  return dir_vec;
+  return dirnames_to_char_ptr_vec (s.c_str ());
 }
 
 /* Update auto_load_safe_path_vec from current AUTO_LOAD_SAFE_PATH.  */
diff --git a/gdb/utils.c b/gdb/utils.c
index b99d444a6e..d4f1398d14 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3052,49 +3052,24 @@ make_bpstat_clear_actions_cleanup (void)
   return make_cleanup (do_bpstat_clear_actions_cleanup, NULL);
 }
 
-/* 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 + to.length ()))
     {
-      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 = pos == 0 ? str[0] : str[pos - 1];
+      char 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 8ca3eb0369..7e6a39ee82 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -298,8 +298,14 @@ extern struct cleanup *make_bpstat_clear_actions_cleanup (void);
 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.  STR
+   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 STR).  */
+
+extern void substitute_path_component (std::string &str,
+				       const std::string &from,
+				       const std::string &to);
 
 std::string ldirname (const char *filename);
 
-- 
2.13.5

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

* [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-17 18:08   ` Simon Marchi
  2018-03-12 15:31 ` [RFC PATCH v5 7/9] Add privileged registers for s390x Philipp Rudo
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

When a target and its target beneath use different ptids to identify a
thread the regcaches ptid has to be set/restored when calls are passed down
to the target beneath to e.g. fetch_registers.  Add a scoped_restore to
simplify this.

gdb/ChangeLog:

	regcache.h (scoped_restore_regcache_ptid): New class.
---
 gdb/regcache.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/gdb/regcache.h b/gdb/regcache.h
index d7bb8b5c93..8e45d7c513 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -403,6 +403,27 @@ private:
   registers_changed_ptid (ptid_t ptid);
 };
 
+/* Save/restore the current ptid of REGCACHE.  */
+
+class scoped_restore_regcache_ptid
+{
+public:
+  scoped_restore_regcache_ptid (regcache *regcache)
+    : m_regcache (regcache), m_ptid (regcache->ptid ())
+  {}
+
+  ~scoped_restore_regcache_ptid ()
+    {
+      m_regcache->set_ptid (m_ptid);
+    }
+
+  DISABLE_COPY_AND_ASSIGN (scoped_restore_regcache_ptid);
+
+private:
+  regcache *m_regcache;
+  ptid_t m_ptid;
+};
+
 class readonly_detached_regcache : public readable_regcache
 {
 public:
-- 
2.13.5

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

* [RFC PATCH v5 6/9] Add commands for linux-kernel target
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
                   ` (2 preceding siblings ...)
  2018-03-12 15:31 ` [RFC PATCH v5 7/9] Add privileged registers for s390x Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-12 15:31 ` [RFC PATCH v5 9/9] Add S390 support " Philipp Rudo
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, 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   |   3 +
 gdb/configure.tgt |   2 +-
 gdb/lk-cmds.c     | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-cmds.h     |  25 ++++++
 gdb/lk-low.c      |   3 +
 gdb/typeprint.c   |   2 +-
 gdb/typeprint.h   |   2 +
 7 files changed, 289 insertions(+), 2 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 afa8039a95..2029c933af 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -715,6 +715,7 @@ ALL_TARGET_OBS = \
 	iq2000-tdep.o \
 	linux-record.o \
 	linux-tdep.o \
+	lk-cmds.o \
 	lk-low.o \
 	lk-modules.o \
 	lm32-tdep.o \
@@ -1278,6 +1279,7 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-cmds.h \
 	lk-low.h \
 	lk-modules.h \
 	location.h \
@@ -2260,6 +2262,7 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.c \
+	lk-cmds.c \
 	lk-low.c \
 	lk-modules.c \
 	lm32-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 39b2144e00..680b3b2e42 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -42,7 +42,7 @@ amd64_tobjs="amd64-tdep.o arch/amd64.o"
 
 # List of objectfiles for Linux kernel support.  To be included into *-linux*
 # targets wich support Linux kernel debugging.
-lk_tobjs="lk-low.o lk-modules.o"
+lk_tobjs="lk-cmds.o lk-low.o lk-modules.o"
 
 # Here are three sections to get a list of target specific object
 # files according to target triplet $TARG.
diff --git a/gdb/lk-cmds.c b/gdb/lk-cmds.c
new file mode 100644
index 0000000000..4046077e2b
--- /dev/null
+++ b/gdb/lk-cmds.c
@@ -0,0 +1,254 @@
+/* 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 "gdbtypes.h"
+#include "inferior.h"
+#include "lk-list.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "typeprint.h"
+#include "valprint.h"
+
+
+static bool
+lk_lsmod_print_name (struct ui_out *uiout, CORE_ADDR mod)
+{
+  char name[LK_MODULE_NAME_LEN + 2];
+  bool loaded;
+
+  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';
+  uiout->field_string ("module", name);
+
+  return loaded;
+}
+
+static void
+lk_lsmod_print_size (struct ui_out *uiout, CORE_ADDR mod)
+{
+  size_t size;
+
+  if (lk_ops->has_field ("module->module_core"))
+    {
+      size = lk_read_uint (mod + lk_offset ("module->init_size"));
+      size += lk_read_uint (mod + lk_offset ("module->core_size"));
+    }
+  else
+    {
+      size = lk_read_uint (mod + lk_offset ("module->init_layout->size"));
+      size += lk_read_uint (mod + lk_offset ("module->core_layout->size"));
+    }
+
+  uiout->field_int ("size", size);
+}
+
+
+static void
+lk_lsmod_print_sources (struct ui_out *uiout, CORE_ADDR mod)
+{
+  CORE_ADDR src_list;
+  int num_sources = 0;
+  std::string sources;
+
+  src_list = mod + lk_offset ("module->source_list");
+  for (CORE_ADDR src : lk_list (src_list, "module_use->source_list", false))
+    {
+      char buf[LK_MODULE_NAME_LEN + 1];
+
+      if (!sources.empty ())
+	sources += ", ";
+
+      src = lk_read_addr (src + lk_offset ("module_use->source"));
+      read_memory_string (src + lk_offset ("module->name"),
+			  buf, LK_MODULE_NAME_LEN);
+
+      sources += buf;
+    }
+
+  uiout->field_int ("src_num", num_sources);
+  uiout->field_string ("src_list", sources.c_str ());
+}
+
+/* Print line for module MOD to UIOUT for lsmod command.  */
+
+static bool
+lk_lsmod_print_single_module (struct ui_out *uiout, CORE_ADDR mod)
+{
+  bool loaded;
+
+  ui_out_emit_tuple tuple_emitter (uiout, NULL);
+
+  uiout->field_fmt ("addr", "0x%s",
+		    phex(mod, lk_builtin_type_size (unsigned_long)));
+  loaded = lk_lsmod_print_name (uiout, mod);
+  lk_lsmod_print_size (uiout, mod);
+  lk_lsmod_print_sources (uiout, mod);
+  uiout->text ("\n");
+
+  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 '*'.  */
+
+static void
+lk_lsmod (const char *args, int from_tty)
+{
+  if (!lk_ifdef (LK_CONFIG_MODULES))
+    {
+      printf_unfiltered (_("Kernel was compiled without module support.\n"));
+      return;
+    }
+
+  struct ui_out *uiout = current_uiout;
+  ui_out_emit_table table_emitter (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 ();
+
+  bool all_loaded = true;
+  for (CORE_ADDR mod : lk_list ("modules", "module->list"))
+      all_loaded &= lk_lsmod_print_single_module (uiout, mod);
+
+  if (!all_loaded)
+    uiout->text ("(*) Missing debug info for module.\n");
+}
+
+static void
+lk_print_struct (const 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);
+}
+
+static void
+lk_print_offset (const char *args_, int from_tty)
+{
+  std::string args (args_);
+  std::string type, field;
+  size_t pos;
+
+  if ((pos = args.find ('.')) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 1);
+    }
+  else if ((pos = args.find ("->")) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 2);
+    }
+  else
+    return;
+
+  expression_up expr = parse_expression (type.c_str ());
+  struct type *tp = value_type (evaluate_type (expr.get ()));
+
+  struct field *first = TYPE_FIELDS (tp);
+  struct field *last = first + TYPE_NFIELDS (tp);
+
+  for (; first != last; first++)
+    if (field.compare (first->name) == 0)
+      break;
+
+  if (first == last)
+    return;
+
+  size_t offset = FIELD_BITPOS (*first);
+
+  if (offset % TARGET_CHAR_BIT)
+    printf_unfiltered ("offset = %lu + %lu\n", offset / 8, offset % TARGET_CHAR_BIT);
+  else
+    printf_unfiltered ("offset = %lu\n", offset / 8);
+}
+
+
+void
+lk_init_cmds ()
+{
+  add_com ("lsmod", class_vars, lk_lsmod, "\n\
+	lsmod\n\n\
+List kernel modules as known by the kernel.  The address belongs to the \
+corresponding struct module.  \
+");
+
+  add_com ("struct", class_vars, lk_print_struct, "\n\
+	struct <struct>.<field> <address>\n\n\
+Print content of field <field> in structure <struct> for structure located\n\
+at address <address>.  If no field is given prints entire content of\n\
+<struct>.  If neither <field> nor <address> is given prints type definition\n\
+of <struct> (equivalent to ptype).\
+");
+
+  add_com ("offset", class_vars, lk_print_offset, "\n\
+	offset <struct>.<field>\n\n\
+Print offset of field <field> in structure <struct> in byte (+ bit for bit fields).\n\
+");
+}
diff --git a/gdb/lk-cmds.h b/gdb/lk-cmds.h
new file mode 100644
index 0000000000..63e424636d
--- /dev/null
+++ b/gdb/lk-cmds.h
@@ -0,0 +1,25 @@
+/* Commands for Linux kernel target.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_CMDS_H__
+#define __LK_CMDS_H__
+
+extern void lk_init_cmds ();
+
+#endif /* __LK_CMDS_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index 54d3b6272d..c61361b687 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -28,6 +28,7 @@
 #include "gdbtypes.h"
 #include "inferior.h"
 #include "lk-bitmap.h"
+#include "lk-cmds.h"
 #include "lk-list.h"
 #include "lk-low.h"
 #include "lk-modules.h"
@@ -821,6 +822,8 @@ lk_try_push_target ()
   else
     warning (_("Could not find all symbols for module support.  "
 	       "Module support turned off."));
+
+  lk_init_cmds ();
 }
 
 /* Function for targets to_open hook.  */
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index c098a3f426..3121fffbf7 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -402,7 +402,7 @@ error_unknown_type (const char *sym_print_name)
 /* Print type of EXP, or last thing in value history if EXP == NULL.
    show is passed to type_print.  */
 
-static void
+void
 whatis_exp (const char *exp, int show)
 {
   struct value *val;
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index ba9588a118..df96e35a53 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -107,4 +107,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 (const char *exp, int show);
+
 #endif
-- 
2.13.5

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

* [RFC PATCH v5 4/9] Add basic Linux kernel support
  2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
  2018-03-13 14:09   ` Kamil Rytarowski
  2018-03-19  0:11   ` Simon Marchi
  2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
  To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

Implement the basic infrastructure and functionality to allow Linux kernel
debugging with GDB.  This contains handling of kernel symbols and data
structures as well as a simple target_ops to hook into GDB.  For the code
to work architectures must provide an implementation for the virtual
methods in linux_kernel_ops.

For simplicity this patch only supports static targets, i.e. core dumps.
Support for live debugging will be provided in a separate patch.

gdb/ChangeLog:

	* gdbarch.sh (get_new_lk_ops): New hook.
	* gdbarch.h: Regenerated.
	* gdbarch.c: Regenerated.
	* defs.h (gdb_osabi): Add GDB_OSABI_LINUX_KERNEL.
	* osabi.c (gdb_osabi_names): Add Linux kernel entry.
	* lk-low.h: New file.
	* lk-low.c: New file.
	* lk-list.h: New file.
	* lk-bitmap.h: New file.
	* Makefile.in (ALLDEPFILES): Add lk-low.c.
	(HFILES_NO_SRCDIR): Add lk-low.h.
	(ALL_TARGET_OBS): Add lk-low.o.
	* configure.tgt (lk_tobjs): New variable with object files for Linux
	kernel support.
	(s390*-*-linux*): Add lk_tobjs.
---
 gdb/Makefile.in   |   3 +
 gdb/configure.tgt |   7 +-
 gdb/defs.h        |   1 +
 gdb/gdbarch.c     |  32 ++
 gdb/gdbarch.h     |   9 +
 gdb/gdbarch.sh    |   4 +
 gdb/lk-bitmap.h   | 226 ++++++++++++++
 gdb/lk-list.h     | 201 +++++++++++++
 gdb/lk-low.c      | 864 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-low.h      | 335 +++++++++++++++++++++
 gdb/osabi.c       |   1 +
 11 files changed, 1682 insertions(+), 1 deletion(-)
 create mode 100644 gdb/lk-bitmap.h
 create mode 100644 gdb/lk-list.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 690653ac04..056333e2cd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -715,6 +715,7 @@ ALL_TARGET_OBS = \
 	iq2000-tdep.o \
 	linux-record.o \
 	linux-tdep.o \
+	lk-low.o \
 	lm32-tdep.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
@@ -1276,6 +1277,7 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-low.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -2256,6 +2258,7 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.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 ba90411782..be68ac50fc 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -40,6 +40,10 @@ esac
 i386_tobjs="i386-tdep.o arch/i386.o i387-tdep.o"
 amd64_tobjs="amd64-tdep.o arch/amd64.o"
 
+# List of objectfiles for Linux kernel support.  To be included into *-linux*
+# targets wich support Linux kernel debugging.
+lk_tobjs="lk-low.o"
+
 # Here are three sections to get a list of target specific object
 # files according to target triplet $TARG.
 
@@ -516,7 +520,8 @@ powerpc*-*-*)
 s390*-*-linux*)
 	# Target: S390 running Linux
 	gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
-			linux-tdep.o linux-record.o symfile-mem.o"
+			linux-tdep.o linux-record.o symfile-mem.o \
+			${lk_tobjs}"
 	build_gdbserver=yes
 	;;
 
diff --git a/gdb/defs.h b/gdb/defs.h
index 91988758a3..692a7b8407 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -490,6 +490,7 @@ enum gdb_osabi
   GDB_OSABI_HURD,
   GDB_OSABI_SOLARIS,
   GDB_OSABI_LINUX,
+  GDB_OSABI_LINUX_KERNEL,
   GDB_OSABI_FREEBSD,
   GDB_OSABI_NETBSD,
   GDB_OSABI_OPENBSD,
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b8703e5a55..fd37c51f6b 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -352,6 +352,7 @@ struct gdbarch
   gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
   char ** disassembler_options;
   const disasm_options_t * valid_disassembler_options;
+  gdbarch_get_new_lk_ops_ftype *get_new_lk_ops;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -713,6 +714,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of addressable_memory_unit_size, invalid_p == 0 */
   /* Skip verify of disassembler_options, invalid_p == 0 */
   /* Skip verify of valid_disassembler_options, invalid_p == 0 */
+  /* Skip verify of get_new_lk_ops, has predicate.  */
   if (!log.empty ())
     internal_error (__FILE__, __LINE__,
                     _("verify_gdbarch: the following are invalid ...%s"),
@@ -1058,6 +1060,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: get_longjmp_target = <%s>\n",
                       host_address_to_string (gdbarch->get_longjmp_target));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_get_new_lk_ops_p() = %d\n",
+                      gdbarch_get_new_lk_ops_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_new_lk_ops = <%s>\n",
+                      host_address_to_string (gdbarch->get_new_lk_ops));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_get_siginfo_type_p() = %d\n",
                       gdbarch_get_siginfo_type_p (gdbarch));
   fprintf_unfiltered (file,
@@ -5077,6 +5085,30 @@ set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch,
   gdbarch->valid_disassembler_options = valid_disassembler_options;
 }
 
+int
+gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->get_new_lk_ops != NULL;
+}
+
+linux_kernel_ops *
+gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_new_lk_ops != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_new_lk_ops called\n");
+  return gdbarch->get_new_lk_ops (gdbarch, target);
+}
+
+void
+set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch,
+                            gdbarch_get_new_lk_ops_ftype get_new_lk_ops)
+{
+  gdbarch->get_new_lk_ops = get_new_lk_ops;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules.  */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 5cb131de1d..d1f54c08c9 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -65,6 +65,7 @@ struct mem_range;
 struct syscalls_info;
 struct thread_info;
 struct ui_out;
+class linux_kernel_ops;
 
 #include "regcache.h"
 
@@ -1554,6 +1555,14 @@ 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);
 
+/* Return a new instance of a class inherited from linux_kernel_ops */
+
+extern int gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch);
+
+typedef linux_kernel_ops * (gdbarch_get_new_lk_ops_ftype) (struct gdbarch *gdbarch, struct target_ops *target);
+extern linux_kernel_ops * gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target);
+extern void set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, gdbarch_get_new_lk_ops_ftype *get_new_lk_ops);
+
 /* 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 33dfa6b349..80167f2dc2 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1160,6 +1160,9 @@ 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)
 
+# Return a new instance of a class inherited from linux_kernel_ops
+M;linux_kernel_ops *;get_new_lk_ops;struct target_ops *target;target
+
 EOF
 }
 
@@ -1285,6 +1288,7 @@ struct mem_range;
 struct syscalls_info;
 struct thread_info;
 struct ui_out;
+class linux_kernel_ops;
 
 #include "regcache.h"
 
diff --git a/gdb/lk-bitmap.h b/gdb/lk-bitmap.h
new file mode 100644
index 0000000000..1247e7f9fb
--- /dev/null
+++ b/gdb/lk-bitmap.h
@@ -0,0 +1,226 @@
+/* Iterator for bitmaps from the Linux kernel.
+
+   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 __LK_BITMAP_H__
+#define __LK_BITMAP_H__
+
+#include "defs.h"
+
+#include "lk-low.h"
+
+/* Short hand access to frequently used bitmap.  */
+#define lk_cpu_online_mask lk_bitmap ("cpu_online_mask", "cpumask->bits")
+
+/* Container class to handle bitmaps declared with DECLARE_BITMAP from
+   <linux>/include/linux/types.h.  */
+
+class lk_bitmap
+{
+public:
+
+  template<class T>
+  class base_iterator
+  : public std::iterator<std::bidirectional_iterator_tag, T>
+  {
+  public:
+    base_iterator (const base_iterator<T> &it) = default;
+    base_iterator (std::vector<unsigned long>::const_iterator start,
+		   size_t bit, size_t size)
+      : m_start (start), m_bit (bit), m_size (size)
+    { next (); }
+
+    base_iterator<T> &operator++ ()
+    { m_bit++; return next (); }
+
+    base_iterator<T> operator++ (int)
+    { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+    base_iterator<T> &operator-- ()
+    { m_bit--; return prev (); }
+
+    base_iterator<T> operator-- (int)
+    { base_iterator<T> retval = *this; --(*this); return retval; }
+
+    bool operator== (base_iterator<T> other) const
+    { return (m_start == other.m_start && m_bit == other.m_bit
+	      && m_size == other.m_size); }
+
+    bool operator!= (base_iterator<T> other) const
+    { return !(*this == other); }
+
+    T operator* () const
+    { return m_bit; }
+
+  private:
+    /* Start of the vector containing the bitmap.  */
+    std::vector<unsigned long>::const_iterator m_start;
+
+    /* Last set bit returned.  */
+    size_t m_bit;
+
+    /* Size of the bitmap in bit.  */
+    size_t m_size;
+
+    /* Get next set bit.  */
+    base_iterator<T> &next ();
+
+    /* Get previous set bit.  */
+    base_iterator<T> &prev ();
+  }; /* class base_iterator  */
+
+  /* Constructor for bitmaps defined as variable NAME.  */
+  inline lk_bitmap (const std::string &name);
+
+  /* Constructor for bitmaps defined as field in variable NAME.  */
+  inline lk_bitmap (const std::string &name, const std::string &alias);
+
+  typedef base_iterator<size_t> iterator;
+  typedef base_iterator<const size_t> const_iterator;
+
+  iterator begin () { return iterator (m_bitmap.cbegin (), 0, size ()); }
+  iterator end () { return iterator (m_bitmap.cbegin (), size (), size ()); }
+
+  const_iterator cbegin () const
+  { return const_iterator (m_bitmap.cbegin (), 0, size ()); }
+  const_iterator cend () const
+  { return const_iterator (m_bitmap.cbegin (), size (), size ()); }
+
+  const_iterator begin () const
+  { return this->cbegin (); }
+  const_iterator end () const
+  { return this->cend (); }
+
+  /* Returns size of bitmap in bits.  */
+  inline size_t size () const;
+
+  /* Returns Hamming weight, i.e. number of set bits, of bitmap.  */
+  inline size_t hweight () const;
+
+private:
+  /* Read content of bitmap NAME.  */
+  inline void read (const std::string &name);
+
+  /* Returns number of unsigned longs needed to store N bytes.  */
+  inline size_t byte_to_ulong (size_t n) const;
+
+  /* Storage for content of bitmap.  */
+  std::vector<unsigned long> m_bitmap;
+}; /* class bitmap  */
+
+/* see declaration.  */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::next ()
+{
+  size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+  auto ulong = m_start + m_bit / ulong_bits;
+  while (m_bit < m_size)
+  {
+    if (*ulong & (1 << m_bit))
+	return *this;
+
+    m_bit++;
+    if ((m_bit % ulong_bits) == 0)
+      ulong++;
+  }
+  return *this;
+}
+
+/* see declaration.  */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::prev ()
+{
+  size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+  auto ulong = m_start + m_bit / ulong_bits;
+  while (m_bit > m_size)
+  {
+    if (*ulong & (1 << m_bit))
+	return *this;
+
+    m_bit--;
+    if ((m_bit % ulong_bits) == 0)
+      ulong--;
+  }
+  return *this;
+}
+
+/* see declaration.  */
+
+lk_bitmap::lk_bitmap (const std::string &name)
+{
+  symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+  size_t size = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym)));
+
+  m_bitmap.resize (byte_to_ulong (size));
+  read (name);
+}
+
+/* see declaration.  */
+
+lk_bitmap::lk_bitmap (const std::string &name, const std::string &alias)
+{
+  field *field = lk_field (alias);
+  m_bitmap.resize (byte_to_ulong (FIELD_SIZE (field)));
+  read (name);
+}
+
+/* see declaration.  */
+
+void
+lk_bitmap::read (const std::string &name)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR addr = lk_address (name);
+
+  for (size_t i = 0; i < m_bitmap.size (); i++)
+    m_bitmap[i] = lk_read_ulong (addr + i * ulong_size);
+}
+
+/* see declaration.  */
+size_t
+lk_bitmap::byte_to_ulong (size_t n) const
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  return (n + ulong_size - 1) / ulong_size;
+}
+
+/* see declaration.  */
+
+size_t
+lk_bitmap::size () const
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  return (m_bitmap.size () * ulong_size * LK_BITS_PER_BYTE);
+}
+
+/* see declaration.  */
+
+size_t
+lk_bitmap::hweight () const
+{
+  size_t ret = 0;
+  for (auto bit : *this)
+    ret++;
+  return ret;
+}
+
+#endif /* __LK_BITMAP_H__ */
diff --git a/gdb/lk-list.h b/gdb/lk-list.h
new file mode 100644
index 0000000000..512a47ba08
--- /dev/null
+++ b/gdb/lk-list.h
@@ -0,0 +1,201 @@
+/* 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_LIST_H__
+#define __LK_LIST_H__
+
+#include "defs.h"
+
+#include "inferior.h"
+#include "lk-low.h"
+
+
+/* Container class to handle doubly linked list using struct list_head from
+   <linux>/include/linux/types.h .  */
+
+class lk_list
+{
+  template<class T>
+  class base_iterator
+  : public std::iterator<std::bidirectional_iterator_tag, T>
+  {
+  public:
+    base_iterator (const base_iterator<T> &it) = default;
+    base_iterator (CORE_ADDR start, CORE_ADDR offset, bool embedded)
+      : m_current (start), m_start (start), m_offset (offset)
+    {
+      if (!embedded)
+	next ();
+    }
+
+    base_iterator<T> &operator++ ()
+    { return next (); }
+
+    base_iterator<T> operator++ (int)
+    { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+    base_iterator<T> &operator-- ()
+    { return next (false); }
+
+    base_iterator<T> operator-- (int)
+    { base_iterator<T> retval = *this; --(*this); return retval; }
+
+    bool operator== (base_iterator<T> &other) const
+    { return (m_start == other.m_start && m_current == other.m_current
+	      && !m_first); }
+
+    bool operator!= (base_iterator<T> &other) const
+    { return !(*this == other); }
+
+    /* Return container of the list_head.  */
+    T operator* () const
+    { return m_current - m_offset; }
+
+  private:
+    /* The list_head we are currently at.  */
+    CORE_ADDR m_current;
+
+    /* First element of the list.  */
+    CORE_ADDR m_start;
+
+    /* Offset of the list_head in the containing struct.  */
+    CORE_ADDR m_offset;
+
+    /* For doubly linked lists start == end.  Use m_first to track if we
+       just started.  */
+    bool m_first = true;
+
+    /* Go to the next (forward) or prev (!forward) element.  */
+    base_iterator<T> &next (bool forward = true);
+
+    /* We must always assume that the data we handle is corrupted.  Use
+       curr->next->prev == curr (or ->prev->next if goining back).  */
+    bool is_valid_next (CORE_ADDR next, bool forward) const;
+  }; /* class base_iterator  */
+
+public:
+  /* Constructor for lists starting at address START.  */
+  inline lk_list (CORE_ADDR start, const std::string &alias,
+		  bool embedded = true);
+
+  /* Constructor for lists starting at variable NAME.  */
+  inline lk_list (const std::string &name, const std::string &alias)
+    : lk_list (lk_address (name), alias, is_embedded (name))
+  {}
+
+  typedef base_iterator<CORE_ADDR> iterator;
+  typedef base_iterator<const CORE_ADDR> const_iterator;
+
+  /* Never advance to next element for end () --> embedded = true.  */
+  iterator begin () { return iterator (m_start, m_offset, m_embedded); }
+  iterator end () { return iterator (m_start, m_offset, true); }
+
+  const_iterator cbegin () const
+  { return const_iterator (m_start, m_offset, m_embedded); }
+  const_iterator cend () const
+  { return const_iterator (m_start, m_offset, true); }
+
+  const_iterator begin () const
+  { return this->cbegin (); }
+  const_iterator end () const
+  { return this->cend (); }
+
+private:
+  /* First element of the list.  */
+  CORE_ADDR m_start;
+
+  /* Offset of the list_head in the containing struct.  */
+  CORE_ADDR m_offset;
+
+  /* Is the first list_head embedded in the containing struct, i.e. do we
+     have to consider m_start as a full element of the list or just an entry
+     point?  */
+  bool m_embedded;
+
+  /* Check whether variable name is embeded, i.e. is not a list_head.  */
+  inline bool is_embedded (const std::string &name) const;
+}; /* class lk_list */
+
+/* see declaration.  */
+
+lk_list::lk_list (CORE_ADDR start, const std::string &alias, bool embedded)
+  : m_offset (lk_offset (alias)), m_embedded (embedded)
+{
+  m_start = start;
+  if (m_embedded)
+    m_start += m_offset;
+}
+
+/* see declaration.  */
+
+bool
+lk_list::is_embedded (const std::string &name) const
+{
+  symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+  type *type = SYMBOL_TYPE (sym);
+
+  return !(TYPE_CODE (type) == TYPE_CODE_STRUCT
+	   && streq ("list_head", TYPE_TAG_NAME (type)));
+}
+
+/* see declaration.  */
+
+template<class T>
+bool
+lk_list::base_iterator<T>::is_valid_next (CORE_ADDR next, bool forward) const
+{
+  if (forward)
+    next += lk_offset ("list_head->prev");
+  else
+    next += lk_offset ("list_head->next");
+
+  return m_current == lk_read_addr (next);
+}
+
+/* see declaration.  */
+
+template<class T>
+lk_list::base_iterator<T> &
+lk_list::base_iterator<T>::next (bool forward)
+{
+  CORE_ADDR next;
+
+  if (m_current == m_start && !m_first)
+    return *this;
+
+  m_first = false;
+
+  if (forward)
+    next = lk_read_addr (m_current + lk_offset ("list_head->next"));
+  else
+    next = lk_read_addr (m_current + lk_offset ("list_head->prev"));
+
+  if (!is_valid_next (next, forward))
+    {
+      error (_("Memory corruption detected while iterating list_head at "
+	       "0x%s: list_head->%s != list_head."),
+	     phex (m_current, lk_builtin_type_size (unsigned_long)),
+	     forward ? "next->prev" : "prev->next");
+    }
+
+  m_current = next;
+
+  return *this;
+}
+#endif /* __LK_LIST_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
new file mode 100644
index 0000000000..193619e6f5
--- /dev/null
+++ b/gdb/lk-low.c
@@ -0,0 +1,864 @@
+/* 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-bitmap.h"
+#include "lk-list.h"
+#include "lk-low.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "solib.h"
+#include "target.h"
+#include "value.h"
+
+#include <algorithm>
+
+target_ops *lk_target_ops = NULL;
+linux_kernel_ops *lk_ops = NULL;
+
+/* Helper function for declare_address.  Returns address of variable NAME on
+   success or -1 on failure.  */
+
+static CORE_ADDR
+lk_find_address (const std::string &name)
+{
+  bound_minimal_symbol bmsym = lookup_minimal_symbol (name.c_str (), NULL,
+						      NULL);
+  if (bmsym.minsym == NULL)
+    return -1;
+
+  return BMSYMBOL_VALUE_ADDRESS (bmsym);
+}
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_address (const std::string &alias,
+				       const std::string &name)
+{
+  if (has_address (alias))
+    return true;
+
+  CORE_ADDR addr = lk_find_address (name);
+  if (addr == -1)
+    return false;
+
+  m_symbols[alias].addr = addr;
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+				   const std::string &name,
+				   const lk_kconfig config)
+{
+  if (!try_declare_address (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing address: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+				   const std::initializer_list<const std::string> names,
+				   const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_address (alias, name))
+      break;
+
+  if (!has_address (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing address: %s"), alias.c_str ());
+    }
+}
+
+/* Helper function for try_declare_type.  Returns type on success or NULL on
+   failure  */
+
+static struct type *
+lk_find_type (const std::string &name)
+{
+  const struct block *global;
+  const struct symbol *sym;
+
+  global = block_global_block(get_selected_block (0));
+  sym = lookup_symbol (name.c_str (), global, STRUCT_DOMAIN, NULL).symbol;
+  if (sym != NULL)
+    return SYMBOL_TYPE (sym);
+
+  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
+  sym = lookup_symbol (name.c_str (), global, VAR_DOMAIN, NULL).symbol;
+  if (sym == NULL)
+    return NULL;
+
+  struct type *type = check_typedef (SYMBOL_TYPE (sym));
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
+    return NULL;
+
+  return type;
+}
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_type (const std::string &alias,
+				    const std::string &name)
+{
+  if (has_type (alias))
+    return true;
+
+  struct type *type = lk_find_type (name);
+
+  if (type == NULL)
+    return false;
+
+  m_symbols[unique_type_alias (alias)].type = type;
+
+  /* Also add an entry with the name actually used to m_symbol.  Needed to
+     support chained field lookup.  */
+  if (alias != name)
+    m_symbols[unique_type_alias (name)].type = type;
+
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+				const std::string &name,
+				const lk_kconfig config)
+{
+  if (!try_declare_type (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+				const std::initializer_list<const std::string> names,
+				const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_type (alias, name))
+      break;
+
+  if (!has_type (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+    }
+}
+
+/* Helper function for try_declare_field.  Returns lk_symbol with field
+   belonging to TYPE on success or empty on failure.  */
+
+static lk_symbol
+lk_find_field (const std::string &f_name, const struct type *type)
+{
+  struct field *field = TYPE_FIELDS (type);
+  struct field *last = field + TYPE_NFIELDS (type);
+
+  while (field != last)
+    {
+      if (streq (field->name, f_name.c_str ()))
+	return lk_symbol (field, FIELD_BYTEPOS (field));
+
+      /* Check if field is defined in anonymous struct within TYPE.  */
+      if (streq (field->name, ""))
+	{
+	  lk_symbol sym = lk_find_field (f_name, FIELD_TYPE (*field));
+	  if (sym.field != NULL)
+	    return lk_symbol (sym.field, FIELD_BYTEPOS (field) + sym.offset);
+	}
+      field++;
+    }
+  return lk_symbol ();
+}
+
+/* Helper class to parse C-like field names (type->field1->field2->...) and
+   generate aliases used in lk_ops->m_symbols.  */
+
+class lk_field_parser
+{
+public:
+  lk_field_parser (const std::string &alias)
+    : m_alias (alias)
+  {
+    /* The alias must begin with s_name->f_name of the first field.  */
+    m_end = m_alias.find (delim);
+    gdb_assert (m_end != std::string::npos);
+    m_end = m_alias.find (delim, m_end + delim.size ());
+  }
+
+  /* Return the struct, i.e. type name of the current field.  */
+  std::string s_name () const
+    {
+      if (m_last_type == NULL)
+	return m_alias.substr (0, m_alias.find (delim));
+
+      if (TYPE_CODE (m_last_type) == TYPE_CODE_TYPEDEF)
+	return TYPE_NAME (m_last_type);
+      else
+	return TYPE_TAG_NAME (m_last_type);
+    }
+
+  /* Return the field name of the current field.  */
+  std::string f_name () const
+    {
+      size_t start;
+
+      if (m_last_type == NULL)
+	start = m_alias.find (delim) + delim.size ();
+      else
+	start = m_start;
+
+      return m_alias.substr (start, m_end - start);
+    }
+
+  /* Return the full name of the current field.  */
+  std::string name () const
+  { return s_name () + delim + f_name (); }
+
+  /* Advance to the next field.  */
+  lk_field_parser *next ()
+    {
+      gdb_assert (!empty ());
+
+      m_last_type = FIELD_TYPE (*lk_field (name ()));
+      m_start = m_end + delim.size ();
+      m_end = m_alias.find (delim, m_start);
+
+      return this;
+    }
+
+  /* True when all fiels have been parsed.  */
+  bool empty () const
+  { return m_end == std::string::npos; }
+
+  /* Return the depth, i.e. number of fields, in m_alias.  */
+  unsigned int depth () const
+  {
+    size_t pos = m_alias.find (delim);
+    unsigned int ret = 0;
+
+    while (pos != std::string::npos)
+      {
+	ret ++;
+	pos = m_alias.find (delim, pos + delim.size ());
+      }
+
+    return ret;
+  }
+
+private:
+  /* Alias originally passed to parser.  */
+  std::string m_alias;
+
+  /* First index of current field in m_alias.  */
+  size_t m_start = 0;
+
+  /* Last index of current field in m_alias.  */
+  size_t m_end = 0;
+
+  /* Type of the last field found.  Needed to get s_name of embedded
+     fields.  */
+  struct type *m_last_type = NULL;
+
+  /* Delemiter used to separate fields.  */
+  const std::string delim = "->";
+};
+
+/* See lk-low.h.  */
+
+bool
+linux_kernel_ops::try_declare_field (const std::string &orig_alias,
+				     const std::string &orig_name)
+{
+  if (has_field (orig_alias))
+    return true;
+
+  lk_field_parser alias (orig_alias);
+  lk_field_parser name (orig_name);
+
+  /* Only allow declaration of one field at a time.  */
+  gdb_assert (alias.depth () == 1);
+  gdb_assert (name.depth () == 1);
+
+  if (!try_declare_type (alias.s_name (), name.s_name ()))
+    return false;
+
+  lk_symbol field = lk_find_field (name.f_name (), type (alias.s_name ()));
+  if (field.field == NULL)
+    return false;
+
+  m_symbols[alias.name ()] = field;
+  return true;
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+				 const std::string &name,
+				 const lk_kconfig config)
+{
+  if (!try_declare_field (alias, name))
+    {
+      m_kconfig |= config;
+      warning (_("Missing field: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+				 const std::initializer_list<const std::string> names,
+				 const lk_kconfig config)
+{
+  for (auto &name: names)
+    if (try_declare_field (alias, name))
+      break;
+
+  if (!has_field (alias))
+    {
+      m_kconfig |= config;
+      warning (_("Missing field: %s"), alias.c_str ());
+    }
+}
+
+/* See lk-low.h.  */
+
+void
+linux_kernel_ops::read_symbols ()
+{
+  if (!m_symbols.empty ())
+    m_symbols.clear ();
+
+  declare_field ("task_struct->tasks", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->pid", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->tgid", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->thread_group", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->comm", LK_CONFIG_ALWAYS);
+  declare_field ("task_struct->thread", LK_CONFIG_ALWAYS);
+
+  declare_field ("list_head->next", LK_CONFIG_ALWAYS);
+  declare_field ("list_head->prev", LK_CONFIG_ALWAYS);
+
+  declare_field ("rq->curr", LK_CONFIG_ALWAYS);
+
+  declare_field ("cpumask->bits", LK_CONFIG_ALWAYS);
+
+  declare_address ("init_task", LK_CONFIG_ALWAYS);
+  declare_address ("runqueues", LK_CONFIG_ALWAYS);
+  declare_address ("__per_cpu_offset", LK_CONFIG_ALWAYS);
+
+  declare_address ("cpu_online_mask", {"__cpu_online_mask", /* linux 4.5+ */
+				       "cpu_online_bits"},  /* linux -4.4 */
+		   LK_CONFIG_ALWAYS);
+
+  arch_read_symbols ();
+
+  if (!ifdef (LK_CONFIG_ALWAYS))
+    error (_("Could not find all symbols needed.  Aborting."));
+}
+
+/* See lk-low.h.  */
+
+CORE_ADDR
+linux_kernel_ops::offset (const std::string &orig_alias) const
+{
+  lk_field_parser alias (orig_alias);
+  CORE_ADDR ret = m_symbols.at (alias.name ()).offset;
+
+  while (!alias.empty ())
+    ret += m_symbols.at (alias.next ()->name ()).offset;
+
+  return ret;
+}
+
+/* Map cpu number CPU to the original PTID from target beneath.  */
+
+static ptid_t
+lk_cpu_to_old_ptid (const unsigned int cpu)
+{
+  struct lk_ptid_map *ptid_map;
+
+  for (ptid_map = lk_ops->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_unsigned_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);
+}
+
+/* See lk-low.h.  */
+
+CORE_ADDR
+linux_kernel_ops::percpu_offset (unsigned int cpu)
+{
+  size_t ulong_size = lk_builtin_type_size (unsigned_long);
+  CORE_ADDR percpu_elt = address ("__per_cpu_offset") + (ulong_size * cpu);
+  return lk_read_addr (percpu_elt);
+}
+
+/* See lk-low.h.  */
+
+unsigned int
+linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti)
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = address ("runqueues") + percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + offset ("rq->curr"));
+      int pid = lk_read_int (curr + offset ("task_struct->pid"));
+
+      if (pid == ti->ptid.lwp ())
+	return cpu;
+    }
+
+  error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
+	 ti->ptid.pid (), ti->ptid.lwp ());
+}
+
+/* Test if a given task TASK is running.  See comment in lk-low.h for
+   details.  */
+
+unsigned int
+lk_task_running (CORE_ADDR task)
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+
+      if (curr == task)
+	return cpu;
+    }
+
+  return LK_CPU_INVAL;
+}
+
+/* Update running tasks with information from struct rq->curr. */
+
+static void
+lk_update_running_tasks ()
+{
+  for (unsigned int cpu : lk_cpu_online_mask)
+    {
+      CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+      CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+      int pid = lk_read_int (curr + lk_offset ("task_struct->pid"));
+      int inf_pid = current_inferior ()->pid;
+
+      ptid_t new_ptid (inf_pid, pid, curr);
+      ptid_t old_ptid = lk_cpu_to_old_ptid (cpu); /* FIXME not suitable for
+						     running targets? */
+
+      thread_info *tp = find_thread_ptid (old_ptid);
+      if (tp && tp->state != THREAD_EXITED)
+	thread_change_ptid (old_ptid, new_ptid);
+    }
+}
+
+/* Update sleeping tasks by walking the task_structs starting from
+   init_task.  */
+
+static void
+lk_update_sleeping_tasks ()
+{
+  int inf_pid = current_inferior ()->pid;
+
+  for (CORE_ADDR task : lk_list ("init_task", "task_struct->tasks"))
+    {
+      for (CORE_ADDR thread : lk_list (task, "task_struct->thread_group"))
+	{
+	  int pid = lk_read_int (thread + lk_offset ("task_struct->pid"));
+	  ptid_t ptid (inf_pid, pid, thread);
+
+	  thread_info *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 = (CORE_ADDR) regcache->ptid ().tid ();
+
+  /* Are we called during init?  */
+  if (task == 0)
+    return target->beneath->to_fetch_registers (target, regcache, regnum);
+
+  unsigned int cpu = lk_task_running (task);
+
+  /* Let the target beneath fetch registers of running tasks.  */
+  if (cpu != LK_CPU_INVAL)
+    {
+      scoped_restore_regcache_ptid restore_regcache (regcache);
+      regcache->set_ptid (lk_cpu_to_old_ptid (cpu));
+
+      lk_ops->beneath ()->to_fetch_registers (target, regcache, regnum);
+    }
+  else
+    {
+      lk_ops->get_registers (task, target, regcache, regnum);
+
+      /* Mark all registers not found as unavailable.  */
+      for (int i = 0; i < gdbarch_num_regs (regcache->arch ()); i++)
+	{
+	  if (regcache->get_register_status (i) != REG_VALID)
+	    regcache->invalidate (i);
+	}
+    }
+}
+
+/* Function for targets to_pid_to_str hook.  Marks running tasks with an
+   asterisk "*".  */
+
+static const char *
+lk_pid_to_str (struct target_ops *target, ptid_t ptid)
+{
+  CORE_ADDR task = (CORE_ADDR) ptid.tid ();
+  static std::string str;
+  const char *fmt;
+
+  if (lk_task_running (task) != LK_CPU_INVAL)
+    fmt = "PID: %5li*, 0x%s";
+  else
+    fmt = "PID: %6li, 0x%s";
+
+  str = string_printf (fmt, ptid.lwp (),
+		       phex (task, lk_builtin_type_size (unsigned_long)));
+
+  return str.c_str ();
+}
+
+/* Function for targets to_thread_name hook.  */
+
+static const char *
+lk_thread_name (struct target_ops *target, struct thread_info *ti)
+{
+  static std::string str (LK_TASK_COMM_LEN, '\0');
+
+  size_t size = std::min ((unsigned int) LK_TASK_COMM_LEN,
+			  LK_ARRAY_LEN(lk_field ("task_struct->comm")));
+
+  CORE_ADDR task = (CORE_ADDR) ti->ptid.tid ();
+  CORE_ADDR comm = task + lk_offset ("task_struct->comm");
+  read_memory (comm, (gdb_byte *) str.data (), size);
+
+  str = string_printf ("%-16s", str.c_str ());
+
+  return str.c_str ();
+}
+
+
+/* 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 bool
+lk_is_linux_kernel (struct objfile *objfile)
+{
+  int ok = 0;
+
+  if (objfile == NULL || !(objfile->flags & OBJF_MAINLINE))
+    return false;
+
+  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);
+}
+
+/* Frees the cpu to old ptid map.  */
+
+static void
+lk_free_ptid_map ()
+{
+  while (lk_ops->old_ptid)
+    {
+      struct lk_ptid_map *tmp;
+
+      tmp = lk_ops->old_ptid;
+      lk_ops->old_ptid = tmp->next;
+      XDELETE (tmp);
+    }
+}
+
+/* Initialize the cpu to old ptid map.  */
+
+static void
+lk_init_ptid_map ()
+{
+  struct thread_info *ti;
+
+  if (lk_ops->old_ptid != NULL)
+    lk_free_ptid_map ();
+
+  ALL_THREADS (ti)
+    {
+      struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
+
+      ptid_map->cpu = lk_ops->beneath_thread_to_cpu (ti);
+      ptid_map->old_ptid = ti->ptid;
+
+      ptid_map->next = lk_ops->old_ptid;
+      lk_ops->old_ptid = ptid_map;
+    }
+}
+
+/* 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_get_new_lk_ops_p (gdbarch)))
+    error (_("Linux kernel debugging not supported on %s."),
+	   gdbarch_bfd_arch_info (gdbarch)->printable_name);
+
+  lk_ops = gdbarch_get_new_lk_ops (gdbarch, lk_target_ops);
+  lk_ops->read_symbols ();
+
+  lk_init_ptid_map ();
+  lk_update_thread_list (lk_ops->target ());
+
+  if (!target_is_pushed (lk_target_ops))
+    push_target (lk_ops->target ());
+}
+
+/* Function for targets to_open hook.  */
+
+static void
+lk_open (const char *args, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (target_is_pushed (lk_target_ops))
+    {
+      printf_unfiltered (_("Linux kernel target already pushed.  Aborting\n"));
+      return;
+    }
+
+  for (objfile = current_program_space->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile)
+	  && inferior_ptid.pid () != 0)
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+  printf_unfiltered (_("Could not find a valid Linux kernel object file.  "
+		       "Aborting.\n"));
+}
+
+/* Function for targets to_close hook.  Deletes all private data.  */
+
+static void
+lk_close (struct target_ops *ops)
+{
+  lk_free_ptid_map ();
+
+  delete (lk_ops);
+}
+
+/* Function for targets to_detach hook.  */
+
+static void
+lk_detach (struct target_ops *t, inferior *inf, int from_tty)
+{
+  struct target_ops *beneath = lk_ops->beneath ();
+
+  unpush_target (lk_ops->target ());
+  reinit_frame_cache ();
+  if (from_tty)
+    printf_filtered (_("Linux kernel target detached.\n"));
+
+  beneath->to_detach (beneath, inf, from_tty);
+}
+
+/* Function for new objfile observer.  */
+
+static void
+lk_observer_new_objfile (struct objfile *objfile)
+{
+  if (lk_is_linux_kernel (objfile) && inferior_ptid.pid () != 0)
+    lk_try_push_target ();
+}
+
+/* Function for inferior created observer.  */
+
+static void
+lk_observer_inferior_created (struct target_ops *ops, int from_tty)
+{
+  struct objfile *objfile;
+
+  if (inferior_ptid.pid () == 0)
+    return;
+
+  for (objfile = current_inferior ()->pspace->objfiles; objfile;
+       objfile = objfile->next)
+    {
+      if (lk_is_linux_kernel (objfile))
+	{
+	  lk_try_push_target ();
+	  return;
+	}
+    }
+}
+
+/* Initialize linux kernel target.  */
+
+static void
+init_lk_target_ops (void)
+{
+  struct target_ops *t;
+
+  if (lk_target_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";
+
+  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;
+
+  lk_target_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_lk_target_ops ();
+
+  observer_attach_new_objfile (lk_observer_new_objfile);
+  observer_attach_inferior_created (lk_observer_inferior_created);
+}
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
new file mode 100644
index 0000000000..39c0d88f43
--- /dev/null
+++ b/gdb/lk-low.h
@@ -0,0 +1,335 @@
+/* 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 <unordered_map>
+
+#include "gdbtypes.h"
+#include "target.h"
+
+/* Copied 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
+
+/* 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);
+
+/* Enum to track the config options used to build the kernel.  Whenever
+   a symbol is declared (in linux_kernel_ops::{arch_}read_symbols) which
+   only exists if the kernel was built with a certain config option an entry
+   has to be added here.  */
+enum lk_kconfig_values
+{
+  LK_CONFIG_ALWAYS	= 1 << 0,
+  LK_CONFIG_SMT		= 1 << 1,
+  LK_CONFIG_MODULES	= 1 << 2,
+};
+DEF_ENUM_FLAGS_TYPE (enum lk_kconfig_values, lk_kconfig);
+
+/* 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;
+};
+
+/* Cache for the value of a symbol.  Used in linux_kernel_ops->m_symbols.  */
+
+union lk_symbol
+{
+  CORE_ADDR addr;
+  struct type *type;
+  struct
+  {
+    struct field *field;
+    CORE_ADDR offset;
+  };
+
+  lk_symbol () {field = NULL; offset = 0;}
+  lk_symbol (struct field *f, CORE_ADDR o) {field = f, offset = o;}
+};
+
+class linux_kernel_ops
+{
+public:
+  linux_kernel_ops (struct target_ops *ops)
+    : m_ops (ops)
+  {}
+
+  virtual ~linux_kernel_ops () = default;
+
+  /* Read registers from the target and supply their content to regcache.  */
+  virtual void get_registers (CORE_ADDR task, struct target_ops *target,
+			      struct regcache *regcache, int regnum) = 0;
+
+  /* Return the per_cpu_offset of cpu CPU.  Default uses __per_cpu_offset
+     array to determine the offset.  */
+  virtual CORE_ADDR percpu_offset (unsigned int cpu);
+
+  /* Maps thread of target beneath to a cpu id.  Default assumes
+     rq->curr->pid == beneath_ptid.lwp.  */
+  virtual unsigned int beneath_thread_to_cpu (thread_info *ti);
+
+  /* Return the linux-kernel target.  */
+  struct target_ops *target () const
+  { return m_ops; }
+
+  /* Return the target beneath the linux-kernel target.  */
+  struct target_ops *beneath () const
+  { return m_ops->beneath; }
+
+  /* Return a previously declared address with key ALIAS.
+     Throws internal_error if requested symbol was not declared first.  */
+  CORE_ADDR address (const std::string &alias) const
+  {
+    gdb_assert (has_address (alias));
+    return m_symbols.at (alias).addr;
+  }
+
+  /* Same like address but for types.  */
+  struct type *type (const std::string &alias) const
+  {
+    gdb_assert (has_type (alias));
+    return m_symbols.at (unique_type_alias(alias)).type;
+  }
+
+  /* Same like address but for fields.  */
+  struct field *field (const std::string &alias) const
+  {
+    gdb_assert (has_field (alias));
+    return m_symbols.at (alias).field;
+  }
+
+  /* Checks whether address ALIAS exists in m_symbols.  */
+  bool has_address (const std::string &alias) const
+  { return has_symbol (alias); }
+
+  /* Same like has_address but for types.  */
+  bool has_type (const std::string &alias) const
+  { return has_symbol (unique_type_alias (alias)); }
+
+  /* Same like has_address but for fields.  */
+  bool has_field (const std::string &alias) const
+  { return has_symbol (alias); }
+
+  /* Return offset of field ALIAS (in byte).  */
+  CORE_ADDR offset (const std::string &alias) const;
+
+  /* Check whether the kernel was build using this config option.  */
+  bool ifdef (lk_kconfig config) const
+  { return !(m_kconfig & config); }
+
+  /* Linked list to map between cpu number and original ptid from target
+     beneath.  */
+  struct lk_ptid_map *old_ptid = NULL;
+
+  /* Declare and initialize all symbols needed.  Must be called _after_
+     symbol tables were initialized.  */
+  void read_symbols ();
+
+protected:
+  /* Virtual method to allow architectures to declare their own symbols.
+     Called by read_symbols.  */
+  virtual void arch_read_symbols ()
+  {}
+
+  /* Helper function to declare_address.  Returns true when symbol NAME
+     using key ALIAS was successfully declared, false otherwise.  Try not to
+     use this function directly but use declare_address instead.  */
+  bool try_declare_address (const std::string &alias,
+			    const std::string &names);
+
+  /* Same like try_declare_address but for types.  */
+  bool try_declare_type (const std::string &alias, const std::string &name);
+
+  /* Same like try_declare_address but for fields.  */
+  bool try_declare_field (const std::string &alias, const std::string &name);
+
+  /* Same like try_declare_field but with NAME = ALIAS.  */
+  bool try_declare_field (const std::string &name)
+  { return try_declare_field (name, name); }
+
+  /* Declare symbol NAME using key ALIAS.  If no symbol NAME could be found
+     mark CONFIG as missing.  */
+  void declare_address (const std::string &alias, const std::string &name,
+			const lk_kconfig config);
+
+  /* Same like above but with NAME = ALIAS.  */
+  void declare_address (const std::string &name, const lk_kconfig config)
+  { declare_address (name, name, config); }
+
+  /* Same like above but only mark CONFIG as missing if none of the symbols
+     in NAMES could be found.  */
+  void declare_address (const std::string &alias,
+			const std::initializer_list<const std::string> names,
+			const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_type (const std::string &alias, const std::string &name,
+		     const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_type (const std::string &name, const lk_kconfig config)
+  { declare_type (name, name, config); }
+
+  /* See declare_address.  */
+  void declare_type (const std::string &alias,
+		     const std::initializer_list<const std::string> names,
+		     const lk_kconfig config);
+
+  /* See declare_address.  */
+  void declare_field (const std::string &alias, const std::string &name,
+		      const lk_kconfig kconfig);
+
+  /* See declare_address.  */
+  void declare_field (const std::string &name, const lk_kconfig kconfig)
+  { declare_field (name, name, kconfig); }
+
+  /* See declare_address.  */
+  void declare_field (const std::string &alias,
+		      const std::initializer_list <const std::string> names,
+		      const lk_kconfig config);
+
+private:
+  /* The target_ops we are working with.  */
+  struct target_ops *m_ops;
+
+  /* The configuration used to build the kernel.  To make the implementation
+     easier m_kconfig is inverse, i.e. it tracks the _missing_ config options
+     not the set ones.  */
+  lk_kconfig m_kconfig = 0;
+
+  /* Collection of all declared symbols (addresses, fields etc.).  */
+  std::unordered_map<std::string, union lk_symbol> m_symbols;
+
+  /* Returns unique alias for struct ALIAS.  */
+  const std::string unique_type_alias (const std::string &alias) const
+  {
+    std::string prefix ("struct ");
+    if (startswith (alias.c_str (), prefix.c_str ()))
+	return alias;
+    return prefix + alias;
+  }
+
+  /* Check if m_symbols contains ALIAS.  */
+  bool has_symbol (const std::string &alias) const
+  { return m_symbols.count (alias) != 0; }
+};
+
+extern target_ops *lk_target_ops;
+extern linux_kernel_ops *lk_ops;
+
+/* Short hand access to frequently used lk_ops methods.  */
+
+static inline CORE_ADDR
+lk_address (const std::string &alias)
+{
+  return lk_ops->address (alias);
+}
+
+static inline struct type *
+lk_type (const std::string &alias)
+{
+  return lk_ops->type (alias);
+}
+
+static inline struct field *
+lk_field (const std::string &alias)
+{
+  return lk_ops->field (alias);
+}
+
+static inline CORE_ADDR
+lk_offset (const std::string &alias)
+{
+  return lk_ops->offset (alias);
+}
+
+static inline bool
+lk_ifdef (lk_kconfig config)
+{
+  return lk_ops->ifdef (config);
+}
+
+/* Align VAL to BASE. BASE must be a power of 2.  */
+#define LK_ALIGN(val, base) LK_ALIGN_MASK ((val), (typeof(val))(base) - 1)
+
+/* Same as LK_ALIGN, but aligns down.  */
+#define LK_ALIGN_DOWN(val, base) ((val) & ~((typeof(val))(base) - 1))
+
+/* Helper for LK_ALIGN.  */
+#define LK_ALIGN_MASK(val, mask) (((val) + (mask)) & ~(mask))
+
+/* 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))
+
+/* 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)))))
+
+#define FIELD_BYTEPOS(field)			\
+  (FIELD_BITPOS (*field) / LK_BITS_PER_BYTE)
+
+/* 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__ */
diff --git a/gdb/osabi.c b/gdb/osabi.c
index fd44deb9fa..14ddd414f3 100644
--- a/gdb/osabi.c
+++ b/gdb/osabi.c
@@ -64,6 +64,7 @@ static const struct osabi_names gdb_osabi_names[] =
   { "GNU/Hurd", NULL },
   { "Solaris", NULL },
   { "GNU/Linux", "linux(-gnu[^-]*)?" },
+  { "Linux kernel", NULL },
   { "FreeBSD", NULL },
   { "NetBSD", NULL },
   { "OpenBSD", NULL },
-- 
2.13.5

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

* Re: [RFC PATCH v5 4/9] Add basic Linux kernel support
  2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
@ 2018-03-13 14:09   ` Kamil Rytarowski
  2018-03-14  9:48     ` Philipp Rudo
  2018-03-19  0:11   ` Simon Marchi
  1 sibling, 1 reply; 18+ messages in thread
From: Kamil Rytarowski @ 2018-03-13 14:09 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez


[-- Attachment #1.1: Type: text/plain, Size: 624 bytes --]

On 12.03.2018 16:31, Philipp Rudo wrote:
> Implement the basic infrastructure and functionality to allow Linux kernel
> debugging with GDB.  This contains handling of kernel symbols and data
> structures as well as a simple target_ops to hook into GDB.  For the code
> to work architectures must provide an implementation for the virtual
> methods in linux_kernel_ops.
> 
> For simplicity this patch only supports static targets, i.e. core dumps.
> Support for live debugging will be provided in a separate patch.
> 


What's wrong with kgdb? It works well on NetBSD for alive and dead
kernels since 90ties.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 850 bytes --]

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

* Re: [RFC PATCH v5 4/9] Add basic Linux kernel support
  2018-03-13 14:09   ` Kamil Rytarowski
@ 2018-03-14  9:48     ` Philipp Rudo
  2018-03-14 23:38       ` Kamil Rytarowski
  0 siblings, 1 reply; 18+ messages in thread
From: Philipp Rudo @ 2018-03-14  9:48 UTC (permalink / raw)
  To: Kamil Rytarowski; +Cc: gdb-patches, Omair Javaid, Yao Qi, arnez

Hi Kamil,

On Tue, 13 Mar 2018 15:08:32 +0100
Kamil Rytarowski <n54@gmx.com> wrote:

> On 12.03.2018 16:31, Philipp Rudo wrote:
> > Implement the basic infrastructure and functionality to allow Linux kernel
> > debugging with GDB.  This contains handling of kernel symbols and data
> > structures as well as a simple target_ops to hook into GDB.  For the code
> > to work architectures must provide an implementation for the virtual
> > methods in linux_kernel_ops.
> > 
> > For simplicity this patch only supports static targets, i.e. core dumps.
> > Support for live debugging will be provided in a separate patch.
> >   
> 
> 
> What's wrong with kgdb? It works well on NetBSD for alive and dead
> kernels since 90ties.

I don't know kgdb well. We at IBM mostly work with dumps and the crash tool,
which is based on an old version of gdb and has some drawbacks. For example it
doesn't have a dwarf unwinder. That's why we wanted to see if the logic needed
for kernel debugging can be added directly to gdb, instead of having a hack
working on top of it.

Furthermore, I don't see a conflict between kgdb and this work. The way I see
it, kgdb is a technique to retrieve information from the kernel. This work
however, teaches gdb how to interpret the data from the kernel in order to make
it easier to find the information you need.

Thanks
Philipp

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

* Re: [RFC PATCH v5 4/9] Add basic Linux kernel support
  2018-03-14  9:48     ` Philipp Rudo
@ 2018-03-14 23:38       ` Kamil Rytarowski
  0 siblings, 0 replies; 18+ messages in thread
From: Kamil Rytarowski @ 2018-03-14 23:38 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, Omair Javaid, Yao Qi, arnez


[-- Attachment #1.1: Type: text/plain, Size: 1664 bytes --]

On 14.03.2018 10:48, Philipp Rudo wrote:
> Hi Kamil,
> 
> On Tue, 13 Mar 2018 15:08:32 +0100
> Kamil Rytarowski <n54@gmx.com> wrote:
> 
>> On 12.03.2018 16:31, Philipp Rudo wrote:
>>> Implement the basic infrastructure and functionality to allow Linux kernel
>>> debugging with GDB.  This contains handling of kernel symbols and data
>>> structures as well as a simple target_ops to hook into GDB.  For the code
>>> to work architectures must provide an implementation for the virtual
>>> methods in linux_kernel_ops.
>>>
>>> For simplicity this patch only supports static targets, i.e. core dumps.
>>> Support for live debugging will be provided in a separate patch.
>>>   
>>
>>
>> What's wrong with kgdb? It works well on NetBSD for alive and dead
>> kernels since 90ties.
> 
> I don't know kgdb well. We at IBM mostly work with dumps and the crash tool,
> which is based on an old version of gdb and has some drawbacks. For example it
> doesn't have a dwarf unwinder. That's why we wanted to see if the logic needed
> for kernel debugging can be added directly to gdb, instead of having a hack
> working on top of it.
> 
> Furthermore, I don't see a conflict between kgdb and this work. The way I see
> it, kgdb is a technique to retrieve information from the kernel. This work
> however, teaches gdb how to interpret the data from the kernel in order to make
> it easier to find the information you need.
> 

I see. Once you will be done, I will have a look whether it can be
useful for other OSes.

I've mentioned kgdb, as it has been ported to Linux and it can work as a
backend for GDB.

> Thanks
> Philipp
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 850 bytes --]

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

* Re: [RFC PATCH v5 1/9] Convert substitute_path_component to C++
  2018-03-12 15:31 ` [RFC PATCH v5 1/9] Convert substitute_path_component to C++ Philipp Rudo
@ 2018-03-16  2:15   ` Simon Marchi
  2018-03-17 20:11     ` Simon Marchi
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2018-03-16  2:15 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

Hi Philipp,

Just a few comments, but in general I think this patch is good and could
go in separate from the rest.

On 2018-03-12 11:31 AM, Philipp Rudo wrote:
> Simplify the code of utils.c:substiute_path_component by converting it to C++.

s/substiute/substitute/
> 
> 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 | 19 ++++++++-----------
>  gdb/utils.c     | 47 +++++++++++------------------------------------
>  gdb/utils.h     | 10 ++++++++--
>  3 files changed, 27 insertions(+), 49 deletions(-)
> 
> diff --git a/gdb/auto-load.c b/gdb/auto-load.c
> index 70bddbc862..a7f9635252 100644
> --- a/gdb/auto-load.c
> +++ b/gdb/auto-load.c
> @@ -175,21 +175,18 @@ std::vector<gdb::unique_xmalloc_ptr<char>> auto_load_safe_path_vec;
>     substitute_path_component.  */
>  
>  static std::vector<gdb::unique_xmalloc_ptr<char>>
> -auto_load_expand_dir_vars (const char *string)
> +auto_load_expand_dir_vars (const std::string &string)

All the usages of auto_load_expand_dir_vars pass in a char pointer.  This means that
a temporary std::string is created for the duration of the call (one copy) and another
one is done lower.  I would suggest either to leave the parameter as const char * to
avoid that copy.

>  {
> -  char *s = xstrdup (string);
> -  substitute_path_component (&s, "$datadir", gdb_datadir);
> -  substitute_path_component (&s, "$debugdir", debug_file_directory);
> +  std::string s (string);
> +  substitute_path_component (s, "$datadir", gdb_datadir);
> +  substitute_path_component (s, "$debugdir", debug_file_directory);
>  
> -  if (debug_auto_load && strcmp (s, string) != 0)
> +  if (debug_auto_load && s.compare (string) != 0)

s != string

>      fprintf_unfiltered (gdb_stdlog,
> -			_("auto-load: Expanded $-variables to \"%s\".\n"), s);
> +			_("auto-load: Expanded $-variables to \"%s\".\n"),
> +			s.c_str ());
>  
> -  std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec
> -    = dirnames_to_char_ptr_vec (s);
> -  xfree(s);
> -
> -  return dir_vec;
> +  return dirnames_to_char_ptr_vec (s.c_str ());
>  }
>  
>  /* Update auto_load_safe_path_vec from current AUTO_LOAD_SAFE_PATH.  */
> diff --git a/gdb/utils.c b/gdb/utils.c
> index b99d444a6e..d4f1398d14 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -3052,49 +3052,24 @@ make_bpstat_clear_actions_cleanup (void)
>    return make_cleanup (do_bpstat_clear_actions_cleanup, NULL);
>  }
>  
> -/* 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 + to.length ()))
>      {
> -      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 = pos == 0 ? str[0] : str[pos - 1];

I think it would be safe to just not set start if pos == 0, given the condition
below, instead of setting it to an unrelated character.

> +      char 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 8ca3eb0369..7e6a39ee82 100644
> --- a/gdb/utils.h
> +++ b/gdb/utils.h
> @@ -298,8 +298,14 @@ extern struct cleanup *make_bpstat_clear_actions_cleanup (void);
>  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.  STR
> +   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 STR).  */

This comment would need to be updated.

> +
> +extern void substitute_path_component (std::string &str,
> +				       const std::string &from,
> +				       const std::string &to);

I sent a proposal to the list that we avoid using non-const references, and use pointers
instead.  See:

https://sourceware.org/ml/gdb-patches/2018-03/msg00145.html

Here, it would mean using a pointer for the first parameter.  I didn't have any feedback
and therefore there hasn't been any formal decision, but I would suggest changing it to
a pointer for the reasons outlined in that message.

Also, I would suggest keeping the from and to parameters as const char *.  All the usages
pass in char pointers, so having the parameters as string only makes unnecessary copies.

>  
>  std::string ldirname (const char *filename);
>  
> 

I wrote and pushed some unit tests for the function while reviewing, you'll have to fixup
that new call with:

  auto test = [] (std::string s, const char *from, const char *to,
		  const char *expected)
    {
      substitute_path_component (s, from, to);
      SELF_CHECK (s == expected);
    };

Thanks,

Simon

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

* Re: [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function
  2018-03-12 15:31 ` [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
@ 2018-03-16  2:47   ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2018-03-16  2:47 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

On 2018-03-12 11:31 AM, Philipp Rudo wrote:
>     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               | 16 ++++++++++++++++
>  3 files changed, 73 insertions(+)
> 
> diff --git a/gdb/common/common-utils.h b/gdb/common/common-utils.h
> index 5408c35469..8e6eb05c2d 100644
> --- a/gdb/common/common-utils.h
> +++ b/gdb/common/common-utils.h
> @@ -109,6 +109,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)

Should this be >=, so that endswith ("Hello", "Hello") returns true?

> +	  && strncmp (string + strlen (string) - strlen (pattern), pattern,
> +		      strlen (pattern)) == 0);
> +}

I believe you can use strcmp, which would avoid the last call to strlen.


> +
>  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 d4f1398d14..4ce3909fca 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -3072,6 +3072,52 @@ substitute_path_component (std::string &str, const std::string &from,
>      }
>  }
>  
> +/* Approximate length of final path.  Helper for concat_path.  */
> +
> +static inline unsigned long

Make the return type size_t too?

> +approx_path_length (const std::initializer_list<const std::string> args,
>
> +		    const std::string &dir_separator)
> +{
> +  size_t length = 0;
> +
> +  for (const std::string &arg: args)
> +      length += arg.length () + dir_separator.length ();

One extra indentation level.

> +
> +  return length;
> +}
> +
> +/* See utils.h.  */
> +
> +std::string
> +_concat_path (const std::initializer_list<const std::string> args,
> +	      const std::string &dir_separator)

dit_separator should probably be a const char *, for the same reason
as mentioned in the previous patch.

> +{
> +  std::string ret;
> +  ret.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 (ret.c_str (), dir_separator.c_str ()))
> +	ret.erase (ret.length () - dir_separator.length (),
> +		   dir_separator.length ());
> +
> +      else if (!ret.empty ()
> +	       && !startswith (arg.c_str (), dir_separator.c_str ())
> +	       && !endswith (ret.c_str (), dir_separator.c_str ())
> +	       && ret != TARGET_SYSROOT_PREFIX)
> +	ret += dir_separator;
> +
> +      ret += arg;
> +    }
> +
> +  ret.shrink_to_fit ();
> +  return ret;
> +}
> +
>  #ifdef HAVE_WAITPID
>  
>  #ifdef SIGALRM
> diff --git a/gdb/utils.h b/gdb/utils.h
> index 7e6a39ee82..aec9c6194d 100644
> --- a/gdb/utils.h
> +++ b/gdb/utils.h
> @@ -307,6 +307,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.

Maybe mention the exception for TARGET_SYSROOT_PREFIX?

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

Should these examples include quotes, eg:

  concat_path ("/first", "second")		=> /first/second

?

Also, those examples would be nice to convert to a selftest/unittest in the
newly created unittests/utils-selftests.c.  :)

> +   */
> +
> +extern std::string _concat_path (const std::initializer_list<const std::string> args,
> +				 const std::string &dir_separator);
> +
> +#define concat_path(...) _concat_path ({__VA_ARGS__}, SLASH_STRING)
> +
>  std::string ldirname (const char *filename);
>  
>  extern int count_path_elements (const char *path);
> 

Thanks,

Simon

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

* Re: [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid
  2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
@ 2018-03-17 18:08   ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2018-03-17 18:08 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

On 2018-03-12 11:31 AM, Philipp Rudo wrote:
> When a target and its target beneath use different ptids to identify a
> thread the regcaches ptid has to be set/restored when calls are passed down
> to the target beneath to e.g. fetch_registers.  Add a scoped_restore to
> simplify this.

Though I don't understand yet why this is needed (I'll find out when reading
the following patches), the implementation looks ok.

I don't think this is an issue (at least for now), but there is this observer
"thread_ptid_changed" that indicates when a thread changes ptid.  If this
observer is triggered while we have temporarily changed a regcache's ptid,
I guess we could miss updating a regcache's ptid, as
scoped_restore_regcache_ptid will reset it to the old ptid.  It's probably
not a problem but I think it's good to be aware of this.

Simon

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

* Re: [RFC PATCH v5 1/9] Convert substitute_path_component to C++
  2018-03-16  2:15   ` Simon Marchi
@ 2018-03-17 20:11     ` Simon Marchi
  0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2018-03-17 20:11 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

On 2018-03-15 10:15 PM, Simon Marchi wrote:
>> diff --git a/gdb/auto-load.c b/gdb/auto-load.c
>> index 70bddbc862..a7f9635252 100644
>> --- a/gdb/auto-load.c
>> +++ b/gdb/auto-load.c
>> @@ -175,21 +175,18 @@ std::vector<gdb::unique_xmalloc_ptr<char>> auto_load_safe_path_vec;
>>     substitute_path_component.  */
>>  
>>  static std::vector<gdb::unique_xmalloc_ptr<char>>
>> -auto_load_expand_dir_vars (const char *string)
>> +auto_load_expand_dir_vars (const std::string &string)
> 
> All the usages of auto_load_expand_dir_vars pass in a char pointer.  This means that
> a temporary std::string is created for the duration of the call (one copy) and another
> one is done lower.  I would suggest either to leave the parameter as const char * to
> avoid that copy.

I stumbled on the discussion you had with Pedro on the v1 patchset:

  https://sourceware.org/ml/gdb-patches/2017-01/msg00210.html

so I thought it would be good to expand a little bit.  A quote from that thread:

> Thus you should only use one kind of string through out GDB, either char * or
> std::string. And as GDB decided to move to C++ for me std::string is the way you
> should go.

I don't think we should be so dogmatic.  There are times where a "const char *" is
appropriate, others where std::string is appropriate.  A const std::string& is not
appropriate for a function that is potentially called with string literals, since it
will always require a copy.  As mentioned in that thread, backporting std::string_view
would be the way to go.  But until then, I think we should keep using const char *
in those cases.

Simon

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

* Re: [RFC PATCH v5 4/9] Add basic Linux kernel support
  2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
  2018-03-13 14:09   ` Kamil Rytarowski
@ 2018-03-19  0:11   ` Simon Marchi
  1 sibling, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2018-03-19  0:11 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez

On 2018-03-12 11:31 AM, Philipp Rudo wrote:
> Implement the basic infrastructure and functionality to allow Linux kernel
> debugging with GDB.  This contains handling of kernel symbols and data
> structures as well as a simple target_ops to hook into GDB.  For the code
> to work architectures must provide an implementation for the virtual
> methods in linux_kernel_ops.
> 
> For simplicity this patch only supports static targets, i.e. core dumps.
> Support for live debugging will be provided in a separate patch.

Hi Phlipp,

I'm going back and forth trying to understand how this interacts with the rest
of GDB.  Meanwhile, could you explain a little bit how this (well, the whole
patchset) is expected to be used, from the user point of view?  How do you
setup a coredump debugging session, and eventually a live remote one?  Does the
user see one or multiple inferiors?  What threads do they see, only kernel threads,
or kernel + userspace?

Here some minor/formatting things I noted while reading the code, it's not meant to
be a full review (I don't feel like I understand well enough yet), but I thought
I would share them anyway since I wrote them.

> +/* Helper function for try_declare_type.  Returns type on success or NULL on
> +   failure  */
> +
> +static struct type *
> +lk_find_type (const std::string &name)
> +{
> +  const struct block *global;
> +  const struct symbol *sym;
> +
> +  global = block_global_block(get_selected_block (0));

Missing space.

> +  sym = lookup_symbol (name.c_str (), global, STRUCT_DOMAIN, NULL).symbol;
> +  if (sym != NULL)
> +    return SYMBOL_TYPE (sym);
> +
> +  /*  Chek for "typedef struct { ... } name;"-like definitions.  */

"Check"

> +  /* True when all fiels have been parsed.  */

"all fields"

> +  bool empty () const
> +  { return m_end == std::string::npos; }
> +
> +  /* Return the depth, i.e. number of fields, in m_alias.  */
> +  unsigned int depth () const
> +  {
> +    size_t pos = m_alias.find (delim);
> +    unsigned int ret = 0;
> +
> +    while (pos != std::string::npos)
> +      {
> +	ret ++;
> +	pos = m_alias.find (delim, pos + delim.size ());
> +      }
> +
> +    return ret;
> +  }
> +
> +private:
> +  /* Alias originally passed to parser.  */
> +  std::string m_alias;
> +
> +  /* First index of current field in m_alias.  */
> +  size_t m_start = 0;
> +
> +  /* Last index of current field in m_alias.  */
> +  size_t m_end = 0;
> +
> +  /* Type of the last field found.  Needed to get s_name of embedded
> +     fields.  */
> +  struct type *m_last_type = NULL;
> +
> +  /* Delemiter used to separate fields.  */

Delimiter.

> +/* Helper functions to read and return basic types at a given ADDRess.  */
> +
> +/* Read and return the integer value at address ADDR.  */

Comments for non-static functions should be /* See lk-low.h.  */ and documented
in lk-low.h, there are a few instances.

> --- /dev/null
> +++ b/gdb/lk-low.h
> @@ -0,0 +1,335 @@
> +/* 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 <unordered_map>
> +
> +#include "gdbtypes.h"
> +#include "target.h"
> +
> +/* Copied 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
> +
> +/* 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);
> +
> +/* Enum to track the config options used to build the kernel.  Whenever
> +   a symbol is declared (in linux_kernel_ops::{arch_}read_symbols) which
> +   only exists if the kernel was built with a certain config option an entry
> +   has to be added here.  */
> +enum lk_kconfig_values
> +{
> +  LK_CONFIG_ALWAYS	= 1 << 0,
> +  LK_CONFIG_SMT		= 1 << 1,
> +  LK_CONFIG_MODULES	= 1 << 2,
> +};
> +DEF_ENUM_FLAGS_TYPE (enum lk_kconfig_values, lk_kconfig);
> +
> +/* 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;

I don't really understand the usage of the name "old_ptid".  If it's the ptid
of the target beneath, why call it "old" and not "beneath_ptid", for example?
It makes it sound like its value changed in time.

> +  /* Check whether the kernel was build using this config option.  */

"was built"

> +  /* Collection of all declared symbols (addresses, fields etc.).  */
> +  std::unordered_map<std::string, union lk_symbol> m_symbols;

Is there a reason to put all symbols in the same map?  It creates the risk of
misusing a symbol using the wrong type, e.g.:

  try_declare_address ("foo", "foo")

then later

  m_symbols.field ("foo")

Is there a reason not to use three different maps?

Thanks,

Simon

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

end of thread, other threads:[~2018-03-19  0:11 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
2018-03-13 14:09   ` Kamil Rytarowski
2018-03-14  9:48     ` Philipp Rudo
2018-03-14 23:38       ` Kamil Rytarowski
2018-03-19  0:11   ` Simon Marchi
2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
2018-03-17 18:08   ` Simon Marchi
2018-03-12 15:31 ` [RFC PATCH v5 7/9] Add privileged registers for s390x Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 6/9] Add commands for linux-kernel target Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 9/9] Add S390 support " Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 5/9] Add kernel module " Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 1/9] Convert substitute_path_component to C++ Philipp Rudo
2018-03-16  2:15   ` Simon Marchi
2018-03-17 20:11     ` Simon Marchi
2018-03-12 15:31 ` [RFC PATCH v5 2/9] Add libiberty/concat styled concat_path function Philipp Rudo
2018-03-16  2:47   ` Simon Marchi
2018-03-12 15:31 ` [RFC PATCH v5 8/9] Link frame_info to thread_info 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).