public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC 7/7] Add S390 support for linux-kernel target
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (4 preceding siblings ...)
  2017-01-12 11:32 ` [RFC 1/7] Convert substitute_path_component to C++ Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-01-12 17:09   ` Luis Machado
  2017-01-12 12:56 ` [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, yao.qi, arnez

Implement functions for the linux-kernel target hooks.

gdb/ChangeLog:

	* s390-linux-tdep.h: Define macros for address translation.
	* s390-linux-tdep.c: Include gdbthread.h and lk-low.h.
	(s390_gregmap_lk): New array.
	(s390_gregset_lk): New type.
	(s390_lk_get_registers, s390_lk_get_percpu_offset)
	(s390_lk_map_running_task_to_cpu, s390_lk_is_kvaddr)
	(s390_lk_read_table_entry, s390_lk_vtop, s390_lk_init_private): New
	function.
	(s390_gdbarch_init): Adjust.
---
 gdb/s390-linux-tdep.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/s390-linux-tdep.h |  62 ++++++++++++
 2 files changed, 332 insertions(+)

diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 054c6d5..f436a74 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -29,6 +29,7 @@
 #include "target.h"
 #include "gdbcore.h"
 #include "gdbcmd.h"
+#include "gdbthread.h"
 #include "objfiles.h"
 #include "floatformat.h"
 #include "regcache.h"
@@ -49,6 +50,8 @@
 #include "auxv.h"
 #include "xml-syscall.h"
 
+#include "lk-low.h"
+
 #include "stap-probe.h"
 #include "ax.h"
 #include "ax-gdb.h"
@@ -783,6 +786,14 @@ static const struct regcache_map_entry s390_gregmap[] =
     { 0 }
   };
 
+static const struct regcache_map_entry s390_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_regmap_cr [] =
   {
     { 16, S390_CR0_REGNUM, 8 },
@@ -891,6 +902,12 @@ const struct regset s390_gregset = {
   regcache_collect_regset
 };
 
+const struct regset s390_gregset_lk = {
+  s390_gregmap_lk,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
 const struct regset s390_cr_regset = {
   s390_regmap_cr,
   regcache_supply_regset,
@@ -1080,6 +1097,256 @@ s390_core_read_description (struct gdbarch *gdbarch,
     }
 }
 
+/* Funktion for Linux kernel target get_registers hook.  Supplies gprs for
+   task TASK to REGCACHE.  Uses r14 (back jump address) as current pswa.  */
+
+void
+s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
+		       struct regcache *regcache, int regnum)
+{
+  const struct regset *regset;
+  CORE_ADDR ksp, gprs, pswa;
+  gdb_byte *buf;
+  size_t size;
+  struct cleanup *old_chain;
+
+  regset = &s390_gregset_lk;
+
+  ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
+		      + LK_OFFSET (thread_struct, ksp));
+  gprs = ksp + LK_OFFSET (stack_frame, gprs);
+  size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
+
+  buf = XCNEWVEC (gdb_byte, size);
+  old_chain = make_cleanup (xfree, buf);
+  read_memory (gprs, buf, size);
+  regset->supply_regset (regset, regcache, -1, buf, size);
+  do_cleanups (old_chain);
+}
+
+/* Function for Linux kernel target get_percpu_offset hook. Returns the
+   percpu_offset from lowcore for cpu CPU.  */
+
+CORE_ADDR
+s390_lk_get_percpu_offset (int cpu)
+{
+  CORE_ADDR lowcore_ptr, lowcore;
+  int ptr_len = lk_builtin_type_size (unsigned_long);
+
+  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
+  lowcore = lk_read_addr (lowcore_ptr);
+
+  return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset));
+}
+
+/* Function for Linux kernel target map_running_task_to_cpu hook.  */
+
+int
+s390_lk_map_running_task_to_cpu (struct thread_info *ti)
+{
+  struct regcache *regcache;
+  enum register_status reg_status;
+  CORE_ADDR lowcore;
+  int cpu;
+
+  regcache = get_thread_regcache (ti->ptid);
+  reg_status = regcache_raw_read_unsigned (regcache, S390_PREFIX_REGNUM,
+					   (ULONGEST *) &lowcore);
+  if (reg_status != REG_VALID)
+    error (_("Could not find prefix register for thread with pid %d, lwp %li."),
+	   ti->ptid.pid, ti->ptid.lwp);
+
+  cpu = lk_read_int (lowcore + LK_OFFSET (lowcore, cpu_nr));
+
+  return cpu;
+}
+
+/* Funktion for Linux kernel target is_kvaddr hook.  */
+
+int
+s390_lk_is_kvaddr (CORE_ADDR addr)
+{
+  return addr >= LK_ADDR (high_memory);
+}
+
+/* Read table entry from TABLE at offset OFFSET.  Helper for s390_lk_vtop.  */
+
+static inline ULONGEST
+s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
+{
+  return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
+}
+
+/* Function for Linux kernel target vtop hook.  Assume 64 bit addresses.  */
+
+CORE_ADDR
+s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
+{
+  ULONGEST entry, offset;
+  CORE_ADDR paddr;
+  int table_type;
+
+  /* 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 %#lx with empty region-first-table entry."),
+		   vaddr);
+	  else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
+	    warning (_("Trying to translate address %#lx with corrupt table type in region-first-table entry."),
+		     vaddr);
+	  else if (entry & S390_LK_RFTE_I)
+	    warning (_("Translating address %#lx with invalid bit set at region-first-table entry."),
+		     vaddr);
+
+	  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 %#lx with empty region-second-table entry."),
+		   vaddr);
+	  else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
+	    warning (_("Trying to translate address %#lx with corrupt table type in region-second-table entry."),
+		     vaddr);
+	  else if (entry & S390_LK_RSTE_I)
+	    warning (_("Translating address %#lx with invalid bit set at region-second-table entry."),
+		     vaddr);
+
+	  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 %#lx with empty region-third-table entry."),
+		   vaddr);
+	  else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
+	    warning (_("Trying to translate address %#lx with corrupt table type in region-third-table entry."),
+		     vaddr);
+	  else if (entry & S390_LK_RTTE_I)
+	    warning (_("Translating address %#lx with invalid bit set at region-third-table entry."),
+		     vaddr);
+
+	  /* Check for huge page.  */
+	  if (entry & S390_LK_RTTE_FC)
+	    {
+	      paddr = ((entry & S390_LK_RTTE_RFAA)
+		       + (vaddr & ~S390_LK_RTTE_RFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_RTTE_O;
+	}
+	/* fall through */
+      case S390_LK_DAT_TT_SEGMENT:
+	{
+	  offset = (vaddr & S390_LK_VADDR_SX) >> 20;
+	  entry = s390_lk_read_table_entry (table, offset);
+
+	  /* Do sanity checks.  */
+	  if (!entry)
+	    warning (_("Trying to translate address %#lx with empty segment-table entry."),
+		   vaddr);
+	  else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
+	    warning (_("Trying to translate address %#lx with corrupt table type in segment-table entry."),
+		     vaddr);
+	  else if (entry & S390_LK_STE_I)
+	    warning (_("Translating address %#lx with invalid bit set at segment-table entry."),
+		     vaddr);
+
+	  /* Check for large page.  */
+	  if (entry & S390_LK_STE_FC)
+	    {
+	      paddr = ((entry & S390_LK_STE_SFAA)
+		       + (vaddr & ~S390_LK_STE_SFAA));
+	      return paddr;
+	    }
+
+	  table = entry & S390_LK_STE_O;
+	  break;
+	}
+    } /* switch (table_type) */
+
+  offset = (vaddr & S390_LK_VADDR_PX) >> 12;
+  entry = s390_lk_read_table_entry (table, offset);
+
+  /* Do sanity checks.  */
+  if (!entry)
+    warning (_("Trying to translate address %#lx with empty page-table entry."),
+	     vaddr);
+  else if (entry & S390_LK_PTE_I)
+    warning (_("Translating address %#lx with invalid bit set at page-table entry."),
+	     vaddr);
+
+  paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA));
+
+  return paddr;
+}
+
+/* Function for Linux kernel target get_module_text_offset hook.  */
+
+CORE_ADDR
+s390_lk_get_module_text_offset (CORE_ADDR mod)
+{
+  CORE_ADDR offset, mod_arch;
+
+  mod_arch = mod + LK_OFFSET (module, arch);
+  offset = lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, got_size));
+  offset += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
+
+  return offset;
+}
+
+/* Initialize s390 dependent private data for linux kernel target.  */
+void
+s390_lk_init_private (struct gdbarch *gdbarch)
+{
+  LK_DECLARE_FIELD (stack_frame, gprs);
+
+  LK_DECLARE_FIELD (thread_struct, ksp);
+
+  LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
+  LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
+  if (LK_STRUCT (lowcore) == NULL)
+    error (_("Could not find struct lowcore. Abort."));
+  LK_DECLARE_FIELD (lowcore, percpu_offset);
+  LK_DECLARE_FIELD (lowcore, current_pid);
+  LK_DECLARE_FIELD (lowcore, cpu_nr);
+
+  LK_DECLARE_FIELD (mod_arch_specific, got_size);
+  LK_DECLARE_FIELD (mod_arch_specific, plt_size);
+
+  LK_DECLARE_ADDR (lowcore_ptr);
+  LK_DECLARE_ADDR (high_memory);
+
+  LK_HOOK->get_registers = s390_lk_get_registers;
+  LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
+  LK_HOOK->vtop = s390_lk_vtop;
+  LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
+  LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
+  LK_HOOK->get_module_text_offset = s390_lk_get_module_text_offset;
+}
+
 
 /* Decoding S/390 instructions.  */
 
@@ -8220,6 +8487,9 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_process_record (gdbarch, s390_process_record);
   set_gdbarch_process_record_signal (gdbarch, s390_linux_record_signal);
 
+  /* Support linux kernel debugging.  */
+  set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
+
   s390_init_linux_record_tdep (&s390_linux_record_tdep, ABI_LINUX_S390);
   s390_init_linux_record_tdep (&s390x_linux_record_tdep, ABI_LINUX_ZSERIES);
 
diff --git a/gdb/s390-linux-tdep.h b/gdb/s390-linux-tdep.h
index fdb3c3d..8a16b0b 100644
--- a/gdb/s390-linux-tdep.h
+++ b/gdb/s390-linux-tdep.h
@@ -238,4 +238,66 @@ extern struct target_desc *tdesc_s390x_te_linux64;
 extern struct target_desc *tdesc_s390x_vx_linux64;
 extern struct target_desc *tdesc_s390x_tevx_linux64;
 
+/* 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-Validy 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-Validy Control */
+#define S390_LK_STE_O    ~0x7ffULL	/* Page-Table Origin */
+#define S390_LK_STE_SFAA ~0xfffffULL	/* Segment-Frame Absolute Address */
+
+/* Page-Table flags.  */
+#define S390_LK_PTE_P     0x200ULL	/* DAT-Protection Bit */
+#define S390_LK_PTE_I     0x400ULL	/* Page-Invalid Bit */
+#define S390_LK_PTE_PFAA ~0xfffULL	/* Page-Frame Absolute Address */
+
+/* Virtual Address Fields.  */
+#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
+#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
+#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
+#define S390_LK_VADDR_SX  0x000000007ff00000ULL
+#define S390_LK_VADDR_PX  0x00000000000ff000ULL
+#define S390_LK_VADDR_BX  0x0000000000000fffULL
+
 #endif
-- 
2.8.4

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

* [RFC 5/7] Add commands for linux-kernel target
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-01-12 11:32 ` [RFC 3/7] Add basic Linux kernel support Philipp Rudo
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, 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.
    (COMMON_OBS): Add lk-cmds.o.
---
 gdb/Makefile.in |   4 +
 gdb/lk-cmds.c   | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-cmds.h   |  25 ++++++
 gdb/lk-low.c    |   3 +
 gdb/typeprint.c |   8 +-
 gdb/typeprint.h |   2 +
 6 files changed, 283 insertions(+), 7 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 be8a91d..10dce6a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1092,6 +1092,7 @@ SFILES = \
 	jit.c \
 	language.c \
 	linespec.c \
+	lk-cmds.c \
 	lk-lists.c \
 	lk-low.c \
 	lk-modules.c \
@@ -1341,6 +1342,7 @@ HFILES_NO_SRCDIR = \
 	linux-nat.h \
 	linux-record.h \
 	linux-tdep.h \
+	lk-cmds.h \
 	lk-lists.h \
 	lk-low.h \
 	lk-modules.h \
@@ -1702,6 +1704,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	jit.o \
 	language.o \
 	linespec.o \
+	lk-cmds.o \
 	lk-lists.o \
 	lk-modules.o \
 	location.o \
@@ -2535,6 +2538,7 @@ ALLDEPFILES = \
 	linux-fork.c \
 	linux-record.c \
 	linux-tdep.c \
+	lk-cmds.c \
 	lk-lists.c \
 	lk-low.c \
 	lk-modules.c \
diff --git a/gdb/lk-cmds.c b/gdb/lk-cmds.c
new file mode 100644
index 0000000..a47aad9
--- /dev/null
+++ b/gdb/lk-cmds.c
@@ -0,0 +1,248 @@
+/* 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 "lk-lists.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "typeprint.h"
+#include "valprint.h"
+
+
+/* Print line for module MOD to UIOUT for lsmod command.  */
+
+static bool
+lk_lsmod_print_single_module (struct ui_out *uiout, CORE_ADDR mod)
+{
+  char *src_list, name[LK_MODULE_NAME_LEN + 2];
+  CORE_ADDR next, src_list_addr;
+  size_t src_num, src_size, list_len;
+  bool loaded;
+  struct cleanup *ui_chain;
+
+
+  /* Get name.  */
+  read_memory_string (mod + LK_OFFSET (module, name), name + 1,
+		      LK_MODULE_NAME_LEN);
+  loaded = lk_modules_debug_info_loaded (name + 1);
+  name[0] = loaded ? ' ' : '*' ;
+  name[LK_MODULE_NAME_LEN + 1] = '\0';
+
+  /* Get size.  */
+  if (LK_FIELD (module, module_core))
+    {
+      src_size = lk_read_uint (mod + LK_OFFSET (module, init_size));
+      src_size += lk_read_uint (mod + LK_OFFSET (module, core_size));
+    }
+  else
+    {
+      src_size = lk_read_uint (mod + LK_OFFSET (module, init_layout)
+			       + LK_OFFSET (module_layout, size));
+      src_size += lk_read_uint (mod + LK_OFFSET (module, core_layout)
+				+ LK_OFFSET (module_layout, size));
+    }
+
+  /* Get number of sources and list of their names.  */
+  src_num = 0;
+  src_list_addr = mod + LK_OFFSET (module, source_list);
+  src_list = xstrdup ("");
+  list_len = 0;
+
+  lk_list_for_each (next, src_list_addr, module, source_list)
+    {
+      char src_name[LK_MODULE_NAME_LEN + 1];
+      CORE_ADDR src_mod, src_addr;
+
+      src_addr = (LK_CONTAINER_OF (next, module_use, source_list)
+		  + LK_OFFSET (module_use, source));
+      src_mod = lk_read_addr (src_addr);
+      read_memory_string (src_mod + LK_OFFSET (module, name), src_name,
+			  LK_MODULE_NAME_LEN);
+
+      /* 2 = strlen (", ").  */
+      list_len += strlen (src_name) + 2;
+      src_list = reconcat (src_list, src_list, src_name, ", ", NULL);
+
+      src_num++;
+    }
+  /* Remove trailing comma.  */
+  src_list [list_len - 2] = '\0';
+
+  ui_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+  uiout->field_fmt ("addr", "%#lx", mod);
+  uiout->field_string ("module", name);
+  uiout->field_int ("size", src_size);
+  uiout->field_int ("src_num", src_num);
+  uiout->field_string ("src_list", src_list);
+  uiout->text ("\n");
+
+  do_cleanups (ui_chain);
+  return loaded;
+}
+
+/* Print information about loaded kernel modules.  Output equivalent to
+   lsmod, but also prints the address of the corrensponfing struct module.
+   Marks modules with missing debug info with an asterix '*'.  */
+
+void
+lk_lsmod (char *args, int from_tty)
+{
+  struct ui_out *uiout;
+  struct cleanup *ui_chain;
+  CORE_ADDR modules, next;
+  bool all_loaded = true;
+
+  uiout = current_uiout;
+  ui_chain = make_cleanup_ui_out_table_begin_end (uiout, 5, -1,
+						  "ModuleTable");
+  uiout->table_header (14, ui_left, "addr", "ADDR");
+  uiout->table_header (20, ui_left, "module", "Module");
+  uiout->table_header (7, ui_right, "size", "Size");
+  uiout->table_header (4, ui_right, "src_num", "");
+  uiout->table_header (40, ui_left, "src_list", "Used by");
+
+  uiout->table_body ();
+
+  modules = LK_ADDR (modules);
+  lk_list_for_each (next, modules, module, list)
+    {
+      CORE_ADDR mod;
+      mod = LK_CONTAINER_OF (next, module, list);
+      all_loaded &= lk_lsmod_print_single_module (uiout, mod);
+    }
+  if (!all_loaded)
+    uiout->text ("(*) Missing debug info for module.\n");
+
+  do_cleanups (ui_chain);
+}
+
+static void
+lk_print_struct (char *args_, int from_tty)
+{
+  struct format_data fmt;
+  size_t pos;
+  print_command_parse_format ((const char **) &args_, "print", &fmt);
+
+  if (!args_)
+    return;
+
+  std::string args (args_);
+  /* No address given default to behave like ptype.  */
+  if ((pos = args.find (" ")) == std::string::npos)
+    {
+      args = "struct " + args;
+      char *tmp = xstrdup (args.c_str ());
+      whatis_exp (tmp, 1);
+      xfree (tmp);
+      return;
+    }
+
+
+  std::string type = args.substr (0, pos);
+  std::string addr = args.substr (args.find_first_not_of (" ", pos));
+
+  if ((pos = type.find ("."))!= std::string::npos)
+    {
+      std::string field = type.substr (pos + 1);
+      type = type.substr (0, pos);
+      args = "((struct " + type + " *) " + addr + ")->" + field;
+    }
+  else if ((pos = type.find ("->"))!= std::string::npos)
+    {
+      std::string field = type.substr (pos + 2);
+      type = type.substr (0, pos);
+      args = "((struct " + type + " *) " + addr + ")->" + field;
+    }
+  else
+    args = "*(struct " + type + " *) " + addr;
+
+  expression_up expr = parse_expression (args.c_str ());
+  struct value *val = evaluate_expression (expr.get ());
+
+  print_value (val, &fmt);
+}
+
+#include "gdbtypes.h"
+void
+lk_print_offset (char *args_, int from_tty)
+{
+  std::string args (args_);
+  std::string type, field;
+  size_t pos;
+
+  if ((pos = args.find ('.')) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 1);
+    }
+  else if ((pos = args.find ("->")) != std::string::npos)
+    {
+      type = "struct " + args.substr (0, pos);
+      field = args.substr (pos + 2);
+    }
+  else
+    return;
+
+  expression_up expr = parse_expression (type.c_str ());
+  struct type *tp = value_type (evaluate_type (expr.get ()));
+
+  struct field *first = TYPE_FIELDS (tp);
+  struct field *last = first + TYPE_NFIELDS (tp);
+
+  for (; first != last; first++)
+    if (field.compare (first->name) == 0)
+      break;
+
+  if (first == last)
+    return;
+
+  size_t offset = FIELD_BITPOS (*first);
+
+  if (offset % TARGET_CHAR_BIT)
+    printf_unfiltered ("offset = %lu + %lu\n", offset / 8, offset % TARGET_CHAR_BIT);
+  else
+    printf_unfiltered ("offset = %lu\n", offset / 8);
+}
+
+void
+lk_init_cmds ()
+{
+  add_com ("lsmod", class_vars, lk_lsmod, "\n\
+	lsmod\n\n\
+List kernel modules as known by the kernel.  The address belongs to the \
+corresponding struct module.  \
+");
+
+  add_com ("struct", class_vars, lk_print_struct, "\n\
+	struct <struct>.<field> <address>\n\n\
+Print content of field <field> in structure <struct> for structure located\n\
+at address <address>.  If no field is given prints entire content of\n\
+<struct>.  If neither <field> nor <address> is given prints type definition\n\
+of <struct> (equivalent to ptype).\
+");
+
+  add_com ("offset", class_vars, lk_print_offset, "\n\
+	offset <struct>.<field>\n\n\
+Print offset of field <field> in structure <struct> in byte (+ bit for bit fields).\n\
+");
+}
diff --git a/gdb/lk-cmds.h b/gdb/lk-cmds.h
new file mode 100644
index 0000000..63e4246
--- /dev/null
+++ b/gdb/lk-cmds.h
@@ -0,0 +1,25 @@
+/* Commands for Linux kernel target.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_CMDS_H__
+#define __LK_CMDS_H__
+
+extern void lk_init_cmds ();
+
+#endif /* __LK_CMDS_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index 81ff61c..4211825 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -27,6 +27,7 @@
 #include "gdbthread.h"
 #include "gdbtypes.h"
 #include "inferior.h"
+#include "lk-cmds.h"
 #include "lk-lists.h"
 #include "lk-low.h"
 #include "lk-modules.h"
@@ -825,6 +826,8 @@ lk_try_push_target ()
   if (!target_is_pushed (linux_kernel_ops))
     push_target (linux_kernel_ops);
 
+  lk_init_cmds ();
+
   set_solib_ops (gdbarch, lk_modules_so_ops);
 }
 
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 56e993e..f30f5dc 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -39,12 +39,6 @@
 
 extern void _initialize_typeprint (void);
 
-static void ptype_command (char *, int);
-
-static void whatis_command (char *, int);
-
-static void whatis_exp (char *, int);
-
 const struct type_print_options type_print_raw_options =
 {
   1,				/* raw */
@@ -396,7 +390,7 @@ type_to_string (struct type *type)
 /* Print type of EXP, or last thing in value history if EXP == NULL.
    show is passed to type_print.  */
 
