aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Rudo <prudo@linux.vnet.ibm.com>2017-02-07 14:17:04 +0100
committerAndreas Arnez <arnez@linux.vnet.ibm.com>2017-02-07 16:25:54 +0100
commit757186093b40ad3b37962c34294d032b0d88739f (patch)
tree52ed1fa809f21d635b542a3ef629fb819dad6c81
parent75d79af4135482bb3cb4b9c348439c64a8e29ca2 (diff)
downloadgdb-757186093b40ad3b37962c34294d032b0d88739f.zip
gdb-757186093b40ad3b37962c34294d032b0d88739f.tar.gz
gdb-757186093b40ad3b37962c34294d032b0d88739f.tar.bz2
Add basic Linux kernel support
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
-rw-r--r--gdb/Makefile.in8
-rw-r--r--gdb/gdbarch.c31
-rw-r--r--gdb/gdbarch.h8
-rwxr-xr-xgdb/gdbarch.sh4
-rw-r--r--gdb/lk-lists.c47
-rw-r--r--gdb/lk-lists.h56
-rw-r--r--gdb/lk-low.c860
-rw-r--r--gdb/lk-low.h309
8 files changed, 1323 insertions, 0 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e0fe442..41dcd4a 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 \
@@ -1092,6 +1093,8 @@ SFILES = \
jit.c \
language.c \
linespec.c \
+ lk-lists.c \
+ lk-low.c \
location.c \
m2-exp.y \
m2-lang.c \
@@ -1339,6 +1342,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 \
@@ -1699,6 +1704,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
jit.o \
language.o \
linespec.o \
+ lk-lists.o \
location.o \
m2-lang.o \
m2-typeprint.o \
@@ -2531,6 +2537,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 266f2e9..64f6e6d 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
@@ -1124,6 +1125,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,
@@ -4956,6 +4963,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 54549b6..e6903aa 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..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..a26373b
--- /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. Aborting."), 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. Aborting."), 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. Aborting."), 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. 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 (inferior_ptid);
+ 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..e644ffe
--- /dev/null
+++ b/gdb/lk-low.h
@@ -0,0 +1,309 @@
+/* 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 *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) (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__ */