@@ -721,7 +721,8 @@ ALL_TARGET_OBS = \
symfile-mem.o \
windows-tdep.o \
linux-record.o \
- ravenscar-thread.o
+ ravenscar-thread.o \
+ lkd-main.o lkd-process.o
# Host-dependent makefile fragment comes in here.
@host_makefile_frag@
@@ -988,7 +989,7 @@ common/common-exceptions.h target/target.h common/symbol.h \
common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h nat/amd64-linux-siginfo.h\
nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \
-tid-parse.h
+tid-parse.h lkd/lkd.h lkd/lkd-process.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -1355,6 +1356,7 @@ init.c: $(INIT_FILES)
-e '/[a-z0-9A-Z_]*-exp.tab.[co]$$/d' \
-e 's/\.[co]$$/.c/' \
-e 's,signals\.c,common/signals\.c,' \
+ -e 's,\(lkd-.*\.c\),lkd/\1,' \
-e 's|^\([^ /][^ ]*\)|$(srcdir)/\1|g' | \
while read f; do \
sed -n -e 's/^_initialize_\([a-z_0-9A-Z]*\).*/\1/p' $$f 2>/dev/null; \
@@ -1754,7 +1756,8 @@ ALLDEPFILES = \
xcoffread.c \
xstormy16-tdep.c \
xtensa-tdep.c xtensa-config.c \
- xtensa-linux-tdep.c xtensa-linux-nat.c xtensa-xtregs.c
+ xtensa-linux-tdep.c xtensa-linux-nat.c xtensa-xtregs.c \
+ lkd/lkd-main.c lkd/lkd-process.c
# Some files need explicit build rules (due to -Werror problems) or due
# to sub-directory fun 'n' games.
@@ -2717,6 +2720,20 @@ py-varobj.o: $(srcdir)/python/py-varobj.c
$(POSTCOMPILE)
#
+# gdb/lkd/ dependencies
+#
+# Need to explicitly specify the compile rule as make will do nothing
+# or try to compile the object file into the sub-directory.
+
+lkd-main.o: $(srcdir)/lkd/lkd-main.c
+lkd-process.o: $(srcdir)/lkd/lkd-process.c
+
+# Generalised lkd-compile rule
+lkd-%:
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+
+#
# Dependency tracking. Most of this is conditional on GNU Make being
# found by configure; if GNU Make is not found, we fall back to a
# simpler scheme.
@@ -33,6 +33,9 @@
/* Define to BFD's default target vector. */
#undef DEFAULT_BFD_VEC
+/* Define to 1 if Linux Kernel Debug is enabled */
+#undef ENABLE_LKD
+
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#undef ENABLE_NLS
@@ -816,6 +816,7 @@ with_auto_load_dir
with_auto_load_safe_path
enable_targets
enable_64_bit_bfd
+enable_linux_kernel_aware
enable_gdbcli
enable_gdbmi
enable_tui
@@ -1506,6 +1507,8 @@ Optional Features:
--enable-targets=TARGETS
alternative target configurations
--enable-64-bit-bfd 64-bit support (on hosts with narrower word sizes)
+ --enable-linux-kernel-aware
+ enable linux kernel aware debugging
--disable-gdbcli disable command-line interface (CLI)
--disable-gdbmi disable machine-interface (MI)
--enable-tui enable full-screen terminal user interface (TUI)
@@ -5687,6 +5690,26 @@ else
want64=false
fi
+# Check whether to support linux kernel aware debugging
+# Check whether --enable-linux-kernel-aware was given.
+if test "${enable_linux_kernel_aware+set}" = set; then :
+ enableval=$enable_linux_kernel_aware; case $enableval in
+ yes | no)
+ ;;
+ *)
+ as_fn_error "bad value $enableval for --enable-linux-kernel-aware" "$LINENO" 5 ;;
+ esac
+else
+ enable_linux_kernel_aware=no
+fi
+
+
+if test "${enable_linux_kernel_aware}" = "yes"; then
+
+$as_echo "#define ENABLE_LKD 1" >>confdefs.h
+
+fi
+
# Provide defaults for some variables set by the per-host and per-target
# configuration.
gdb_host_obs=posix-hdep.o
@@ -198,6 +198,21 @@ AS_HELP_STRING([--enable-64-bit-bfd], [64-bit support (on hosts with narrower wo
*) AC_MSG_ERROR(bad value ${enableval} for 64-bit-bfd option) ;;
esac],[want64=false])dnl
+# Check whether to support linux kernel aware debugging
+AC_ARG_ENABLE(linux-kernel-aware,
+AS_HELP_STRING([--enable-linux-kernel-aware], [enable linux kernel aware debugging]),
+ [case $enableval in
+ yes | no)
+ ;;
+ *)
+ AC_MSG_ERROR([bad value $enableval for --enable-linux-kernel-aware]) ;;
+ esac],
+ [enable_linux_kernel_aware=no])
+
+if test "${enable_linux_kernel_aware}" = "yes"; then
+ AC_DEFINE(ENABLE_LKD, 1, [Define to 1 if Linux Kernel Debug is enabled])
+fi
+
# Provide defaults for some variables set by the per-host and per-target
# configuration.
gdb_host_obs=posix-hdep.o
@@ -725,3 +725,11 @@ for t in x ${gdb_target_obs}; do
gdb_have_gcore=true
fi
done
+
+
+# Add linux kernel awareness obs and set no default OS ABI
+if test x"$enable_linux_kernel_aware" = xyes; then
+ gdb_target_obs="$gdb_target_obs lkd-main.o lkd-process.o"
+ gdb_osabi=
+fi
+
new file mode 100644
@@ -0,0 +1,2160 @@
+/*
+ Copyright 2005-2013 STMicroelectronics.
+
+ This file contains the architecture neutral part of the Linux
+ Awareness layer for GDB.
+
+ */
+
+#include <ctype.h>
+
+#include "defs.h"
+#include "ui-out.h"
+#include "arch-utils.h"
+#include "block.h"
+#include "breakpoint.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-script.h"
+#include "command.h"
+#include "completer.h"
+#include "dictionary.h"
+#include "event-loop.h"
+#include "exceptions.h"
+#include "exec.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "gdb.h"
+#include "gdb_assert.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "location.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "regcache.h"
+#include "solib.h"
+#include "solist.h"
+#include "symtab.h"
+#include "psympriv.h"
+#include "target.h"
+#include "top.h"
+
+#include "bfd.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+
+#include "tui/tui.h"
+
+
+#include "lkd.h"
+#include "lkd-process.h"
+
+/******************************* from shtdi.c *********************************/
+/* Signal Handling. */
+#include <signal.h>
+
+/* A target interrupt has been requested. */
+
+/* A SIGTERM has been requested. */
+static volatile int terminate_requested = 0;
+
+/* Remember the regular SIG handlers while ours are installed. */
+static void (*old_interrupt_handler) (int);
+static void (*old_terminate_handler) (int);
+static void terminate_once (int signo);
+static void disable_terminate (void);
+static void enable_terminate (void);
+
+/******************************************************************************/
+
+#define BENEATH linux_aware_ops.beneath
+
+struct lkd_private_data lkd_private;
+
+/***************************** Added commands *********************************/
+static char linux_awareness_doc[] = "";
+
+/* The cmd_list_element where we register all the 'set
+ linux-awareness...' and 'show linux-awareness...' commands. */
+struct cmd_list_element *set_linux_awareness_cmd_list;
+struct cmd_list_element *show_linux_awareness_cmd_list;
+
+/* The definition of the log domains and the storage for their
+ associated log levels. */
+struct debug_domain linux_aware_debug_domains_info[] = {
+ {"debug-task", 0},
+ {"debug-target", 0},
+ {"debug-init", 0},
+ {NULL, 0}
+};
+
+/* The definition of the log domains and the storage for their
+ associated log levels. */
+struct linux_awareness_params lkd_params = {
+ .enabled = 0,
+ .loaded = LKD_NOTLOADED,
+ .enable_task_awareness = 1,
+ .auto_activate = 1,
+ .skip_schedule_frame = 0, /*RnDCT0001394: changed default behavior */
+ .no_colors = 0,
+ .loglevel = 0
+};
+
+
+/* 'set target-root-prefix' is introduced as an alias for 'set
+ solib-absolute-prefix', this variable contains a pointer to the value
+ of that variable. */
+char **target_root_prefix;
+static int target_root_prefix_dirty = 1;
+
+/********************************* GDB glue ***********************************/
+
+/* The target ops that adds the linux awareness. */
+struct target_ops linux_aware_ops;
+
+/* The structure that gives access to target-dependent knowledge
+ required by the Linux awareness layer. Declared in lkd.h
+ and defined in a targetting file (eg. lkd-arm.c). */
+struct linux_awareness_ops *linux_awareness_ops;
+
+/**************************** Execution control *******************************/
+
+/* This ugly variable is a way to pass information from the
+ target_ops->to_resume to the target_ops->to_wait callbacks. It
+ indicates if the last execution request was a singlstep. It's used
+ in linux_aware_wait to perform additional step in some
+ circumstances. */
+int lkd_stepping;
+
+/* A simple flag indicating that the target is running (ie. we are
+ between a target_resume () and a target_wait () call). This is used
+ to properly disconnect when the debugger is killed by a signal. */
+static int running;
+
+/* This is the observer registered for the normal_stop event. The
+ observer callback (normal_stop_callback ()) is called when the user
+ gets the hand back after a target execution. It's used to collect
+ some information and to cleanup some state. */
+static struct observer *normal_stop_observer;
+
+/******************************* Function Prototypes **************************/
+
+void set_skip_schedule_frame (char *arg, int from_tty,
+ struct cmd_list_element *c);
+
+/*********************** Addresses and Structure descriptions *****************/
+
+/* The Linux Awareness Layer needs to know a lot about the addresses
+ and layout of the data structures used in the kernel. This is
+ handled through these declaration and the associated macros and
+ functions (see linux-awareness.h). */
+
+/* Storage for the field layout and addresses already gathered. */
+struct field_info *field_info;
+struct addr_info *addr_info;
+
+/* Declaration of the required addresses. */
+DECLARE_ADDR (start_kernel);
+DECLARE_ADDR (do_exit);
+
+DECLARE_ADDR (try_to_unmap);
+
+DECLARE_ADDR (linux_banner);
+
+DECLARE_ADDR (system_utsname);
+DECLARE_ADDR (init_uts_ns);
+
+/* Structure fields */
+
+DECLARE_FIELD (list_head, next);
+DECLARE_FIELD (new_utsname, release);
+
+
+DECLARE_FIELD (pid_namespace, last_pid);
+DECLARE_FIELD (qstr, len);
+DECLARE_FIELD (qstr, name);
+
+DECLARE_FIELD (task_struct, mm);
+DECLARE_FIELD (task_struct, tasks);
+DECLARE_FIELD (task_struct, children);
+DECLARE_FIELD (task_struct, thread_group);
+DECLARE_FIELD (task_struct, sibling);
+DECLARE_FIELD (task_struct, pid);
+DECLARE_FIELD (task_struct, tgid);
+DECLARE_FIELD (task_struct, namespace);
+DECLARE_FIELD (task_struct, nsproxy);
+DECLARE_FIELD (task_struct, prio);
+DECLARE_FIELD (task_struct, cred);
+DECLARE_FIELD (task_struct, comm); /* far offset in the task_struct, to bulk-read everything needed. */
+
+
+DECLARE_FIELD (thread_info, preempt_count);
+DECLARE_FIELD (uts_namespace, name);
+
+
+int max_cores = MAX_CORES;
+
+/****************************** Task handling *********************************/
+
+/* The internal breakpoints used to be notified of interesting task
+ events. */
+static struct breakpoint *thread_event_low_mem_bp;
+static struct breakpoint *thread_event_do_exec_bp;
+static struct breakpoint *thread_event_do_exit_bp;
+static struct breakpoint *thread_event_do_exec_return_bp;
+
+/* This value is incremented and decremented by
+ thread_awareness_(in|ex)hibit (). The target actions (eg. read
+ register) requested by the platform specific part of the linux
+ awareness are guaranteed to access the real state of the target,
+ whatever the user selected as current task in the debugger. To
+ achieve this, this variable is incremented/decremented around the
+ calls to linux_awareness_ops, and tested in each target access
+ method. */
+static int _inhibit_thread_register_awareness;
+
+/* the core that triggered the event (zero-based)*/
+int stop_core = 0;
+/* When doing userspace debug, there's the very annoying limitation
+ that the debugged application creation isn't handled by the
+ debugger, but by the user in an environement decorelated from the
+ debugger. This precludes using the debugger to debug an application
+ from the start.
+ To overcome that annoying limitation, tha command 'wait_exe' has
+ been added. It's used like that:
+
+ (gdb) wait_exe foo
+ Type commands that will be executed the next time the binary is exec'd:
+ End with a line saying just "end".
+ >b main
+ >p global = 1
+ >end
+
+ This tells the debugger to watch for process creation, and that
+ when a process called 'foo' is launched, the debugger should
+ execute 'b main' and 'p global = 1' before the process is allowed
+ to start executing. Once the user launches the process it should
+ then stop on main. Of course this is just an example; the
+ machinery is generic enough to allow many other uses.
+
+ Note that putting a control execution command (next, continue...)
+ inside the wait_exe command list will break things (Unfortunately
+ it's not possible to detect it genericaly).
+
+ The below struct and list contains the wait_exe requests currently
+ in fly.
+ */
+struct waited_exe
+{
+ struct waited_exe *next;
+ char *name;
+ struct command_line *cmds;
+ uid_t uid;
+} *waited_exes = NULL;
+
+/* The bellow data structures deal with the handling of breakpoints on
+ virtual memory pages that aren't mapped to memory yet. The idea is
+ to put a watchpoint on the page table entry that represents the
+ page and to associate commands that create the final breakpoint
+ with the watchpoint. */
+struct bp_list
+{
+ struct bp_list *next;
+ struct breakpoint *b;
+};
+
+
+/* This key is used to store a reference count associated with GDB
+ objfiles. Objfiles are shared between userspace thread of the same
+ application, thus we reference count them to know when we can
+ discard the information. */
+const struct objfile_data *linux_uprocess_objfile_data_key;
+
+/****************** Local functions forward declarations **********************/
+static void normal_stop_callback (struct bpstats *bs, int);
+
+/***************** End Local functions forward declarations *******************/
+
+/* Called by ADDR to fetch the address of a symbol declared using
+ DECLARE_ADDR. */
+int
+linux_init_addr (struct addr_info *addr, int check)
+{
+ if (addr->bmsym.minsym)
+ return 1;
+
+ addr->bmsym = lookup_minimal_symbol (addr->name, NULL, NULL);
+
+ if (addr->bmsym.minsym)
+ {
+ DEBUG (D_INIT, 4, "Checking for address of '%s' : OK\n", addr->name);
+ }
+ else
+ {
+ DEBUG (D_INIT, 1, "Checking for address of '%s' : NOT FOUND\n",
+ addr->name);
+ if (!check)
+ error ("Couldn't find address of %s", addr->name);
+ return 0;
+ }
+
+ /* Chain initialized entries for cleanup. */
+ addr->next = addr_info;
+ addr_info = addr;
+
+ DEBUG (D_INIT, 4, "%s address is %s\n", addr->name,
+ phex (BMSYMBOL_VALUE_ADDRESS (addr->bmsym), 4));
+ return 1;
+}
+
+/* Helper for linux_init_field. */
+static int
+find_struct_field (struct type *type, char *field, int *offset, int *size)
+{
+ int i;
+
+ for (i = 0; i < TYPE_NFIELDS (type); ++i)
+ {
+ if (!strcmp (FIELD_NAME (TYPE_FIELDS (type)[i]), field))
+ break;
+ }
+
+ if (i >= TYPE_NFIELDS (type))
+ return 0;
+
+ *offset = FIELD_BITPOS (TYPE_FIELDS (type)[i]) / TARGET_CHAR_BIT;
+ *size = TYPE_LENGTH (check_typedef (TYPE_FIELDS (type)[i].type));
+ return 1;
+}
+
+/* Called by F_OFFSET or F_SIZE to compute the description of a field
+ declared using DECLARE_FIELD. */
+int
+linux_init_field (struct field_info *field, int check)
+{
+ if (field->type != NULL)
+ return 1;
+
+ field->type =
+ lookup_symbol (field->struct_name, NULL, STRUCT_DOMAIN, NULL).symbol;
+ if (field->type)
+ {
+ DEBUG (D_INIT, 4, "Checking for 'struct %s' : OK\n",
+ field->struct_name);
+ }
+ else
+ {
+ field->type = lookup_symbol (field->struct_name,
+ NULL, VAR_DOMAIN, NULL).symbol;
+
+ if (field->type
+ && TYPE_CODE (check_typedef (SYMBOL_TYPE (field->type)))
+ != TYPE_CODE_STRUCT)
+ field->type = NULL;
+
+ if (field->type != NULL)
+ DEBUG (D_INIT, 4, "Checking for 'struct %s' : TYPEDEF\n",
+ field->struct_name);
+ else
+ DEBUG (D_INIT, 1, "Checking for 'struct %s' : NOT FOUND\n",
+ field->struct_name);
+ }
+
+ if (field->type == NULL
+ || !find_struct_field (check_typedef (SYMBOL_TYPE (field->type)),
+ field->field_name, &field->offset, &field->size))
+ {
+ field->type = NULL;
+ if (!check)
+ error ("No such field %s::%s\n", field->struct_name,
+ field->field_name);
+
+ return 0;
+ }
+
+ /* Chain initialized entries for cleanup. */
+ field->next = field_info;
+ field_info = field;
+
+ DEBUG (D_INIT, 4, "%s::%s => offset %i size %i\n", field->struct_name,
+ field->field_name, field->offset, field->size);
+ return 1;
+}
+
+/* Cleanup all the field and address info that has been gathered. */
+static void
+fields_and_addrs_clear (void)
+{
+ struct field_info *next_field = field_info;
+ struct addr_info *next_addr = addr_info;
+
+ while (next_field)
+ {
+ next_field = field_info->next;
+ field_info->type = NULL;
+ field_info->next = NULL;
+ field_info = next_field;
+ }
+
+ while (next_addr)
+ {
+ next_addr = addr_info->next;
+ addr_info->bmsym.minsym = NULL;
+ addr_info->bmsym.objfile = NULL;
+ addr_info->next = NULL;
+ addr_info = next_addr;
+ }
+}
+
+/* If this returns true, we want to access the target registers and
+ memory whatever the user nominated as the current task. */
+static int
+thread_awareness_inhibited (void)
+{
+ return !lkd_params.enable_task_awareness
+ || _inhibit_thread_register_awareness;
+}
+
+/* See the description of _inhibit_thread_register_awareness. */
+static void
+thread_awareness_inhibit (void)
+{
+ ++_inhibit_thread_register_awareness;
+}
+
+/* See the description of _inhibit_thread_register_awareness. */
+static void
+thread_awareness_exhibit (void *unused)
+{
+ --_inhibit_thread_register_awareness;
+}
+
+/* Remove the trailing white spaces from target-root-prefix.
+ FIXME: do this when the string is input, not all the time !
+ */
+void
+sanitize_path (char *path)
+{
+ char *dir = path + strlen (path) - 1;
+ while (dir > path && isspace (*dir))
+ {
+ *dir-- = '\0';
+ };
+ /* remove trailing '/' too.
+ **/
+ if (*dir == '/')
+ *dir = '\0';
+}
+
+char *
+linux_aware_get_target_root_prefix (void)
+{
+ if (target_root_prefix_dirty && *target_root_prefix)
+ {
+ sanitize_path (*target_root_prefix);
+ target_root_prefix_dirty = 0;
+ }
+ return *target_root_prefix;
+}
+
+
+/****************************************************************************/
+
+/* Reads the Linux version string from memory
+ * into global lkd_private.utsname_release. */
+static int
+set_utsname_release (void)
+{
+ int i = 0;
+ CORE_ADDR release_addr;
+ asection *data;
+ int ret = 0;
+ int build = 0;
+
+ gdb_assert (lkd_private.utsname_release);
+
+ lkd_private.utsname_release[0] = '\0';
+ lkd_private.utsname_release_valid = 0;
+
+ /* Find the address of the string. */
+ if (HAS_ADDR (init_uts_ns)
+ && HAS_FIELD (uts_namespace, name) && HAS_FIELD (new_utsname, release))
+ release_addr = ADDR (init_uts_ns)
+ + F_OFFSET (uts_namespace, name) + F_OFFSET (new_utsname, release);
+ else if (HAS_ADDR (system_utsname) && HAS_FIELD (new_utsname, release))
+ release_addr = ADDR (system_utsname) + F_OFFSET (new_utsname, release);
+ else
+ return -1;
+
+ ret = target_read_memory (release_addr,
+ (gdb_byte *) (lkd_private.utsname_release),
+ lkd_private.utsname_release_size);
+
+ lkd_private.utsname_release_valid = ret ? 0 : 1;
+
+ return ret;
+}
+
+/******************************************************************************/
+/***************** TASK AWARENESS ******************/
+/******************************************************************************/
+
+/* target_ops callback that GDB queries to know if the given PTID is
+ still an active task. */
+static int
+linux_aware_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ /* if we are resetting the thread list, return false
+ * because shtdi will return true for the h/w thread
+ * and this would lead to keep a previous occurrence of the ptid.
+ */
+ if (lkd_private.loaded == LKD_LOADING)
+ return 0;
+
+ if (lkd_private.loaded == LKD_NOTLOADED)
+ {
+ if (BENEATH && BENEATH->to_thread_alive)
+ return BENEATH->to_thread_alive (ops, ptid);
+ return 0; /* GDB default */
+ }
+
+ return (lkd_proc_get_by_ptid (ptid) != NULL);
+}
+
+/* target_ops callback that GDB queries to know core id of the given PTID. */
+static int
+linux_aware_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ if (lkd_private.loaded == LKD_LOADED)
+ return lkd_proc_core_of_thread (ptid);
+ else if (BENEATH->to_core_of_thread)
+ return BENEATH->to_core_of_thread (ops, ptid);
+
+ return CORE_INVAL;
+}
+
+/* target_ops callback that GDB queries add new threads to its thread
+ list. */
+static void
+linux_aware_update_thread_list (struct target_ops *ops)
+{
+ if (lkd_private.loaded != LKD_LOADED)
+ {
+ /* in case the user attaches, but did not yet set the target pack. */
+ if (BENEATH->to_update_thread_list)
+ BENEATH->to_update_thread_list (ops); /* may not exist */
+ }
+ else
+ lkd_proc_get_list ();
+}
+
+/*
+ * the default post exec stop handler tries to activatie L-A
+ * if it previously failed
+ **/
+static void
+linux_aware_post_exec_stop (int step)
+{
+ if (!step && lkd_private.connected /*make sure kernel code is in target-ram */
+ && (lkd_private.loaded == LKD_NOTLOADED)
+ && (lkd_params.auto_activate == 1))
+ {
+ CORE_ADDR pc = regcache_read_pc (get_current_regcache ());
+
+ /* if we broke in start_kernel, we may be about to execute
+ * the user "set" commands, do not rely on lkd_params yet.
+ **/
+ if (pc == ADDR (start_kernel))
+ return;
+ /* In case we need to delay set loaded by the time the mmu is correctly set,
+ * this will be set at latest by the time the user breaks
+ **/
+ lkd_params.loaded = LKD_LOADED;
+ lkd_loaded_set (NULL, 0, NULL);
+ }
+}
+
+extern void nullify_last_target_wait_ptid (void);
+
+static int
+dump_thread_list (struct thread_info *tp, void *ignored)
+{
+ printf_filtered ("thread_list: {%d.%d}= {%d-%ld-%ld}\n",
+ tp->global_num, tp->per_inf_num,
+ ptid_get_pid (tp->ptid),
+ ptid_get_lwp (tp->ptid), ptid_get_tid (tp->ptid));
+ return 0;
+}
+
+void
+lkd_reset_thread_list (void)
+{
+ struct thread_info *tp = NULL;
+ int pid = ptid_get_pid (inferior_ptid);
+ int loaded_state = lkd_private.loaded;
+ struct cleanup *cleanup;
+
+ switch_to_thread (null_ptid); /* Ensure inferior_ptid is invalid. */
+
+ /* remove all gdb threads, we need to call this as
+ * gdb and complying targets assume there is always a thread
+ * but we need to manage our own thread numbering.
+ */
+ init_thread_list ();
+
+ DBG_IF (D_INIT)
+ iterate_over_threads (dump_thread_list, NULL);
+ DBG_ENDIF (D_INIT)
+ /* setup LKD "thread alive" method to return FALSE and "find new threads"
+ method to call the BENEATH version */
+ lkd_private.loaded = LKD_LOADING;
+
+ cleanup = make_cleanup_restore_integer (&print_thread_events);
+ print_thread_events = 0;
+ update_thread_list ();
+ do_cleanups (cleanup);
+
+ DBG_IF (D_INIT)
+ iterate_over_threads (dump_thread_list, NULL);
+ DBG_ENDIF (D_INIT)
+
+ tp = any_live_thread_of_process (pid);
+ gdb_assert (tp != NULL); /* A live thread must exist. */
+ switch_to_thread (tp->ptid);
+
+ /* make sure update_inferior_thread will not switch to a wait_ptid
+ * that is no-more upon resuming
+ */
+ nullify_last_target_wait_ptid ();
+
+ /* return LKD "thread alive" and "find new threads" methods to their
+ natural states */
+ lkd_private.loaded = loaded_state;
+}
+
+/* target_ops callback that GDB queries to translate a PTID to a human
+ readable string to display. */
+static char *
+linux_aware_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+ process_t *ps;
+ struct thread_info *tp;
+
+ if (lkd_private.loaded == LKD_NOTLOADED)
+ {
+ if (BENEATH && BENEATH->to_pid_to_str)
+ return BENEATH->to_pid_to_str (ops, ptid);
+ return normal_pid_to_str (ptid); /* GDB default */
+ }
+
+ if (!ptid_get_tid (ptid)) /* when quitting typically */
+ return "Linux Kernel";
+
+ tp = find_thread_ptid (ptid);
+
+ if (!tp || !tp->priv)
+ return "";
+
+ /* we use the gdb thread private field for storing the process_t */
+ ps = (process_t *) tp->priv;
+
+ gdb_assert (ps->comm);
+ return ps->comm;
+}
+
+/* extra informations with display options
+ **/
+static char *
+extra_thread_info_ext (struct thread_info *thread)
+{
+ static char msg[256];
+ char *pos = msg, *ret;
+ process_t *ps;
+ int core;
+
+ if (!(tui_active || lkd_params.no_colors))
+ pos += sprintf (pos, "\e[30m");
+ else
+ *pos = '\0';
+ ret = pos; /* skip color setting by default */
+
+ ps = (process_t *) thread->priv;
+
+ DBG_IF (TASK) if (!ps)
+ {
+ printf_filtered ("thread_info %p not found in process_list\n", thread);
+ printf_filtered (" thread_info.ptid = {%d, %ld, %ld}\n",
+ ptid_get_pid (thread->ptid),
+ ptid_get_lwp (thread->ptid),
+ ptid_get_tid (thread->ptid));
+ printf_filtered (" thread_info.num = %d.%d\n", thread->global_num,
+ thread->per_inf_num);
+ }
+ DBG_ENDIF (TASK) if (ps)
+ {
+ core = ps->core;
+
+ DBG_IF (TASK) if (core != CORE_INVAL)
+ pos += sprintf (pos,
+ "lwp=%li, tid=%li, gdbt=%lx, task_str=%lx-%lx",
+ ptid_get_lwp (PTID_OF (ps)),
+ ptid_get_tid (PTID_OF (ps)),
+ (unsigned long) ps->gdb_thread,
+ (unsigned long) ps->task_struct,
+ (unsigned long) lkd_proc_get_rq_curr (core));
+ else
+ pos += sprintf (pos,
+ "lwp=%li, tid=%li, gdbt=%lx, task_str=%lx",
+ ptid_get_lwp (PTID_OF (ps)),
+ ptid_get_tid (PTID_OF (ps)),
+ (unsigned long) ps->gdb_thread,
+ (unsigned long) ps->task_struct);
+ DBG_ELSE if (ps->tgid == ptid_get_lwp (PTID_OF (ps)))
+ /* thread group leader */
+ pos += sprintf (pos, "TGID:%i", ps->tgid);
+ else
+ /* thread of a thread group */
+ pos += sprintf (pos, "|----%li", ptid_get_lwp (PTID_OF (ps)));
+ DBG_ENDIF (TASK) if (lkd_proc_is_curr_task (ps))
+ {
+ pos += sprintf (pos, " <C%u>", core);
+
+ /* highlight currently running threads */
+ if (!(tui_active || lkd_params.no_colors))
+ {
+ ret = msg;
+ msg[3] = '1' + (2 * core);
+ strcpy (pos, "\e[m");
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* target_ops callback that GDB queries to get extra information to
+ display for a given thread. */
+static char *
+linux_aware_extra_thread_info (struct target_ops *ops,
+ struct thread_info *thread)
+{
+ if (lkd_private.loaded == LKD_LOADED)
+ {
+ /* FIXME: Bodge for STWorkbench which expects different format */
+ if (lkd_params.no_colors)
+ {
+ process_t *ps = (process_t *) thread->priv;
+
+ if (ps)
+ {
+ static char msg[256];
+ char *pos = msg;
+
+ pos += sprintf (pos, "pid: %li tgid: %i",
+ ptid_get_lwp (PTID_OF (ps)), ps->tgid);
+ if (lkd_proc_is_curr_task (ps))
+ sprintf (pos, " <C%u>", ps->core);
+
+ return msg;
+ }
+ }
+ else
+ return extra_thread_info_ext (thread);
+ }
+ else if (BENEATH->to_extra_thread_info)
+ return BENEATH->to_extra_thread_info (ops, thread);
+
+ return "";
+}
+
+static const char *
+linux_aware_thread_name (struct target_ops *ops, struct thread_info *thread)
+{
+ /* All the thread name information has generally been
+ * returned already through the pid_to_str.
+ *
+ * We could refactor this around and 'correct' the naming
+ * but then you wouldn't get niceties such as
+ * [Switching to thread 52 (getty)]
+ */
+
+ return NULL;
+}
+
+/* target_ops callback queried by GDB to read the registers of the
+ currently selected task. */
+static void
+linux_aware_fetch_registers (struct target_ops *ops,
+ struct regcache *rc, int regno)
+{
+ struct cleanup *cleanup;
+ process_t *ps;
+ int res;
+
+ DEBUG (TARGET, 2, "fetch_registers %i\n", regno);
+
+ if ((lkd_private.loaded != LKD_LOADED) /*check this first */
+ || !(ps = lkd_proc_get_by_ptid (inferior_ptid))
+ || lkd_proc_is_curr_task (ps))
+ return BENEATH->to_fetch_registers (ops, rc, regno);
+
+ /* Call the platform specific code. */
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+ res =
+ linux_awareness_ops->lo_fetch_context_register (regno, ps->task_struct);
+ do_cleanups (cleanup);
+
+ if (!res)
+ warning ("Could not fetch task register.");
+
+ return;
+}
+
+/* target_ops callback queried by GDB to write the registers of the
+ currently selected task. */
+static void
+linux_aware_store_registers (struct target_ops *ops,
+ struct regcache *rc, int regno)
+{
+ struct cleanup *cleanup;
+ process_t *ps;
+ int res;
+
+ DEBUG (TARGET, 2, "store_registers %i\n", regno);
+
+ if ((lkd_private.loaded != LKD_LOADED) /*check this first */
+ || !(ps = lkd_proc_get_by_ptid (inferior_ptid))
+ || lkd_proc_is_curr_task (ps))
+ return BENEATH->to_store_registers (ops, rc, regno);
+
+ /* Call the platform specific code. */
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+ res =
+ linux_awareness_ops->lo_store_context_register (regno, ps->task_struct);
+ do_cleanups (cleanup);
+
+ if (res)
+ warning ("Could not store task register.");
+
+ return;
+}
+
+
+/* This is the target_ops callback that is called by GDB to start the
+ execution of the processor. */
+static void
+linux_aware_resume (struct target_ops *ops,
+ ptid_t pid, int step, enum gdb_signal sig)
+{
+ struct cleanup *cleanup;
+
+ DEBUG (TARGET, 1, "Resuming %i with sig %i (step %i)\n",
+ (int) ptid_get_pid (pid), (int) sig, step);
+
+ /* Store the last execution request type. See the stepping
+ variable comment above, and the usage in linux_aware_wait. */
+ lkd_stepping = step;
+
+ /* Call platform dependant resume callback if needed. */
+ if (linux_awareness_ops && linux_awareness_ops->lo_pre_exec_start)
+ {
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+ linux_awareness_ops->lo_pre_exec_start ();
+ do_cleanups (cleanup);
+ }
+
+ if (lkd_private.loaded != LKD_LOADED)
+ return BENEATH->to_resume (ops, pid, step, sig);
+
+ if (thread_event_do_exec_bp && thread_event_do_exec_return_bp)
+ {
+ delete_breakpoint (thread_event_do_exec_bp);
+ thread_event_do_exec_bp = NULL;
+ }
+
+ lkd_uninstall_do_exit_event ();
+
+ /* Before restarting first need to clear some caches. */
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+ linux_awareness_ops->lo_clear_cache ();
+ do_cleanups (cleanup);
+
+ /* Perform the execution request. */
+ BENEATH->to_resume (ops, pid, step, sig);
+
+
+ /* Set the running flag. (See the variable's comment). */
+ running = 1;
+}
+
+/* This is the target_ops callback that is called by GDB to wait for the
+ processor to stop executing. */
+static ptid_t
+linux_aware_wait (struct target_ops *ops,
+ ptid_t ptid, struct target_waitstatus *status, int opts)
+{
+ struct cleanup *cleanup;
+ ptid_t stop_ptid;
+
+ /* We aren't running anymore. */
+ running = 0;
+
+ if (thread_awareness_inhibited () || !(lkd_private.kflags & KFLAG_DBGINFO))
+ return BENEATH->to_wait (ops, ptid, status, opts);
+
+ /* The linux aware wait begins here. */
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+
+ stop_ptid = BENEATH->to_wait (ops, ptid, status, opts);
+ if (max_cores > 1)
+ stop_core = ptid_get_tid (stop_ptid) - 1;
+ else
+ stop_core = 0;
+
+ disable_terminate ();
+
+ /*reset the inferior_ptid to the stopped ptid */
+ inferior_ptid = stop_ptid;
+
+ /* if we are not just stepping, check for auto-activation */
+ linux_aware_post_exec_stop (lkd_stepping);
+
+ if (lkd_private.loaded == LKD_LOADED)
+ {
+ CORE_ADDR pc;
+ CORE_ADDR task;
+ int i;
+ struct regcache *regcache;
+
+ /* rescan for new task, but avoid storming the debug connection
+ **/
+ lkd_proc_refresh_info (stop_core);
+
+ /* The above calls might will end up accessing the registers
+ of the target because of inhibit_thread_awareness(). However,
+ this will populate a register cache associated with
+ inferior_ptid, which we haven't updated yet. Force a flush
+ of these cached values so that they end up associated to
+ the right context. */
+ registers_changed ();
+
+ /* This is normally done by infrun.c:handle_inferior_event (),
+ but we need it set to access the frames for some operations
+ below (eg. in check_exec_actions (), where we don't know
+ what the user will ask in his commands. */
+ set_executing (minus_one_ptid, 0);
+
+ regcache = get_thread_regcache (inferior_ptid);
+
+ pc = regcache_read_pc (regcache);
+
+ /*
+ * Handle the installation of the module's specific init routine hook.
+ **/
+ thread_awareness_inhibit ();
+ make_cleanup (thread_awareness_exhibit, NULL);
+
+ /* pull-in the symbols of each core's running process if auto-debug
+ * is activated
+ **/
+ target_root_prefix_dirty = 1; // fixme: code a proper command handler.
+
+ /* wait_process is non-null once we've successfully loaded lkd,
+ * and we could compute the current process for that core.
+ **/
+ if (wait_process)
+ {
+ inferior_ptid = PTID_OF (wait_process);
+ stop_ptid = inferior_ptid;
+ }
+ }
+
+ do_cleanups (cleanup);
+
+ enable_terminate ();
+
+ return stop_ptid;
+}
+
+/*
+ * arch-common post load op
+ **/
+
+/*
+ * try to find a BFD
+ **/
+static bfd *
+get_cur_bfd (int from_tty)
+{
+ /*give precedence to symfile */
+ if (symfile_objfile && symfile_objfile->obfd)
+ return symfile_objfile->obfd;
+
+ if (from_tty && !exec_bfd)
+ printf_filtered ("No executable file specified\n");
+
+ return exec_bfd;
+}
+
+/* the post load hook, and the callback to re-install
+ * what ever LKD patches into the gdb arch.*/
+static void
+linux_aware_post_load (char *prog, int fromtty)
+{
+ DEBUG (D_INIT, 1, "linux_aware_POST_load %s\n", prog);
+
+ /*let the arch specific part decide when loaded = 1 */
+ if (linux_awareness_ops->lo_post_load)
+ linux_awareness_ops->lo_post_load (prog, fromtty);
+
+}
+
+/* The target_ops callback called by GDB to load the debugged program
+ to the target. Just a wrapper for BENEATH->to_load with hooks that
+ call into the platform specific part. */
+static void
+linux_aware_load (struct target_ops *ops, const char *prog, int fromtty)
+{
+ DEBUG (D_INIT, 1, "linux_aware_load %s\n", prog);
+
+ /* make sure the load conditions are met, so that putting
+ 'set linux-awareness loaded 1' in his .shgdbinit file does not crash. */
+
+ if ((BENEATH->to_shortname[0] == 'e' /*exec */ )
+ || (BENEATH->to_shortname[0] == 'n' /*none */ ))
+ {
+ execute_command ("maint print target-stack", 0);
+ error ("underlying target is not valid (might be exec or none).\n");
+ }
+
+ BENEATH->to_load (ops, prog, fromtty);
+
+ lkd_params.loaded = LKD_NOTLOADED;
+ lkd_private.loaded = LKD_NOTLOADED;
+
+ if (lkd_params.auto_activate)
+ {
+ lkd_params.loaded = LKD_LOADED;
+ lkd_loaded_set (0, 0, 0);
+ }
+
+ lkd_private.connected = 1;
+}
+
+/* The target_ops callback called by GDB to load the attach to an
+ already running program. Just sets 'loaded' to 1, as the program is
+ already loaded. If you attach with a non standard command, you have
+ to do 'set linux-awareness loaded 1' by hand. */
+static void
+linux_aware_attach (struct target_ops *ops, const char *prog, int fromtty)
+{
+ DEBUG (D_INIT, 1, "linux_aware_attach %s\n", prog);
+
+ if (BENEATH && BENEATH->to_attach != NULL)
+ BENEATH->to_attach (ops, prog, fromtty);
+
+ if (lkd_params.auto_activate)
+ {
+ lkd_params.loaded = LKD_LOADED;
+ lkd_loaded_set (0, 0, 0);
+ }
+
+ lkd_private.connected = 1;
+ DEBUG (TARGET, 3, "end linux_aware_attach %s\n", prog);
+}
+
+/* Used to trigger the disconnection. */
+static void
+linux_aware_disconnect (struct target_ops *target, const char *args,
+ int from_tty)
+{
+ DEBUG (D_INIT, 1, "linux_aware_disconnect\n");
+
+ unpush_target (&linux_aware_ops);
+ target_disconnect (args, from_tty);
+
+ lkd_private.connected = 0;
+}
+
+/* This callback is called on 'normal stop', ie. when the user gets
+ the control back. We do various bookkeeping at this point. */
+static void
+normal_stop_callback (struct bpstats *bs, int unsused)
+{
+ /* If there's no userspace breakpoint left, remove the breakpoints
+ we use to notify about conditions we need to handle like 'out of
+ memory' or 'process exit'. */
+ if (thread_event_low_mem_bp != NULL)
+ {
+ int has_bp = 0;
+ struct bp_location *loc;
+ struct breakpoint *bp;
+
+ ALL_BREAKPOINTS (bp) for (loc = bp->loc; loc; loc = loc->next)
+ {
+ if (loc->address
+ && linux_awareness_ops->lo_is_user_address (loc->address))
+ {
+ has_bp = 1;
+ break;
+ }
+ }
+
+ if (!has_bp)
+ {
+ if (thread_event_low_mem_bp != NULL)
+ {
+ delete_breakpoint (thread_event_low_mem_bp);
+ thread_event_low_mem_bp = NULL;
+ }
+ }
+ }
+}
+
+/* target_has_all_memory() target_ops callback.
+ Note that if a beneath target exists then return 0 to indicate that the
+ beneath target to be used if this target cannot handle the request. */
+static int
+linux_aware_has_all_memory (struct target_ops *ops)
+{
+ if (BENEATH && BENEATH->to_has_all_memory)
+ return 0;
+ return default_child_has_all_memory (ops);
+}
+
+/* target_has_memory() target_ops callback. */
+static int
+linux_aware_has_memory (struct target_ops *ops)
+{
+ if (BENEATH && BENEATH->to_has_memory)
+ return BENEATH->to_has_memory (ops);
+ return default_child_has_memory (ops);
+}
+
+/* target_has_stack() target_ops callback. */
+static int
+linux_aware_has_stack (struct target_ops *ops)
+{
+ if (BENEATH && BENEATH->to_has_stack)
+ return BENEATH->to_has_stack (ops);
+ return default_child_has_stack (ops);
+}
+
+/* target_has_registers() target_ops callback. */
+static int
+linux_aware_has_registers (struct target_ops *ops)
+{
+ if (BENEATH && BENEATH->to_has_registers)
+ return BENEATH->to_has_registers (ops);
+ return default_child_has_registers (ops);
+}
+
+/* target_has_execution() target_ops callback. */
+static int
+linux_aware_has_execution (struct target_ops *ops, ptid_t ptid)
+{
+ if (BENEATH && BENEATH->to_has_execution)
+ return BENEATH->to_has_execution (ops, ptid);
+ return default_child_has_execution (ops, ptid);
+}
+
+/* target_close() target_ops callback. */
+static void
+linux_aware_close (struct target_ops *ops)
+{
+ struct target_waitstatus dummy;
+
+ DEBUG (D_INIT, 1, "Closing... \n");
+
+ /* We might be called by a signal handler */
+ if (running)
+ {
+ target_stop (inferior_ptid);
+ if (BENEATH != NULL && BENEATH->to_wait != NULL)
+ BENEATH->to_wait (&linux_aware_ops, minus_one_ptid, &dummy, 0);
+ }
+
+ lkd_params.enabled = 0;
+ lkd_enabled_set (0, 0, 0);
+
+ if (normal_stop_observer)
+ {
+ observer_detach_normal_stop (normal_stop_observer);
+ normal_stop_observer = NULL;
+ }
+
+ wait_process = NULL;
+
+ _inhibit_thread_register_awareness = 0;
+
+ if (thread_event_low_mem_bp)
+ {
+ delete_breakpoint (thread_event_low_mem_bp);
+ thread_event_low_mem_bp = NULL;
+ }
+
+
+ lkd_proc_free_list ();
+
+ fields_and_addrs_clear ();
+
+ if (running)
+ {
+ /* If we leave the board run, we'd better remove breakpoints
+ so that it's functional. */
+ remove_breakpoints ();
+
+ if (BENEATH != NULL && BENEATH->to_resume != NULL)
+ BENEATH->to_resume (&linux_aware_ops, inferior_ptid, 0, 0);
+ }
+
+ lkd_private.banner_file_valid = 0;
+ lkd_private.banner_mem_valid = 0;
+ running = 0;
+}
+
+static void
+linux_aware_files_info (struct target_ops *target)
+{
+ printf_filtered (_("Connected to remote linux kernel\n"));
+}
+
+static int
+linux_aware_can_async_p (struct target_ops *ops)
+{
+ return 0;
+}
+
+static int
+linux_aware_is_async_p (struct target_ops *ops)
+{
+ return 0;
+}
+
+/* Setup the target_ops callbacks. */
+static void
+init_linux_aware_target (void)
+{
+ DEBUG (D_INIT, 1, "init_linux_aware_target\n");
+
+ linux_aware_ops.to_shortname = "linux-aware";
+ linux_aware_ops.to_longname = "Linux-aware target interface";
+ linux_aware_ops.to_doc = linux_awareness_doc;
+
+ /* Dirty hook to stack above anythin else, event something above
+ the thread stratum (like starm) */
+ linux_aware_ops.to_stratum = LKD_STRATUM_LINUX;
+
+ linux_aware_ops.to_load = linux_aware_load;
+ linux_aware_ops.to_close = linux_aware_close;
+ linux_aware_ops.to_attach = linux_aware_attach;
+ linux_aware_ops.to_disconnect = linux_aware_disconnect;
+ linux_aware_ops.to_magic = OPS_MAGIC;
+
+
+ /* Registers */
+ linux_aware_ops.to_fetch_registers = linux_aware_fetch_registers;
+ linux_aware_ops.to_store_registers = linux_aware_store_registers;
+
+ /* Execution */
+ linux_aware_ops.to_resume = linux_aware_resume;
+ linux_aware_ops.to_wait = linux_aware_wait;
+
+ /* Threads */
+ linux_aware_ops.to_thread_alive = linux_aware_thread_alive;
+ linux_aware_ops.to_update_thread_list = linux_aware_update_thread_list;
+ linux_aware_ops.to_pid_to_str = linux_aware_pid_to_str;
+ linux_aware_ops.to_extra_thread_info = linux_aware_extra_thread_info;
+ linux_aware_ops.to_thread_name = linux_aware_thread_name;
+ linux_aware_ops.to_core_of_thread = linux_aware_core_of_thread;
+ linux_aware_ops.to_has_thread_control = tc_none;
+
+ linux_aware_ops.to_has_all_memory = linux_aware_has_all_memory;
+ linux_aware_ops.to_has_memory = linux_aware_has_memory;
+ linux_aware_ops.to_has_stack = linux_aware_has_stack;
+ linux_aware_ops.to_has_registers = linux_aware_has_registers;
+ linux_aware_ops.to_has_execution = linux_aware_has_execution;
+ linux_aware_ops.to_files_info = linux_aware_files_info;
+
+ /* Prevent Async operations
+ * LKD doesn't yet support ASync,
+ * Particularly on connect/resume, which can break things
+ * when connecting to an async target such as QEmu
+ */
+ linux_aware_ops.to_can_async_p = linux_aware_can_async_p;
+ linux_aware_ops.to_is_async_p = linux_aware_is_async_p;
+}
+
+
+
+/* This function is here to replace the default display for
+ breakpoints set on code that has been unloaded. This display
+ routine doesn't display the 0xFFFFFFFF that could confuse the
+ user. */
+static void
+init_bp_mention (struct breakpoint *bpt)
+{
+ bpt->ops = NULL;
+ printf_filtered (_("Breakpoint %d (%s) pending."),
+ bpt->number, event_location_to_string (bpt->location));
+}
+
+static struct breakpoint_ops init_breakpoints_ops = {
+ .print_mention = init_bp_mention
+};
+
+static void
+lkd_install_do_exit_event (void)
+{
+/* install hook for do_exit */
+ if (thread_event_do_exit_bp == NULL)
+ {
+ thread_event_do_exit_bp =
+ create_thread_event_breakpoint (target_gdbarch (), ADDR (do_exit));
+
+ /*do no remove on resume */
+ lkd_private.keep_do_exit_event = 1;
+ }
+}
+
+void
+lkd_uninstall_do_exit_event (void)
+{
+ /* uninstall hook for do_exit */
+ if ((thread_event_do_exit_bp != NULL) && (!lkd_private.keep_do_exit_event))
+ {
+ delete_breakpoint (thread_event_do_exit_bp);
+ thread_event_do_exit_bp = NULL;
+ }
+}
+
+/* This callback is called when a new breakpoint is created. */
+static void
+linux_aware_create_breakpoint_hook (struct breakpoint *bpt)
+{
+ struct lm_info *info;
+
+ if (lkd_private.loaded != LKD_LOADED)
+ return;
+
+
+ if (bpt->loc && bpt->loc->address == ~(CORE_ADDR) 0)
+ {
+ warning
+ ("You have inserted a breakpoint on a location that is not currently\n"
+ "mapped to memory (it is flagged as __init code and the initialization\n"
+ "phase of the module has completed). The breakpoint will be reset if you\n"
+ "reload the module.");
+ /* Display correct breakpoint info and disable the breakpoint. */
+ bpt->ops = &init_breakpoints_ops;
+ bpt->loc->shlib_disabled = 1;
+ }
+ else if (bpt->loc
+ && bpt->loc->address
+ && bpt->loc->loc_type == bp_loc_software_breakpoint
+ && linux_awareness_ops->lo_is_user_address (bpt->loc->address))
+ {
+ CORE_ADDR addr = bpt->loc->address;
+
+ /* All the userspace breakpoints are set to a specific task. */
+ bpt->thread = ptid_to_global_thread_id (inferior_ptid);
+
+
+ lkd_install_do_exit_event ();
+
+ /*Q: does setting a bpt to a usermode pages prevent Linux
+ * to unmap it ? This seems to say so.*/
+ if (HAS_ADDR (try_to_unmap))
+ {
+ if (thread_event_low_mem_bp == NULL)
+ thread_event_low_mem_bp =
+ create_thread_event_breakpoint
+ (target_gdbarch (), ADDR (try_to_unmap));
+ }
+ else
+ warning ("'try_to_unmap' wasn't found.");
+ }
+}
+
+
+static char *
+get_banner_from_file (bfd * cur_bfd)
+{
+ CORE_ADDR banner_addr = ADDR (linux_banner);
+ CORE_ADDR section_addr, section_size;
+ asection *data;
+
+ gdb_assert (lkd_private.banner_file);
+
+ if (lkd_private.banner_file_valid)
+ return lkd_private.banner_file;
+
+ lkd_private.banner_file_valid = 1;
+
+ /* first try to find linux_banner in .rodata */
+ data = bfd_get_section_by_name (cur_bfd, ".rodata");
+ if (data)
+ {
+ section_addr = bfd_get_section_vma (cur_bfd, data);
+ section_size = bfd_get_section_size (data);
+ if ((banner_addr < section_addr)
+ || (banner_addr >= section_addr + section_size))
+ data = NULL;
+ }
+
+ if (!data)
+ {
+ /* then, try .text */
+ data = bfd_get_section_by_name (cur_bfd, ".text");
+ if (data)
+ {
+ section_addr = bfd_get_section_vma (cur_bfd, data);
+ section_size = bfd_get_section_size (data);
+ if ((banner_addr < section_addr)
+ || (banner_addr >= section_addr + section_size))
+ data = NULL;
+ }
+ }
+
+ if (data)
+ {
+ int i = 0;
+ char c;
+ bfd_seek (cur_bfd,
+ data->filepos + banner_addr -
+ bfd_get_section_vma (cur_bfd, data), SEEK_SET);
+ do
+ {
+ bfd_bread (&c, 1, cur_bfd);
+ lkd_private.banner_file[i++] = c;
+ }
+ while ((c != '\0') && (i < lkd_private.banner_file_size));
+ }
+ else
+ {
+ printf_filtered ("Linux banner not found in any of .text or .rodata\n");
+ lkd_private.banner_file_valid = 0;
+ }
+
+ /* for security */
+ lkd_private.banner_file[lkd_private.banner_file_size - 1] = '\0';
+
+ DEBUG (D_INIT, 1, "Got banner from file: %s\n", lkd_private.banner_file);
+
+ return lkd_private.banner_file;
+}
+
+static char *
+get_banner (void)
+{
+ if (lkd_private.banner_mem_valid)
+ return lkd_private.banner_mem;
+
+ gdb_assert (lkd_private.banner_mem);
+
+ lkd_private.banner_mem_valid = 1;
+
+ read_memory_string (ADDR (linux_banner),
+ lkd_private.banner_mem, lkd_private.banner_mem_size);
+
+ lkd_private.banner_mem[lkd_private.banner_mem_size - 1] = '\0';
+
+ DEBUG (D_INIT, 1, "Read banner from mem: %s\n", lkd_private.banner_mem);
+
+ return lkd_private.banner_mem;
+}
+
+
+
+
+/******************************************************************************/
+/************* LINUX AWARENESS INIT / AUTODETECTION / ENABLEMENT ***********/
+/******************************************************************************/
+int
+lkd_try_push_target (void)
+{
+ DEBUG (D_INIT, 1, "lkd_try_push_target\n");
+
+ if (!BENEATH)
+ push_target (&linux_aware_ops);
+
+ return LKD_LOADED;
+}
+
+/* This function is called after load, or after attach, when we know
+ that the kernel code is in memory. (This might be called direclty
+ by the user by issuing 'set linux-awareness loaded', if he doesn't
+ use a standard attach mechanism. */
+void
+lkd_loaded_set (char *arg, int from_tty, struct cmd_list_element *c)
+{
+ bfd *cur_bfd;
+ process_t *ps;
+ char *banner1;
+ char *banner2, *file_name;
+ int i;
+
+ DEBUG (D_INIT, 1, "lkd_loaded_set\n");
+
+ if (lkd_params.loaded == lkd_private.loaded)
+ return;
+
+ if ((lkd_params.loaded == LKD_LOADED) && (!lkd_params.enabled))
+ {
+ lkd_enabled_set ((char *) 1, 0, NULL);
+ /* Could not enable so not debugging a kernel */
+ if (!lkd_params.enabled)
+ goto __sl_fail;
+ }
+
+ stop_core = 0;
+ cur_bfd = get_cur_bfd (from_tty);
+
+ /* if user forces loaded = off, also remove auto-load
+ **/
+ if (lkd_params.loaded == LKD_NOTLOADED)
+ {
+ struct cleanup *cleanup;
+
+ /* Before switching off first need to clear some caches. */
+ thread_awareness_inhibit ();
+ cleanup = make_cleanup (thread_awareness_exhibit, NULL);
+ linux_awareness_ops->lo_clear_cache ();
+ do_cleanups (cleanup);
+
+ lkd_private.loaded = LKD_NOTLOADED;
+
+ if (lkd_params.auto_activate)
+ {
+ lkd_params.auto_activate = 0;
+ if (from_tty)
+ printf_filtered
+ ("(also disabling linux-awareness auto-activation)\n");
+ }
+
+ lkd_proc_free_list ();
+
+ /* fallback to any thread that makes sense for the beneath target */
+ lkd_reset_thread_list ();
+
+ return;
+ }
+
+ /* if the user want to set loaded = on, do some sanity checks
+ **/
+ if (lkd_params.loaded == LKD_LOADED)
+ {
+ /* check if symbol-file was set first */
+ if (!get_cur_bfd (from_tty))
+ goto __sl_fail;
+
+ cur_bfd = get_cur_bfd (from_tty);
+ banner2 = get_banner_from_file (cur_bfd);
+ file_name = bfd_get_filename (cur_bfd);
+
+ lkd_private.target_pointer_type =
+ builtin_type (target_gdbarch ())->builtin_data_ptr;
+
+ if (linux_awareness_ops->lo_pre_load)
+ linux_awareness_ops->lo_pre_load (file_name, from_tty);
+
+ lkd_private.loaded = LKD_LOADING;
+
+ gdb_assert (lkd_params.enabled);
+
+ if (!(lkd_private.kflags & KFLAG_DBGINFO))
+ {
+ warning
+ ("\"set loaded\" failed because kernel has no debug info.\n");
+ goto __sl_fail;
+ }
+
+ if (lkd_try_push_target () != LKD_LOADED)
+ {
+ warning
+ ("\"set loaded\" failed because L-A target could not be pushed.\n");
+ goto __sl_fail;
+ }
+
+
+ /* (re)init the thread_list with hardware threads */
+ lkd_proc_init ();
+
+ /*do arch-specific fixup (mmu for instance) */
+ linux_aware_post_load (file_name, 0);
+
+ /*needs access to mem */
+ if (set_utsname_release () != 0 /*EOK*/)
+ {
+ warning
+ ("\"set loaded\" failed because L-A target could not access target memory.\n");
+ goto __sl_fail;
+ }
+
+ /*get banner from mem */
+ banner1 = get_banner ();
+
+ /* Check that the kernel in memory corresponds to the
+ * binary file we were given.
+ */
+ if (banner1 == NULL || banner2 == NULL || strcmp (banner1, banner2))
+ {
+ if (!nquery
+ ("Kernel banner in debugger file: \n%s\n"
+ "Kernel banner in target memory: \n%s\n"
+ "WARNING: do you want to continue ?\n"
+ "(if the kernels don't match the debugger might crash)",
+ banner2, banner1))
+ {
+ error ("Aborted (kernel banner mismatch).");
+ goto __sl_fail;
+ }
+ }
+
+ lkd_proc_invalidate_list ();
+
+ /* scan the linux threads */
+
+ if (!lkd_proc_refresh_info (stop_core))
+ {
+ if (from_tty)
+ printf_filtered ("failed: has this kernel started?\n");
+ goto __sl_fail;
+ }
+
+ lkd_proc_set_symfile ();
+
+
+ lkd_private.loaded = lkd_params.loaded;
+ }
+
+ printf_filtered ("Kernel image version: %s\n", lkd_private.utsname_release);
+
+ return;
+
+__sl_fail:
+ /* silently fail, we retry later.
+ **/
+ lkd_params.loaded = LKD_NOTLOADED;
+ lkd_private.loaded = LKD_NOTLOADED;
+ return;
+}
+
+/* Helper for linux_awareness_fix_debug_info() that locates the lowest
+ section in a BFD. */
+static void
+find_min_load_addr (bfd * abfd, asection * sectp, void *addr)
+{
+ CORE_ADDR *min_addr = addr;
+ CORE_ADDR vma;
+
+ if (!(bfd_get_section_flags (abfd, sectp) & SEC_ALLOC))
+ return;
+ if (!(bfd_get_section_flags (abfd, sectp) & SEC_HAS_CONTENTS))
+ return;
+
+ vma = bfd_get_section_vma (abfd, sectp);
+ if (vma < *min_addr)
+ *min_addr = vma;
+}
+
+/*
+ * The functionalities that can be built as modules often have a
+ * cleanup routine (marked with module_exit). When the driver is built
+ * into the kernel (i.e. not as a module), the cleanup routines aren't
+ * linked in, but their debug information remains... These routines
+ * all have very low addresses (as they haven't been relocated). This
+ * functions try to partially fix the debug info, so that the psymtabs
+ * don't advertise a too wide range of text addresses.
+ */
+static void
+linux_awareness_fix_debug_info (void)
+{
+ CORE_ADDR min_load_addr = (CORE_ADDR) - 1;
+ struct partial_symtab *pst;
+
+ bfd_map_over_sections (symfile_objfile->obfd, find_min_load_addr,
+ &min_load_addr);
+
+ ALL_OBJFILE_PSYMTABS (symfile_objfile, pst)
+ {
+ if (pst->textlow < min_load_addr)
+ pst->textlow = min_load_addr;
+ }
+}
+
+/* GDB wants the stack it reads to have a strictly decreasing SP. This
+ relation isn't true when the backtrace goes from kerne- to
+ user-space. Overload the inner_than method to hide this
+ peculiarity. */
+static int
+linux_aware_inner_than (CORE_ADDR lhs, CORE_ADDR rhs)
+{
+ if (linux_awareness_ops->lo_is_kernel_address (rhs)
+ && linux_awareness_ops->lo_is_user_address (lhs))
+ return 0;
+
+ return core_addr_lessthan (lhs, rhs);
+}
+
+void
+set_skip_schedule_frame (char *arg, int from_tty, struct cmd_list_element *c)
+{
+ reinit_frame_cache ();
+}
+
+
+/* Function called to init the linux awareness layer once we know
+ we're debugging a known kernel. */
+static void
+linux_awareness_init (void)
+{
+ struct cmd_list_element *c;
+ const char *linux_awareness_postinit = "linux-awareness-postinit";
+
+ DEBUG (D_INIT, 1, "linux_awareness_init\n");
+
+ /* Stack our layer over the real target stack. */
+ lkd_try_push_target ();
+
+ /* this may set the OS ABI (reset the solib ops too!) */
+ linux_awareness_ops->lo_init ();
+
+ printf_filtered ("Enabling Linux Kernel Debugger %s build %s.\n",
+ LKD_VERSION_STRING, __DATE__);
+
+ /* Set this as early as we can */
+ lkd_private.target_pointer_type =
+ builtin_type (target_gdbarch ())->builtin_data_ptr;
+
+ /* Init some data structures. */
+ linux_awareness_fix_debug_info ();
+
+ /* Register the various callbacks we use. */
+ normal_stop_observer = observer_attach_normal_stop (normal_stop_callback);
+ observer_attach_breakpoint_created (linux_aware_create_breakpoint_hook);
+
+
+ set_gdbarch_inner_than (target_gdbarch (), linux_aware_inner_than);
+
+
+ add_info_alias ("tasks", "threads", 0);
+ add_com_alias ("task", "thread", class_run, 0);
+
+ add_com ("running_task", class_lkd, running_task_command,
+ "Switch to the currently running task.");
+
+
+ add_setshow_integer_cmd ("skip_schedule_frame",
+ class_lkd,
+ &lkd_params.skip_schedule_frame,
+ "Set whether the debugger should hide the schedule() frame for sleeping tasks",
+ "Show whether the debugger should hide the schedule() frame for sleeping tasks",
+ "Typical value is between 0 for full stack to 4",
+ &set_skip_schedule_frame, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+
+ /* Call the user-defined linux_awareness_postinit command if it
+ exists. (Allows the user to put code in his .gdbinit that will
+ be run only if the layer is loaded). */
+ c = lookup_cmd (&linux_awareness_postinit, cmdlist, "", 1, 1);
+ if (c != NULL && c->theclass == class_user)
+ execute_user_command (c, 0);
+
+}
+
+/* Helper for the autoactivation symbol lookup. */
+static inline int
+linux_awareness_lookup_symbol (const char *name)
+{
+ int found = lookup_minimal_symbol (name, NULL, NULL).minsym != NULL;
+
+ if (!found)
+ DEBUG (D_INIT, 1, "Symbol '%s' not found\n", name);
+ else
+ DEBUG (D_INIT, 4, "Symbol '%s' found\n", name);
+
+ return found;
+}
+
+/* Helper for the autoactivation symbol lookup. */
+static inline int
+linux_awareness_lookup_symtab (const char *name)
+{
+ int found = lookup_symtab (name) != NULL;
+
+ if (!found)
+ DEBUG (D_INIT, 1, "Symtab '%s' not found\n", name);
+ else
+ DEBUG (D_INIT, 4, "Symtab '%s' found\n", name);
+
+ return found;
+}
+
+/* make sure we have a kernel objfile */
+static int
+linux_awareness_check (void)
+{
+ int build = 0;
+ int check = 0;
+
+ DEBUG (D_INIT, 1, "linux_awareness_check\n");
+
+ /* reset flags */
+ lkd_private.kflags &= ~(KFLAG_LINUX | KFLAG_DBGINFO);
+
+ /* Look for some specific Linux symbols. */
+ if (!linux_awareness_lookup_symbol ("schedule")
+ || !linux_awareness_lookup_symbol ("linux_banner"))
+ return 0; /* KO */
+
+ /* Make sure we have an architecture layer */
+ if (!linux_awareness_ops)
+ {
+ /* More verbose here, as we believe we are looking at a kernel,
+ * and the --enable-linux-awareness configure flag has been set */
+ warning ("Architecture Layer Missing. Can't enable linux awareness");
+ return 0;
+ }
+
+ lkd_private.kflags |= KFLAG_LINUX;
+
+ /* More checks. */
+ lookup_symtab ("page_io.c");
+
+ /* check for mandatory structure and fields */
+
+ check = HAS_FIELD (pid_namespace, last_pid)
+ && HAS_FIELD (task_struct, nsproxy);
+
+ if (!check) /*look for older kernels */
+ check = HAS_FIELD (list_head, next) && HAS_FIELD (task_struct, namespace);
+
+ check = check && HAS_FIELD (task_struct, children)
+ && HAS_FIELD (task_struct, sibling)
+ && HAS_FIELD (task_struct, thread_group)
+ && HAS_FIELD (task_struct, pid)
+ && HAS_FIELD (task_struct, tgid)
+ && HAS_FIELD (task_struct, comm)
+ && HAS_FIELD (thread_info, preempt_count);
+
+ /* load some data that GDB seems to loose otherwise */
+ if (check && linux_awareness_lookup_symtab ("mmap.c")
+ && linux_awareness_lookup_symtab ("fork.c")
+ && linux_awareness_lookup_symtab ("block_dev.c")
+ && linux_awareness_lookup_symtab ("vmalloc.c")
+ && linux_awareness_lookup_symtab ("page_alloc.c")
+ && linux_awareness_ops->lo_check_kernel ())
+ lkd_private.kflags |= KFLAG_DBGINFO;
+ else
+ warning ("debug information missing.");
+
+ return 1; /* OK */
+}
+
+/* Function called when we enable or disable the linux awareness
+ layer. */
+void
+lkd_enabled_set (char *args, int from_tty, struct cmd_list_element *c)
+{
+ static int stored_state = 0;
+
+ DEBUG (D_INIT, 1, "lkd_enabled_set\n");
+
+ /*if not from tty, we can use args as a parameter. */
+ if ((!from_tty) && ((uintptr_t) args == 1))
+ lkd_params.enabled = 1;
+
+ if (lkd_params.enabled == stored_state)
+ return;
+
+ if (lkd_params.enabled == 1)
+ {
+
+ /* make sure that a bfd is available,
+ * reevaluate the current objfile
+ */
+ if (linux_awareness_check () && get_cur_bfd (from_tty))
+ linux_awareness_init ();
+ else
+ {
+ /* bare machine debuggee, do not auto-enable
+ * but allow bold user commands...*/
+ lkd_params.enabled = 0;
+
+ /* try to push the target anyway,
+ * so that we get called into to_load */
+ if (!BENEATH)
+ push_target (&linux_aware_ops);
+
+ if (from_tty)
+ warning ("Could not enable linux-awareness: no objfile ?");
+ }
+
+ }
+ else if (!ptid_equal (inferior_ptid, null_ptid)) /*if not `mourned`already */
+ {
+ struct target_waitstatus dummy;
+ struct thread_info *tp;
+
+
+ /* this will reinit the thread_list.
+ * and record the current "loaded" status.
+ **/
+ lkd_params.loaded = LKD_NOTLOADED;
+ lkd_loaded_set (NULL, 0, NULL);
+ }
+
+ stored_state = lkd_params.enabled;
+}
+
+/* This callback is called each time the user loads a new executable
+ in GDB, and it tries to determine if it's a Linux kernel. If it's
+ the case, it loads the linux awareness layer. */
+static void
+linux_awareness_on_new_objfile (struct objfile *objf)
+{
+ DEBUG (D_INIT, 1, "linux_awareness_on_new_objfile\n");
+
+ /* try pushing target and enabling at least.
+ **/
+ if (lkd_params.auto_activate)
+ lkd_enabled_set ((char *) 1, 0, 0);
+}
+
+static void
+set_linux_awareness (char *arg, int from_tty)
+{
+ printf_unfiltered
+ ("'set linux-awareness' must be followed by the name of a print subcommand.\n");
+ help_list (set_linux_awareness_cmd_list, "set linux-awareness ", -1,
+ gdb_stdout);
+}
+
+static void
+show_linux_awareness (char *args, int from_tty)
+{
+ cmd_show_list (show_linux_awareness_cmd_list, from_tty, "");
+}
+
+static void
+set_global_loglevel (char *arg, int from_tty, struct cmd_list_element *c)
+{
+ struct debug_domain *domain = linux_aware_debug_domains_info;
+
+ while (domain->name != NULL)
+ domain++->level = lkd_params.loglevel;
+}
+
+volatile int stop_loop = 1;
+
+/* initialize private data
+ * when will gdb go OOP at least !??
+ **/
+static void
+init_private_data (void)
+{
+ lkd_private.string_buf_size = 4096;
+ lkd_private.string_buf =
+ xcalloc (lkd_private.string_buf_size, sizeof (char));
+ lkd_private.banner_file_size = 256;
+ lkd_private.banner_file =
+ xcalloc (lkd_private.banner_file_size, sizeof (char));
+ lkd_private.banner_file_valid = 0;
+ lkd_private.banner_mem_size = 256;
+ lkd_private.banner_mem =
+ xcalloc (lkd_private.banner_mem_size, sizeof (char));
+ lkd_private.banner_mem_valid = 0;
+ lkd_private.utsname_release_size = 256;
+ lkd_private.utsname_release =
+ xcalloc (lkd_private.banner_mem_size, sizeof (char));
+ lkd_private.utsname_release_valid = 0;
+ lkd_private.kflags = KFLAG_NOLINUX;
+}
+
+static void
+linux_awareness_inferior_created (struct target_ops *ops, int from_tty)
+{
+ DEBUG (D_INIT, 1, "linux_awareness_inferior_created\n");
+ if (lkd_params.auto_activate)
+ lkd_enabled_set ((char *) 1, 0, 0);
+}
+
+static ptid_t target_thread_ptid;
+
+static void
+linux_awareness_target_thread_changed (ptid_t ptid)
+{
+ DEBUG (D_INIT, 1, "linux_awareness_target_thread_changed {%d, %ld, %ld}\n",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid), ptid_get_tid (ptid));
+
+ if (ptid_equal (ptid, null_ptid) || ptid_equal (ptid, minus_one_ptid))
+ target_thread_ptid = null_ptid;
+ else if (ptid_get_tid (ptid) != CORE_INVAL)
+ target_thread_ptid = ptid;
+}
+
+int
+linux_aware_target_core (void)
+{
+ gdb_assert (!ptid_equal (target_thread_ptid, null_ptid));
+
+ return ptid_get_tid (target_thread_ptid) - 1;
+}
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_linux_awareness;
+
+/* Function called automatically by GDB on each start. */
+void
+_initialize_linux_awareness (void)
+{
+ struct debug_domain *domain;
+
+ while (!stop_loop);
+
+ linux_uprocess_objfile_data_key = register_objfile_data ();
+
+ init_private_data ();
+
+ target_thread_ptid = null_ptid;
+
+ observer_attach_inferior_created (linux_awareness_inferior_created);
+ observer_attach_new_objfile (linux_awareness_on_new_objfile);
+ observer_attach_target_thread_changed
+ (linux_awareness_target_thread_changed);
+
+ init_linux_aware_target ();
+ add_target (&linux_aware_ops);
+
+ target_root_prefix = &gdb_sysroot;
+
+ add_prefix_cmd ("linux-awareness",
+ class_lkd,
+ set_linux_awareness,
+ "Command for setting linux-awareness variables",
+ &set_linux_awareness_cmd_list,
+ "set linux-awareness ", 0, &setlist);
+
+ add_prefix_cmd ("linux-awareness",
+ class_lkd,
+ show_linux_awareness,
+ "Command for showing linux-awareness variables",
+ &show_linux_awareness_cmd_list,
+ "show linux-awareness ", 0, &showlist);
+
+ add_setshow_boolean_cmd ("enabled",
+ class_lkd,
+ &lkd_params.enabled,
+ "Set the activation state of the the linux "
+ "awareness layer",
+ "Show the activation state of the the linux "
+ "awareness layer",
+ NULL, &lkd_enabled_set, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+ add_setshow_boolean_cmd ("loaded", class_lkd, (int *) &lkd_params.loaded, /*warn: can be '2' to detect transient states */
+ "Set the loaded state of the kernel image",
+ "Show the loaded state of the kernel image",
+ NULL, &lkd_loaded_set, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+ domain = linux_aware_debug_domains_info;
+
+ while (domain->name != NULL)
+ {
+ static const char fmt[] =
+ "%s the debug level of the linux awareness " "layer %s part.";
+ const char *name = domain->name + 6; /* Skip debug- */
+ char *help_set = xstrprintf (fmt, "Set", name);
+ char *help_show = xstrprintf (fmt, "Show", name);
+ add_setshow_zinteger_cmd ((char *) domain->name,
+ class_lkd,
+ &(domain->level),
+ help_set,
+ help_show,
+ NULL,
+ NULL, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+ xfree (help_set);
+ xfree (help_show);
+ ++domain;
+ }
+
+ add_setshow_zinteger_cmd ("debug-all",
+ class_lkd,
+ &lkd_params.loglevel,
+ "Set the debug level of the linux awareness "
+ "layer",
+ "Show the debug level of the linux awareness "
+ "layer",
+ NULL,
+ &set_global_loglevel, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+ add_setshow_boolean_cmd ("enable_task_awareness",
+ class_lkd,
+ &lkd_params.enable_task_awareness,
+ "Set whether we implement task awareness",
+ "Show whether we implement task awareness",
+ NULL, NULL, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+ add_setshow_boolean_cmd ("auto_activate",
+ class_lkd,
+ &lkd_params.auto_activate,
+ "Set whether we try to autodetect linux kernels.",
+ "Show whether we try to autodetect linux kernels.",
+ NULL, NULL, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+
+ add_setshow_boolean_cmd ("no-colors",
+ class_lkd,
+ &lkd_params.no_colors,
+ "Set disable thread info coloring.",
+ "Show thread info coloring status.",
+ NULL, NULL, NULL,
+ &set_linux_awareness_cmd_list,
+ &show_linux_awareness_cmd_list);
+}
+
+/******************************* from shtdi.c *********************************/
+
+static void
+terminate_once (int signo)
+{
+ signal (signo, SIG_IGN); /* Ignore further signals */
+ terminate_requested = signo;
+}
+
+static void
+disable_terminate (void)
+{
+ terminate_requested = 0; /* Reset */
+ old_terminate_handler = signal (SIGTERM, terminate_once);
+ old_interrupt_handler = signal (SIGINT, terminate_once);
+}
+
+static void
+enable_terminate (void)
+{
+ signal (SIGTERM, old_terminate_handler);
+ signal (SIGINT, old_interrupt_handler);
+ if (terminate_requested)
+ raise (terminate_requested);
+}
new file mode 100644
@@ -0,0 +1,892 @@
+/*
+ Linux Awareness extension target (Linux Kernel Debugger)
+ Copyright 2011-2013 STMicroelectronics.
+*/
+
+#include "defs.h"
+#include "ui-out.h"
+#include "arch-utils.h"
+#include "block.h"
+#include "breakpoint.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-script.h"
+#include "command.h"
+#include "completer.h"
+#include "dictionary.h"
+#include "event-loop.h"
+#include "exceptions.h"
+#include "exec.h"
+#include "frame.h"
+#include "gdb.h"
+#include "gdb_assert.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "regcache.h"
+#include "solib.h"
+#include "solist.h"
+#include "symtab.h"
+#include "psympriv.h"
+#include "target.h"
+
+#include "bfd.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+
+#include "tui/tui.h"
+#include "lkd.h"
+#include "lkd-process.h"
+
+#define BENEATH linux_aware_ops.beneath
+
+/* Declaration of the required addresses. */
+DECLARE_ADDR (swapper_pg_dir);
+DECLARE_ADDR (init_thread_union);
+DECLARE_ADDR (init_task);
+
+DECLARE_ADDR (init_pid_ns);
+DECLARE_FIELD (pid_namespace, last_pid);
+
+/*realize cur_rq(cpu)->curr*/
+DECLARE_ADDR (__per_cpu_offset);
+DECLARE_ADDR (per_cpu__process_counts);
+DECLARE_ADDR (process_counts);
+DECLARE_ADDR (per_cpu__runqueues);
+DECLARE_ADDR (runqueues);
+
+DECLARE_FIELD (rq, curr);
+DECLARE_FIELD (rq, idle);
+DECLARE_FIELD (rq, lock);
+DECLARE_FIELD (raw_spinlock, magic);
+
+DECLARE_FIELD (list_head, next);
+
+
+DECLARE_FIELD (task_struct, active_mm);
+DECLARE_FIELD (mnt_namespace, list);
+ /**/ DECLARE_FIELD (path, dentry);
+ /**/ DECLARE_FIELD (task_struct, mm);
+DECLARE_FIELD (task_struct, tasks);
+DECLARE_FIELD (task_struct, children);
+DECLARE_FIELD (task_struct, thread_group);
+DECLARE_FIELD (task_struct, sibling);
+DECLARE_FIELD (task_struct, pid);
+DECLARE_FIELD (task_struct, tgid);
+DECLARE_FIELD (task_struct, namespace);
+DECLARE_FIELD (task_struct, nsproxy);
+DECLARE_FIELD (task_struct, prio);
+DECLARE_FIELD (task_struct, cred);
+DECLARE_FIELD (task_struct, comm); /* far offset in the task_struct, to bulk-read everything needed. */
+
+
+ /**/ DECLARE_FIELD (thread_info, preempt_count);
+DECLARE_FIELD (vm_area_struct, vm_next);
+DECLARE_FIELD (vm_area_struct, vm_file);
+DECLARE_FIELD (vm_area_struct, vm_flags);
+DECLARE_FIELD (vm_area_struct, vm_start);
+DECLARE_FIELD (vm_area_struct, vm_end);
+DECLARE_FIELD (vm_area_struct, vm_pgoff);
+DECLARE_FIELD (vm_struct, next);
+DECLARE_FIELD (vm_struct, size);
+
+/* The current task. */
+
+process_t *process_list = NULL; /*the processes list from the linux prospective */
+process_t *wait_process = NULL; /*process we stopped at in target_wait */
+process_t *running_process[MAX_CORES]; /*scheduled process as seen by each core */
+uint32_t per_cpu_offset[MAX_CORES]; /*__per_cpu_offset*/
+
+/* per cpu peeks */
+CORE_ADDR runqueues_addr;
+CORE_ADDR rq_curr[MAX_CORES]; /*cur_rq(cpu) */
+CORE_ADDR rq_idle[MAX_CORES]; /*rq->idle */
+
+/* process list housekeeping*/
+static int process_counts[MAX_CORES];
+static int last_pid;
+
+struct mmu_infos mmu_info[MAX_CORES];
+
+
+static int
+find_thread_lwp (struct thread_info *tp, void *arg)
+{
+ long lwp = *(long*)arg;
+
+ return (ptid_get_lwp(tp->ptid) == lwp);
+}
+
+static int
+find_thread_swapper (struct thread_info *tp, void *arg)
+{
+ long core = *(long*)arg;
+
+ if ((!ptid_get_lwp(tp->ptid)) && (ptid_get_tid(tp->ptid) == core))
+ {
+ DEBUG (TASK, 2, "swapper found: tp->ptid(%d-%ld-%ld) core=%ld\n",
+ ptid_get_pid(tp->ptid),
+ ptid_get_lwp(tp->ptid),
+ ptid_get_tid(tp->ptid),
+ core);
+ return 1;
+ }
+ return 0;
+}
+
+/* invalidate the cached task list. */
+static void
+proc_private_dtor (struct private_thread_info * dummy)
+{
+ /* nop, do not free. */
+}
+
+/* Create the 'process_t' for the task pointed by the passed
+ TASK_STRUCT. */
+static void
+get_task_info (CORE_ADDR task_struct, process_t ** ps,
+ int core /*zero-based */ )
+{
+ process_t *l_ps;
+ size_t size;
+ unsigned char *task_name;
+ int i = 0;
+ int lwp = 0;
+ ptid_t this_ptid;
+
+ while (*ps && (*ps)->valid)
+ ps = &((*ps)->next);
+
+ if (*ps == NULL)
+ *ps = XCNEW (process_t);
+
+ l_ps = *ps;
+
+ if (task_struct == 0)
+ {
+ /* create a fake swapper entry now for the additional core
+ * to keep the gdb_thread ordering
+ **/
+ l_ps->task_struct = 0;
+ l_ps->mm = 0;
+ l_ps->tgid = 0;
+ l_ps->prio = 0;
+ l_ps->core = -1;
+
+ if (l_ps->comm)
+ {
+ xfree (l_ps->comm);
+ l_ps->comm = NULL;
+ }
+ l_ps->comm = xstrdup ("[swapper]");
+ }
+ else
+ {
+ size = F_OFFSET (task_struct, comm) + F_SIZE (task_struct, comm);
+
+ task_name = lkd_private.string_buf + F_OFFSET (task_struct, comm);
+
+ /* use scratch area for messing around with strings
+ * to avoid static arrays and dispersed mallocs and frees
+ **/
+ gdb_assert (lkd_private.string_buf);
+ gdb_assert (lkd_private.string_buf_size >= size);
+
+ /* the task struct is not likely to change much from one kernel version
+ * to another. Knowing that comm is one of the far fields,
+ * try read the task struct in one command */
+ read_memory (task_struct, lkd_private.string_buf, size);
+
+ l_ps->task_struct = task_struct;
+ lwp = extract_unsigned_field (lkd_private.string_buf, task_struct, pid);
+ l_ps->mm = extract_pointer_field (lkd_private.string_buf,
+ task_struct, mm);
+ l_ps->active_mm = extract_pointer_field (lkd_private.string_buf,
+ task_struct, active_mm);
+ l_ps->tgid = extract_unsigned_field (lkd_private.string_buf,
+ task_struct, tgid);
+ l_ps->prio = extract_unsigned_field (lkd_private.string_buf,
+ task_struct, prio);
+ l_ps->core = core; /* for to_core_of_threads */
+
+ if (!l_ps->mm)
+ {
+ int len = strlen ((char *)task_name);
+ *(task_name + len) = ']';
+ *(task_name + len + 1) = '\0';
+ *(--task_name) = '[';
+ }
+
+ if (l_ps->comm)
+ {
+ xfree (l_ps->comm);
+ l_ps->comm = NULL;
+ }
+ l_ps->comm = xstrdup ((char*)task_name);
+ }
+
+ if (core != CORE_INVAL)
+ {
+ /* Long usage to map to LWP */
+ long core_mapped = core + 1;
+
+ /* swapper[core] */
+ gdb_assert (lwp==0);
+
+ this_ptid = ptid_build (ptid_get_pid(inferior_ptid), lwp /* == 0 */ , core_mapped);
+ l_ps->gdb_thread =
+ iterate_over_threads (find_thread_swapper, &core_mapped);
+ }
+ else
+ {
+ this_ptid = ptid_build (ptid_get_pid(inferior_ptid), lwp, CORE_INVAL);
+ l_ps->gdb_thread = iterate_over_threads (find_thread_lwp, &lwp);
+
+ /*reset the thread core value, if existing */
+ if (l_ps->gdb_thread)
+ {
+ gdb_assert (!l_ps->gdb_thread->priv);
+ PTID_OF (l_ps).tid = CORE_INVAL;
+ }
+ }
+
+ l_ps->pgd = 0;
+ l_ps->valid = 1;
+
+ /* allocate if not found
+ */
+ if (!l_ps->gdb_thread)
+ {
+ DBG_IF (TASK)
+ /*sanity check : go through the list and check if lwp already there */
+ process_t *tps = process_list;
+
+ while (tps && (tps)->valid)
+ {
+ if (lwp && (tps)->gdb_thread && (ptid_get_lwp(PTID_OF (tps)) == lwp))
+ gdb_assert (0);
+ tps = tps->next;
+ };
+ DBG_ENDIF (TASK)
+
+ /* add with info so that pid_to_string works. */
+ l_ps->gdb_thread = add_thread_with_info (this_ptid,
+ (struct private_thread_info *)l_ps);
+ }
+
+ /* forcibly update the private fields, as some thread may
+ * already have been created without, like hw threads.
+ * and this also tell is the gdb_thread is pruned or not!*/
+ l_ps->gdb_thread->priv = (struct private_thread_info *)l_ps;
+
+ DEBUG (TASK, 1, "gdb_thread->lwp %ld <=> ps %p\n",
+ ptid_get_lwp(PTID_OF (*ps)), ps);
+
+ /* the process list freeing is not handled thanks to
+ * this `private` facility, yet.
+ */
+ l_ps->gdb_thread->private_dtor = proc_private_dtor;
+
+ /* keep trace of the last state to notify a change */
+ l_ps->old_ptid = PTID_OF (l_ps);
+}
+
+
+/* Returns the 'process_t' corresponding to the passed task_struct or
+ NULL if not in the list. */
+process_t *
+lkd_proc_get_by_task_struct (CORE_ADDR task_struct)
+{
+ process_t *ps = lkd_proc_get_list ();
+
+ while ((ps != NULL) && (ps->valid == 1))
+ {
+ if (ps->task_struct == task_struct)
+ return ps;
+ ps = ps->next;
+ }
+ return NULL;
+}
+
+/* Return the process currently scheduled on one core */
+process_t *
+lkd_proc_get_running (int core)
+{
+ process_t *current = NULL;
+ CORE_ADDR task;
+ struct thread_info *tp; /*gdb ti */
+ ptid_t old_ptid;
+
+ if (core == CORE_INVAL)
+ return NULL;
+
+ if (running_process[core] == NULL)
+ {
+
+ gdb_assert (lkd_proc_get_runqueues (0));
+
+ task = lkd_proc_get_rq_curr (core);
+
+ if (task)
+ { /* smp cpu is initialized */
+ current = lkd_proc_get_by_task_struct (task);
+
+ if (!current)
+ {
+ /* this task struct is not known yet AND was not seen
+ * while running down the tasks lists, so this is presumably
+ * the swapper of an secondary SMP core.
+ */
+ current =
+ lkd_proc_get_by_ptid (ptid_build
+ (ptid_get_pid(inferior_ptid),
+ 0, core + 1));
+ gdb_assert(current);
+
+ current->task_struct = task;
+ }
+ else
+ {
+ /* update the thread's tid in thread_list if it exists and wasn't scheduled
+ * so that tid makes sense for both the gdbserver and infrun.c
+ **/
+ PTID_OF (current).tid = core + 1;
+ }
+
+ current->core = core; /* was CORE_INVAL */
+ running_process[core] = current;
+ } // task
+ } // running_process[core]
+
+ return running_process[core];
+}
+
+/* Return 1 if this is a current task (or 0)*/
+int
+lkd_proc_is_curr_task (process_t * ps)
+{
+ return (ps && (ps == lkd_proc_get_running (ps->core)));
+}
+
+static CORE_ADDR get_rq_idle (int core); /* forward decl. */
+
+/* Helper function that iterates the task list in the kernel
+ memory which are stored in a tree like structure. From sched.h:
+
+ #define do_each_thread(g, t) \
+ for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do
+
+ #define while_each_thread(g, t) \
+ while ((t = next_thread(t)) != g)
+*/
+static CORE_ADDR
+_next_task (CORE_ADDR p)
+{
+ CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, tasks, list_head, next);
+
+ if (!cur_entry)
+ {
+ warning ("kernel task list contains NULL pointer");
+ return 0;
+ }
+
+ return container_of (cur_entry, task_struct, tasks);
+}
+
+static CORE_ADDR
+_next_thread (CORE_ADDR p)
+{
+ CORE_ADDR cur_entry = read_unsigned_embedded_field (p, task_struct, thread_group, list_head, next);
+
+ if (!cur_entry)
+ {
+ DEBUG (TASK, 3, "kernel thread group list contains NULL pointer\n");
+ return 0;
+ }
+
+ return container_of (cur_entry, task_struct, thread_group);
+}
+
+static process_t **
+get_list_helper (process_t ** ps)
+{
+ CORE_ADDR g, t, init_task_addr;
+ int core;
+
+ init_task_addr = ADDR (init_task);
+ g = init_task_addr;
+ core = 0;
+
+ do
+ {
+ t = g;
+ do
+ {
+ if (!linux_awareness_ops->lo_is_kernel_address (t))
+ {
+ warning ("parsing of task list stopped because of invalid address %s", phex (t, 4));
+ break;
+ }
+
+ get_task_info (t, ps, core /*zero-based */ );
+ core = CORE_INVAL;
+
+ if (ptid_get_lwp (PTID_OF (*ps)) == 0)
+ {
+ /* this is init_task, let's insert the other cores swapper now */
+ int i;
+ for (i = 1; i < max_cores; i++)
+ {
+ CORE_ADDR idle;
+ ps = &((*ps)->next);
+ idle = get_rq_idle (i);
+ get_task_info (idle, ps, i);
+ }
+ }
+
+ DEBUG (TASK, 2, "Got task info for %s (%li)\n",
+ (*ps)->comm, ptid_get_lwp (PTID_OF (*ps)));
+
+ ps = &((*ps)->next);
+
+ /* mark end of chain and remove those threads
+ * that disappeared from the thread_list
+ * to avoid any_thread_of_process() to select a ghost.
+ **/
+ if (*ps)
+ (*ps)->valid = 0;
+
+ t = _next_thread (t);
+ } while (t && (t != g));
+
+ g = _next_task (g);
+ } while (g && (g != init_task_addr));
+
+ return ps;
+}
+
+
+/*----------------------------------------------------------------------------------------------*/
+
+/* This function returns a the list of 'process_t' corresponding
+ to the tasks in the kernel's task list. */
+process_t *
+lkd_proc_get_list (void)
+{
+ /* Return the cached copy if there's one,
+ * or rebuild it.
+ **/
+ if (process_list && process_list->valid)
+ return process_list;
+
+ gdb_assert(lkd_private.proc_list_invalid);
+
+ get_list_helper (&process_list);
+
+ lkd_private.proc_list_invalid = 0;
+
+ return process_list;
+}
+
+/* Returns a valid 'process_t' corresponding to
+ * the passed ptid or NULL if not found.
+ */
+process_t *
+lkd_proc_get_by_ptid (ptid_t ptid)
+{
+ struct thread_info *tp;
+ long lwp = ptid_get_lwp(ptid);
+ process_t *ps;
+
+ gdb_assert(!lkd_private.proc_list_invalid);
+
+ if (lwp)
+ /*non-swapper, ignore TID */
+ tp = iterate_over_threads (find_thread_lwp, &lwp);
+ else
+ /*swapper, TID gives the core, lwp = 0 is not unique */
+ tp = find_thread_ptid(ptid);
+
+ ps = (process_t *)tp->priv;
+
+ /* Prune the gdb-thread is the process is not valid
+ * meaning is was no longer found in the task list. */
+ return ps;
+}
+
+/* invalidate the gdb thread is the linux ps has died.*/
+static int
+thread_clear_info (struct thread_info *tp, void *ignored)
+{
+ tp->priv = NULL;
+ return 0;
+}
+
+/* invalidate the cached task list. */
+void
+lkd_proc_invalidate_list (void)
+{
+ process_t *ps = process_list;
+ process_t *cur;
+ while (ps)
+ {
+ cur = ps;
+ ps = ps->next;
+ cur->valid = 0;
+ }
+
+ /* We invalidate the processes attached to the gdb_thread
+ * setting tp->private to null tells if the thread can
+ * be deleted or not. */
+ iterate_over_threads (thread_clear_info, NULL);
+
+ lkd_private.proc_list_invalid = 1;
+}
+
+void
+lkd_proc_free_list (void)
+{
+ process_t *ps = process_list;
+ process_t *cur;
+ while (ps)
+ {
+ cur = ps;
+ ps = ps->next;
+ // xfree does check for null pointers.
+ xfree (cur->comm);
+ xfree (cur);
+ }
+ process_list = NULL;
+}
+
+/* Return the processor core that thread PTID was last seen on.
+ This information is updated only when:
+ - update_thread_list is called
+ - thread stops
+ If the core cannot be determined -- either for the specified thread, or
+ right now, or in this debug session, or for this target -- return -1. */
+int
+lkd_proc_core_of_thread (ptid_t ptid)
+{
+ int i = 0;
+ process_t *ps;
+
+ ps = lkd_proc_get_by_ptid (ptid);
+
+ if (!ps || (ps != lkd_proc_get_running (ps->core)))
+ return CORE_INVAL;
+ else
+ return ps->core;
+}
+
+CORE_ADDR
+lkd_proc_get_runqueues (int reset)
+{
+ CORE_ADDR swapper = 0;
+ process_t *test_ps;
+
+ runqueues_addr = 0;
+
+ if (HAS_ADDR (runqueues))
+ {
+ runqueues_addr = ADDR (runqueues);
+ }
+ else
+ {
+ runqueues_addr = ADDR (per_cpu__runqueues);
+ }
+ /* check validity */
+
+ DBG_IF (TASK)
+ if (HAS_FIELD (raw_spinlock, magic))
+ {
+
+ CORE_ADDR lock_magic = ADDR (runqueues)
+ + (CORE_ADDR) per_cpu_offset[0]
+ + F_OFFSET (rq, lock) + F_OFFSET (raw_spinlock,
+ magic);
+
+ if ((read_memory_unsigned_integer (lock_magic, 4 /*uint32 */ ,
+ LKD_BYTE_ORDER) & 0xdead0000)
+ != 0xdead0000)
+ error ("accessing the core runqueues seems to be compromised.");
+ }
+ else
+ printf_filtered ("runqueues access validated OK.");
+ DBG_ENDIF (TASK)
+
+ return runqueues_addr;
+}
+
+/*attempt getting the runqueue address for a core*/
+CORE_ADDR
+lkd_proc_get_rq_curr (int core)
+{
+
+ if (!rq_curr[core])
+ {
+ CORE_ADDR curr_addr = lkd_proc_get_runqueues (0);
+ if (!curr_addr)
+ return 0;
+ curr_addr =
+ curr_addr + (CORE_ADDR) per_cpu_offset[core] + F_OFFSET (rq, curr);
+ rq_curr[core] = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ ,
+ LKD_BYTE_ORDER);
+ }
+ return rq_curr[core];
+};
+
+/*attempt getting the idle task for a core*/
+static CORE_ADDR
+get_rq_idle (int core)
+{
+ CORE_ADDR curr_addr = lkd_proc_get_runqueues (0);
+
+ if (!curr_addr || !HAS_FIELD (rq, idle))
+ return 0;
+
+ if (!rq_idle[core])
+ {
+ curr_addr += (CORE_ADDR) per_cpu_offset[core] + F_OFFSET (rq, idle);
+
+ rq_idle[core] = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ ,
+ LKD_BYTE_ORDER);
+ }
+ return rq_idle[core];
+};
+
+static int
+get_process_count (int core)
+{
+ CORE_ADDR curr_addr = (CORE_ADDR) per_cpu_offset[core];
+ int proc_cnt;
+ static int warned = 0;
+
+ /* curr_addr can be null on UNI systems
+ * */
+ if (HAS_ADDR (process_counts))
+ curr_addr += ADDR (process_counts);
+ else if (HAS_ADDR (per_cpu__process_counts))
+ curr_addr += ADDR (per_cpu__process_counts);
+ else
+ {
+ /* return a fake, changing value
+ * at lest the list will be refreshed, but in a less optimal way.*/
+ if (!warned)
+ printf_filtered ("this kernel does not support `process_counts`\n");
+
+ if (!lkd_stepping)
+ warned++;
+
+ return warned;
+ }
+
+ proc_cnt = read_memory_unsigned_integer (curr_addr, 4 /*uint32 */ ,
+ LKD_BYTE_ORDER);
+
+ return proc_cnt;
+};
+
+static int
+get_last_pid (void)
+{
+ int new_last_pid = 0;
+
+ if (HAS_ADDR (init_pid_ns))
+ {
+ /* Since STLinux 2.3 (2.6.23) */
+ new_last_pid = read_signed_field (ADDR (init_pid_ns),
+ pid_namespace, last_pid);
+ }
+ else
+ printf_filtered ("this kernel does not support `init_pid_ns`\n");
+
+ return new_last_pid;
+};
+
+void
+lkd_proc_init (void)
+{
+ int i = MAX_CORES;
+ struct thread_info *th = NULL;
+ struct cleanup *cleanup;
+
+ memset (per_cpu_offset, 0, MAX_CORES * sizeof (uint32_t));
+ memset (mmu_info, 0, MAX_CORES * sizeof (struct mmu_infos));
+
+ /* ensure thread list from beneath target is up to date */
+ cleanup = make_cleanup_restore_integer (&print_thread_events);
+ print_thread_events = 0;
+ update_thread_list ();
+ do_cleanups (cleanup);
+
+ /* count the h/w threads
+ */
+ max_cores = thread_count ();
+ gdb_assert (max_cores);
+
+ if (HAS_ADDR (__per_cpu_offset))
+ {
+ int core = max_cores;
+
+ read_memory (ADDR (__per_cpu_offset),
+ (gdb_byte *) (per_cpu_offset),
+ max_cores * sizeof (uint32_t));
+
+ while (--core)
+ if (!per_cpu_offset[core])
+ {
+ warning ("Suspicious null per-cpu offsets,"
+ " or wrong number of detected cores:\n"
+ "ADDR (__per_cpu_offset) = %s\nmax_cores = %d",
+ phex (ADDR (__per_cpu_offset),4), max_cores);
+ break;
+ }
+ }
+ else
+ {
+ DEBUG (D_INIT, 1, "Assuming non-SMP kernel.\n");
+ }
+
+ if (!lkd_proc_get_runqueues (1 /*reset */ ) && (max_cores > 1))
+ printf_filtered ("\nCould not find the address of cpu runqueues:"
+ "\ncurrent context information maybe less precise\n.");
+}
+
+/* still useful with non-smp systems
+ **/
+CORE_ADDR current_task_struct[MAX_CORES];
+CORE_ADDR current_thread_info[MAX_CORES];
+
+int
+lkd_proc_refresh_info (int cur_core)
+{
+ int i = max_cores;
+ int new_last_pid;
+ process_t *ps;
+ int do_invalidate = 0;
+
+ memset (running_process, 0, max_cores * sizeof (process_t *));
+ memset (current_thread_info, 0, max_cores * (sizeof (CORE_ADDR)));
+ memset (current_task_struct, 0, max_cores * (sizeof (CORE_ADDR)));
+ memset (rq_curr, 0, max_cores * sizeof (CORE_ADDR));
+
+ DEBUG (TASK, 1, "WAS: last_pid=%d, pcount[0]=%d, pcount[1]=%d\n",
+ last_pid, process_counts[0], process_counts[1]);
+
+ new_last_pid = get_last_pid ();
+ if (new_last_pid != last_pid)
+ {
+ do_invalidate = 1;
+ last_pid = new_last_pid;
+ }
+
+ /* check if a process exited */
+ for (i = 0; i < max_cores; i++)
+ {
+ int new_pcount = get_process_count (i);
+ if (new_pcount != process_counts[i])
+ {
+ process_counts[i] = new_pcount;
+ do_invalidate = 1;
+ }
+ }
+
+ DEBUG (TASK, 1, "NEW: last_pid=%d, pcount[0]=%d, pcount[1]=%d\n",
+ last_pid, process_counts[0], process_counts[1]);
+
+ if (do_invalidate)
+ lkd_proc_invalidate_list ();
+
+ /* Update the process_list now, so that init_task is in there. */
+ (void) lkd_proc_get_list ();
+
+ /* Call update to prune gdb_thread no longer linked to a linux task.*/
+ if (lkd_private.loaded == LKD_LOADED)
+ update_thread_list();
+
+ /* Set the running process
+ *
+ * we now have a thread_list looking like this:
+ * [1] = { 42000, 0, 1 }
+ * [2] = { 42000, 0, 2 }
+ * [3] = { 42000, 1, -1 }
+ * ....
+ * [N] = { 42000, PID_N, -1 }
+ *
+ * Now set the tid according to the running core,
+ * */
+ for (i = 0; i < max_cores; i++)
+ lkd_proc_get_running (i);
+
+ wait_process = lkd_proc_get_running (cur_core);
+
+ if (!wait_process)
+ return 0;
+
+ DEBUG (TASK, 1, "wait_process: lwp = %ld\n", ptid_get_lwp(PTID_OF (wait_process)));
+ DEBUG (TASK, 1, "wait_process: pid = %d\n", ptid_get_pid(PTID_OF (wait_process)));
+ DEBUG (TASK, 1, "wait_process: tid = %ld\n", ptid_get_tid(PTID_OF (wait_process)));
+
+ gdb_assert(wait_process->gdb_thread);
+ gdb_assert((process_t *) wait_process->gdb_thread->priv == wait_process);
+
+ /* Notify ptid changed. */
+ ps = process_list;
+ while (ps && ps->valid)
+ {
+ if (ptid_get_tid(ps->old_ptid) != ptid_get_tid(PTID_OF (ps)))
+ {
+ observer_notify_thread_ptid_changed (ps->old_ptid, PTID_OF (ps));
+ ps->old_ptid.tid = ptid_get_tid(PTID_OF (ps));
+ }
+ ps = ps->next;
+ }
+
+ switch_to_thread(PTID_OF (wait_process));
+ gdb_assert(lkd_proc_get_by_ptid(inferior_ptid) == wait_process);
+
+ return 1;
+}
+
+/* Setup the symbols to reflect the namespace of the passed
+ process. GDB doesn't really support this. We hack this support by
+ appending the list of objfiles containing the debug information
+ for the process to the list of objfiles that contain the debug
+ info for the kernel. When we switch to another process, we remove
+ the objfiles for the old process and replace it with the objfiles
+ for the new one. The pointers to the objfiles for a given process
+ are stored in the associated process_t. */
+void
+lkd_proc_set_symfile (void)
+{
+ process_t *ps;
+
+
+ ps = lkd_proc_get_by_ptid (inferior_ptid);
+ if (ps && ps->main_objfile)
+ symfile_objfile = ps->main_objfile;
+}
+
+
+/* Selected the task that is really running on the CPU. */
+void
+running_task_command (char *args, int from_tty)
+{
+ ptid_t ptid;
+ char *thread_id;
+
+ if (!wait_process)
+ return;
+
+ ptid = PTID_OF (wait_process);
+ thread_id = xstrprintf ("%d", ptid_to_global_thread_id (ptid));
+
+ /* switch_to_thread() sets inferior_ptid to ptid */
+ gdb_thread_select (current_uiout, thread_id, NULL);
+
+ xfree (thread_id);
+}
new file mode 100644
@@ -0,0 +1,63 @@
+/*
+ Linux Awareness extension target (Linux Kernel Debugger)
+ Copyright 2011-2013 STMicroelectronics.
+*/
+
+#ifndef __LKD_PROCESS_H__
+#define __LKD_PROCESS_H__
+
+/* The list of tasks as cached in the debugger. */
+typedef struct process_t_
+{
+ struct process_t_ *next;
+ CORE_ADDR task_struct;
+ CORE_ADDR mm;
+ CORE_ADDR active_mm;
+ CORE_ADDR pgd; /* FIXME: this should be arch specific, needs fix for SH4 */
+
+ ptid_t old_ptid;
+
+ int core; /*this is the "dynamic" core info */
+
+ int tgid;
+ unsigned int prio;
+ char *comm;
+ int valid;
+
+ struct thread_info *gdb_thread;
+
+ /* user process info */
+ struct objfile *objfiles;
+ struct objfile *main_objfile;
+} process_t;
+
+#define VM_EXECUTABLE 0x00001000
+#define VM_EXEC 0x00000004
+
+#define PTID_OF(ps) ((ps)->gdb_thread->ptid)
+
+extern process_t *running_process[];
+extern process_t *selected_process;
+extern process_t *wait_process;
+extern uint32_t per_cpu_offset[];
+
+
+/* API */
+void lkd_proc_init (void);
+int lkd_proc_core_of_thread (ptid_t ptid);
+void lkd_proc_invalidate_list (void);
+void lkd_proc_free_list (void);
+int lkd_proc_refresh_info (int core);
+void lkd_proc_set_symfile (void);
+process_t *lkd_proc_get_list (void);
+process_t *lkd_proc_get_by_ptid (ptid_t ptid);
+process_t *lkd_proc_get_by_task_struct (CORE_ADDR task);
+process_t *lkd_proc_get_running (int core);
+CORE_ADDR lkd_proc_get_runqueues (int reset);
+CORE_ADDR lkd_proc_get_rq_curr (int core);
+int lkd_proc_is_curr_task (process_t * ps);
+
+/*commands*/
+void running_task_command (char *args, int from_tty);
+
+#endif /*__LKD_PROCESS_H__*/
new file mode 100644
@@ -0,0 +1,486 @@
+/*
+ Linux Awareness extension target (Linux Kernel Debugger)
+
+ Copyright 2010-2013 STMicroelectronics.
+
+ The Linux Awareness layer allows GDB to debug a Linux kernel
+ through a H/W link (presumably a JTAG) as if it were a normal
+ executable. One major issue with debugging the kernel through a
+ JTAG link is the handling of virtual memory.
+
+ Virtual memory raises some issues while debugging through the JTAG
+ because the processor needs to have been correctly setup to be able
+ to access such memory. Usually, the TLB needs to be loaded with the
+ correct virtual memory translation so that the debugger may access
+ it. When some code running above (or inside) Linuux accesses virtual
+ memory, the processor raises an exception that will trigger
+ handlers in the kernel that in turn will setup everything needed
+ for the access to succeed. When the user wants to access such
+ memory from the debugger, the processor is basically 'stalled'. No
+ handlers will be called in case of exception and the access will
+ (sometimes silently) fail. This means the debugger has to setup
+ everything correctly *before* trying any virtual memory access.
+
+ Once the debugger knows how to handle virtual memory, it can try to
+ debug modules. In most Linux ports, kernel modules are loaded in
+ kernel virtual memory (as returned by vmalloc()). This layer tries
+ to expose kernel modules as shared libraries to the rest of
+ GDB. This is done by registering a 'struct target_so_ops' that
+ implements a shared library handling that know how to deal with
+ kernel modules. Indeed, kernel modules are different from shared
+ libraries in some fundamental ways. First, the kernel module binary
+ files are relocatable files, they haven't undergone a link
+ step. This means eg. that the files might contain multiple sections
+ at the same address. This isn't handled nicely by GDB. The solution
+ implemented is to mimic the little linker that does the relocation
+ job in the kernel and do a real link that generates a fully
+ relocated file. This file can then be handled like a stantdard
+ shared library. There's another major difference between modules
+ and shared libraries: part of the module code and data might be
+ unmapped after the module's init step is over. GDB isn't designed
+ to allow part of debug information to disappear, thus we have to
+ work around that. It's important to handle that case, because the
+ freed memory will be reused by subsequent module loads, leading to
+ a big mess of overlapping informations if the information from the
+ unloaded code isn't somehow handled. As said above, GDB can't
+ forget part of the debug information. The workaround use is to
+ 'destroy' the debug information relating to the unloaded code by
+ pretending that every object that disappeared is located at
+ 0xFFFFFFFF (there is a repeating ~(CORE_ADDR)0 pattern in the
+ file). The detection of module load, unloads and end of init events
+ is done by installing breakpoints at carefully chosen points.
+
+ The other major thing done in this file, is to expose all the
+ running tasks of the Linux kernel as threads of the debugged
+ application to GDB. To make abstraction more natural from an UI
+ point of view, the command 'info tasks' is introduced as an alias
+ for 'info threads'.
+
+ Most of the above is achieved by inserting the 'struct target_ops
+ linux_aware_ops' on the top of the target stack. The target stack
+ is the way GDB talks to the debugged target. It exposes an API
+ defined in 'struct target_ops'. Most of the functions have explicit
+ names, like 'fetch_register', 'resume', 'xfer_memory'. This is a
+ stack because it is constructed by piling up layers that provide
+ certain functionality. For example, when you first open an
+ executable file, a layer (or stratum in GDB speak) that knows how
+ to read the memory from the file is on the target stack. Then you
+ run the executable and a layer that knows how to access the runtime
+ memory and control the execution of the process is pushed on the
+ stack. Then it might be that this is a threaded application, and
+ the thread awareness is pushed as another layer on the target
+ stack. Each stratum has the ability to overload the functions
+ defined by the target_ops structure and to access the layers below
+ its level. The code in target.c constructs the current_target
+ structure each time the target stack is modified by a call to
+ (push|unpush)_target (). In this file we push 'linux_aware_ops' at
+ the very top of the target stack, and thus it's able to intercept
+ every communication between GDB and the debugged processor. For
+ example we trap virtual memory accesses this way and do the
+ required setup before passing the access to the lower layer of the
+ target stack that will really perform the access.
+
+ A last thing that is implemented in this file is a few generic data
+ display commands like 'dmesg', 'proc_iomem', 'proc_ioports', ...
+ */
+#ifndef __LINUX_AWARENESS_H__
+#define __LINUX_AWARENESS_H__
+
+#define LKD_VERSION_STRING "7.11-development"
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+
+/********** from breakpoint.c **********************/
+
+extern struct breakpoint *breakpoint_chain;
+extern struct bp_location **bp_location;
+extern int breakpoint_count;
+extern unsigned bp_location_count;
+
+#define ALL_BREAKPOINTS(B) for (B = breakpoint_chain; B; B = B->next)
+
+#define ALL_BREAKPOINTS_SAFE(B,TMP) \
+ for (B = breakpoint_chain; \
+ B ? (TMP=B->next, 1): 0; \
+ B = TMP)
+
+#define ALL_BP_LOCATIONS(B,BP_TMP) \
+ for (BP_TMP = bp_location; \
+ BP_TMP < bp_location + bp_location_count && (B = *BP_TMP); \
+ BP_TMP++)
+
+/*************************************************/
+
+#define LKD_BYTE_ORDER BFD_ENDIAN_LITTLE
+
+/* target stack ordering */
+#define LKD_STRATUM_LINUX (thread_stratum + 10)
+
+
+typedef enum
+{
+ LKD_NOTLOADED = 0,
+ LKD_LOADED = 1, /*must be one */
+ LKD_LOADING = 2,
+} lkd_load_states_t;
+
+/**
+ * LKD user parameters.
+ */
+struct linux_awareness_params
+{
+
+ /* Global flag set by 'set linux-awareness enabled'. The user
+ shouldn't have to use that as the enablementlinux_awareness_check should be triggered
+ automatically by the kernel autodetection routines. */
+ int enabled;
+
+ /* Flag indicating that the kernel has been loaded. This is set by the
+ 'load' command, but it might be necessary to set it by hand ('set
+ linux-awareness loaded 1'), eg. when attaching to a running target
+ with another command than the standard 'attach'. */
+ lkd_load_states_t loaded;
+
+ /* For debugging. One can disable the task handling
+ issuing 'set linux-awareness enable_task_awareness 0' */
+ int enable_task_awareness;
+
+ /* A user might disable the automatic load of the Linux awareness
+ layer by issuing 'set linux-awareness auto_activate 0' before
+ loading the kernel binary. (This might be usefull in a .(sh)gdbinit
+ file. */
+ int auto_activate;
+
+ /* The current global loglevel as set by 'set linux-awareness
+ debug-all' */
+ int loglevel;
+
+ /* skip the schedule() frame in the backtrace */
+ int skip_schedule_frame;
+
+ /* disable thread info coloring */
+ int no_colors;
+};
+
+extern struct linux_awareness_params lkd_params;
+
+/* use scratch area for messing around with strings
+ * to avoid static arrays and dispersed mallocs and frees
+ **/
+struct lkd_private_data
+{
+ lkd_load_states_t loaded;
+ int connected;
+ int keep_do_exit_event;
+
+ unsigned char *string_buf;
+ int string_buf_size;
+
+ char *banner_file; /* string for the banner as read from vmlinx */
+ int banner_file_size; /* max size allocated */
+ int banner_file_valid; /* valid or to refresh */
+
+ int proc_list_invalid;
+
+ char *banner_mem; /* string for the banner as read from vmlinx */
+ int banner_mem_size; /* max size allocated */
+ int banner_mem_valid; /* valid or to refresh */
+
+ /* The UTS name as extracted from the file or the memory. This serves
+ to build the path that points to the depmod cache. */
+ char *utsname_release;
+ int utsname_release_size; /* max size allocated */
+ int utsname_release_valid; /* valid or to refresh */
+
+ struct type *target_pointer_type;
+
+ uint32_t kflags;
+};
+
+extern struct lkd_private_data lkd_private;
+
+void lkd_loaded_set (char *arg, int from_tty, struct cmd_list_element *c);
+void lkd_enabled_set (char *args, int from_tty, struct cmd_list_element *c);
+void lkd_reset_thread_list (void);
+long lkd_eval_long (char *string);
+
+/* lkd-irqs.c*/
+void interrupts_command (char *args, int from_tty);
+
+/* Mimic kernel macros */
+#define container_of(ptr, struc, field) ((ptr) - F_OFFSET(struc, field))
+
+extern int max_cores;
+#define MAX_CORES 5
+#define CORE_INVAL (-1) /* 0 = name on the inferior, cannot be used */
+
+extern const struct objfile_data *linux_uprocess_objfile_data_key;
+
+extern int stopped_core;
+extern int lkd_stepping;
+
+struct process_t_;
+
+struct linux_awareness_ops
+{
+ const char *name;
+
+ /* Check if the current application is a compatible Linux kernel. */
+ int (*lo_check_kernel) ();
+
+ int (*lo_check_mem_rdy) (); /*must implement. */
+
+ /* Called at the beginning of a debugging session. */
+ int (*lo_init) ();
+ /* Called at the end of a debugging session. Can be NULL. */
+ void (*lo_close) ();
+ /* Called before detaching from the targe. Can be NULL. */
+ int (*lo_pre_detach) (char *prog, int fromtty);
+ /* Called before the load of the kernel. Can be NULL. */
+ void (*lo_pre_load) (char *prog, int fromtty);
+ /* Called after the load of the kernel. Can be NULL. */
+ void (*lo_post_load) (char *prog, int fromtty);
+ /* Called before the processor is resumed. Can be NULL. */
+ void (*lo_pre_exec_start) ();
+
+
+ /* Returns wether the page containing addr in the context of task_struct
+ is mapped as writable. */
+ int (*lo_can_write) (CORE_ADDR addr, CORE_ADDR task_struct);
+ /* Returns wether the passed address is a userspace address. */
+ int (*lo_is_user_address) (CORE_ADDR addr);
+ /* Returns wether the passed address is a kernelspace address. */
+ int (*lo_is_kernel_address) (CORE_ADDR addr);
+ /* Returns wether the passed handler lies inside a TLB miss
+ handler. This is used to singlestep through code in virtual
+ memory: when this code produces an exception, the awareness
+ layer will hide from the user (by silently singlstepping
+ through) all the code where that callback returns true. */
+ int (*lo_is_tlb_miss_handler) (CORE_ADDR addr);
+ /* Flush the processor cache lines corresponding to the virtual
+ address virtaddr and physical addr physaddr (as returned by
+ lo_translate_memory_address() above).
+ Called with virtaddr == physaddr, the aim is to flush the
+ aliased line that might have been introduced by directly
+ accessing the physical address.
+
+ Precondition :
+ [virtaddr..virtaddr+len[ lies on the same physical page.
+ */
+ void (*lo_flush_cache) (CORE_ADDR virtaddr, CORE_ADDR physaddr,
+ int len, int write);
+ /* If the target has to use software singlestepping, this callback
+ is called and returns the instruction that'll be executed after
+ the one at pc. Can be NULL. */
+ CORE_ADDR (*lo_single_step_destination) (CORE_ADDR pc);
+ /* This callback is called when the execution is resumed and we
+ might switch task. It's different from lo_pre_exec_start() that
+ will be called systematically. For example, cached virtual
+ memory translations can be discarded here. When this called
+ back isn't called, the debugger stays in the same task, thus
+ the cached translations should stay valid. */
+ void (*lo_clear_cache) ();
+
+ /* Should supply the current regcache with the value of register
+ regno in the context of task_struct. This will only be called
+ for tasks that are stopped in the scheduler. The way the task
+ state is stored on context switch is target specific. */
+ int (*lo_fetch_context_register) (int regno, CORE_ADDR task_struct);
+ /* Should store in the target the value of register regno from the
+ current regcache in the context of task_struct. This will only
+ be called for tasks that are stopped in the scheduler. The way
+ the task state is stored on context switch is target specific. */
+ int (*lo_store_context_register) (int regno, CORE_ADDR task_struct);
+
+ int kernel_offset;
+};
+
+extern struct target_ops linux_aware_ops;
+extern struct linux_awareness_ops *linux_awareness_ops;
+
+extern bfd *cur_bfd;
+
+struct type;
+struct cmd_list_element;
+
+extern struct cmd_list_element *set_linux_awareness_cmd_list;
+extern struct cmd_list_element *show_linux_awareness_cmd_list;
+
+struct addr_info
+{
+ char *name;
+ struct bound_minimal_symbol bmsym;
+ struct addr_info *next;
+};
+
+struct field_info
+{
+ char *struct_name;
+ char *field_name;
+ struct symbol *type;
+ int offset;
+ int size;
+ struct field_info *next;
+};
+
+#define FIELD_INFO(s_name, field) _FIELD_##s_name##__##field
+
+#define DECLARE_FIELD(s_name, field) \
+ static struct field_info FIELD_INFO(s_name, field) \
+ = { .struct_name = #s_name, .field_name = #field, 0 }
+
+#define F_OFFSET(struct, field) \
+ linux_get_field_offset (&FIELD_INFO(struct, field))
+#define F_SIZE(struct, field) \
+ linux_get_field_size (&FIELD_INFO(struct, field))
+#define HAS_FIELD(struct, field) \
+ (FIELD_INFO(struct, field).type != NULL \
+ || (linux_init_field(&FIELD_INFO(struct, field), 1), \
+ FIELD_INFO(struct, field).type != NULL))
+
+#define ADDR_INFO(symb) _ADDR_##symb
+
+#define DECLARE_ADDR(symb) \
+ static struct addr_info ADDR_INFO(symb) = { .name = #symb, .bmsym = {NULL, NULL} }
+
+#define HAS_ADDR(symb) \
+ (ADDR_INFO(symb).bmsym.minsym != NULL \
+ || (linux_init_addr(&ADDR_INFO(symb), 1), ADDR_INFO(symb).bmsym.minsym != NULL))
+
+#define ADDR(sym) linux_get_address (&ADDR_INFO(sym))
+
+#define read_unsigned_field(base, struct, field) \
+ read_memory_unsigned_integer (base + F_OFFSET (struct, field), \
+ F_SIZE (struct, field), LKD_BYTE_ORDER)
+
+#define read_signed_field(base, struct, field) \
+ read_memory_integer (base + F_OFFSET (struct, field), \
+ F_SIZE (struct, field), LKD_BYTE_ORDER)
+
+#define read_pointer_field(base, struct, field) \
+ read_memory_typed_address (base + F_OFFSET (struct, field), \
+ builtin_type (target_gdbarch ())->builtin_data_ptr)
+
+#define read_unsigned_embedded_field(base, struct, field, emb_str, emb_field) \
+ read_memory_unsigned_integer (base + F_OFFSET (struct, field) \
+ + F_OFFSET (emb_str, emb_field), \
+ F_SIZE (emb_str, emb_field), LKD_BYTE_ORDER)
+
+#define read_signed_embedded_field(base, struct, field, emb_str, emb_field) \
+ read_memory_integer (base + F_OFFSET (struct, field) \
+ + F_OFFSET (emb_str, emb_field), \
+ F_SIZE (emb_str, emb_field), LKD_BYTE_ORDER)
+
+#define read_pointer_embedded_field(base, struct, field, emb_str, emb_field) \
+ read_memory_typed_address (base + F_OFFSET (struct, field) \
+ + F_OFFSET (emb_str, emb_field), \
+ builtin_type (target_gdbarch ())->builtin_data_ptr)
+
+#define extract_unsigned_field(base, struct, field) \
+ extract_unsigned_integer(base + F_OFFSET (struct, field), \
+ F_SIZE (struct, field), LKD_BYTE_ORDER)
+
+#define extract_signed_field(base, struct, field) \
+ extract_signed_integer (base + F_OFFSET (struct, field), \
+ F_SIZE (struct, field), LKD_BYTE_ORDER)
+
+#define extract_pointer_field(base, struct, field) \
+ extract_typed_address (base + F_OFFSET (struct, field), \
+ builtin_type(target_gdbarch ())->builtin_data_ptr)
+
+/*cleanup macro to compare an address to a bp loc value*/
+#define IS_LOC(pc,evnt) ((evnt != NULL) && (pc == evnt->loc->address))
+
+enum page_status
+{
+ PAGE_PRESENT,
+ PAGE_SWAPPED,
+ PAGE_NOTMAPPED,
+ PAGE_NOPAGE,
+ PAGE_UNKNOWN
+};
+
+/*kversion building macros */
+#define KERNEL_VERSION(MM,mm,bb) (((MM) << 16) + ((mm) << 8) + (bb))
+
+/* last known (validated) STLinux kernel version for this LKD.
+ * if we're beyond this version, of not on STLinux, issue a warning
+ **/
+#define KFLAG_NOLINUX 0x00000000 /* not linux */
+#define KFLAG_LINUX 0x00000001 /* is linux */
+#define KFLAG_STLINUX 0x00000002 /* STMicroelectronics linux distro */
+#define KFLAG_DBGINFO 0x00000004 /* has debug information */
+
+/* kernel release string */
+extern char g_utsname_release[];
+
+struct debug_domain
+{
+ const char *name;
+ int level;
+};
+
+extern struct debug_domain linux_aware_debug_domains_info[];
+
+enum linux_aware_debug_domain
+{
+ TASK,
+ TARGET,
+ D_INIT,
+ KEEP_LAST
+};
+
+#define DEBUG(domain, l, ...) \
+ ({if (domain < KEEP_LAST \
+ && linux_aware_debug_domains_info[domain].level >= l) \
+ fprintf_filtered(gdb_stdlog, "linux: " __VA_ARGS__);})
+
+/*do thing a different way in debug mode.*/
+#define DBG_IF(domain) if (linux_aware_debug_domains_info[domain].level) {
+#define DBG_ELSE } else {
+#define DBG_ENDIF(domain) }
+
+#define LA_NOT_LOADED_STRING "Please type \"set linux-awareness loaded\".\n"
+
+int linux_init_addr (struct addr_info *field, int check);
+int linux_init_field (struct field_info *field, int check);
+
+static inline CORE_ADDR
+linux_get_address (struct addr_info *addr)
+{
+ if (addr->bmsym.minsym == NULL)
+ linux_init_addr (addr, 0);
+
+ return BMSYMBOL_VALUE_ADDRESS (addr->bmsym);
+}
+
+static inline unsigned int
+linux_get_field_offset (struct field_info *field)
+{
+ if (field->type == NULL)
+ linux_init_field (field, 0);
+
+ return field->offset;
+}
+
+static inline unsigned int
+linux_get_field_size (struct field_info *field)
+{
+ if (field->type == NULL)
+ linux_init_field (field, 0);
+
+ return field->size;
+}
+
+int lkd_try_push_target (void);
+void lkd_uninstall_do_exit_event (void);
+
+void sanitize_path (char *path);
+char *linux_aware_get_target_root_prefix (void);
+
+int linux_aware_target_core (void);
+
+#endif /*__LINUX_AWARENESS_H__*/