-static void
+void
 whatis_exp (char *exp, int show)
 {
   struct value *val;
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 72da7f4..4cbe189 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -78,4 +78,6 @@ extern void val_print_not_allocated (struct ui_file *stream);
 
 extern void val_print_not_associated (struct ui_file *stream);
 
+extern void whatis_exp (char *exp, int show);
+
 #endif
-- 
2.8.4

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

* [RFC 0/7] Support for Linux kernel debugging
@ 2017-01-12 11:32 Philipp Rudo
  2017-01-12 11:32 ` [RFC 5/7] Add commands for linux-kernel target Philipp Rudo
                   ` (8 more replies)
  0 siblings, 9 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, yao.qi, arnez

Hi Peter
Hi everybody else

This series implements a new Linux kernel target very similar to Peters
patch sent shortly before Christmas [1]. In contrast to Peter, i
concentrated on core dumps on S390. Thus there are some differences and we
can benefit from each other.

The series's structure is the following. Patch 1 and 2 contain small,
general changes independent of the actual target which are a pre-req for the
rest. Patch 3 contains the basic target with the same functionality of
Peters patch. Patch 4 contains the implementation for module handling and
kernel virtual addresses. Patch 5 contains some commands as a byproduct of
development. Its main purpose is to start a discussion how (C++/Python) and
where (GDB/kernel) commands for the target schould be implemented. Finally
patch 6 and 7 contain the S390 specific code with patch 7 containing the logic
needed for the target.  The patches apply to and compile with the current
master. You need --enable-targets=all
to compile.

While the basic structure is very similar i made some different design
decisions compared to Peter. Most notably i store the needed private data in
a libiberty/htab (shouldn't be much of a problem to move to
std::unordered_map) with the variables name as key and its address/struct
type/struct field as value. Thus it is closer to the approach using
std::array Yao suggested [2].

In addition i also have a implementation to handle kernel modules. Together
with this goes the handling of kernel virtual addresses which i quite like.
Unfortunately generalizing this handling to any kind of virtual address
would need a mechanism to pass the address space to target_xfer_partial.

The biggest drawback of my design currently is the mapping between the CPUs
and the task_strucs of the running tasks. Currently the mapping is static,
i.e. it is generated once at initialization and cannot be updated. Knowing
this weakness i discussed quite long with Andreas how to improve it. In this
context we also discussed the reavenscar-approach Peter is using. In the end
we decided against this approach. In particular we discussed a scenario when
you also stack a userspace target on top of the kernel target. In this case
you would have three different views on threads (hardware, kernel,
userspace). With the ravenscar-approach this scenario is impossible to
implement as the private_thread_info is already occupied by the kernel
target and the userspace would have no chance to use it. Furthermore you
would like to have the possibility to switch between different views, i.e.
see what the kernel and userspace view on a given process is. The idea to
solve this is to move the global thread_list to the target and allow mapping
between the threads of the different list. Thus every target can manage its
own threads. Furthermore there needs to be a way to allow several targets at
the same time and a way for the user to switch between them. Of course this
idea would need some work to implement ...

Unfortunately there is no way to test the code unless you have access to an
S390 machine. But if you like and give me some time i can try to make an
dump available to you. Cross debugging shouldn't be a problem.

Any kind of feedback is welcome.

Kind regards
Philipp

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

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

 gdb/Makefile.in                       |  16 +
 gdb/auto-load.c                       |  18 +-
 gdb/common/common-utils.h             |  16 +-
 gdb/features/Makefile                 |  11 +-
 gdb/features/s390-cr.xml              |  26 +
 gdb/features/s390x-cr-linux64.c       |  99 ++++
 gdb/features/s390x-cr-linux64.xml     |  24 +
 gdb/features/s390x-vxcr-linux64.c     | 169 ++++++
 gdb/features/s390x-vxcr-linux64.xml   |  25 +
 gdb/gdbarch.c                         |  31 ++
 gdb/gdbarch.h                         |   8 +
 gdb/gdbarch.sh                        |   4 +
 gdb/lk-cmds.c                         | 248 +++++++++
 gdb/lk-cmds.h                         |  25 +
 gdb/lk-lists.c                        |  47 ++
 gdb/lk-lists.h                        |  56 ++
 gdb/lk-low.c                          | 964 ++++++++++++++++++++++++++++++++++
 gdb/lk-low.h                          | 330 ++++++++++++
 gdb/lk-modules.c                      | 412 +++++++++++++++
 gdb/lk-modules.h                      |  29 +
 gdb/regformats/s390x-cr-linux64.dat   |  76 +++
 gdb/regformats/s390x-vxcr-linux64.dat | 108 ++++
 gdb/s390-linux-tdep.c                 | 390 +++++++++++++-
 gdb/s390-linux-tdep.h                 |  86 ++-
 gdb/solib.c                           |   8 +
 gdb/solib.h                           |   5 +
 gdb/typeprint.c                       |   8 +-
 gdb/typeprint.h                       |   2 +
 gdb/utils.c                           |  88 ++--
 gdb/utils.h                           |  26 +-
 30 files changed, 3294 insertions(+), 61 deletions(-)
 create mode 100644 gdb/features/s390-cr.xml
 create mode 100644 gdb/features/s390x-cr-linux64.c
 create mode 100644 gdb/features/s390x-cr-linux64.xml
 create mode 100644 gdb/features/s390x-vxcr-linux64.c
 create mode 100644 gdb/features/s390x-vxcr-linux64.xml
 create mode 100644 gdb/lk-cmds.c
 create mode 100644 gdb/lk-cmds.h
 create mode 100644 gdb/lk-lists.c
 create mode 100644 gdb/lk-lists.h
 create mode 100644 gdb/lk-low.c
 create mode 100644 gdb/lk-low.h
 create mode 100644 gdb/lk-modules.c
 create mode 100644 gdb/lk-modules.h
 create mode 100644 gdb/regformats/s390x-cr-linux64.dat
 create mode 100644 gdb/regformats/s390x-vxcr-linux64.dat

-- 
2.8.4

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

* [RFC 3/7] Add basic Linux kernel support
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
  2017-01-12 11:32 ` [RFC 5/7] Add commands for linux-kernel target Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-02-07 10:54   ` Yao Qi
  2017-01-12 11:32 ` [RFC 6/7] Add privileged registers for s390x Philipp Rudo
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, yao.qi, arnez

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

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

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

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

gdb/ChangeLog:

    * gdbarch.sh (lk_init_private): New hook.
    * gdbarch.h: Regenerated.
    * gdbarch.c: Regenerated.
    * lk-low.h: New file.
    * lk-low.c: New file.
    * lk-lists.h: New file.
    * lk-lists.c: New file.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-low.c and lk-lists.c.
    (HFILES_NO_SRCDIR): Add lk-low.h and lk-lists.h.
    (ALL_TARGET_OBS): Add lk-low.o
    (COMMON_OBS): Add lk-lists.o
---
 gdb/Makefile.in |   8 +
 gdb/gdbarch.c   |  31 ++
 gdb/gdbarch.h   |   8 +
 gdb/gdbarch.sh  |   4 +
 gdb/lk-lists.c  |  47 ++++
 gdb/lk-lists.h  |  56 ++++
 gdb/lk-low.c    | 860 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-low.h    | 305 ++++++++++++++++++++
 8 files changed, 1319 insertions(+)
 create mode 100644 gdb/lk-lists.c
 create mode 100644 gdb/lk-lists.h
 create mode 100644 gdb/lk-low.c
 create mode 100644 gdb/lk-low.h

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

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

* [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (2 preceding siblings ...)
  2017-01-12 11:32 ` [RFC 6/7] Add privileged registers for s390x Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-01-12 12:00   ` Pedro Alves
  2017-01-12 11:32 ` [RFC 1/7] Convert substitute_path_component to C++ Philipp Rudo
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, 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 (startswith): Convert to C++.
	(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 | 16 +++++++++++++---
 gdb/utils.c               | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/utils.h               | 17 +++++++++++++++++
 3 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/gdb/common/common-utils.h b/gdb/common/common-utils.h
index 618d266..be3c106 100644
--- a/gdb/common/common-utils.h
+++ b/gdb/common/common-utils.h
@@ -74,13 +74,23 @@ char *savestring (const char *ptr, size_t len);
 
 extern char *safe_strerror (int);
 
-/* Return non-zero if the start of STRING matches PATTERN, zero
+/* Return non-zero if the start of STR matches PATTERN, zero
    otherwise.  */
 
 static inline int
-startswith (const char *string, const char *pattern)
+startswith (std::string str, std::string pattern)
 {
-  return strncmp (string, pattern, strlen (pattern)) == 0;
+  return (str.find (pattern) == 0);
+}
+
+/* Return non-zero if the end of STR matches PATTERN, zero
+   otherwise.  */
+
+static inline int
+endswith (std::string str, std::string pattern)
+{
+  return (str.rfind (pattern) != std::string::npos
+	  && str.rfind (pattern) == (str.length () - pattern.length ()));
 }
 
 ULONGEST strtoulst (const char *num, const char **trailer, int base);
diff --git a/gdb/utils.c b/gdb/utils.c
index ee37e49..e924b2c 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3193,6 +3193,52 @@ substitute_path_component (std::string &str, const std::string &from,
     }
 }
 
+/* Approximate length of final path.  Helper for concat_path.  */
+
+static inline unsigned long
+approx_path_length (std::initializer_list<std::string> args,
+		   std::string dir_separator)
+{
+  size_t length = 0;
+
+  for (std::string arg: args)
+      length += arg.length () + dir_separator.length ();
+
+  return length;
+}
+
+/* See utils.h.  */
+
+std::string
+_concat_path (std::initializer_list<std::string> args,
+	      std::string dir_separator)
+{
+  std::string dst;
+  dst.reserve (approx_path_length (args, dir_separator));
+
+  for (std::string arg: args)
+    {
+      if (arg.empty ())
+	continue;
+
+      if (startswith (arg, dir_separator)
+	  && endswith (dst, dir_separator))
+	dst.erase (dst.length () - dir_separator.length (),
+		   dir_separator.length ());
+
+      else if (!dst.empty ()
+	       && !startswith (arg, dir_separator)
+	       && !endswith (dst, dir_separator)
+	       && dst != TARGET_SYSROOT_PREFIX)
+	dst += dir_separator;
+
+      dst += arg;
+    }
+
+  dst.shrink_to_fit ();
+  return dst;
+}
+
 #ifdef HAVE_WAITPID
 
 #ifdef SIGALRM
diff --git a/gdb/utils.h b/gdb/utils.h
index 674f672..5d68a18 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -24,6 +24,7 @@
 #include "exceptions.h"
 #include "common/scoped_restore.h"
 #include <chrono>
+#include <string>
 
 extern void initialize_utils (void);
 
@@ -143,6 +144,22 @@ extern void substitute_path_component (std::string &str,
 				       const std::string &from,
 				       const std::string &to);
 
+/* Concatenate an arbitrary number of path elements.  Adds and removes
+   directory separators as needed.
+
+   concat_path (/first, second)		=> /first/second
+   concat_path (first, second)		=> first/second
+   concat_path (first/, second)		=> first/second
+   concat_path (first, /second)		=> first/second
+   concat_path (first/, /second)	=> first/second
+   concat_path (target:, second)	=> target:second
+   */
+
+extern std::string _concat_path (std::initializer_list<std::string> args,
+				 std::string dir_separator);
+
+#define concat_path(...) _concat_path ({__VA_ARGS__}, SLASH_STRING)
+
 char *ldirname (const char *filename);
 
 extern int count_path_elements (const char *path);
-- 
2.8.4

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

* [RFC 6/7] Add privileged registers for s390x
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
  2017-01-12 11:32 ` [RFC 5/7] Add commands for linux-kernel target Philipp Rudo
  2017-01-12 11:32 ` [RFC 3/7] Add basic Linux kernel support Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-01-12 11:32 ` [RFC 2/7] Add libiberty/concat styled concat_path function Philipp Rudo
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, yao.qi, arnez

The Linux kernel and thus the linux-kernel target needs access to S390x's
privileged registers. So add them now.

gdb/ChangeLog:

	* features/s390-cr.xml: New file.
	* features/s390x-cr-linux64.xml: New file
	* features/s390x-vxcr-linux64.xml: New file
	* features/Makefile: Add s390x-cr-linux64-expedite and
	s390x-vxcr-linux64-expedite
	(WICH): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
	(XMLTOC): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
	* features/s390x-cr-linux64.c: Generated.
	* features/s390x-vxcr-linux64.c: Generated.
	* regformats/s390x-cr-linux64.dat: Generated.
	* regformats/s390x-vxcr-linux64.dat: Generated.
	* s390-linux-tdep.h: Define regnums for control registers.
	(S390_NUM_REGS): Adjust.
	* s390-linux-tdep.c: Include features/s390x-cr-linux64.c and
	features/s390x-vxcr-linux64.c.
	(s390_dwarf_regmat): Add control registers.
	(s390_regmap_cr, s390_regmap_timer, s390_regmap_todcmp)
	(s390_regmap_todpreg, s390_regmap_prefix): New array.
	(s390_cr_regset, s390_timer_regset, s390_todcmp_regset)
	(s390_todpreg_regset, s390_prefix_regset): New type.
	(s390_iterate_over_regset_sections, s390_core_read_description)
	(s390_gdbarch_init, _initialize_s390_tdep): Adjust.
---
 gdb/features/Makefile                 |  11 ++-
 gdb/features/s390-cr.xml              |  26 ++++++
 gdb/features/s390x-cr-linux64.c       |  99 ++++++++++++++++++++
 gdb/features/s390x-cr-linux64.xml     |  24 +++++
 gdb/features/s390x-vxcr-linux64.c     | 169 ++++++++++++++++++++++++++++++++++
 gdb/features/s390x-vxcr-linux64.xml   |  25 +++++
 gdb/regformats/s390x-cr-linux64.dat   |  76 +++++++++++++++
 gdb/regformats/s390x-vxcr-linux64.dat | 108 ++++++++++++++++++++++
 gdb/s390-linux-tdep.c                 | 120 +++++++++++++++++++++++-
 gdb/s390-linux-tdep.h                 |  24 ++++-
 10 files changed, 674 insertions(+), 8 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 63f8248..284692d 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -76,6 +76,7 @@ WHICH = aarch64 \
 	s390-linux32v2 s390-linux64v2 s390x-linux64v2 \
 	s390-te-linux64 s390x-te-linux64 s390-vx-linux64 s390x-vx-linux64 \
 	s390-tevx-linux64 s390x-tevx-linux64 \
+	s390x-cr-linux64 s390x-vxcr-linux64 \
 	tic6x-c64xp tic6x-c64x tic6x-c62x \
 	tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
 
@@ -109,6 +110,8 @@ s390x-linux64v2-expedite = r14,r15,pswa
 s390x-te-linux64-expedite = r14,r15,pswa
 s390x-vx-linux64-expedite = r14,r15,pswa
 s390x-tevx-linux64-expedite = r14,r15,pswa
+s390x-cr-linux64-expedite = r14,r15,pswa
+s390x-vxcr-linux64-expedite = r14,r15,pswa
 tic6x-c64xp-expedite = A15,PC
 tic6x-c64x-expedite = A15,PC
 tic6x-c62x-expedite = A15,PC
@@ -214,14 +217,16 @@ XMLTOC = \
 	s390-linux64v1.xml \
 	s390-linux64v2.xml \
 	s390-te-linux64.xml \
+	s390-vx-linux64.xml \
+	s390-tevx-linux64.xml \
 	s390x-linux64.xml \
 	s390x-linux64v1.xml \
 	s390x-linux64v2.xml \
 	s390x-te-linux64.xml \
-	s390-tevx-linux64.xml \
-	s390-vx-linux64.xml \
-	s390x-tevx-linux64.xml \
 	s390x-vx-linux64.xml \
+	s390x-cr-linux64.xml \
+	s390x-tevx-linux64.xml \
+	s390x-vxcr-linux64.xml \
 	tic6x-c62x-linux.xml \
 	tic6x-c62x.xml \
 	tic6x-c64x-linux.xml \
diff --git a/gdb/features/s390-cr.xml b/gdb/features/s390-cr.xml
new file mode 100644
index 0000000..15ae4f9
--- /dev/null
+++ b/gdb/features/s390-cr.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.cr">
+  <reg name="cr0" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr1" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr2" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr3" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr4" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr5" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr6" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr7" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr8" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr9" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr10" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr11" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr12" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr13" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr14" bitsize="64" type="uint64" group="control"/>
+  <reg name="cr15" bitsize="64" type="uint64" group="control"/>
+</feature>
diff --git a/gdb/features/s390x-cr-linux64.c b/gdb/features/s390x-cr-linux64.c
new file mode 100644
index 0000000..caf535b
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.c
@@ -0,0 +1,99 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: s390x-cr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_cr_linux64;
+static void
+initialize_tdesc_s390x_cr_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+  tdesc_create_reg (feature, "cr0", 51, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr1", 52, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr2", 53, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr3", 54, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr4", 55, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr5", 56, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr6", 57, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr7", 58, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr8", 59, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr9", 60, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr10", 61, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr11", 62, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr12", 63, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr13", 64, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr14", 65, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr15", 66, 1, "control", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+  tdesc_create_reg (feature, "timer", 67, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todcmp", 68, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todpreg", 69, 1, "system", 32, "uint32");
+  tdesc_create_reg (feature, "prefix", 70, 1, "system", 32, "uint32");
+
+  tdesc_s390x_cr_linux64 = result;
+}
diff --git a/gdb/features/s390x-cr-linux64.xml b/gdb/features/s390x-cr-linux64.xml
new file mode 100644
index 0000000..d38bc35
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 64-bit kernel-level code.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:64-bit</architecture>
+  <xi:include href="s390x-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+  <xi:include href="s390-cr.xml"/>
+
+  <feature name="org.gnu.gdb.s390.privileged">
+    <reg name="timer" bitsize="64" type="uint64" group="system"/>
+    <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+    <reg name="todpreg" bitsize="32" type="uint32" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/features/s390x-vxcr-linux64.c b/gdb/features/s390x-vxcr-linux64.c
new file mode 100644
index 0000000..36453d2
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.c
@@ -0,0 +1,169 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: s390x-vxcr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_vxcr_linux64;
+static void
+initialize_tdesc_s390x_vxcr_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+  struct tdesc_type *field_type;
+  struct tdesc_type *type;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "pswa", 1, 1, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.vx");
+  field_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_create_vector (feature, "v4f", field_type, 4);
+
+  field_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_create_vector (feature, "v2d", field_type, 2);
+
+  field_type = tdesc_named_type (feature, "int8");
+  tdesc_create_vector (feature, "v16i8", field_type, 16);
+
+  field_type = tdesc_named_type (feature, "int16");
+  tdesc_create_vector (feature, "v8i16", field_type, 8);
+
+  field_type = tdesc_named_type (feature, "int32");
+  tdesc_create_vector (feature, "v4i32", field_type, 4);
+
+  field_type = tdesc_named_type (feature, "int64");
+  tdesc_create_vector (feature, "v2i64", field_type, 2);
+
+  type = tdesc_create_union (feature, "vec128");
+  field_type = tdesc_named_type (feature, "v4f");
+  tdesc_add_field (type, "v4_float", field_type);
+  field_type = tdesc_named_type (feature, "v2d");
+  tdesc_add_field (type, "v2_double", field_type);
+  field_type = tdesc_named_type (feature, "v16i8");
+  tdesc_add_field (type, "v16_int8", field_type);
+  field_type = tdesc_named_type (feature, "v8i16");
+  tdesc_add_field (type, "v8_int16", field_type);
+  field_type = tdesc_named_type (feature, "v4i32");
+  tdesc_add_field (type, "v4_int32", field_type);
+  field_type = tdesc_named_type (feature, "v2i64");
+  tdesc_add_field (type, "v2_int64", field_type);
+  field_type = tdesc_named_type (feature, "uint128");
+  tdesc_add_field (type, "uint128", field_type);
+
+  tdesc_create_reg (feature, "v0l", 51, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v1l", 52, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v2l", 53, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v3l", 54, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v4l", 55, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v5l", 56, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v6l", 57, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v7l", 58, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v8l", 59, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v9l", 60, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v10l", 61, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v11l", 62, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v12l", 63, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v13l", 64, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v14l", 65, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v15l", 66, 1, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "v16", 67, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v17", 68, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v18", 69, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v19", 70, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v20", 71, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v21", 72, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v22", 73, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v23", 74, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v24", 75, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v25", 76, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v26", 77, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v27", 78, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v28", 79, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v29", 80, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v30", 81, 1, NULL, 128, "vec128");
+  tdesc_create_reg (feature, "v31", 82, 1, NULL, 128, "vec128");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+  tdesc_create_reg (feature, "cr0", 83, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr1", 84, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr2", 85, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr3", 86, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr4", 87, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr5", 88, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr6", 89, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr7", 90, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr8", 91, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr9", 92, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr10", 93, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr11", 94, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr12", 95, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr13", 96, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr14", 97, 1, "control", 64, "uint64");
+  tdesc_create_reg (feature, "cr15", 98, 1, "control", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+  tdesc_create_reg (feature, "timer", 99, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todcmp", 100, 1, "system", 64, "uint64");
+  tdesc_create_reg (feature, "todpreg", 101, 1, "system", 32, "uint32");
+  tdesc_create_reg (feature, "prefix", 102, 1, "system", 32, "uint32");
+
+  tdesc_s390x_vxcr_linux64 = result;
+}
diff --git a/gdb/features/s390x-vxcr-linux64.xml b/gdb/features/s390x-vxcr-linux64.xml
new file mode 100644
index 0000000..30a1ceb
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 64-bit kernel-level code.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:64-bit</architecture>
+  <xi:include href="s390x-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+  <xi:include href="s390-vx.xml"/>
+  <xi:include href="s390-cr.xml"/>
+
+  <feature name="org.gnu.gdb.s390.privileged">
+    <reg name="timer" bitsize="64" type="uint64" group="system"/>
+    <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+    <reg name="todpreg" bitsize="32" type="uint32" group="system"/>
+    <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+  </feature>
+</target>
diff --git a/gdb/regformats/s390x-cr-linux64.dat b/gdb/regformats/s390x-cr-linux64.dat
new file mode 100644
index 0000000..e8a4dd7
--- /dev/null
+++ b/gdb/regformats/s390x-cr-linux64.dat
@@ -0,0 +1,76 @@
+# THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-cr-linux64.xml
+name:s390x_cr_linux64
+xmltarget:s390x-cr-linux64.xml
+expedite:r14,r15,pswa
+64:pswm
+64:pswa
+64:r0
+64:r1
+64:r2
+64:r3
+64:r4
+64:r5
+64:r6
+64:r7
+64:r8
+64:r9
+64:r10
+64:r11
+64:r12
+64:r13
+64:r14
+64:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+32:todpreg
+32:prefix
diff --git a/gdb/regformats/s390x-vxcr-linux64.dat b/gdb/regformats/s390x-vxcr-linux64.dat
new file mode 100644
index 0000000..8c28fd1
--- /dev/null
+++ b/gdb/regformats/s390x-vxcr-linux64.dat
@@ -0,0 +1,108 @@
+# THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-vxcr-linux64.xml
+name:s390x_vxcr_linux64
+xmltarget:s390x-vxcr-linux64.xml
+expedite:r14,r15,pswa
+64:pswm
+64:pswa
+64:r0
+64:r1
+64:r2
+64:r3
+64:r4
+64:r5
+64:r6
+64:r7
+64:r8
+64:r9
+64:r10
+64:r11
+64:r12
+64:r13
+64:r14
+64:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
+64:v0l
+64:v1l
+64:v2l
+64:v3l
+64:v4l
+64:v5l
+64:v6l
+64:v7l
+64:v8l
+64:v9l
+64:v10l
+64:v11l
+64:v12l
+64:v13l
+64:v14l
+64:v15l
+128:v16
+128:v17
+128:v18
+128:v19
+128:v20
+128:v21
+128:v22
+128:v23
+128:v24
+128:v25
+128:v26
+128:v27
+128:v28
+128:v29
+128:v30
+128:v31
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+32:todpreg
+32:prefix
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index febdd14..054c6d5 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -75,6 +75,8 @@
 #include "features/s390x-te-linux64.c"
 #include "features/s390x-vx-linux64.c"
 #include "features/s390x-tevx-linux64.c"
+#include "features/s390x-cr-linux64.c"
+#include "features/s390x-vxcr-linux64.c"
 
 #define XML_SYSCALL_FILENAME_S390 "syscalls/s390-linux.xml"
 #define XML_SYSCALL_FILENAME_S390X "syscalls/s390x-linux.xml"
@@ -219,9 +221,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,
@@ -779,6 +783,36 @@ static const struct regcache_map_entry s390_gregmap[] =
     { 0 }
   };
 
+static const struct regcache_map_entry s390_regmap_cr [] =
+  {
+    { 16, S390_CR0_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_timer [] =
+  {
+    { 1, S390_TIMER_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_todcmp [] =
+  {
+    { 1, S390_TODCMP_REGNUM, 8 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_todpreg [] =
+  {
+    { 1, S390_TODPREG_REGNUM, 4 },
+    { 0 }
+  };
+
+static const struct regcache_map_entry s390_regmap_prefix [] =
+  {
+    { 1, S390_PREFIX_REGNUM, 4 },
+    { 0 }
+  };
+
 static const struct regcache_map_entry s390_fpregmap[] =
   {
     { 1, S390_FPC_REGNUM, 8 },
@@ -857,6 +891,36 @@ const struct regset s390_gregset = {
   regcache_collect_regset
 };
 
+const struct regset s390_cr_regset = {
+  s390_regmap_cr,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390_timer_regset = {
+  s390_regmap_timer,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390_todcmp_regset = {
+  s390_regmap_todcmp,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390_todpreg_regset = {
+  s390_regmap_todpreg,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset s390_prefix_regset = {
+  s390_regmap_prefix,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
 const struct regset s390_fpregset = {
   s390_fpregmap,
   regcache_supply_regset,
@@ -951,6 +1015,22 @@ s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
       cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
 	  "s390 vector registers 16-31", cb_data);
     }
+
+  /* Privileged registers for kernel debugging.  */
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-timer"))
+    cb (".reg-s390-timer", 8, &s390_timer_regset, "s390 timer", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todcmp"))
+    cb (".reg-s390-todcmp", 8, &s390_todcmp_regset,
+	"s390 clock comperator", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-todpreg"))
+    cb (".reg-s390-todpreg", 4, &s390_todpreg_regset,
+	"s390 TOD programable register", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-ctrs"))
+    cb (".reg-s390-ctrs", 16 * 8, &s390_cr_regset,
+	"s390 control registers", cb_data);
+  if (bfd_get_section_by_name (core_bfd, ".reg-s390-prefix"))
+    cb (".reg-s390-prefix", 4, &s390_prefix_regset,
+	"s390 prefix area", cb_data);
 }
 
 static const struct target_desc *
@@ -959,7 +1039,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
 {
   asection *section = bfd_get_section_by_name (abfd, ".reg");
   CORE_ADDR hwcap = 0;
-  int high_gprs, v1, v2, te, vx;
+  int high_gprs, v1, v2, te, vx, cr;
 
   target_auxv_search (target, AT_HWCAP, &hwcap);
   if (!section)
@@ -971,6 +1051,7 @@ s390_core_read_description (struct gdbarch *gdbarch,
   v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL);
   vx = (hwcap & HWCAP_S390_VX);
   te = (hwcap & HWCAP_S390_TE);
+  cr = (bfd_get_section_by_name (abfd, ".reg-s390-ctrs") != NULL);
 
   switch (bfd_section_size (abfd, section))
     {
@@ -987,6 +1068,8 @@ s390_core_read_description (struct gdbarch *gdbarch,
 
     case s390x_sizeof_gregset:
       return (te && vx ? tdesc_s390x_tevx_linux64 :
+	      vx && cr ? tdesc_s390x_vxcr_linux64 :
+	      cr ? tdesc_s390x_cr_linux64 :
 	      vx ? tdesc_s390x_vx_linux64 :
 	      te ? tdesc_s390x_te_linux64 :
 	      v2 ? tdesc_s390x_linux64v2 :
@@ -7809,6 +7892,10 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24",
 	"v25", "v26", "v27", "v28", "v29", "v30", "v31",
       };
+      static const char *const cr[] = {
+	"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+	"cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15"
+      };
       const struct tdesc_feature *feature;
       int i, valid_p = 1;
 
@@ -7912,6 +7999,29 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	  have_vx = 1;
 	}
 
+      /* Control registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr");
+      if (feature)
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_CR0_REGNUM + i, cr[i]);
+	}
+
+      /* Privileged registers.  */
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged");
+      if (feature)
+	{
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TIMER_REGNUM, "timer");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODCMP_REGNUM, "todcmp");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_TODPREG_REGNUM, "todpreg");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      S390_PREFIX_REGNUM, "prefix");
+	}
+
       if (!valid_p)
 	{
 	  tdesc_data_cleanup (tdesc_data);
@@ -8141,4 +8251,6 @@ _initialize_s390_tdep (void)
   initialize_tdesc_s390x_te_linux64 ();
   initialize_tdesc_s390x_vx_linux64 ();
   initialize_tdesc_s390x_tevx_linux64 ();
+  initialize_tdesc_s390x_cr_linux64 ();
+  initialize_tdesc_s390x_vxcr_linux64 ();
 }
diff --git a/gdb/s390-linux-tdep.h b/gdb/s390-linux-tdep.h
index 4f818b4..fdb3c3d 100644
--- a/gdb/s390-linux-tdep.h
+++ b/gdb/s390-linux-tdep.h
@@ -166,8 +166,30 @@
 #define S390_V29_REGNUM 119
 #define S390_V30_REGNUM 120
 #define S390_V31_REGNUM 121
+/* Control registers.  */
+#define S390_TIMER_REGNUM 122
+#define S390_TODCMP_REGNUM 123
+#define S390_TODPREG_REGNUM 124
+#define S390_CR0_REGNUM 125
+#define S390_CR1_REGNUM 126
+#define S390_CR2_REGNUM 127
+#define S390_CR3_REGNUM 128
+#define S390_CR4_REGNUM 129
+#define S390_CR5_REGNUM 130
+#define S390_CR6_REGNUM 131
+#define S390_CR7_REGNUM 132
+#define S390_CR8_REGNUM 133
+#define S390_CR9_REGNUM 134
+#define S390_CR10_REGNUM 135
+#define S390_CR11_REGNUM 136
+#define S390_CR12_REGNUM 137
+#define S390_CR13_REGNUM 138
+#define S390_CR14_REGNUM 139
+#define S390_CR15_REGNUM 140
+#define S390_PREFIX_REGNUM 141
+
 /* Total.  */
-#define S390_NUM_REGS 122
+#define S390_NUM_REGS 142
 
 /* Special register usage.  */
 #define S390_SP_REGNUM S390_R15_REGNUM
-- 
2.8.4

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

* [RFC 1/7] Convert substitute_path_component to C++
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (3 preceding siblings ...)
  2017-01-12 11:32 ` [RFC 2/7] Add libiberty/concat styled concat_path function Philipp Rudo
@ 2017-01-12 11:32 ` Philipp Rudo
  2017-01-12 11:32 ` [RFC 7/7] Add S390 support for linux-kernel target Philipp Rudo
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 11:32 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, 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 | 18 +++++++++---------
 gdb/utils.c     | 50 ++++++++++++++------------------------------------
 gdb/utils.h     |  9 +++++++--
 3 files changed, 30 insertions(+), 47 deletions(-)

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

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

* Re: [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 11:32 ` [RFC 2/7] Add libiberty/concat styled concat_path function Philipp Rudo
@ 2017-01-12 12:00   ` Pedro Alves
  2017-01-12 13:33     ` Philipp Rudo
  0 siblings, 1 reply; 28+ messages in thread
From: Pedro Alves @ 2017-01-12 12:00 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: peter.griffin, yao.qi, arnez

On 01/12/2017 11:32 AM, Philipp Rudo wrote:
>  static inline int
> -startswith (const char *string, const char *pattern)
> +startswith (std::string str, std::string pattern)

NAK.

This is passing by copy, so will force unnecessary deep
string copies all over the place.

>  {
> -  return strncmp (string, pattern, strlen (pattern)) == 0;
> +  return (str.find (pattern) == 0);
> +}
> +

I.e., before, this caused 0 copies:

  startswith ("foo, "f");

After, you force a deep string copy for "foo", and another
for "f".  It's as if you wrote:

  startswith (xstrdup ("foo), xstrdup ("f"));


Also, this function is a static inline in a header so that
the compiler can see when "pattern" is a string literal, and
thus strlen can be optimized to a plain 'sizeof (pattern) - 1',
which is very frequent.

If you want to add overloads that can take "const std::string &"
for convenience, to avoid str.c_str(), that's maybe fine,
but you'd have to add all the combinations of
'const char *' x 'const std::string &' in the parameters, I
suppose.

Thanks,
Pedro Alves

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

* Re: [RFC 0/7] Support for Linux kernel debugging
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (5 preceding siblings ...)
  2017-01-12 11:32 ` [RFC 7/7] Add S390 support for linux-kernel target Philipp Rudo
@ 2017-01-12 12:56 ` Philipp Rudo
  2017-01-12 13:02 ` [RFC 4/7] Add kernel module support for linux-kernel target Philipp Rudo
  2017-01-25 18:10 ` [RFC 0/7] Support for Linux kernel debugging Peter Griffin
  8 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 12:56 UTC (permalink / raw)
  To: gdb-patches

Hi

apparently patch 4 got lost on its way to the mailing list. Lets try it
this way...

Philipp


From 16d2d2759f68475b2792a1426ddcfc2d727607ba Mon Sep 17 00:00:00 2001
From: Philipp Rudo <prudo@linux.vnet.ibm.com>
Date: Tue, 22 Nov 2016 11:19:17 +0100
Subject: [RFC 4/7] Add kernel module support for linux-kernel target

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

gdb/ChangeLog:

    * lk-modules.h: New file.
    * lk-modules.c: New file.
    * lk-low.h (lk_hook_is_kvaddr, lk_hook_vtop)
    (lk_hook_get_module_text_offset): New arch dependent hooks.
    (sturct lk_private_hooks): Add new hooks.
    (LK_MODULES_NAME_LEN, LK_UTS_NAME_LEN): New define.
    * lk-low.c (lk-modules.h): New include.
    (lk_kvtop, restore_current_target, lk_xfer_partial): New functions.
    (lk_init_private_data): Declare needed debug symbols.
    (lk_try_push_target): Assert for new hooks and set solib_ops.
    (init_linux_kernel_ops): Add implementation for to_xfer_partial.
    * solib.c (get_solib_search_path): New function.
    * solib.h (get_solib_search_path): New export.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-modules.c.
    (HFILES_NO_SRCDIR): Add lk-modules.h.
    (COMMON_OBS): Add lk-modules.o.
---
 gdb/Makefile.in  |   4 +
 gdb/lk-low.c     | 101 ++++++++++++++
 gdb/lk-low.h     |  29 +++-
 gdb/lk-modules.c | 412
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/lk-modules.h |  29 ++++ gdb/solib.c      |   8 ++
 gdb/solib.h      |   5 +
 7 files changed, 586 insertions(+), 2 deletions(-)
 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 fef5c2e..be8a91d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1094,6 +1094,7 @@ SFILES = \
 	linespec.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1342,6 +1343,7 @@ HFILES_NO_SRCDIR = \
 	linux-tdep.h \
 	lk-lists.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -1701,6 +1703,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	language.o \
 	linespec.o \
 	lk-lists.o \
+	lk-modules.o \
 	location.o \
 	m2-lang.o \
 	m2-typeprint.o \
@@ -2534,6 +2537,7 @@ ALLDEPFILES = \
 	linux-tdep.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index ee753a4..81ff61c 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -29,6 +29,7 @@
 #include "inferior.h"
 #include "lk-lists.h"
 #include "lk-low.h"
+#include "lk-modules.h"
 #include "objfiles.h"
 #include "observer.h"
 #include "solib.h"
@@ -564,6 +565,46 @@ lk_thread_name (struct target_ops *target, struct
thread_info *ti) return buf;
 }
 
+/* Translate a kernel virtual address ADDR to a physical address.  */
+
+CORE_ADDR
+lk_kvtop (CORE_ADDR addr)
+{
+  CORE_ADDR pgd = lk_read_addr (LK_ADDR (init_mm)
+				+ LK_OFFSET (mm_struct, pgd));
+  return LK_HOOK->vtop (pgd, addr);
+}
+
+/* Restore current_target to TARGET.  */
+static void
+restore_current_target (void *target)
+{
+  current_target.beneath = (struct target_ops *) target;
+}
+
+/* Function for targets to_xfer_partial hook.  */
+
+enum target_xfer_status
+lk_xfer_partial (struct target_ops *ops, enum target_object object,
+		 const char *annex, gdb_byte *readbuf,
+		 const gdb_byte *writebuf, ULONGEST offset, ULONGEST
len,
+		 ULONGEST *xfered_len)
+{
+  enum target_xfer_status ret_val;
+  struct cleanup *old_chain = make_cleanup (restore_current_target,
ops); +
+  current_target.beneath = ops->beneath;
+
+  if (LK_HOOK->is_kvaddr (offset))
+    offset = lk_kvtop (offset);
+
+  ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object,
annex,
+					    readbuf, writebuf, offset,
len,
+					    xfered_len);
+  do_cleanups (old_chain);
+  return ret_val;
+}
+
 /* Functions to initialize and free target_ops and its private data.
As well as functions for targets to_open/close/detach hooks.  */
 
@@ -599,6 +640,9 @@ lk_init_private ()
 /* Initialize architecture independent private data.  Must be called
    _after_ symbol tables were initialized.  */
 
+/* FIXME: throw error more fine-grained.  */
+/* FIXME: make independent of compile options.  */
+
 static void
 lk_init_private_data ()
 {
@@ -619,10 +663,61 @@ lk_init_private_data ()
 
   LK_DECLARE_FIELD (cpumask, bits);
 
+  LK_DECLARE_FIELD (mm_struct, pgd);
+
+  LK_DECLARE_FIELD (pgd_t, pgd);
+
+  LK_DECLARE_FIELD (module, list);
+  LK_DECLARE_FIELD (module, name);
+  LK_DECLARE_FIELD (module, source_list);
+  LK_DECLARE_FIELD (module, arch);
+  LK_DECLARE_FIELD (module, init);
+  LK_DECLARE_FIELD (module, percpu);
+  LK_DECLARE_FIELD (module, percpu_size);
+
+  /* Module offset moved to new struct module_layout with linux 4.5.
+     It must be checked in code which of this fields exist.  */
+  if (LK_DECLARE_FIELD_SILENT (module_layout, base)) /* linux 4.5+ */
+    {
+      LK_DECLARE_FIELD (module, init_layout);
+      LK_DECLARE_FIELD (module, core_layout);
+
+      LK_DECLARE_FIELD (module_layout, size);
+      LK_DECLARE_FIELD (module_layout, text_size);
+      LK_DECLARE_FIELD (module_layout, ro_size);
+    }
+  else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux
-4.4 */
+    {
+      LK_DECLARE_FIELD (module, init_size);
+      LK_DECLARE_FIELD (module, core_size);
+
+      LK_DECLARE_FIELD (module, core_text_size);
+      LK_DECLARE_FIELD (module, core_ro_size);
+    }
+  else
+    {
+      error (_("Could not find module base. Abort."));
+    }
+
+  LK_DECLARE_FIELD (module_use, source_list);
+  LK_DECLARE_FIELD (module_use, source);
+
+  LK_DECLARE_FIELD (uts_namespace, name);
+
+  LK_DECLARE_STRUCT_ALIAS (new_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (old_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (oldold_utsname, utsname);
+  if (LK_STRUCT (utsname) == NULL)
+    error (_("Could not find struct utsname. Abort."));
+  LK_DECLARE_FIELD (utsname, version);
+  LK_DECLARE_FIELD (utsname, release);
+
   LK_DECLARE_ADDR (init_task);
   LK_DECLARE_ADDR (runqueues);
   LK_DECLARE_ADDR (__per_cpu_offset);
   LK_DECLARE_ADDR (init_mm);
+  LK_DECLARE_ADDR (modules);
+  LK_DECLARE_ADDR (init_uts_ns);
 
   LK_DECLARE_ADDR_ALIAS (__cpu_online_mask,
cpu_online_mask);	/* linux 4.5+ */ LK_DECLARE_ADDR_ALIAS
(cpu_online_bits, cpu_online_mask);	/* linux -4.4 */ @@ -720,12
+815,17 @@ lk_try_push_target () gdbarch_lk_init_private (gdbarch);
   /* Check for required arch hooks.  */
   gdb_assert (LK_HOOK->get_registers);
+  gdb_assert (LK_HOOK->is_kvaddr);
+  gdb_assert (LK_HOOK->vtop);
+  gdb_assert (LK_HOOK->get_module_text_offset);
 
   lk_init_ptid_map ();
   lk_update_thread_list (linux_kernel_ops);
 
   if (!target_is_pushed (linux_kernel_ops))
     push_target (linux_kernel_ops);
+
+  set_solib_ops (gdbarch, lk_modules_so_ops);
 }
 
 /* Function for targets to_open hook.  */
@@ -838,6 +938,7 @@ init_linux_kernel_ops (void)
   t->to_update_thread_list = lk_update_thread_list;
   t->to_pid_to_str = lk_pid_to_str;
   t->to_thread_name = lk_thread_name;
+  t->to_xfer_partial = lk_xfer_partial;
 
   t->to_stratum = thread_stratum;
   t->to_magic = OPS_MAGIC;
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
index 57b7bf4..cde78b6 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -27,6 +27,8 @@ extern struct target_ops *linux_kernel_ops;
 /* Copy constants defined in Linux kernel.  */
 #define LK_TASK_COMM_LEN 16
 #define LK_BITS_PER_BYTE 8
+#define LK_MODULE_NAME_LEN 56
+#define LK_UTS_NAME_LEN 64
 
 /* Private data structs for this target.  */
 /* Forward declarations.  */
@@ -200,13 +202,27 @@ typedef void (*lk_hook_get_registers) (CORE_ADDR
task, struct regcache *regcache,
 				       int regnum);
 
+/* Hook to check if address ADDR is a kernel virtual address.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef int (*lk_hook_is_kvaddr) (CORE_ADDR addr);
+
+/* Hook to translate virtual adress ADDR to a pysical address using
page
+   table located at PGD.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef CORE_ADDR (*lk_hook_vtop) (CORE_ADDR addr, CORE_ADDR pgd);
+
+/* Hook to get the offset between a modules base and the start of its
+   .text section.  */
+
+typedef CORE_ADDR (*lk_hook_get_module_text_offset) (CORE_ADDR mod);
+
 /* Hook to return the per_cpu_offset of cpu CPU.  Only architectures
that do not use the __per_cpu_offset array to determin the offset have
to supply this hook.  */
 typedef CORE_ADDR (*lk_hook_get_percpu_offset) (int cpu);
 
-/* Hook to map a running task to a logical CPU.  Required if the
target beneath
-   uses a different PID as struct rq.  */
+/* Hook to map a running task to a logical CPU.  Required if the target
+   beneath uses a different PID as struct rq.  */
 typedef int (*lk_hook_map_running_task_to_cpu) (struct thread_info
*ti); 
 struct lk_private_hooks
@@ -214,6 +230,15 @@ struct lk_private_hooks
   /* required */
   lk_hook_get_registers get_registers;
 
+  /* required */
+  lk_hook_is_kvaddr is_kvaddr;
+
+  /* required */
+  lk_hook_vtop vtop;
+
+  /* reqired */
+  lk_hook_get_module_text_offset get_module_text_offset;
+
   /* optional, required if __per_cpu_offset array is not used to
determine offset.  */
   lk_hook_get_percpu_offset get_percpu_offset;
diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
new file mode 100644
index 0000000..8bb04d6
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,412 @@
+/* Handle Linux kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see
<http://www.gnu.org/licenses/>.  */ +
+#include "defs.h"
+
+#include "common/filestuff.h"
+#include "filenames.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_regex.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "readline/readline.h"
+#include "solib.h"
+#include "solist.h"
+#include "utils.h"
+
+#include <unordered_map>
+#include <string>
+
+struct target_so_ops *lk_modules_so_ops = NULL;
+
+/* Info for single section type.  */
+
+struct lm_info_sec
+{
+  CORE_ADDR start;
+  CORE_ADDR offset;
+  unsigned int size;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lm_info
+{
+  CORE_ADDR base;
+  unsigned int size;
+
+  struct lm_info_sec text;
+  struct lm_info_sec init_text;
+  struct lm_info_sec ro_data;
+  struct lm_info_sec rw_data;
+  struct lm_info_sec percpu;
+};
+
+/* Check if debug info for module NAME are loaded.  */
+
+bool
+lk_modules_debug_info_loaded (const std::string &name)
+{
+  struct so_list *so;
+
+  for (so = master_so_list (); so; so = so->next)
+    {
+      if (name == so->so_original_name)
+	return (so->symbols_loaded && objfile_has_symbols
(so->objfile));
+    }
+
+  return false;
+}
+
+/* Replace tags, like '$release', with corresponding data in
+   solib_search_path.
+
+   Known tags:
+   $release	Linux kernel release, same as 'uname -r'
+
+   Returns the expanded path.  */
+
+static std::string
+lk_modules_expand_search_path ()
+{
+  char release[LK_UTS_NAME_LEN + 1];
+  CORE_ADDR utsname;
+
+  utsname = LK_ADDR (init_uts_ns) + LK_OFFSET (uts_namespace, name);
+  read_memory_string (utsname + LK_OFFSET (utsname, release),
+		      release, LK_UTS_NAME_LEN);
+  release[LK_UTS_NAME_LEN] = '\0';
+
+  std::string search_path = get_solib_search_path ();
+  substitute_path_component (search_path, "$release", release);
+
+  return search_path;
+}
+
+/* With kernel modules there is the problem that the kernel only stores
+   the modules name but not the path from wich it was loaded from.
+   Thus we need to map the name to a path GDB can read from.  We use
file
+   modules.order to do so.  It is created by kbuild containing the
order in
+   which the modules appear in the Makefile and is also used by
modprobe.
+   The drawback of this method is that it needs the modules.order file
and
+   all relative paths, starting from <solib-search-path>, must be
exactly the
+   same as decribed in it.  */
+
+/* Open file <solib-search-path>/modules.order and return its file
+   pointer.  */
+
+FILE *
+lk_modules_open_mod_order ()
+{
+  FILE *mod_order;
+  std::string filename = concat_path (lk_modules_expand_search_path (),
+				      "modules.order");
+  mod_order = gdb_fopen_cloexec (filename.c_str (), "r");
+
+  if (!mod_order)
+    {
+      error (_("\
+Can not find file module.order at %s \
+to load module symbol files.\n\
+Please check if solib-search-path is set correctly."),
+	     filename.c_str ());
+    }
+
+  return mod_order;
+}
+
+/* Build map between module name and path to binary file by reading
file
+   modules.order.  Returns unordered_map with module name as key and
its
+   path as value.  */
+
+std::unordered_map<std::string, std::string>
+lk_modules_build_path_map ()
+{
+  std::unordered_map<std::string, std::string> umap;
+  FILE *mod_order;
+  struct cleanup *old_chain;
+  char line[SO_NAME_MAX_PATH_SIZE + 1];
+
+  mod_order = lk_modules_open_mod_order ();
+  old_chain = make_cleanup_fclose (mod_order);
+
+  line[SO_NAME_MAX_PATH_SIZE] = '\0';
+  std::string search_path = lk_modules_expand_search_path ();
+  while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order))
+    {
+      /* Remove trailing newline.  */
+      line[strlen (line) - 1] = '\0';
+
+      std::string name = lbasename (line);
+
+      /* 3 = strlen (".ko").  */
+      if (!endswith (name, ".ko")
+	  || name.length () >= LK_MODULE_NAME_LEN + 3)
+	continue;
+
+      name = name.substr (0, name.length () - 3);
+
+      /* Kernel modules are named after the files they are stored in
with
+	 all minus '-' replaced by underscore '_'.  Do the same to
enable
+	 mapping.  */
+      for (size_t p = name.find('-'); p != std::string::npos;
+	   p = name.find ('-', p + 1))
+	name[p] = '_';
+
+      umap[name] = concat_path(search_path, line);
+    }
+
+  do_cleanups (old_chain);
+  return umap;
+}
+
+/* Allocate and fill a copy of struct lm_info for module at address
MOD.  */ +
+struct lm_info *
+lk_modules_read_lm_info (CORE_ADDR mod)
+{
+  struct lm_info *lmi = XNEW (struct lm_info);
+  struct cleanup *old_chain = make_cleanup (xfree, lmi);
+
+  if (LK_FIELD (module, module_core)) /* linux -4.4 */
+    {
+      lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
+      lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod + LK_OFFSET (module,
core_text_size)); +
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod + LK_OFFSET (module,
+
core_ro_size));
+    }
+  else /* linux 4.5+ */
+    {
+      CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
+
+      lmi->base = lk_read_addr (mod_core
+				+ LK_OFFSET (module_layout, base));
+      lmi->size = lk_read_uint (mod_core
+				+ LK_OFFSET (module_layout, size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod_core
+				     + LK_OFFSET (module_layout,
text_size)); +
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod_core
+					 + LK_OFFSET (module_layout,
ro_size));
+    }
+
+  lmi->rw_data.start = lmi->base + lmi->ro_data.size;
+  lmi->rw_data.offset = 0;
+  lmi->rw_data.size = lmi->size - lmi->ro_data.size;
+
+  lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
+  lmi->init_text.offset = 0;
+
+  lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
+  lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module,
percpu_size));
+  lmi->percpu.offset = 0;
+
+  discard_cleanups (old_chain);
+  return lmi;
+}
+
+/* Function for current_sos hook.  */
+
+struct so_list *
+lk_modules_current_sos (void)
+{
+  CORE_ADDR modules, next;
+  FILE *mod_order;
+  struct so_list *list = NULL;
+  std::unordered_map<std::string, std::string> umap;
+
+  umap = lk_modules_build_path_map ();
+  modules = LK_ADDR (modules);
+  lk_list_for_each (next, modules, module, list)
+    {
+      char name[LK_MODULE_NAME_LEN];
+      CORE_ADDR mod, name_addr;
+
+      mod = LK_CONTAINER_OF (next, module, list);
+      name_addr = mod + LK_OFFSET (module, name);
+      read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
+
+      if (umap.count (name))
+	{
+	  struct so_list *newso = XCNEW (struct so_list);
+
+	  newso->next = list;
+	  list = newso;
+	  newso->lm_info = lk_modules_read_lm_info (mod);
+	  strncpy (newso->so_original_name, name,
SO_NAME_MAX_PATH_SIZE);
+	  strncpy (newso->so_name, umap[name].c_str (),
SO_NAME_MAX_PATH_SIZE);
+	  newso->pspace = current_program_space;
+	}
+    }
+
+  return list;
+}
+
+/* Relocate target_section SEC to section type LMI_SEC.  Helper
function for
+   lk_modules_relocate_section_addresses.  */
+
+void
+lk_modules_relocate_sec (struct target_section *sec,
+			 struct lm_info_sec *lmi_sec)
+{
+  unsigned int alignment = 1;
+
+ alignment = 1 << sec->the_bfd_section->alignment_power;
+
+  /* Adjust offset to section alignment.  */
+  if (lmi_sec->offset % alignment != 0)
+    lmi_sec->offset += alignment - (lmi_sec->offset % alignment);
+
+  sec->addr += lmi_sec->start + lmi_sec->offset;
+  sec->endaddr += lmi_sec->start + lmi_sec->offset;
+  lmi_sec->offset += sec->endaddr - sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  struct lm_info *lmi = so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  if (streq (name, ".modinfo") || streq (name, "__versions"))
+      return;
+
+  /* FIXME: Make dependent on module state, i.e. only map .init
sections if
+   * state is MODULE_STATE_COMING.  */
+  if (startswith (name, ".init"))
+    lk_modules_relocate_sec (sec, &lmi->init_text);
+  else if (endswith (name, ".percpu"))
+    lk_modules_relocate_sec (sec, &lmi->percpu);
+  else if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, &lmi->text);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, &lmi->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, &lmi->rw_data);
+
+  /* Set address range to be displayed with info shared.
+     size = text + (ro + rw) data without .init sections.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = lmi->base;
+      so->addr_high = lmi->base + lmi->size;
+    }
+}
+
+/* Function for free_so hook.  */
+
+void
+lk_modules_free_so (struct so_list *so)
+{
+  xfree (so->lm_info);
+}
+
+/* Function for clear_so hook.  */
+
+void
+lk_modules_clear_so (struct so_list *so)
+{
+  if (so->lm_info != NULL)
+    memset (so->lm_info, 0, sizeof (struct lm_info));
+}
+
+/* Function for clear_solib hook.  */
+
+void
+lk_modules_clear_solib ()
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+void
+lk_modules_create_inferior_hook (int from_tty)
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+int
+lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+  return 0;
+}
+
+/* Function for same hook.  */
+
+int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  return streq (gdb->so_name, inf->so_name);
+}
+
+/* Initialize linux modules solib target.  */
+
+void
+init_lk_modules_so_ops (void)
+{
+  struct target_so_ops *t;
+
+  if (lk_modules_so_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_so_ops);
+  t->relocate_section_addresses =
lk_modules_relocate_section_addresses;
+  t->free_so = lk_modules_free_so;
+  t->clear_so = lk_modules_clear_so;
+  t->clear_solib = lk_modules_clear_solib;
+  t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
+  t->current_sos = lk_modules_current_sos;
+  t->bfd_open = solib_bfd_open;
+  t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
+  t->same = lk_modules_same;
+
+  lk_modules_so_ops = t;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_lk_modules;
+
+void
+_initialize_lk_modules (void)
+{
+  init_lk_modules_so_ops ();
+}
diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h
new file mode 100644
index 0000000..47e6dde
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,29 @@
+/* Handle kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see
<http://www.gnu.org/licenses/>.  */ +
+#ifndef __LK_MODULES_H__
+#define __LK_MODULES_H__
+
+extern struct target_so_ops *lk_modules_so_ops;
+
+/* Check if debug info for module NAME are loaded.  Needed by lsmod
command.  */ +
+extern bool lk_modules_debug_info_loaded (const std::string &name);
+
+#endif /*  __LK_MODULES_H__  */
diff --git a/gdb/solib.c b/gdb/solib.c
index fc45133..595828a 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -107,6 +107,14 @@ show_solib_search_path (struct ui_file *file, int
from_tty, value);
 }
 
+/* see solib.h.  */
+
+const char *
+get_solib_search_path ()
+{
+  return solib_search_path ? solib_search_path : "";
+}
+
 /* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue.  */
 #if (HAVE_DOS_BASED_FILE_SYSTEM)
 #  define DOS_BASED_FILE_SYSTEM 1
diff --git a/gdb/solib.h b/gdb/solib.h
index dd07636..99b5cda 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -28,6 +28,11 @@ struct program_space;
 
 #include "symfile-add-flags.h"
 
+/* Returns the solib_search_path.  The returned string is malloc'ed
and must be
+   freed by the caller.  */
+
+extern const char *get_solib_search_path ();
+
 /* Called when we free all symtabs, to free the shared library
information as well.  */
 
-- 
2.8.4

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

* [RFC 4/7] Add kernel module support for linux-kernel target
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (6 preceding siblings ...)
  2017-01-12 12:56 ` [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
@ 2017-01-12 13:02 ` Philipp Rudo
  2017-01-25 18:10 ` [RFC 0/7] Support for Linux kernel debugging Peter Griffin
  8 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 13:02 UTC (permalink / raw)
  To: gdb-patches; +Cc: peter.griffin, yao.qi, arnez

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

gdb/ChangeLog:

    * lk-modules.h: New file.
    * lk-modules.c: New file.
    * lk-low.h (lk_hook_is_kvaddr, lk_hook_vtop)
    (lk_hook_get_module_text_offset): New arch dependent hooks.
    (sturct lk_private_hooks): Add new hooks.
    (LK_MODULES_NAME_LEN, LK_UTS_NAME_LEN): New define.
    * lk-low.c (lk-modules.h): New include.
    (lk_kvtop, restore_current_target, lk_xfer_partial): New functions.
    (lk_init_private_data): Declare needed debug symbols.
    (lk_try_push_target): Assert for new hooks and set solib_ops.
    (init_linux_kernel_ops): Add implementation for to_xfer_partial.
    * solib.c (get_solib_search_path): New function.
    * solib.h (get_solib_search_path): New export.
    * Makefile.in (SFILES, ALLDEPFILES): Add lk-modules.c.
    (HFILES_NO_SRCDIR): Add lk-modules.h.
    (COMMON_OBS): Add lk-modules.o.
---
 gdb/Makefile.in  |   4 +
 gdb/lk-low.c     | 101 ++++++++++++++
 gdb/lk-low.h     |  29 +++-
 gdb/lk-modules.c | 412 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/lk-modules.h |  29 ++++
 gdb/solib.c      |   8 ++
 gdb/solib.h      |   5 +
 7 files changed, 586 insertions(+), 2 deletions(-)
 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 fef5c2e..be8a91d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1094,6 +1094,7 @@ SFILES = \
 	linespec.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	location.c \
 	m2-exp.y \
 	m2-lang.c \
@@ -1342,6 +1343,7 @@ HFILES_NO_SRCDIR = \
 	linux-tdep.h \
 	lk-lists.h \
 	lk-low.h \
+	lk-modules.h \
 	location.h \
 	m2-lang.h \
 	m32r-tdep.h \
@@ -1701,6 +1703,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	language.o \
 	linespec.o \
 	lk-lists.o \
+	lk-modules.o \
 	location.o \
 	m2-lang.o \
 	m2-typeprint.o \
@@ -2534,6 +2537,7 @@ ALLDEPFILES = \
 	linux-tdep.c \
 	lk-lists.c \
 	lk-low.c \
+	lk-modules.c \
 	lm32-tdep.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
index ee753a4..81ff61c 100644
--- a/gdb/lk-low.c
+++ b/gdb/lk-low.c
@@ -29,6 +29,7 @@
 #include "inferior.h"
 #include "lk-lists.h"
 #include "lk-low.h"
+#include "lk-modules.h"
 #include "objfiles.h"
 #include "observer.h"
 #include "solib.h"
@@ -564,6 +565,46 @@ lk_thread_name (struct target_ops *target, struct thread_info *ti)
   return buf;
 }
 
+/* Translate a kernel virtual address ADDR to a physical address.  */
+
+CORE_ADDR
+lk_kvtop (CORE_ADDR addr)
+{
+  CORE_ADDR pgd = lk_read_addr (LK_ADDR (init_mm)
+				+ LK_OFFSET (mm_struct, pgd));
+  return LK_HOOK->vtop (pgd, addr);
+}
+
+/* Restore current_target to TARGET.  */
+static void
+restore_current_target (void *target)
+{
+  current_target.beneath = (struct target_ops *) target;
+}
+
+/* Function for targets to_xfer_partial hook.  */
+
+enum target_xfer_status
+lk_xfer_partial (struct target_ops *ops, enum target_object object,
+		 const char *annex, gdb_byte *readbuf,
+		 const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+		 ULONGEST *xfered_len)
+{
+  enum target_xfer_status ret_val;
+  struct cleanup *old_chain = make_cleanup (restore_current_target, ops);
+
+  current_target.beneath = ops->beneath;
+
+  if (LK_HOOK->is_kvaddr (offset))
+    offset = lk_kvtop (offset);
+
+  ret_val =  ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+					    readbuf, writebuf, offset, len,
+					    xfered_len);
+  do_cleanups (old_chain);
+  return ret_val;
+}
+
 /* Functions to initialize and free target_ops and its private data.  As well
    as functions for targets to_open/close/detach hooks.  */
 
@@ -599,6 +640,9 @@ lk_init_private ()
 /* Initialize architecture independent private data.  Must be called
    _after_ symbol tables were initialized.  */
 
+/* FIXME: throw error more fine-grained.  */
+/* FIXME: make independent of compile options.  */
+
 static void
 lk_init_private_data ()
 {
@@ -619,10 +663,61 @@ lk_init_private_data ()
 
   LK_DECLARE_FIELD (cpumask, bits);
 
+  LK_DECLARE_FIELD (mm_struct, pgd);
+
+  LK_DECLARE_FIELD (pgd_t, pgd);
+
+  LK_DECLARE_FIELD (module, list);
+  LK_DECLARE_FIELD (module, name);
+  LK_DECLARE_FIELD (module, source_list);
+  LK_DECLARE_FIELD (module, arch);
+  LK_DECLARE_FIELD (module, init);
+  LK_DECLARE_FIELD (module, percpu);
+  LK_DECLARE_FIELD (module, percpu_size);
+
+  /* Module offset moved to new struct module_layout with linux 4.5.
+     It must be checked in code which of this fields exist.  */
+  if (LK_DECLARE_FIELD_SILENT (module_layout, base)) /* linux 4.5+ */
+    {
+      LK_DECLARE_FIELD (module, init_layout);
+      LK_DECLARE_FIELD (module, core_layout);
+
+      LK_DECLARE_FIELD (module_layout, size);
+      LK_DECLARE_FIELD (module_layout, text_size);
+      LK_DECLARE_FIELD (module_layout, ro_size);
+    }
+  else if (LK_DECLARE_FIELD_SILENT (module, module_core)) /* linux -4.4 */
+    {
+      LK_DECLARE_FIELD (module, init_size);
+      LK_DECLARE_FIELD (module, core_size);
+
+      LK_DECLARE_FIELD (module, core_text_size);
+      LK_DECLARE_FIELD (module, core_ro_size);
+    }
+  else
+    {
+      error (_("Could not find module base. Abort."));
+    }
+
+  LK_DECLARE_FIELD (module_use, source_list);
+  LK_DECLARE_FIELD (module_use, source);
+
+  LK_DECLARE_FIELD (uts_namespace, name);
+
+  LK_DECLARE_STRUCT_ALIAS (new_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (old_utsname, utsname);
+  LK_DECLARE_STRUCT_ALIAS (oldold_utsname, utsname);
+  if (LK_STRUCT (utsname) == NULL)
+    error (_("Could not find struct utsname. Abort."));
+  LK_DECLARE_FIELD (utsname, version);
+  LK_DECLARE_FIELD (utsname, release);
+
   LK_DECLARE_ADDR (init_task);
   LK_DECLARE_ADDR (runqueues);
   LK_DECLARE_ADDR (__per_cpu_offset);
   LK_DECLARE_ADDR (init_mm);
+  LK_DECLARE_ADDR (modules);
+  LK_DECLARE_ADDR (init_uts_ns);
 
   LK_DECLARE_ADDR_ALIAS (__cpu_online_mask, cpu_online_mask);	/* linux 4.5+ */
   LK_DECLARE_ADDR_ALIAS (cpu_online_bits, cpu_online_mask);	/* linux -4.4 */
@@ -720,12 +815,17 @@ lk_try_push_target ()
   gdbarch_lk_init_private (gdbarch);
   /* Check for required arch hooks.  */
   gdb_assert (LK_HOOK->get_registers);
+  gdb_assert (LK_HOOK->is_kvaddr);
+  gdb_assert (LK_HOOK->vtop);
+  gdb_assert (LK_HOOK->get_module_text_offset);
 
   lk_init_ptid_map ();
   lk_update_thread_list (linux_kernel_ops);
 
   if (!target_is_pushed (linux_kernel_ops))
     push_target (linux_kernel_ops);
+
+  set_solib_ops (gdbarch, lk_modules_so_ops);
 }
 
 /* Function for targets to_open hook.  */
@@ -838,6 +938,7 @@ init_linux_kernel_ops (void)
   t->to_update_thread_list = lk_update_thread_list;
   t->to_pid_to_str = lk_pid_to_str;
   t->to_thread_name = lk_thread_name;
+  t->to_xfer_partial = lk_xfer_partial;
 
   t->to_stratum = thread_stratum;
   t->to_magic = OPS_MAGIC;
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
index 57b7bf4..cde78b6 100644
--- a/gdb/lk-low.h
+++ b/gdb/lk-low.h
@@ -27,6 +27,8 @@ extern struct target_ops *linux_kernel_ops;
 /* Copy constants defined in Linux kernel.  */
 #define LK_TASK_COMM_LEN 16
 #define LK_BITS_PER_BYTE 8
+#define LK_MODULE_NAME_LEN 56
+#define LK_UTS_NAME_LEN 64
 
 /* Private data structs for this target.  */
 /* Forward declarations.  */
@@ -200,13 +202,27 @@ typedef void (*lk_hook_get_registers) (CORE_ADDR task,
 				       struct regcache *regcache,
 				       int regnum);
 
+/* Hook to check if address ADDR is a kernel virtual address.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef int (*lk_hook_is_kvaddr) (CORE_ADDR addr);
+
+/* Hook to translate virtual adress ADDR to a pysical address using page
+   table located at PGD.
+   NOTE: This hook is called in the context of target beneath.  */
+typedef CORE_ADDR (*lk_hook_vtop) (CORE_ADDR addr, CORE_ADDR pgd);
+
+/* Hook to get the offset between a modules base and the start of its
+   .text section.  */
+
+typedef CORE_ADDR (*lk_hook_get_module_text_offset) (CORE_ADDR mod);
+
 /* Hook to return the per_cpu_offset of cpu CPU.  Only architectures that
    do not use the __per_cpu_offset array to determin the offset have to
    supply this hook.  */
 typedef CORE_ADDR (*lk_hook_get_percpu_offset) (int cpu);
 
-/* Hook to map a running task to a logical CPU.  Required if the target beneath
-   uses a different PID as struct rq.  */
+/* Hook to map a running task to a logical CPU.  Required if the target
+   beneath uses a different PID as struct rq.  */
 typedef int (*lk_hook_map_running_task_to_cpu) (struct thread_info *ti);
 
 struct lk_private_hooks
@@ -214,6 +230,15 @@ struct lk_private_hooks
   /* required */
   lk_hook_get_registers get_registers;
 
+  /* required */
+  lk_hook_is_kvaddr is_kvaddr;
+
+  /* required */
+  lk_hook_vtop vtop;
+
+  /* reqired */
+  lk_hook_get_module_text_offset get_module_text_offset;
+
   /* optional, required if __per_cpu_offset array is not used to determine
      offset.  */
   lk_hook_get_percpu_offset get_percpu_offset;
diff --git a/gdb/lk-modules.c b/gdb/lk-modules.c
new file mode 100644
index 0000000..8bb04d6
--- /dev/null
+++ b/gdb/lk-modules.c
@@ -0,0 +1,412 @@
+/* Handle Linux kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "common/filestuff.h"
+#include "filenames.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdb_regex.h"
+#include "lk-lists.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "readline/readline.h"
+#include "solib.h"
+#include "solist.h"
+#include "utils.h"
+
+#include <unordered_map>
+#include <string>
+
+struct target_so_ops *lk_modules_so_ops = NULL;
+
+/* Info for single section type.  */
+
+struct lm_info_sec
+{
+  CORE_ADDR start;
+  CORE_ADDR offset;
+  unsigned int size;
+};
+
+/* Link map info to include in an allocated so_list entry.  */
+
+struct lm_info
+{
+  CORE_ADDR base;
+  unsigned int size;
+
+  struct lm_info_sec text;
+  struct lm_info_sec init_text;
+  struct lm_info_sec ro_data;
+  struct lm_info_sec rw_data;
+  struct lm_info_sec percpu;
+};
+
+/* Check if debug info for module NAME are loaded.  */
+
+bool
+lk_modules_debug_info_loaded (const std::string &name)
+{
+  struct so_list *so;
+
+  for (so = master_so_list (); so; so = so->next)
+    {
+      if (name == so->so_original_name)
+	return (so->symbols_loaded && objfile_has_symbols (so->objfile));
+    }
+
+  return false;
+}
+
+/* Replace tags, like '$release', with corresponding data in
+   solib_search_path.
+
+   Known tags:
+   $release	Linux kernel release, same as 'uname -r'
+
+   Returns the expanded path.  */
+
+static std::string
+lk_modules_expand_search_path ()
+{
+  char release[LK_UTS_NAME_LEN + 1];
+  CORE_ADDR utsname;
+
+  utsname = LK_ADDR (init_uts_ns) + LK_OFFSET (uts_namespace, name);
+  read_memory_string (utsname + LK_OFFSET (utsname, release),
+		      release, LK_UTS_NAME_LEN);
+  release[LK_UTS_NAME_LEN] = '\0';
+
+  std::string search_path = get_solib_search_path ();
+  substitute_path_component (search_path, "$release", release);
+
+  return search_path;
+}
+
+/* With kernel modules there is the problem that the kernel only stores
+   the modules name but not the path from wich it was loaded from.
+   Thus we need to map the name to a path GDB can read from.  We use file
+   modules.order to do so.  It is created by kbuild containing the order in
+   which the modules appear in the Makefile and is also used by modprobe.
+   The drawback of this method is that it needs the modules.order file and
+   all relative paths, starting from <solib-search-path>, must be exactly the
+   same as decribed in it.  */
+
+/* Open file <solib-search-path>/modules.order and return its file
+   pointer.  */
+
+FILE *
+lk_modules_open_mod_order ()
+{
+  FILE *mod_order;
+  std::string filename = concat_path (lk_modules_expand_search_path (),
+				      "modules.order");
+  mod_order = gdb_fopen_cloexec (filename.c_str (), "r");
+
+  if (!mod_order)
+    {
+      error (_("\
+Can not find file module.order at %s \
+to load module symbol files.\n\
+Please check if solib-search-path is set correctly."),
+	     filename.c_str ());
+    }
+
+  return mod_order;
+}
+
+/* Build map between module name and path to binary file by reading file
+   modules.order.  Returns unordered_map with module name as key and its
+   path as value.  */
+
+std::unordered_map<std::string, std::string>
+lk_modules_build_path_map ()
+{
+  std::unordered_map<std::string, std::string> umap;
+  FILE *mod_order;
+  struct cleanup *old_chain;
+  char line[SO_NAME_MAX_PATH_SIZE + 1];
+
+  mod_order = lk_modules_open_mod_order ();
+  old_chain = make_cleanup_fclose (mod_order);
+
+  line[SO_NAME_MAX_PATH_SIZE] = '\0';
+  std::string search_path = lk_modules_expand_search_path ();
+  while (fgets (line, SO_NAME_MAX_PATH_SIZE, mod_order))
+    {
+      /* Remove trailing newline.  */
+      line[strlen (line) - 1] = '\0';
+
+      std::string name = lbasename (line);
+
+      /* 3 = strlen (".ko").  */
+      if (!endswith (name, ".ko")
+	  || name.length () >= LK_MODULE_NAME_LEN + 3)
+	continue;
+
+      name = name.substr (0, name.length () - 3);
+
+      /* Kernel modules are named after the files they are stored in with
+	 all minus '-' replaced by underscore '_'.  Do the same to enable
+	 mapping.  */
+      for (size_t p = name.find('-'); p != std::string::npos;
+	   p = name.find ('-', p + 1))
+	name[p] = '_';
+
+      umap[name] = concat_path(search_path, line);
+    }
+
+  do_cleanups (old_chain);
+  return umap;
+}
+
+/* Allocate and fill a copy of struct lm_info for module at address MOD.  */
+
+struct lm_info *
+lk_modules_read_lm_info (CORE_ADDR mod)
+{
+  struct lm_info *lmi = XNEW (struct lm_info);
+  struct cleanup *old_chain = make_cleanup (xfree, lmi);
+
+  if (LK_FIELD (module, module_core)) /* linux -4.4 */
+    {
+      lmi->base = lk_read_addr (mod + LK_OFFSET (module, module_core));
+      lmi->size = lk_read_addr (mod + LK_OFFSET (module, core_size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod + LK_OFFSET (module, core_text_size));
+
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod + LK_OFFSET (module,
+							  core_ro_size));
+    }
+  else /* linux 4.5+ */
+    {
+      CORE_ADDR mod_core = mod + LK_OFFSET (module, core_layout);
+
+      lmi->base = lk_read_addr (mod_core
+				+ LK_OFFSET (module_layout, base));
+      lmi->size = lk_read_uint (mod_core
+				+ LK_OFFSET (module_layout, size));
+
+      lmi->text.start = lmi->base;
+      lmi->text.offset = LK_HOOK->get_module_text_offset (mod);
+      lmi->text.size = lk_read_uint (mod_core
+				     + LK_OFFSET (module_layout, text_size));
+
+      lmi->ro_data.start = lmi->base + lmi->text.size;
+      lmi->ro_data.offset = 0;
+      lmi->ro_data.size =  lk_read_uint (mod_core
+					 + LK_OFFSET (module_layout, ro_size));
+    }
+
+  lmi->rw_data.start = lmi->base + lmi->ro_data.size;
+  lmi->rw_data.offset = 0;
+  lmi->rw_data.size = lmi->size - lmi->ro_data.size;
+
+  lmi->init_text.start = lk_read_addr (mod + LK_OFFSET (module, init));
+  lmi->init_text.offset = 0;
+
+  lmi->percpu.start = lk_read_addr (mod + LK_OFFSET (module, percpu));
+  lmi->percpu.size = lk_read_uint (mod + LK_OFFSET (module, percpu_size));
+  lmi->percpu.offset = 0;
+
+  discard_cleanups (old_chain);
+  return lmi;
+}
+
+/* Function for current_sos hook.  */
+
+struct so_list *
+lk_modules_current_sos (void)
+{
+  CORE_ADDR modules, next;
+  FILE *mod_order;
+  struct so_list *list = NULL;
+  std::unordered_map<std::string, std::string> umap;
+
+  umap = lk_modules_build_path_map ();
+  modules = LK_ADDR (modules);
+  lk_list_for_each (next, modules, module, list)
+    {
+      char name[LK_MODULE_NAME_LEN];
+      CORE_ADDR mod, name_addr;
+
+      mod = LK_CONTAINER_OF (next, module, list);
+      name_addr = mod + LK_OFFSET (module, name);
+      read_memory_string (name_addr, name, LK_MODULE_NAME_LEN);
+
+      if (umap.count (name))
+	{
+	  struct so_list *newso = XCNEW (struct so_list);
+
+	  newso->next = list;
+	  list = newso;
+	  newso->lm_info = lk_modules_read_lm_info (mod);
+	  strncpy (newso->so_original_name, name, SO_NAME_MAX_PATH_SIZE);
+	  strncpy (newso->so_name, umap[name].c_str (), SO_NAME_MAX_PATH_SIZE);
+	  newso->pspace = current_program_space;
+	}
+    }
+
+  return list;
+}
+
+/* Relocate target_section SEC to section type LMI_SEC.  Helper function for
+   lk_modules_relocate_section_addresses.  */
+
+void
+lk_modules_relocate_sec (struct target_section *sec,
+			 struct lm_info_sec *lmi_sec)
+{
+  unsigned int alignment = 1;
+
+ alignment = 1 << sec->the_bfd_section->alignment_power;
+
+  /* Adjust offset to section alignment.  */
+  if (lmi_sec->offset % alignment != 0)
+    lmi_sec->offset += alignment - (lmi_sec->offset % alignment);
+
+  sec->addr += lmi_sec->start + lmi_sec->offset;
+  sec->endaddr += lmi_sec->start + lmi_sec->offset;
+  lmi_sec->offset += sec->endaddr - sec->addr;
+}
+
+/* Function for relocate_section_addresses hook.  */
+
+void
+lk_modules_relocate_section_addresses (struct so_list *so,
+				       struct target_section *sec)
+{
+  struct lm_info *lmi = so->lm_info;
+  unsigned int flags = sec->the_bfd_section->flags;
+  const char *name = sec->the_bfd_section->name;
+
+  if (streq (name, ".modinfo") || streq (name, "__versions"))
+      return;
+
+  /* FIXME: Make dependent on module state, i.e. only map .init sections if
+   * state is MODULE_STATE_COMING.  */
+  if (startswith (name, ".init"))
+    lk_modules_relocate_sec (sec, &lmi->init_text);
+  else if (endswith (name, ".percpu"))
+    lk_modules_relocate_sec (sec, &lmi->percpu);
+  else if (flags & SEC_CODE)
+    lk_modules_relocate_sec (sec, &lmi->text);
+  else if (flags & SEC_READONLY)
+    lk_modules_relocate_sec (sec, &lmi->ro_data);
+  else if (flags & SEC_ALLOC)
+    lk_modules_relocate_sec (sec, &lmi->rw_data);
+
+  /* Set address range to be displayed with info shared.
+     size = text + (ro + rw) data without .init sections.  */
+  if (so->addr_low == so->addr_high)
+    {
+      so->addr_low = lmi->base;
+      so->addr_high = lmi->base + lmi->size;
+    }
+}
+
+/* Function for free_so hook.  */
+
+void
+lk_modules_free_so (struct so_list *so)
+{
+  xfree (so->lm_info);
+}
+
+/* Function for clear_so hook.  */
+
+void
+lk_modules_clear_so (struct so_list *so)
+{
+  if (so->lm_info != NULL)
+    memset (so->lm_info, 0, sizeof (struct lm_info));
+}
+
+/* Function for clear_solib hook.  */
+
+void
+lk_modules_clear_solib ()
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+void
+lk_modules_create_inferior_hook (int from_tty)
+{
+  /* Nothing to do.  */
+}
+
+/* Function for clear_create_inferior_hook hook.  */
+
+int
+lk_modules_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+  return 0;
+}
+
+/* Function for same hook.  */
+
+int
+lk_modules_same (struct so_list *gdb, struct so_list *inf)
+{
+  return streq (gdb->so_name, inf->so_name);
+}
+
+/* Initialize linux modules solib target.  */
+
+void
+init_lk_modules_so_ops (void)
+{
+  struct target_so_ops *t;
+
+  if (lk_modules_so_ops != NULL)
+    return;
+
+  t = XCNEW (struct target_so_ops);
+  t->relocate_section_addresses = lk_modules_relocate_section_addresses;
+  t->free_so = lk_modules_free_so;
+  t->clear_so = lk_modules_clear_so;
+  t->clear_solib = lk_modules_clear_solib;
+  t->solib_create_inferior_hook = lk_modules_create_inferior_hook;
+  t->current_sos = lk_modules_current_sos;
+  t->bfd_open = solib_bfd_open;
+  t->in_dynsym_resolve_code = lk_modules_in_dynsym_resolve_code;
+  t->same = lk_modules_same;
+
+  lk_modules_so_ops = t;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_lk_modules;
+
+void
+_initialize_lk_modules (void)
+{
+  init_lk_modules_so_ops ();
+}
diff --git a/gdb/lk-modules.h b/gdb/lk-modules.h
new file mode 100644
index 0000000..47e6dde
--- /dev/null
+++ b/gdb/lk-modules.h
@@ -0,0 +1,29 @@
+/* Handle kernel modules as shared libraries.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __LK_MODULES_H__
+#define __LK_MODULES_H__
+
+extern struct target_so_ops *lk_modules_so_ops;
+
+/* Check if debug info for module NAME are loaded.  Needed by lsmod command.  */
+
+extern bool lk_modules_debug_info_loaded (const std::string &name);
+
+#endif /*  __LK_MODULES_H__  */
diff --git a/gdb/solib.c b/gdb/solib.c
index fc45133..595828a 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -107,6 +107,14 @@ show_solib_search_path (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* see solib.h.  */
+
+const char *
+get_solib_search_path ()
+{
+  return solib_search_path ? solib_search_path : "";
+}
+
 /* Same as HAVE_DOS_BASED_FILE_SYSTEM, but useable as an rvalue.  */
 #if (HAVE_DOS_BASED_FILE_SYSTEM)
 #  define DOS_BASED_FILE_SYSTEM 1
diff --git a/gdb/solib.h b/gdb/solib.h
index dd07636..99b5cda 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -28,6 +28,11 @@ struct program_space;
 
 #include "symfile-add-flags.h"
 
+/* Returns the solib_search_path.  The returned string is malloc'ed and must be
+   freed by the caller.  */
+
+extern const char *get_solib_search_path ();
+
 /* Called when we free all symtabs, to free the shared library information
    as well.  */
 
-- 
2.8.4

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

* Re: [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 12:00   ` Pedro Alves
@ 2017-01-12 13:33     ` Philipp Rudo
  2017-01-12 13:48       ` Pedro Alves
  0 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 13:33 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

Hi Pedro,

i see your point. 

My goal here was to get rid of any C-string. While making this patch i
also wanted to use it to get rid of all those

concat (path, need_dirsep ? SLASH_STRING : "", NULL)

or

strcat (path, "/")
strcat (path, file)

constructs. I gave up when it repeatedly caused memory leaks and use
after free errors because of the mixture of C and C++ strings. Fixing
them made the code less readable than before. 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. Even when it costs performance.

Just my 0.02$
Philipp


On Thu, 12 Jan 2017 12:00:22 +0000
Pedro Alves <palves@redhat.com> wrote:

> On 01/12/2017 11:32 AM, Philipp Rudo wrote:
> >  static inline int
> > -startswith (const char *string, const char *pattern)
> > +startswith (std::string str, std::string pattern)  
> 
> NAK.
> 
> This is passing by copy, so will force unnecessary deep
> string copies all over the place.
> 
> >  {
> > -  return strncmp (string, pattern, strlen (pattern)) == 0;
> > +  return (str.find (pattern) == 0);
> > +}
> > +  
> 
> I.e., before, this caused 0 copies:
> 
>   startswith ("foo, "f");
> 
> After, you force a deep string copy for "foo", and another
> for "f".  It's as if you wrote:
> 
>   startswith (xstrdup ("foo), xstrdup ("f"));
> 
> 
> Also, this function is a static inline in a header so that
> the compiler can see when "pattern" is a string literal, and
> thus strlen can be optimized to a plain 'sizeof (pattern) - 1',
> which is very frequent.
> 
> If you want to add overloads that can take "const std::string &"
> for convenience, to avoid str.c_str(), that's maybe fine,
> but you'd have to add all the combinations of
> 'const char *' x 'const std::string &' in the parameters, I
> suppose.
> 
> Thanks,
> Pedro Alves
> 

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

* Re: [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 13:33     ` Philipp Rudo
@ 2017-01-12 13:48       ` Pedro Alves
  2017-01-12 15:09         ` Philipp Rudo
  0 siblings, 1 reply; 28+ messages in thread
From: Pedro Alves @ 2017-01-12 13:48 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

On 01/12/2017 01:33 PM, Philipp Rudo wrote:
> Hi Pedro,
> 
> i see your point. 
> 
> My goal here was to get rid of any C-string. While making this patch i
> also wanted to use it to get rid of all those
> 
> concat (path, need_dirsep ? SLASH_STRING : "", NULL)
> 
> or
> 
> strcat (path, "/")
> strcat (path, file)
> 
> constructs. I gave up when it repeatedly caused memory leaks and use
> after free errors because of the mixture of C and C++ strings. Fixing
> them made the code less readable than before. 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. 

Even if we used std::string throughout, we should still be careful
with unnecessary string copying.  "std::string" vs "const string &"
in function parameters (use the former only when the function already
needs to work with a copy).  

Similarly, please don't write:

+  for (std::string arg: args)
+    {

Please write instead:

 for (const std::string &arg : args)

Or 

 for (const auto &arg : args)

"for (std::string arg: args)" creates/destroys
one deep string copy on each iteration.

I hope it's obvious that I'm all for C++ conversion, but ...

> Even when it costs performance.

... not at any cost.  startswith _is_ used in performance
critical paths.

BTW, I've been thinking that we may want to add our version
of C++17 std::string_view to avoid these kinds of problems.

Thanks,
Pedro Alves

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

* Re: [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 13:48       ` Pedro Alves
@ 2017-01-12 15:09         ` Philipp Rudo
  2017-01-12 15:42           ` Pedro Alves
  0 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-12 15:09 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

On Thu, 12 Jan 2017 13:48:03 +0000
Pedro Alves <palves@redhat.com> wrote:

> On 01/12/2017 01:33 PM, Philipp Rudo wrote:
> > Hi Pedro,
> > 
> > i see your point. 
> > 
> > My goal here was to get rid of any C-string. While making this
> > patch i also wanted to use it to get rid of all those
> > 
> > concat (path, need_dirsep ? SLASH_STRING : "", NULL)
> > 
> > or
> > 
> > strcat (path, "/")
> > strcat (path, file)
> > 
> > constructs. I gave up when it repeatedly caused memory leaks and use
> > after free errors because of the mixture of C and C++ strings.
> > Fixing them made the code less readable than before. 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.   
> 
> Even if we used std::string throughout, we should still be careful
> with unnecessary string copying.  "std::string" vs "const string &"
> in function parameters (use the former only when the function already
> needs to work with a copy).  

You are right here.

> Similarly, please don't write:
> 
> +  for (std::string arg: args)
> +    {
> 
> Please write instead:
> 
>  for (const std::string &arg : args)
> 
> Or 
> 
>  for (const auto &arg : args)
> 
> "for (std::string arg: args)" creates/destroys
> one deep string copy on each iteration.

Thanks for the tip. I will check my patches again.
 
> I hope it's obvious that I'm all for C++ conversion, but ...

its quite obvious that you are pro C++. My own feelings are quite
mixed. I must amid that the standard library is quite handy, at least
when it works. Debugging it is quite a pain. But the syntax sometimes
is unreadable, especially when you use those teplates containing
templates using types is different namespaces.
Furthermore i don't think that simply porting GDB to C++ solves its
major problem that there are no clear structures in code. Although it
could help if you not only port it one to one but restructure the code
while you work at it. But that would requires quite some work...

> > Even when it costs performance.  
> 
> ... not at any cost.  startswith _is_ used in performance
> critical paths.
>
> BTW, I've been thinking that we may want to add our version
> of C++17 std::string_view to avoid these kinds of problems.

As much as i know about C++ this sounds like a good idea.

Thanks
Philipp

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

* Re: [RFC 2/7] Add libiberty/concat styled concat_path function
  2017-01-12 15:09         ` Philipp Rudo
@ 2017-01-12 15:42           ` Pedro Alves
  0 siblings, 0 replies; 28+ messages in thread
From: Pedro Alves @ 2017-01-12 15:42 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

On 01/12/2017 03:08 PM, Philipp Rudo wrote:
> its quite obvious that you are pro C++. My own feelings are quite
> mixed. I must amid that the standard library is quite handy, at least
> when it works. Debugging it is quite a pain.

There's definitely lots of scope for making C++ debugging less painful.
I think 2017 will see good advancements here.  For example, with the
palves/cp-linespec branch on my github, setting breakpoints in C++ methods
is soooooo much easier, mainly because I've made linespec tab completion
Just Work.  The whole "compile" feature for C++ (making use of g++ for
the parsing, via the libcc1 plugin) should be making it upstream this year.
Etc.

Keep in mind that a significant (if not the largest) chunk of
our users is using GDB to debug their C++ code too.  So in a sense,
any pain we now may feel, a good chunk of our users have been feeling
for a long while.  We just hadn't been dogfooding.

> But the syntax sometimes
> is unreadable, especially when you use those teplates containing
> templates using types is different namespaces.

OTOH, stepping through C code that emulates templates using
C #defines OTOH is just plain impossible, since all of it
is compiled down to a single source line...  So in that sense,
I think debugging C++ is better than C here.

It also helps if you're using a distro that installs pretty printers
for the standard library correctly.  Fedora does.  Ubuntu didn't use
to, but I don't know the current state.

Thanks,
Pedro Alves

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

* Re: [RFC 7/7] Add S390 support for linux-kernel target
  2017-01-12 11:32 ` [RFC 7/7] Add S390 support for linux-kernel target Philipp Rudo
@ 2017-01-12 17:09   ` Luis Machado
  2017-01-13 11:46     ` Philipp Rudo
  2017-02-06 15:52     ` Yao Qi
  0 siblings, 2 replies; 28+ messages in thread
From: Luis Machado @ 2017-01-12 17:09 UTC (permalink / raw)
  To: Philipp Rudo, gdb-patches; +Cc: peter.griffin, yao.qi, arnez

On 01/12/2017 05:32 AM, Philipp Rudo wrote:
> Implement functions for the linux-kernel target hooks.
>
> gdb/ChangeLog:
>
> 	* s390-linux-tdep.h: Define macros for address translation.
> 	* s390-linux-tdep.c: Include gdbthread.h and lk-low.h.
> 	(s390_gregmap_lk): New array.
> 	(s390_gregset_lk): New type.
> 	(s390_lk_get_registers, s390_lk_get_percpu_offset)
> 	(s390_lk_map_running_task_to_cpu, s390_lk_is_kvaddr)
> 	(s390_lk_read_table_entry, s390_lk_vtop, s390_lk_init_private): New
> 	function.
> 	(s390_gdbarch_init): Adjust.
> ---
>  gdb/s390-linux-tdep.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  gdb/s390-linux-tdep.h |  62 ++++++++++++
>  2 files changed, 332 insertions(+)
>
> diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
> index 054c6d5..f436a74 100644
> --- a/gdb/s390-linux-tdep.c
> +++ b/gdb/s390-linux-tdep.c

I wonder if we should put the new lk-specific code in its own file, like 
s390-lk-tdep.c or something. It would make thing a bit more clear and 
would pollute the current s390-linux-tdep.c file?

> @@ -29,6 +29,7 @@
>  #include "target.h"
>  #include "gdbcore.h"
>  #include "gdbcmd.h"
> +#include "gdbthread.h"
>  #include "objfiles.h"
>  #include "floatformat.h"
>  #include "regcache.h"
> @@ -49,6 +50,8 @@
>  #include "auxv.h"
>  #include "xml-syscall.h"
>
> +#include "lk-low.h"
> +
>  #include "stap-probe.h"
>  #include "ax.h"
>  #include "ax-gdb.h"
> @@ -783,6 +786,14 @@ static const struct regcache_map_entry s390_gregmap[] =
>      { 0 }
>    };
>
> +static const struct regcache_map_entry s390_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_regmap_cr [] =
>    {
>      { 16, S390_CR0_REGNUM, 8 },
> @@ -891,6 +902,12 @@ const struct regset s390_gregset = {
>    regcache_collect_regset
>  };
>
> +const struct regset s390_gregset_lk = {
> +  s390_gregmap_lk,
> +  regcache_supply_regset,
> +  regcache_collect_regset
> +};
> +
>  const struct regset s390_cr_regset = {
>    s390_regmap_cr,
>    regcache_supply_regset,
> @@ -1080,6 +1097,256 @@ s390_core_read_description (struct gdbarch *gdbarch,
>      }
>  }
>
> +/* Funktion for Linux kernel target get_registers hook.  Supplies gprs for

"Function..."

> +   task TASK to REGCACHE.  Uses r14 (back jump address) as current pswa.  */
> +
> +void
> +s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
> +		       struct regcache *regcache, int regnum)
> +{
> +  const struct regset *regset;
> +  CORE_ADDR ksp, gprs, pswa;
> +  gdb_byte *buf;
> +  size_t size;
> +  struct cleanup *old_chain;
> +
> +  regset = &s390_gregset_lk;
> +
> +  ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
> +		      + LK_OFFSET (thread_struct, ksp));
> +  gprs = ksp + LK_OFFSET (stack_frame, gprs);
> +  size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
> +

Do we have a reasonable MAX for this size? If so, we may try to have a 
static array instead of allocating and deallocating in such a short 
time. Just a suggestion.

> +  buf = XCNEWVEC (gdb_byte, size);
> +  old_chain = make_cleanup (xfree, buf);
> +  read_memory (gprs, buf, size);
> +  regset->supply_regset (regset, regcache, -1, buf, size);
> +  do_cleanups (old_chain);
> +}
> +
> +/* Function for Linux kernel target get_percpu_offset hook. Returns the

Two spaces after period.

> +   percpu_offset from lowcore for cpu CPU.  */
> +
> +CORE_ADDR
> +s390_lk_get_percpu_offset (int cpu)

Is cpu ever negative? If not, make it unsigned?

> +{
> +  CORE_ADDR lowcore_ptr, lowcore;
> +  int ptr_len = lk_builtin_type_size (unsigned_long);
> +
> +  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
> +  lowcore = lk_read_addr (lowcore_ptr);
> +
> +  return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset));
> +}
> +
> +/* Function for Linux kernel target map_running_task_to_cpu hook.  */
> +
> +int
> +s390_lk_map_running_task_to_cpu (struct thread_info *ti)
> +{
> +  struct regcache *regcache;
> +  enum register_status reg_status;
> +  CORE_ADDR lowcore;
> +  int cpu;
> +
> +  regcache = get_thread_regcache (ti->ptid);
> +  reg_status = regcache_raw_read_unsigned (regcache, S390_PREFIX_REGNUM,
> +					   (ULONGEST *) &lowcore);
> +  if (reg_status != REG_VALID)
> +    error (_("Could not find prefix register for thread with pid %d, lwp %li."),
> +	   ti->ptid.pid, ti->ptid.lwp);
> +
> +  cpu = lk_read_int (lowcore + LK_OFFSET (lowcore, cpu_nr));
> +
> +  return cpu;
> +}
> +
> +/* Funktion for Linux kernel target is_kvaddr hook.  */

"Function..."

> +
> +int
> +s390_lk_is_kvaddr (CORE_ADDR addr)
> +{
> +  return addr >= LK_ADDR (high_memory);
> +}
> +
> +/* Read table entry from TABLE at offset OFFSET.  Helper for s390_lk_vtop.  */
> +
> +static inline ULONGEST
> +s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
> +{
> +  return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
> +}
> +
> +/* Function for Linux kernel target vtop hook.  Assume 64 bit addresses.  */
> +
> +CORE_ADDR
> +s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
> +{
> +  ULONGEST entry, offset;
> +  CORE_ADDR paddr;
> +  int table_type;
> +
> +  /* 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 %#lx with empty region-first-table entry."),
> +		   vaddr);

Use paddress (...) to print the hex address of vaddr or phex (...). More 
occurrences of this throughout the patch.

> +	  else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
> +	    warning (_("Trying to translate address %#lx with corrupt table type in region-first-table entry."),
> +		     vaddr);
> +	  else if (entry & S390_LK_RFTE_I)
> +	    warning (_("Translating address %#lx with invalid bit set at region-first-table entry."),
> +		     vaddr);
> +
> +	  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 %#lx with empty region-second-table entry."),
> +		   vaddr);
> +	  else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
> +	    warning (_("Trying to translate address %#lx with corrupt table type in region-second-table entry."),
> +		     vaddr);
> +	  else if (entry & S390_LK_RSTE_I)
> +	    warning (_("Translating address %#lx with invalid bit set at region-second-table entry."),
> +		     vaddr);
> +
> +	  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 %#lx with empty region-third-table entry."),
> +		   vaddr);
> +	  else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
> +	    warning (_("Trying to translate address %#lx with corrupt table type in region-third-table entry."),
> +		     vaddr);
> +	  else if (entry & S390_LK_RTTE_I)
> +	    warning (_("Translating address %#lx with invalid bit set at region-third-table entry."),
> +		     vaddr);
> +
> +	  /* Check for huge page.  */
> +	  if (entry & S390_LK_RTTE_FC)
> +	    {
> +	      paddr = ((entry & S390_LK_RTTE_RFAA)
> +		       + (vaddr & ~S390_LK_RTTE_RFAA));
> +	      return paddr;
> +	    }
> +
> +	  table = entry & S390_LK_RTTE_O;
> +	}
> +	/* fall through */
> +      case S390_LK_DAT_TT_SEGMENT:
> +	{
> +	  offset = (vaddr & S390_LK_VADDR_SX) >> 20;
> +	  entry = s390_lk_read_table_entry (table, offset);
> +
> +	  /* Do sanity checks.  */
> +	  if (!entry)
> +	    warning (_("Trying to translate address %#lx with empty segment-table entry."),
> +		   vaddr);
> +	  else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
> +	    warning (_("Trying to translate address %#lx with corrupt table type in segment-table entry."),
> +		     vaddr);
> +	  else if (entry & S390_LK_STE_I)
> +	    warning (_("Translating address %#lx with invalid bit set at segment-table entry."),
> +		     vaddr);
> +
> +	  /* Check for large page.  */

Is large different from huge?

> +	  if (entry & S390_LK_STE_FC)
> +	    {
> +	      paddr = ((entry & S390_LK_STE_SFAA)
> +		       + (vaddr & ~S390_LK_STE_SFAA));
> +	      return paddr;
> +	    }
> +
> +	  table = entry & S390_LK_STE_O;
> +	  break;
> +	}
> +    } /* switch (table_type) */
> +
> +  offset = (vaddr & S390_LK_VADDR_PX) >> 12;
> +  entry = s390_lk_read_table_entry (table, offset);
> +
> +  /* Do sanity checks.  */
> +  if (!entry)
> +    warning (_("Trying to translate address %#lx with empty page-table entry."),
> +	     vaddr);
> +  else if (entry & S390_LK_PTE_I)
> +    warning (_("Translating address %#lx with invalid bit set at page-table entry."),
> +	     vaddr);
> +
> +  paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA));
> +
> +  return paddr;
> +}
> +
> +/* Function for Linux kernel target get_module_text_offset hook.  */
> +
> +CORE_ADDR
> +s390_lk_get_module_text_offset (CORE_ADDR mod)
> +{
> +  CORE_ADDR offset, mod_arch;
> +
> +  mod_arch = mod + LK_OFFSET (module, arch);
> +  offset = lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, got_size));
> +  offset += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size));
> +
> +  return offset;
> +}
> +
> +/* Initialize s390 dependent private data for linux kernel target.  */

Newline between comment and function declaration.

> +void
> +s390_lk_init_private (struct gdbarch *gdbarch)
> +{
> +  LK_DECLARE_FIELD (stack_frame, gprs);
> +
> +  LK_DECLARE_FIELD (thread_struct, ksp);
> +
> +  LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
> +  LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
> +  if (LK_STRUCT (lowcore) == NULL)
> +    error (_("Could not find struct lowcore. Abort."));

Two spaces after period. Also, "Aborting" instead of "Abort"?

> +  LK_DECLARE_FIELD (lowcore, percpu_offset);
> +  LK_DECLARE_FIELD (lowcore, current_pid);
> +  LK_DECLARE_FIELD (lowcore, cpu_nr);
> +
> +  LK_DECLARE_FIELD (mod_arch_specific, got_size);
> +  LK_DECLARE_FIELD (mod_arch_specific, plt_size);
> +
> +  LK_DECLARE_ADDR (lowcore_ptr);
> +  LK_DECLARE_ADDR (high_memory);
> +
> +  LK_HOOK->get_registers = s390_lk_get_registers;
> +  LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
> +  LK_HOOK->vtop = s390_lk_vtop;
> +  LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
> +  LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu;
> +  LK_HOOK->get_module_text_offset = s390_lk_get_module_text_offset;
> +}
> +
>
>  /* Decoding S/390 instructions.  */
>
> @@ -8220,6 +8487,9 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_process_record (gdbarch, s390_process_record);
>    set_gdbarch_process_record_signal (gdbarch, s390_linux_record_signal);
>
> +  /* Support linux kernel debugging.  */
> +  set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
> +
>    s390_init_linux_record_tdep (&s390_linux_record_tdep, ABI_LINUX_S390);
>    s390_init_linux_record_tdep (&s390x_linux_record_tdep, ABI_LINUX_ZSERIES);
>
> diff --git a/gdb/s390-linux-tdep.h b/gdb/s390-linux-tdep.h
> index fdb3c3d..8a16b0b 100644
> --- a/gdb/s390-linux-tdep.h
> +++ b/gdb/s390-linux-tdep.h
> @@ -238,4 +238,66 @@ extern struct target_desc *tdesc_s390x_te_linux64;
>  extern struct target_desc *tdesc_s390x_vx_linux64;
>  extern struct target_desc *tdesc_s390x_tevx_linux64;
>
> +/* 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-Validy Control */

"Validy" or "Validity"?

> +#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-Validy Control */
> +#define S390_LK_STE_O    ~0x7ffULL	/* Page-Table Origin */
> +#define S390_LK_STE_SFAA ~0xfffffULL	/* Segment-Frame Absolute Address */
> +
> +/* Page-Table flags.  */
> +#define S390_LK_PTE_P     0x200ULL	/* DAT-Protection Bit */
> +#define S390_LK_PTE_I     0x400ULL	/* Page-Invalid Bit */
> +#define S390_LK_PTE_PFAA ~0xfffULL	/* Page-Frame Absolute Address */
> +
> +/* Virtual Address Fields.  */
> +#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
> +#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
> +#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
> +#define S390_LK_VADDR_SX  0x000000007ff00000ULL
> +#define S390_LK_VADDR_PX  0x00000000000ff000ULL
> +#define S390_LK_VADDR_BX  0x0000000000000fffULL
> +
>  #endif
>

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

* Re: [RFC 7/7] Add S390 support for linux-kernel target
  2017-01-12 17:09   ` Luis Machado
@ 2017-01-13 11:46     ` Philipp Rudo
  2017-02-06 15:52     ` Yao Qi
  1 sibling, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-01-13 11:46 UTC (permalink / raw)
  To: Luis Machado; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

Hi Luis


On Thu, 12 Jan 2017 11:08:59 -0600
Luis Machado <lgustavo@codesourcery.com> wrote:

> On 01/12/2017 05:32 AM, Philipp Rudo wrote:
> > Implement functions for the linux-kernel target hooks.
> >
> > gdb/ChangeLog:
> >
> > 	* s390-linux-tdep.h: Define macros for address translation.
> > 	* s390-linux-tdep.c: Include gdbthread.h and lk-low.h.
> > 	(s390_gregmap_lk): New array.
> > 	(s390_gregset_lk): New type.
> > 	(s390_lk_get_registers, s390_lk_get_percpu_offset)
> > 	(s390_lk_map_running_task_to_cpu, s390_lk_is_kvaddr)
> > 	(s390_lk_read_table_entry, s390_lk_vtop,
> > s390_lk_init_private): New function.
> > 	(s390_gdbarch_init): Adjust.
> > ---
> >  gdb/s390-linux-tdep.c | 270
> > ++++++++++++++++++++++++++++++++++++++++++++++++++
> > gdb/s390-linux-tdep.h |  62 ++++++++++++ 2 files changed, 332
> > insertions(+)
> >
> > diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
> > index 054c6d5..f436a74 100644
> > --- a/gdb/s390-linux-tdep.c
> > +++ b/gdb/s390-linux-tdep.c  
> 
> I wonder if we should put the new lk-specific code in its own file,
> like s390-lk-tdep.c or something. It would make thing a bit more
> clear and would pollute the current s390-linux-tdep.c file?

It probably is better. I'll talk to Andreas as S390 maintainer what he
prefers.
 
> > @@ -29,6 +29,7 @@
> >  #include "target.h"
> >  #include "gdbcore.h"
> >  #include "gdbcmd.h"
> > +#include "gdbthread.h"
> >  #include "objfiles.h"
> >  #include "floatformat.h"
> >  #include "regcache.h"
> > @@ -49,6 +50,8 @@
> >  #include "auxv.h"
> >  #include "xml-syscall.h"
> >
> > +#include "lk-low.h"
> > +
> >  #include "stap-probe.h"
> >  #include "ax.h"
> >  #include "ax-gdb.h"
> > @@ -783,6 +786,14 @@ static const struct regcache_map_entry
> > s390_gregmap[] = { 0 }
> >    };
> >
> > +static const struct regcache_map_entry s390_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_regmap_cr [] =
> >    {
> >      { 16, S390_CR0_REGNUM, 8 },
> > @@ -891,6 +902,12 @@ const struct regset s390_gregset = {
> >    regcache_collect_regset
> >  };
> >
> > +const struct regset s390_gregset_lk = {
> > +  s390_gregmap_lk,
> > +  regcache_supply_regset,
> > +  regcache_collect_regset
> > +};
> > +
> >  const struct regset s390_cr_regset = {
> >    s390_regmap_cr,
> >    regcache_supply_regset,
> > @@ -1080,6 +1097,256 @@ s390_core_read_description (struct gdbarch
> > *gdbarch, }
> >  }
> >
> > +/* Funktion for Linux kernel target get_registers hook.  Supplies
> > gprs for  
> 
> "Function..."

fixed.

> 
> > +   task TASK to REGCACHE.  Uses r14 (back jump address) as current
> > pswa.  */ +
> > +void
> > +s390_lk_get_registers (CORE_ADDR task, struct target_ops *target,
> > +		       struct regcache *regcache, int regnum)
> > +{
> > +  const struct regset *regset;
> > +  CORE_ADDR ksp, gprs, pswa;
> > +  gdb_byte *buf;
> > +  size_t size;
> > +  struct cleanup *old_chain;
> > +
> > +  regset = &s390_gregset_lk;
> > +
> > +  ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread)
> > +		      + LK_OFFSET (thread_struct, ksp));
> > +  gprs = ksp + LK_OFFSET (stack_frame, gprs);
> > +  size = FIELD_SIZE (LK_FIELD (stack_frame, gprs));
> > +  
> 
> Do we have a reasonable MAX for this size? If so, we may try to have
> a static array instead of allocating and deallocating in such a short 
> time. Just a suggestion.

You are right this isn't perfect. Changed it to

gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */

The number of registers never changed since there is a git history for
the kernel tree so this should be pretty save.

> > +  buf = XCNEWVEC (gdb_byte, size);
> > +  old_chain = make_cleanup (xfree, buf);
> > +  read_memory (gprs, buf, size);
> > +  regset->supply_regset (regset, regcache, -1, buf, size);
> > +  do_cleanups (old_chain);
> > +}
> > +
> > +/* Function for Linux kernel target get_percpu_offset hook.
> > Returns the  
> 
> Two spaces after period.

fixed.

> > +   percpu_offset from lowcore for cpu CPU.  */
> > +
> > +CORE_ADDR
> > +s390_lk_get_percpu_offset (int cpu)  
> 
> Is cpu ever negative? If not, make it unsigned?

No it can't be negative. While looking into it i found that there are
more variables that could be made unsigned, like the size of an bitmap.
Will fix for v2.

> > +{
> > +  CORE_ADDR lowcore_ptr, lowcore;
> > +  int ptr_len = lk_builtin_type_size (unsigned_long);
> > +
> > +  lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu);
> > +  lowcore = lk_read_addr (lowcore_ptr);
> > +
> > +  return lk_read_addr (lowcore + LK_OFFSET (lowcore,
> > percpu_offset)); +}
> > +
> > +/* Function for Linux kernel target map_running_task_to_cpu hook.
> > */ +
> > +int
> > +s390_lk_map_running_task_to_cpu (struct thread_info *ti)
> > +{
> > +  struct regcache *regcache;
> > +  enum register_status reg_status;
> > +  CORE_ADDR lowcore;
> > +  int cpu;
> > +
> > +  regcache = get_thread_regcache (ti->ptid);
> > +  reg_status = regcache_raw_read_unsigned (regcache,
> > S390_PREFIX_REGNUM,
> > +					   (ULONGEST *) &lowcore);
> > +  if (reg_status != REG_VALID)
> > +    error (_("Could not find prefix register for thread with pid
> > %d, lwp %li."),
> > +	   ti->ptid.pid, ti->ptid.lwp);
> > +
> > +  cpu = lk_read_int (lowcore + LK_OFFSET (lowcore, cpu_nr));
> > +
> > +  return cpu;
> > +}
> > +
> > +/* Funktion for Linux kernel target is_kvaddr hook.  */  
> 
> "Function..."

fixed.
 
> > +
> > +int
> > +s390_lk_is_kvaddr (CORE_ADDR addr)
> > +{
> > +  return addr >= LK_ADDR (high_memory);
> > +}
> > +
> > +/* Read table entry from TABLE at offset OFFSET.  Helper for
> > s390_lk_vtop.  */ +
> > +static inline ULONGEST
> > +s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
> > +{
> > +  return lk_read_ulong (table + offset * lk_builtin_type_size
> > (unsigned_long)); +}
> > +
> > +/* Function for Linux kernel target vtop hook.  Assume 64 bit
> > addresses.  */ +
> > +CORE_ADDR
> > +s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr)
> > +{
> > +  ULONGEST entry, offset;
> > +  CORE_ADDR paddr;
> > +  int table_type;
> > +
> > +  /* 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 %#lx with
> > empty region-first-table entry."),
> > +		   vaddr);  
> 
> Use paddress (...) to print the hex address of vaddr or phex (...).
> More occurrences of this throughout the patch.

I'm using phex (...) now. Also fixed in the other files.
 
> > +	  else if ((entry & S390_LK_RFTE_TT) >> 2 !=
> > S390_LK_DAT_TT_REGION1)
> > +	    warning (_("Trying to translate address %#lx with
> > corrupt table type in region-first-table entry."),
> > +		     vaddr);
> > +	  else if (entry & S390_LK_RFTE_I)
> > +	    warning (_("Translating address %#lx with invalid bit
> > set at region-first-table entry."),
> > +		     vaddr);
> > +
> > +	  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 %#lx with
> > empty region-second-table entry."),
> > +		   vaddr);
> > +	  else if ((entry & S390_LK_RSTE_TT) >> 2 !=
> > S390_LK_DAT_TT_REGION2)
> > +	    warning (_("Trying to translate address %#lx with
> > corrupt table type in region-second-table entry."),
> > +		     vaddr);
> > +	  else if (entry & S390_LK_RSTE_I)
> > +	    warning (_("Translating address %#lx with invalid bit
> > set at region-second-table entry."),
> > +		     vaddr);
> > +
> > +	  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 %#lx with
> > empty region-third-table entry."),
> > +		   vaddr);
> > +	  else if ((entry & S390_LK_RTTE_TT) >> 2 !=
> > S390_LK_DAT_TT_REGION3)
> > +	    warning (_("Trying to translate address %#lx with
> > corrupt table type in region-third-table entry."),
> > +		     vaddr);
> > +	  else if (entry & S390_LK_RTTE_I)
> > +	    warning (_("Translating address %#lx with invalid bit
> > set at region-third-table entry."),
> > +		     vaddr);
> > +
> > +	  /* Check for huge page.  */
> > +	  if (entry & S390_LK_RTTE_FC)
> > +	    {
> > +	      paddr = ((entry & S390_LK_RTTE_RFAA)
> > +		       + (vaddr & ~S390_LK_RTTE_RFAA));
> > +	      return paddr;
> > +	    }
> > +
> > +	  table = entry & S390_LK_RTTE_O;
> > +	}
> > +	/* fall through */
> > +      case S390_LK_DAT_TT_SEGMENT:
> > +	{
> > +	  offset = (vaddr & S390_LK_VADDR_SX) >> 20;
> > +	  entry = s390_lk_read_table_entry (table, offset);
> > +
> > +	  /* Do sanity checks.  */
> > +	  if (!entry)
> > +	    warning (_("Trying to translate address %#lx with
> > empty segment-table entry."),
> > +		   vaddr);
> > +	  else if ((entry & S390_LK_STE_TT) >> 2 !=
> > S390_LK_DAT_TT_SEGMENT)
> > +	    warning (_("Trying to translate address %#lx with
> > corrupt table type in segment-table entry."),
> > +		     vaddr);
> > +	  else if (entry & S390_LK_STE_I)
> > +	    warning (_("Translating address %#lx with invalid bit
> > set at segment-table entry."),
> > +		     vaddr);
> > +
> > +	  /* Check for large page.  */  
> 
> Is large different from huge?

In principle its the same. Its just a nomenclature to distinguish 1 MB
and 2 GB pages.

> > +	  if (entry & S390_LK_STE_FC)
> > +	    {
> > +	      paddr = ((entry & S390_LK_STE_SFAA)
> > +		       + (vaddr & ~S390_LK_STE_SFAA));
> > +	      return paddr;
> > +	    }
> > +
> > +	  table = entry & S390_LK_STE_O;
> > +	  break;
> > +	}
> > +    } /* switch (table_type) */
> > +
> > +  offset = (vaddr & S390_LK_VADDR_PX) >> 12;
> > +  entry = s390_lk_read_table_entry (table, offset);
> > +
> > +  /* Do sanity checks.  */
> > +  if (!entry)
> > +    warning (_("Trying to translate address %#lx with empty
> > page-table entry."),
> > +	     vaddr);
> > +  else if (entry & S390_LK_PTE_I)
> > +    warning (_("Translating address %#lx with invalid bit set at
> > page-table entry."),
> > +	     vaddr);
> > +
> > +  paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr &
> > ~S390_LK_PTE_PFAA)); +
> > +  return paddr;
> > +}
> > +
> > +/* Function for Linux kernel target get_module_text_offset hook.
> > */ +
> > +CORE_ADDR
> > +s390_lk_get_module_text_offset (CORE_ADDR mod)
> > +{
> > +  CORE_ADDR offset, mod_arch;
> > +
> > +  mod_arch = mod + LK_OFFSET (module, arch);
> > +  offset = lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific,
> > got_size));
> > +  offset += lk_read_ulong (mod_arch + LK_OFFSET
> > (mod_arch_specific, plt_size)); +
> > +  return offset;
> > +}
> > +
> > +/* Initialize s390 dependent private data for linux kernel
> > target.  */  
> 
> Newline between comment and function declaration.

fixed.

> > +void
> > +s390_lk_init_private (struct gdbarch *gdbarch)
> > +{
> > +  LK_DECLARE_FIELD (stack_frame, gprs);
> > +
> > +  LK_DECLARE_FIELD (thread_struct, ksp);
> > +
> > +  LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */
> > +  LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */
> > +  if (LK_STRUCT (lowcore) == NULL)
> > +    error (_("Could not find struct lowcore. Abort."));  
> 
> Two spaces after period. Also, "Aborting" instead of "Abort"?

fixed and using Aborting now. Also changed in the other files.

> > +  LK_DECLARE_FIELD (lowcore, percpu_offset);
> > +  LK_DECLARE_FIELD (lowcore, current_pid);
> > +  LK_DECLARE_FIELD (lowcore, cpu_nr);
> > +
> > +  LK_DECLARE_FIELD (mod_arch_specific, got_size);
> > +  LK_DECLARE_FIELD (mod_arch_specific, plt_size);
> > +
> > +  LK_DECLARE_ADDR (lowcore_ptr);
> > +  LK_DECLARE_ADDR (high_memory);
> > +
> > +  LK_HOOK->get_registers = s390_lk_get_registers;
> > +  LK_HOOK->is_kvaddr = s390_lk_is_kvaddr;
> > +  LK_HOOK->vtop = s390_lk_vtop;
> > +  LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset;
> > +  LK_HOOK->map_running_task_to_cpu =
> > s390_lk_map_running_task_to_cpu;
> > +  LK_HOOK->get_module_text_offset = s390_lk_get_module_text_offset;
> > +}
> > +
> >
> >  /* Decoding S/390 instructions.  */
> >
> > @@ -8220,6 +8487,9 @@ s390_gdbarch_init (struct gdbarch_info info,
> > struct gdbarch_list *arches) set_gdbarch_process_record (gdbarch,
> > s390_process_record); set_gdbarch_process_record_signal (gdbarch,
> > s390_linux_record_signal);
> >
> > +  /* Support linux kernel debugging.  */
> > +  set_gdbarch_lk_init_private (gdbarch, s390_lk_init_private);
> > +
> >    s390_init_linux_record_tdep (&s390_linux_record_tdep,
> > ABI_LINUX_S390); s390_init_linux_record_tdep
> > (&s390x_linux_record_tdep, ABI_LINUX_ZSERIES);
> >
> > diff --git a/gdb/s390-linux-tdep.h b/gdb/s390-linux-tdep.h
> > index fdb3c3d..8a16b0b 100644
> > --- a/gdb/s390-linux-tdep.h
> > +++ b/gdb/s390-linux-tdep.h
> > @@ -238,4 +238,66 @@ extern struct target_desc
> > *tdesc_s390x_te_linux64; extern struct target_desc
> > *tdesc_s390x_vx_linux64; extern struct target_desc
> > *tdesc_s390x_tevx_linux64;
> >
> > +/* 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-Validy Control */  
> 
> "Validy" or "Validity"?

Its Validity ...
fixed.

Thanks a lot
Philipp


> > +#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-Validy
> > Control */ +#define S390_LK_STE_O    ~0x7ffULL	/* Page-Table
> > Origin */ +#define S390_LK_STE_SFAA ~0xfffffULL	/*
> > Segment-Frame Absolute Address */ +
> > +/* Page-Table flags.  */
> > +#define S390_LK_PTE_P     0x200ULL	/* DAT-Protection Bit */
> > +#define S390_LK_PTE_I     0x400ULL	/* Page-Invalid Bit */
> > +#define S390_LK_PTE_PFAA ~0xfffULL	/* Page-Frame Absolute
> > Address */ +
> > +/* Virtual Address Fields.  */
> > +#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
> > +#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
> > +#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
> > +#define S390_LK_VADDR_SX  0x000000007ff00000ULL
> > +#define S390_LK_VADDR_PX  0x00000000000ff000ULL
> > +#define S390_LK_VADDR_BX  0x0000000000000fffULL
> > +
> >  #endif
> >  
> 

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

* Re: [RFC 0/7] Support for Linux kernel debugging
  2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
                   ` (7 preceding siblings ...)
  2017-01-12 13:02 ` [RFC 4/7] Add kernel module support for linux-kernel target Philipp Rudo
@ 2017-01-25 18:10 ` Peter Griffin
  2017-01-26 13:12   ` Philipp Rudo
  8 siblings, 1 reply; 28+ messages in thread
From: Peter Griffin @ 2017-01-25 18:10 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, yao.qi, arnez

Hi Philipp,

On Thu, 12 Jan 2017, Philipp Rudo wrote:

> Hi Peter
> Hi everybody else
> 
> This series implements a new Linux kernel target very similar to Peters
> patch sent shortly before Christmas [1]. In contrast to Peter, i
> concentrated on core dumps on S390. Thus there are some differences and we
> can benefit from each other.
> 
> The series's structure is the following. Patch 1 and 2 contain small,
> general changes independent of the actual target which are a pre-req for the
> rest. Patch 3 contains the basic target with the same functionality of
> Peters patch. Patch 4 contains the implementation for module handling and
> kernel virtual addresses. Patch 5 contains some commands as a byproduct of
> development. Its main purpose is to start a discussion how (C++/Python) and
> where (GDB/kernel) commands for the target schould be implemented. Finally
> patch 6 and 7 contain the S390 specific code with patch 7 containing the logic
> needed for the target.  The patches apply to and compile with the current
> master. You need --enable-targets=all
> to compile.
> 
> While the basic structure is very similar i made some different design
> decisions compared to Peter. Most notably i store the needed private data in
> a libiberty/htab (shouldn't be much of a problem to move to
> std::unordered_map) with the variables name as key and its address/struct
> type/struct field as value. Thus it is closer to the approach using
> std::array Yao suggested [2].
> 
> In addition i also have a implementation to handle kernel modules. Together
> with this goes the handling of kernel virtual addresses which i quite like.
> Unfortunately generalizing this handling to any kind of virtual address
> would need a mechanism to pass the address space to target_xfer_partial.
> 
> The biggest drawback of my design currently is the mapping between the CPUs
> and the task_strucs of the running tasks. Currently the mapping is static,
> i.e. it is generated once at initialization and cannot be updated.

Live debug of a target is the main use case we are trying to support with the
linux-kthread patches. So for us on-going thread synchronisation between
GDB and the Linux target is a key feature we need.

> Knowing
> this weakness i discussed quite long with Andreas how to improve it. In this
> context we also discussed the reavenscar-approach Peter is using. In the end
> we decided against this approach. In particular we discussed a scenario when
> you also stack a userspace target on top of the kernel target.

How do you stack a userspace target on top with a coredump?

> In this case
> you would have three different views on threads (hardware, kernel,
> userspace). With the ravenscar-approach this scenario is impossible to
> implement as the private_thread_info is already occupied by the kernel
> target and the userspace would have no chance to use it. Furthermore you
> would like to have the possibility to switch between different views, i.e.
> see what the kernel and userspace view on a given process is.

Is this a feature you are actively using today with the coredump
(stacking userspace)?

IMO we will need to converge on one 'Linux Kernel thread' layer for
both core dumps and live debug use cases and as you point out with the
current GDB threads using private_thread_info is mandatory to keep the GDB
thread list synchronised with the target Linux kernel over time.

So my preference would be until the changes you talk about below (each
target_ops being responsible for its own thread list) are implemented. If
a user wants to see a userspace view of the system, they should launch a
separate GDB session. Then the 'Linux kernel' threading layer can use
private_thread_info like linux-kthreead and ravenscar do today, and it can
be used for both core dumps and live debug use case.

As an aside, what the original ST patches on which linux-kthread is very
heavily based would do, is if halting the target and it was executing
in userspace it would: -

1) Switch MMU to kernel mapping
2) Read task_struct->mm
3) Pull in the user symbols from the root filesystem
4) Do the VM address translation.

