]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdbserver/win32-low.cc
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdbserver / win32-low.cc
index 9c715fea3622741292fb7b29703f3756e3a56107..756507709d81971ef59fcaddc9768a37679ca1d4 100644 (file)
@@ -1,5 +1,5 @@
 /* Low level interface to Windows debugging, for gdbserver.
-   Copyright (C) 2006-2020 Free Software Foundation, Inc.
+   Copyright (C) 2006-2023 Free Software Foundation, Inc.
 
    Contributed by Leo Zayas.  Based on "win32-nat.c" from GDB.
 
@@ -20,7 +20,7 @@
 
 #include "server.h"
 #include "regcache.h"
-#include "gdb/fileio.h"
+#include "gdbsupport/fileio.h"
 #include "mem-break.h"
 #include "win32-low.h"
 #include "gdbthread.h"
 #include "gdbsupport/common-inferior.h"
 #include "gdbsupport/gdb_wait.h"
 
+using namespace windows_nat;
+
+/* See win32-low.h.  */
+gdbserver_windows_process windows_process;
+
 #ifndef USE_WIN32API
 #include <sys/cygwin.h>
 #endif
 #define _T(x) TEXT (x)
 #endif
 
-#ifndef COUNTOF
-#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
-#endif
-
-#ifdef _WIN32_WCE
-# define GETPROCADDRESS(DLL, PROC) \
-  ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC)))
-#else
-# define GETPROCADDRESS(DLL, PROC) \
-  ((winapi_ ## PROC) GetProcAddress (DLL, #PROC))
-#endif
-
 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 EXCEPTION_RECORD siginfo_er;    /* Contents of $_siginfo */
-static enum gdb_signal last_sig = GDB_SIGNAL_0;
-
-/* The current debug event from WaitForDebugEvent.  */
-static DEBUG_EVENT current_event;
-
-/* A status that hasn't been reported to the core yet, and so
-   win32_wait should return it next, instead of fetching the next
-   debug event off the win32 API.  */
-static struct target_waitstatus cached_status;
-
-/* 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;
-
 const struct target_desc *win32_tdesc;
-
-#define NUM_REGS (the_low_target.num_regs)
-
-typedef BOOL (WINAPI *winapi_DebugActiveProcessStop) (DWORD dwProcessId);
-typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
-typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE);
-typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
-
-#ifndef _WIN32_WCE
-static void win32_add_all_dlls (void);
+#ifdef __x86_64__
+const struct target_desc *wow64_win32_tdesc;
 #endif
 
+#define NUM_REGS (the_low_target.num_regs ())
+
 /* Get the thread ID from the current selected inferior (the current
    thread).  */
 static ptid_t
@@ -125,32 +88,26 @@ debug_event_ptid (DEBUG_EVENT *event)
 /* Get the thread context of the thread associated with TH.  */
 
 static void
-win32_get_thread_context (win32_thread_info *th)
+win32_get_thread_context (windows_thread_info *th)
 {
-  memset (&th->context, 0, sizeof (CONTEXT));
-  (*the_low_target.get_thread_context) (th);
-#ifdef _WIN32_WCE
-  memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
+#ifdef __x86_64__
+  if (windows_process.wow64_process)
+    memset (&th->wow64_context, 0, sizeof (WOW64_CONTEXT));
+  else
 #endif
+    memset (&th->context, 0, sizeof (CONTEXT));
+  (*the_low_target.get_thread_context) (th);
 }
 
 /* Set the thread context of the thread associated with TH.  */
 
 static void
-win32_set_thread_context (win32_thread_info *th)
+win32_set_thread_context (windows_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 likely 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)
+#ifdef __x86_64__
+  if (windows_process.wow64_process)
+    Wow64SetThreadContext (th->h, &th->wow64_context);
+  else
 #endif
     SetThreadContext (th->h, &th->context);
 }
@@ -158,7 +115,7 @@ win32_set_thread_context (win32_thread_info *th)
 /* Set the thread context of the thread associated with TH.  */
 
 static void
-win32_prepare_to_resume (win32_thread_info *th)
+win32_prepare_to_resume (windows_thread_info *th)
 {
   if (the_low_target.prepare_to_resume != NULL)
     (*the_low_target.prepare_to_resume) (th);
@@ -167,55 +124,56 @@ win32_prepare_to_resume (win32_thread_info *th)
 /* See win32-low.h.  */
 
 void
-win32_require_context (win32_thread_info *th)
+win32_require_context (windows_thread_info *th)
 {
-  if (th->context.ContextFlags == 0)
+  DWORD context_flags;
+#ifdef __x86_64__
+  if (windows_process.wow64_process)
+    context_flags = th->wow64_context.ContextFlags;
+  else
+#endif
+    context_flags = th->context.ContextFlags;
+  if (context_flags == 0)
     {
-      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;
-       }
-
+      th->suspend ();
       win32_get_thread_context (th);
     }
 }
 
-/* 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 *
-thread_rec (ptid_t ptid, int get_context)
+/* See nat/windows-nat.h.  */
+
+windows_thread_info *
+gdbserver_windows_process::thread_rec
+     (ptid_t ptid, thread_disposition_type disposition)
 {
   thread_info *thread = find_thread_ptid (ptid);
   if (thread == NULL)
     return NULL;
 
-  win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
-  if (get_context)
+  windows_thread_info *th = (windows_thread_info *) thread_target_data (thread);
+  if (disposition != DONT_INVALIDATE_CONTEXT)
     win32_require_context (th);
   return th;
 }
 
 /* Add a thread to the thread list.  */
-static win32_thread_info *
+static windows_thread_info *
 child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
 {
-  win32_thread_info *th;
+  windows_thread_info *th;
   ptid_t ptid = ptid_t (pid, tid, 0);
 
-  if ((th = thread_rec (ptid, FALSE)))
+  if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT)))
     return th;
 
-  th = XCNEW (win32_thread_info);
-  th->tid = tid;
-  th->h = h;
-  th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
+  CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
+#ifdef __x86_64__
+  /* For WOW64 processes, this is actually the pointer to the 64bit TIB,
+     and the 32bit TIB is exactly 2 pages after it.  */
+  if (windows_process.wow64_process)
+    base += 2 * 4096; /* page size = 4096 */
+#endif
+  th = new windows_thread_info (tid, h, base);
 
   add_thread (ptid, th);
 
@@ -229,11 +187,10 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
 static void
 delete_thread_info (thread_info *thread)
 {
-  win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+  windows_thread_info *th = (windows_thread_info *) thread_target_data (thread);
 
   remove_thread (thread);
-  CloseHandle (th->h);
-  free (th);
+  delete th;
 }
 
 /* Delete a thread from the list of threads.  */
