Decouple target code from remote protocol.
[deliverable/binutils-gdb.git] / gdb / gdbserver / win32-low.c
index 42cbb90fc4f4ec72078cae562d53383d1db349ca..2fed73cba48447ef0c9be7bc49c91fc44c0a7e23 100644 (file)
@@ -1,5 +1,5 @@
 /* Low level interface to Windows debugging, for gdbserver.
-   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    Contributed by Leo Zayas.  Based on "win32-nat.c" from GDB.
 
@@ -21,6 +21,7 @@
 #include "server.h"
 #include "regcache.h"
 #include "gdb/signals.h"
+#include "gdb/fileio.h"
 #include "mem-break.h"
 #include "win32-low.h"
 
 int using_threads = 1;
 
 /* Globals.  */
+static int attaching = 0;
 static HANDLE current_process_handle = NULL;
 static DWORD current_process_id = 0;
+static DWORD main_thread_id = 0;
 static enum target_signal last_sig = TARGET_SIGNAL_0;
 
 /* The current debug event from WaitForDebugEvent.  */
 static DEBUG_EVENT current_event;
 
+/* Non zero if an interrupt request is to be satisfied by suspending
+   all threads.  */
+static int soft_interrupt_requested = 0;
+
+/* Non zero if the inferior is stopped in a simulated breakpoint done
+   by suspending all the threads.  */
+static int faked_breakpoint = 0;
+
 #define NUM_REGS (the_low_target.num_regs)
 
 typedef BOOL WINAPI (*winapi_DebugActiveProcessStop) (DWORD dwProcessId);
@@ -79,9 +90,7 @@ typedef BOOL WINAPI (*winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
 typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE);
 typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
 
-static DWORD main_thread_id = 0;
-
-static void win32_resume (struct thread_resume *resume_info);
+static void win32_resume (struct thread_resume *resume_info, size_t n);
 
 /* Get the thread ID from the current selected inferior (the current
    thread).  */
@@ -92,6 +101,39 @@ current_inferior_tid (void)
   return th->tid;
 }
 
+/* Get the thread context of the thread associated with TH.  */
+
+static void
+win32_get_thread_context (win32_thread_info *th)
+{
+  memset (&th->context, 0, sizeof (CONTEXT));
+  (*the_low_target.get_thread_context) (th, &current_event);
+#ifdef _WIN32_WCE
+  memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
+#endif
+}
+
+/* Set the thread context of the thread associated with TH.  */
+
+static void
+win32_set_thread_context (win32_thread_info *th)
+{
+#ifdef _WIN32_WCE
+  /* Calling SuspendThread on a thread that is running kernel code
+     will report that the suspending was successful, but in fact, that
+     will often not be true.  In those cases, the context returned by
+     GetThreadContext will not be correct by the time the thread
+     stops, hence we can't set that context back into the thread when
+     resuming - it will most likelly crash the inferior.
+     Unfortunately, there is no way to know when the thread will
+     really stop.  To work around it, we'll only write the context
+     back to the thread when either the user or GDB explicitly change
+     it between stopping and resuming.  */
+  if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0)
+#endif
+    (*the_low_target.set_thread_context) (th, &current_event);
+}
+
 /* Find a thread record given a thread id.  If GET_CONTEXT is set then
    also retrieve the context for this thread.  */
 static win32_thread_info *
@@ -105,12 +147,21 @@ thread_rec (DWORD id, int get_context)
     return NULL;
 
   th = inferior_target_data (thread);
-  if (!th->suspend_count && get_context)
+  if (get_context && th->context.ContextFlags == 0)
     {
-      if (id != current_event.dwThreadId)
-       th->suspend_count = SuspendThread (th->h) + 1;
+      if (!th->suspended)
+       {
+         if (SuspendThread (th->h) == (DWORD) -1)
+           {
+             DWORD err = GetLastError ();
+             OUTMSG (("warning: SuspendThread failed in thread_rec, "
+                      "(error %d): %s\n", (int) err, strwinerror (err)));
+           }
+         else
+           th->suspended = 1;
+       }
 
-      (*the_low_target.get_thread_context) (th, &current_event);
+      win32_get_thread_context (th);
     }
 
   return th;