This would then allow the backtrace to work right through from user space across
the user/kernel boundary and into the kernel.

> The idea to
> solve this is to move the global thread_list to the target and allow mapping
> between the threads of the different list. Thus every target can manage its
> own threads. Furthermore there needs to be a way to allow several targets at
> the same time and a way for the user to switch between them. Of course this
> idea would need some work to implement ...

Having each target_ops have its own thread list seems like a very neat solution.
However my preference would be to gate the 'stacking userspace' feature on
implementing this functionality, rather than the other way around.

> 
> Unfortunately there is no way to test the code unless you have access to an
> S390 machine. But if you like and give me some time i can try to make an
> dump available to you. Cross debugging shouldn't be a problem.

A s390 dump could be useful for testing. Also how are you making the dumps (via
kdump)?. As it could be useful to make an ARM dump for testing a ARM arch
layer for your patch series.

regards,

Peter.

> 
> Any kind of feedback is welcome.
> 
> Kind regards
> Philipp
> 
> [1] https://sourceware.org/ml/gdb-patches/2016-12/msg00382.html
> [2] https://sourceware.org/ml/gdb-patches/2017-01/msg00183.html
> 
> Philipp Rudo (7):
>   Convert substitute_path_component to C++
>   Add libiberty/concat styled concat_path function
>   Add basic Linux kernel support
>   Add kernel module support for linux-kernel target
>   Add commands for linux-kernel target
>   Add privileged registers for s390x
>   Add S390 support for linux-kernel target
> 
>  gdb/Makefile.in                       |  16 +
>  gdb/auto-load.c                       |  18 +-
>  gdb/common/common-utils.h             |  16 +-
>  gdb/features/Makefile                 |  11 +-
>  gdb/features/s390-cr.xml              |  26 +
>  gdb/features/s390x-cr-linux64.c       |  99 ++++
>  gdb/features/s390x-cr-linux64.xml     |  24 +
>  gdb/features/s390x-vxcr-linux64.c     | 169 ++++++
>  gdb/features/s390x-vxcr-linux64.xml   |  25 +
>  gdb/gdbarch.c                         |  31 ++
>  gdb/gdbarch.h                         |   8 +
>  gdb/gdbarch.sh                        |   4 +
>  gdb/lk-cmds.c                         | 248 +++++++++
>  gdb/lk-cmds.h                         |  25 +
>  gdb/lk-lists.c                        |  47 ++
>  gdb/lk-lists.h                        |  56 ++
>  gdb/lk-low.c                          | 964 ++++++++++++++++++++++++++++++++++
>  gdb/lk-low.h                          | 330 ++++++++++++
>  gdb/lk-modules.c                      | 412 +++++++++++++++
>  gdb/lk-modules.h                      |  29 +
>  gdb/regformats/s390x-cr-linux64.dat   |  76 +++
>  gdb/regformats/s390x-vxcr-linux64.dat | 108 ++++
>  gdb/s390-linux-tdep.c                 | 390 +++++++++++++-
>  gdb/s390-linux-tdep.h                 |  86 ++-
>  gdb/solib.c                           |   8 +
>  gdb/solib.h                           |   5 +
>  gdb/typeprint.c                       |   8 +-
>  gdb/typeprint.h                       |   2 +
>  gdb/utils.c                           |  88 ++--
>  gdb/utils.h                           |  26 +-
>  30 files changed, 3294 insertions(+), 61 deletions(-)
>  create mode 100644 gdb/features/s390-cr.xml
>  create mode 100644 gdb/features/s390x-cr-linux64.c
>  create mode 100644 gdb/features/s390x-cr-linux64.xml
>  create mode 100644 gdb/features/s390x-vxcr-linux64.c
>  create mode 100644 gdb/features/s390x-vxcr-linux64.xml
>  create mode 100644 gdb/lk-cmds.c
>  create mode 100644 gdb/lk-cmds.h
>  create mode 100644 gdb/lk-lists.c
>  create mode 100644 gdb/lk-lists.h
>  create mode 100644 gdb/lk-low.c
>  create mode 100644 gdb/lk-low.h
>  create mode 100644 gdb/lk-modules.c
>  create mode 100644 gdb/lk-modules.h
>  create mode 100644 gdb/regformats/s390x-cr-linux64.dat
>  create mode 100644 gdb/regformats/s390x-vxcr-linux64.dat
> 

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