@@ -257,15 +214,18 @@ child_delete_thread (DWORD pid, DWORD tid)
 bool
 win32_process_target::supports_z_point_type (char z_type)
 {
-  return (the_low_target.supports_z_point_type != NULL
-         && the_low_target.supports_z_point_type (z_type));
+  return (z_type == Z_PACKET_SW_BP
+         || (the_low_target.supports_z_point_type != NULL
+             && the_low_target.supports_z_point_type (z_type)));
 }
 
 int
 win32_process_target::insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
                                    int size, raw_breakpoint *bp)
 {
-  if (the_low_target.insert_point != NULL)
+  if (type == raw_bkpt_type_sw)
+    return insert_memory_breakpoint (bp);
+  else if (the_low_target.insert_point != NULL)
     return the_low_target.insert_point (type, addr, size, bp);
   else
     /* Unsupported (see target.h).  */
@@ -276,7 +236,9 @@ int
 win32_process_target::remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
                                    int size, raw_breakpoint *bp)
 {
-  if (the_low_target.remove_point != NULL)
+  if (type == raw_bkpt_type_sw)
+    return remove_memory_breakpoint (bp);
+  else if (the_low_target.remove_point != NULL)
     return the_low_target.remove_point (type, addr, size, bp);
   else
     /* Unsupported (see target.h).  */
@@ -314,15 +276,15 @@ child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
 
   if (write)
     {
-      success = WriteProcessMemory (current_process_handle, (LPVOID) addr,
+      success = WriteProcessMemory (windows_process.handle, (LPVOID) addr,
                                    (LPCVOID) our, len, &done);
       if (!success)
        lasterror = GetLastError ();
-      FlushInstructionCache (current_process_handle, (LPCVOID) addr, len);
+      FlushInstructionCache (windows_process.handle, (LPCVOID) addr, len);
     }
   else
     {
-      success = ReadProcessMemory (current_process_handle, (LPCVOID) addr,
+      success = ReadProcessMemory (windows_process.handle, (LPCVOID) addr,
                                   (LPVOID) our, len, &done);
       if (!success)
        lasterror = GetLastError ();
@@ -341,35 +303,55 @@ child_init_thread_list (void)
   for_each_thread (delete_thread_info);
 }
 
-/* Zero during the child initialization phase, and nonzero otherwise.  */
-
-static int child_initialization_done = 0;
-
 static void
 do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
 {
   struct process_info *proc;
 
-  last_sig = GDB_SIGNAL_0;
+  windows_process.last_sig = GDB_SIGNAL_0;
+  windows_process.handle = proch;
+  windows_process.main_thread_id = 0;
 
-  current_process_handle = proch;
-  current_process_id = pid;
-  main_thread_id = 0;
+  windows_process.soft_interrupt_requested = 0;
+  windows_process.faked_breakpoint = 0;
+  windows_process.open_process_used = true;
 
-  soft_interrupt_requested = 0;
-  faked_breakpoint = 0;
+  memset (&windows_process.current_event, 0,
+         sizeof (windows_process.current_event));
 
-  memset (&current_event, 0, sizeof (current_event));
+#ifdef __x86_64__
+  BOOL wow64;
+  if (!IsWow64Process (proch, &wow64))
+    {
+      DWORD err = GetLastError ();
+      error ("Check if WOW64 process failed (error %d): %s\n",
+            (int) err, strwinerror (err));
+    }
+  windows_process.wow64_process = wow64;
+
+  if (windows_process.wow64_process
+      && (Wow64GetThreadContext == nullptr
+         || Wow64SetThreadContext == nullptr))
+    error ("WOW64 debugging is not supported on this system.\n");
+
+  windows_process.ignore_first_breakpoint
+    = !attached && windows_process.wow64_process;
+#endif
 
   proc = add_process (pid, attached);
-  proc->tdesc = win32_tdesc;
+#ifdef __x86_64__
+  if (windows_process.wow64_process)
+    proc->tdesc = wow64_win32_tdesc;
+  else
+#endif
+    proc->tdesc = win32_tdesc;
   child_init_thread_list ();
-  child_initialization_done = 0;
+  windows_process.child_initialization_done = 0;
 
   if (the_low_target.initial_stuff != NULL)
     (*the_low_target.initial_stuff) ();
 
-  cached_status.kind = TARGET_WAITKIND_IGNORE;
+  windows_process.cached_status.set_ignore ();
 
   /* Flush all currently pending debug events (thread and dll list) up
      to the initial breakpoint.  */
@@ -377,12 +359,12 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
     {
       struct target_waitstatus status;
 
-      the_target->pt->wait (minus_one_ptid, &status, 0);
+      the_target->wait (minus_one_ptid, &status, 0);
 
       /* Note win32_wait doesn't return thread events.  */
-      if (status.kind != TARGET_WAITKIND_LOADED)
+      if (status.kind () != TARGET_WAITKIND_LOADED)
        {
-         cached_status = status;
+         windows_process.cached_status = status;
          break;
        }
 
@@ -393,11 +375,10 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
        resume.kind = resume_continue;
        resume.sig = 0;
 
-       the_target->pt->resume (&resume, 1);
+       the_target->resume (&resume, 1);
       }
     }
 
-#ifndef _WIN32_WCE
   /* Now that the inferior has been started and all DLLs have been mapped,
      we can iterate over all DLLs and load them in.
 
@@ -413,10 +394,9 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
      Rather than try to work around this sort of issue, it is much
      simpler to just ignore DLL load/unload events during the startup
      phase, and then process them all in one batch now.  */
-  win32_add_all_dlls ();
-#endif
+  windows_process.add_all_dlls ();
 
-  child_initialization_done = 1;
+  windows_process.child_initialization_done = 1;
 }
 
 /* Resume all artificially suspended threads if we are continuing
@@ -424,7 +404,7 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
 static void
 continue_one_thread (thread_info *thread, int thread_id)
 {
-  win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+  windows_thread_info *th = (windows_thread_info *) thread_target_data (thread);
 
   if (thread_id == -1 || thread_id == th->tid)
     {
@@ -432,19 +412,20 @@ continue_one_thread (thread_info *thread, int thread_id)
 
       if (th->suspended)
        {
-         if (th->context.ContextFlags)
+         DWORD *context_flags;
+#ifdef __x86_64__
+         if (windows_process.wow64_process)
+           context_flags = &th->wow64_context.ContextFlags;
+         else
+#endif
+           context_flags = &th->context.ContextFlags;
+         if (*context_flags)
            {
              win32_set_thread_context (th);
-             th->context.ContextFlags = 0;
+             *context_flags = 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;
+         th->resume ();
        }
     }
 }
@@ -452,20 +433,19 @@ continue_one_thread (thread_info *thread, int thread_id)
 static BOOL
 child_continue (DWORD continue_status, int thread_id)
 {
+  windows_process.desired_stop_thread_id = thread_id;
+  if (windows_process.matching_pending_stop (debug_threads))
+    return TRUE;
+
   /* The inferior will only continue after the ContinueDebugEvent
      call.  */
   for_each_thread ([&] (thread_info *thread)
     {
       continue_one_thread (thread, thread_id);
     });
-  faked_breakpoint = 0;
+  windows_process.faked_breakpoint = 0;
 
-  if (!ContinueDebugEvent (current_event.dwProcessId,
-                          current_event.dwThreadId,
-                          continue_status))
-    return FALSE;
-
-  return TRUE;
+  return continue_last_debug_event (continue_status, debug_threads);
 }
 
 /* Fetch register(s) from the current thread context.  */
@@ -473,7 +453,9 @@ static void
 child_fetch_inferior_registers (struct regcache *regcache, int r)
 {
   int regno;
-  win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+  windows_thread_info *th
+    = windows_process.thread_rec (current_thread_ptid (),
+                                 INVALIDATE_CONTEXT);
   if (r == -1 || r > NUM_REGS)
     child_fetch_inferior_registers (regcache, NUM_REGS);
   else
@@ -487,7 +469,9 @@ static void
 child_store_inferior_registers (struct regcache *regcache, int r)
 {
   int regno;
-  win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+  windows_thread_info *th
+    = windows_process.thread_rec (current_thread_ptid (),
+                                 INVALIDATE_CONTEXT);
   if (r == -1 || r == 0 || r > NUM_REGS)
     child_store_inferior_registers (regcache, NUM_REGS);
   else
@@ -495,132 +479,34 @@ child_store_inferior_registers (struct regcache *regcache, int r)
       (*the_low_target.store_inferior_register) (regcache, th, regno);
 }
 
-/* Map the Windows error number in ERROR to a locale-dependent error
-   message string and return a pointer to it.  Typically, the values
-   for ERROR come from GetLastError.
-
-   The string pointed to shall not be modified by the application,
-   but may be overwritten by a subsequent call to strwinerror
-
-   The strwinerror function does not change the current setting
-   of GetLastError.  */
-
-char *
-strwinerror (DWORD error)
-{
-  static char buf[1024];
-  TCHAR *msgbuf;
-  DWORD lasterr = GetLastError ();
-  DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
-                              | FORMAT_MESSAGE_ALLOCATE_BUFFER,
-                              NULL,
-                              error,
-                              0, /* Default language */
-                              (LPTSTR) &msgbuf,
-                              0,
-                              NULL);
-  if (chars != 0)
-    {
-      /* If there is an \r\n appended, zap it.  */
-      if (chars >= 2
-         && msgbuf[chars - 2] == '\r'
-         && msgbuf[chars - 1] == '\n')
-       {
-         chars -= 2;
-         msgbuf[chars] = 0;
-       }
-
-      if (chars > ((COUNTOF (buf)) - 1))
-       {
-         chars = COUNTOF (buf) - 1;
-         msgbuf [chars] = 0;
-       }
-
-#ifdef UNICODE
-      wcstombs (buf, msgbuf, chars + 1);
-#else
-      strncpy (buf, msgbuf, chars + 1);
-#endif
-      LocalFree (msgbuf);
-    }
-  else
-    sprintf (buf, "unknown win32 error (%u)", (unsigned) error);
-
-  SetLastError (lasterr);
-  return buf;
-}
-
 static BOOL
 create_process (const char *program, char *args,
                DWORD flags, PROCESS_INFORMATION *pi)
 {
-  const char *inferior_cwd = get_inferior_cwd ();
+  const std::string &inferior_cwd = get_inferior_cwd ();
   BOOL ret;
   size_t argslen, proglen;
 
   proglen = strlen (program) + 1;
   argslen = strlen (args) + proglen;
 
-#ifdef _WIN32_WCE
-  wchar_t *p, *wprogram, *wargs, *wcwd = NULL;
-
-  wprogram = (wchar_t *) alloca (proglen * sizeof (wchar_t));
-  mbstowcs (wprogram, program, proglen);
-
-  for (p = wprogram; *p; ++p)
-    if (L'/' == *p)
-      *p = L'\\';
-
-  wargs = alloca ((argslen + 1) * sizeof (wchar_t));
-  wcscpy (wargs, wprogram);
-  wcscat (wargs, L" ");
-  mbstowcs (wargs + proglen, args, argslen + 1 - proglen);
-
-  if (inferior_cwd != NULL)
-    {
-      std::string expanded_infcwd = gdb_tilde_expand (inferior_cwd);
-      std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
-                   '/', '\\');
-      wcwd = alloca ((expanded_infcwd.size () + 1) * sizeof (wchar_t));
-      if (mbstowcs (wcwd, expanded_infcwd.c_str (),
-                   expanded_infcwd.size () + 1) == NULL)
-       {
-         error (_("\
-Could not convert the expanded inferior cwd to wide-char."));
-       }
-    }
-
-  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 */
-                       wcwd,     /* current directory */
-                       NULL,     /* start info, not supported */
-                       pi);      /* proc info */
-#else
   STARTUPINFOA si = { sizeof (STARTUPINFOA) };
   char *program_and_args = (char *) alloca (argslen + 1);
 
   strcpy (program_and_args, program);
   strcat (program_and_args, " ");
   strcat (program_and_args, args);