@@ -125,8 +176,7 @@ child_add_thread (DWORD tid, HANDLE h)
   if ((th = thread_rec (tid, FALSE)))
     return th;
 
-  th = (win32_thread_info *) malloc (sizeof (*th));
-  memset (th, 0, sizeof (*th));
+  th = xcalloc (1, sizeof (*th));
   th->tid = tid;
   th->h = h;
 
@@ -191,45 +241,6 @@ child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
   return done;
 }
 
-/* Generally, what has the program done?  */
-enum target_waitkind
-{
-  /* The program has exited.  The exit status is in value.integer.  */
-  TARGET_WAITKIND_EXITED,
-
-  /* The program has stopped with a signal.  Which signal is in
-     value.sig.  */
-  TARGET_WAITKIND_STOPPED,
-
-  /* The program is letting us know that it dynamically loaded
-     or unloaded something.  */
-  TARGET_WAITKIND_LOADED,
-
-  /* The program has exec'ed a new executable file.  The new file's
-     pathname is pointed to by value.execd_pathname.  */
-  TARGET_WAITKIND_EXECD,
-
-  /* Nothing interesting happened, but we stopped anyway.  We take the
-     chance to check if GDB requested an interrupt.  */
-  TARGET_WAITKIND_SPURIOUS,
-};
-
-struct target_waitstatus
-{
-  enum target_waitkind kind;
-
-  /* Forked child pid, execd pathname, exit status or signal number.  */
-  union
-  {
-    int integer;
-    enum target_signal sig;
-    int related_pid;
-    char *execd_pathname;
-    int syscall_id;
-  }
-  value;
-};
-
 /* Clear out any old thread list and reinitialize it to a pristine
    state. */
 static void
@@ -239,10 +250,17 @@ child_init_thread_list (void)
 }
 
 static void
-do_initial_child_stuff (DWORD pid)
+do_initial_child_stuff (HANDLE proch, DWORD pid)
 {
   last_sig = TARGET_SIGNAL_0;
 
+  current_process_handle = proch;
+  current_process_id = pid;
+  main_thread_id = 0;
+
+  soft_interrupt_requested = 0;
+  faked_breakpoint = 0;
+
   memset (&current_event, 0, sizeof (current_event));
 
   child_init_thread_list ();
@@ -259,20 +277,23 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
   struct thread_info *thread = (struct thread_info *) this_thread;
   int thread_id = * (int *) id_ptr;
   win32_thread_info *th = inferior_target_data (thread);
-  int i;
 
   if ((thread_id == -1 || thread_id == th->tid)
-      && th->suspend_count)
+      && th->suspended)
     {
       if (th->context.ContextFlags)
        {
-         (*the_low_target.set_thread_context) (th, &current_event);
+         win32_set_thread_context (th);
          th->context.ContextFlags = 0;
        }
 
-      for (i = 0; i < th->suspend_count; i++)
-       (void) ResumeThread (th->h);
-      th->suspend_count = 0;
+      if (ResumeThread (th->h) == (DWORD) -1)
+       {
+         DWORD err = GetLastError ();
+         OUTMSG (("warning: ResumeThread failed in continue_one_thread, "
+                  "(error %d): %s\n", (int) err, strwinerror (err)));
+       }
+      th->suspended = 0;
     }
 
   return 0;
@@ -281,14 +302,17 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
 static BOOL
 child_continue (DWORD continue_status, int thread_id)
 {
-  BOOL res;
+  /* The inferior will only continue after the ContinueDebugEvent
+     call.  */
+  find_inferior (&all_threads, continue_one_thread, &thread_id);
+  faked_breakpoint = 0;
 
-  res = ContinueDebugEvent (current_event.dwProcessId,
-                           current_event.dwThreadId, continue_status);
-  if (res)
-    find_inferior (&all_threads, continue_one_thread, &thread_id);
+  if (!ContinueDebugEvent (current_event.dwProcessId,
+                          current_event.dwThreadId,
+                          continue_status))
+    return FALSE;
 
-  return res;
+  return TRUE;
 }
 
 /* Fetch register(s) from the current thread context.  */