* Re: [RFC 0/7] Support for Linux kernel debugging
  2017-01-25 18:10 ` [RFC 0/7] Support for Linux kernel debugging Peter Griffin
@ 2017-01-26 13:12   ` Philipp Rudo
  2017-02-03 17:45     ` Yao Qi
  0 siblings, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-01-26 13:12 UTC (permalink / raw)
  To: Peter Griffin; +Cc: gdb-patches, yao.qi, arnez

Hi Peter

On Wed, 25 Jan 2017 18:09:50 +0000
Peter Griffin <peter.griffin@linaro.org> wrote:

> Hi Philipp,
> 
> On Thu, 12 Jan 2017, Philipp Rudo wrote:
> 
> > Hi Peter
> > Hi everybody else
> > 
> > This series implements a new Linux kernel target very similar to
> > Peters patch sent shortly before Christmas [1]. In contrast to
> > Peter, i concentrated on core dumps on S390. Thus there are some
> > differences and we can benefit from each other.
> > 
> > The series's structure is the following. Patch 1 and 2 contain
> > small, general changes independent of the actual target which are a
> > pre-req for the rest. Patch 3 contains the basic target with the
> > same functionality of Peters patch. Patch 4 contains the
> > implementation for module handling and kernel virtual addresses.
> > Patch 5 contains some commands as a byproduct of development. Its
> > main purpose is to start a discussion how (C++/Python) and where
> > (GDB/kernel) commands for the target schould be implemented.
> > Finally patch 6 and 7 contain the S390 specific code with patch 7
> > containing the logic needed for the target.  The patches apply to
> > and compile with the current master. You need --enable-targets=all
> > to compile.
> > 
> > While the basic structure is very similar i made some different
> > design decisions compared to Peter. Most notably i store the needed
> > private data in a libiberty/htab (shouldn't be much of a problem to
> > move to std::unordered_map) with the variables name as key and its
> > address/struct type/struct field as value. Thus it is closer to the
> > approach using std::array Yao suggested [2].
> > 
> > In addition i also have a implementation to handle kernel modules.
> > Together with this goes the handling of kernel virtual addresses
> > which i quite like. Unfortunately generalizing this handling to any
> > kind of virtual address would need a mechanism to pass the address
> > space to target_xfer_partial.
> > 
> > The biggest drawback of my design currently is the mapping between
> > the CPUs and the task_strucs of the running tasks. Currently the
> > mapping is static, i.e. it is generated once at initialization and
> > cannot be updated.  
> 
> Live debug of a target is the main use case we are trying to support
> with the linux-kthread patches. So for us on-going thread
> synchronisation between GDB and the Linux target is a key feature we
> need.

For us live debugging is more a nice-to-have. That's why we
wanted to delay implementation of on-going synchronisation after the
basic structure of our code was discussed on the mailing list. So we
could avoid some work if we needed to rework it. Apparently we need to
change this plan now ;)

> > Knowing
> > this weakness i discussed quite long with Andreas how to improve
> > it. In this context we also discussed the reavenscar-approach Peter
> > is using. In the end we decided against this approach. In
> > particular we discussed a scenario when you also stack a userspace
> > target on top of the kernel target.  
> 
> How do you stack a userspace target on top with a coredump?

You don't. At least with the current code base it is impossible.

Andreas and I see the ravenscar-approach as a workaround for
limitations in GDB. Thus while discussing it we thought about possible
scenarios for the future which would be impossible to implement using
this approach. The userspace-on-kernel was just meant to be an example.
Other examples would be a Go target where the libthreaddb
(POSIX-threads) and Go (goroutines) targets would compete on
thread stratum. Or (for switching targets) a program that runs
simultaneous on CPU and GPU and needs different targets for both code
parts.
 
> > In this case
> > you would have three different views on threads (hardware, kernel,
> > userspace). With the ravenscar-approach this scenario is impossible
> > to implement as the private_thread_info is already occupied by the
> > kernel target and the userspace would have no chance to use it.
> > Furthermore you would like to have the possibility to switch
> > between different views, i.e. see what the kernel and userspace
> > view on a given process is.  
> 
> Is this a feature you are actively using today with the coredump
> (stacking userspace)?

We are not using it, as discussed above. In particular with our dumps
it is even impossible as we strip it of all userspace memory (a crash
of our build server created a ~9 GB dump (kdump, kernelspace only)
imagine adding all of userspace to that ...). But for live debugging or
smaller systems it could be an interesting scenario to find bugs
triggered by "buggy" userspace behavior.

> IMO we will need to converge on one 'Linux Kernel thread' layer for
> both core dumps and live debug use cases and as you point out with the
> current GDB threads using private_thread_info is mandatory to keep
> the GDB thread list synchronised with the target Linux kernel over
> time.
> 
> So my preference would be until the changes you talk about below (each
> target_ops being responsible for its own thread list) are
> implemented. If a user wants to see a userspace view of the system,
> they should launch a separate GDB session. Then the 'Linux kernel'
> threading layer can use private_thread_info like linux-kthreead and
> ravenscar do today, and it can be used for both core dumps and live
> debug use case.
>
> As an aside, what the original ST patches on which linux-kthread is
> very heavily based would do, is if halting the target and it was
> executing in userspace it would: -
> 
> 1) Switch MMU to kernel mapping
> 2) Read task_struct->mm
> 3) Pull in the user symbols from the root filesystem
> 4) Do the VM address translation.
> 
> This would then allow the backtrace to work right through from user
> space across the user/kernel boundary and into the kernel.

Sounds like an interesting idea. I haven't thought about a solution
like that. 

> > The idea to
> > solve this is to move the global thread_list to the target and
> > allow mapping between the threads of the different list. Thus every
> > target can manage its own threads. Furthermore there needs to be a
> > way to allow several targets at the same time and a way for the
> > user to switch between them. Of course this idea would need some
> > work to implement ...  
> 
> Having each target_ops have its own thread list seems like a very
> neat solution. However my preference would be to gate the 'stacking
> userspace' feature on implementing this functionality, rather than
> the other way around.

Well we thought about this solution when we thought we had plenty of
time. With the new situation it probably is better to first use your
approach and afterwards do the "proper fix". At least this is the
fastest way to have a working solution for both of us.

On the other hand, once there is a working workaround it's hard to keep
up the momentum to do the "proper fix"...
 
> > Unfortunately there is no way to test the code unless you have
> > access to an S390 machine. But if you like and give me some time i
> > can try to make an dump available to you. Cross debugging shouldn't
> > be a problem.  
> 
> A s390 dump could be useful for testing. Also how are you making the
> dumps (via kdump)?. As it could be useful to make an ARM dump for
> testing a ARM arch layer for your patch series.

I'll see how i can make a S390 dump available to you. I hope I have it
by beginning of next week.

Kdump is one possibility for us. Others are getting dumps from the
hypervisor (usually we are running in a VM) but those tools are
S390 specific. Nevertheless you could try getting a dump from a system
running in KVM/Qeum. At least in theory there should be a way for ARM,
too.

You just have to take care to take the dump in the ELF format. It is
the only dump format currently supported by GDB.

Best regards
Philipp

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

* Re: [RFC 0/7] Support for Linux kernel debugging
  2017-01-26 13:12   ` Philipp Rudo
