This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC v3 3/8] Add basic Linux kernel support


Hi Omair,


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

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

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

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

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

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

Thanks

Philipp

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]