@@ -373,6 +397,55 @@ strwinerror (DWORD error)
   return buf;
 }
 
+static BOOL
+create_process (const char *program, char *args,
+               DWORD flags, PROCESS_INFORMATION *pi)
+{
+  BOOL ret;
+
+#ifdef _WIN32_WCE
+  wchar_t *p, *wprogram, *wargs;
+  size_t argslen;
+
+  wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
+  mbstowcs (wprogram, program, strlen (program) + 1);
+
+  for (p = wprogram; *p; ++p)
+    if (L'/' == *p)
+      *p = L'\\';
+
+  argslen = strlen (args);
+  wargs = alloca ((argslen + 1) * sizeof (wchar_t));
+  mbstowcs (wargs, args, argslen + 1);
+
+  ret = CreateProcessW (wprogram, /* image name */
+                       wargs,    /* command line */
+                       NULL,     /* security, not supported */
+                       NULL,     /* thread, not supported */
+                       FALSE,    /* inherit handles, not supported */
+                       flags,    /* start flags */
+                       NULL,     /* environment, not supported */
+                       NULL,     /* current directory, not supported */
+                       NULL,     /* start info, not supported */
+                       pi);      /* proc info */
+#else
+  STARTUPINFOA si = { sizeof (STARTUPINFOA) };
+
+  ret = CreateProcessA (program,  /* image name */
+                       args,     /* command line */
+                       NULL,     /* security */
+                       NULL,     /* thread */
+                       TRUE,     /* inherit handles */
+                       flags,    /* start flags */
+                       NULL,     /* environment */
+                       NULL,     /* current directory */
+                       &si,      /* start info */
+                       pi);      /* proc info */
+#endif
+
+  return ret;
+}
+
 /* Start a new process.
    PROGRAM is a path to the program to execute.
    ARGS is a standard NULL-terminated array of arguments,
@@ -392,12 +465,10 @@ win32_create_inferior (char *program, char **program_args)
   int argslen;
   int argc;
   PROCESS_INFORMATION pi;
-#ifndef __MINGW32CE__
-  STARTUPINFOA si = { sizeof (STARTUPINFOA) };
-  char *winenv = NULL;
-#else
-  wchar_t *wargs, *wprogram;
-#endif
+  DWORD err;
+
+  /* win32_wait needs to know we're not attaching.  */
+  attaching = 0;
 
   if (!program)
     error ("No executable specified, specify executable to debug.\n");
@@ -427,7 +498,7 @@ win32_create_inferior (char *program, char **program_args)
   for (argc = 1; program_args[argc]; argc++)
     {
       /* FIXME: Can we do better about quoting?  How does Cygwin
-         handle this?  */
+        handle this?  */
       strcat (args, " ");
       strcat (args, program_args[argc]);
     }
@@ -437,34 +508,15 @@ win32_create_inferior (char *program, char **program_args)
   flags |= CREATE_NEW_PROCESS_GROUP;
 #endif
 