-  ret = CreateProcessA (program,           /* image name */
+  ret = create_process (program,           /* image name */
                        program_and_args,  /* command line */
-                       NULL,              /* security */
-                       NULL,              /* thread */
-                       TRUE,              /* inherit handles */
                        flags,             /* start flags */
                        NULL,              /* environment */
                        /* current directory */
-                       (inferior_cwd == NULL
+                       (inferior_cwd.empty ()
                         ? NULL
-                        : gdb_tilde_expand (inferior_cwd).c_str()),
+                        : gdb_tilde_expand (inferior_cwd.c_str ()).c_str()),
+                       get_client_state ().disable_randomization,
                        &si,               /* start info */
                        pi);               /* proc info */
-#endif
 
   return ret;
 }
@@ -643,11 +529,11 @@ win32_process_target::create_inferior (const char *program,
   DWORD flags;
   PROCESS_INFORMATION pi;
   DWORD err;
-  std::string str_program_args = stringify_argv (program_args);
+  std::string str_program_args = construct_inferior_arguments (program_args);
   char *args = (char *) str_program_args.c_str ();
 
   /* win32_wait needs to know we're not attaching.  */
-  attaching = 0;
+  windows_process.attaching = 0;
 
   if (!program)
     error ("No executable specified, specify executable to debug.\n");
@@ -701,23 +587,18 @@ win32_process_target::create_inferior (const char *program,
       OUTMSG2 (("Process created: %s %s\n", program, (char *) args));
     }
 
-#ifndef _WIN32_WCE
-  /* On Windows CE this handle can't be closed.  The OS reuses
-     it in the debug events, while the 9x/NT versions of Windows
-     probably use a DuplicateHandle'd one.  */
   CloseHandle (pi.hThread);
-#endif
 
   do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0);
 
   /* Wait till we are at 1st instruction in program, return new pid
      (assuming success).  */
-  cs.last_ptid = wait (ptid_t (current_process_id), &cs.last_status, 0);
+  cs.last_ptid = wait (ptid_t (pi.dwProcessId), &cs.last_status, 0);
 
   /* Necessary for handle_v_kill.  */
-  signal_pid = current_process_id;
+  signal_pid = pi.dwProcessId;
 
-  return current_process_id;
+  return pi.dwProcessId;
 }
 
 /* Attach to a running process.
@@ -727,25 +608,17 @@ int
 win32_process_target::attach (unsigned long pid)
 {
   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
-  DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
 
   h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
   if (h != NULL)
     {
       if (DebugActiveProcess (pid))
        {
-         if (DebugSetProcessKillOnExit != NULL)
-           DebugSetProcessKillOnExit (FALSE);
+         DebugSetProcessKillOnExit (FALSE);
 
          /* win32_wait needs to know we're attaching.  */
-         attaching = 1;
+         windows_process.attaching = 1;
          do_initial_child_stuff (h, pid, 1);
          return 0;
        }
@@ -758,9 +631,11 @@ win32_process_target::attach (unsigned long pid)
         (int) err, strwinerror (err));
 }
 