@ 2017-02-03 17:45     ` Yao Qi
  2017-02-03 19:46       ` Andreas Arnez
  0 siblings, 1 reply; 28+ messages in thread
From: Yao Qi @ 2017-02-03 17:45 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: Peter Griffin, gdb-patches, yao.qi, arnez

On 17-01-26 14:12:25, Philipp Rudo wrote:
> > 
> > Live debug of a target is the main use case we are trying to support
> > with the linux-kthread patches. So for us on-going thread
> > synchronisation between GDB and the Linux target is a key feature we
> > need.
> 
> For us live debugging is more a nice-to-have. That's why we
> wanted to delay implementation of on-going synchronisation after the
> basic structure of our code was discussed on the mailing list. So we
> could avoid some work if we needed to rework it. Apparently we need to
> change this plan now ;)
>

Nowadays, when GDB debugs normal application, it has four target layers,

The current target stack is:

  - multi-thread (multi-threaded child process.)
  - native (Native process)
  - exec (Local exec file)
  - None (None)

when it debugs corefile, it becomes

The current target stack is:
  - core (Local core dump file)
  - exec (Local exec file)
  - None (None)

This same can apply to kernel debugging.  GDB can have the right target layer
(Peter's patch) and when GDB debugs kernel dump, GDB has the target layer
from your patch.  We can share something between these two target layers.
I think all code about parsing kernel data structures can be shared.
Even more, we can add a shared target layer for linux kernel debugging,
and live debugging target layer and dump debugging target layer can sit
on top it.  They can use the beneath linux kernel target layer to fetch
registers, get thread names, etc.

> > > Knowing
> > > this weakness i discussed quite long with Andreas how to improve
> > > it. In this context we also discussed the reavenscar-approach Peter
> > > is using. In the end we decided against this approach. In
> > > particular we discussed a scenario when you also stack a userspace
> > > target on top of the kernel target.  
> > 
> > How do you stack a userspace target on top with a coredump?
> 
> You don't. At least with the current code base it is impossible.
> 
> Andreas and I see the ravenscar-approach as a workaround for
> limitations in GDB. Thus while discussing it we thought about possible
> scenarios for the future which would be impossible to implement using
> this approach. The userspace-on-kernel was just meant to be an example.
> Other examples would be a Go target where the libthreaddb
> (POSIX-threads) and Go (goroutines) targets would compete on
> thread stratum. Or (for switching targets) a program that runs
> simultaneous on CPU and GPU and needs different targets for both code
> parts.
>  

IMO, https://sourceware.org/gdb/wiki/MultiTarget is the right way to
solve such problem.  We can have one JTAG remote target debugging kernel,
and one GDBserver debugging user-space app on that machine.

> > > In this case
> > > you would have three different views on threads (hardware, kernel,
> > > userspace). With the ravenscar-approach this scenario is impossible
> > > to implement as the private_thread_info is already occupied by the
> > > kernel target and the userspace would have no chance to use it.
> > > Furthermore you would like to have the possibility to switch
> > > between different views, i.e. see what the kernel and userspace
> > > view on a given process is.  
> > 
> > Is this a feature you are actively using today with the coredump
> > (stacking userspace)?
> 
> We are not using it, as discussed above. In particular with our dumps
> it is even impossible as we strip it of all userspace memory (a crash
> of our build server created a ~9 GB dump (kdump, kernelspace only)
> imagine adding all of userspace to that ...). But for live debugging or
> smaller systems it could be an interesting scenario to find bugs
> triggered by "buggy" userspace behavior.
> 

With MultiTarget in place, we need to somehow associate a function call
in one target to another function in another different target.  A
user-space program call a syscall and trap into kernel, GDB just associate
the user-space call stack on one target with the right kernel space call
stack in another target.  I remember some one presented something on
GDB show stack traces across over RPC calls in the GNU Cauldron several
years.  This is about live debugging.

-- 
Yao (齐尧)

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

* Re: [RFC 0/7] Support for Linux kernel debugging
  2017-02-03 17:45     ` Yao Qi
@ 2017-02-03 19:46       ` Andreas Arnez
  0 siblings, 0 replies; 28+ messages in thread
From: Andreas Arnez @ 2017-02-03 19:46 UTC (permalink / raw)
  To: Yao Qi; +Cc: Philipp Rudo, Peter Griffin, gdb-patches, yao.qi

On Fri, Feb 03 2017, Yao Qi wrote:

> On 17-01-26 14:12:25, Philipp Rudo wrote:

[...]

>> Andreas and I see the ravenscar-approach as a workaround for
>> limitations in GDB. Thus while discussing it we thought about possible
>> scenarios for the future which would be impossible to implement using
>> this approach. The userspace-on-kernel was just meant to be an example.
>> Other examples would be a Go target where the libthreaddb
>> (POSIX-threads) and Go (goroutines) targets would compete on
>> thread stratum. Or (for switching targets) a program that runs
>> simultaneous on CPU and GPU and needs different targets for both code
>> parts.
>>  
>
> IMO, https://sourceware.org/gdb/wiki/MultiTarget is the right way to
> solve such problem.  We can have one JTAG remote target debugging kernel,
> and one GDBserver debugging user-space app on that machine.

Interesting thought; I haven't looked much into that yet.  This would
only be applicable to live debugging, though, right?

Philipp's and my line of thought was more directed towards an extension
of the current target stack.  We considered the possibility to have a
target stack like this:

  - goroutine (Goroutine)
  - multi-thread (multi-threaded child process.)  <-- linux-thread-db
  - lk-user (User-space task) <-- focuses on one user-space process
  - lk-thread (Linux kernel threads) <-- uses the kernel's task list
  - core (Local core dump file) <-- "threads" are actually CPUs
  - exec (Local exec file)
  - None (None)

I.e., having multiple thread layers stacked one atop another.  In this
scenario the user should be able to specify which layer to focus on,
going up and down in the hierarchy.  When at the lk-thread layer, "info
threads" would show kernel threads; when at the "goroutine" layer, it
would show the Goroutines of a certain user-space process in the given
system dump.  Some of these layers would have their own notion of exec
file, solibs, symbols, etc.

The layers above "core" would generally apply to a running target as
well.

Of course, this is a long-range vision and not something I'd expect to
be implemented as part of the Linux kernel debugging support.  For now
we'd just need the lk-thread layer, where mostly just the thread list
needs to be overridden.  And such a "thread-only" layering
infrastructure would also be usable for a goroutine layer in a usual
user-space debugging scenario.

--
Andreas

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

* Re: [RFC 7/7] Add S390 support for linux-kernel target
  2017-01-12 17:09   ` Luis Machado
  2017-01-13 11:46     ` Philipp Rudo
@ 2017-02-06 15:52     ` Yao Qi
  2017-02-06 18:48       ` Andreas Arnez
  1 sibling, 1 reply; 28+ messages in thread
From: Yao Qi @ 2017-02-06 15:52 UTC (permalink / raw)
  To: Luis Machado; +Cc: Philipp Rudo, gdb-patches, peter.griffin, yao.qi, arnez

On 17-01-12 11:08:59, Luis Machado wrote:
> On 01/12/2017 05:32 AM, Philipp Rudo wrote:
> >
> >diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
> >index 054c6d5..f436a74 100644
> >--- a/gdb/s390-linux-tdep.c
> >+++ b/gdb/s390-linux-tdep.c
> 
> I wonder if we should put the new lk-specific code in its own file,
> like s390-lk-tdep.c or something. It would make thing a bit more
> clear and would pollute the current s390-linux-tdep.c file?
> 

+1.

-- 
Yao (齐尧)

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

* Re: [RFC 7/7] Add S390 support for linux-kernel target
  2017-02-06 15:52     ` Yao Qi
@ 2017-02-06 18:48       ` Andreas Arnez
  0 siblings, 0 replies; 28+ messages in thread
From: Andreas Arnez @ 2017-02-06 18:48 UTC (permalink / raw)
  To: Yao Qi; +Cc: Luis Machado, Philipp Rudo, gdb-patches, peter.griffin, yao.qi

On Mon, Feb 06 2017, Yao Qi wrote:

> On 17-01-12 11:08:59, Luis Machado wrote:
>> On 01/12/2017 05:32 AM, Philipp Rudo wrote:
>> >
>> >diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
>> >index 054c6d5..f436a74 100644
>> >--- a/gdb/s390-linux-tdep.c
>> >+++ b/gdb/s390-linux-tdep.c
>> 
>> I wonder if we should put the new lk-specific code in its own file,
>> like s390-lk-tdep.c or something. It would make thing a bit more
>> clear and would pollute the current s390-linux-tdep.c file?
>> 
>
> +1.

+1.

The Linux kernel runtime will be the second "operating system" supported
by GDB for the s390/s390x architectures.  So if we stick to the usual
naming, we will have three components instead of one:

  1. s390-tdep -- common
  2. s390-linux-tdep -- Linux user-space-specific
  3. s390-lk-tdep -- Linux kernel-specific

--
Andreas

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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-01-12 11:32 ` [RFC 3/7] Add basic Linux kernel support Philipp Rudo
@ 2017-02-07 10:54   ` Yao Qi
  2017-02-07 15:04     ` Philipp Rudo
  2017-02-09 13:06     ` Yao Qi
  0 siblings, 2 replies; 28+ messages in thread
From: Yao Qi @ 2017-02-07 10:54 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

On 17-01-12 12:32:13, Philipp Rudo wrote:
>     (ALL_TARGET_OBS): Add lk-low.o

ALL_TARGET_OBS is used with --enable-targets=all, so if we put lk-low.o
in it, lk-low.o can't be linked into GDB if we don't enable all targets.

>     (COMMON_OBS): Add lk-lists.o
> ---

> +
> +/* Initialize a private data entry for an address, where NAME is the name
> +   of the symbol, i.e. variable name in Linux, ALIAS the name used to
> +   retrieve the entry from hashtab, and SILENT a flag to determine if
> +   errors should be ignored.
> +
> +   Returns a pointer to the new entry.  In case of an error, either returns
> +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If SILENT = TRUE
> +   the caller is responsible to check for errors.
> +
> +   Do not use directly, use LK_DECLARE_* macros defined in lk-low.h instead.  */
> +
> +struct lk_private_data *
> +lk_init_addr (const char *name, const char *alias, int silent)
> +{
> +  /* Initialize to NULL to silence gcc.  */
> +  struct value *val = NULL;
> +  struct lk_private_data *data;
> +  void **new_slot;
> +  void *old_slot;
> +
> +  if ((old_slot = lk_find (alias)) != NULL)
> +    return (struct lk_private_data *) old_slot;
> +
> +  TRY
> +    {
> +      /* Choose global block for search, in case the variable was redefined
> +	 in the current context.  */
> +      const struct block *global = block_global_block (get_selected_block (0));
> +      const char *tmp = name;
> +      expression_up expr = parse_exp_1 (&tmp, 0, global, 0);

Why don't you look up symbol or msymbol?  like Peter's patch does.

> +
> +      gdb_assert (*tmp == '\0');
> +      val = evaluate_expression (expr.get ());
> +    }
> +  CATCH (except, RETURN_MASK_ALL)
> +    {
> +      if (!silent)
> +	error (_("Could not find address %s. Abort."), alias);
> +
> +      return NULL;
> +    }
> +  END_CATCH
> +
> +  data = XCNEW (struct lk_private_data);
> +  data->alias = alias;
> +  data->data.addr = value_address (val);
> +
> +  new_slot = lk_find_slot (alias);
> +  *new_slot = data;
> +
> +  return data;
> +}
> +
> +/* Same as lk_init_addr but for structs.  */
> +
> +struct lk_private_data *
> +lk_init_struct (const char *name, const char *alias, int silent)
> +{
> +  /* Initialize to NULL to silence GCC.  */
> +  struct value *val = NULL;
> +  struct lk_private_data *data;
> +  void **new_slot;
> +  void *old_slot;
> +
> +  if ((old_slot = lk_find (alias)) != NULL)
> +    return (struct lk_private_data *) old_slot;
> +
> +  /* There are two ways to define structs
> +	o struct name { ... };
> +	o typedef struct { ... } name;
> +    Both are used in the linux kernel.  Thus we have to check for both ways.
> +    We do this by first searching for "struct name" (the "struct " is added
> +    by macro LK_STRUCT_NAME in lk-low.h) and if not found seach for "name".
> +
> +    Note: The alias will always keep its "struct "-prefix, even when
> +    given explicitely. Besides some weird error messages this has no effect.
> +  */
> +retry:
> +  TRY
> +    {
> +      /* Choose global block for search, in case the struct was redefined
> +	 in the current context.  */
> +      const struct block *global = block_global_block(get_selected_block (0));
> +      const char *tmp = name;
> +      expression_up expr = parse_exp_1 (&tmp, 0, global, 0);

Likewise.

> +
> +      gdb_assert (*tmp == '\0');
> +      /* parsing just for 'name' can cause name clashes.  Thus also check for
> +	 OP_TYPE.  */
> +      if (expr->elts[0].opcode != OP_TYPE)
> +	    error ("We just need to get to the catch block");
> +
> +      val = evaluate_type (expr.get ());
> +    }
> +  CATCH (except, RETURN_MASK_ALL)
> +    {
> +      /* 7 = strlen ("struct ").  */
> +      if (strncmp (name, "struct ", 7) == 0)
> +	{
> +	  name += 7;
> +	  goto retry;
> +	}
> +
> +      if (!silent)
> +	error (_("Could not find %s. Abort."), alias);
> +
> +      return NULL;
> +    }
> +  END_CATCH
> +
> +  data = XCNEW (struct lk_private_data);
> +  data->alias = alias;
> +  data->data.type = value_type (val);
> +
> +  new_slot = lk_find_slot (alias);
> +  *new_slot = data;
> +
> +  return data;
> +}
> +

I am playing your first three patches on x86_64 with some hacks.
I write a small program with the same linux kernel "signature", and
want GDB treat it as a linux kernel.

(gdb) b main
Breakpoint 1 at 0x4004fa: file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c, line 59.
(gdb) run
Starting program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/linux-kernel
Could not map thread with pid 28278, lwp 28278 to a cpu.

I hope we can have a small test case in gdb testsuite to test linux
kernel debugging.  Is it possible?  We can generate a normal coredump
to linux-kernel in test, and we can also set up task_struct and expect
GDB sees some "threads".  What do you think?

-- 
Yao (齐尧)


static char linux_banner[10];
static int _stext = 0;
static int _etext = 0;

typedef int pid_t;

struct list_head
{
  struct list_head *next, *prev;
};

struct thread_struct
{};

struct task_struct
{
  struct list_head tasks;
  pid_t pid;
  pid_t tgid;
  struct list_head thread_group;
  char comm[20];

  struct thread_struct thread;
};

struct rq
{
  struct task_struct *curr;
};

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE           8
#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

#define DECLARE_BITMAP(name,bits) \
  unsigned long name[BITS_TO_LONGS(bits)]

#define NR_CPUS 10

struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); };