-#ifdef __MINGW32CE__
-  to_back_slashes (program);
-  wargs = alloca (argslen * sizeof (wchar_t));
-  mbstowcs (wargs, args, argslen);
-  wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
-  mbstowcs (wprogram, program, strlen (program) + 1);
-  ret = CreateProcessW (wprogram, /* image name */
-                        wargs,    /* command line */
-                        NULL,     /* security, not supported */
-                        NULL,     /* thread, not supported */
-                        FALSE,    /* inherit handles, not supported */
-                        flags,    /* start flags */
-                        NULL,     /* environment, not supported */
-                        NULL,     /* current directory, not supported */
-                        NULL,     /* start info, not supported */
-                        &pi);     /* proc info */
-#else
-  ret = CreateProcessA (program,  /* image name */
-                       args,     /* command line */
-                       NULL,     /* security */
-                       NULL,     /* thread */
-                       TRUE,     /* inherit handles */
-                       flags,    /* start flags */
-                       winenv,   /* environment */
-                       NULL,     /* current directory */
-                       &si,      /* start info */
-                       &pi);     /* proc info */
-#endif
+  ret = create_process (program, args, flags, &pi);
+  err = GetLastError ();
+  if (!ret && err == ERROR_FILE_NOT_FOUND)
+    {
+      char *exename = alloca (strlen (program) + 5);
+      strcat (strcpy (exename, program), ".exe");
+      ret = create_process (exename, args, flags, &pi);
+      err = GetLastError ();
+    }
 
 #ifndef USE_WIN32API
   if (orig_path)
@@ -473,7 +525,6 @@ win32_create_inferior (char *program, char **program_args)
 
   if (!ret)
     {
-      DWORD err = GetLastError ();
       error ("Error creating process \"%s%s\", (error %d): %s\n",
             program, args, (int) err, strwinerror (err));
     }
@@ -489,10 +540,7 @@ win32_create_inferior (char *program, char **program_args)
   CloseHandle (pi.hThread);
 #endif
 
-  current_process_handle = pi.hProcess;
-  current_process_id = pi.dwProcessId;
-
-  do_initial_child_stuff (current_process_id);
+  do_initial_child_stuff (pi.hProcess, pi.dwProcessId);
 
   return current_process_id;
 }
@@ -503,34 +551,36 @@ win32_create_inferior (char *program, char **program_args)
 static int
 win32_attach (unsigned long pid)
 {
-  winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
+  HANDLE h;
   winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+  DWORD err;
 #ifdef _WIN32_WCE
   HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
 #else
   HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
 #endif
-  DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop);
   DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
 
-  if (DebugActiveProcess (pid))
+  h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+  if (h != NULL)
     {
-      if (DebugSetProcessKillOnExit != NULL)
-       DebugSetProcessKillOnExit (FALSE);
-
-      current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
-
-      if (current_process_handle != NULL)
-       {
-         current_process_id = pid;
-         do_initial_child_stuff (pid);
-         return 0;
-       }
-      if (DebugActiveProcessStop != NULL)
-       DebugActiveProcessStop (current_process_id);
+      if (DebugActiveProcess (pid))
+       {
+         if (DebugSetProcessKillOnExit != NULL)
+           DebugSetProcessKillOnExit (FALSE);
+
+         /* win32_wait needs to know we're attaching.  */
+         attaching = 1;
+         do_initial_child_stuff (h, pid);
+         return 0;
+       }
+
+      CloseHandle (h);
     }
 
-  error ("Attach to process failed.");
+  err = GetLastError ();
+  error ("Attach to process failed (error %d): %s\n",
+        (int) err, strwinerror (err));
 }
 
 /* Handle OUTPUT_DEBUG_STRING_EVENT from child process.  */
@@ -553,7 +603,7 @@ handle_output_debug_string (struct target_waitstatus *ourstatus)
   if (current_event.u.DebugString.fUnicode)
     {
       /* The event tells us how many bytes, not chars, even
-         in Unicode.  */
+        in Unicode.  */
       WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 };
       if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0)
        return;
@@ -578,12 +628,20 @@ handle_output_debug_string (struct target_waitstatus *ourstatus)
 #undef READ_BUFFER_LEN
 }
 
+static void
+win32_clear_inferiors (void)
+{
+  if (current_process_handle != NULL)
+    CloseHandle (current_process_handle);
+
+  for_each_inferior (&all_threads, delete_thread_info);
+  clear_inferiors ();
+}
+
 /* Kill all inferiors.  */
 static void
 win32_kill (void)
 {
-  win32_thread_info *current_thread;
-
   if (current_process_handle == NULL)
     return;
 
@@ -598,27 +656,18 @@ win32_kill (void)
        break;
       else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
        {
-         struct target_waitstatus our_status = { 0 };
+         struct target_waitstatus our_status = { 0 };
          handle_output_debug_string (&our_status);
-       }
+       }
     }
 