-/* Handle OUTPUT_DEBUG_STRING_EVENT from child process.  */
-static void
-handle_output_debug_string (void)
+/* See nat/windows-nat.h.  */
+
+int
+gdbserver_windows_process::handle_output_debug_string
+     (struct target_waitstatus *ourstatus)
 {
 #define READ_BUFFER_LEN 1024
   CORE_ADDR addr;
@@ -768,7 +643,7 @@ handle_output_debug_string (void)
   DWORD nbytes = current_event.u.DebugString.nDebugStringLength;
 
   if (nbytes == 0)
-    return;
+    return 0;
 
   if (nbytes > READ_BUFFER_LEN)
     nbytes = READ_BUFFER_LEN;
@@ -781,13 +656,13 @@ handle_output_debug_string (void)
         in Unicode.  */
       WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 };
       if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0)
-       return;
+       return 0;
       wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR));
     }
   else
     {
       if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0)
-       return;
+       return 0;
     }
 
   if (!startswith (s, "cYg"))
@@ -795,22 +670,27 @@ handle_output_debug_string (void)
       if (!server_waiting)
        {
          OUTMSG2(("%s", s));
-         return;
+         return 0;
        }
 
       monitor_output (s);
     }
 #undef READ_BUFFER_LEN
+
+  return 0;
 }
 
 static void
 win32_clear_inferiors (void)
 {
-  if (current_process_handle != NULL)
-    CloseHandle (current_process_handle);
+  if (windows_process.open_process_used)
+    {
+      CloseHandle (windows_process.handle);
+      windows_process.open_process_used = false;
+    }
 
   for_each_thread (delete_thread_info);
-  siginfo_er.ExceptionCode = 0;
+  windows_process.siginfo_er.ExceptionCode = 0;
   clear_inferiors ();
 }
 
@@ -819,17 +699,19 @@ win32_clear_inferiors (void)
 int
 win32_process_target::kill (process_info *process)
 {
-  TerminateProcess (current_process_handle, 0);
+  TerminateProcess (windows_process.handle, 0);
   for (;;)
     {
       if (!child_continue (DBG_CONTINUE, -1))
        break;
-      if (!WaitForDebugEvent (&current_event, INFINITE))
+      if (!wait_for_debug_event (&windows_process.current_event, INFINITE))
        break;
-      if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
+      if (windows_process.current_event.dwDebugEventCode
+         == EXIT_PROCESS_DEBUG_EVENT)
        break;
-      else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
-       handle_output_debug_string ();
+      else if (windows_process.current_event.dwDebugEventCode
+              == OUTPUT_DEBUG_STRING_EVENT)
+       windows_process.handle_output_debug_string (nullptr);
     }
 
   win32_clear_inferiors ();
@@ -843,29 +725,13 @@ win32_process_target::kill (process_info *process)
 int
 win32_process_target::detach (process_info *process)
 {
-  winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
-  winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
-#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 (DebugSetProcessKillOnExit == NULL
-      || DebugActiveProcessStop == NULL)
-    return -1;
+  struct thread_resume resume;
+  resume.thread = minus_one_ptid;
+  resume.kind = resume_continue;
+  resume.sig = 0;
+  this->resume (&resume, 1);
 
-  {
-    struct thread_resume resume;
-    resume.thread = minus_one_ptid;
-    resume.kind = resume_continue;
-    resume.sig = 0;
-    this->resume (&resume, 1);
-  }
-
-  if (!DebugActiveProcessStop (current_process_id))
+  if (!DebugActiveProcessStop (process->pid))
     return -1;
 
   DebugSetProcessKillOnExit (FALSE);
@@ -911,7 +777,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
   DWORD tid;
   enum gdb_signal sig;
   int step;
-  win32_thread_info *th;
+  windows_thread_info *th;
   DWORD continue_status = DBG_CONTINUE;
   ptid_t ptid;
 
@@ -925,7 +791,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
   else
     /* Yes, we're ignoring resume_info[0].thread.  It'd be tricky to make
        the Windows resume code do the right thing for thread switching.  */
-    tid = current_event.dwThreadId;
+    tid = windows_process.current_event.dwThreadId;
 
   if (resume_info[0].thread != minus_one_ptid)
     {
@@ -940,28 +806,36 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
 
   if (sig != GDB_SIGNAL_0)
     {
-      if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
+      if (windows_process.current_event.dwDebugEventCode
+         != EXCEPTION_DEBUG_EVENT)
        {
          OUTMSG (("Cannot continue with signal %s here.\n",
                   gdb_signal_to_string (sig)));
        }
-      else if (sig == last_sig)
+      else if (sig == windows_process.last_sig)
        continue_status = DBG_EXCEPTION_NOT_HANDLED;
       else
        OUTMSG (("Can only continue with received signal %s.\n",
-                gdb_signal_to_string (last_sig)));
+                gdb_signal_to_string (windows_process.last_sig)));
     }
 
-  last_sig = GDB_SIGNAL_0;
+  windows_process.last_sig = GDB_SIGNAL_0;
 
   /* Get context for the currently selected thread.  */
-  ptid = debug_event_ptid (&current_event);
-  th = thread_rec (ptid, FALSE);
+  ptid = debug_event_ptid (&windows_process.current_event);
+  th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
   if (th)
     {
       win32_prepare_to_resume (th);
 
-      if (th->context.ContextFlags)
+      DWORD *context_flags;
+#ifdef __x86_64__
+      if (windows_process.wow64_process)
+       context_flags = &th->wow64_context.ContextFlags;
+      else
+#endif
+       context_flags = &th->context.ContextFlags;
+      if (*context_flags)
        {
          /* Move register values from the inferior into the thread
             context structure.  */
@@ -977,7 +851,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
            }
 
          win32_set_thread_context (th);
-         th->context.ContextFlags = 0;
+         *context_flags = 0;
        }
     }
 
@@ -987,21 +861,18 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
   child_continue (continue_status, tid);
 }
 