struct cpumask __cpu_online_mask;

struct task_struct init_task;
struct rq runqueues;
struct cpumask baz;

unsigned long __per_cpu_offset[NR_CPUS];

struct mm_struct
{};

struct mm_struct init_mm;

int
main (void)
{
  return 0;
}


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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-02-07 10:54   ` Yao Qi
@ 2017-02-07 15:04     ` Philipp Rudo
  2017-02-07 17:39       ` Yao Qi
  2017-02-09 13:06     ` Yao Qi
  1 sibling, 1 reply; 28+ messages in thread
From: Philipp Rudo @ 2017-02-07 15:04 UTC (permalink / raw)
  To: gdb-patches

Hi Yao


On Tue, 7 Feb 2017 10:54:03 +0000
Yao Qi <qiyaoltc@gmail.com> wrote:

> On 17-01-12 12:32:13, Philipp Rudo wrote:
> >     (ALL_TARGET_OBS): Add lk-low.o  
> 
> ALL_TARGET_OBS is used with --enable-targets=all, so if we put
> lk-low.o in it, lk-low.o can't be linked into GDB if we don't enable
> all targets.

you kind of lost me here.  As I understand it lk-low.o needs to be
added to ALL_TARGET_OPS.  The thing I could do is adding entries in
configure.tgt.  Although I'm not sure if adding lk-low to all
arch*-*-linux* (like Peter did) is better than adding a general *-*-lk*
target.  The benefit of the first is that the target is added
automatically to all linux builds.  While the second allows more
flexibility and turning kernel debugging on/off as needed. 