-  CloseHandle (current_process_handle);
-
-  current_thread = inferior_target_data (current_inferior);
-  if (current_thread && current_thread->h)
-    {
-      /* This may fail in an attached process, so don't check.  */
-      (void) CloseHandle (current_thread->h);
-    }
+  win32_clear_inferiors ();
 }
 
 /* Detach from all inferiors.  */
 static int
 win32_detach (void)
 {
-  HANDLE h;
-
   winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
   winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
 #ifdef _WIN32_WCE
@@ -633,33 +682,20 @@ win32_detach (void)
       || DebugActiveProcessStop == NULL)
     return -1;
 
-  /* We need a new handle, since DebugActiveProcessStop
-     closes all the ones that came through the events.  */
-  if ((h = OpenProcess (PROCESS_ALL_ACCESS,
-                       FALSE,
-                       current_process_id)) == NULL)
-    {
-      /* The process died.  */
-      return -1;
-    }
-
   {
     struct thread_resume resume;
     resume.thread = -1;
     resume.step = 0;
     resume.sig = 0;
-    resume.leave_stopped = 0;
-    win32_resume (&resume);
+    win32_resume (&resume, 1);
   }
 
   if (!DebugActiveProcessStop (current_process_id))
-    {
-      CloseHandle (h);
-      return -1;
-    }
+    return -1;
+
   DebugSetProcessKillOnExit (FALSE);
 
-  current_process_handle = h;
+  win32_clear_inferiors ();
   return 0;
 }
 
@@ -667,15 +703,14 @@ win32_detach (void)
 static void
 win32_join (void)
 {
-  if (current_process_id == 0
-      || current_process_handle == NULL)
-    return;
+  extern unsigned long signal_pid;
 
-  WaitForSingleObject (current_process_handle, INFINITE);
-  CloseHandle (current_process_handle);
-
-  current_process_handle = NULL;
-  current_process_id = 0;
+  HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, signal_pid);
+  if (h != NULL)
+    {
+      WaitForSingleObject (h, INFINITE);
+      CloseHandle (h);
+    }
 }
 
 /* Return 1 iff the thread with thread ID TID is alive.  */
@@ -696,7 +731,7 @@ win32_thread_alive (unsigned long tid)
 /* Resume the inferior process.  RESUME_INFO describes how we want
    to resume.  */
 static void
-win32_resume (struct thread_resume *resume_info)
+win32_resume (struct thread_resume *resume_info, size_t n)
 {
   DWORD tid;
   enum target_signal sig;
@@ -707,9 +742,9 @@ win32_resume (struct thread_resume *resume_info)
   /* This handles the very limited set of resume packets that GDB can
      currently produce.  */
 
-  if (resume_info[0].thread == -1)
+  if (n == 1 && resume_info[0].thread == -1)
     tid = -1;
-  else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped)
+  else if (n > 1)
     tid = -1;
   else
     /* Yes, we're ignoring resume_info[0].thread.  It'd be tricky to make
@@ -760,7 +795,7 @@ win32_resume (struct thread_resume *resume_info)
                       "in this configuration.\n");
            }
 
-         (*the_low_target.set_thread_context) (th, &current_event);
+         win32_set_thread_context (th);
          th->context.ContextFlags = 0;
        }
     }
@@ -1066,9 +1101,18 @@ handle_load_dll (void)
 
   dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
 
-  if (!psapi_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf)
-      && !toolhelp_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf))
-    dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
+  /* Windows does not report the image name of the dlls in the debug
+     event on attaches.  We resort to iterating over the list of
+     loaded dlls looking for a match by image base.  */
+  if (!psapi_get_dll_name ((DWORD) event->lpBaseOfDll, dll_buf))
+    {
+      if (!server_waiting)
+       /* On some versions of Windows and Windows CE, we can't create
+          toolhelp snapshots while the inferior is stopped in a
+          LOAD_DLL_DEBUG_EVENT due to a dll load, but we can while
+          Windows is reporting the already loaded dlls.  */
+       toolhelp_get_dll_name ((DWORD) event->lpBaseOfDll, dll_buf);
+    }
 
   dll_name = dll_buf;
 