-static void
-win32_add_one_solib (const char *name, CORE_ADDR load_addr)
+/* See nat/windows-nat.h.  */
+
+void
+gdbserver_windows_process::handle_load_dll (const char *name, LPVOID base)
 {
+  CORE_ADDR load_addr = (CORE_ADDR) (uintptr_t) base;
+
   char buf[MAX_PATH + 1];
   char buf2[MAX_PATH + 1];
 
-#ifdef _WIN32_WCE
-  WIN32_FIND_DATA w32_fd;
-  WCHAR wname[MAX_PATH + 1];
-  mbstowcs (wname, name, MAX_PATH);
-  HANDLE h = FindFirstFile (wname, &w32_fd);
-#else
   WIN32_FIND_DATAA w32_fd;
   HANDLE h = FindFirstFileA (name, &w32_fd);
-#endif
 
   /* The symbols in a dll are offset by 0x1000, which is the
      offset from 0 of the first byte in an image - because
@@ -1014,7 +885,6 @@ win32_add_one_solib (const char *name, CORE_ADDR load_addr)
     {
       FindClose (h);
       strcpy (buf, name);
-#ifndef _WIN32_WCE
       {
        char cwd[MAX_PATH + 1];
        char *p;
@@ -1028,16 +898,13 @@ win32_add_one_solib (const char *name, CORE_ADDR load_addr)
            SetCurrentDirectoryA (cwd);
          }
       }
-#endif
     }
 
-#ifndef _WIN32_WCE
   if (strcasecmp (buf, "ntdll.dll") == 0)
     {
       GetSystemDirectoryA (buf, sizeof (buf));
       strcat (buf, "\\ntdll.dll");
     }
-#endif
 
 #ifdef __CYGWIN__
   cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2));
@@ -1048,182 +915,10 @@ win32_add_one_solib (const char *name, CORE_ADDR load_addr)
   loaded_dll (buf2, load_addr);
 }
 
-static char *
-get_image_name (HANDLE h, void *address, int unicode)
-{
-  static char buf[(2 * MAX_PATH) + 1];
-  DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
-  char *address_ptr;
-  int len = 0;
-  char b[2];
-  SIZE_T done;
-
-  /* Attempt to read the name of the dll that was detected.
-     This is documented to work only when actively debugging
-     a program.  It will not work for attached processes. */
-  if (address == NULL)
-    return NULL;
-
-#ifdef _WIN32_WCE
-  /* Windows CE reports the address of the image name,
-     instead of an address of a pointer into the image name.  */
-  address_ptr = address;
-#else
-  /* See if we could read the address of a string, and that the
-     address isn't null. */
-  if (!ReadProcessMemory (h, address,  &address_ptr,
-                         sizeof (address_ptr), &done)
-      || done != sizeof (address_ptr)
-      || !address_ptr)
-    return NULL;
-#endif
-
-  /* Find the length of the string */
-  while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
-        && (b[0] != 0 || b[size - 1] != 0) && done == size)
-    continue;
-
-  if (!unicode)
-    ReadProcessMemory (h, address_ptr, buf, len, &done);
-  else
-    {
-      WCHAR *unicode_address = XALLOCAVEC (WCHAR, len);
-      ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
-                        &done);
-
-      WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
-    }
-
-  return buf;
-}
-
-typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
-                                                 DWORD, LPDWORD);
-typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
-                                                   LPMODULEINFO, DWORD);
-typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
-                                                    LPSTR, DWORD);
-
-static winapi_EnumProcessModules win32_EnumProcessModules;
-static winapi_GetModuleInformation win32_GetModuleInformation;
-static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
-
-static BOOL
-load_psapi (void)
-{
-  static int psapi_loaded = 0;
-  static HMODULE dll = NULL;
-
-  if (!psapi_loaded)
-    {
-      psapi_loaded = 1;
-      dll = LoadLibrary (TEXT("psapi.dll"));
-      if (!dll)
-       return FALSE;
-      win32_EnumProcessModules =
-             GETPROCADDRESS (dll, EnumProcessModules);
-      win32_GetModuleInformation =
-             GETPROCADDRESS (dll, GetModuleInformation);
-      win32_GetModuleFileNameExA =
-             GETPROCADDRESS (dll, GetModuleFileNameExA);
-    }
-
-  return (win32_EnumProcessModules != NULL
-         && win32_GetModuleInformation != NULL
-         && win32_GetModuleFileNameExA != NULL);
-}
-
-#ifndef _WIN32_WCE
-
-/* Iterate over all DLLs currently mapped by our inferior, and
-   add them to our list of solibs.  */
-
-static void
-win32_add_all_dlls (void)
-{
-  size_t i;
-  HMODULE dh_buf[1];
-  HMODULE *DllHandle = dh_buf;
-  DWORD cbNeeded;
-  BOOL ok;
-
-  if (!load_psapi ())
-    return;
-
-  cbNeeded = 0;
-  ok = (*win32_EnumProcessModules) (current_process_handle,
-                                   DllHandle,
-                                   sizeof (HMODULE),
-                                   &cbNeeded);
-
-  if (!ok || !cbNeeded)
-    return;
-
-  DllHandle = (HMODULE *) alloca (cbNeeded);
-  if (!DllHandle)
-    return;
-
-  ok = (*win32_EnumProcessModules) (current_process_handle,
-                                   DllHandle,
-                                   cbNeeded,
-                                   &cbNeeded);
-  if (!ok)
-    return;
-
-  for (i = 1; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
-    {
-      MODULEINFO mi;
-      char dll_name[MAX_PATH];
-
-      if (!(*win32_GetModuleInformation) (current_process_handle,
-                                         DllHandle[i],
-                                         &mi,
-                                         sizeof (mi)))
-       continue;
-      if ((*win32_GetModuleFileNameExA) (current_process_handle,
-                                        DllHandle[i],
-                                        dll_name,
-                                        MAX_PATH) == 0)
-       continue;
-      win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll);
-    }
-}
-#endif
-
-typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
-typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
-typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
-
-/* Handle a DLL load event.
-
-   This function assumes that this event did not occur during inferior
-   initialization, where their event info may be incomplete (see
-   do_initial_child_stuff and win32_add_all_dlls for more info on
-   how we handle DLL loading during that phase).  */
-
-static void
-handle_load_dll (void)
-{
-  LOAD_DLL_DEBUG_INFO *event = &current_event.u.LoadDll;
-  char *dll_name;
-
-  dll_name = get_image_name (current_process_handle,
-                            event->lpImageName, event->fUnicode);
-  if (!dll_name)
-    return;
-
-  win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll);
-}
-
-/* Handle a DLL unload event.
-
-   This function assumes that this event did not occur during inferior
-   initialization, where their event info may be incomplete (see
-   do_initial_child_stuff and win32_add_one_solib for more info
-   on how we handle DLL loading during that phase).  */
+/* See nat/windows-nat.h.  */
 
