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


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

[PATCH 13/18] Add new "wtx" target.


This adds the bulk of the support for VxWorks systems through a new
target: wtx.

gdb/ChangeLog:

        * remote-wtx.h, remote-wtx.c: New files.
---
 gdb/remote-wtx.c | 3278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/remote-wtx.h |   25 +
 2 files changed, 3303 insertions(+), 0 deletions(-)
 create mode 100644 gdb/remote-wtx.c
 create mode 100644 gdb/remote-wtx.h

diff --git a/gdb/remote-wtx.c b/gdb/remote-wtx.c
new file mode 100644
index 0000000..95b02ef
--- /dev/null
+++ b/gdb/remote-wtx.c
@@ -0,0 +1,3278 @@
+/* Support for the WTX protocol.
+
+   Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+   Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "remote-wtx.h"
+#include "ada-lang.h"
+#include "language.h"
+#include "remote-wtxapi.h"
+#include "remote-wtx-opt.h"
+#include "remote-wtx-pd.h"
+#include "target.h"
+#include "exceptions.h"
+#include "symfile.h"
+#include "filenames.h"
+#include "command.h"
+#include "objfiles.h"
+#include "remote-wtx-utils.h"
+#include "remote-wtx-bp.h"
+#include "remote-wtx-tasks.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "remote-wtx-hw.h"
+#include "regcache.h"
+#include "gdb_select.h"
+#include "observer.h"
+#include "gdbcmd.h"
+
+#include <signal.h>
+
+/* The target_ops vectors for the WTX protocol support.
+   There is one vector per mode of operation:
+     - Connected to the target server, but not attached;
+     - Attached to a task ("task" and "multi-tasks" modes);
+     - Attached to the system ("system" mode).  */
+
+static struct target_ops wtx_ops;
+static struct target_ops wtx_task_mode_ops;
+static struct target_ops wtx_system_mode_ops;
+
+/* The directory name extracted from from the boot-line info.
+   This directory is initialized when we connect to the target server,
+   and will mosly be used to locate the boot image.  */
+static char *kernel_directory = NULL;
+
+/* The PID of the task we are currently attached to.  This is used
+   to identify the CTX_EXIT event of the task we are debugging from
+   the same event for other tasks.  */
+static int attached_context_id = 0;
+
+/* Set to non-zero if we need to fake an "attach" event in the wait loop.  */
+static int fake_attach_event = 0;
+
+/* Status of the inferior after an asynchronous interruption (attach,
+   to_stop...) It is used to determine whether to use the "cont" or
+   the "resume" request to resume the execution of the inferior.  */
+static enum wtx_task_status inferior_status = WTX_TASK_STATUS_UNKNOWN;
+
+/* A variable that is set to non-zero when the inferior was stopped
+   by a watchpoint.  Contains the address of the data being watched.  */
+static CORE_ADDR watchpoint_data_address = 0;
+
+/* Some function advance declarations.  */
+
+static void unload_module_symbols (const char *module_filename, int from_tty);
+static int wtx_context_alive (int context_id);
+
+/* Return non-zero if we are currently running in system-mode.  */
+
+static int
+system_mode_p (void)
+{
+  return (target_shortname == wtx_system_mode_ops.to_shortname);
+}
+
+/* Build a ptid from the given TASK_ID.  */
+
+static ptid_t
+context_id_to_ptid (WTX_CONTEXT_ID_T context_id)
+{
+  /* Build the ptid by using the CONTEXT_ID as the pid, and leave
+     the thread & lwp ids set to 0.  However, in system mode, the
+     system context ID is -1, which would normally translate to
+     a ptid that GDB treats specially.  So, for the system context id,
+     we use a special PID of 1.  */
+  if (context_id == SYSTEM_CID)
+    return ptid_build (1, 0, 1);
+
+  return ptid_build (1, 0, context_id);
+}
+
+/* Return the task context ID of the given PTID.  */
+
+static WTX_CONTEXT_ID_T
+ptid_get_context_id (ptid_t ptid)
+{
+  int tid = ptid_get_tid (ptid);
+
+  if (tid == 1)
+    /* This is the special PID we use in system mode for the system ptid
+       (see context_id_to_ptid above).  Return the system context ID.  */
+    return SYSTEM_CID;
+
+  return tid;
+}
+
+/* Record PTID as a new inferior.  ATTACHED should be non-zero if
+   we just attached to this process, as oppposed to creating it.  */
+
+static void
+wtx_add_inferior (ptid_t ptid, int attached)
+{
+  struct inferior *inf;
+
+  /* For now, we use a model where we only have one inferior at a time.
+     So we simply bind the inferior to the current inferior.  */
+  inf = current_inferior ();
+  inferior_appeared (inf, ptid_get_pid (ptid));
+  inf->attach_flag = attached;
+
+  /* This inferior is also the main thread, so add it as a thread. Reset
+     thread list in order to clean up leftovers from previous attach/detach
+     operations.  */
+  init_thread_list ();
+  add_thread_silent (ptid);
+}
+
+/* Add PTID to the thread list (if needed).  */
+
+static void
+wtx_add_thread (ptid_t ptid)
+{
+  if (!in_thread_list (ptid))
+    add_thread (ptid);
+}
+
+/* Perform a WTX_CONTEXT_CONT operation for the given context type
+   and ID.
+
+   FIXME: brobecker/2007-07-06: This function is not completely
+   implemented yet.  The handling of expected error situations
+   is still missing.  See the vxworks-5.3 code, and in particular
+   the little tap-dance with current_wtx_operation, and its use
+   in errorHandler.  For now, this function is basically a simple
+   wrapper around wtxapi_context_cont.  */
+
+static int
+wtx_context_continue (WTX_CONTEXT_TYPE context_type,
+                      WTX_CONTEXT_ID_T context_id)
+{
+  return wtxapi_context_cont (context_type, context_id);
+}
+
+/* Resume the execution of the inferior given its CONTEXT_TYPE and
+   CONTEXT_ID.
+
+   On 653, the system makes the distinction between the STOPPED state
+   which occurs after the thread has hit a breakpoint, and the SUSPEND
+   state, which happens after we attach to the system.  As a result,
+   the debugger needs to use either the "cont" or "resume" WTX request
+   to resume the thread execution.
+
+   On Tornado 2, either request will work on all cases, because
+   the system only provides the SUSPEND mode.  However, the documentation
+   explains that it is better to use the "cont" request for breakpoint
+   events on T2 as well.  This is very convenient as the same code
+   can be shared for all Tornado versions.  */
+
+static int
+continue_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+  int result = 0;
+
+  switch (inferior_status)
+    {
+    case WTX_TASK_STATUS_CREATED:
+      result = wtxapi_context_resume (context_type, context_id);
+      break;
+    case WTX_TASK_STATUS_STOPPED:
+      if (multi_task_mode_is_on ())
+        result = continue_all_ada_tasks ();
+      else
+        result = wtx_context_continue (context_type, context_id);
+      break;
+    case WTX_TASK_STATUS_SUSPENDED:
+      if (multi_task_mode_is_on ())
+        result = continue_all_ada_tasks ();
+      else
+        result = wtxapi_context_resume (context_type, context_id);
+      break;
+    default:
+      result = wtxapi_context_resume (context_type, context_id);
+      break;
+    }
+
+  if (!result)
+    inferior_status = WTX_TASK_STATUS_UNKNOWN;
+  else
+    inferior_status = WTX_TASK_STATUS_RUNNING;
+  return result;
+}
+
+/* Perform a "suspend" operation on the inferior, given its
+   CONTEXT_TYPE and its CONTEXT_ID. inferior_status is updated
+   accordingly.  */
+
+static int
+suspend_inferior  (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+  int result = wtxapi_context_suspend (context_type, context_id);
+  if (!result)
+    inferior_status = WTX_TASK_STATUS_UNKNOWN;
+  else
+    inferior_status = WTX_TASK_STATUS_SUSPENDED;
+  return result;
+}
+
+/* Perform a "stop" operation on the inferior, given its
+   CONTEXT_TYPE and its CONTEXT_ID. inferior_status is updated
+   accordingly.  */
+
+static int
+stop_inferior  (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+  int result = wtxapi_context_stop (context_type, context_id);
+  if (!result)
+    inferior_status = WTX_TASK_STATUS_UNKNOWN;
+  else
+    inferior_status = WTX_TASK_STATUS_STOPPED;
+  return result;
+}
+
+/* A function that is meant to be used as the callback function for
+   bfd_map_over_sections, an whose purpose is to make a shallow
+   duplication of the array of sections for the given BFD object.  */
+
+static void
+collect_bfd_sections (bfd *abfd, asection *sect, void *ptr)
+{
+  asection **sections = (asection **) ptr;
+
+  sections[sect->index] = sect;
+}
+
+/* Return nonzero if the the given section SECT is "loadable" (as indicated
+   by its section flags).  Loadable here means either that the section is
+   loaded in memory, or that it triggers the creation of a memory region.  */
+
+static int
+section_is_loadable (asection *sect)
+{
+  flagword flags = bfd_get_section_flags (NULL, sect);
+
+  return ((flags & SEC_ALLOC) || (flags & SEC_LOAD));
+}
+
+/* Remove any section entry in SECTION_ADDRS if the associated section
+   is not "loadable" (see section_is_loadable above).  The removal is
+   performend in-pace, and SECTION_ADDRS->NUM_SECTIONS is adjusted to
+   reflect the new number of sections.  */
+
+static void
+remove_non_loadable_sections (struct section_addr_info *section_addrs,
+                              asection **bfd_sections)
+{
+  int i;
+  int new_num_sections = 0;
+
+  for (i = 0; i < section_addrs->num_sections; i++)
+    if (section_is_loadable (bfd_sections[section_addrs->other[i].sectindex]))
+      {
+	if (i != new_num_sections)
+	  section_addrs->other[new_num_sections] = section_addrs->other[i];
+	new_num_sections++;
+      }
+  section_addrs->num_sections = new_num_sections;
+}
+
+/* Build the SECTION_ADDRS field of the give MODULE_INFO using
+   the module segment base addresses.
+
+   We compute the section addresses by:
+     1. Getting the list of sections and their attribute from
+        the object file (using bfd)
+     2. Iterate in order over each section and:
+          a. Determine to which segment it belongs
+          b. Compute its address as being just after the previous
+             section that lives in the same segment (modulo alignment)
+
+   This function assumes that MODULE_INFO->SEGMENTS is not NULL,
+   or will abort with a failed assertion.  */
+
+static void
+build_module_info_section_addrs (struct wtxapi_module_info *module_info,
+                                 bfd *abfd)
+{
+  CORE_ADDR text_addr;
+  CORE_ADDR data_addr;
+  CORE_ADDR bss_addr;
+  int num_sections;
+  int i;
+  asection **bfd_sections;
+
+  gdb_assert (module_info->segments != NULL);
+
+  text_addr = module_info->segments->text_addr;
+  data_addr = module_info->segments->data_addr;
+  bss_addr = module_info->segments->bss_addr;
+
+  num_sections = bfd_count_sections (abfd);
+  module_info->section_addrs = alloc_section_addr_info (num_sections);
+
+  bfd_sections = (asection **) xzalloc (num_sections * sizeof (asection *));
+  bfd_map_over_sections (abfd, collect_bfd_sections, (void *) bfd_sections);
+
+  for (i = 0; i < num_sections; i++)
+    {
+      CORE_ADDR *addr_ptr = NULL;
+      asection *sect = bfd_sections[i];
+      flagword flags = bfd_get_section_flags (NULL, sect)
+        & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA);
+
+      switch (flags)
+        {
+          case (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE):  /* text */
+          case (SEC_ALLOC | SEC_LOAD | SEC_CODE):         /* writable text */
+            addr_ptr = &text_addr;
+            break;
+
+          case (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA):  /* rodata */
+            if (strcmp (sect->name, ".rodata") == 0)
+              addr_ptr = &text_addr;
+            break;
+          case (SEC_ALLOC | SEC_LOAD | SEC_DATA):                 /* data */
+            addr_ptr = &data_addr;
+            break;
+
+          default:
+            if ((flags & SEC_ALLOC) && !(flags & SEC_LOAD))       /* bss */
+              addr_ptr = &bss_addr;
+            break;
+        }
+
+      if (addr_ptr != NULL)
+        {
+          module_info->section_addrs->other[i].addr =
+            align_power (*addr_ptr, bfd_get_section_alignment (abfd, sect));
+          *addr_ptr = module_info->section_addrs->other[i].addr
+                        + bfd_section_size (abfd, sect);
+        }
+      module_info->section_addrs->other[i].name =
+        xstrdup (bfd_get_section_name (abfd, sect));
+      module_info->section_addrs->other[i].sectindex = sect->index;
+
+      /* FIMXE:brobecker/2007-05-18: On VxWorks systems, the offset
+         of each section of our object file seems to be set to zero.
+         This isn't a problem when we have one section per segment,
+         but it actually causes trouble when we have more than one
+         section per segment (eg when building with -ffunction-sections).
+         What happens in that case is that the relocation module thinks
+         that all these sections are located at the beginning of the
+         associated segment, and ends up computing wrong addresses.
+         This is particularly visible in the relocation of the debugging
+         info sections.
+
+         In my opinion, this is a linker issue.  But we can cover
+         this issue by fixing up the section offsets, at least until
+         the linker is updated.  As far as we know, this is only useful
+         for ".text.*" sections, we do the fixup only for these; it just
+         makes it easier because the base address for the offset is
+         always the text segment base address.  */
+      if (sect->name != NULL
+          && strncmp (bfd_sections[i]->name, ".text.", 6) == 0)
+        bfd_set_section_vma
+          (abfd, sect,
+           module_info->section_addrs->other[i].addr
+           - module_info->segments->text_addr);
+    }
+
+  /* We only expect to find the sections that have been loaded in memory
+     in MODULE_INFO->SECTION_ADDRS, so remove any section which are not
+     loadable.  It seemed easy enough at first to create the array without
+     those sections to start with.  But, in practice, the BFD API made it
+     easier to do it this way.  */
+  remove_non_loadable_sections (module_info->section_addrs, bfd_sections);
+
+  xfree (bfd_sections);
+}
+
+/* Read the symbols from the given MODULE_FILENAME, using the information
+   available in MODULE_INFO.
+
+   This function assumes that the current PD is equal to the module PD.  */
+
+static int
+read_module_symbols_1 (struct wtxapi_module_info *module_info,
+                       char *module_filename)
+{
+  struct gdb_exception e;
+
+
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      bfd *abfd = symfile_bfd_open (module_filename);
+
+      if (module_info->section_addrs == NULL)
+        build_module_info_section_addrs (module_info, abfd);
+
+      symbol_file_add_from_bfd (abfd, 0, module_info->section_addrs,
+				0 /* flags */);
+    }
+  if (e.reason < 0)
+    {
+      printf_filtered (_("Unable to load symbols for module %s: %s\n"),
+                       module_info->module_name, e.message);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Load the symbols from the given module (identified by its MODULE_ID).
+
+   This function handles the case when the module PD is different from
+   the current PD by temporarily switching to the module PD during
+   the symbol reading phase.  */
+
+static void
+read_module_symbols (module_id_t module_id, int ignored_verbose)
+{
+  const pd_id_t current_pd = wtxapi_pd_current_get ();
+  const pd_id_t module_pd = wtx_pd_get_module_pd (module_id);
+  struct cleanup *old_chain = NULL;
+  struct wtxapi_module_info *module_info;
+  struct gdb_exception e;
+  char *module_filename;
+
+  if (module_pd != current_pd)
+    {
+      wtx_pd_switch_to_pd (module_pd);
+      old_chain = wtx_pd_make_switch_to_pd_cleanup (current_pd);
+    }
+
+  module_info = wtxapi_obj_module_info_get (module_pd, module_id);
+  if (module_info == NULL)
+    error (_("Cannot get info for module 0x%x: %s"),
+           (unsigned int) module_id, wtxapi_err_msg_get ());
+
+  printf_filtered ("\t%s: ", module_info->module_name);
+  gdb_flush (gdb_stdout);
+
+  /* The module name is a basename, not an absolute filename.
+     If this file is not present in the current directory, then
+     try to see if it is in the kernel_directory.  */
+
+  module_filename = module_info->module_name;
+  if (!is_regular_file (module_filename) && kernel_directory != NULL)
+    {
+      char *filename = concat (kernel_directory, module_filename, NULL);
+
+      make_cleanup (xfree, filename);
+      if (is_regular_file (filename))
+        module_filename = filename;
+    }
+
+  /* Try reading the symbols from the given module.  */
+  if (read_module_symbols_1 (module_info, module_filename))
+    printf_filtered (_("ok\n"));
+
+  free_wtxapi_module_info (module_info);
+  if (old_chain)
+    do_cleanups (old_chain);
+}
+
+/* Query the target about the list of modules currently loaded,
+   and load the symbols from each module.  */
+
+static void
+read_symbols_from_all_modules (int from_tty)
+{
+  struct wtxapi_module_list *module_list;
+  int i;
+
+  if (from_tty)
+    printf_filtered (_("Looking for all loaded modules:\n"));
+
+  module_list = wtxapi_obj_module_list_get (WTX_MOD_FIND_IN_ALL_PD);
+  if (module_list == NULL)
+    error (_("Could not get target module list: %s"), wtxapi_err_msg_get ());
+
+  make_cleanup (cleanup_wtxapi_module_list, module_list);
+
+  for (i = 0; i < module_list->num_obj_mod; i++)
+    {
+      read_module_symbols (module_list->mod_id_array[i], from_tty);
+      QUIT;
+    }
+
+  if (from_tty)
+    printf_filtered (_("Done.\n"));
+
+  /* Now that we have some object files, GDB should recompute the gdbarch
+     vector using them.  VxWorks does not really have a concept of "the"
+     executable, but we know that all the modules (aka objfiles) are on
+     the same target and are using the same architecture.  So we just
+     pick the first objfile we have and use that to recompute the gdbarch
+     vector.  */
+  if (object_files != NULL && object_files->obfd != NULL)
+    set_gdbarch_from_file (object_files->obfd);
+}
+
+/* Compute the name of the directory where the boot image was loaded
+   from.
+
+   When successful, save the result in KERNEL_DIRECTORY. When not
+   successful, then KERNEL_DIRECTORY is set to NULL.  */
+
+static void
+cache_kernel_directory_name (void)
+{
+  const char *boot_line = wtxapi_target_bootline_get ();
+  char *separator;
+  char *basename = NULL;
+  char *tmp;
+
+  /* If kernel_directory was previous set, free it.  We want to recompute it
+     each time, to make sure it's correct (we may have switched from
+     one board to the other, for instance).  */
+  if (kernel_directory != NULL)
+    {
+      xfree (kernel_directory);
+      kernel_directory = NULL;
+    }
+
+  if (boot_line == NULL)
+    return;  /* No boot-line information, so nothing much we can do.  */
+
+  /* The format of the boot path follows the following syntax:
+     [<hostname> ":"]<kernel module full path>  */
+
+  /* Skip the hostname if specified, being careful of not stripping
+     the drive letter on Windows hosts...  */
+
+  separator = strchr (boot_line, ':');
+  if (separator != NULL
+      && !(have_dos_based_filesystem ()
+           && (separator - boot_line == 1)))
+    boot_line = separator + 1;
+
+  /* wtxapi_target_bootline_get returns a pointer into temporary memory,
+     so we need to duplicate kernel_directory for our own usage.  */
+  kernel_directory = xstrdup (boot_line);
+
+  /* Strip the module name from the path.
+
+     Keep the trailing directory separator, as this directory will be
+     concatenated with filenames to form full paths.  So no need to strip
+     it here only to add it back later.  */
+
+  tmp = kernel_directory;
+  while (*tmp != '\0')
+    {
+      if (IS_DIR_SEPARATOR (*tmp))
+        basename = tmp + 1;
+      tmp++;
+    }
+
+  if (basename != NULL)
+    *basename = '\0';
+}
+
+/* Implement the to_open method of the target_ops structure
+   for the WTX protocol.  */
+
+void
+wtx_open (char *args, int from_tty)
+{
+  char **argv = buildargv (args);
+  char *target_server_name;
+  WTX_AGENT_MODE_TYPE target_agent_mode;
+  struct gdb_exception ex;
+
+  make_cleanup_freeargv (argv);
+
+  if (argv[0] == NULL)
+    error (_("target name is missing"));
+  target_server_name = argv[0];
+
+  printf_filtered (_("Connecting to target server: %s...\n"),
+                   target_server_name);
+  unpush_target (&wtx_ops);
+
+  if (!wtxapi_tool_attach (target_server_name, wtx_opt_tool_name ()))
+    error (_("Could not connect to target server."));
+
+  push_target (&wtx_ops);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* Make sure that we can communicate with the target server.
+         If the attempt failed and WTX reports that the target server
+         is not attached (target server locked by another user, server
+         is up but target rebooted, server is up but not attached to
+         the target agent, ...), then print a warning and try attaching
+         again.  */
+      if (!wtxapi_target_server_available_p ()
+          && wtxapi_err_get () == WTX_ERR_SVR_TARGET_NOT_ATTACHED)
+        {
+          warning (_("target not attached to target server, reattaching..."));
+
+          if (!wtxapi_target_attach ())
+            error (_("Failed to attach to target: %s"), wtxapi_err_msg_get ());
+        }
+
+      /* Make sure that we can communicate with the target server now.
+         If all our previous attemps to connect properly failed, then
+         abort.  */
+      if (!wtxapi_target_server_available_p ())
+        error (_("Connection to target server is not usable: %s."),
+               wtxapi_err_msg_get ());
+
+      target_agent_mode = wtxapi_agent_mode_get ();
+      if (target_agent_mode == invalid_agent_mode)
+        error (_("Could not get agent mode: %s"), wtxapi_err_msg_get ());
+
+      if (!wtxapi_register_for_event (".*"))
+        error (_("Could not register for target events: %s"),
+               wtxapi_err_msg_get ());
+
+      printf_filtered (_("Connected to %s\n"), wtxapi_ts_name_get ());
+
+      cache_kernel_directory_name ();
+      read_symbols_from_all_modules (from_tty);
+      ada_language_auto_set ();
+
+      wtx_hw_initialize ();
+      wtx_opt_init_task_options ();
+      wtxapi_notify_connection_established ();
+    }
+  if (ex.reason < 0)
+    {
+      printf_filtered ("%s\n", ex.message);
+      unpush_target (&wtx_ops);
+    }
+}
+
+/* Implement the to_close method of the target_ops for the WTX protocol.  */
+
+static void
+wtx_close (int quitting)
+{
+  /* We don't need to remove all the breakpoints from the inferior,
+     because GDB is handling their insertion and removal for us.  */
+
+  if (!wtxapi_tool_detach ())
+    warning (_("error detected while detaching from target (ignored): %s"),
+             wtxapi_err_msg_get ());
+}
+
+/* Implement the to_close method when in "task" mode.  */
+
+static void
+wtx_task_mode_close (int quitting)
+{
+  /* Nothing to do.  */
+}
+
+/* Implement the to_close method when in "system" mode.  */
+
+static void
+wtx_system_mode_close (int quitting)
+{
+  /* Nothing to do.  */
+}
+
+/* Attach to the "system".  In other words, start debugging in
+   the target in system mode.  */
+
+static void
+wtx_system_mode_attach (int from_tty)
+{
+  if (from_tty)
+    printf_filtered (_("Entering system-mode.\n"));
+
+  if (!wtxapi_system_mode_support_p ())
+    error (_("system-mode not supported."));
+
+  if (!wtxapi_agent_mode_set (WTX_AGENT_MODE_EXTERN))
+    error (_("Failed to enter system mode: %s"), wtxapi_err_msg_get ());
+
+  if (!suspend_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID))
+    error (_("Failed to suspend system context: %s"), wtxapi_err_msg_get ());
+
+  push_target (&wtx_system_mode_ops);
+
+  inferior_ptid =
+    context_id_to_ptid (wtxapi_system_mode_get_current_context_id ());
+
+  /* If the user turned the multi-tasks-mode on, then warn the user that
+     we are turning it off now.  This mode doesn't make sense in system
+     mode.
+
+     We could silently ignore the multi-task-mode user setting, but
+     it makes the code clearer to turn it off, because it allows us
+     to determine that if we are in multi-tasks mode iff the user
+     setting is non-zero (without having to check which mode (task-mode
+     vs system-mode we are using).  */
+  if (multi_task_mode_is_on ())
+    {
+      warning (_("The multi-tasks mode can not be used in system mode.\n\
+multi-tasks mode turned off."));
+      turn_multi_task_mode_off ();
+    }
+}
+
+/* Register for exit notification for the given task ID.  */
+
+static int
+register_for_exit_event (int context_id)
+{
+  evtpt_id_t evtpt_id;
+
+  evtpt_id = wtxapi_context_exit_notify_add (WTX_CONTEXT_TASK, context_id);
+  if (evtpt_id == invalid_evtpt_id)
+    return 0;
+
+  attached_context_id = context_id;
+  return 1;
+}
+
+/* If NAME is the name of a task currently running on the system,
+   return its ID.  Return zero otherwize.  */
+
+static int
+wtx_context_id_from_name (char *name)
+{
+  struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+  struct wtxapi_thread_info *this_thread = threads;
+  int pid = 0;
+
+  while (this_thread != NULL)
+    {
+      if (strcmp (this_thread->name, name) == 0)
+        {
+          pid = this_thread->id;
+          break;
+        }
+      this_thread = this_thread->next;
+    }
+
+  free_wtxapi_thread_info (threads);
+  return pid;
+}
+
+/* Implement the "attach" method for the task mode.  */
+
+static void
+wtx_task_mode_attach (char *args, int from_tty)
+{
+  int context_id;
+
+  /* Check to see if the argument was a task name.  */
+
+  context_id = wtx_context_id_from_name (args);
+
+  /* If not a valid task name, then try parsing the argument as a task ID.  */
+  if (context_id == 0)
+    context_id = parse_and_eval_address (args);
+
+  if (from_tty)
+    printf_filtered (_("Attaching to task %#x.\n"), context_id);
+
+  if (multi_task_mode_is_on ())
+    {
+      start_multi_task_mode ();
+      if (!stop_all_ada_tasks (&inferior_status))
+        error (_("Failed to stop task: %s"), wtxapi_err_msg_get ());
+    }
+  else
+    {
+      if (!stop_inferior (WTX_CONTEXT_TASK, context_id))
+        error (_("Failed to stop task: %s"), wtxapi_err_msg_get ());
+    }
+
+  push_target (&wtx_task_mode_ops);
+
+  wtx_pd_switch_to_task_pd (context_id);
+  inferior_ptid = context_id_to_ptid (context_id);
+
+  if (multi_task_mode_is_on ())
+    {
+      /* In multi-tasks mode, we want to receive the EXIT event
+         of the MAIN task, which is not necessarily the task that
+         we attached too.  Otherwise, we'll end up reporting that
+         the program exited when in fact it was just one task that
+         exited.  So get the main task context ID.  */
+      struct ada_task_info *main_task = ada_get_environment_task ();
+
+      context_id = ptid_get_context_id (main_task->ptid);
+    }
+
+  if (!register_for_exit_event (context_id))
+    error (_("Failed to register exit event: %s"), wtxapi_err_msg_get ());
+
+}
+
+/* Implement the "to_attach" target_ops method.  */
+
+static void
+wtx_attach (struct target_ops *ops, char *args, int from_tty)
+{
+  dont_repeat ();
+
+  if (args == NULL || args[0] == '\0')
+    error_no_arg ("task-id | task-name | system");
+
+  if (strcasecmp (args, "system") == 0)
+    wtx_system_mode_attach (from_tty);
+  else
+    wtx_task_mode_attach (args, from_tty);
+  wtx_add_inferior (inferior_ptid, 1);
+
+  /* GDB is expecting that attaching to the inferior will cause
+     some event to be generated (most likely a SIGTRAP).  To purge
+     that event, GDB will soon call the to_wait target method.
+     Since VxWorks does not generate any event when suspending a task,
+     we need to inform our event loop that the next event-wait should
+     fake a SIGTRAP.
+
+     brobecker/2007-10-12: It is actually possible configure GDB
+     in a way that it will not expect that event, but it involves
+     the definition of the ATTACH_NO_WAIT macro, which we used
+     to do.  But doing so deactivates as a side-effect the printing
+     of the current frame that tells the user where his program is.
+     Given that this macro is currently not multiarched either,
+     and only used by one target, it seems clear that it's preferable
+     to just fake the expected event.  */
+  fake_attach_event = 1;
+}
+
+/* Implement the "detach" command for the tasks mode.  */
+
+static void
+wtx_task_mode_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  int success;
+  WTX_CONTEXT_ID_T context_id = ptid_get_context_id (inferior_ptid);
+
+  printf_filtered (_("Detaching from task 0x%lx...\n"), context_id);
+
+  /* Resume the execution of the inferior.  */
+  success = continue_inferior (WTX_CONTEXT_TASK, context_id);
+  if (!success)
+    warning (_("Failed to resume task: %s"), wtxapi_err_msg_get ());
+
+  if (multi_task_mode_is_on ())
+    stop_multi_task_mode ();
+
+  /* And last, revert back to the basic WTX mode.  */
+  pop_target ();
+}
+
+/* Implement the "detach" command for the system mode.  */
+
+static void
+wtx_system_mode_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  int success;
+
+  printf_filtered (_("Leaving system-mode...\n"));
+
+  /* switch back to task mode.  Does this resume the target execution? */
+  success = wtxapi_agent_mode_set (WTX_AGENT_MODE_TASK);
+  if (!success)
+    warning (_("Operation failed, target may still be in EXTERN mode: %s"),
+             wtxapi_err_msg_get ());
+
+  pop_target ();
+}
+
+/* Perform a step operation in the given CONTEXT_ID & CONTEXT_TYPE.
+
+   Whether a single-instruction step of a multi-instruction step
+   is to be done is decided based on the value of the STEP_RANGE_START
+   and STEP_RANGE_END global variables.  */
+
+static void
+step_inferior (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+  gdb_assert (tp);
+
+  if (tp->control.step_range_end
+      && (tp->control.step_range_end > tp->control.step_range_start))
+    wtxapi_context_step (context_type, context_id,
+			 tp->control.step_range_start,
+			 tp->control.step_range_end);
+  else  /* Step only exactly one instruction.  */
+    wtxapi_context_step (context_type, context_id, 0, 0);
+  inferior_status = WTX_TASK_STATUS_UNKNOWN;
+}
+
+/* Implement the "to_resume" command in task mode.  */
+
+static void
+wtx_task_mode_resume (struct target_ops *ops, ptid_t ptid, int step,
+                      enum target_signal signo)
+{
+  WTX_CONTEXT_ID_T context_id;
+  struct thread_info *tp;
+
+  /* Find the context_id of the task to resume.  If GDB wants to
+     step a specific task, then it will give us its ptid, but
+     otherwise, we should be resuming the inferior_ptid task.  */
+  context_id = ptid_get_context_id (ptid);
+  if (ptid_equal (ptid, minus_one_ptid))
+    {
+      context_id = ptid_get_context_id (inferior_ptid);
+      tp = find_thread_ptid (inferior_ptid);
+    }
+  else
+    {
+      tp = find_thread_ptid (ptid);
+    }
+
+  gdb_assert (tp);
+
+  if (signo != 0 && signo != tp->suspend.stop_signal)
+    error (_("The WTX protocol does not support sending signals"));
+
+  /* Clear any WTX error status before resuming.  */
+  wtxapi_err_clear ();
+
+  if (step)
+    step_inferior (WTX_CONTEXT_TASK, context_id);
+  else
+    continue_inferior (WTX_CONTEXT_TASK, context_id);
+
+  /* Make sure that all went well.  */
+
+  if (wtxapi_err_get () != WTX_ERR_NONE)
+    error (_("wtx_task_mode_resume (step = %d, context_id = 0x%lx) failed: %s"),
+           step, context_id, wtxapi_err_msg_get ());
+}
+
+/* Implement the "to_resume" command in system mode.  */
+
+static void
+wtx_system_mode_resume (struct target_ops *ops,
+                        ptid_t ptid, int step, enum target_signal signo)
+{
+  const int resume_all = ptid_equal (ptid, minus_one_ptid);
+  const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+  struct thread_info *tp = find_thread_ptid (ptid);
+
+  /* If GDB wants to step a specific task:
+     - its thread info should be valid (non null);
+     - it won't support sending signals to this specific task.
+
+     In the other case (resume whole system):
+     - the context_id will be 0;
+     - tp will not be valid.
+  */
+
+  gdb_assert (resume_all || tp);
+  if (!resume_all && signo != 0 && signo != tp->suspend.stop_signal)
+    error (_("The WTX protocol does not support sending signals"));
+
+  /* If the debugger asks for a specific task to be stepped, verify
+     that this task is in fact the current task.  In system mode,
+     only this task can be stepped  (or in other words, one cannot
+     switch to a different task, and step that task).  In practice,
+     this is not a real limitation, since other task are usually
+     executing system calls for which there is little need for next/step
+     operations.  */
+  if (step && !resume_all)
+    {
+      const WTX_CONTEXT_ID_T current_context_id =
+        wtxapi_system_mode_get_current_context_id ();
+
+      if (context_id != current_context_id)
+        error (_("task 0x%lx cannot be stepped"), context_id);
+    }
+
+  /* Clear any WTX error status before resuming.  */
+  wtxapi_err_clear ();
+
+  if (step)
+    step_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+  else
+    continue_inferior (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+
+  /* Make sure that all went well.  */
+
+  if (wtxapi_err_get () != WTX_ERR_NONE)
+    error (_("wtx_system_mode_resume (step = %d) failed: %s"),
+           step, wtxapi_err_msg_get ());
+}
+
+static int
+wtx_event_is_foreign (WTX_CONTEXT_ID_T context_id,
+                      WTX_CONTEXT_TYPE context_type)
+{
+  /* In system mode, we are debugging the entire system, and we should
+     handle all events.  */
+  if (system_mode_p ())
+    return 0;
+
+  /* In multi-tasks mode, we are debugging an application that has
+     more than one task, and we should logically only process the events
+     from one of these tasks.  But because we restrict the user to one
+     debugger per partition when debugging in multi-tasks mode, we can
+     simplify this condition by simply verifying that the event was
+     triggered by a task running in the current partition.  */
+  if (multi_task_mode_is_on ())
+    {
+      pd_id_t context_pd;
+      int success;
+
+      success = wtxapi_get_task_pd (context_id, &context_pd);
+      if (!success)
+        {
+          /* Probably a CTX_EXIT event from a task we're not watching,
+             maybe in a different partition.  Treat as a foreign event.  */
+          return 1;
+        }
+
+      return (context_pd != wtxapi_pd_current_get ());
+    }
+
+  /* In single-task mode, we should only handle events triggered
+     inside the task we are debugging.  */
+  return (context_id != ptid_get_context_id (inferior_ptid));
+}
+
+/* Interrupt handling during event polling.
+
+   There are two parts involved in interrupt-handling:
+
+     1. Stopping the inferior:
+        This part is performed by the SIGINT handler that we install
+        during the event queue polling loop.
+
+     2. Telling GDB that the inferior stopped:
+        This part is handled by the target "wait" routine.
+        On most systems, the interruption performed in (1)
+        will result in an associated event that can be processed.
+        However, this is not the case with WTX, and so we must
+        maintain a global variable that will be set in the SIGINT
+        handler to notify the event polling loop to stop polling,
+        and let the wait routine fake a SIGINT event.  */
+
+/* The old SIGINT handler that we save during the period when
+   it is replaced by our own SIGINT handler.  */
+static void (*ofunc) (int);
+
+/* Set to non-zero if the inferior has been interrupted while
+   we were polling for events.  */
+static int inferior_interrupted = 0;
+
+/* To be called when reiceiving a SIGINT signal while polling for
+   WTX events.  This routine interrupts the inferior, and sets
+   TARGET_WAIT_INTERRUPTED to 1.  */
+
+static void
+handle_target_wait_interrupt (int signo)
+{
+  target_stop (inferior_ptid);
+  inferior_interrupted = 1;
+}
+
+/* Setup the interrupt-handler to be used during the event polling
+   phase.  As soon as we receive an event, the old handler should
+   be restored.
+
+   INFERIOR_INTERRUPTED is also set to zero.  */
+
+static void
+set_target_wait_interrupt_handler (void)
+{
+  inferior_interrupted = 0;
+  ofunc = signal (SIGINT, handle_target_wait_interrupt);
+}
+
+/* Restore the old handler set by set_target_wait_interrupt_handler.  */
+
+static void
+unset_target_wait_interrupt_handler (void)
+{
+  signal (SIGINT, ofunc);
+}
+
+/* Poll the WTX event queue for event.
+
+   This routine is blocking until either:
+     1. We receive an event;
+     2. Or the user requested that the inferior be interrupted.
+
+   In the first case, a non-NULL wtxapi_event_desc object will
+   be returned. In the second case, NULL is returned.  */
+
+static struct wtxapi_event_desc *
+wtx_event_poll (void)
+{
+  /* An event that we fetched from the event queue the previous
+     time this function was called, but could not be processed
+     because the user interrupted the inferior at the same time.  */
+  static struct wtxapi_event_desc *postponed_event = NULL;
+  struct wtxapi_event_desc *event_desc;
+
+  /* If there is a postponed event, then return it now.  */
+  if (postponed_event != NULL)
+     {
+       event_desc = postponed_event;
+       postponed_event = NULL;
+       return event_desc;
+     }
+
+  set_target_wait_interrupt_handler ();
+
+  /* Wait either for the user to interrupt the inferior, or for
+     a WTX event.  */
+  while (1)
+    {
+      struct timeval timeout;
+
+      event_desc = wtxapi_event_get ();
+
+      if (inferior_interrupted || event_desc != NULL)
+        break;
+
+      /* There was no event available on the queue.  Wait 10ms before
+         each try to avoid eating up all the CPU.  Note that we cannot
+         use usleep because it's not portable.  */
+      timeout.tv_sec = 0;
+      timeout.tv_usec = 10000;
+      gdb_select (0, 0, 0, 0, &timeout);
+    }
+
+  unset_target_wait_interrupt_handler ();
+
+  if (inferior_interrupted && event_desc != NULL)
+    {
+      /* The user interrupted the inferior, but we also received
+         an event at the same time.  Store that event for later
+         processing, and pretend we haven't received it yet.  */
+      postponed_event = event_desc;
+      return NULL;
+    }
+
+  return event_desc;
+}
+
+/* Process a TEXT_ACCESS or DATA_ACCESS event triggered by the given
+   TASK_ID, and fill in STATUS appropriately. CONTEXT_ID and CONTEXT_TYPE
+   are the context id & type of the breakpoint that triggered this event.
+
+   This routines assumes that we have already verified that this event
+   was meant for us (ie is not a foreign event).
+
+   Return TASK_ID.  */
+
+static int
+handle_access_event (WTX_CONTEXT_ID_T task_id,
+                     WTX_CONTEXT_ID_T context_id,
+                     WTX_CONTEXT_TYPE context_type,
+                     struct target_waitstatus *status)
+{
+  /* Update the status.  */
+  status->kind = TARGET_WAITKIND_STOPPED;
+  status->value.sig = TARGET_SIGNAL_TRAP;
+
+  /* When in system-mode, chances are the task_id will be equal to
+     SYSTEM_CID, but this is not precise enough for our needs: We are
+     interested in telling the user which task actually triggered
+     the event.  This piece of information is actually not part of
+     the WTX event (at least not with VxWorks 5.5), but since the task
+     that triggered the event is necessarily the current task, we can
+     derive this piece of information from there.  */
+  if (context_type == WTX_CONTEXT_SYSTEM)
+    task_id = wtxapi_system_mode_get_current_context_id ();
+
+  /* Switch to the task that triggered that event.  */
+  inferior_ptid = context_id_to_ptid (task_id);
+
+  /* If the task is running in a different partition than the current one,
+     automatically switch to the task partition.  */
+  wtx_pd_switch_to_task_pd (task_id);
+
+  return task_id;
+}
+
+/* Process the given context-exit EVENT, and fill in STATUS appropriately.
+
+   If the event was not meant for us (the task that exited is not the
+   one we're attached to), then ignore the event and return -1.
+   Otherwise, return the ID of the task that exited.  */
+
+static int
+handle_ctx_exit_event (struct wtxapi_ctx_exit_event event,
+                       struct target_waitstatus *status)
+{
+  /* If the task that exited is not the main task of our application,
+     then ignore this event.  Otherwise, we end up reporting the completion
+     of our program prematurely.  */
+  if (event.context_id != attached_context_id)
+    return -1;
+
+  status->kind = TARGET_WAITKIND_EXITED;
+  status->value.integer = event.exit_code;
+
+  return event.context_id;
+}
+
+/* Process the given data-access (watchpoint) EVENT, and fill in
+   STATUS appropriately.
+
+   If the event was not meant for us, then ignore the event and
+   return -1.  */
+
+static int
+handle_data_access_event (struct wtxapi_data_access_event event,
+                          struct target_waitstatus *status)
+{
+  int pid;
+
+  if (wtx_event_is_foreign (event.context_id, event.context_type))
+    return -1;
+
+  /* Record the location that triggered the watchpoint.  */
+  watchpoint_data_address = event.data_addr;
+
+  return handle_access_event (event.task_id, event.context_id,
+                              event.context_type, status);
+}
+
+/* Process the given exception EVENT, and fill in STATUS appropriately.
+
+   If the event was not meant for us, then ignore the event and
+   return -1.  */
+
+static int
+handle_exception_event (struct wtxapi_exception_event event,
+                        struct target_waitstatus *status)
+{
+  WTX_CONTEXT_ID_T context_id = event.context_id;
+
+  if (wtx_event_is_foreign (event.context_id, event.context_type))
+    return -1;
+
+  /* Similarly to when we handle access events in system mode,
+     we need to find the ID of the current task, because the
+     context_id returned by the event is equal to SYSTEM_CID
+     which is not precise enough.  We want to tell the user
+     which task is currently executing and triggered that event.  */
+  if (context_id == SYSTEM_CID)
+    context_id = wtxapi_system_mode_get_current_context_id ();
+
+  /* FIXME: brobecker/2007-08-31: The current event handling in GDB
+     doesn't really include support for exceptions.  For now, treat
+     this as an "unknown signal".  Later on, we might want to improve
+     a bit, and in particular display the exception value to the user.  */
+  status->kind = TARGET_WAITKIND_STOPPED;
+  status->value.sig = TARGET_SIGNAL_UNKNOWN;
+
+  if (multi_task_mode_is_on ())
+    stop_all_ada_tasks (&inferior_status);
+
+  return context_id;
+}
+
+/* Process the given obj-loaded event by loading the symbols from
+   that new module.  */
+
+static void
+handle_obj_loaded_event (struct wtxapi_obj_loaded_event event)
+{
+  printf_filtered ("New module on %s\n", wtxapi_ts_name_get ());
+
+  read_module_symbols (event.module_id, 0);
+}
+
+/* Process the given obj-unloaded event by unloading the symbols from
+   that old module.  */
+
+static void
+handle_obj_unloaded_event (struct wtxapi_obj_unloaded_event event)
+{
+  printf_filtered ("Module unloaded on %s\n", wtxapi_ts_name_get ());
+
+  unload_module_symbols (event.module_filename, 0);
+}
+
+/* Process the given text-access (breakpoint) EVENT, and fill in
+   STATUS appropriately.
+
+   If the event was not meant for us, then ignore the event and
+   return -1.  */
+
+static int
+handle_text_access_event (struct wtxapi_text_access_event event,
+                          struct target_waitstatus *status)
+{
+  if (wtx_event_is_foreign (event.context_id, event.context_type))
+    return -1;
+
+  return handle_access_event (event.task_id, event.context_id,
+                              event.context_type, status);
+}
+
+/* Process the given vio-write event.  */
+
+static void
+handle_vio_write_event (struct wtxapi_vio_write_event event)
+{
+  /* The DATA field in EVENT contains the output that our inferior
+     just printed, so simply relay it to our standard output.  */
+  printf_unfiltered ("%s", event.data);
+}
+
+/* Implement the "to_wait" target_ops method.  */
+
+static ptid_t
+wtx_wait (struct target_ops *ops, ptid_t ptid,
+	  struct target_waitstatus *status, int options)
+{
+  int event_context_id = -1;
+  int done_waiting = 0;
+
+  /* Reset the WATCHPOINT_DATA_ADDRESS value.  Now that the system
+     has been restarted, the watchpoint address is no longer relevant.  */
+  watchpoint_data_address = 0;
+
+  if (fake_attach_event)
+    {
+      fake_attach_event = 0;
+      status->kind = TARGET_WAITKIND_STOPPED;
+      status->value.sig = TARGET_SIGNAL_TRAP;
+
+      return inferior_ptid;
+    }
+
+  while (!done_waiting)
+    {
+      struct wtxapi_event_desc *event_desc = wtx_event_poll ();
+
+      if (event_desc == NULL)
+        {
+          /* The event polling was interrupted on user request. The target
+             has already been stopped by the interrupt handler, but there
+             will be no associated WTX event.  So we just fake a SIGINT
+             event received from the current inferior.  */
+          status->kind = TARGET_WAITKIND_STOPPED;
+          status->value.sig = TARGET_SIGNAL_INT;
+
+          return inferior_ptid;
+        }
+
+      switch (event_desc->event_type)
+        {
+          case WTX_EVENT_CTX_EXIT:
+            event_context_id =
+              handle_ctx_exit_event (event_desc->desc.ctx_exit, status);
+            if (event_context_id != -1)
+              done_waiting = 1;
+            break;
+
+          case WTX_EVENT_DATA_ACCESS:
+            event_context_id =
+              handle_data_access_event (event_desc->desc.data_access, status);
+            if (event_context_id != -1)
+              done_waiting = 1;
+            inferior_status = WTX_TASK_STATUS_STOPPED;
+            break;
+
+          case WTX_EVENT_EXCEPTION:
+            event_context_id =
+              handle_exception_event (event_desc->desc.exception, status);
+            if (event_context_id != -1)
+              done_waiting = 1;
+            inferior_status = WTX_TASK_STATUS_STOPPED;
+            break;
+
+          case WTX_EVENT_OBJ_LOADED:
+            handle_obj_loaded_event (event_desc->desc.obj_loaded);
+            break;
+
+          case WTX_EVENT_OBJ_UNLOADED:
+            handle_obj_unloaded_event (event_desc->desc.obj_unloaded);
+            break;
+
+          case WTX_EVENT_TEXT_ACCESS:
+            event_context_id =
+              handle_text_access_event (event_desc->desc.text_access, status);
+            if (event_context_id != -1)
+              done_waiting = 1;
+            inferior_status = WTX_TASK_STATUS_STOPPED;
+            break;
+
+          case WTX_EVENT_VIO_WRITE:
+            handle_vio_write_event (event_desc->desc.vio_write);
+            break;
+        }
+
+      free_wtxapi_event_desc (event_desc);
+    }
+
+  wtx_add_thread (context_id_to_ptid (event_context_id));
+  return context_id_to_ptid (event_context_id);
+}
+
+/* Fetch the value of one register identified by its register number.  */
+
+static void
+wtx_fetch_one_register (struct regcache *regcache,
+                        WTX_CONTEXT_TYPE context_type,
+                        WTX_CONTEXT_ID_T context_id,
+                        int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int result;
+  gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+
+  gdb_assert (regnum != -1);
+
+  memset (buf, 0, sizeof (buf));
+  result = wtx_hw_fetch_register (gdbarch, context_type, context_id,
+				  regnum, buf);
+  if (result)
+    regcache_raw_supply (regcache, regnum, buf);
+}
+
+/* Fetch the register REGNUM using the given CONTEXT_TYPE and CONTEXT_ID.  */
+
+static
+void
+wtx_fetch_registers (struct regcache *regcache,
+                     WTX_CONTEXT_TYPE context_type,
+                     WTX_CONTEXT_ID_T context_id,
+                     int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int total_regs = gdbarch_num_regs (gdbarch)
+    + gdbarch_num_pseudo_regs (gdbarch);
+  int i;
+
+  if (regnum != -1)
+    wtx_fetch_one_register (regcache, context_type, context_id, regnum);
+  else
+    for (i = 0; i < total_regs; i++)
+      wtx_fetch_one_register (regcache, context_type, context_id, i);
+}
+
+/* Implement the "to_fetch_registers" target_ops method when
+   in "task" or "multi-tasks" mode.  */
+
+static void
+wtx_task_mode_fetch_registers (struct target_ops *ops,
+                               struct regcache *regcache, int regnum)
+{
+  wtx_fetch_registers (regcache, WTX_CONTEXT_TASK,
+                       ptid_get_context_id (inferior_ptid),
+                       regnum);
+}
+
+/* Fetch the value of one register identified by its register number.
+
+   REGS_ADDR should be set to the address where the general purpose
+   registers are stored for the task we want to read the registers from.
+
+   FP_REGS_ADDR should be set to the address where the FP registers
+   are stored for the task we want to read the registers from.
+   If this address cannot be computed, then it should be set to zero.
+
+   This function should be called only when currently in system mode.  */
+
+static void
+wtx_system_mode_fetch_one_register_in_memory (struct regcache *regcache,
+                                              CORE_ADDR regs_addr,
+                                              CORE_ADDR fp_regs_addr,
+                                              int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int result;
+  gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+  memset (buf, 0, sizeof (buf));
+
+  gdb_assert (regnum != -1);
+
+  result = wtx_hw_fetch_register_in_memory (gdbarch,
+                                            WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+                                            regs_addr, fp_regs_addr, regnum,
+                                            buf);
+  if (result)
+    regcache_raw_supply (regcache, regnum, buf);
+}
+
+/* For the given context ID:
+     - Set REGS_ADDR to the address where the GP registers are stored.
+     - Set FP_REGS_ADDR to the address where the FP registers are stored.
+
+   Any address that could not be computed is set to zero.  */
+
+static void
+wtx_get_context_register_block_addrs (WTX_CONTEXT_ID_T context_id,
+                                      CORE_ADDR *regs_addr,
+                                      CORE_ADDR *fp_regs_addr)
+{
+  struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+  struct wtxapi_thread_info *this_thread = threads;
+
+  *regs_addr = 0;
+  *fp_regs_addr = 0;
+
+  while (this_thread != NULL)
+    {
+      if (this_thread->id == context_id)
+        {
+          *regs_addr = this_thread->regs_addr;
+          *fp_regs_addr = this_thread->fp_regs_addr;
+          break;
+        }
+      this_thread = this_thread->next;
+    }
+
+  free_wtxapi_thread_info (threads);
+}
+
+/* Implement the "to_fetch_registers" target_ops method when
+   in "system" mode.  */
+
+static void
+wtx_system_mode_fetch_registers (struct target_ops *ops,
+                                 struct regcache *regcache, int regnum)
+{
+  const WTX_CONTEXT_ID_T current_system_context_id =
+    wtxapi_system_mode_get_current_context_id ();
+  const WTX_CONTEXT_ID_T gdb_context_id = ptid_get_context_id (inferior_ptid);
+  CORE_ADDR regs_addr;
+  CORE_ADDR fp_regs_addr;
+
+  if (gdb_context_id == SYSTEM_CID
+      || gdb_context_id == current_system_context_id)
+    {
+      wtx_fetch_registers (regcache, WTX_CONTEXT_SYSTEM, SYSTEM_CID, regnum);
+      return;
+    }
+
+  wtx_get_context_register_block_addrs (gdb_context_id, &regs_addr,
+                                        &fp_regs_addr);
+  gdb_assert (regs_addr != 0); /* But fp_regs_addr may be null.  */
+
+  if (regnum != -1)
+    wtx_system_mode_fetch_one_register_in_memory (regcache, regs_addr,
+                                                  fp_regs_addr, regnum);
+  else
+    {
+      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      const int total_regs = gdbarch_num_regs (gdbarch)
+        + gdbarch_num_pseudo_regs (gdbarch);
+      int i;
+
+      /* Implementation note: The code below causes a succession
+         of small memory reads, which we could optimize by making
+         one large memory read right from the start, and then fetching
+         the register from there.  However, this complicates a bit
+         the implementation, so we'll stick to this simple approach
+         for now.  The delay should not be noticeable.  */
+      for (i = 0; i < total_regs; i++)
+        wtx_system_mode_fetch_one_register_in_memory (regcache, regs_addr,
+                                                      fp_regs_addr, i);
+    }
+}
+
+/* Store the value of one register identified by its register number.  */
+
+static void
+wtx_store_one_register (struct regcache *regcache,
+                        WTX_CONTEXT_TYPE context_type,
+                        WTX_CONTEXT_ID_T context_id,
+                        int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+
+  regcache_raw_collect (regcache, regnum, buf);
+  wtx_hw_store_register (gdbarch, context_type, context_id, regnum, buf);
+}
+
+static void
+wtx_store_registers (struct regcache *regcache,
+                     WTX_CONTEXT_TYPE context_type,
+                     WTX_CONTEXT_ID_T context_id,
+                     int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int total_regs = gdbarch_num_regs (gdbarch)
+    + gdbarch_num_pseudo_regs (gdbarch);
+  int i;
+
+  if (regnum != -1)
+    wtx_store_one_register (regcache, context_type, context_id, regnum);
+  else
+    for (i = 0; i < total_regs; i++)
+      wtx_store_one_register (regcache, context_type, context_id, i);
+}
+
+/* Implement the "to_store_registers" target_ops method when
+   in "task" or "multi-tasks" mode.  */
+
+static void
+wtx_task_mode_store_registers (struct target_ops *ops,
+                               struct regcache *regcache, int regnum)
+{
+  wtx_store_registers (regcache, WTX_CONTEXT_TASK,
+                       ptid_get_context_id (inferior_ptid),
+                       regnum);
+}
+
+/* Store the value of one register identified by its register number.
+
+   REGS_ADDR should be set to the address where the general purpose
+   registers are stored for the task whose register is being written.
+
+   FP_REGS_ADDR should be set to the address where the FP registers
+   are stored for the task we want to read the registers from.
+   If this address cannot be computed, then it should be set to zero.
+
+   This function should be called only when currently in system mode.  */
+
+static void
+wtx_system_mode_store_one_register_in_memory (struct regcache *regcache,
+                                              CORE_ADDR regs_addr,
+                                              CORE_ADDR fp_regs_addr,
+                                              int regnum)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  gdb_byte *buf = alloca (register_size (gdbarch, regnum));
+  memset (buf, 0, sizeof (buf));
+
+  regcache_raw_collect (regcache, regnum, buf);
+  wtx_hw_store_register_in_memory (gdbarch, WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+                                   regs_addr, fp_regs_addr, regnum, buf);
+}
+
+/* Implement the "to_store_registers" target_ops method when
+   in "system" mode.  */
+
+static void
+wtx_system_mode_store_registers (struct target_ops *ops,
+                                 struct regcache *regcache, int regnum)
+{
+  const WTX_CONTEXT_ID_T current_system_context_id =
+    wtxapi_system_mode_get_current_context_id ();
+  const WTX_CONTEXT_ID_T gdb_context_id = ptid_get_context_id (inferior_ptid);
+  CORE_ADDR regs_addr;
+  CORE_ADDR fp_regs_addr;
+
+  if (gdb_context_id == SYSTEM_CID
+      || gdb_context_id == current_system_context_id)
+    {
+      wtx_store_registers (regcache, WTX_CONTEXT_SYSTEM, SYSTEM_CID, regnum);
+      return;
+    }
+
+  wtx_get_context_register_block_addrs (gdb_context_id, &regs_addr,
+                                        &fp_regs_addr);
+  gdb_assert (regs_addr != 0); /* But fp_regs_addr may be null.  */
+
+  if (regnum != -1)
+    wtx_system_mode_store_one_register_in_memory (regcache, regs_addr,
+                                                  fp_regs_addr, regnum);
+  else
+    {
+      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      const int total_regs = gdbarch_num_regs (gdbarch)
+        + gdbarch_num_pseudo_regs (gdbarch);
+      int i;
+
+      /* Implementation note: The code below causes a succession
+         of small memory reads, which we could optimize by making
+         one large memory read right from the start, and then fetching
+         the register from there.  However, this complicates a bit
+         the implementation, so we'll stick to this simple approach
+         for now.  The delay should not be noticeable.  */
+      for (i = 0; i < total_regs; i++)
+        wtx_system_mode_store_one_register_in_memory (regcache, regs_addr,
+                                                      fp_regs_addr, i);
+    }
+}
+
+/* Implement the "to_store" method in the target_ops vector.  */
+
+static void
+wtx_prepare_to_store (struct regcache *regcache)
+{
+  /* Nothing to do.  */
+}
+
+/* If there are any undefined symbols referenced by MODULE_INFO,
+   then print a list containing the name of all such symbols.  */
+
+static void
+print_undefined_symbols (struct wtxapi_module_info *module_info)
+{
+  if (module_info->undef_list == NULL)
+    return;
+  if (! go_to_first_element_in_wtxapi_sym_list (module_info->undef_list))
+    return;
+
+  printf_filtered ("\n");
+  warning (_("the module contains some undefined symbols:"));
+  do
+    {
+      char *symbol_name = current_wtxapi_symbol_name (module_info->undef_list);
+      printf_filtered ("\t%s\n", symbol_name);
+      xfree (symbol_name);
+    }
+  while (go_to_next_element_in_wtxapi_sym_list (module_info->undef_list));
+}
+
+/* Kill the task with the given CONTEXT_ID.
+   Print a warning if not successful.  */
+
+static void
+wtx_kill_one_task (WTX_CONTEXT_ID_T context_id)
+{
+  const int success = wtxapi_context_kill (WTX_CONTEXT_TASK, context_id);
+
+  /* Print a warning if we failed to kill the target task.
+     Do not throw an error as the failure is not serious enough
+     that we would abort the kill command, especially since one
+     possible reason for failing is if someone alreayd killed
+     the task using a different tool.  We just warn the user
+     and proceed as if the killing was successful.  The user
+     can then investigate more precisely why it failed and
+     try killing his task manually if necessary.  */
+  if (!success)
+    warning (_("Failed to kill task 0x%lx: %s"), context_id,
+             wtxapi_err_msg_get ());
+}
+
+/* Kill the task identified by TASK.  */
+
+static void
+wtx_kill_one_task_from_task_info (struct ada_task_info *task)
+{
+  wtx_kill_one_task (ptid_get_context_id (task->ptid));
+}
+
+/* Assuming we are currently debugging in multi-tasks mode,
+   kill all the tasks of inferior.  */
+
+static void
+wtx_kill_all_tasks (void)
+{
+  iterate_over_live_ada_tasks (wtx_kill_one_task_from_task_info);
+}
+
+/* Wait for the CTX_EXIT event which is generated by the target server
+   when we kill the inferior.  Discard any other events that may be
+   received while waiting for that event.
+
+   The user pressing ctrl-c while waiting for that event will cause
+   the wait to be aborted.  Normally, we expect the exit event to be
+   received immediately after the kill, so the user should never really
+   have the time to do that before the event is received.  But in case
+   the event is not received for any reason, this allows the user to get
+   out of what could be an infinite loop.  */
+
+static void
+wtx_wait_for_exit_event (void)
+{
+  while (1)
+    {
+      struct wtxapi_event_desc *event_desc = wtx_event_poll ();
+
+      if (event_desc == NULL)  /* The user pressed Ctrl-c.  */
+        return;
+
+      if (event_desc->event_type == WTX_EVENT_CTX_EXIT
+          && event_desc->desc.ctx_exit.context_id == attached_context_id)
+	{
+	  free_wtxapi_event_desc (event_desc);
+          return;
+	}
+
+      /* Not the event we were waiting for.  Free it, and wait for
+         the next event.  */
+      free_wtxapi_event_desc (event_desc);
+    }
+}
+
+/* Implement the "to_kill" method in task mode.  */
+
+static void
+wtx_task_mode_kill (struct target_ops *ops)
+{
+  if (ptid_equal (inferior_ptid, null_ptid))
+    {
+      /* brobecker/2007-09-28:  Can this really ever happen?
+         Add a warning to help us catch any situation where this
+         actually does happen, and only then return.  Eventually,
+         if we find that this has never happened after a few years,
+         then we can remove this entire block.  */
+      warning (_("cannot find id of task to kill"));
+      return;
+    }
+
+  if (multi_task_mode_is_on ())
+    wtx_kill_all_tasks ();
+  else
+    wtx_kill_one_task (ptid_get_context_id (inferior_ptid));
+  wtx_wait_for_exit_event ();
+
+  target_mourn_inferior ();
+}
+
+/* Implement the "to_kill" target_ops method when in "system" mode.
+   In pratice, one cannot kill the system task without rebooting it.
+   WindRiver chose to provide that command as a way of rebooting the
+   target, but it really is not that useful.  There are enough ways
+   to reboot a system already, no need to provide one extra in GDB.
+   So just tell the user that he cannot "kill" the system.  */
+
+static void
+wtx_system_mode_kill (struct target_ops *ops)
+{
+  error (_("The system cannot be killed."));
+}
+
+/* Find a given module using its name and unload it from the target.
+   The module lookup uses the module basename to do the matching.  */
+
+static void
+unload_module_from_target (const char *module_name)
+{
+  const char *base_name = lbasename (module_name);
+  const module_id_t module_id = wtxapi_obj_module_in_system_find_id (base_name);
+  pd_id_t module_pd;
+
+  if (module_id == invalid_module_id)
+    {
+      warning (_("could not find %s on target: %s"),
+               base_name, wtxapi_err_msg_get ());
+      return;
+    }
+
+  module_pd = wtx_pd_get_module_pd (module_id);
+
+  /* unload the object module */
+  if (!wtxapi_obj_module_unload (module_pd, module_id))
+      warning (_("could not unload %s from target: %s"),
+               base_name, wtxapi_err_msg_get ());
+
+}
+
+/* Implement the "unload" target_ops method.  */
+
+static void
+wtx_unload (char *args, int from_tty)
+{
+  char **argv = buildargv (args);
+
+  if (argv == NULL)
+    error_no_arg (_("module name to unload"));
+  make_cleanup_freeargv (argv);
+
+  unload_module_from_target (argv[0]);
+  unload_module_symbols (argv[0], from_tty);
+}
+
+/* Implement the "load" command for the WTX protocol.
+
+   The usage is as follow:
+
+       load <filename> [remote filename]
+
+   where <filename> is the name of the module as seen from GDB,
+   and [remote filename] should the full path of module given to
+   the target server.  */
+
+static void
+wtx_load (char *args, int from_tty)
+{
+  char **argv = buildargv (args);
+  char *module_name = NULL;
+  /*  The name/path of the module as seen from the debugger.  */
+  char *gdb_path = NULL;
+  /*  Same thing but for the target server.  */
+  char *target_server_path = NULL;
+  int fd;
+  struct wtxapi_module_info *module_info;
+  struct objfile *objfile;
+
+  dont_repeat ();
+
+  if (argv == NULL)
+    error_no_arg (_("filename of module to load"));
+  make_cleanup_freeargv (argv);
+
+  /* Process the command line arguments, and issue a warning
+     if too many arguments were specified (we know at this point
+     that there is at least one argument so we cannot have too few
+     arguments).  */
+
+  module_name = argv[0];
+  target_server_path = argv[1];
+  if (argv[1] != NULL && argv[2] != NULL)
+    warning
+      (_("too many arguments (\"%s ...\"), extra arguments will be ignored"),
+       argv[2]);
+
+  /* Resolve the module_name into a full path name.  This allows us
+     to make sure we can open this file for reading (to load the
+     symbols), but more importantly, this allows us to pass an absolute
+     path to the target server if the remote filename was not provided
+     in the load command (TARGET_SERVER_PATH is NULL).  */
+
+  fd = openp (source_path, OPF_TRY_CWD_FIRST, module_name,
+	      O_RDONLY, &gdb_path);
+  if (fd < 0)
+    error (_("Unable to load %s: %s"), module_name, strerror (errno));
+  close (fd);
+
+  /* If the optional [remote filename] argument was not specified, then
+     use the full path of the module name.  */
+
+  if (target_server_path == NULL)
+    target_server_path = gdb_path;
+
+  /* Scan the list of object files; if the module has already been
+     loaded, then unload it first. At reload, the symbols loaded
+     previously are discarded by the target server; so they should
+     also be discarded by the debugger.  */
+
+  for (objfile = object_files; objfile; objfile = objfile->next)
+    {
+      if (strcmp (objfile->name, gdb_path) == 0)
+         {
+           wtx_unload (module_name, 0);
+           break;
+         }
+    }
+
+  /* Temporarily switch the timeout value to the load timeout, and
+     attempt to load the module on the board.  Error out if we failed.  */
+
+  printf_filtered (_("Downloading %s..."), target_server_path);
+  module_info = wtxapi_module_load (wtxapi_pd_current_get (),
+                                    target_server_path,
+                                    WTX_LOAD_GLOBAL_SYMBOLS,
+                                    wtx_opt_load_timeout () * 1000);
+  if (module_info == NULL)
+    error (_("Failed to load module %s: %s"), target_server_path,
+           wtxapi_err_msg_get ());
+
+  print_undefined_symbols (module_info);
+  printf_filtered (_("done.\n"));
+
+  /* Load the module symbols.  */
+  read_module_symbols_1 (module_info, gdb_path);
+  ada_language_auto_set ();
+}
+
+/* Implement the "to_create_inferior" target method.
+
+   Implementation Note:
+   We must get the address of the entry point directly from the target,
+   as opposed to from doing a local symbol lookups. This is necessary
+   in order to avoid using an incorrect address if some of our objfiles
+   are not up to date. This can happen for instance in the following
+   scenario:
+     - from GDB: load [app], run [app]
+     - from Windsh: unld "[app]", ld < [app]
+     - from GDB: run [app]
+   What would happen is that GDB would use an obsolete objfile (the
+   one from the previous run) to compute the address of the entry
+   point, thus leading to catastrophic errors...
+
+   Note that it is technically possible to load 2 modules on
+   the target with the same entry point. In this case, it is not
+   clear what would happen. Right now, we just assume that this
+   not going to happen. Anybody trying to do that would just be
+   shooting themselves in the foot...  */
+
+static void
+wtx_create_inferior (struct target_ops *ops, char *exec_file, char *args,
+                     char **env, int from_tty)
+{
+  struct wtxapi_context_desc context_desc;
+  struct cleanup *old_chain = NULL;
+  char **argv;
+  char *entry_name;
+  int new_context_id;
+
+  memset (&context_desc, 0, sizeof (context_desc));
+
+  /* Parse the arguments.  */
+
+  argv = buildargv (args);
+  if (argv == NULL)
+    nomem (0);
+  old_chain = make_cleanup_freeargv (argv);
+
+  /* Get the name of the entry point, which is the first argument.  */
+  entry_name = argv[0];
+  if (entry_name == NULL || entry_name[0] == '\0')
+    error_no_arg (_("module entry point"));
+
+  /* FIXME: brobecker/2007-09-26: Add handling of the arguments...
+     For now, pretend there are none.  */
+
+  TASK_CONTEXT_ARGC (context_desc) = 0;
+
+  /* Now, fill-in the rest of the CONTEXT_DESC structure.  */
+  remote_wtxapi_get_symbol_address
+    (entry_name, &TASK_CONTEXT_ENTRY (context_desc));
+  if (TASK_CONTEXT_ENTRY (context_desc) == 0)
+    error (_("Failed to lookup entry point address"));
+  TASK_CONTEXT_NAME (context_desc) = "tDbgTask";
+  TASK_CONTEXT_CONTEXT_TYPE (context_desc) = WTX_CONTEXT_TASK;
+  TASK_CONTEXT_STACK_SIZE (context_desc) = wtx_opt_stack_size ();
+  TASK_CONTEXT_OPTIONS (context_desc) = wtx_opt_task_options ();
+  TASK_CONTEXT_PRIORITY (context_desc) = wtx_opt_task_priority ();
+  TASK_CONTEXT_PDID (context_desc) = wtxapi_pd_current_get ();
+
+  /* New create a new context for that task.  */
+  new_context_id = wtxapi_context_create (&context_desc);
+  if (new_context_id == invalid_context_id)
+    error (_("failed to create task: %s"), wtxapi_err_msg_get ());
+
+  /* FIXME: brobecker/2007-09-26: Perform I/O redirection if necessary.  */
+
+  inferior_ptid = context_id_to_ptid (new_context_id);
+  wtx_add_inferior (inferior_ptid, 0);
+  push_target (&wtx_task_mode_ops);
+  register_for_exit_event (new_context_id);
+  inferior_status = WTX_TASK_STATUS_CREATED;
+
+  if (multi_task_mode_is_on ())
+    start_multi_task_mode ();
+
+  /* Do our cleanup actions.  */
+  do_cleanups (old_chain);
+}
+
+/* Return True if a module which name is equal to module_name is loaded
+   on the target.  */
+
+static int
+module_is_loaded_on_target (const char *module_name)
+{
+  module_id_t module_id;
+
+  module_id = wtxapi_obj_module_in_system_find_id (module_name);
+  return (module_id != invalid_module_id);
+}
+
+/* Search the global object_files list for the objfile associated to
+   the module whose name is BASE_NAME, and free it.  */
+
+static void
+free_module_objfile (const char *base_name)
+{
+  struct objfile *objfile;
+  struct objfile *temp;
+
+  /* search for matching objfile known to gdb, and free it */
+  ALL_OBJFILES_SAFE (objfile, temp)
+    {
+      if (!strcmp (lbasename (objfile->name), base_name))
+        {
+          const pd_id_t current_pd = wtxapi_pd_current_get ();
+
+          /* Print a message notifying the user that a module has been
+             unloaded.  If the target also supports PDs, then print the
+             PD id and name from which the module has been unloaded.  */
+          if (wtx_pd_system_has_pds ())
+            {
+              char *current_pd_name = wtx_pd_get_pd_name (current_pd);
+              printf_filtered (_("Unloaded file %s from PD 0x%lx (%s)\n"),
+                               objfile->name, current_pd, current_pd_name);
+              xfree (current_pd_name);
+            }
+          else
+            printf_filtered (_("Unloaded file %s\n"), objfile->name);
+          free_objfile (objfile);
+          return;
+        }
+    }
+  warning (_("could not unload module %s from gdb"), base_name);
+}
+
+/* Given the name of a module that has just been unloaded from
+   the target, discard all the symbols associated to that module.  */
+
+static void
+unload_module_symbols (const char *module_filename, int from_tty)
+{
+  const pd_id_t current_pd = wtxapi_pd_current_get ();
+  const char *module_name = lbasename (module_filename);
+  pd_id_t module_pd;
+  struct cleanup *old_chain = NULL;
+
+  /* Ignore this request if it comes from a WTX_EVENT_OBJ_UNLOADED event
+     and the object has been loaded back on the target.  We can safely
+     ignore this event because either:
+       - it has been reloaded from this debugger, in which case we
+         already updated our objfile
+       - it has been reloaded from another client (eg windsh), in
+         which case there is necessarily a pending WTX_EVENT_OBJ_LOADED
+         event, which will cause us to update our objfile.
+
+     Not ignoring this event is causing the debugger to destroy the
+     associated objfile in scenarios like this:
+       1/ from GDB: load <program>
+          (causes GDB to create an associated objfile)
+       2/ from Windsh: unload <program>
+       3/ from GDB: load <program>
+          (GDB correctly recreates the associated objfile)
+       4/ from GDB: run <program>
+          (causes GDB to process the "unloaded" wtx event from step 2,
+          hence destroying our good objfile)  */
+  if (!from_tty && module_is_loaded_on_target (module_name))
+    return;
+
+  module_pd = wtx_pd_get_unloaded_module_pd (module_name);
+  if (module_pd != current_pd)
+    {
+      wtx_pd_switch_to_pd (module_pd);
+      old_chain = wtx_pd_make_switch_to_pd_cleanup (current_pd);
+    }
+
+  free_module_objfile (module_name);
+
+  if (old_chain != NULL)
+    do_cleanups (old_chain);
+}
+
+/* Implements the to_xfer_partial method of the target vector for WTX.  */
+
+static LONGEST
+wtx_xfer_partial (struct target_ops *ops, enum target_object object,
+                  const char *annex, gdb_byte *readbuf,
+                  const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+  int status;
+
+  /* The only object transfer that we support at this moment is memory
+     read/write.  */
+  if (object != TARGET_OBJECT_MEMORY)
+    return -1;
+
+  if (readbuf != NULL)
+    status = wtxapi_mem_read (wtxapi_pd_current_get (), offset, readbuf, len);
+  else
+    status = wtxapi_mem_write (wtxapi_pd_current_get (),
+                               (gdb_byte *) writebuf, offset, len);
+
+  if (status == WTX_ERROR)
+    return 0;  /* No further transfer possible???  */
+
+  return status;
+}
+
+/* Handling of eventpoint IDs.
+
+   VxWorks provides eventpoints for breakpoints and watchpoints.  For each
+   inserted breakpoint, the target server returns an evenpoint ID that we
+   need to record, because we will need this ID to remove this breakpoint.
+
+   We store that information as private data in the breakpoint location's
+   target_info.  */
+
+struct eventpoint_info
+{
+  evtpt_id_t id;
+  WTX_CONTEXT_TYPE context_type;
+  WTX_CONTEXT_ID_T context_id;
+};
+
+/* Insert a breakpoint eventpoint using the information provided.
+   Return a non-zero errno code when the insertion failed.  */
+
+static int
+wtx_insert_breakpoint_1 (struct bp_target_info *target_info,
+                         WTX_CONTEXT_TYPE context_type,
+                         WTX_CONTEXT_ID_T context_id,
+                         WTX_ACTION_TYPE action_type,
+			 CORE_ADDR call_rtn)
+{
+  struct wtxapi_evtpt evtpt;
+  evtpt_id_t evtpt_id;
+  struct eventpoint_info *evtpt_info;
+
+  evtpt.context.context_type = context_type;
+  evtpt.context.context_id = context_id;
+  evtpt.action.action_type = action_type | WTX_ACTION_NOTIFY;
+  evtpt.event.event_type = WTX_EVENT_TEXT_ACCESS;
+  evtpt.event.num_args = 1;
+  evtpt.event.args = &target_info->placed_address;
+
+  if (call_rtn != 0)
+    evtpt.action.call_rtn = call_rtn;
+
+  evtpt_id = wtxapi_eventpoint_add (&evtpt);
+  wtx_opt_breakpoints_debug
+    ("wtxapi_eventpoint_add (context_type = 0x%x, context_id = 0x%lx, "
+     "address = 0x%s, target_info = 0x%s) -> 0x%x",
+     context_type, context_id,
+     paddress (target_gdbarch, target_info->placed_address),
+     host_address_to_string (target_info), evtpt_id);
+
+  if (evtpt_id == invalid_evtpt_id)
+    return ENOMEM;
+
+  /* Save the eventpoint info.  We will use it later when removing
+     the breakpoint.  */
+  evtpt_info = xmalloc (sizeof (struct eventpoint_info));
+  evtpt_info->id = evtpt_id;
+  evtpt_info->context_type = context_type;
+  evtpt_info->context_id = context_id;
+  target_info->target_private_data = evtpt_info;
+
+  return 0;
+}
+
+/* Insert a task-mode breakpoint at the location specified by
+   TARGET_INFO.  TARGET_INFO is also used to determine what
+   scope & action to use for that breakpoint.
+
+   Return a non-zero errno code if the insertion failed.  */
+
+static int
+wtx_insert_task_mode_breakpoint (struct gdbarch *gdbarch,
+				 struct bp_target_info *target_info)
+{
+  const enum wtx_breakpoint_action action = get_break_command_action ();
+  WTX_CONTEXT_TYPE context_type;
+  WTX_CONTEXT_ID_T context_id;
+  WTX_ACTION_TYPE action_type;
+  CORE_ADDR call_rtn = 0;
+
+  context_type = WTX_CONTEXT_TASK;
+  context_id = ptid_get_context_id (inferior_ptid);
+
+  switch (action)
+    {
+      case action_task:
+        action_type = WTX_ACTION_STOP;
+        break;
+
+      case action_all:
+        action_type = WTX_ACTION_STOP_ALL;
+        break;
+
+      case action_call:
+        context_type = WTX_CONTEXT_ANY_TASK;
+        action_type =  WTX_ACTION_STOP;
+        call_rtn = get_break_command_call_rtn ();
+	if (call_rtn != 0)
+	  action_type |= WTX_ACTION_CALL;
+        break;
+
+      default:
+        internal_error (__FILE__, __LINE__, _("Unexpected action value: %d"),
+                        action);
+    }
+
+  return wtx_insert_breakpoint_1 (target_info, context_type, context_id,
+                                  action_type, call_rtn);
+}
+
+/* On systems that support partitions, we need to record for each
+   breakpoint/watchpoint the associated partition ID, which is
+   the current partition at the time when the breakpoint was
+   created.
+
+   We achieve this by using a couple of observers, one that will
+   notify us of the creation of new breakpoints, and one that will
+   notify us of their destruction.  They allow us to maintain a list
+   of (breakpoint, pd) elements that will later allow us to find
+   the PD associated to any breakpoint.
+
+   This is actually only useful when debugging in system mode, because
+   this is the only mode where we debug more than one partition.
+   However, we still need to maintain this information at all times,
+   because the user may be setting breakpoints while not in system
+   mode.
+
+   brobecker/2007-10-26:  This is probably not going to be the final
+   implementation for this concept.  For instance, it would probably
+   be nicer to have this information embedded directly inside the
+   breakpoint structure as a target-dependent data (like we do in
+   the gdbarch for instance), and avoid the need for the parallel
+   list that we're maintaining.
+
+   brobecker/2007-10-26: Another issue: When we insert breakpoints,
+   we are given a pointer to the target_info data. Not the bp_location.
+   Having the bp_location would allow us to directly find the associated
+   breakpoint, and from there its partition.
+
+   But all this needs to be discussed with the rest of the maintainers.
+   In the meantime, we're trying to make this change as contained as
+   possible.  */
+
+struct bp_partition_info
+{
+  struct breakpoint *b;
+  WTX_CONTEXT_ID_T partition_id;
+
+  struct bp_partition_info *next;
+};
+
+static struct bp_partition_info *bp_partition_list = NULL;
+
+/* Add the breakpoint/context-id pair to BP_PARTITION_LIST.  */
+
+static void
+add_bp_partition_info (struct breakpoint *b, WTX_CONTEXT_ID_T partition_id)
+{
+  struct bp_partition_info *info = xmalloc (sizeof (struct bp_partition_info));
+
+  info->b = b;
+  info->partition_id = partition_id;
+  info->next = bp_partition_list;
+
+  bp_partition_list = info;
+}
+
+/* Delete the element in BP_PARTITION_LIST corresponding to the given
+   breakpoint.  */
+
+static void
+delete_bp_partition_info (struct breakpoint *b)
+{
+  struct bp_partition_info *prev = NULL;
+  struct bp_partition_info *this = bp_partition_list;
+
+  while (this != NULL && this->b != b)
+    {
+      prev = this;
+      this = this->next;
+    }
+
+  if (this == NULL)
+    return;
+
+  if (this == bp_partition_list)
+    bp_partition_list = bp_partition_list->next;
+  else
+    prev->next = this->next;
+
+  xfree (this);
+}
+
+/* Find the breakpoint associated to the given TARGET_INFO and
+   return its corresponding partition ID.  */
+
+static WTX_CONTEXT_ID_T
+wtx_partition_id_from_target_info (struct bp_target_info *target_info)
+{
+  struct breakpoint *bp;
+
+  /* Shortcut: If the target system does not support more than one partition,
+     then we know which partition this breakpoint belongs to.  */
+  if (!wtx_pd_system_has_pds ())
+    return wtxapi_pd_current_get ();
+
+  /* FIXME: We do not support partition switching just yet.  So,
+     the breakpoint is necessarily in the current partition.  */
+  return wtxapi_pd_current_get ();
+}
+
+/* Observer for the "breakpoint_created" event.  */
+
+static void
+wtx_breakpoint_created_observer (int bpnum)
+{
+  /* FIXME: The observer only passes the bpnum, which forces us
+     to do a reverse search for the associated breakpoint structure.
+     It is silly to have to do so when the observer had in fact
+     the breakpoint structure and had to dereference it in order to
+     pass the bpnum.  Propose that the observer be enhanced when
+     submitting this code to the FSF.  */
+  struct breakpoint *b = get_breakpoint (bpnum);
+
+  gdb_assert (b != NULL);
+  add_bp_partition_info (b, wtxapi_pd_current_get ());
+}
+
+/* Observer for the "delete_breakpoint" event.  */
+
+static void
+wtx_delete_breakpoint_observer (int bpnum)
+{
+  struct breakpoint *b = get_breakpoint (bpnum);
+
+  gdb_assert (b != NULL);
+  delete_bp_partition_info (b);
+}
+
+/* Insert a system-mode breakpoint at the location specified by TARGET_INFO.
+   Return a non-zero errno code if the insertion failed.  */
+
+static int
+wtx_insert_system_mode_breakpoint (struct gdbarch *gdbarch,
+				   struct bp_target_info *target_info)
+{
+  const WTX_CONTEXT_ID_T breakpoint_pd
+    = wtx_partition_id_from_target_info (target_info);
+
+  return wtx_insert_breakpoint_1 (target_info,
+                                  WTX_CONTEXT_SYSTEM, /* context_type */
+                                  breakpoint_pd,      /* context_id */
+                                  WTX_ACTION_STOP,    /* action_type */
+				  0);                 /* call_rtn */
+}
+
+/* Implement the "to_remove_breakpoint" method in the target_ops vector.  */
+
+static int
+wtx_remove_breakpoint (struct gdbarch *gdbarch,
+		       struct bp_target_info *target_info)
+{
+  struct eventpoint_info *evtpt_info = target_info->target_private_data;
+  evtpt_id_t evtpt_id;
+  WTX_CONTEXT_TYPE context_type;
+  WTX_CONTEXT_ID_T context_id;
+
+  if (evtpt_info == NULL)
+    {
+      /* This is really an internal error, but the user might be able
+	 to continue debugging normally if the user is luckly (if his
+	 code does not hit the breakpoint).  So just report a warning
+	 instead of an internal error.  */
+      warning (_("Cannot find eventpoint info for breakpoint at 0x%lx"),
+               target_info->placed_address);
+      return EINVAL;
+    }
+
+  /* Free the target_info private data now, after having made a copy
+     of its contents for our use here.  It's easier to do it that way,
+     rather than avoiding the copies, because of the multiple returns
+     used in this routine (we would have to be careful to xfree it before
+     each and every return).  */
+  evtpt_id = evtpt_info->id;
+  context_type = evtpt_info->context_type;
+  context_id = evtpt_info->context_id;
+  xfree (target_info->target_private_data);
+  target_info->target_private_data = NULL;
+
+  if (evtpt_id == invalid_evtpt_id)
+    {
+      warning (_("Cannot find eventpoint ID for breakpoint at 0x%lx"),
+               target_info->placed_address);
+      return EINVAL;
+    }
+
+  wtx_opt_breakpoints_debug
+    ("wtx_remove_breakpoint (target_info = 0x%s) -> "
+     "evtpt_id = 0x%x, context_type = 0x%x, context_id = 0x%lx",
+     host_address_to_string (target_info), evtpt_id,
+     context_type, context_id);
+
+  if (!wtxapi_eventpoint_delete (evtpt_id))
+    {
+      /* If this eventpoint is a task-specific eventpoint, and if the
+	 corresponding task is dead, then this eventpoint has already
+	 been deleted.  In this case, the error returned by
+	 wtxapi_eventpoint_delete is expected.  Ignore it.  */
+      if (context_type == WTX_CONTEXT_TASK
+	  && !wtx_context_alive (context_id))
+	return 0;
+
+      warning (_("Failed to delete eventpoint for breakpoint at 0x%lx"),
+               target_info->placed_address);
+      return EINVAL;
+    }
+
+  return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for
+   the targets that support BoDA events.  */
+
+static int
+can_use_BoDA_breakpoint (int bp_type, int count, int other_type)
+{
+  /* BoDA events provide all types of watchpoints, read, write, access,
+     so it's not necessary to check BP_TYPE.  */
+
+  /* The system only provides one BoDA event.  */
+  if (count < 2)
+    return 1;
+
+  /* All the conditions to use a hardware watchpoint are not met, so
+     we cannot use one.  */
+  return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for
+   the targets where we use data-access events to implement watchpoints.  */
+
+static int
+can_use_data_access_breakpoint (int bp_type, int count, int other_type)
+{
+  /* Data-access events provide all types of watchpoints, read, write,
+     access, so it's not necessary to check BP_TYPE.  */
+
+  /* The system only provides one data-access event.  */
+  if (count < 2)
+    return 1;
+
+  /* All the conditions to use a hardware watchpoint are not met, so
+     we cannot use one.  */
+  return 0;
+}
+
+/* Implement the target_can_use_hw_breakpoint target method for all WTX
+   targets.  */
+
+static int
+wtx_can_use_hw_breakpoint (int bp_type, int count, int other_type)
+{
+  /* Tornado systems provide at most one hardware watchpoint,
+     so if another type of watchpoint is already being used,
+     then we have no hardware watchpoint left.  Return -1 to
+     signal that the rejection is because of OTHER_TYPE.  */
+  if (other_type)
+    return -1;
+
+  if (wtxapi_target_has_BoDA ())
+    return can_use_BoDA_breakpoint (bp_type, count, other_type);
+
+  /* On targets that do not support BoDA, we have access to
+     data-access events.  Try using that as a fallback.  */
+  if (can_use_data_access_breakpoint (bp_type, count, other_type))
+    return 1;
+
+  /* The system cannot provide hardware watchpoint support for this case.  */
+  return 0;
+}
+
+/* Return non-zero if we just stopped after hitting a watchpoint.  */
+
+static int
+wtx_stopped_by_watchpoint (void)
+{
+  int result = (watchpoint_data_address != 0);
+
+  wtx_opt_watchpoints_debug ("wtx_stopped_by_watchpoint() -> %d", result);
+
+  return result;
+}
+
+/* Return the address monitored by the watchpoint we just hit.  */
+
+static int
+wtx_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
+{
+  const int success = wtx_stopped_by_watchpoint ();
+
+  if (wtx_stopped_by_watchpoint ())
+    {
+      *addr_p = watchpoint_data_address;
+      wtx_opt_watchpoints_debug ("wtx_stopped_data_address(0x%s) -> 1",
+                                 paddress (target_gdbarch, *addr_p));
+      return 1;
+    }
+
+  wtx_opt_watchpoints_debug ("wtx_stopped_data_address() -> 0");
+  return 0;
+}
+
+/* Implement the region_ok_for_hw_watchpoint target method for
+   targets that support BoDA.  */
+
+static int
+region_ok_for_BoDA (CORE_ADDR addr, int len)
+{
+  /* BoDA events only allow us to watch a data that is 4 bytes
+     long.  Apparently, there are no constraints in terms of
+     alignment, so we don't need to worry about it.  */
+  return (len <= 4);
+}
+
+/* Return non-zero if a WTX_EVENT_DATA_ACCESS event can watch
+   a memory region of LEN bytes.  */
+
+static int
+region_ok_for_data_access_event (CORE_ADDR addr, int len)
+{
+  /* brobecker/2006-09-27: As far as I know, DATA_ACCESS events
+     can only monitor 4-byte memory regions.  */
+  return (len <= 4);
+}
+
+
+/* Implement the region_ok_for_hw_watchpoint for all tornado targets.
+   Basicallly determines the type of watchpoint the target supports and
+   dispatches to the appropriate function.  */
+
+static int
+wtx_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+{
+  if (wtxapi_target_has_BoDA ())
+    return region_ok_for_BoDA (addr, len);
+
+  return (region_ok_for_data_access_event (addr, len));
+}
+
+/* Given the watchpoint type that needs to be inserted (that follows
+   GDB's encoding), return the equivalent value to be used in WTX
+   event insertion requests.  */
+
+static wtxapi_tgt_addr_t
+gdb_watchpoint_type_to_wtx_breakpoint_type (int type)
+{
+  switch (type)
+    {
+      case hw_write: return 3; break;
+      case hw_read:  return 2; break;
+      case hw_access: return 1; break;
+      case hw_execute: return 0; break;
+    }
+
+  /* We should never reach this code.  If we do, print a warning and
+     pretend we were trying to use a read+write=access watchpoint type.  */
+  warning (_("unexpected watchpoint type: %d"), type);
+  return gdb_watchpoint_type_to_wtx_breakpoint_type (hw_access);
+}
+
+/* Fill up EVENT with all the necessary information to insert
+   a WTX_EVENT_HW_BP eventpoint.
+
+   Even though this function does not setup EVENT to insert a BoDA,
+   this function assumes that the target does support BoDA.  If not,
+   use set_data_access_event below.  */
+
+static void
+set_hardware_watchpoint_event_no_value (struct wtxapi_event *event,
+                                        CORE_ADDR addr,
+                                        int type)
+{
+  event->event_type = WTX_EVENT_HW_BP;
+
+  /* WTX_EVENT_HW_BP eventpoints have 3 arguments:
+       . args[0] is the address of the data being watched.
+       . args[1] is the watchpoint count (the number of times
+                 the watchpoint should be hit before generating
+                 an event to the debugger). Unused by us, always
+                 set to 0.
+       . args[2] is the type of watchpoint (read/write/access).  */
+  event->num_args = 3;
+  event->args[0] = addr;
+  event->args[1] = 0;
+  event->args[2] = gdb_watchpoint_type_to_wtx_breakpoint_type (type);
+
+  wtx_opt_watchpoints_debug
+    ("Using WTX_EVENT_HW_BP event (addr = 0x%s, type = %d)",
+     paddress (target_gdbarch, event->args[0]), (int) event->args[2]);
+}
+
+/* Fill up EVENT with all the necessary information to insert
+   a WTX_EVENT_DATA_ACCESS eventpoint.
+
+   This function should be used with all systems that do NOT support BoDA.  */
+
+static void
+set_data_access_event (struct wtxapi_event *event,
+                       const CORE_ADDR addr,
+                       const int type)
+{
+  event->event_type = WTX_EVENT_DATA_ACCESS;
+
+  /* WTX_EVENT_DATA_ACCESS eventpoints have 3 arguments:
+       . args[0] is the address of the data being watched.
+       . args[1] is the watchpoint count (the number of times
+                 the watchpoint should be hit before generating
+                 an event to the debugger). Unused by us, always
+                 set to 0.
+       . args[2] is the type of watchpoint (read/write/access).  */
+  event->num_args = 3;
+  event->args[0] = addr;
+  event->args[1] = 0;
+  event->args[2] = gdb_watchpoint_type_to_wtx_breakpoint_type (type);
+
+  wtx_opt_watchpoints_debug
+    ("Using DATA_ACCESS event (addr = 0x%s, type = %d)",
+     paddress (target_gdbarch, event->args[0]), (int) event->args[2]);
+}
+
+/* Implement the target_insert_watchpoint target method for all WTX
+   targets.  */
+
+static int
+wtx_insert_watchpoint (WTX_CONTEXT_TYPE context_type,
+                       WTX_CONTEXT_ID_T context_id,
+                       WTX_ACTION_TYPE action_type,
+                       CORE_ADDR addr, int len, int type)
+{
+  struct wtxapi_evtpt evtpt;
+  wtxapi_tgt_arg_t args[4];
+  evtpt_id_t watchpoint_id;
+
+  /* First, clear the evtpt struture.  In other parts of this file,
+     we actually calloc this structure which makes the memset unecessary,
+     but doing it this way avoids the need to free the newly allocated
+     memory.  */
+  memset (&evtpt, 0, sizeof (evtpt));
+
+  evtpt.context.context_type = context_type;
+  evtpt.context.context_id = context_id;
+  evtpt.action.action_type = action_type;
+  evtpt.event.args = args;
+
+  /* Tell the target server to notify us when the watchpoint is hit.  */
+  evtpt.action.action_type |= WTX_ACTION_NOTIFY;
+
+  /* If the system provides data-matching support with its watchpoints,
+     and the watchpoint has a condition that can be tested using that
+     support, then use it.  The data value in BoDA watchpoints will
+     be optional.  */
+
+  if (wtxapi_target_has_BoDA ())
+    set_hardware_watchpoint_event_no_value (&evtpt.event, addr, type);
+  else
+    set_data_access_event (&evtpt.event, addr, type);
+
+  /* Insert the eventpoint.  */
+  watchpoint_id = wtxapi_eventpoint_add (&evtpt);
+
+  if (watchpoint_id == invalid_evtpt_id)
+    error (_("Failed to insert watchpoint"));
+
+  /* Save the watchpoint_id in our list of eventpoints.  Will be needed
+     later to map GDB watchpoints info back into WTX event ids.  */
+  wtx_bp_register_watchpoint_id (watchpoint_id, addr, len, type);
+
+  return 0;
+}
+
+/* Implement the task-mode "to_insert_watchpoint" target_ops method.  */
+
+static int
+wtx_insert_task_mode_watchpoint (CORE_ADDR addr, int len, int type,
+				 struct expression *cond)
+{
+  const enum wtx_breakpoint_action action = get_break_command_action ();
+  WTX_CONTEXT_TYPE context_type;
+  WTX_CONTEXT_ID_T context_id;
+  WTX_ACTION_TYPE action_type;
+  CORE_ADDR call_rtn = 0;
+
+  /* FIXME: We do the exact same thing when inserting a task-mode
+     breakpoint.  Put this code inside remote-wtx-bp and then reuse.  */
+  context_type = WTX_CONTEXT_TASK;
+  context_id = ptid_get_context_id (inferior_ptid);
+
+  switch (action)
+    {
+      case action_task:
+        action_type = WTX_ACTION_STOP;
+        break;
+
+      case action_all:
+        action_type = WTX_ACTION_STOP_ALL;
+        break;
+
+      case action_call:
+        context_type = WTX_CONTEXT_ANY_TASK;
+        action_type =  WTX_ACTION_STOP;
+        call_rtn = get_break_command_call_rtn ();
+	if (call_rtn != 0)
+	  action_type |= WTX_ACTION_CALL;
+        break;
+
+      default:
+        internal_error (__FILE__, __LINE__, _("Unexpected action value: %d"),
+                        action);
+    }
+
+  return wtx_insert_watchpoint (context_type, context_id,
+                                action_type, addr, len, type);
+}
+
+/* Implement the system-mode "to_insert_watchpoint" target_ops method.  */
+
+static int
+wtx_insert_system_mode_watchpoint (CORE_ADDR addr, int len, int type,
+				   struct expression *cond)
+{
+  return wtx_insert_watchpoint (WTX_CONTEXT_SYSTEM, SYSTEM_CID,
+                                WTX_ACTION_STOP, addr, len, type);
+}
+
+/* Implement the "to_remove_watchpoint" target_ops method.  */
+
+static int
+wtx_remove_watchpoint (CORE_ADDR addr, int len, int type,
+		       struct expression *cond)
+{
+  const evtpt_id_t watchpoint_id = wtx_bp_pop_watchpoint_id (addr, len, type);
+
+  if (watchpoint_id == invalid_evtpt_id)
+    {
+      warning (_("Unknown watchpoint at 0x%s (len = %d, type = %d)"),
+               paddress (target_gdbarch, addr), len, type);
+      return EINVAL;
+    }
+
+  if (!wtxapi_eventpoint_delete (watchpoint_id))
+    {
+      warning (_("Unable to remove watchpoint id 0x%x"), watchpoint_id);
+      return EINVAL;
+    }
+
+  return 0;
+}
+
+/* Implement the "to_mourn_inferior" method of the target_ops vector
+   for the task mode.  */
+
+static void
+wtx_task_mode_mourn_inferior (struct target_ops *ops)
+{
+  remove_breakpoints ();
+  if (multi_task_mode_is_on ())
+    stop_multi_task_mode ();
+
+  unpush_target (&wtx_task_mode_ops);
+  generic_mourn_inferior ();
+}
+
+/* Return 1 if the thread whose id is CONTEXT_ID is still alive.  */
+
+static int
+wtx_context_alive (int context_id)
+{
+  struct wtxapi_thread_info *thread_list = wtxapi_get_thread_list ();
+  struct wtxapi_thread_info *thread;
+  int is_alive = 0;
+
+  for (thread = thread_list; thread != NULL; thread = thread->next)
+    if (thread->id == context_id)
+      {
+        is_alive = 1;
+        break;
+      }
+  free_wtxapi_thread_info (thread_list);
+
+  return is_alive;
+}
+
+/* Implement the "to_thread_alive" method of the target_ops vector.  */
+
+static int
+wtx_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  const int context_id = ptid_get_context_id (ptid);
+  return wtx_context_alive (context_id);
+}
+
+/* Implement the "to_find_new_threads" method when in single-task mode.  */
+
+static void
+wtx_single_task_mode_find_new_threads (void)
+{
+  /* In this mode, we only debug one thread.  */
+  wtx_add_thread (inferior_ptid);
+}
+
+/* Add the thread associated to the given TASK to the thread list.
+   Does nothing if that thread was already added earlier.  */
+
+static void
+wtx_multi_task_mode_add_thread_from_task (struct ada_task_info *task)
+{
+  wtx_add_thread (task->ptid);
+}
+
+/* Implement the "to_find_new_threads" method when in multi-tasks mode.  */
+
+static void
+wtx_multi_task_mode_find_new_threads (void)
+{
+  iterate_over_live_ada_tasks (wtx_multi_task_mode_add_thread_from_task);
+}
+
+/* Implement the "to_find_new_threads" method of the task-mode target_ops
+   vector.  Handles both single-task and multi-tasks modes.  */
+
+static void
+wtx_task_mode_find_new_threads (struct target_ops *ops)
+{
+  if (multi_task_mode_is_on ())
+    wtx_multi_task_mode_find_new_threads ();
+  else
+    wtx_single_task_mode_find_new_threads ();
+}
+
+/* Implement the "to_find_new_threads" method of the system-mode
+   target_ops vector.  */
+
+static void
+wtx_system_mode_find_new_threads (struct target_ops *ops)
+{
+  struct wtxapi_thread_info *thread_list = wtxapi_get_thread_list ();
+  struct wtxapi_thread_info *thread;
+
+  for (thread = thread_list; thread != NULL; thread = thread->next)
+    {
+      const ptid_t thread_ptid = context_id_to_ptid (thread->id);
+
+      wtx_add_thread (thread_ptid);
+    }
+
+  free_wtxapi_thread_info (thread_list);
+}
+
+/* Suspend the given CONTEXT_TYPE/CONTEXT_ID.  */
+
+static void
+wtx_stop (WTX_CONTEXT_TYPE context_type, WTX_CONTEXT_ID_T context_id)
+{
+  if (multi_task_mode_is_on ())
+    stop_all_ada_tasks (&inferior_status);
+  else
+    stop_inferior (context_type, context_id);
+}
+
+/* Implement the task-mode "to_stop" target_ops method.  */
+
+static void
+wtx_task_mode_stop (ptid_t ptid)
+{
+  const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+
+  wtx_stop (WTX_CONTEXT_TASK, context_id);
+}
+
+/* Implement the system-mode "to_stop" target_ops method.  */
+
+static void
+wtx_system_mode_stop (ptid_t ptid)
+{
+  wtx_stop (WTX_CONTEXT_SYSTEM, SYSTEM_CID);
+  inferior_ptid =
+    context_id_to_ptid (wtxapi_system_mode_get_current_context_id ());
+}
+
+/* Return the name of a task from its CONTEXT_ID, or NULL if the task
+   could not be found.
+
+   The returned string must be deallocated after use.  */
+
+static char *
+wtx_name_from_context_id (WTX_CONTEXT_ID_T context_id)
+{
+  struct wtxapi_thread_info *threads = wtxapi_get_thread_list ();
+  struct wtxapi_thread_info *this_thread = threads;
+  char *name = NULL;
+
+  while (this_thread != NULL)
+    {
+      if (this_thread->id == context_id)
+        {
+	  name = xstrdup (this_thread->name);
+	  break;
+	}
+      this_thread = this_thread->next;
+    }
+
+  free_wtxapi_thread_info (threads);
+  return name;
+}
+
+/* Implement the "to_pid_to_str" target_ops method.  */
+
+static char *
+wtx_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  static char *buf = NULL;
+  const WTX_CONTEXT_ID_T context_id = ptid_get_context_id (ptid);
+  char *task_name = wtx_name_from_context_id (context_id);
+
+  if (buf != NULL)
+    xfree (buf);
+  xasprintf (&buf, "task 0x%lx\t(%s)\t", context_id,
+             task_name ? task_name : "[no name]");
+  if (task_name != NULL)
+    xfree (task_name);
+
+  return buf;
+}
+
+/* Implement the to_get_ada_task_ptid function for the WTX targets.  */
+
+static ptid_t
+wtx_get_ada_task_ptid (long lwp, long thread)
+{
+  return context_id_to_ptid (thread);
+}
+
+/* Always return zero.  */
+
+static int
+wtx_return_zero (struct target_ops *target)
+{
+  return 0;
+}
+
+/* Always return one.  */
+
+static int
+wtx_return_one (struct target_ops *target)
+{
+  return 1;
+}
+
+/* Initialize the WTX_OPS target_ops object.  */
+
+static void
+init_wtx_ops (void)
+{
+  /* First, clear the structure.  This is not strictly necessary provided
+     we make sure to always set all the fields of the our structure.
+     But in the event that we did not, this makes sure that the fields
+     we missed are initialized to something consistent.  */
+  memset (&wtx_ops, 0, sizeof (wtx_ops));
+
+  wtx_ops.to_shortname = "wtx";
+  wtx_ops.to_longname = "WTX protocol";
+  wtx_ops.to_doc = "\
+Remote target connected via the WTX protocol.\n\
+Specify the name of the target server as the argument.";
+  wtx_ops.to_open = wtx_open;
+  wtx_ops.to_close = wtx_close;
+  wtx_ops.to_attach = wtx_attach;
+  /* No need to set to_detach, will never be called, because the detach
+     option from a higher stratum will be used instead.  */
+  wtx_ops.to_load = wtx_load;
+  wtx_ops.to_unload = wtx_unload;
+  wtx_ops.to_create_inferior = wtx_create_inferior;
+  wtx_ops.to_pid_to_str = wtx_pid_to_str;
+  wtx_ops.to_stratum = file_stratum;
+  wtx_ops.to_has_all_memory = wtx_return_one;
+  wtx_ops.to_has_memory = wtx_return_one;
+  wtx_ops.to_has_stack = wtx_return_zero;
+  wtx_ops.to_has_registers = wtx_return_zero;
+  wtx_ops.to_has_execution = wtx_return_zero;
+  wtx_ops.to_xfer_partial = wtx_xfer_partial;
+  wtx_ops.to_get_ada_task_ptid = wtx_get_ada_task_ptid;
+  wtx_ops.to_magic = OPS_MAGIC;
+}
+
+static void
+init_wtx_task_mode_ops (void)
+{
+  /* First, clear the structure.  This is not strictly necessary provided
+     we make sure to always set all the fields of the our structure.
+     But in the event that we did not, this makes sure that the fields
+     we missed are initialized to something consistent.  */
+  memset (&wtx_task_mode_ops, 0, sizeof (wtx_task_mode_ops));
+
+  wtx_task_mode_ops.to_shortname = "wtx task mode";
+  wtx_task_mode_ops.to_longname =
+    "Task and Multi-tasks Mode support for the WTX protocol";
+  wtx_task_mode_ops.to_doc =
+    "Debugging a task or a set of tasks using the WTX protocol.";
+  wtx_task_mode_ops.to_open = NULL;
+  wtx_task_mode_ops.to_close = wtx_task_mode_close;
+  wtx_task_mode_ops.to_attach = wtx_attach;
+  wtx_task_mode_ops.to_detach = wtx_task_mode_detach;
+  wtx_task_mode_ops.to_resume = wtx_task_mode_resume;
+  wtx_task_mode_ops.to_wait = wtx_wait;
+  wtx_task_mode_ops.to_fetch_registers = wtx_task_mode_fetch_registers;
+  wtx_task_mode_ops.to_store_registers = wtx_task_mode_store_registers;
+  wtx_task_mode_ops.to_prepare_to_store = wtx_prepare_to_store;
+  wtx_task_mode_ops.to_insert_breakpoint = wtx_insert_task_mode_breakpoint;
+  wtx_task_mode_ops.to_remove_breakpoint = wtx_remove_breakpoint;
+  wtx_task_mode_ops.to_can_use_hw_breakpoint = wtx_can_use_hw_breakpoint;
+  wtx_task_mode_ops.to_remove_watchpoint = wtx_remove_watchpoint;
+  wtx_task_mode_ops.to_insert_watchpoint = wtx_insert_task_mode_watchpoint;
+  wtx_task_mode_ops.to_stopped_by_watchpoint = wtx_stopped_by_watchpoint;
+  wtx_task_mode_ops.to_stopped_data_address = wtx_stopped_data_address;
+  wtx_task_mode_ops.to_region_ok_for_hw_watchpoint =
+    wtx_region_ok_for_hw_watchpoint;
+
+  wtx_task_mode_ops.to_kill = wtx_task_mode_kill;
+  wtx_task_mode_ops.to_load = wtx_load;
+  wtx_task_mode_ops.to_unload = wtx_unload;
+  wtx_task_mode_ops.to_mourn_inferior = wtx_task_mode_mourn_inferior;
+  wtx_task_mode_ops.to_thread_alive = wtx_thread_alive;
+  wtx_task_mode_ops.to_find_new_threads = wtx_task_mode_find_new_threads;
+  wtx_task_mode_ops.to_stop = wtx_task_mode_stop;
+  wtx_task_mode_ops.to_stratum = process_stratum;
+  wtx_task_mode_ops.to_has_all_memory = wtx_return_one;
+  wtx_task_mode_ops.to_has_memory = wtx_return_one;
+  wtx_task_mode_ops.to_has_stack = wtx_return_one;
+  wtx_task_mode_ops.to_has_registers = wtx_return_one;
+  wtx_task_mode_ops.to_has_execution = wtx_return_one;
+  wtx_task_mode_ops.to_xfer_partial = wtx_xfer_partial;
+  wtx_task_mode_ops.to_magic = OPS_MAGIC;
+}
+
+static void
+init_wtx_system_mode_ops (void)
+{
+  /* First, clear the structure.  This is not strictly necessary provided
+     we make sure to always set all the fields of the our structure.
+     But in the event that we did not, this makes sure that the fields
+     we missed are initialized to something consistent.  */
+  memset (&wtx_system_mode_ops, 0, sizeof (wtx_system_mode_ops));
+
+  wtx_system_mode_ops.to_shortname = "wtx system mode";
+  wtx_system_mode_ops.to_longname =
+    "System Mode support for the WTX protocol";
+  wtx_system_mode_ops.to_doc =
+    "Debugging a target in system mode using the WTX protocol.";
+  wtx_system_mode_ops.to_open = NULL;
+  wtx_system_mode_ops.to_close = wtx_system_mode_close;
+  wtx_system_mode_ops.to_attach = wtx_attach;
+  wtx_system_mode_ops.to_detach = wtx_system_mode_detach;
+  wtx_system_mode_ops.to_resume = wtx_system_mode_resume;
+  wtx_system_mode_ops.to_wait = wtx_wait;
+  wtx_system_mode_ops.to_fetch_registers = wtx_system_mode_fetch_registers;
+  wtx_system_mode_ops.to_store_registers = wtx_system_mode_store_registers;
+  wtx_system_mode_ops.to_prepare_to_store = wtx_prepare_to_store;
+  wtx_system_mode_ops.to_insert_breakpoint = wtx_insert_system_mode_breakpoint;
+  wtx_system_mode_ops.to_remove_breakpoint = wtx_remove_breakpoint;
+  wtx_system_mode_ops.to_can_use_hw_breakpoint = wtx_can_use_hw_breakpoint;
+  wtx_system_mode_ops.to_remove_watchpoint = wtx_remove_watchpoint;
+  wtx_system_mode_ops.to_insert_watchpoint = wtx_insert_system_mode_watchpoint;
+  wtx_system_mode_ops.to_stopped_by_watchpoint = wtx_stopped_by_watchpoint;
+  wtx_system_mode_ops.to_stopped_data_address = wtx_stopped_data_address;
+  wtx_system_mode_ops.to_region_ok_for_hw_watchpoint =
+    wtx_region_ok_for_hw_watchpoint;
+
+  wtx_system_mode_ops.to_kill = wtx_system_mode_kill;
+  wtx_system_mode_ops.to_load = wtx_load;
+  wtx_system_mode_ops.to_unload = wtx_unload;
+  wtx_system_mode_ops.to_thread_alive = wtx_thread_alive;
+  wtx_system_mode_ops.to_find_new_threads = wtx_system_mode_find_new_threads;
+  wtx_system_mode_ops.to_stop = wtx_system_mode_stop;
+  wtx_system_mode_ops.to_stratum = process_stratum;
+  wtx_system_mode_ops.to_has_all_memory = wtx_return_one;
+  wtx_system_mode_ops.to_has_memory = wtx_return_one;
+  wtx_system_mode_ops.to_has_stack = wtx_return_one;
+  wtx_system_mode_ops.to_has_registers = wtx_return_one;
+  wtx_system_mode_ops.to_has_execution = wtx_return_one;
+  wtx_system_mode_ops.to_xfer_partial = wtx_xfer_partial;
+  wtx_system_mode_ops.to_magic = OPS_MAGIC;
+}
+
+static struct cmd_list_element *wtx_list = NULL;
+
+/* Implement the "wtx" command.
+   This is not actually a valid command, so inform the user of that fact,
+   and print the list of valid subcommands to help him.  */
+
+static void
+wtx_command (char *args, int from_tty)
+{
+  printf_filtered (_("\"wtx\" must be followed by a subcommand.\n"));
+  help_list (wtx_list, "wtx ", -1, gdb_stdout);
+}
+
+/* Implement the "wtx add-symbol-file" command: same as add-symbol-file,
+   except that the load addresses are not taken from the command
+   line, but from the WTX module info.  */
+
+static void
+wtx_add_symbol_file (char *args, int from_tty)
+{
+  char *module_name, *module_filename;
+  module_id_t module_id;
+  pd_id_t module_pd;
+  struct cleanup *old_chain = NULL;
+  struct wtxapi_module_info *module_info;
+
+  if (args == NULL)
+    error (_("wtx add-symbol-file takes a file name"));
+
+  module_filename = args;
+  module_name = (char *) lbasename (module_filename);
+  module_id = wtxapi_obj_module_in_system_find_id (module_name);
+
+  if (module_id == invalid_module_id)
+    {
+      warning (_("could not find %s on target: %s"),
+	       module_name, wtxapi_err_msg_get ());
+      return;
+    }
+
+  module_pd = wtx_pd_get_module_pd (module_id);
+  module_info = wtxapi_obj_module_info_get (module_pd, module_id);
+  old_chain = make_cleanup (cleanup_wtxapi_module_info, module_info);
+
+  if (module_info == NULL)
+    error (_("Cannot get info for module 0x%x: %s"),
+	   (unsigned int) module_id, wtxapi_err_msg_get ());
+
+  printf_filtered (_("Add symbols from file %s:\n"), module_filename);
+  if (read_module_symbols_1 (module_info, module_filename))
+    printf_filtered (_("ok\n"));
+  else
+    printf_filtered (_("failed\n"));
+
+  if (old_chain)
+    do_cleanups (old_chain);
+}
+
+void
+_initialize_remote_wtx (void)
+{
+  /* Initialize the WTX library.  */
+  if (!wtxapi_initialize ())
+    {
+      warning (_("Failed to initialize WTX library:\n\t%s\n\
+(WTX target not activated)."),
+               wtxapi_err_msg_get ());
+
+      /* We cannot use the WTX protocol if for some reason we failed
+         to initialized the WTX API. So abort now, instead of pushing
+         the WTX target ops.  */
+      return;
+    }
+
+  init_wtx_ops ();
+  init_wtx_task_mode_ops ();
+  init_wtx_system_mode_ops ();
+
+  add_target (&wtx_ops);
+
+  /* New commands.  */
+
+  add_prefix_cmd ("wtx", no_class, wtx_command,
+		  _("WTX protocol specific commands\n"),
+		  &wtx_list, "wtx ",
+		  0 /* allow-unknown */, &cmdlist);
+
+  add_cmd ("add-symbol-file", no_class, wtx_add_symbol_file, _("\
+WTX specific version of add-symbol-file\n\
+Usage: add-symbol-file FILE\n\
+Load symbols from FILE, assuming that FILE has been dynamically loaded.\n\
+The section addresses for this module are fetched from the target server.\n\
+"), &wtx_list);
+
+  /* Observers.  */
+
+  if (wtx_pd_system_has_pds ())
+    {
+      /* The following observers are only useful when the target system
+         supports partitions.  */
+      observer_attach_breakpoint_created (wtx_breakpoint_created_observer);
+      observer_attach_breakpoint_deleted (wtx_delete_breakpoint_observer);
+    }
+}
diff --git a/gdb/remote-wtx.h b/gdb/remote-wtx.h
new file mode 100644
index 0000000..a81f55e
--- /dev/null
+++ b/gdb/remote-wtx.h
@@ -0,0 +1,25 @@
+/* WTX backend for GDB.
+
+   Copyright 2007, 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef REMOTE_WTX_H
+#define REMOTE_WTX_H
+
+extern void wtx_open (char *args, int from_tty);
+
+#endif
-- 
1.7.0.4


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