@@ -1158,7 +1202,7 @@ handle_exception (struct target_waitstatus *ourstatus)
 #ifdef _WIN32_WCE
       /* Remove the initial breakpoint.  */
       check_breakpoints ((CORE_ADDR) (long) current_event
-                         .u.Exception.ExceptionRecord.ExceptionAddress);
+                        .u.Exception.ExceptionRecord.ExceptionAddress);
 #endif
       break;
     case DBG_CONTROL_C:
@@ -1202,19 +1246,108 @@ handle_exception (struct target_waitstatus *ourstatus)
   last_sig = ourstatus->value.sig;
 }
 
-/* Get the next event from the child.  */
+
 static void
-get_child_debug_event (struct target_waitstatus *ourstatus)
+suspend_one_thread (struct inferior_list_entry *entry)
 {
-  BOOL debug_event;
+  struct thread_info *thread = (struct thread_info *) entry;
+  win32_thread_info *th = inferior_target_data (thread);
 
+  if (!th->suspended)
+    {
+      if (SuspendThread (th->h) == (DWORD) -1)
+       {
+         DWORD err = GetLastError ();
+         OUTMSG (("warning: SuspendThread failed in suspend_one_thread, "
+                  "(error %d): %s\n", (int) err, strwinerror (err)));
+       }
+      else
+       th->suspended = 1;
+    }
+}
+
+static void
+fake_breakpoint_event (void)
+{
+  OUTMSG2(("fake_breakpoint_event\n"));
+
+  faked_breakpoint = 1;
+
+  memset (&current_event, 0, sizeof (current_event));
+  current_event.dwThreadId = main_thread_id;
+  current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
+  current_event.u.Exception.ExceptionRecord.ExceptionCode
+    = EXCEPTION_BREAKPOINT;
+
+  for_each_inferior (&all_threads, suspend_one_thread);
+}
+
+#ifdef _WIN32_WCE
+static int
+auto_delete_breakpoint (CORE_ADDR stop_pc)
+{
+  return 1;
+}
+#endif
+
+/* Get the next event from the child.  */
+
+static int
+get_child_debug_event (struct target_waitstatus *ourstatus)
+{
   last_sig = TARGET_SIGNAL_0;
   ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
 
-  /* Keep the wait time low enough for confortable remote interruption,
-     but high enough so gdbserver doesn't become a bottleneck.  */
-  if (!(debug_event = WaitForDebugEvent (&current_event, 250)))
-    return;
+  /* Check if GDB sent us an interrupt request.  */
+  check_remote_input_interrupt_request ();
+
+  if (soft_interrupt_requested)
+    {
+      soft_interrupt_requested = 0;
+      fake_breakpoint_event ();
+      goto gotevent;
+    }
+
+#ifndef _WIN32_WCE
+  attaching = 0;
+#else
+  if (attaching)
+    {
+      /* WinCE doesn't set an initial breakpoint automatically.  To
+        stop the inferior, we flush all currently pending debug
+        events -- the thread list and the dll list are always
+        reported immediatelly without delay, then, we suspend all
+        threads and pretend we saw a trap at the current PC of the
+        main thread.
+
+        Contrary to desktop Windows, Windows CE *does* report the dll
+        names on LOAD_DLL_DEBUG_EVENTs resulting from a
+        DebugActiveProcess call.  This limits the way we can detect
+        if all the dlls have already been reported.  If we get a real
+        debug event before leaving attaching, the worst that will
+        happen is the user will see a spurious breakpoint.  */
+
+      current_event.dwDebugEventCode = 0;
+      if (!WaitForDebugEvent (&current_event, 0))
+       {
+         OUTMSG2(("no attach events left\n"));
+         fake_breakpoint_event ();
+         attaching = 0;
+       }
+      else
+       OUTMSG2(("got attach event\n"));
+    }
+  else
+#endif
+    {
+      /* Keep the wait time low enough for confortable remote
+        interruption, but high enough so gdbserver doesn't become a
+        bottleneck.  */
+      if (!WaitForDebugEvent (&current_event, 250))
+       return 0;
+    }
+
+ gotevent:
 
   current_inferior =
     (struct thread_info *) find_inferior_id (&all_threads,
@@ -1260,12 +1393,16 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
 
       ourstatus->value.related_pid = current_event.dwThreadId;
 #ifdef _WIN32_WCE
-      /* Windows CE doesn't set the initial breakpoint automatically
-        like the desktop versions of Windows do.  We add it explicitly
-        here.  It will be removed as soon as it is hit.  */
-      set_breakpoint_at ((CORE_ADDR) (long) current_event.u
-                        .CreateProcessInfo.lpStartAddress,
-                        delete_breakpoint_at);
+      if (!attaching)
+       {
+         /* Windows CE doesn't set the initial breakpoint
+            automatically like the desktop versions of Windows do.
+            We add it explicitly here.  It will be removed as soon as
+            it is hit.  */
+         set_breakpoint_at ((CORE_ADDR) (long) current_event.u
+                            .CreateProcessInfo.lpStartAddress,
+                            auto_delete_breakpoint);
+       }
 #endif
       break;
 
@@ -1276,6 +1413,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
                (unsigned) current_event.dwThreadId));
       ourstatus->kind = TARGET_WAITKIND_EXITED;
       ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