-static void
-handle_unload_dll (void)
+void
+gdbserver_windows_process::handle_unload_dll ()
 {
   CORE_ADDR load_addr =
          (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
@@ -1235,133 +930,12 @@ handle_unload_dll (void)
   unloaded_dll (NULL, load_addr);
 }
 
-static void
-handle_exception (struct target_waitstatus *ourstatus)
-{
-  DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
-
-  memcpy (&siginfo_er, &current_event.u.Exception.ExceptionRecord,
-         sizeof siginfo_er);
-
-  ourstatus->kind = TARGET_WAITKIND_STOPPED;
-
-  switch (code)
-    {
-    case EXCEPTION_ACCESS_VIOLATION:
-      OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION"));
-      ourstatus->value.sig = GDB_SIGNAL_SEGV;
-      break;
-    case STATUS_STACK_OVERFLOW:
-      OUTMSG2 (("STATUS_STACK_OVERFLOW"));
-      ourstatus->value.sig = GDB_SIGNAL_SEGV;
-      break;
-    case STATUS_FLOAT_DENORMAL_OPERAND:
-      OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
-      OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_INEXACT_RESULT:
-      OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_INVALID_OPERATION:
-      OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_OVERFLOW:
-      OUTMSG2 (("STATUS_FLOAT_OVERFLOW"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_STACK_CHECK:
-      OUTMSG2 (("STATUS_FLOAT_STACK_CHECK"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_UNDERFLOW:
-      OUTMSG2 (("STATUS_FLOAT_UNDERFLOW"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_FLOAT_DIVIDE_BY_ZERO:
-      OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_INTEGER_DIVIDE_BY_ZERO:
-      OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case STATUS_INTEGER_OVERFLOW:
-      OUTMSG2 (("STATUS_INTEGER_OVERFLOW"));
-      ourstatus->value.sig = GDB_SIGNAL_FPE;
-      break;
-    case EXCEPTION_BREAKPOINT:
-      OUTMSG2 (("EXCEPTION_BREAKPOINT"));
-      ourstatus->value.sig = GDB_SIGNAL_TRAP;
-#ifdef _WIN32_WCE
-      /* Remove the initial breakpoint.  */
-      check_breakpoints ((CORE_ADDR) (long) current_event
-                        .u.Exception.ExceptionRecord.ExceptionAddress);
-#endif
-      break;
-    case DBG_CONTROL_C:
-      OUTMSG2 (("DBG_CONTROL_C"));
-      ourstatus->value.sig = GDB_SIGNAL_INT;
-      break;
-    case DBG_CONTROL_BREAK:
-      OUTMSG2 (("DBG_CONTROL_BREAK"));
-      ourstatus->value.sig = GDB_SIGNAL_INT;
-      break;
-    case EXCEPTION_SINGLE_STEP:
-      OUTMSG2 (("EXCEPTION_SINGLE_STEP"));
-      ourstatus->value.sig = GDB_SIGNAL_TRAP;
-      break;
-    case EXCEPTION_ILLEGAL_INSTRUCTION:
-      OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION"));
-      ourstatus->value.sig = GDB_SIGNAL_ILL;
-      break;
-    case EXCEPTION_PRIV_INSTRUCTION:
-      OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION"));
-      ourstatus->value.sig = GDB_SIGNAL_ILL;
-      break;
-    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
-      OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION"));
-      ourstatus->value.sig = GDB_SIGNAL_ILL;
-      break;
-    default:
-      if (current_event.u.Exception.dwFirstChance)
-       {
-         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
-         return;
-       }
-      OUTMSG2 (("gdbserver: unknown target exception 0x%08x at 0x%s",
-           (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
-           phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord.
-           ExceptionAddress, sizeof (uintptr_t))));
-      ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
-      break;
-    }
-  OUTMSG2 (("\n"));
-  last_sig = ourstatus->value.sig;
-}
-
-
 static void
 suspend_one_thread (thread_info *thread)
 {
-  win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+  windows_thread_info *th = (windows_thread_info *) thread_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;
-    }
+  th->suspend ();
 }
 
 static void
@@ -1369,239 +943,255 @@ fake_breakpoint_event (void)
 {
   OUTMSG2(("fake_breakpoint_event\n"));
 
-  faked_breakpoint = 1;
+  windows_process.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
+  memset (&windows_process.current_event, 0,
+         sizeof (windows_process.current_event));
+  windows_process.current_event.dwThreadId = windows_process.main_thread_id;
+  windows_process.current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
+  windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
     = EXCEPTION_BREAKPOINT;
 
   for_each_thread (suspend_one_thread);
 }
 
-#ifdef _WIN32_WCE
-static int
-auto_delete_breakpoint (CORE_ADDR stop_pc)
+/* See nat/windows-nat.h.  */
+
+bool
+gdbserver_windows_process::handle_access_violation
+     (const EXCEPTION_RECORD *rec)
 {
-  return 1;
+  return false;
+}
+
+/* A helper function that will, if needed, set
+   'stopped_at_software_breakpoint' on the thread and adjust the
+   PC.  */
+
+static void
+maybe_adjust_pc ()
+{
+  struct regcache *regcache = get_thread_regcache (current_thread, 1);
+  child_fetch_inferior_registers (regcache, -1);
+
+  windows_thread_info *th
+    = windows_process.thread_rec (current_thread_ptid (),
+                                 DONT_INVALIDATE_CONTEXT);
+  th->stopped_at_software_breakpoint = false;
+
+  if (windows_process.current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
+      && ((windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
+          == EXCEPTION_BREAKPOINT)
+         || (windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
+             == STATUS_WX86_BREAKPOINT))
+      && windows_process.child_initialization_done)
+    {
+      th->stopped_at_software_breakpoint = true;
+      CORE_ADDR pc = regcache_read_pc (regcache);
+      CORE_ADDR sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break;
+      regcache_write_pc (regcache, sw_breakpoint_pc);
+    }
 }
-#endif
 
 /* Get the next event from the child.  */
 
 static int
-get_child_debug_event (struct target_waitstatus *ourstatus)
+get_child_debug_event (DWORD *continue_status,
+                      struct target_waitstatus *ourstatus)
 {
   ptid_t ptid;
 
-  last_sig = GDB_SIGNAL_0;
-  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+  windows_process.last_sig = GDB_SIGNAL_0;
+  ourstatus->set_spurious ();
+  *continue_status = DBG_CONTINUE;
 
   /* Check if GDB sent us an interrupt request.  */
   check_remote_input_interrupt_request ();
 
-  if (soft_interrupt_requested)
+  DEBUG_EVENT *current_event = &windows_process.current_event;
+
+  if (windows_process.soft_interrupt_requested)
     {
-      soft_interrupt_requested = 0;
+      windows_process.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 comfortable remote
-        interruption, but high enough so gdbserver doesn't become a
-        bottleneck.  */
-      if (!WaitForDebugEvent (&current_event, 250))
-        {
-         DWORD e  = GetLastError();
-
-         if (e == ERROR_PIPE_NOT_CONNECTED)
-           {
-             /* This will happen if the loader fails to succesfully
-                load the application, e.g., if the main executable
-                tries to pull in a non-existing export from a
-                DLL.  */
-             ourstatus->kind = TARGET_WAITKIND_EXITED;
-             ourstatus->value.integer = 1;
-             return 1;
-           }
+  windows_process.attaching = 0;
+  {
+    gdb::optional<pending_stop> stop
+      = windows_process.fetch_pending_stop (debug_threads);
+    if (stop.has_value ())
+      {
+       *ourstatus = stop->status;
+       windows_process.current_event = stop->event;
+       ptid = debug_event_ptid (&windows_process.current_event);
+       switch_to_thread (find_thread_ptid (ptid));
+       return 1;
+      }
 
-         return 0;
-        }
-    }
+    /* Keep the wait time low enough for comfortable remote
+       interruption, but high enough so gdbserver doesn't become a
+       bottleneck.  */
+    if (!wait_for_debug_event (&windows_process.current_event, 250))
+      {
+       DWORD e  = GetLastError();
+
+       if (e == ERROR_PIPE_NOT_CONNECTED)
+         {
+           /* This will happen if the loader fails to succesfully
+              load the application, e.g., if the main executable
+              tries to pull in a non-existing export from a
+              DLL.  */
+           ourstatus->set_exited (1);
+           return 1;
+         }
+
+       return 0;
+      }
+  }
 
  gotevent:
 
-  switch (current_event.dwDebugEventCode)
+  switch (current_event->dwDebugEventCode)
     {
     case CREATE_THREAD_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT "
                "for pid=%u tid=%x)\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
 
       /* Record the existence of this thread.  */
-      child_add_thread (current_event.dwProcessId,
-                       current_event.dwThreadId,
-                       current_event.u.CreateThread.hThread,
-                       current_event.u.CreateThread.lpThreadLocalBase);
+      child_add_thread (current_event->dwProcessId,
+                       current_event->dwThreadId,
+                       current_event->u.CreateThread.hThread,
+                       current_event->u.CreateThread.lpThreadLocalBase);
       break;
 
     case EXIT_THREAD_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      child_delete_thread (current_event.dwProcessId,
-                          current_event.dwThreadId);
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      child_delete_thread (current_event->dwProcessId,
+                          current_event->dwThreadId);
 
-      current_thread = get_first_thread ();
+      switch_to_thread (get_first_thread ());
       return 1;
 
     case CREATE_PROCESS_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      CloseHandle (current_event.u.CreateProcessInfo.hFile);
-
-      current_process_handle = current_event.u.CreateProcessInfo.hProcess;
-      main_thread_id = current_event.dwThreadId;
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      CloseHandle (current_event->u.CreateProcessInfo.hFile);
 
-      /* Add the main thread.  */
-      child_add_thread (current_event.dwProcessId,
-                       main_thread_id,
-                       current_event.u.CreateProcessInfo.hThread,
-                       current_event.u.CreateProcessInfo.lpThreadLocalBase);
-
-#ifdef _WIN32_WCE
-      if (!attaching)
+      if (windows_process.open_process_used)
        {
-         /* 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);
+         CloseHandle (windows_process.handle);
+         windows_process.open_process_used = false;
        }
-#endif
+
+      windows_process.handle = current_event->u.CreateProcessInfo.hProcess;
+      windows_process.main_thread_id = current_event->dwThreadId;
+
+      /* Add the main thread.  */
+      child_add_thread (current_event->dwProcessId,
+                       windows_process.main_thread_id,
+                       current_event->u.CreateProcessInfo.hThread,
+                       current_event->u.CreateProcessInfo.lpThreadLocalBase);
       break;
 
     case EXIT_PROCESS_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
       {
-       DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
+       DWORD exit_status = current_event->u.ExitProcess.dwExitCode;
        /* If the exit status looks like a fatal exception, but we
           don't recognize the exception's code, make the original
           exit status value available, to avoid losing information.  */
        int exit_signal
          = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
        if (exit_signal == -1)
-         {
-           ourstatus->kind = TARGET_WAITKIND_EXITED;
-           ourstatus->value.integer = exit_status;
-         }
+         ourstatus->set_exited (exit_status);
        else
-         {
-           ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
-           ourstatus->value.sig = gdb_signal_from_host (exit_signal);
-         }
+         ourstatus->set_signalled (gdb_signal_from_host (exit_signal));
       }
-      child_continue (DBG_CONTINUE, -1);
-      CloseHandle (current_process_handle);
-      current_process_handle = NULL;
+      child_continue (DBG_CONTINUE, windows_process.desired_stop_thread_id);
       break;
 
     case LOAD_DLL_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      CloseHandle (current_event.u.LoadDll.hFile);
-      if (! child_initialization_done)
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      CloseHandle (current_event->u.LoadDll.hFile);
+      if (! windows_process.child_initialization_done)
        break;
-      handle_load_dll ();
+      windows_process.dll_loaded_event ();
 
-      ourstatus->kind = TARGET_WAITKIND_LOADED;
-      ourstatus->value.sig = GDB_SIGNAL_TRAP;
+      ourstatus->set_loaded ();
       break;
 
     case UNLOAD_DLL_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      if (! child_initialization_done)
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      if (! windows_process.child_initialization_done)
        break;
-      handle_unload_dll ();
-      ourstatus->kind = TARGET_WAITKIND_LOADED;
-      ourstatus->value.sig = GDB_SIGNAL_TRAP;
+      windows_process.handle_unload_dll ();
+      ourstatus->set_loaded ();
       break;
 
     case EXCEPTION_DEBUG_EVENT:
       OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      handle_exception (ourstatus);
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      if (windows_process.handle_exception (ourstatus, debug_threads)
+         == HANDLE_EXCEPTION_UNHANDLED)
+       *continue_status = DBG_EXCEPTION_NOT_HANDLED;
       break;
 
     case OUTPUT_DEBUG_STRING_EVENT:
       /* A message from the kernel (or Cygwin).  */
       OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT "
                "for pid=%u tid=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId));
-      handle_output_debug_string ();
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId));
+      windows_process.handle_output_debug_string (nullptr);
       break;
 
     default:
       OUTMSG2 (("gdbserver: kernel event unknown "
                "for pid=%u tid=%x code=%x\n",
-               (unsigned) current_event.dwProcessId,
-               (unsigned) current_event.dwThreadId,
-               (unsigned) current_event.dwDebugEventCode));
+               (unsigned) current_event->dwProcessId,
+               (unsigned) current_event->dwThreadId,
+               (unsigned) current_event->dwDebugEventCode));
       break;
     }
 
-  ptid = debug_event_ptid (&current_event);
-  current_thread = find_thread_ptid (ptid);
+  ptid = debug_event_ptid (&windows_process.current_event);
+
+  if (windows_process.desired_stop_thread_id != -1
+      && windows_process.desired_stop_thread_id != ptid.lwp ())
+    {
+      /* Pending stop.  See the comment by the definition of
+        "pending_stops" for details on why this is needed.  */
+      OUTMSG2 (("get_windows_debug_event - "
+               "unexpected stop in 0x%lx (expecting 0x%x)\n",
+               ptid.lwp (), windows_process.desired_stop_thread_id));
+      maybe_adjust_pc ();
+      windows_process.pending_stops.push_back
+       ({(DWORD) ptid.lwp (), *ourstatus, *current_event});
+      ourstatus->set_spurious ();
+    }
+  else
+    switch_to_thread (find_thread_ptid (ptid));
+
   return 1;
 }
 
@@ -1610,48 +1200,49 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
    Returns the signal which caused the process to stop. */
 ptid_t
 win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
-                           int options)
+                           target_wait_flags options)
 {
-  struct regcache *regcache;
-
-  if (cached_status.kind != TARGET_WAITKIND_IGNORE)
+  if (windows_process.cached_status.kind () != TARGET_WAITKIND_IGNORE)
     {
       /* The core always does a wait after creating the inferior, and
         do_initial_child_stuff already ran the inferior to the
         initial breakpoint (or an exit, if creating the process
         fails).  Report it now.  */
-      *ourstatus = cached_status;
-      cached_status.kind = TARGET_WAITKIND_IGNORE;
-      return debug_event_ptid (&current_event);
+      *ourstatus = windows_process.cached_status;
+      windows_process.cached_status.set_ignore ();
+      return debug_event_ptid (&windows_process.current_event);
     }
 
   while (1)
     {
-      if (!get_child_debug_event (ourstatus))
+      DWORD continue_status;
+      if (!get_child_debug_event (&continue_status, ourstatus))
        continue;
 
-      switch (ourstatus->kind)
+      switch (ourstatus->kind ())
        {
        case TARGET_WAITKIND_EXITED:
          OUTMSG2 (("Child exited with retcode = %x\n",
-                   ourstatus->value.integer));
+                   ourstatus->exit_status ()));
          win32_clear_inferiors ();
-         return ptid_t (current_event.dwProcessId);
+         return ptid_t (windows_process.current_event.dwProcessId);
        case TARGET_WAITKIND_STOPPED:
        case TARGET_WAITKIND_SIGNALLED:
        case TARGET_WAITKIND_LOADED:
-         OUTMSG2 (("Child Stopped with signal = %d \n",
-                   ourstatus->value.sig));
-
-         regcache = get_thread_regcache (current_thread, 1);
-         child_fetch_inferior_registers (regcache, -1);
-         return debug_event_ptid (&current_event);
+         {
+           OUTMSG2 (("Child Stopped with signal = %d \n",
+                     ourstatus->sig ()));
+           maybe_adjust_pc ();
+           return debug_event_ptid (&windows_process.current_event);
+         }
        default:
-         OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
+         OUTMSG (("Ignoring unknown internal event, %d\n",
+                 ourstatus->kind ()));
          /* fall-through */
        case TARGET_WAITKIND_SPURIOUS:
          /* do nothing, just continue */
-         child_continue (DBG_CONTINUE, -1);
+         child_continue (continue_status,
+                         windows_process.desired_stop_thread_id);
          break;
        }
     }