I don't have a strong opinion on which way to go.  Do you prefer one
way?

> >     (COMMON_OBS): Add lk-lists.o
> > ---  
> 
> > +
> > +/* Initialize a private data entry for an address, where NAME is
> > the name
> > +   of the symbol, i.e. variable name in Linux, ALIAS the name used
> > to
> > +   retrieve the entry from hashtab, and SILENT a flag to determine
> > if
> > +   errors should be ignored.
> > +
> > +   Returns a pointer to the new entry.  In case of an error,
> > either returns
> > +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If
> > SILENT = TRUE
> > +   the caller is responsible to check for errors.
> > +
> > +   Do not use directly, use LK_DECLARE_* macros defined in
> > lk-low.h instead.  */ +
> > +struct lk_private_data *
> > +lk_init_addr (const char *name, const char *alias, int silent)
> > +{
> > +  /* Initialize to NULL to silence gcc.  */
> > +  struct value *val = NULL;
> > +  struct lk_private_data *data;
> > +  void **new_slot;
> > +  void *old_slot;
> > +
> > +  if ((old_slot = lk_find (alias)) != NULL)
> > +    return (struct lk_private_data *) old_slot;
> > +
> > +  TRY
> > +    {
> > +      /* Choose global block for search, in case the variable was
> > redefined
> > +	 in the current context.  */
> > +      const struct block *global = block_global_block
> > (get_selected_block (0));
> > +      const char *tmp = name;
> > +      expression_up expr = parse_exp_1 (&tmp, 0, global, 0);  
> 
> Why don't you look up symbol or msymbol?  like Peter's patch does.

I used lookup_symbol in the beginning.  But at some point it
suddenly made problems and couldn't find a symbol.  I actually never
found out what went wrong but to parse_exp solved the problem for me.
And, as it's an init function only used only once per symbol I thought
that adding this extra overhead is excusable.

> > +
> > +      gdb_assert (*tmp == '\0');
> > +      val = evaluate_expression (expr.get ());
> > +    }
> > +  CATCH (except, RETURN_MASK_ALL)
> > +    {
> > +      if (!silent)
> > +	error (_("Could not find address %s. Abort."), alias);
> > +
> > +      return NULL;
> > +    }
> > +  END_CATCH
> > +
> > +  data = XCNEW (struct lk_private_data);
> > +  data->alias = alias;
> > +  data->data.addr = value_address (val);
> > +
> > +  new_slot = lk_find_slot (alias);
> > +  *new_slot = data;
> > +
> > +  return data;
> > +}
> > +
> > +/* Same as lk_init_addr but for structs.  */
> > +
> > +struct lk_private_data *
> > +lk_init_struct (const char *name, const char *alias, int silent)
> > +{
> > +  /* Initialize to NULL to silence GCC.  */
> > +  struct value *val = NULL;
> > +  struct lk_private_data *data;
> > +  void **new_slot;
> > +  void *old_slot;
> > +
> > +  if ((old_slot = lk_find (alias)) != NULL)
> > +    return (struct lk_private_data *) old_slot;
> > +
> > +  /* There are two ways to define structs
> > +	o struct name { ... };
> > +	o typedef struct { ... } name;
> > +    Both are used in the linux kernel.  Thus we have to check for
> > both ways.
> > +    We do this by first searching for "struct name" (the "struct "
> > is added
> > +    by macro LK_STRUCT_NAME in lk-low.h) and if not found seach
> > for "name". +
> > +    Note: The alias will always keep its "struct "-prefix, even
> > when
> > +    given explicitely. Besides some weird error messages this has
> > no effect.
> > +  */
> > +retry:
> > +  TRY
> > +    {
> > +      /* Choose global block for search, in case the struct was
> > redefined
> > +	 in the current context.  */
> > +      const struct block *global =
> > block_global_block(get_selected_block (0));
> > +      const char *tmp = name;
> > +      expression_up expr = parse_exp_1 (&tmp, 0, global, 0);  
> 
> Likewise.
> 
> > +
> > +      gdb_assert (*tmp == '\0');
> > +      /* parsing just for 'name' can cause name clashes.  Thus
> > also check for
> > +	 OP_TYPE.  */
> > +      if (expr->elts[0].opcode != OP_TYPE)
> > +	    error ("We just need to get to the catch block");
> > +
> > +      val = evaluate_type (expr.get ());
> > +    }
> > +  CATCH (except, RETURN_MASK_ALL)
> > +    {
> > +      /* 7 = strlen ("struct ").  */
> > +      if (strncmp (name, "struct ", 7) == 0)
> > +	{
> > +	  name += 7;
> > +	  goto retry;
> > +	}
> > +
> > +      if (!silent)
> > +	error (_("Could not find %s. Abort."), alias);
> > +
> > +      return NULL;
> > +    }
> > +  END_CATCH
> > +
> > +  data = XCNEW (struct lk_private_data);
> > +  data->alias = alias;
> > +  data->data.type = value_type (val);
> > +
> > +  new_slot = lk_find_slot (alias);
> > +  *new_slot = data;
> > +
> > +  return data;
> > +}
> > +  
> 
> I am playing your first three patches on x86_64 with some hacks.
> I write a small program with the same linux kernel "signature", and
> want GDB treat it as a linux kernel.
> 
> (gdb) b main
> Breakpoint 1 at 0x4004fa:
> file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c,
> line 59. (gdb) run Starting
> program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/linux-kernel
> Could not map thread with pid 28278, lwp 28278 to a cpu.
> 
> I hope we can have a small test case in gdb testsuite to test linux
> kernel debugging.  Is it possible?  We can generate a normal coredump
> to linux-kernel in test, and we can also set up task_struct and expect
> GDB sees some "threads".  What do you think?