+      child_continue (DBG_CONTINUE, -1);
       CloseHandle (current_process_handle);
       current_process_handle = NULL;
       break;
@@ -1331,46 +1469,36 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
   current_inferior =
     (struct thread_info *) find_inferior_id (&all_threads,
                                             current_event.dwThreadId);
+  return 1;
 }
 
 /* Wait for the inferior process to change state.
    STATUS will be filled in with a response code to send to GDB.
    Returns the signal which caused the process to stop. */
-static unsigned char
-win32_wait (char *status)
+static unsigned long
+win32_wait (struct target_waitstatus *ourstatus)
 {
-  struct target_waitstatus our_status;
-
-  *status = 'T';
-
   while (1)
     {
-      /* Check if GDB sent us an interrupt request.  */
-      check_remote_input_interrupt_request ();
+      if (!get_child_debug_event (ourstatus))
+       continue;
 
-      get_child_debug_event (&our_status);
-
-      switch (our_status.kind)
+      switch (ourstatus->kind)
        {
        case TARGET_WAITKIND_EXITED:
          OUTMSG2 (("Child exited with retcode = %x\n",
-                   our_status.value.integer));
-
-         *status = 'W';
-
-         child_fetch_inferior_registers (-1);
+                   ourstatus->value.integer));
 
-         return our_status.value.integer;
+         win32_clear_inferiors ();
+         return current_event.dwProcessId;
        case TARGET_WAITKIND_STOPPED:
-       case TARGET_WAITKIND_LOADED:
+       case TARGET_WAITKIND_LOADED:
          OUTMSG2 (("Child Stopped with signal = %d \n",
                    our_status.value.sig));
 
-         *status = 'T';
-
          child_fetch_inferior_registers (-1);
 
-         if (our_status.kind == TARGET_WAITKIND_LOADED
+         if (ourstatus->kind == TARGET_WAITKIND_LOADED
              && !server_waiting)
            {
              /* When gdb connects, we want to be stopped at the
@@ -1379,12 +1507,17 @@ win32_wait (char *status)
              break;
            }
 
-         return our_status.value.sig;
-       default:
-         OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind));
-         /* fall-through */
-       case TARGET_WAITKIND_SPURIOUS:
-       case TARGET_WAITKIND_EXECD:
+         /* We don't expose _LOADED events to gdbserver core.  See
+            the `dlls_changed' global.  */
+         if (ourstatus->kind == TARGET_WAITKIND_LOADED)
+           ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+         return current_event.dwThreadId;
+       default:
+         OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
+         /* fall-through */
+       case TARGET_WAITKIND_SPURIOUS:
+       case TARGET_WAITKIND_EXECD:
          /* do nothing, just continue */
          child_continue (DBG_CONTINUE, -1);
          break;
@@ -1458,14 +1591,68 @@ win32_request_interrupt (void)
       && DebugBreakProcess (current_process_handle))
     return;
 
-  OUTMSG (("Could not interrupt process.\n"));
+  /* Last resort, suspend all threads manually.  */
+  soft_interrupt_requested = 1;
+}
+
+#ifdef _WIN32_WCE
+int
+win32_error_to_fileio_error (DWORD err)
+{
+  switch (err)
+    {
+    case ERROR_BAD_PATHNAME:
+    case ERROR_FILE_NOT_FOUND:
+    case ERROR_INVALID_NAME:
+    case ERROR_PATH_NOT_FOUND:
+      return FILEIO_ENOENT;
+    case ERROR_CRC:
+    case ERROR_IO_DEVICE:
+    case ERROR_OPEN_FAILED:
+      return FILEIO_EIO;
+    case ERROR_INVALID_HANDLE:
+      return FILEIO_EBADF;
+    case ERROR_ACCESS_DENIED:
+    case ERROR_SHARING_VIOLATION:
+      return FILEIO_EACCES;
+    case ERROR_NOACCESS:
+      return FILEIO_EFAULT;
+    case ERROR_BUSY:
+      return FILEIO_EBUSY;
+    case ERROR_ALREADY_EXISTS:
+    case ERROR_FILE_EXISTS:
+      return FILEIO_EEXIST;
+    case ERROR_BAD_DEVICE:
+      return FILEIO_ENODEV;
+    case ERROR_DIRECTORY:
+      return FILEIO_ENOTDIR;
+    case ERROR_FILENAME_EXCED_RANGE:
+    case ERROR_INVALID_DATA:
+    case ERROR_INVALID_PARAMETER:
+    case ERROR_NEGATIVE_SEEK:
+      return FILEIO_EINVAL;
+    case ERROR_TOO_MANY_OPEN_FILES:
+      return FILEIO_EMFILE;
+    case ERROR_HANDLE_DISK_FULL:
+    case ERROR_DISK_FULL:
+      return FILEIO_ENOSPC;
+    case ERROR_WRITE_PROTECT:
+      return FILEIO_EROFS;
+    case ERROR_NOT_SUPPORTED:
+      return FILEIO_ENOSYS;
+    }
+
+  return FILEIO_EUNKNOWN;
 }
 
-static const char *
-win32_arch_string (void)
+static void
+wince_hostio_last_error (char *buf)
 {
-  return the_low_target.arch_string;
+  DWORD winerr = GetLastError ();
+  int fileio_err = win32_error_to_fileio_error (winerr);
+  sprintf (buf, "F-1,%x", fileio_err);
 }
+#endif
 
 static struct target_ops win32_target_ops = {
   win32_create_inferior,
@@ -1489,7 +1676,12 @@ static struct target_ops win32_target_ops = {
   NULL,
   NULL,
   NULL,
-  win32_arch_string
+  NULL,
+#ifdef _WIN32_WCE
+  wince_hostio_last_error,
+#else
+  hostio_last_error_from_errno,
+#endif
 };
 
 /* Initialize the Win32 backend.  */
@@ -1500,5 +1692,5 @@ initialize_low (void)
   if (the_low_target.breakpoint != NULL)
     set_breakpoint_data (the_low_target.breakpoint,
                         the_low_target.breakpoint_len);
-  init_registers ();
+  the_low_target.arch_setup ();
 }
This page took 0.03459 seconds and 4 git commands to generate.