@@ -1698,19 +1289,7 @@ win32_process_target::write_memory (CORE_ADDR memaddr,
 void
 win32_process_target::request_interrupt ()
 {
-  winapi_DebugBreakProcess DebugBreakProcess;
-  winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent;
-
-#ifdef _WIN32_WCE
-  HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
-#else
-  HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
-#endif
-
-  GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent);
-
-  if (GenerateConsoleCtrlEvent != NULL
-      && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id))
+  if (GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, signal_pid))
     return;
 
   /* GenerateConsoleCtrlEvent can fail if process id being debugged is
@@ -1718,14 +1297,11 @@ win32_process_target::request_interrupt ()
      Fallback to XP/Vista 'DebugBreakProcess', which generates a
      breakpoint exception in the interior process.  */
 
-  DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess);
-
-  if (DebugBreakProcess != NULL
-      && DebugBreakProcess (current_process_handle))
+  if (DebugBreakProcess (windows_process.handle))
     return;
 
   /* Last resort, suspend all threads manually.  */
-  soft_interrupt_requested = 1;
+  windows_process.soft_interrupt_requested = 1;
 }
 
 bool
@@ -1734,65 +1310,6 @@ win32_process_target::supports_hardware_single_step ()
   return true;
 }
 
-#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;
-}
-
-void
-win32_process_target::hostio_last_error (char *buf)
-{
-  DWORD winerr = GetLastError ();
-  int fileio_err = win32_error_to_fileio_error (winerr);
-  sprintf (buf, "F-1,%x", fileio_err);
-}
-#endif
-
 bool
 win32_process_target::supports_qxfer_siginfo ()
 {
@@ -1807,19 +1324,43 @@ win32_process_target::qxfer_siginfo (const char *annex,
                                     unsigned const char *writebuf,
                                     CORE_ADDR offset, int len)
 {
-  if (siginfo_er.ExceptionCode == 0)
+  if (windows_process.siginfo_er.ExceptionCode == 0)
     return -1;
 
   if (readbuf == nullptr)
     return -1;
 
-  if (offset > sizeof (siginfo_er))
+  char *buf = (char *) &windows_process.siginfo_er;
+  size_t bufsize = sizeof (windows_process.siginfo_er);
+
+#ifdef __x86_64__
+  EXCEPTION_RECORD32 er32;
+  if (windows_process.wow64_process)
+    {
+      buf = (char *) &er32;
+      bufsize = sizeof (er32);
+
+      er32.ExceptionCode = windows_process.siginfo_er.ExceptionCode;
+      er32.ExceptionFlags = windows_process.siginfo_er.ExceptionFlags;
+      er32.ExceptionRecord
+       = (uintptr_t) windows_process.siginfo_er.ExceptionRecord;
+      er32.ExceptionAddress
+       = (uintptr_t) windows_process.siginfo_er.ExceptionAddress;
+      er32.NumberParameters = windows_process.siginfo_er.NumberParameters;
+      int i;
+      for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
+       er32.ExceptionInformation[i]
+         = windows_process.siginfo_er.ExceptionInformation[i];
+    }
+#endif
+
+  if (offset > bufsize)
     return -1;
 
-  if (offset + len > sizeof (siginfo_er))
-    len = sizeof (siginfo_er) - offset;
+  if (offset + len > bufsize)
+    len = bufsize - offset;
 
-  memcpy (readbuf, (char *) &siginfo_er + offset, len);
+  memcpy (readbuf, buf + offset, len);
 
   return len;
 }
@@ -1835,8 +1376,8 @@ win32_process_target::supports_get_tib_address ()
 int
 win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
 {
-  win32_thread_info *th;
-  th = thread_rec (ptid, 0);
+  windows_thread_info *th;
+  th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
   if (th == NULL)
     return 0;
   if (addr != NULL)
@@ -1846,46 +1387,65 @@ win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
 
 /* Implementation of the target_ops method "sw_breakpoint_from_kind".  */
 
-static const gdb_byte *
-win32_sw_breakpoint_from_kind (int kind, int *size)
+const gdb_byte *
+win32_process_target::sw_breakpoint_from_kind (int kind, int *size)
 {
   *size = the_low_target.breakpoint_len;
   return the_low_target.breakpoint;
 }
 
+bool
+win32_process_target::stopped_by_sw_breakpoint ()
+{
+  windows_thread_info *th
+    = windows_process.thread_rec (current_thread_ptid (),
+                                 DONT_INVALIDATE_CONTEXT);
+  return th == nullptr ? false : th->stopped_at_software_breakpoint;
+}
+
+bool
+win32_process_target::supports_stopped_by_sw_breakpoint ()
+{
+  return true;
+}
+
+CORE_ADDR
+win32_process_target::read_pc (struct regcache *regcache)
+{
+  return (*the_low_target.get_pc) (regcache);
+}
+
+void
+win32_process_target::write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  return (*the_low_target.set_pc) (regcache, pc);
+}
+
+const char *
+win32_process_target::thread_name (ptid_t thread)
+{
+  windows_thread_info *th
+    = windows_process.thread_rec (current_thread_ptid (),
+                                 DONT_INVALIDATE_CONTEXT);
+  return th->thread_name ();
+}
+
+const char *
+win32_process_target::pid_to_exec_file (int pid)
+{
+  return windows_process.pid_to_exec_file (pid);
+}
+
 /* The win32 target ops object.  */
 
 static win32_process_target the_win32_target;
 
-static process_stratum_target win32_target_ops = {
-  NULL, /* emit_ops */
-  NULL, /* supports_disable_randomization */
-  NULL, /* qxfer_libraries_svr4 */
-  NULL, /* support_agent */
-  NULL, /* enable_btrace */
-  NULL, /* disable_btrace */
-  NULL, /* read_btrace */
-  NULL, /* read_btrace_conf */
-  NULL, /* supports_range_stepping */
-  NULL, /* pid_to_exec_file */
-  NULL, /* multifs_open */
-  NULL, /* multifs_unlink */
-  NULL, /* multifs_readlink */
-  NULL, /* breakpoint_kind_from_pc */
-  win32_sw_breakpoint_from_kind,
-  NULL, /* thread_name */
-  NULL, /* breakpoint_kind_from_current_state */
-  NULL, /* supports_software_single_step */
-  NULL, /* supports_catch_syscall */
-  NULL, /* get_ipa_tdesc_idx */
-  NULL, /* thread_handle */
-  &the_win32_target,
-};
-
 /* Initialize the Win32 backend.  */
 void
 initialize_low (void)
 {
-  set_target_ops (&win32_target_ops);
+  set_target_ops (&the_win32_target);
   the_low_target.arch_setup ();
+
+  initialize_loadable ();
 }