Andreas and I had the same idea :-)
I don't know If it really works.  But we think its the most promising
approach for the testsuite.  The biggest problem I see is the
relocation done for the kernel modules.  Here you need to somehow mimic
a dynamic linking done in the kernel.  Which I suppose not to be that
simple, but still doable.

Thanks
Philipp

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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-02-07 15:04     ` Philipp Rudo
@ 2017-02-07 17:39       ` Yao Qi
  2017-02-09  9:54         ` Philipp Rudo
  0 siblings, 1 reply; 28+ messages in thread
From: Yao Qi @ 2017-02-07 17:39 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: gdb-patches

On 17-02-07 16:04:44, Philipp Rudo wrote:
> Hi Yao
> 
> 
> On Tue, 7 Feb 2017 10:54:03 +0000
> Yao Qi <qiyaoltc@gmail.com> wrote:
> 
> > On 17-01-12 12:32:13, Philipp Rudo wrote:
> > >     (ALL_TARGET_OBS): Add lk-low.o  
> > 
> > ALL_TARGET_OBS is used with --enable-targets=all, so if we put
> > lk-low.o in it, lk-low.o can't be linked into GDB if we don't enable
> > all targets.
> 
> you kind of lost me here.  As I understand it lk-low.o needs to be
> added to ALL_TARGET_OPS.  The thing I could do is adding entries in
> configure.tgt.  Although I'm not sure if adding lk-low to all
> arch*-*-linux* (like Peter did) is better than adding a general *-*-lk*
> target.  The benefit of the first is that the target is added
> automatically to all linux builds.  While the second allows more
> flexibility and turning kernel debugging on/off as needed. 
> 
> I don't have a strong opinion on which way to go.  Do you prefer one
> way?

Yes, lk-low.o needs to be added to ALL_TARGET_OBS, but that is not enough.
I get the following compile error on x86_64-linux,

lk-lists.o: In function `lk_find':
/home/yao/SourceCode/gnu/gdb/git/gdb/lk-low.h:125: undefined reference to `linux_kernel_ops'
/home/yao/SourceCode/gnu/gdb/git/gdb/lk-low.h:125: undefined reference to `linux_kernel_ops'
lk-lists.o: In function `lk_list_head_next(unsigned long, char const*, char const*)':
/home/yao/SourceCode/gnu/gdb/git/gdb/lk-lists.c:35: undefined reference to `lk_read_addr(unsigned long)'
/home/yao/SourceCode/gnu/gdb/git/gdb/lk-lists.c:36: undefined reference to `lk_read_addr(unsigned long)'

because lk-low.o isn't linked into the executable.

I think both lk-low.o and lk-lists.o should be added for each
$ARCH-linux target in configure.tgt.

> 
> > >     (COMMON_OBS): Add lk-lists.o
> > > ---  
> > 
> > > +
> > > +/* Initialize a private data entry for an address, where NAME is
> > > the name
> > > +   of the symbol, i.e. variable name in Linux, ALIAS the name used
> > > to
> > > +   retrieve the entry from hashtab, and SILENT a flag to determine
> > > if
> > > +   errors should be ignored.
> > > +
> > > +   Returns a pointer to the new entry.  In case of an error,
> > > either returns
> > > +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).  If
> > > SILENT = TRUE
> > > +   the caller is responsible to check for errors.
> > > +
> > > +   Do not use directly, use LK_DECLARE_* macros defined in
> > > lk-low.h instead.  */ +
> > > +struct lk_private_data *
> > > +lk_init_addr (const char *name, const char *alias, int silent)
> > > +{
> > > +  /* Initialize to NULL to silence gcc.  */
> > > +  struct value *val = NULL;
> > > +  struct lk_private_data *data;
> > > +  void **new_slot;
> > > +  void *old_slot;
> > > +
> > > +  if ((old_slot = lk_find (alias)) != NULL)
> > > +    return (struct lk_private_data *) old_slot;
> > > +
> > > +  TRY
> > > +    {
> > > +      /* Choose global block for search, in case the variable was
> > > redefined
> > > +	 in the current context.  */
> > > +      const struct block *global = block_global_block
> > > (get_selected_block (0));
> > > +      const char *tmp = name;
> > > +      expression_up expr = parse_exp_1 (&tmp, 0, global, 0);  
> > 
> > Why don't you look up symbol or msymbol?  like Peter's patch does.
> 
> I used lookup_symbol in the beginning.  But at some point it
> suddenly made problems and couldn't find a symbol.  I actually never
> found out what went wrong but to parse_exp solved the problem for me.
> And, as it's an init function only used only once per symbol I thought
> that adding this extra overhead is excusable.

Do you have an example that lookup_symbol doesn't work but parse_exp
works?

> > 
> > (gdb) b main
> > Breakpoint 1 at 0x4004fa:
> > file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c,
> > line 59. (gdb) run Starting
> > program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/linux-kernel
> > Could not map thread with pid 28278, lwp 28278 to a cpu.
> > 
> > I hope we can have a small test case in gdb testsuite to test linux
> > kernel debugging.  Is it possible?  We can generate a normal coredump
> > to linux-kernel in test, and we can also set up task_struct and expect
> > GDB sees some "threads".  What do you think?
> 
> Andreas and I had the same idea :-)
> I don't know If it really works.  But we think its the most promising
> approach for the testsuite.  The biggest problem I see is the
> relocation done for the kernel modules.  Here you need to somehow mimic
> a dynamic linking done in the kernel.  Which I suppose not to be that
> simple, but still doable.
> 

How is it related to kernel modules?  I don't want to test kernel module
handling in this test case.

-- 
Yao (齐尧)

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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-02-07 17:39       ` Yao Qi
@ 2017-02-09  9:54         ` Philipp Rudo
  0 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-02-09  9:54 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, Andreas Arnez

Hi Yao,

On Tue, 7 Feb 2017 17:39:44 +0000
Yao Qi <qiyaoltc@gmail.com> wrote:

> On 17-02-07 16:04:44, Philipp Rudo wrote:
> > Hi Yao
> > 
> > 
> > On Tue, 7 Feb 2017 10:54:03 +0000
> > Yao Qi <qiyaoltc@gmail.com> wrote:
> >   
> > > On 17-01-12 12:32:13, Philipp Rudo wrote:  
> > > >     (ALL_TARGET_OBS): Add lk-low.o    
> > > 
> > > ALL_TARGET_OBS is used with --enable-targets=all, so if we put
> > > lk-low.o in it, lk-low.o can't be linked into GDB if we don't
> > > enable all targets.  
> > 
> > you kind of lost me here.  As I understand it lk-low.o needs to be
> > added to ALL_TARGET_OPS.  The thing I could do is adding entries in
> > configure.tgt.  Although I'm not sure if adding lk-low to all
> > arch*-*-linux* (like Peter did) is better than adding a general
> > *-*-lk* target.  The benefit of the first is that the target is
> > added automatically to all linux builds.  While the second allows
> > more flexibility and turning kernel debugging on/off as needed. 
> > 
> > I don't have a strong opinion on which way to go.  Do you prefer one
> > way?  
> 
> Yes, lk-low.o needs to be added to ALL_TARGET_OBS, but that is not
> enough. I get the following compile error on x86_64-linux,
> 
> lk-lists.o: In function `lk_find':
> /home/yao/SourceCode/gnu/gdb/git/gdb/lk-low.h:125: undefined
> reference to
> `linux_kernel_ops' /home/yao/SourceCode/gnu/gdb/git/gdb/lk-low.h:125:
> undefined reference to `linux_kernel_ops' lk-lists.o: In function
> `lk_list_head_next(unsigned long, char const*, char
> const*)': /home/yao/SourceCode/gnu/gdb/git/gdb/lk-lists.c:35:
> undefined reference to `lk_read_addr(unsigned
> long)' /home/yao/SourceCode/gnu/gdb/git/gdb/lk-lists.c:36: undefined
> reference to `lk_read_addr(unsigned long)'
> 
> because lk-low.o isn't linked into the executable.
> 
> I think both lk-low.o and lk-lists.o should be added for each
> $ARCH-linux target in configure.tgt.

Ok, now I get it. Thanks for pointing this out I simply forgot to test
without --enable-targets=all...

While fixing this I also noticed that I forgot adding the new
s390-tdep.o in my split up patch in v2.  I will fix it in v3.

> > > >     (COMMON_OBS): Add lk-lists.o
> > > > ---    
> > >   
> > > > +
> > > > +/* Initialize a private data entry for an address, where NAME
> > > > is the name
> > > > +   of the symbol, i.e. variable name in Linux, ALIAS the name
> > > > used to
> > > > +   retrieve the entry from hashtab, and SILENT a flag to
> > > > determine if
> > > > +   errors should be ignored.
> > > > +
> > > > +   Returns a pointer to the new entry.  In case of an error,
> > > > either returns
> > > > +   NULL (SILENT = TRUE) or throws an error (SILENT = FALSE).
> > > > If SILENT = TRUE
> > > > +   the caller is responsible to check for errors.
> > > > +
> > > > +   Do not use directly, use LK_DECLARE_* macros defined in
> > > > lk-low.h instead.  */ +
> > > > +struct lk_private_data *
> > > > +lk_init_addr (const char *name, const char *alias, int silent)
> > > > +{
> > > > +  /* Initialize to NULL to silence gcc.  */
> > > > +  struct value *val = NULL;
> > > > +  struct lk_private_data *data;
> > > > +  void **new_slot;
> > > > +  void *old_slot;
> > > > +
> > > > +  if ((old_slot = lk_find (alias)) != NULL)
> > > > +    return (struct lk_private_data *) old_slot;
> > > > +
> > > > +  TRY
> > > > +    {
> > > > +      /* Choose global block for search, in case the variable
> > > > was redefined
> > > > +	 in the current context.  */
> > > > +      const struct block *global = block_global_block
> > > > (get_selected_block (0));
> > > > +      const char *tmp = name;
> > > > +      expression_up expr = parse_exp_1 (&tmp, 0, global,
> > > > 0);    
> > > 
> > > Why don't you look up symbol or msymbol?  like Peter's patch
> > > does.  
> > 
> > I used lookup_symbol in the beginning.  But at some point it
> > suddenly made problems and couldn't find a symbol.  I actually never
> > found out what went wrong but to parse_exp solved the problem for
> > me. And, as it's an init function only used only once per symbol I
> > thought that adding this extra overhead is excusable.  
> 
> Do you have an example that lookup_symbol doesn't work but parse_exp
> works?

No sorry not anymore it got lost in some cleanup.  I did it quite early
in development and can't remember everything but I guess that it was
either a memory corruption (I had an use-after-relocate bug in my htab
handling) or that I just didn't understood GDB's symbolhandling when I
stared working on the project.

Nevertheless I looked at it again and it works.  In addition it is much
nicer and shorter.  The only thing I don't really like are the
"goto out/error" in lk_init_struct but that is just a minor flaw. The
two functions now look like this

struct lk_private_data *
lk_init_addr (const char *name, const char *alias, int silent)
{
  struct lk_private_data *data;
  struct bound_minimal_symbol bmsym;
  void **new_slot;
  void *old_slot;

  if ((old_slot = lk_find (alias)) != NULL)
    return (struct lk_private_data *) old_slot;

  bmsym = lookup_minimal_symbol (name, NULL, NULL);

  if (bmsym.minsym == NULL)
    {
      if (!silent)
	error (_("Could not find address %s.  Aborting."), alias);

      return NULL;
    }

  data = XCNEW (struct lk_private_data);
  data->alias = alias;
  data->data.addr = BMSYMBOL_VALUE_ADDRESS (bmsym);

  new_slot = lk_find_slot (alias);
  *new_slot = data;

  return data;
}

struct lk_private_data *
lk_init_struct (const char *name, const char *alias, int silent)
{
  struct lk_private_data *data;
  const struct block *global;
  const struct symbol *sym;
  struct type *type;
  void **new_slot;
  void *old_slot;

  if ((old_slot = lk_find (alias)) != NULL)
    return (struct lk_private_data *) old_slot;

  global = block_global_block(get_selected_block (0));
  sym = lookup_symbol (name, global, STRUCT_DOMAIN, NULL).symbol;

  if (sym != NULL)
    {
      type = SYMBOL_TYPE (sym);
      goto out;
    }

  /*  Chek for "typedef struct { ... } name;"-like definitions.  */
  sym = lookup_symbol (name, global, VAR_DOMAIN, NULL).symbol;
  if (sym == NULL)
    goto error;

  type = check_typedef (SYMBOL_TYPE (sym));

  if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
    goto out;

error:
  if (!silent)
    error (_("Could not find %s.  Aborting."), alias);

  return NULL;

out:
  data = XCNEW (struct lk_private_data);
  data->alias = alias;
  data->data.type = type;

  new_slot = lk_find_slot (alias);
  *new_slot = data;

  return data;
}


> > > (gdb) b main
> > > Breakpoint 1 at 0x4004fa:
> > > file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c,
> > > line 59. (gdb) run Starting
> > > program: /scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/linux-kernel
> > > Could not map thread with pid 28278, lwp 28278 to a cpu.
> > > 
> > > I hope we can have a small test case in gdb testsuite to test
> > > linux kernel debugging.  Is it possible?  We can generate a
> > > normal coredump to linux-kernel in test, and we can also set up
> > > task_struct and expect GDB sees some "threads".  What do you
> > > think?  
> > 
> > Andreas and I had the same idea :-)
> > I don't know If it really works.  But we think its the most
> > promising approach for the testsuite.  The biggest problem I see is
> > the relocation done for the kernel modules.  Here you need to
> > somehow mimic a dynamic linking done in the kernel.  Which I
> > suppose not to be that simple, but still doable.
> >   
> 
> How is it related to kernel modules?  I don't want to test kernel
> module handling in this test case.

When you are only interested in threads it isn't related at all.  My
intention was to indicate that when you want to have test cases for the
full target, including module handling, you will need to somehow cope
with it.  Although I'm not sure if this approach is the best for
modules.  Besides the relocation problem, they also live in kernel
virtual memory.  Thus you'd need a test case which simulates it's own
address space...
In my opinion both functions need to be tested when the code is
integrated to master.  Otherwise GDB might print wrong and misleading
information to the user without noticing it.  Although I must admit
that writing working test cases won't be easy.

Thanks
Philipp

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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-02-07 10:54   ` Yao Qi
  2017-02-07 15:04     ` Philipp Rudo
@ 2017-02-09 13:06     ` Yao Qi
  2017-02-09 15:09       ` Philipp Rudo
  1 sibling, 1 reply; 28+ messages in thread
From: Yao Qi @ 2017-02-09 13:06 UTC (permalink / raw)
  To: Yao Qi; +Cc: Philipp Rudo, gdb-patches, peter.griffin, yao.qi, arnez

Yao Qi <qiyaoltc@gmail.com> writes:

> I am playing your first three patches on x86_64 with some hacks.
> I write a small program with the same linux kernel "signature", and
> want GDB treat it as a linux kernel.
>

I make some progress on writing such small test case,
see the code below.  I hacked lk_try_push_target not to do the
sanity check, and not to call lk_try_push_target in
lk_observer_inferior_created, so that I can push this target layer when
I want.

(gdb) break stop^M
Breakpoint 2 at 0x400711: file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c, line 104.^M
(gdb) continue^M
Continuing.^M
^M
Breakpoint 2, stop () at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104^M
104     {}^M
(gdb) PASS: gdb.base/linux-kernel.exp: continue to breakpoint: stop

at this point, the list of tasks are set up, switch to linux-kernel
target layer,

target linux-kernel^M
[New process 8001]^M
(gdb) PASS: gdb.base/linux-kernel.exp: target linux-kernel
maintenance print target-stack^M
The current target stack is:^M
  - linux-kernel (linux kernel support)^M
  - native (Native process)^M
  - exec (Local exec file)^M
  - None (None)^M

It works!  In this way, we can test that GDB can successfully parse the
these data structures in Linux kernel without Linux kernel image at all.

Then, we can generate a coredump,

(gdb) generate-core-file 
Saved corefile core.9614

Remove the hack in lk_observer_inferior_created, so that GDB can
automatically push linux-kernel target layer,

$ ./gdb ./testsuite/outputs/gdb.base/linux-kernel/linux-kernel ./core.9614
[New LWP 9614]
[New process 9614]
Core was generated by `/scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/li'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0  stop () at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104
104	{}
[Current thread is 1 (PID:  9614*, 0x602010)]
(gdb) maintenance print target-stack 
The current target stack is:
  - linux-kernel (linux kernel support)
  - core (Local core dump file)
  - exec (Local exec file)
  - None (None)

The next step would be extending the test case to a multi-threaded
program, so that we can create task lists for these threads, and
generate coredump which is similar to the kernel coredump.

-- 
Yao (齐尧)

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static char linux_banner[10];
static int _stext = 0;
static int _etext = 0;

typedef int pid_t;

struct list_head
{
  struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) (struct list_head) { &(name), &(name) }

struct thread_struct
{};

struct task_struct
{
  struct list_head tasks;
  pid_t pid;
  pid_t tgid;
  struct list_head thread_group;
  char comm[20];

  struct thread_struct thread;
};

struct rq
{
  struct task_struct *curr;
};

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE           8
#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof (long))

#define DECLARE_BITMAP(name,bits) \
  unsigned long name[BITS_TO_LONGS(bits)]

#define NR_CPUS 10

struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); };

struct cpumask __cpu_online_mask;

static void
cpumask_set_cpu (unsigned int cpu, struct cpumask* dstp)
{
  dstp->bits[cpu / (BITS_PER_BYTE * sizeof (long))]
    |= 1UL << (cpu % (BITS_PER_BYTE * sizeof (long)));
}

struct task_struct init_task;;
struct rq runqueues[NR_CPUS];
struct cpumask baz;

unsigned long __per_cpu_offset[NR_CPUS];

struct mm_struct
{};

struct mm_struct init_mm;


static void
setup (void)
{
  int i;
  struct task_struct *task;

  /* Set up __per_cpu_offset.  */
  for (i = 0; i < NR_CPUS; i++)
    __per_cpu_offset[i] = i * sizeof (runqueues[0]);

  /* Mark cpu 0 is online.  */
  cpumask_set_cpu (0, &__cpu_online_mask);

  init_task.tasks = LIST_HEAD_INIT(init_task.tasks);
  init_task.thread_group = LIST_HEAD_INIT(init_task.thread_group);

  task = malloc (sizeof (struct task_struct));
  memset (task, 0, sizeof (sizeof (struct task_struct)));

  task->pid = getpid ();

  runqueues[0].curr = task;

  /* Chain it tasks list.  */
  init_task.tasks.next = (struct list_head *) task;
  init_task.tasks.prev = (struct list_head *) task;
  task->tasks.next = (struct list_head *) &init_task;
  task->tasks.prev = (struct list_head *) &init_task;

  /* TASK is the group leader.  */
  task->thread_group = LIST_HEAD_INIT(task->thread_group);
}

static void
stop (void)
{}

int
main (void)
{
  setup ();

  stop ();
  return 0;
}

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

* Re: [RFC 3/7] Add basic Linux kernel support
  2017-02-09 13:06     ` Yao Qi
@ 2017-02-09 15:09       ` Philipp Rudo
  0 siblings, 0 replies; 28+ messages in thread
From: Philipp Rudo @ 2017-02-09 15:09 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches, peter.griffin, yao.qi, arnez

Hi Yao

wow.  Thats wonderful.  Now we know we can go this way for testing.

One minor thing I noticed in your code (which is actually my mistake)

> struct mm_struct
> {};
>
> struct mm_struct init_mm;

You don't need those two when you are only interested in thread
handling.  The mm_struct and init_mm are needed for address translation
and actually belong in the commit with module handling ...

Fixed it for v3.

Thanks a lot
Philipp


On Thu, 09 Feb 2017 13:05:58 +0000
Yao Qi <qiyaoltc@gmail.com> wrote:

> Yao Qi <qiyaoltc@gmail.com> writes:
> 
> > I am playing your first three patches on x86_64 with some hacks.
> > I write a small program with the same linux kernel "signature", and
> > want GDB treat it as a linux kernel.
> >  
> 
> I make some progress on writing such small test case,
> see the code below.  I hacked lk_try_push_target not to do the
> sanity check, and not to call lk_try_push_target in
> lk_observer_inferior_created, so that I can push this target layer
> when I want.
> 
> (gdb) break stop^M
> Breakpoint 2 at 0x400711:
> file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c,
> line 104.^M (gdb) continue^M Continuing.^M
> ^M
> Breakpoint 2, stop ()
> at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104^M
> 104     {}^M (gdb) PASS: gdb.base/linux-kernel.exp: continue to
> breakpoint: stop
> 
> at this point, the list of tasks are set up, switch to linux-kernel
> target layer,
> 
> target linux-kernel^M
> [New process 8001]^M
> (gdb) PASS: gdb.base/linux-kernel.exp: target linux-kernel
> maintenance print target-stack^M
> The current target stack is:^M
>   - linux-kernel (linux kernel support)^M
>   - native (Native process)^M
>   - exec (Local exec file)^M
>   - None (None)^M
> 
> It works!  In this way, we can test that GDB can successfully parse
> the these data structures in Linux kernel without Linux kernel image
> at all.
> 
> Then, we can generate a coredump,
> 
> (gdb) generate-core-file 
> Saved corefile core.9614
> 
> Remove the hack in lk_observer_inferior_created, so that GDB can
> automatically push linux-kernel target layer,
> 
> $ ./gdb ./testsuite/outputs/gdb.base/linux-kernel/linux-kernel ./core.9614
> [New LWP 9614]
> [New process 9614]
> Core was generated by
> `/scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/li'.
> Program terminated with signal SIGTRAP, Trace/breakpoint trap. #0
> stop ()
> at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104
> 104	{} [Current thread is 1 (PID:  9614*, 0x602010)] (gdb)
> maintenance print target-stack The current target stack is:
>   - linux-kernel (linux kernel support)
>   - core (Local core dump file)
>   - exec (Local exec file)
>   - None (None)
> 
> The next step would be extending the test case to a multi-threaded
> program, so that we can create task lists for these threads, and
> generate coredump which is similar to the kernel coredump.
> 

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

end of thread, other threads:[~2017-02-09 15:09 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-12 11:32 [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
2017-01-12 11:32 ` [RFC 5/7] Add commands for linux-kernel target Philipp Rudo
2017-01-12 11:32 ` [RFC 3/7] Add basic Linux kernel support Philipp Rudo
2017-02-07 10:54   ` Yao Qi
2017-02-07 15:04     ` Philipp Rudo
2017-02-07 17:39       ` Yao Qi
2017-02-09  9:54         ` Philipp Rudo
2017-02-09 13:06     ` Yao Qi
2017-02-09 15:09       ` Philipp Rudo
2017-01-12 11:32 ` [RFC 6/7] Add privileged registers for s390x Philipp Rudo
2017-01-12 11:32 ` [RFC 2/7] Add libiberty/concat styled concat_path function Philipp Rudo
2017-01-12 12:00   ` Pedro Alves
2017-01-12 13:33     ` Philipp Rudo
2017-01-12 13:48       ` Pedro Alves
2017-01-12 15:09         ` Philipp Rudo
2017-01-12 15:42           ` Pedro Alves
2017-01-12 11:32 ` [RFC 1/7] Convert substitute_path_component to C++ Philipp Rudo
2017-01-12 11:32 ` [RFC 7/7] Add S390 support for linux-kernel target Philipp Rudo
2017-01-12 17:09   ` Luis Machado
2017-01-13 11:46     ` Philipp Rudo
2017-02-06 15:52     ` Yao Qi
2017-02-06 18:48       ` Andreas Arnez
2017-01-12 12:56 ` [RFC 0/7] Support for Linux kernel debugging Philipp Rudo
2017-01-12 13:02 ` [RFC 4/7] Add kernel module support for linux-kernel target Philipp Rudo
2017-01-25 18:10 ` [RFC 0/7] Support for Linux kernel debugging Peter Griffin
2017-01-26 13:12   ` Philipp Rudo
2017-02-03 17:45     ` Yao Qi
2017-02-03 19:46       ` Andreas Arnez

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).