| /* Target-vector operations for controlling windows child processes, for GDB. | 
 |  | 
 |    Copyright (C) 1995-2024 Free Software Foundation, Inc. | 
 |  | 
 |    Contributed by Cygnus Solutions, A Red Hat Company. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* Originally by Steve Chamberlain, sac@cygnus.com */ | 
 |  | 
 | #include "defs.h" | 
 | #include "frame.h" | 
 | #include "inferior.h" | 
 | #include "infrun.h" | 
 | #include "target.h" | 
 | #include "gdbcore.h" | 
 | #include "command.h" | 
 | #include "completer.h" | 
 | #include "regcache.h" | 
 | #include "top.h" | 
 | #include <signal.h> | 
 | #include <sys/types.h> | 
 | #include <fcntl.h> | 
 | #include <windows.h> | 
 | #include <imagehlp.h> | 
 | #ifdef __CYGWIN__ | 
 | #include <wchar.h> | 
 | #include <sys/cygwin.h> | 
 | #include <cygwin/version.h> | 
 | #endif | 
 | #include <algorithm> | 
 | #include <vector> | 
 | #include <queue> | 
 |  | 
 | #include "filenames.h" | 
 | #include "symfile.h" | 
 | #include "objfiles.h" | 
 | #include "gdb_bfd.h" | 
 | #include "gdbsupport/gdb_obstack.h" | 
 | #include "gdbthread.h" | 
 | #include "gdbcmd.h" | 
 | #include <unistd.h> | 
 | #include "exec.h" | 
 | #include "solist.h" | 
 | #include "solib.h" | 
 | #include "xml-support.h" | 
 | #include "inttypes.h" | 
 |  | 
 | #include "i386-tdep.h" | 
 | #include "i387-tdep.h" | 
 |  | 
 | #include "windows-tdep.h" | 
 | #include "windows-nat.h" | 
 | #include "x86-nat.h" | 
 | #include "complaints.h" | 
 | #include "inf-child.h" | 
 | #include "gdbsupport/gdb_tilde_expand.h" | 
 | #include "gdbsupport/pathstuff.h" | 
 | #include "gdbsupport/gdb_wait.h" | 
 | #include "nat/windows-nat.h" | 
 | #include "gdbsupport/symbol.h" | 
 | #include "ser-event.h" | 
 | #include "inf-loop.h" | 
 |  | 
 | using namespace windows_nat; | 
 |  | 
 | /* Maintain a linked list of "so" information.  */ | 
 | struct windows_solib | 
 | { | 
 |   LPVOID load_addr = 0; | 
 |   CORE_ADDR text_offset = 0; | 
 |  | 
 |   /* Original name.  */ | 
 |   std::string original_name; | 
 |   /* Expanded form of the name.  */ | 
 |   std::string name; | 
 | }; | 
 |  | 
 | struct windows_per_inferior : public windows_process_info | 
 | { | 
 |   windows_thread_info *thread_rec (ptid_t ptid, | 
 | 				   thread_disposition_type disposition) override; | 
 |   int handle_output_debug_string (struct target_waitstatus *ourstatus) override; | 
 |   void handle_load_dll (const char *dll_name, LPVOID base) override; | 
 |   void handle_unload_dll () override; | 
 |   bool handle_access_violation (const EXCEPTION_RECORD *rec) override; | 
 |  | 
 |  | 
 |   int have_saved_context = 0;	/* True if we've saved context from a | 
 | 				   cygwin signal.  */ | 
 |  | 
 |   uintptr_t dr[8] {}; | 
 |  | 
 |   int windows_initialization_done = 0; | 
 |  | 
 |   std::vector<std::unique_ptr<windows_thread_info>> thread_list; | 
 |  | 
 |   /* Counts of things.  */ | 
 |   int saw_create = 0; | 
 |   int open_process_used = 0; | 
 | #ifdef __x86_64__ | 
 |   void *wow64_dbgbreak = nullptr; | 
 | #endif | 
 |  | 
 |   /* This vector maps GDB's idea of a register's number into an offset | 
 |      in the windows exception context vector. | 
 |  | 
 |      It also contains the bit mask needed to load the register in question. | 
 |  | 
 |      The contents of this table can only be computed by the units | 
 |      that provide CPU-specific support for Windows native debugging. | 
 |  | 
 |      One day we could read a reg, we could inspect the context we | 
 |      already have loaded, if it doesn't have the bit set that we need, | 
 |      we read that set of registers in using GetThreadContext.  If the | 
 |      context already contains what we need, we just unpack it.  Then to | 
 |      write a register, first we have to ensure that the context contains | 
 |      the other regs of the group, and then we copy the info in and set | 
 |      out bit.  */ | 
 |  | 
 |   const int *mappings = nullptr; | 
 |  | 
 |   /* The function to use in order to determine whether a register is | 
 |      a segment register or not.  */ | 
 |   segment_register_p_ftype *segment_register_p = nullptr; | 
 |  | 
 |   std::vector<windows_solib> solibs; | 
 |  | 
 | #ifdef __CYGWIN__ | 
 |   CONTEXT saved_context {};	/* Contains the saved context from a | 
 | 				   cygwin signal.  */ | 
 |  | 
 |   /* The starting and ending address of the cygwin1.dll text segment.  */ | 
 |   CORE_ADDR cygwin_load_start = 0; | 
 |   CORE_ADDR cygwin_load_end = 0; | 
 | #endif /* __CYGWIN__ */ | 
 | }; | 
 |  | 
 | /* The current process.  */ | 
 | static windows_per_inferior windows_process; | 
 |  | 
 | #undef STARTUPINFO | 
 |  | 
 | #ifndef __CYGWIN__ | 
 | # define __PMAX	(MAX_PATH + 1) | 
 | # define STARTUPINFO STARTUPINFOA | 
 | #else | 
 | # define __PMAX	PATH_MAX | 
 | #   define STARTUPINFO STARTUPINFOW | 
 | #endif | 
 |  | 
 | /* If we're not using the old Cygwin header file set, define the | 
 |    following which never should have been in the generic Win32 API | 
 |    headers in the first place since they were our own invention...  */ | 
 | #ifndef _GNU_H_WINDOWS_H | 
 | enum | 
 |   { | 
 |     FLAG_TRACE_BIT = 0x100, | 
 |   }; | 
 | #endif | 
 |  | 
 | #ifndef CONTEXT_EXTENDED_REGISTERS | 
 | /* This macro is only defined on ia32.  It only makes sense on this target, | 
 |    so define it as zero if not already defined.  */ | 
 | #define CONTEXT_EXTENDED_REGISTERS 0 | 
 | #endif | 
 |  | 
 | #define CONTEXT_DEBUGGER_DR CONTEXT_FULL | CONTEXT_FLOATING_POINT \ | 
 | 	| CONTEXT_SEGMENTS | CONTEXT_DEBUG_REGISTERS \ | 
 | 	| CONTEXT_EXTENDED_REGISTERS | 
 |  | 
 | #define DR6_CLEAR_VALUE 0xffff0ff0 | 
 |  | 
 | /* The string sent by cygwin when it processes a signal. | 
 |    FIXME: This should be in a cygwin include file.  */ | 
 | #ifndef _CYGWIN_SIGNAL_STRING | 
 | #define _CYGWIN_SIGNAL_STRING "cYgSiGw00f" | 
 | #endif | 
 |  | 
 | #define CHECK(x)	check (x, __FILE__,__LINE__) | 
 | #define DEBUG_EXEC(fmt, ...) \ | 
 |   debug_prefixed_printf_cond (debug_exec, "windows exec", fmt, ## __VA_ARGS__) | 
 | #define DEBUG_EVENTS(fmt, ...) \ | 
 |   debug_prefixed_printf_cond (debug_events, "windows events", fmt, \ | 
 | 			      ## __VA_ARGS__) | 
 | #define DEBUG_MEM(fmt, ...) \ | 
 |   debug_prefixed_printf_cond (debug_memory, "windows mem", fmt, \ | 
 | 			      ## __VA_ARGS__) | 
 | #define DEBUG_EXCEPT(fmt, ...) \ | 
 |   debug_prefixed_printf_cond (debug_exceptions, "windows except", fmt, \ | 
 | 			      ## __VA_ARGS__) | 
 |  | 
 | static void cygwin_set_dr (int i, CORE_ADDR addr); | 
 | static void cygwin_set_dr7 (unsigned long val); | 
 | static CORE_ADDR cygwin_get_dr (int i); | 
 | static unsigned long cygwin_get_dr6 (void); | 
 | static unsigned long cygwin_get_dr7 (void); | 
 |  | 
 | /* User options.  */ | 
 | static bool new_console = false; | 
 | #ifdef __CYGWIN__ | 
 | static bool cygwin_exceptions = false; | 
 | #endif | 
 | static bool new_group = true; | 
 | static bool debug_exec = false;		/* show execution */ | 
 | static bool debug_events = false;	/* show events from kernel */ | 
 | static bool debug_memory = false;	/* show target memory accesses */ | 
 | static bool debug_exceptions = false;	/* show target exceptions */ | 
 | static bool useshell = false;		/* use shell for subprocesses */ | 
 |  | 
 | /* See windows_nat_target::resume to understand why this is commented | 
 |    out.  */ | 
 | #if 0 | 
 | /* This vector maps the target's idea of an exception (extracted | 
 |    from the DEBUG_EVENT structure) to GDB's idea.  */ | 
 |  | 
 | struct xlate_exception | 
 |   { | 
 |     DWORD them; | 
 |     enum gdb_signal us; | 
 |   }; | 
 |  | 
 | static const struct xlate_exception xlate[] = | 
 | { | 
 |   {EXCEPTION_ACCESS_VIOLATION, GDB_SIGNAL_SEGV}, | 
 |   {STATUS_STACK_OVERFLOW, GDB_SIGNAL_SEGV}, | 
 |   {EXCEPTION_BREAKPOINT, GDB_SIGNAL_TRAP}, | 
 |   {DBG_CONTROL_C, GDB_SIGNAL_INT}, | 
 |   {EXCEPTION_SINGLE_STEP, GDB_SIGNAL_TRAP}, | 
 |   {STATUS_FLOAT_DIVIDE_BY_ZERO, GDB_SIGNAL_FPE} | 
 | }; | 
 |  | 
 | #endif /* 0 */ | 
 |  | 
 | struct windows_nat_target final : public x86_nat_target<inf_child_target> | 
 | { | 
 |   windows_nat_target (); | 
 |  | 
 |   void close () override; | 
 |  | 
 |   void attach (const char *, int) override; | 
 |  | 
 |   bool attach_no_wait () override | 
 |   { return true; } | 
 |  | 
 |   void detach (inferior *, int) override; | 
 |  | 
 |   void resume (ptid_t, int , enum gdb_signal) override; | 
 |  | 
 |   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override; | 
 |  | 
 |   void fetch_registers (struct regcache *, int) override; | 
 |   void store_registers (struct regcache *, int) override; | 
 |  | 
 |   bool stopped_by_sw_breakpoint () override | 
 |   { | 
 |     windows_thread_info *th | 
 |       = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT); | 
 |     return th->stopped_at_software_breakpoint; | 
 |   } | 
 |  | 
 |   bool supports_stopped_by_sw_breakpoint () override | 
 |   { | 
 |     return true; | 
 |   } | 
 |  | 
 |   enum target_xfer_status xfer_partial (enum target_object object, | 
 | 					const char *annex, | 
 | 					gdb_byte *readbuf, | 
 | 					const gdb_byte *writebuf, | 
 | 					ULONGEST offset, ULONGEST len, | 
 | 					ULONGEST *xfered_len) override; | 
 |  | 
 |   void files_info () override; | 
 |  | 
 |   void kill () override; | 
 |  | 
 |   void create_inferior (const char *, const std::string &, | 
 | 			char **, int) override; | 
 |  | 
 |   void mourn_inferior () override; | 
 |  | 
 |   bool thread_alive (ptid_t ptid) override; | 
 |  | 
 |   std::string pid_to_str (ptid_t) override; | 
 |  | 
 |   void interrupt () override; | 
 |   void pass_ctrlc () override; | 
 |  | 
 |   const char *pid_to_exec_file (int pid) override; | 
 |  | 
 |   ptid_t get_ada_task_ptid (long lwp, ULONGEST thread) override; | 
 |  | 
 |   bool get_tib_address (ptid_t ptid, CORE_ADDR *addr) override; | 
 |  | 
 |   const char *thread_name (struct thread_info *) override; | 
 |  | 
 |   ptid_t get_windows_debug_event (int pid, struct target_waitstatus *ourstatus, | 
 | 				  target_wait_flags options); | 
 |  | 
 |   void do_initial_windows_stuff (DWORD pid, bool attaching); | 
 |  | 
 |   bool supports_disable_randomization () override | 
 |   { | 
 |     return disable_randomization_available (); | 
 |   } | 
 |  | 
 |   bool can_async_p () override | 
 |   { | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool is_async_p () override | 
 |   { | 
 |     return m_is_async; | 
 |   } | 
 |  | 
 |   void async (bool enable) override; | 
 |  | 
 |   int async_wait_fd () override | 
 |   { | 
 |     return serial_event_fd (m_wait_event); | 
 |   } | 
 |  | 
 | private: | 
 |  | 
 |   windows_thread_info *add_thread (ptid_t ptid, HANDLE h, void *tlb, | 
 | 				   bool main_thread_p); | 
 |   void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p); | 
 |   DWORD fake_create_process (); | 
 |  | 
 |   BOOL windows_continue (DWORD continue_status, int id, int killed, | 
 | 			 bool last_call = false); | 
 |  | 
 |   /* Helper function to start process_thread.  */ | 
 |   static DWORD WINAPI process_thread_starter (LPVOID self); | 
 |  | 
 |   /* This function implements the background thread that starts | 
 |      inferiors and waits for events.  */ | 
 |   void process_thread (); | 
 |  | 
 |   /* Push FUNC onto the queue of requests for process_thread, and wait | 
 |      until it has been called.  On Windows, certain debugging | 
 |      functions can only be called by the thread that started (or | 
 |      attached to) the inferior.  These are all done in the worker | 
 |      thread, via calls to this method.  If FUNC returns true, | 
 |      process_thread will wait for debug events when FUNC returns.  */ | 
 |   void do_synchronously (gdb::function_view<bool ()> func); | 
 |  | 
 |   /* This waits for a debug event, dispatching to the worker thread as | 
 |      needed.  */ | 
 |   void wait_for_debug_event_main_thread (DEBUG_EVENT *event); | 
 |  | 
 |   /* Queue used to send requests to process_thread.  This is | 
 |      implicitly locked.  */ | 
 |   std::queue<gdb::function_view<bool ()>> m_queue; | 
 |  | 
 |   /* Event used to signal process_thread that an item has been | 
 |      pushed.  */ | 
 |   HANDLE m_pushed_event; | 
 |   /* Event used by process_thread to indicate that it has processed a | 
 |      single function call.  */ | 
 |   HANDLE m_response_event; | 
 |  | 
 |   /* Serial event used to communicate wait event availability to the | 
 |      main loop.  */ | 
 |   serial_event *m_wait_event; | 
 |  | 
 |   /* The last debug event, when M_WAIT_EVENT has been set.  */ | 
 |   DEBUG_EVENT m_last_debug_event {}; | 
 |   /* True if a debug event is pending.  */ | 
 |   std::atomic<bool> m_debug_event_pending { false }; | 
 |  | 
 |   /* True if currently in async mode.  */ | 
 |   bool m_is_async = false; | 
 | }; | 
 |  | 
 | static void | 
 | check (BOOL ok, const char *file, int line) | 
 | { | 
 |   if (!ok) | 
 |     { | 
 |       unsigned err = (unsigned) GetLastError (); | 
 |       gdb_printf ("error return %s:%d was %u: %s\n", file, line, | 
 | 		  err, strwinerror (err)); | 
 |     } | 
 | } | 
 |  | 
 | windows_nat_target::windows_nat_target () | 
 |   : m_pushed_event (CreateEvent (nullptr, false, false, nullptr)), | 
 |     m_response_event (CreateEvent (nullptr, false, false, nullptr)), | 
 |     m_wait_event (make_serial_event ()) | 
 | { | 
 |   HANDLE bg_thread = CreateThread (nullptr, 64 * 1024, | 
 | 				   process_thread_starter, this, 0, nullptr); | 
 |   CloseHandle (bg_thread); | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::async (bool enable) | 
 | { | 
 |   if (enable == is_async_p ()) | 
 |     return; | 
 |  | 
 |   if (enable) | 
 |     add_file_handler (async_wait_fd (), | 
 | 		      [] (int, gdb_client_data) | 
 | 		      { | 
 | 			inferior_event_handler (INF_REG_EVENT); | 
 | 		      }, | 
 | 		      nullptr, "windows_nat_target"); | 
 |   else | 
 |     delete_file_handler (async_wait_fd ()); | 
 |  | 
 |   m_is_async = enable; | 
 | } | 
 |  | 
 | /* A wrapper for WaitForSingleObject that issues a warning if | 
 |    something unusual happens.  */ | 
 | static void | 
 | wait_for_single (HANDLE handle, DWORD howlong) | 
 | { | 
 |   while (true) | 
 |     { | 
 |       DWORD r = WaitForSingleObject (handle, howlong); | 
 |       if (r == WAIT_OBJECT_0) | 
 | 	return; | 
 |       if (r == WAIT_FAILED) | 
 | 	{ | 
 | 	  unsigned err = (unsigned) GetLastError (); | 
 | 	  warning ("WaitForSingleObject failed (code %u): %s", | 
 | 		   err, strwinerror (err)); | 
 | 	} | 
 |       else | 
 | 	warning ("unexpected result from WaitForSingleObject: %u", | 
 | 		 (unsigned) r); | 
 |     } | 
 | } | 
 |  | 
 | DWORD WINAPI | 
 | windows_nat_target::process_thread_starter (LPVOID self) | 
 | { | 
 |   ((windows_nat_target *) self)->process_thread (); | 
 |   return 0; | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::process_thread () | 
 | { | 
 |   while (true) | 
 |     { | 
 |       wait_for_single (m_pushed_event, INFINITE); | 
 |  | 
 |       gdb::function_view<bool ()> func = std::move (m_queue.front ()); | 
 |       m_queue.pop (); | 
 |  | 
 |       bool should_wait = func (); | 
 |       SetEvent (m_response_event); | 
 |  | 
 |       if (should_wait) | 
 | 	{ | 
 | 	  if (!m_debug_event_pending) | 
 | 	    { | 
 | 	      wait_for_debug_event (&m_last_debug_event, INFINITE); | 
 | 	      m_debug_event_pending = true; | 
 | 	    } | 
 | 	  serial_event_set (m_wait_event); | 
 | 	} | 
 |    } | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::do_synchronously (gdb::function_view<bool ()> func) | 
 | { | 
 |   m_queue.emplace (std::move (func)); | 
 |   SetEvent (m_pushed_event); | 
 |   wait_for_single (m_response_event, INFINITE); | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::wait_for_debug_event_main_thread (DEBUG_EVENT *event) | 
 | { | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       if (m_debug_event_pending) | 
 | 	{ | 
 | 	  *event = m_last_debug_event; | 
 | 	  m_debug_event_pending = false; | 
 | 	  serial_event_clear (m_wait_event); | 
 | 	} | 
 |       else | 
 | 	wait_for_debug_event (event, INFINITE); | 
 |       return false; | 
 |     }); | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | windows_thread_info * | 
 | windows_per_inferior::thread_rec | 
 |      (ptid_t ptid, thread_disposition_type disposition) | 
 | { | 
 |   for (auto &th : thread_list) | 
 |     if (th->tid == ptid.lwp ()) | 
 |       { | 
 | 	if (!th->suspended) | 
 | 	  { | 
 | 	    switch (disposition) | 
 | 	      { | 
 | 	      case DONT_INVALIDATE_CONTEXT: | 
 | 		/* Nothing.  */ | 
 | 		break; | 
 | 	      case INVALIDATE_CONTEXT: | 
 | 		if (ptid.lwp () != current_event.dwThreadId) | 
 | 		  th->suspend (); | 
 | 		th->reload_context = true; | 
 | 		break; | 
 | 	      case DONT_SUSPEND: | 
 | 		th->reload_context = true; | 
 | 		th->suspended = -1; | 
 | 		break; | 
 | 	      } | 
 | 	  } | 
 | 	return th.get (); | 
 |       } | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* Add a thread to the thread list. | 
 |  | 
 |    PTID is the ptid of the thread to be added. | 
 |    H is its Windows handle. | 
 |    TLB is its thread local base. | 
 |    MAIN_THREAD_P should be true if the thread to be added is | 
 |    the main thread, false otherwise.  */ | 
 |  | 
 | windows_thread_info * | 
 | windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb, | 
 | 				bool main_thread_p) | 
 | { | 
 |   windows_thread_info *th; | 
 |  | 
 |   gdb_assert (ptid.lwp () != 0); | 
 |  | 
 |   if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT))) | 
 |     return th; | 
 |  | 
 |   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 += 0x2000; | 
 | #endif | 
 |   th = new windows_thread_info (ptid.lwp (), h, base); | 
 |   windows_process.thread_list.emplace_back (th); | 
 |  | 
 |   /* Add this new thread to the list of threads. | 
 |  | 
 |      To be consistent with what's done on other platforms, we add | 
 |      the main thread silently (in reality, this thread is really | 
 |      more of a process to the user than a thread).  */ | 
 |   if (main_thread_p) | 
 |     add_thread_silent (this, ptid); | 
 |   else | 
 |     ::add_thread (this, ptid); | 
 |  | 
 |   /* It's simplest to always set this and update the debug | 
 |      registers.  */ | 
 |   th->debug_registers_changed = true; | 
 |  | 
 |   return th; | 
 | } | 
 |  | 
 | /* Clear out any old thread list and reinitialize it to a | 
 |    pristine state.  */ | 
 | static void | 
 | windows_init_thread_list (void) | 
 | { | 
 |   DEBUG_EVENTS ("called"); | 
 |   windows_process.thread_list.clear (); | 
 | } | 
 |  | 
 | /* Delete a thread from the list of threads. | 
 |  | 
 |    PTID is the ptid of the thread to be deleted. | 
 |    EXIT_CODE is the thread's exit code. | 
 |    MAIN_THREAD_P should be true if the thread to be deleted is | 
 |    the main thread, false otherwise.  */ | 
 |  | 
 | void | 
 | windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code, | 
 | 				   bool main_thread_p) | 
 | { | 
 |   DWORD id; | 
 |  | 
 |   gdb_assert (ptid.lwp () != 0); | 
 |  | 
 |   id = ptid.lwp (); | 
 |  | 
 |   /* Note that no notification was printed when the main thread was | 
 |      created, and thus, unless in verbose mode, we should be symmetrical, | 
 |      and avoid an exit notification for the main thread here as well.  */ | 
 |  | 
 |   bool silent = (main_thread_p && !info_verbose); | 
 |   thread_info *to_del = this->find_thread (ptid); | 
 |   delete_thread_with_exit_code (to_del, exit_code, silent); | 
 |  | 
 |   auto iter = std::find_if (windows_process.thread_list.begin (), | 
 | 			    windows_process.thread_list.end (), | 
 | 			    [=] (std::unique_ptr<windows_thread_info> &th) | 
 | 			    { | 
 | 			      return th->tid == id; | 
 | 			    }); | 
 |  | 
 |   if (iter != windows_process.thread_list.end ()) | 
 |     windows_process.thread_list.erase (iter); | 
 | } | 
 |  | 
 | /* Fetches register number R from the given windows_thread_info, | 
 |    and supplies its value to the given regcache. | 
 |  | 
 |    This function assumes that R is non-negative.  A failed assertion | 
 |    is raised if that is not true. | 
 |  | 
 |    This function assumes that TH->RELOAD_CONTEXT is not set, meaning | 
 |    that the windows_thread_info has an up-to-date context.  A failed | 
 |    assertion is raised if that assumption is violated.  */ | 
 |  | 
 | static void | 
 | windows_fetch_one_register (struct regcache *regcache, | 
 | 			    windows_thread_info *th, int r) | 
 | { | 
 |   gdb_assert (r >= 0); | 
 |   gdb_assert (!th->reload_context); | 
 |  | 
 |   char *context_ptr = (char *) &th->context; | 
 | #ifdef __x86_64__ | 
 |   if (windows_process.wow64_process) | 
 |     context_ptr = (char *) &th->wow64_context; | 
 | #endif | 
 |  | 
 |   char *context_offset = context_ptr + windows_process.mappings[r]; | 
 |   struct gdbarch *gdbarch = regcache->arch (); | 
 |   i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch); | 
 |  | 
 |   gdb_assert (!gdbarch_read_pc_p (gdbarch)); | 
 |   gdb_assert (gdbarch_pc_regnum (gdbarch) >= 0); | 
 |   gdb_assert (!gdbarch_write_pc_p (gdbarch)); | 
 |  | 
 |   /* GDB treats some registers as 32-bit, where they are in fact only | 
 |      16 bits long.  These cases must be handled specially to avoid | 
 |      reading extraneous bits from the context.  */ | 
 |   if (r == I387_FISEG_REGNUM (tdep) || windows_process.segment_register_p (r)) | 
 |     { | 
 |       gdb_byte bytes[4] = {}; | 
 |       memcpy (bytes, context_offset, 2); | 
 |       regcache->raw_supply (r, bytes); | 
 |     } | 
 |   else if (r == I387_FOP_REGNUM (tdep)) | 
 |     { | 
 |       long l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1); | 
 |       regcache->raw_supply (r, &l); | 
 |     } | 
 |   else | 
 |     { | 
 |       if (th->stopped_at_software_breakpoint | 
 | 	  && !th->pc_adjusted | 
 | 	  && r == gdbarch_pc_regnum (gdbarch)) | 
 | 	{ | 
 | 	  int size = register_size (gdbarch, r); | 
 | 	  if (size == 4) | 
 | 	    { | 
 | 	      uint32_t value; | 
 | 	      memcpy (&value, context_offset, size); | 
 | 	      value -= gdbarch_decr_pc_after_break (gdbarch); | 
 | 	      memcpy (context_offset, &value, size); | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      gdb_assert (size == 8); | 
 | 	      uint64_t value; | 
 | 	      memcpy (&value, context_offset, size); | 
 | 	      value -= gdbarch_decr_pc_after_break (gdbarch); | 
 | 	      memcpy (context_offset, &value, size); | 
 | 	    } | 
 | 	  /* Make sure we only rewrite the PC a single time.  */ | 
 | 	  th->pc_adjusted = true; | 
 | 	} | 
 |       regcache->raw_supply (r, context_offset); | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::fetch_registers (struct regcache *regcache, int r) | 
 | { | 
 |   windows_thread_info *th | 
 |     = windows_process.thread_rec (regcache->ptid (), INVALIDATE_CONTEXT); | 
 |  | 
 |   /* Check if TH exists.  Windows sometimes uses a non-existent | 
 |      thread id in its events.  */ | 
 |   if (th == NULL) | 
 |     return; | 
 |  | 
 |   if (th->reload_context) | 
 |     { | 
 | #ifdef __CYGWIN__ | 
 |       if (windows_process.have_saved_context) | 
 | 	{ | 
 | 	  /* Lie about where the program actually is stopped since | 
 | 	     cygwin has informed us that we should consider the signal | 
 | 	     to have occurred at another location which is stored in | 
 | 	     "saved_context.  */ | 
 | 	  memcpy (&th->context, &windows_process.saved_context, | 
 | 		  __COPY_CONTEXT_SIZE); | 
 | 	  windows_process.have_saved_context = 0; | 
 | 	} | 
 |       else | 
 | #endif | 
 | #ifdef __x86_64__ | 
 |       if (windows_process.wow64_process) | 
 | 	{ | 
 | 	  th->wow64_context.ContextFlags = CONTEXT_DEBUGGER_DR; | 
 | 	  CHECK (Wow64GetThreadContext (th->h, &th->wow64_context)); | 
 | 	  /* Copy dr values from that thread. | 
 | 	     But only if there were not modified since last stop. | 
 | 	     PR gdb/2388 */ | 
 | 	  if (!th->debug_registers_changed) | 
 | 	    { | 
 | 	      windows_process.dr[0] = th->wow64_context.Dr0; | 
 | 	      windows_process.dr[1] = th->wow64_context.Dr1; | 
 | 	      windows_process.dr[2] = th->wow64_context.Dr2; | 
 | 	      windows_process.dr[3] = th->wow64_context.Dr3; | 
 | 	      windows_process.dr[6] = th->wow64_context.Dr6; | 
 | 	      windows_process.dr[7] = th->wow64_context.Dr7; | 
 | 	    } | 
 | 	} | 
 |       else | 
 | #endif | 
 | 	{ | 
 | 	  th->context.ContextFlags = CONTEXT_DEBUGGER_DR; | 
 | 	  CHECK (GetThreadContext (th->h, &th->context)); | 
 | 	  /* Copy dr values from that thread. | 
 | 	     But only if there were not modified since last stop. | 
 | 	     PR gdb/2388 */ | 
 | 	  if (!th->debug_registers_changed) | 
 | 	    { | 
 | 	      windows_process.dr[0] = th->context.Dr0; | 
 | 	      windows_process.dr[1] = th->context.Dr1; | 
 | 	      windows_process.dr[2] = th->context.Dr2; | 
 | 	      windows_process.dr[3] = th->context.Dr3; | 
 | 	      windows_process.dr[6] = th->context.Dr6; | 
 | 	      windows_process.dr[7] = th->context.Dr7; | 
 | 	    } | 
 | 	} | 
 |       th->reload_context = false; | 
 |     } | 
 |  | 
 |   if (r < 0) | 
 |     for (r = 0; r < gdbarch_num_regs (regcache->arch()); r++) | 
 |       windows_fetch_one_register (regcache, th, r); | 
 |   else | 
 |     windows_fetch_one_register (regcache, th, r); | 
 | } | 
 |  | 
 | /* Collect the register number R from the given regcache, and store | 
 |    its value into the corresponding area of the given thread's context. | 
 |  | 
 |    This function assumes that R is non-negative.  A failed assertion | 
 |    assertion is raised if that is not true.  */ | 
 |  | 
 | static void | 
 | windows_store_one_register (const struct regcache *regcache, | 
 | 			    windows_thread_info *th, int r) | 
 | { | 
 |   gdb_assert (r >= 0); | 
 |  | 
 |   char *context_ptr = (char *) &th->context; | 
 | #ifdef __x86_64__ | 
 |   if (windows_process.wow64_process) | 
 |     context_ptr = (char *) &th->wow64_context; | 
 | #endif | 
 |  | 
 |   struct gdbarch *gdbarch = regcache->arch (); | 
 |   i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch); | 
 |  | 
 |   /* GDB treats some registers as 32-bit, where they are in fact only | 
 |      16 bits long.  These cases must be handled specially to avoid | 
 |      overwriting other registers in the context.  */ | 
 |   if (r == I387_FISEG_REGNUM (tdep) || windows_process.segment_register_p (r)) | 
 |     { | 
 |       gdb_byte bytes[4]; | 
 |       regcache->raw_collect (r, bytes); | 
 |       memcpy (context_ptr + windows_process.mappings[r], bytes, 2); | 
 |     } | 
 |   else if (r == I387_FOP_REGNUM (tdep)) | 
 |     { | 
 |       gdb_byte bytes[4]; | 
 |       regcache->raw_collect (r, bytes); | 
 |       /* The value of FOP occupies the top two bytes in the context, | 
 | 	 so write the two low-order bytes from the cache into the | 
 | 	 appropriate spot.  */ | 
 |       memcpy (context_ptr + windows_process.mappings[r] + 2, bytes, 2); | 
 |     } | 
 |   else | 
 |     regcache->raw_collect (r, context_ptr + windows_process.mappings[r]); | 
 | } | 
 |  | 
 | /* Store a new register value into the context of the thread tied to | 
 |    REGCACHE.  */ | 
 |  | 
 | void | 
 | windows_nat_target::store_registers (struct regcache *regcache, int r) | 
 | { | 
 |   windows_thread_info *th | 
 |     = windows_process.thread_rec (regcache->ptid (), INVALIDATE_CONTEXT); | 
 |  | 
 |   /* Check if TH exists.  Windows sometimes uses a non-existent | 
 |      thread id in its events.  */ | 
 |   if (th == NULL) | 
 |     return; | 
 |  | 
 |   if (r < 0) | 
 |     for (r = 0; r < gdbarch_num_regs (regcache->arch ()); r++) | 
 |       windows_store_one_register (regcache, th, r); | 
 |   else | 
 |     windows_store_one_register (regcache, th, r); | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | static windows_solib * | 
 | windows_make_so (const char *name, LPVOID load_addr) | 
 | { | 
 | #ifndef __CYGWIN__ | 
 |   char *p; | 
 |   char buf[__PMAX]; | 
 |   char cwd[__PMAX]; | 
 |   WIN32_FIND_DATA w32_fd; | 
 |   HANDLE h = FindFirstFile(name, &w32_fd); | 
 |  | 
 |   if (h == INVALID_HANDLE_VALUE) | 
 |     strcpy (buf, name); | 
 |   else | 
 |     { | 
 |       FindClose (h); | 
 |       strcpy (buf, name); | 
 |       if (GetCurrentDirectory (MAX_PATH + 1, cwd)) | 
 | 	{ | 
 | 	  p = strrchr (buf, '\\'); | 
 | 	  if (p) | 
 | 	    p[1] = '\0'; | 
 | 	  SetCurrentDirectory (buf); | 
 | 	  GetFullPathName (w32_fd.cFileName, MAX_PATH, buf, &p); | 
 | 	  SetCurrentDirectory (cwd); | 
 | 	} | 
 |     } | 
 |   if (strcasecmp (buf, "ntdll.dll") == 0) | 
 |     { | 
 |       GetSystemDirectory (buf, sizeof (buf)); | 
 |       strcat (buf, "\\ntdll.dll"); | 
 |     } | 
 | #else | 
 |   wchar_t buf[__PMAX]; | 
 |  | 
 |   buf[0] = 0; | 
 |   if (access (name, F_OK) != 0) | 
 |     { | 
 |       if (strcasecmp (name, "ntdll.dll") == 0) | 
 | 	{ | 
 | 	  GetSystemDirectoryW (buf, sizeof (buf) / sizeof (wchar_t)); | 
 | 	  wcscat (buf, L"\\ntdll.dll"); | 
 | 	} | 
 |     } | 
 | #endif | 
 |   windows_solib *so = &windows_process.solibs.emplace_back (); | 
 |   so->load_addr = load_addr; | 
 |   so->original_name = name; | 
 | #ifndef __CYGWIN__ | 
 |   so->name = buf; | 
 | #else | 
 |   if (buf[0]) | 
 |     { | 
 |       char cname[SO_NAME_MAX_PATH_SIZE]; | 
 |       cygwin_conv_path (CCP_WIN_W_TO_POSIX, buf, cname, | 
 | 			SO_NAME_MAX_PATH_SIZE); | 
 |       so->name = cname; | 
 |     } | 
 |   else | 
 |     { | 
 |       char *rname = realpath (name, NULL); | 
 |       if (rname && strlen (rname) < SO_NAME_MAX_PATH_SIZE) | 
 | 	{ | 
 | 	  so->name = rname; | 
 | 	  free (rname); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  warning (_("dll path for \"%s\" too long or inaccessible"), name); | 
 | 	  so->name = so->original_name; | 
 | 	} | 
 |     } | 
 |   /* Record cygwin1.dll .text start/end.  */ | 
 |   size_t len = sizeof ("/cygwin1.dll") - 1; | 
 |   if (so->name.size () >= len | 
 |       && strcasecmp (so->name.c_str () + so->name.size () - len, | 
 | 		     "/cygwin1.dll") == 0) | 
 |     { | 
 |       asection *text = NULL; | 
 |  | 
 |       gdb_bfd_ref_ptr abfd (gdb_bfd_open (so->name.c_str(), "pei-i386")); | 
 |  | 
 |       if (abfd == NULL) | 
 | 	return so; | 
 |  | 
 |       if (bfd_check_format (abfd.get (), bfd_object)) | 
 | 	text = bfd_get_section_by_name (abfd.get (), ".text"); | 
 |  | 
 |       if (!text) | 
 | 	return so; | 
 |  | 
 |       /* The symbols in a dll are offset by 0x1000, which is the | 
 | 	 offset from 0 of the first byte in an image - because of the | 
 | 	 file header and the section alignment.  */ | 
 |       windows_process.cygwin_load_start = (CORE_ADDR) (uintptr_t) ((char *) | 
 | 								   load_addr + 0x1000); | 
 |       windows_process.cygwin_load_end = windows_process.cygwin_load_start + | 
 | 	bfd_section_size (text); | 
 |     } | 
 | #endif | 
 |  | 
 |   return so; | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | void | 
 | windows_per_inferior::handle_load_dll (const char *dll_name, LPVOID base) | 
 | { | 
 |   windows_solib *solib = windows_make_so (dll_name, base); | 
 |   DEBUG_EVENTS ("Loading dll \"%s\" at %s.", solib->name.c_str (), | 
 | 		host_address_to_string (solib->load_addr)); | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | void | 
 | windows_per_inferior::handle_unload_dll () | 
 | { | 
 |   LPVOID lpBaseOfDll = current_event.u.UnloadDll.lpBaseOfDll; | 
 |  | 
 |   auto iter = std::remove_if (windows_process.solibs.begin (), | 
 | 			      windows_process.solibs.end (), | 
 | 			      [&] (windows_solib &lib) | 
 |     { | 
 |       if (lib.load_addr == lpBaseOfDll) | 
 | 	{ | 
 | 	  DEBUG_EVENTS ("Unloading dll \"%s\".", lib.name.c_str ()); | 
 | 	  return true; | 
 | 	} | 
 |       return false; | 
 |     }); | 
 |  | 
 |   if (iter != windows_process.solibs.end ()) | 
 |     { | 
 |       windows_process.solibs.erase (iter, windows_process.solibs.end ()); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* We did not find any DLL that was previously loaded at this address, | 
 |      so register a complaint.  We do not report an error, because we have | 
 |      observed that this may be happening under some circumstances.  For | 
 |      instance, running 32bit applications on x64 Windows causes us to receive | 
 |      4 mysterious UNLOAD_DLL_DEBUG_EVENTs during the startup phase (these | 
 |      events are apparently caused by the WOW layer, the interface between | 
 |      32bit and 64bit worlds).  */ | 
 |   complaint (_("dll starting at %s not found."), | 
 | 	     host_address_to_string (lpBaseOfDll)); | 
 | } | 
 |  | 
 | /* Clear list of loaded DLLs.  */ | 
 | static void | 
 | windows_clear_solib (void) | 
 | { | 
 |   windows_process.solibs.clear (); | 
 | } | 
 |  | 
 | static void | 
 | signal_event_command (const char *args, int from_tty) | 
 | { | 
 |   uintptr_t event_id = 0; | 
 |   char *endargs = NULL; | 
 |  | 
 |   if (args == NULL) | 
 |     error (_("signal-event requires an argument (integer event id)")); | 
 |  | 
 |   event_id = strtoumax (args, &endargs, 10); | 
 |  | 
 |   if ((errno == ERANGE) || (event_id == 0) || (event_id > UINTPTR_MAX) || | 
 |       ((HANDLE) event_id == INVALID_HANDLE_VALUE)) | 
 |     error (_("Failed to convert `%s' to event id"), args); | 
 |  | 
 |   SetEvent ((HANDLE) event_id); | 
 |   CloseHandle ((HANDLE) event_id); | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | int | 
 | windows_per_inferior::handle_output_debug_string | 
 |      (struct target_waitstatus *ourstatus) | 
 | { | 
 |   int retval = 0; | 
 |  | 
 |   gdb::unique_xmalloc_ptr<char> s | 
 |     = (target_read_string | 
 |        ((CORE_ADDR) (uintptr_t) current_event.u.DebugString.lpDebugStringData, | 
 | 	1024)); | 
 |   if (s == nullptr || !*(s.get ())) | 
 |     /* nothing to do */; | 
 |   else if (!startswith (s.get (), _CYGWIN_SIGNAL_STRING)) | 
 |     { | 
 | #ifdef __CYGWIN__ | 
 |       if (!startswith (s.get (), "cYg")) | 
 | #endif | 
 | 	{ | 
 | 	  char *p = strchr (s.get (), '\0'); | 
 |  | 
 | 	  if (p > s.get () && *--p == '\n') | 
 | 	    *p = '\0'; | 
 | 	  warning (("%s"), s.get ()); | 
 | 	} | 
 |     } | 
 | #ifdef __CYGWIN__ | 
 |   else | 
 |     { | 
 |       /* Got a cygwin signal marker.  A cygwin signal is followed by | 
 | 	 the signal number itself and then optionally followed by the | 
 | 	 thread id and address to saved context within the DLL.  If | 
 | 	 these are supplied, then the given thread is assumed to have | 
 | 	 issued the signal and the context from the thread is assumed | 
 | 	 to be stored at the given address in the inferior.  Tell gdb | 
 | 	 to treat this like a real signal.  */ | 
 |       char *p; | 
 |       int sig = strtol (s.get () + sizeof (_CYGWIN_SIGNAL_STRING) - 1, &p, 0); | 
 |       gdb_signal gotasig = gdb_signal_from_host (sig); | 
 |  | 
 |       if (gotasig) | 
 | 	{ | 
 | 	  LPCVOID x; | 
 | 	  SIZE_T n; | 
 |  | 
 | 	  ourstatus->set_stopped (gotasig); | 
 | 	  retval = strtoul (p, &p, 0); | 
 | 	  if (!retval) | 
 | 	    retval = current_event.dwThreadId; | 
 | 	  else if ((x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0)) | 
 | 		   && ReadProcessMemory (handle, x, | 
 | 					 &saved_context, | 
 | 					 __COPY_CONTEXT_SIZE, &n) | 
 | 		   && n == __COPY_CONTEXT_SIZE) | 
 | 	    have_saved_context = 1; | 
 | 	} | 
 |     } | 
 | #endif | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | static int | 
 | display_selector (HANDLE thread, DWORD sel) | 
 | { | 
 |   LDT_ENTRY info; | 
 |   BOOL ret; | 
 | #ifdef __x86_64__ | 
 |   if (windows_process.wow64_process) | 
 |     ret = Wow64GetThreadSelectorEntry (thread, sel, &info); | 
 |   else | 
 | #endif | 
 |     ret = GetThreadSelectorEntry (thread, sel, &info); | 
 |   if (ret) | 
 |     { | 
 |       int base, limit; | 
 |       gdb_printf ("0x%03x: ", (unsigned) sel); | 
 |       if (!info.HighWord.Bits.Pres) | 
 | 	{ | 
 | 	  gdb_puts ("Segment not present\n"); | 
 | 	  return 0; | 
 | 	} | 
 |       base = (info.HighWord.Bits.BaseHi << 24) + | 
 | 	     (info.HighWord.Bits.BaseMid << 16) | 
 | 	     + info.BaseLow; | 
 |       limit = (info.HighWord.Bits.LimitHi << 16) + info.LimitLow; | 
 |       if (info.HighWord.Bits.Granularity) | 
 | 	limit = (limit << 12) | 0xfff; | 
 |       gdb_printf ("base=0x%08x limit=0x%08x", base, limit); | 
 |       if (info.HighWord.Bits.Default_Big) | 
 | 	gdb_puts(" 32-bit "); | 
 |       else | 
 | 	gdb_puts(" 16-bit "); | 
 |       switch ((info.HighWord.Bits.Type & 0xf) >> 1) | 
 | 	{ | 
 | 	case 0: | 
 | 	  gdb_puts ("Data (Read-Only, Exp-up"); | 
 | 	  break; | 
 | 	case 1: | 
 | 	  gdb_puts ("Data (Read/Write, Exp-up"); | 
 | 	  break; | 
 | 	case 2: | 
 | 	  gdb_puts ("Unused segment ("); | 
 | 	  break; | 
 | 	case 3: | 
 | 	  gdb_puts ("Data (Read/Write, Exp-down"); | 
 | 	  break; | 
 | 	case 4: | 
 | 	  gdb_puts ("Code (Exec-Only, N.Conf"); | 
 | 	  break; | 
 | 	case 5: | 
 | 	  gdb_puts ("Code (Exec/Read, N.Conf"); | 
 | 	  break; | 
 | 	case 6: | 
 | 	  gdb_puts ("Code (Exec-Only, Conf"); | 
 | 	  break; | 
 | 	case 7: | 
 | 	  gdb_puts ("Code (Exec/Read, Conf"); | 
 | 	  break; | 
 | 	default: | 
 | 	  gdb_printf ("Unknown type 0x%lx", | 
 | 		      (unsigned long) info.HighWord.Bits.Type); | 
 | 	} | 
 |       if ((info.HighWord.Bits.Type & 0x1) == 0) | 
 | 	gdb_puts(", N.Acc"); | 
 |       gdb_puts (")\n"); | 
 |       if ((info.HighWord.Bits.Type & 0x10) == 0) | 
 | 	gdb_puts("System selector "); | 
 |       gdb_printf ("Privilege level = %ld. ", | 
 | 		  (unsigned long) info.HighWord.Bits.Dpl); | 
 |       if (info.HighWord.Bits.Granularity) | 
 | 	gdb_puts ("Page granular.\n"); | 
 |       else | 
 | 	gdb_puts ("Byte granular.\n"); | 
 |       return 1; | 
 |     } | 
 |   else | 
 |     { | 
 |       DWORD err = GetLastError (); | 
 |       if (err == ERROR_NOT_SUPPORTED) | 
 | 	gdb_printf ("Function not supported\n"); | 
 |       else | 
 | 	gdb_printf ("Invalid selector 0x%x.\n", (unsigned) sel); | 
 |       return 0; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | display_selectors (const char * args, int from_tty) | 
 | { | 
 |   if (inferior_ptid == null_ptid) | 
 |     { | 
 |       gdb_puts ("Impossible to display selectors now.\n"); | 
 |       return; | 
 |     } | 
 |  | 
 |   windows_thread_info *current_windows_thread | 
 |     = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT); | 
 |  | 
 |   if (!args) | 
 |     { | 
 | #ifdef __x86_64__ | 
 |       if (windows_process.wow64_process) | 
 | 	{ | 
 | 	  gdb_puts ("Selector $cs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegCs); | 
 | 	  gdb_puts ("Selector $ds\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegDs); | 
 | 	  gdb_puts ("Selector $es\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegEs); | 
 | 	  gdb_puts ("Selector $ss\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegSs); | 
 | 	  gdb_puts ("Selector $fs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegFs); | 
 | 	  gdb_puts ("Selector $gs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->wow64_context.SegGs); | 
 | 	} | 
 |       else | 
 | #endif | 
 | 	{ | 
 | 	  gdb_puts ("Selector $cs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegCs); | 
 | 	  gdb_puts ("Selector $ds\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegDs); | 
 | 	  gdb_puts ("Selector $es\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegEs); | 
 | 	  gdb_puts ("Selector $ss\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegSs); | 
 | 	  gdb_puts ("Selector $fs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegFs); | 
 | 	  gdb_puts ("Selector $gs\n"); | 
 | 	  display_selector (current_windows_thread->h, | 
 | 			    current_windows_thread->context.SegGs); | 
 | 	} | 
 |     } | 
 |   else | 
 |     { | 
 |       int sel; | 
 |       sel = parse_and_eval_long (args); | 
 |       gdb_printf ("Selector \"%s\"\n",args); | 
 |       display_selector (current_windows_thread->h, sel); | 
 |     } | 
 | } | 
 |  | 
 | /* See nat/windows-nat.h.  */ | 
 |  | 
 | bool | 
 | windows_per_inferior::handle_access_violation | 
 |      (const EXCEPTION_RECORD *rec) | 
 | { | 
 | #ifdef __CYGWIN__ | 
 |   /* See if the access violation happened within the cygwin DLL | 
 |      itself.  Cygwin uses a kind of exception handling to deal with | 
 |      passed-in invalid addresses.  gdb should not treat these as real | 
 |      SEGVs since they will be silently handled by cygwin.  A real SEGV | 
 |      will (theoretically) be caught by cygwin later in the process and | 
 |      will be sent as a cygwin-specific-signal.  So, ignore SEGVs if | 
 |      they show up within the text segment of the DLL itself.  */ | 
 |   const char *fn; | 
 |   CORE_ADDR addr = (CORE_ADDR) (uintptr_t) rec->ExceptionAddress; | 
 |  | 
 |   if ((!cygwin_exceptions && (addr >= cygwin_load_start | 
 | 			      && addr < cygwin_load_end)) | 
 |       || (find_pc_partial_function (addr, &fn, NULL, NULL) | 
 | 	  && startswith (fn, "KERNEL32!IsBad"))) | 
 |     return true; | 
 | #endif | 
 |   return false; | 
 | } | 
 |  | 
 | /* Resume thread specified by ID, or all artificially suspended | 
 |    threads, if we are continuing execution.  KILLED non-zero means we | 
 |    have killed the inferior, so we should ignore weird errors due to | 
 |    threads shutting down.  LAST_CALL is true if we expect this to be | 
 |    the last call to continue the inferior -- we are either mourning it | 
 |    or detaching.  */ | 
 | BOOL | 
 | windows_nat_target::windows_continue (DWORD continue_status, int id, | 
 | 				      int killed, bool last_call) | 
 | { | 
 |   windows_process.desired_stop_thread_id = id; | 
 |  | 
 |   if (windows_process.matching_pending_stop (debug_events)) | 
 |     { | 
 |       /* There's no need to really continue, because there's already | 
 | 	 another event pending.  However, we do need to inform the | 
 | 	 event loop of this.  */ | 
 |       serial_event_set (m_wait_event); | 
 |       return TRUE; | 
 |     } | 
 |  | 
 |   for (auto &th : windows_process.thread_list) | 
 |     if (id == -1 || id == (int) th->tid) | 
 |       { | 
 | #ifdef __x86_64__ | 
 | 	if (windows_process.wow64_process) | 
 | 	  { | 
 | 	    if (th->debug_registers_changed) | 
 | 	      { | 
 | 		th->wow64_context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; | 
 | 		th->wow64_context.Dr0 = windows_process.dr[0]; | 
 | 		th->wow64_context.Dr1 = windows_process.dr[1]; | 
 | 		th->wow64_context.Dr2 = windows_process.dr[2]; | 
 | 		th->wow64_context.Dr3 = windows_process.dr[3]; | 
 | 		th->wow64_context.Dr6 = DR6_CLEAR_VALUE; | 
 | 		th->wow64_context.Dr7 = windows_process.dr[7]; | 
 | 		th->debug_registers_changed = false; | 
 | 	      } | 
 | 	    if (th->wow64_context.ContextFlags) | 
 | 	      { | 
 | 		DWORD ec = 0; | 
 |  | 
 | 		if (GetExitCodeThread (th->h, &ec) | 
 | 		    && ec == STILL_ACTIVE) | 
 | 		  { | 
 | 		    BOOL status = Wow64SetThreadContext (th->h, | 
 | 							 &th->wow64_context); | 
 |  | 
 | 		    if (!killed) | 
 | 		      CHECK (status); | 
 | 		  } | 
 | 		th->wow64_context.ContextFlags = 0; | 
 | 	      } | 
 | 	  } | 
 | 	else | 
 | #endif | 
 | 	  { | 
 | 	    if (th->debug_registers_changed) | 
 | 	      { | 
 | 		th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; | 
 | 		th->context.Dr0 = windows_process.dr[0]; | 
 | 		th->context.Dr1 = windows_process.dr[1]; | 
 | 		th->context.Dr2 = windows_process.dr[2]; | 
 | 		th->context.Dr3 = windows_process.dr[3]; | 
 | 		th->context.Dr6 = DR6_CLEAR_VALUE; | 
 | 		th->context.Dr7 = windows_process.dr[7]; | 
 | 		th->debug_registers_changed = false; | 
 | 	      } | 
 | 	    if (th->context.ContextFlags) | 
 | 	      { | 
 | 		DWORD ec = 0; | 
 |  | 
 | 		if (GetExitCodeThread (th->h, &ec) | 
 | 		    && ec == STILL_ACTIVE) | 
 | 		  { | 
 | 		    BOOL status = SetThreadContext (th->h, &th->context); | 
 |  | 
 | 		    if (!killed) | 
 | 		      CHECK (status); | 
 | 		  } | 
 | 		th->context.ContextFlags = 0; | 
 | 	      } | 
 | 	  } | 
 | 	th->resume (); | 
 |       } | 
 |     else | 
 |       { | 
 | 	/* When single-stepping a specific thread, other threads must | 
 | 	   be suspended.  */ | 
 | 	th->suspend (); | 
 |       } | 
 |  | 
 |   std::optional<unsigned> err; | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       if (!continue_last_debug_event (continue_status, debug_events)) | 
 | 	err = (unsigned) GetLastError (); | 
 |       /* On the last call, do not block waiting for an event that will | 
 | 	 never come.  */ | 
 |       return !last_call; | 
 |     }); | 
 |  | 
 |   if (err.has_value ()) | 
 |     throw_winerror_with_name (_("Failed to resume program execution" | 
 | 				" - ContinueDebugEvent failed"), | 
 | 			      *err); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /* Called in pathological case where Windows fails to send a | 
 |    CREATE_PROCESS_DEBUG_EVENT after an attach.  */ | 
 | DWORD | 
 | windows_nat_target::fake_create_process () | 
 | { | 
 |   windows_process.handle | 
 |     = OpenProcess (PROCESS_ALL_ACCESS, FALSE, | 
 | 		   windows_process.current_event.dwProcessId); | 
 |   if (windows_process.handle != NULL) | 
 |     windows_process.open_process_used = 1; | 
 |   else | 
 |     { | 
 |       unsigned err = (unsigned) GetLastError (); | 
 |       throw_winerror_with_name (_("OpenProcess call failed"), err); | 
 |       /*  We can not debug anything in that case.  */ | 
 |     } | 
 |   add_thread (ptid_t (windows_process.current_event.dwProcessId, 0, | 
 | 			      windows_process.current_event.dwThreadId), | 
 | 		      windows_process.current_event.u.CreateThread.hThread, | 
 | 		      windows_process.current_event.u.CreateThread.lpThreadLocalBase, | 
 | 		      true /* main_thread_p */); | 
 |   return windows_process.current_event.dwThreadId; | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig) | 
 | { | 
 |   windows_thread_info *th; | 
 |   DWORD continue_status = DBG_CONTINUE; | 
 |  | 
 |   /* A specific PTID means `step only this thread id'.  */ | 
 |   int resume_all = ptid == minus_one_ptid; | 
 |  | 
 |   /* If we're continuing all threads, it's the current inferior that | 
 |      should be handled specially.  */ | 
 |   if (resume_all) | 
 |     ptid = inferior_ptid; | 
 |  | 
 |   if (sig != GDB_SIGNAL_0) | 
 |     { | 
 |       if (windows_process.current_event.dwDebugEventCode | 
 | 	  != EXCEPTION_DEBUG_EVENT) | 
 | 	{ | 
 | 	  DEBUG_EXCEPT ("Cannot continue with signal %d here.", sig); | 
 | 	} | 
 |       else if (sig == windows_process.last_sig) | 
 | 	continue_status = DBG_EXCEPTION_NOT_HANDLED; | 
 |       else | 
 | #if 0 | 
 | /* This code does not seem to work, because | 
 |   the kernel does probably not consider changes in the ExceptionRecord | 
 |   structure when passing the exception to the inferior. | 
 |   Note that this seems possible in the exception handler itself.  */ | 
 | 	{ | 
 | 	  for (const xlate_exception &x : xlate) | 
 | 	    if (x.us == sig) | 
 | 	      { | 
 | 		current_event.u.Exception.ExceptionRecord.ExceptionCode | 
 | 		  = x.them; | 
 | 		continue_status = DBG_EXCEPTION_NOT_HANDLED; | 
 | 		break; | 
 | 	      } | 
 | 	  if (continue_status == DBG_CONTINUE) | 
 | 	    { | 
 | 	      DEBUG_EXCEPT ("Cannot continue with signal %d.", sig); | 
 | 	    } | 
 | 	} | 
 | #endif | 
 |       DEBUG_EXCEPT ("Can only continue with received signal %d.", | 
 | 		    windows_process.last_sig); | 
 |     } | 
 |  | 
 |   windows_process.last_sig = GDB_SIGNAL_0; | 
 |  | 
 |   DEBUG_EXEC ("pid=%d, tid=0x%x, step=%d, sig=%d", | 
 | 	      ptid.pid (), (unsigned) ptid.lwp (), step, sig); | 
 |  | 
 |   /* Get context for currently selected thread.  */ | 
 |   th = windows_process.thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT); | 
 |   if (th) | 
 |     { | 
 | #ifdef __x86_64__ | 
 |       if (windows_process.wow64_process) | 
 | 	{ | 
 | 	  if (step) | 
 | 	    { | 
 | 	      /* Single step by setting t bit.  */ | 
 | 	      regcache *regcache = get_thread_regcache (inferior_thread ()); | 
 | 	      struct gdbarch *gdbarch = regcache->arch (); | 
 | 	      fetch_registers (regcache, gdbarch_ps_regnum (gdbarch)); | 
 | 	      th->wow64_context.EFlags |= FLAG_TRACE_BIT; | 
 | 	    } | 
 |  | 
 | 	  if (th->wow64_context.ContextFlags) | 
 | 	    { | 
 | 	      if (th->debug_registers_changed) | 
 | 		{ | 
 | 		  th->wow64_context.Dr0 = windows_process.dr[0]; | 
 | 		  th->wow64_context.Dr1 = windows_process.dr[1]; | 
 | 		  th->wow64_context.Dr2 = windows_process.dr[2]; | 
 | 		  th->wow64_context.Dr3 = windows_process.dr[3]; | 
 | 		  th->wow64_context.Dr6 = DR6_CLEAR_VALUE; | 
 | 		  th->wow64_context.Dr7 = windows_process.dr[7]; | 
 | 		  th->debug_registers_changed = false; | 
 | 		} | 
 | 	      CHECK (Wow64SetThreadContext (th->h, &th->wow64_context)); | 
 | 	      th->wow64_context.ContextFlags = 0; | 
 | 	    } | 
 | 	} | 
 |       else | 
 | #endif | 
 | 	{ | 
 | 	  if (step) | 
 | 	    { | 
 | 	      /* Single step by setting t bit.  */ | 
 | 	      regcache *regcache = get_thread_regcache (inferior_thread ()); | 
 | 	      struct gdbarch *gdbarch = regcache->arch (); | 
 | 	      fetch_registers (regcache, gdbarch_ps_regnum (gdbarch)); | 
 | 	      th->context.EFlags |= FLAG_TRACE_BIT; | 
 | 	    } | 
 |  | 
 | 	  if (th->context.ContextFlags) | 
 | 	    { | 
 | 	      if (th->debug_registers_changed) | 
 | 		{ | 
 | 		  th->context.Dr0 = windows_process.dr[0]; | 
 | 		  th->context.Dr1 = windows_process.dr[1]; | 
 | 		  th->context.Dr2 = windows_process.dr[2]; | 
 | 		  th->context.Dr3 = windows_process.dr[3]; | 
 | 		  th->context.Dr6 = DR6_CLEAR_VALUE; | 
 | 		  th->context.Dr7 = windows_process.dr[7]; | 
 | 		  th->debug_registers_changed = false; | 
 | 		} | 
 | 	      CHECK (SetThreadContext (th->h, &th->context)); | 
 | 	      th->context.ContextFlags = 0; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   /* Allow continuing with the same signal that interrupted us. | 
 |      Otherwise complain.  */ | 
 |  | 
 |   if (resume_all) | 
 |     windows_continue (continue_status, -1, 0); | 
 |   else | 
 |     windows_continue (continue_status, ptid.lwp (), 0); | 
 | } | 
 |  | 
 | /* Interrupt the inferior.  */ | 
 |  | 
 | void | 
 | windows_nat_target::interrupt () | 
 | { | 
 |   DEBUG_EVENTS ("interrupt"); | 
 | #ifdef __x86_64__ | 
 |   if (windows_process.wow64_process) | 
 |     { | 
 |       /* Call DbgUiRemoteBreakin of the 32bit ntdll.dll in the target process. | 
 | 	 DebugBreakProcess would call the one of the 64bit ntdll.dll, which | 
 | 	 can't be correctly handled by gdb.  */ | 
 |       if (windows_process.wow64_dbgbreak == nullptr) | 
 | 	{ | 
 | 	  CORE_ADDR addr; | 
 | 	  if (!find_minimal_symbol_address ("ntdll!DbgUiRemoteBreakin", | 
 | 					    &addr, 0)) | 
 | 	    windows_process.wow64_dbgbreak = (void *) addr; | 
 | 	} | 
 |  | 
 |       if (windows_process.wow64_dbgbreak != nullptr) | 
 | 	{ | 
 | 	  HANDLE thread = CreateRemoteThread (windows_process.handle, NULL, | 
 | 					      0, (LPTHREAD_START_ROUTINE) | 
 | 					      windows_process.wow64_dbgbreak, | 
 | 					      NULL, 0, NULL); | 
 | 	  if (thread) | 
 | 	    { | 
 | 	      CloseHandle (thread); | 
 | 	      return; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |   else | 
 | #endif | 
 |     if (DebugBreakProcess (windows_process.handle)) | 
 |       return; | 
 |   warning (_("Could not interrupt program.  " | 
 | 	     "Press Ctrl-c in the program console.")); | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::pass_ctrlc () | 
 | { | 
 |   interrupt (); | 
 | } | 
 |  | 
 | /* Get the next event from the child.  Returns the thread ptid.  */ | 
 |  | 
 | ptid_t | 
 | windows_nat_target::get_windows_debug_event | 
 |      (int pid, struct target_waitstatus *ourstatus, target_wait_flags options) | 
 | { | 
 |   DWORD continue_status, event_code; | 
 |   DWORD thread_id = 0; | 
 |  | 
 |   /* If there is a relevant pending stop, report it now.  See the | 
 |      comment by the definition of "pending_stops" for details on why | 
 |      this is needed.  */ | 
 |   std::optional<pending_stop> stop | 
 |     = windows_process.fetch_pending_stop (debug_events); | 
 |   if (stop.has_value ()) | 
 |     { | 
 |       thread_id = stop->thread_id; | 
 |       *ourstatus = stop->status; | 
 |  | 
 |       ptid_t ptid (windows_process.current_event.dwProcessId, thread_id); | 
 |       windows_thread_info *th | 
 | 	= windows_process.thread_rec (ptid, INVALIDATE_CONTEXT); | 
 |       th->reload_context = true; | 
 |  | 
 |       return ptid; | 
 |     } | 
 |  | 
 |   windows_process.last_sig = GDB_SIGNAL_0; | 
 |   DEBUG_EVENT *current_event = &windows_process.current_event; | 
 |  | 
 |   if ((options & TARGET_WNOHANG) != 0 && !m_debug_event_pending) | 
 |     { | 
 |       ourstatus->set_ignore (); | 
 |       return minus_one_ptid; | 
 |     } | 
 |  | 
 |   wait_for_debug_event_main_thread (&windows_process.current_event); | 
 |  | 
 |   continue_status = DBG_CONTINUE; | 
 |  | 
 |   event_code = windows_process.current_event.dwDebugEventCode; | 
 |   ourstatus->set_spurious (); | 
 |   windows_process.have_saved_context = 0; | 
 |  | 
 |   switch (event_code) | 
 |     { | 
 |     case CREATE_THREAD_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "CREATE_THREAD_DEBUG_EVENT"); | 
 |       if (windows_process.saw_create != 1) | 
 | 	{ | 
 | 	  inferior *inf = find_inferior_pid (this, current_event->dwProcessId); | 
 | 	  if (!windows_process.saw_create && inf->attach_flag) | 
 | 	    { | 
 | 	      /* Kludge around a Windows bug where first event is a create | 
 | 		 thread event.  Caused when attached process does not have | 
 | 		 a main thread.  */ | 
 | 	      thread_id = fake_create_process (); | 
 | 	      if (thread_id) | 
 | 		windows_process.saw_create++; | 
 | 	    } | 
 | 	  break; | 
 | 	} | 
 |       /* Record the existence of this thread.  */ | 
 |       thread_id = current_event->dwThreadId; | 
 |       add_thread | 
 | 	(ptid_t (current_event->dwProcessId, current_event->dwThreadId, 0), | 
 | 	 current_event->u.CreateThread.hThread, | 
 | 	 current_event->u.CreateThread.lpThreadLocalBase, | 
 | 	 false /* main_thread_p */); | 
 |  | 
 |       break; | 
 |  | 
 |     case EXIT_THREAD_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "EXIT_THREAD_DEBUG_EVENT"); | 
 |       delete_thread (ptid_t (current_event->dwProcessId, | 
 | 			     current_event->dwThreadId, 0), | 
 | 		     current_event->u.ExitThread.dwExitCode, | 
 | 		     false /* main_thread_p */); | 
 |       break; | 
 |  | 
 |     case CREATE_PROCESS_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "CREATE_PROCESS_DEBUG_EVENT"); | 
 |       CloseHandle (current_event->u.CreateProcessInfo.hFile); | 
 |       if (++windows_process.saw_create != 1) | 
 | 	break; | 
 |  | 
 |       windows_process.handle = current_event->u.CreateProcessInfo.hProcess; | 
 |       /* Add the main thread.  */ | 
 |       add_thread | 
 | 	(ptid_t (current_event->dwProcessId, | 
 | 		 current_event->dwThreadId, 0), | 
 | 	 current_event->u.CreateProcessInfo.hThread, | 
 | 	 current_event->u.CreateProcessInfo.lpThreadLocalBase, | 
 | 	 true /* main_thread_p */); | 
 |       thread_id = current_event->dwThreadId; | 
 |       break; | 
 |  | 
 |     case EXIT_PROCESS_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "EXIT_PROCESS_DEBUG_EVENT"); | 
 |       if (!windows_process.windows_initialization_done) | 
 | 	{ | 
 | 	  target_terminal::ours (); | 
 | 	  target_mourn_inferior (inferior_ptid); | 
 | 	  error (_("During startup program exited with code 0x%x."), | 
 | 		 (unsigned int) current_event->u.ExitProcess.dwExitCode); | 
 | 	} | 
 |       else if (windows_process.saw_create == 1) | 
 | 	{ | 
 | 	  delete_thread (ptid_t (current_event->dwProcessId, | 
 | 				 current_event->dwThreadId, 0), | 
 | 			 0, true /* main_thread_p */); | 
 | 	  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->set_exited (exit_status); | 
 | 	  else | 
 | 	    ourstatus->set_signalled (gdb_signal_from_host (exit_signal)); | 
 |  | 
 | 	  thread_id = current_event->dwThreadId; | 
 | 	} | 
 |       break; | 
 |  | 
 |     case LOAD_DLL_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "LOAD_DLL_DEBUG_EVENT"); | 
 |       CloseHandle (current_event->u.LoadDll.hFile); | 
 |       if (windows_process.saw_create != 1 | 
 | 	  || ! windows_process.windows_initialization_done) | 
 | 	break; | 
 |       try | 
 | 	{ | 
 | 	  windows_process.dll_loaded_event (); | 
 | 	} | 
 |       catch (const gdb_exception &ex) | 
 | 	{ | 
 | 	  exception_print (gdb_stderr, ex); | 
 | 	} | 
 |       ourstatus->set_loaded (); | 
 |       thread_id = current_event->dwThreadId; | 
 |       break; | 
 |  | 
 |     case UNLOAD_DLL_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "UNLOAD_DLL_DEBUG_EVENT"); | 
 |       if (windows_process.saw_create != 1 | 
 | 	  || ! windows_process.windows_initialization_done) | 
 | 	break; | 
 |       try | 
 | 	{ | 
 | 	  windows_process.handle_unload_dll (); | 
 | 	} | 
 |       catch (const gdb_exception &ex) | 
 | 	{ | 
 | 	  exception_print (gdb_stderr, ex); | 
 | 	} | 
 |       ourstatus->set_loaded (); | 
 |       thread_id = current_event->dwThreadId; | 
 |       break; | 
 |  | 
 |     case EXCEPTION_DEBUG_EVENT: | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "EXCEPTION_DEBUG_EVENT"); | 
 |       if (windows_process.saw_create != 1) | 
 | 	break; | 
 |       switch (windows_process.handle_exception (ourstatus, debug_exceptions)) | 
 | 	{ | 
 | 	case HANDLE_EXCEPTION_UNHANDLED: | 
 | 	default: | 
 | 	  continue_status = DBG_EXCEPTION_NOT_HANDLED; | 
 | 	  break; | 
 | 	case HANDLE_EXCEPTION_HANDLED: | 
 | 	  thread_id = current_event->dwThreadId; | 
 | 	  break; | 
 | 	case HANDLE_EXCEPTION_IGNORED: | 
 | 	  continue_status = DBG_CONTINUE; | 
 | 	  break; | 
 | 	} | 
 |       break; | 
 |  | 
 |     case OUTPUT_DEBUG_STRING_EVENT:	/* Message from the kernel.  */ | 
 |       DEBUG_EVENTS ("kernel event for pid=%u tid=0x%x code=%s", | 
 | 		    (unsigned) current_event->dwProcessId, | 
 | 		    (unsigned) current_event->dwThreadId, | 
 | 		    "OUTPUT_DEBUG_STRING_EVENT"); | 
 |       if (windows_process.saw_create != 1) | 
 | 	break; | 
 |       thread_id = windows_process.handle_output_debug_string (ourstatus); | 
 |       break; | 
 |  | 
 |     default: | 
 |       if (windows_process.saw_create != 1) | 
 | 	break; | 
 |       gdb_printf ("gdb: kernel event for pid=%u tid=0x%x\n", | 
 | 		  (unsigned) current_event->dwProcessId, | 
 | 		  (unsigned) current_event->dwThreadId); | 
 |       gdb_printf ("                 unknown event code %u\n", | 
 | 		  (unsigned) current_event->dwDebugEventCode); | 
 |       break; | 
 |     } | 
 |  | 
 |   if (!thread_id || windows_process.saw_create != 1) | 
 |     { | 
 |       CHECK (windows_continue (continue_status, | 
 | 			       windows_process.desired_stop_thread_id, 0)); | 
 |     } | 
 |   else if (windows_process.desired_stop_thread_id != -1 | 
 | 	   && windows_process.desired_stop_thread_id != thread_id) | 
 |     { | 
 |       /* Pending stop.  See the comment by the definition of | 
 | 	 "pending_stops" for details on why this is needed.  */ | 
 |       DEBUG_EVENTS ("get_windows_debug_event - " | 
 | 		    "unexpected stop in 0x%x (expecting 0x%x)", | 
 | 		    thread_id, windows_process.desired_stop_thread_id); | 
 |  | 
 |       if (current_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT | 
 | 	  && ((current_event->u.Exception.ExceptionRecord.ExceptionCode | 
 | 	       == EXCEPTION_BREAKPOINT) | 
 | 	      || (current_event->u.Exception.ExceptionRecord.ExceptionCode | 
 | 		  == STATUS_WX86_BREAKPOINT)) | 
 | 	  && windows_process.windows_initialization_done) | 
 | 	{ | 
 | 	  ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0); | 
 | 	  windows_thread_info *th | 
 | 	    = windows_process.thread_rec (ptid, INVALIDATE_CONTEXT); | 
 | 	  th->stopped_at_software_breakpoint = true; | 
 | 	  th->pc_adjusted = false; | 
 | 	} | 
 |       windows_process.pending_stops.push_back | 
 | 	({thread_id, *ourstatus, windows_process.current_event}); | 
 |       thread_id = 0; | 
 |       CHECK (windows_continue (continue_status, | 
 | 			       windows_process.desired_stop_thread_id, 0)); | 
 |     } | 
 |  | 
 |   if (thread_id == 0) | 
 |     return null_ptid; | 
 |   return ptid_t (windows_process.current_event.dwProcessId, thread_id, 0); | 
 | } | 
 |  | 
 | /* Wait for interesting events to occur in the target process.  */ | 
 | ptid_t | 
 | windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, | 
 | 			  target_wait_flags options) | 
 | { | 
 |   int pid = -1; | 
 |  | 
 |   /* We loop when we get a non-standard exception rather than return | 
 |      with a SPURIOUS because resume can try and step or modify things, | 
 |      which needs a current_thread->h.  But some of these exceptions mark | 
 |      the birth or death of threads, which mean that the current thread | 
 |      isn't necessarily what you think it is.  */ | 
 |  | 
 |   while (1) | 
 |     { | 
 |       ptid_t result = get_windows_debug_event (pid, ourstatus, options); | 
 |  | 
 |       if (result != null_ptid) | 
 | 	{ | 
 | 	  if (ourstatus->kind () != TARGET_WAITKIND_EXITED | 
 | 	      && ourstatus->kind () !=  TARGET_WAITKIND_SIGNALLED) | 
 | 	    { | 
 | 	      windows_thread_info *th | 
 | 		= windows_process.thread_rec (result, INVALIDATE_CONTEXT); | 
 |  | 
 | 	      if (th != nullptr) | 
 | 		{ | 
 | 		  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.windows_initialization_done) | 
 | 		    { | 
 | 		      th->stopped_at_software_breakpoint = true; | 
 | 		      th->pc_adjusted = false; | 
 | 		    } | 
 | 		} | 
 | 	    } | 
 |  | 
 | 	  return result; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  int detach = 0; | 
 |  | 
 | 	  if (deprecated_ui_loop_hook != NULL) | 
 | 	    detach = deprecated_ui_loop_hook (0); | 
 |  | 
 | 	  if (detach) | 
 | 	    kill (); | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching) | 
 | { | 
 |   int i; | 
 |   struct inferior *inf; | 
 |  | 
 |   windows_process.last_sig = GDB_SIGNAL_0; | 
 |   windows_process.open_process_used = 0; | 
 |   for (i = 0; | 
 |        i < sizeof (windows_process.dr) / sizeof (windows_process.dr[0]); | 
 |        i++) | 
 |     windows_process.dr[i] = 0; | 
 | #ifdef __CYGWIN__ | 
 |   windows_process.cygwin_load_start = 0; | 
 |   windows_process.cygwin_load_end = 0; | 
 | #endif | 
 |   windows_process.current_event.dwProcessId = pid; | 
 |   memset (&windows_process.current_event, 0, | 
 | 	  sizeof (windows_process.current_event)); | 
 |   inf = current_inferior (); | 
 |   if (!inf->target_is_pushed (this)) | 
 |     inf->push_target (this); | 
 |   disable_breakpoints_in_shlibs (); | 
 |   windows_clear_solib (); | 
 |   clear_proceed_status (0); | 
 |   init_wait_for_inferior (); | 
 |  | 
 | #ifdef __x86_64__ | 
 |   windows_process.ignore_first_breakpoint | 
 |     = !attaching && windows_process.wow64_process; | 
 |  | 
 |   if (!windows_process.wow64_process) | 
 |     { | 
 |       windows_process.mappings  = amd64_mappings; | 
 |       windows_process.segment_register_p = amd64_windows_segment_register_p; | 
 |     } | 
 |   else | 
 | #endif | 
 |     { | 
 |       windows_process.mappings  = i386_mappings; | 
 |       windows_process.segment_register_p = i386_windows_segment_register_p; | 
 |     } | 
 |  | 
 |   inferior_appeared (inf, pid); | 
 |   inf->attach_flag = attaching; | 
 |  | 
 |   target_terminal::init (); | 
 |   target_terminal::inferior (); | 
 |  | 
 |   windows_process.windows_initialization_done = 0; | 
 |  | 
 |   ptid_t last_ptid; | 
 |  | 
 |   while (1) | 
 |     { | 
 |       struct target_waitstatus status; | 
 |  | 
 |       last_ptid = this->wait (minus_one_ptid, &status, 0); | 
 |  | 
 |       /* Note windows_wait returns TARGET_WAITKIND_SPURIOUS for thread | 
 | 	 events.  */ | 
 |       if (status.kind () != TARGET_WAITKIND_LOADED | 
 | 	  && status.kind () != TARGET_WAITKIND_SPURIOUS) | 
 | 	break; | 
 |  | 
 |       this->resume (minus_one_ptid, 0, GDB_SIGNAL_0); | 
 |     } | 
 |  | 
 |   switch_to_thread (this->find_thread (last_ptid)); | 
 |  | 
 |   /* Now that the inferior has been started and all DLLs have been mapped, | 
 |      we can iterate over all DLLs and load them in. | 
 |  | 
 |      We avoid doing it any earlier because, on certain versions of Windows, | 
 |      LOAD_DLL_DEBUG_EVENTs are sometimes not complete.  In particular, | 
 |      we have seen on Windows 8.1 that the ntdll.dll load event does not | 
 |      include the DLL name, preventing us from creating an associated SO. | 
 |      A possible explanation is that ntdll.dll might be mapped before | 
 |      the SO info gets created by the Windows system -- ntdll.dll is | 
 |      the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs | 
 |      do not seem to suffer from that problem. | 
 |  | 
 |      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.  */ | 
 |   windows_process.add_all_dlls (); | 
 |  | 
 |   windows_process.windows_initialization_done = 1; | 
 |   return; | 
 | } | 
 |  | 
 | /* Try to set or remove a user privilege to the current process.  Return -1 | 
 |    if that fails, the previous setting of that privilege otherwise. | 
 |  | 
 |    This code is copied from the Cygwin source code and rearranged to allow | 
 |    dynamically loading of the needed symbols from advapi32 which is only | 
 |    available on NT/2K/XP.  */ | 
 | static int | 
 | set_process_privilege (const char *privilege, BOOL enable) | 
 | { | 
 |   HANDLE token_hdl = NULL; | 
 |   LUID restore_priv; | 
 |   TOKEN_PRIVILEGES new_priv, orig_priv; | 
 |   int ret = -1; | 
 |   DWORD size; | 
 |  | 
 |   if (!OpenProcessToken (GetCurrentProcess (), | 
 | 			 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, | 
 | 			 &token_hdl)) | 
 |     goto out; | 
 |  | 
 |   if (!LookupPrivilegeValueA (NULL, privilege, &restore_priv)) | 
 |     goto out; | 
 |  | 
 |   new_priv.PrivilegeCount = 1; | 
 |   new_priv.Privileges[0].Luid = restore_priv; | 
 |   new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; | 
 |  | 
 |   if (!AdjustTokenPrivileges (token_hdl, FALSE, &new_priv, | 
 | 			      sizeof orig_priv, &orig_priv, &size)) | 
 |     goto out; | 
 | #if 0 | 
 |   /* Disabled, otherwise every `attach' in an unprivileged user session | 
 |      would raise the "Failed to get SE_DEBUG_NAME privilege" warning in | 
 |      windows_attach().  */ | 
 |   /* AdjustTokenPrivileges returns TRUE even if the privilege could not | 
 |      be enabled.  GetLastError () returns an correct error code, though.  */ | 
 |   if (enable && GetLastError () == ERROR_NOT_ALL_ASSIGNED) | 
 |     goto out; | 
 | #endif | 
 |  | 
 |   ret = orig_priv.Privileges[0].Attributes == SE_PRIVILEGE_ENABLED ? 1 : 0; | 
 |  | 
 | out: | 
 |   if (token_hdl) | 
 |     CloseHandle (token_hdl); | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Attach to process PID, then initialize for debugging it.  */ | 
 |  | 
 | void | 
 | windows_nat_target::attach (const char *args, int from_tty) | 
 | { | 
 |   DWORD pid; | 
 |  | 
 |   pid = parse_pid_to_attach (args); | 
 |  | 
 |   if (set_process_privilege (SE_DEBUG_NAME, TRUE) < 0) | 
 |     warning ("Failed to get SE_DEBUG_NAME privilege\n" | 
 | 	     "This can cause attach to fail on Windows NT/2K/XP"); | 
 |  | 
 |   windows_init_thread_list (); | 
 |   windows_process.saw_create = 0; | 
 |  | 
 |   std::optional<unsigned> err; | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       BOOL ok = DebugActiveProcess (pid); | 
 |  | 
 | #ifdef __CYGWIN__ | 
 |       if (!ok) | 
 | 	{ | 
 | 	  /* Try fall back to Cygwin pid.  */ | 
 | 	  pid = cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid); | 
 |  | 
 | 	  if (pid > 0) | 
 | 	    ok = DebugActiveProcess (pid); | 
 | 	} | 
 | #endif | 
 |  | 
 |       if (!ok) | 
 | 	err = (unsigned) GetLastError (); | 
 |  | 
 |       return true; | 
 |     }); | 
 |  | 
 |   if (err.has_value ()) | 
 |     { | 
 |       std::string msg = string_printf (_("Can't attach to process %u"), | 
 | 				       (unsigned) pid); | 
 |       throw_winerror_with_name (msg.c_str (), *err); | 
 |     } | 
 |  | 
 |   DebugSetProcessKillOnExit (FALSE); | 
 |  | 
 |   target_announce_attach (from_tty, pid); | 
 |  | 
 | #ifdef __x86_64__ | 
 |   HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid); | 
 |   if (h != NULL) | 
 |     { | 
 |       BOOL wow64; | 
 |       if (IsWow64Process (h, &wow64)) | 
 | 	windows_process.wow64_process = wow64; | 
 |       CloseHandle (h); | 
 |     } | 
 | #endif | 
 |  | 
 |   do_initial_windows_stuff (pid, 1); | 
 |   target_terminal::ours (); | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::detach (inferior *inf, int from_tty) | 
 | { | 
 |   windows_continue (DBG_CONTINUE, -1, 0, true); | 
 |  | 
 |   std::optional<unsigned> err; | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       if (!DebugActiveProcessStop (windows_process.current_event.dwProcessId)) | 
 | 	err = (unsigned) GetLastError (); | 
 |       else | 
 | 	DebugSetProcessKillOnExit (FALSE); | 
 |       return false; | 
 |     }); | 
 |  | 
 |   if (err.has_value ()) | 
 |     { | 
 |       std::string msg | 
 | 	= string_printf (_("Can't detach process %u"), | 
 | 			 (unsigned) windows_process.current_event.dwProcessId); | 
 |       throw_winerror_with_name (msg.c_str (), *err); | 
 |     } | 
 |  | 
 |   target_announce_detach (from_tty); | 
 |  | 
 |   x86_cleanup_dregs (); | 
 |   switch_to_no_thread (); | 
 |   detach_inferior (inf); | 
 |  | 
 |   maybe_unpush_target (); | 
 | } | 
 |  | 
 | /* The pid_to_exec_file target_ops method for this platform.  */ | 
 |  | 
 | const char * | 
 | windows_nat_target::pid_to_exec_file (int pid) | 
 | { | 
 |   return windows_process.pid_to_exec_file (pid); | 
 | } | 
 |  | 
 | /* Print status information about what we're accessing.  */ | 
 |  | 
 | void | 
 | windows_nat_target::files_info () | 
 | { | 
 |   struct inferior *inf = current_inferior (); | 
 |  | 
 |   gdb_printf ("\tUsing the running image of %s %s.\n", | 
 | 	      inf->attach_flag ? "attached" : "child", | 
 | 	      target_pid_to_str (ptid_t (inf->pid)).c_str ()); | 
 | } | 
 |  | 
 | /* Modify CreateProcess parameters for use of a new separate console. | 
 |    Parameters are: | 
 |    *FLAGS: DWORD parameter for general process creation flags. | 
 |    *SI: STARTUPINFO structure, for which the console window size and | 
 |    console buffer size is filled in if GDB is running in a console. | 
 |    to create the new console. | 
 |    The size of the used font is not available on all versions of | 
 |    Windows OS.  Furthermore, the current font might not be the default | 
 |    font, but this is still better than before. | 
 |    If the windows and buffer sizes are computed, | 
 |    SI->DWFLAGS is changed so that this information is used | 
 |    by CreateProcess function.  */ | 
 |  | 
 | static void | 
 | windows_set_console_info (STARTUPINFO *si, DWORD *flags) | 
 | { | 
 |   HANDLE hconsole = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE, | 
 | 				FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); | 
 |  | 
 |   if (hconsole != INVALID_HANDLE_VALUE) | 
 |     { | 
 |       CONSOLE_SCREEN_BUFFER_INFO sbinfo; | 
 |       COORD font_size; | 
 |       CONSOLE_FONT_INFO cfi; | 
 |  | 
 |       GetCurrentConsoleFont (hconsole, FALSE, &cfi); | 
 |       font_size = GetConsoleFontSize (hconsole, cfi.nFont); | 
 |       GetConsoleScreenBufferInfo(hconsole, &sbinfo); | 
 |       si->dwXSize = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1; | 
 |       si->dwYSize = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1; | 
 |       if (font_size.X) | 
 | 	si->dwXSize *= font_size.X; | 
 |       else | 
 | 	si->dwXSize *= 8; | 
 |       if (font_size.Y) | 
 | 	si->dwYSize *= font_size.Y; | 
 |       else | 
 | 	si->dwYSize *= 12; | 
 |       si->dwXCountChars = sbinfo.dwSize.X; | 
 |       si->dwYCountChars = sbinfo.dwSize.Y; | 
 |       si->dwFlags |= STARTF_USESIZE | STARTF_USECOUNTCHARS; | 
 |     } | 
 |   *flags |= CREATE_NEW_CONSOLE; | 
 | } | 
 |  | 
 | #ifndef __CYGWIN__ | 
 | /* Function called by qsort to sort environment strings.  */ | 
 |  | 
 | static int | 
 | envvar_cmp (const void *a, const void *b) | 
 | { | 
 |   const char **p = (const char **) a; | 
 |   const char **q = (const char **) b; | 
 |   return strcasecmp (*p, *q); | 
 | } | 
 | #endif | 
 |  | 
 | #ifdef __CYGWIN__ | 
 | static void | 
 | clear_win32_environment (char **env) | 
 | { | 
 |   int i; | 
 |   size_t len; | 
 |   wchar_t *copy = NULL, *equalpos; | 
 |  | 
 |   for (i = 0; env[i] && *env[i]; i++) | 
 |     { | 
 |       len = mbstowcs (NULL, env[i], 0) + 1; | 
 |       copy = (wchar_t *) xrealloc (copy, len * sizeof (wchar_t)); | 
 |       mbstowcs (copy, env[i], len); | 
 |       equalpos = wcschr (copy, L'='); | 
 |       if (equalpos) | 
 | 	*equalpos = L'\0'; | 
 |       SetEnvironmentVariableW (copy, NULL); | 
 |     } | 
 |   xfree (copy); | 
 | } | 
 | #endif | 
 |  | 
 | #ifndef __CYGWIN__ | 
 |  | 
 | /* Redirection of inferior I/O streams for native MS-Windows programs. | 
 |    Unlike on Unix, where this is handled by invoking the inferior via | 
 |    the shell, on MS-Windows we need to emulate the cmd.exe shell. | 
 |  | 
 |    The official documentation of the cmd.exe redirection features is here: | 
 |  | 
 |      http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx | 
 |  | 
 |    (That page talks about Windows XP, but there's no newer | 
 |    documentation, so we assume later versions of cmd.exe didn't change | 
 |    anything.) | 
 |  | 
 |    Caveat: the documentation on that page seems to include a few lies. | 
 |    For example, it describes strange constructs 1<&2 and 2<&1, which | 
 |    seem to work only when 1>&2 resp. 2>&1 would make sense, and so I | 
 |    think the cmd.exe parser of the redirection symbols simply doesn't | 
 |    care about the < vs > distinction in these cases.  Therefore, the | 
 |    supported features are explicitly documented below. | 
 |  | 
 |    The emulation below aims at supporting all the valid use cases | 
 |    supported by cmd.exe, which include: | 
 |  | 
 |      < FILE    redirect standard input from FILE | 
 |      0< FILE   redirect standard input from FILE | 
 |      <&N       redirect standard input from file descriptor N | 
 |      0<&N      redirect standard input from file descriptor N | 
 |      > FILE    redirect standard output to FILE | 
 |      >> FILE   append standard output to FILE | 
 |      1>> FILE  append standard output to FILE | 
 |      >&N       redirect standard output to file descriptor N | 
 |      1>&N      redirect standard output to file descriptor N | 
 |      >>&N      append standard output to file descriptor N | 
 |      1>>&N     append standard output to file descriptor N | 
 |      2> FILE   redirect standard error to FILE | 
 |      2>> FILE  append standard error to FILE | 
 |      2>&N      redirect standard error to file descriptor N | 
 |      2>>&N     append standard error to file descriptor N | 
 |  | 
 |      Note that using N > 2 in the above construct is supported, but | 
 |      requires that the corresponding file descriptor be open by some | 
 |      means elsewhere or outside GDB.  Also note that using ">&0" or | 
 |      "<&2" will generally fail, because the file descriptor redirected | 
 |      from is normally open in an incompatible mode (e.g., FD 0 is open | 
 |      for reading only).  IOW, use of such tricks is not recommended; | 
 |      you are on your own. | 
 |  | 
 |      We do NOT support redirection of file descriptors above 2, as in | 
 |      "3>SOME-FILE", because MinGW compiled programs don't (supporting | 
 |      that needs special handling in the startup code that MinGW | 
 |      doesn't have).  Pipes are also not supported. | 
 |  | 
 |      As for invalid use cases, where the redirection contains some | 
 |      error, the emulation below will detect that and produce some | 
 |      error and/or failure.  But the behavior in those cases is not | 
 |      bug-for-bug compatible with what cmd.exe does in those cases. | 
 |      That's because what cmd.exe does then is not well defined, and | 
 |      seems to be a side effect of the cmd.exe parsing of the command | 
 |      line more than anything else.  For example, try redirecting to an | 
 |      invalid file name, as in "> foo:bar". | 
 |  | 
 |      There are also minor syntactic deviations from what cmd.exe does | 
 |      in some corner cases.  For example, it doesn't support the likes | 
 |      of "> &foo" to mean redirect to file named literally "&foo"; we | 
 |      do support that here, because that, too, sounds like some issue | 
 |      with the cmd.exe parser.  Another nicety is that we support | 
 |      redirection targets that use file names with forward slashes, | 
 |      something cmd.exe doesn't -- this comes in handy since GDB | 
 |      file-name completion can be used when typing the command line for | 
 |      the inferior.  */ | 
 |  | 
 | /* Support routines for redirecting standard handles of the inferior.  */ | 
 |  | 
 | /* Parse a single redirection spec, open/duplicate the specified | 
 |    file/fd, and assign the appropriate value to one of the 3 standard | 
 |    file descriptors. */ | 
 | static int | 
 | redir_open (const char *redir_string, int *inp, int *out, int *err) | 
 | { | 
 |   int *fd, ref_fd = -2; | 
 |   int mode; | 
 |   const char *fname = redir_string + 1; | 
 |   int rc = *redir_string; | 
 |  | 
 |   switch (rc) | 
 |     { | 
 |     case '0': | 
 |       fname++; | 
 |       [[fallthrough]]; | 
 |     case '<': | 
 |       fd = inp; | 
 |       mode = O_RDONLY; | 
 |       break; | 
 |     case '1': case '2': | 
 |       fname++; | 
 |       [[fallthrough]]; | 
 |     case '>': | 
 |       fd = (rc == '2') ? err : out; | 
 |       mode = O_WRONLY | O_CREAT; | 
 |       if (*fname == '>') | 
 | 	{ | 
 | 	  fname++; | 
 | 	  mode |= O_APPEND; | 
 | 	} | 
 |       else | 
 | 	mode |= O_TRUNC; | 
 |       break; | 
 |     default: | 
 |       return -1; | 
 |     } | 
 |  | 
 |   if (*fname == '&' && '0' <= fname[1] && fname[1] <= '9') | 
 |     { | 
 |       /* A reference to a file descriptor.  */ | 
 |       char *fdtail; | 
 |       ref_fd = (int) strtol (fname + 1, &fdtail, 10); | 
 |       if (fdtail > fname + 1 && *fdtail == '\0') | 
 | 	{ | 
 | 	  /* Don't allow redirection when open modes are incompatible.  */ | 
 | 	  if ((ref_fd == 0 && (fd == out || fd == err)) | 
 | 	      || ((ref_fd == 1 || ref_fd == 2) && fd == inp)) | 
 | 	    { | 
 | 	      errno = EPERM; | 
 | 	      return -1; | 
 | 	    } | 
 | 	  if (ref_fd == 0) | 
 | 	    ref_fd = *inp; | 
 | 	  else if (ref_fd == 1) | 
 | 	    ref_fd = *out; | 
 | 	  else if (ref_fd == 2) | 
 | 	    ref_fd = *err; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  errno = EBADF; | 
 | 	  return -1; | 
 | 	} | 
 |     } | 
 |   else | 
 |     fname++;	/* skip the separator space */ | 
 |   /* If the descriptor is already open, close it.  This allows | 
 |      multiple specs of redirections for the same stream, which is | 
 |      somewhat nonsensical, but still valid and supported by cmd.exe. | 
 |      (But cmd.exe only opens a single file in this case, the one | 
 |      specified by the last redirection spec on the command line.)  */ | 
 |   if (*fd >= 0) | 
 |     _close (*fd); | 
 |   if (ref_fd == -2) | 
 |     { | 
 |       *fd = _open (fname, mode, _S_IREAD | _S_IWRITE); | 
 |       if (*fd < 0) | 
 | 	return -1; | 
 |     } | 
 |   else if (ref_fd == -1) | 
 |     *fd = -1;	/* reset to default destination */ | 
 |   else | 
 |     { | 
 |       *fd = _dup (ref_fd); | 
 |       if (*fd < 0) | 
 | 	return -1; | 
 |     } | 
 |   /* _open just sets a flag for O_APPEND, which won't be passed to the | 
 |      inferior, so we need to actually move the file pointer.  */ | 
 |   if ((mode & O_APPEND) != 0) | 
 |     _lseek (*fd, 0L, SEEK_END); | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Canonicalize a single redirection spec and set up the corresponding | 
 |    file descriptor as specified.  */ | 
 | static int | 
 | redir_set_redirection (const char *s, int *inp, int *out, int *err) | 
 | { | 
 |   char buf[__PMAX + 2 + 5]; /* extra space for quotes & redirection string */ | 
 |   char *d = buf; | 
 |   const char *start = s; | 
 |   int quote = 0; | 
 |  | 
 |   *d++ = *s++;	/* copy the 1st character, < or > or a digit */ | 
 |   if ((*start == '>' || *start == '1' || *start == '2') | 
 |       && *s == '>') | 
 |     { | 
 |       *d++ = *s++; | 
 |       if (*s == '>' && *start != '>') | 
 | 	*d++ = *s++; | 
 |     } | 
 |   else if (*start == '0' && *s == '<') | 
 |     *d++ = *s++; | 
 |   /* cmd.exe recognizes "&N" only immediately after the redirection symbol.  */ | 
 |   if (*s != '&') | 
 |     { | 
 |       while (isspace (*s))  /* skip whitespace before file name */ | 
 | 	s++; | 
 |       *d++ = ' ';	    /* separate file name with a single space */ | 
 |     } | 
 |  | 
 |   /* Copy the file name.  */ | 
 |   while (*s) | 
 |     { | 
 |       /* Remove quoting characters from the file name in buf[].  */ | 
 |       if (*s == '"')	/* could support '..' quoting here */ | 
 | 	{ | 
 | 	  if (!quote) | 
 | 	    quote = *s++; | 
 | 	  else if (*s == quote) | 
 | 	    { | 
 | 	      quote = 0; | 
 | 	      s++; | 
 | 	    } | 
 | 	  else | 
 | 	    *d++ = *s++; | 
 | 	} | 
 |       else if (*s == '\\') | 
 | 	{ | 
 | 	  if (s[1] == '"')	/* could support '..' here */ | 
 | 	    s++; | 
 | 	  *d++ = *s++; | 
 | 	} | 
 |       else if (isspace (*s) && !quote) | 
 | 	break; | 
 |       else | 
 | 	*d++ = *s++; | 
 |       if (d - buf >= sizeof (buf) - 1) | 
 | 	{ | 
 | 	  errno = ENAMETOOLONG; | 
 | 	  return 0; | 
 | 	} | 
 |     } | 
 |   *d = '\0'; | 
 |  | 
 |   /* Windows doesn't allow redirection characters in file names, so we | 
 |      can bail out early if they use them, or if there's no target file | 
 |      name after the redirection symbol.  */ | 
 |   if (d[-1] == '>' || d[-1] == '<') | 
 |     { | 
 |       errno = ENOENT; | 
 |       return 0; | 
 |     } | 
 |   if (redir_open (buf, inp, out, err) == 0) | 
 |     return s - start; | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Parse the command line for redirection specs and prepare the file | 
 |    descriptors for the 3 standard streams accordingly.  */ | 
 | static bool | 
 | redirect_inferior_handles (const char *cmd_orig, char *cmd, | 
 | 			   int *inp, int *out, int *err) | 
 | { | 
 |   const char *s = cmd_orig; | 
 |   char *d = cmd; | 
 |   int quote = 0; | 
 |   bool retval = false; | 
 |  | 
 |   while (isspace (*s)) | 
 |     *d++ = *s++; | 
 |  | 
 |   while (*s) | 
 |     { | 
 |       if (*s == '"')	/* could also support '..' quoting here */ | 
 | 	{ | 
 | 	  if (!quote) | 
 | 	    quote = *s; | 
 | 	  else if (*s == quote) | 
 | 	    quote = 0; | 
 | 	} | 
 |       else if (*s == '\\') | 
 | 	{ | 
 | 	  if (s[1] == '"')	/* escaped quote char */ | 
 | 	    s++; | 
 | 	} | 
 |       else if (!quote) | 
 | 	{ | 
 | 	  /* Process a single redirection candidate.  */ | 
 | 	  if (*s == '<' || *s == '>' | 
 | 	      || ((*s == '1' || *s == '2') && s[1] == '>') | 
 | 	      || (*s == '0' && s[1] == '<')) | 
 | 	    { | 
 | 	      int skip = redir_set_redirection (s, inp, out, err); | 
 |  | 
 | 	      if (skip <= 0) | 
 | 		return false; | 
 | 	      retval = true; | 
 | 	      s += skip; | 
 | 	    } | 
 | 	} | 
 |       if (*s) | 
 | 	*d++ = *s++; | 
 |     } | 
 |   *d = '\0'; | 
 |   return retval; | 
 | } | 
 | #endif	/* !__CYGWIN__ */ | 
 |  | 
 | /* Start an inferior windows child process and sets inferior_ptid to its pid. | 
 |    EXEC_FILE is the file to run. | 
 |    ALLARGS is a string containing the arguments to the program. | 
 |    ENV is the environment vector to pass.  Errors reported with error().  */ | 
 |  | 
 | void | 
 | windows_nat_target::create_inferior (const char *exec_file, | 
 | 				     const std::string &origallargs, | 
 | 				     char **in_env, int from_tty) | 
 | { | 
 |   STARTUPINFO si; | 
 | #ifdef __CYGWIN__ | 
 |   wchar_t real_path[__PMAX]; | 
 |   wchar_t shell[__PMAX]; /* Path to shell */ | 
 |   wchar_t infcwd[__PMAX]; | 
 |   const char *sh; | 
 |   wchar_t *toexec; | 
 |   wchar_t *cygallargs; | 
 |   wchar_t *args; | 
 |   char **old_env = NULL; | 
 |   PWCHAR w32_env; | 
 |   size_t len; | 
 |   int tty; | 
 |   int ostdin, ostdout, ostderr; | 
 | #else  /* !__CYGWIN__ */ | 
 |   char shell[__PMAX]; /* Path to shell */ | 
 |   const char *toexec; | 
 |   char *args, *allargs_copy; | 
 |   size_t args_len, allargs_len; | 
 |   int fd_inp = -1, fd_out = -1, fd_err = -1; | 
 |   HANDLE tty = INVALID_HANDLE_VALUE; | 
 |   bool redirected = false; | 
 |   char *w32env; | 
 |   char *temp; | 
 |   size_t envlen; | 
 |   int i; | 
 |   size_t envsize; | 
 |   char **env; | 
 | #endif	/* !__CYGWIN__ */ | 
 |   const char *allargs = origallargs.c_str (); | 
 |   PROCESS_INFORMATION pi; | 
 |   std::optional<unsigned> ret; | 
 |   DWORD flags = 0; | 
 |   const std::string &inferior_tty = current_inferior ()->tty (); | 
 |  | 
 |   if (!exec_file) | 
 |     error (_("No executable specified, use `target exec'.")); | 
 |  | 
 |   const char *inferior_cwd = current_inferior ()->cwd ().c_str (); | 
 |   std::string expanded_infcwd; | 
 |   if (*inferior_cwd == '\0') | 
 |     inferior_cwd = nullptr; | 
 |   else | 
 |     { | 
 |       expanded_infcwd = gdb_tilde_expand (inferior_cwd); | 
 |       /* Mirror slashes on inferior's cwd.  */ | 
 |       std::replace (expanded_infcwd.begin (), expanded_infcwd.end (), | 
 | 		    '/', '\\'); | 
 |       inferior_cwd = expanded_infcwd.c_str (); | 
 |     } | 
 |  | 
 |   memset (&si, 0, sizeof (si)); | 
 |   si.cb = sizeof (si); | 
 |  | 
 |   if (new_group) | 
 |     flags |= CREATE_NEW_PROCESS_GROUP; | 
 |  | 
 |   if (new_console) | 
 |     windows_set_console_info (&si, &flags); | 
 |  | 
 | #ifdef __CYGWIN__ | 
 |   if (!useshell) | 
 |     { | 
 |       flags |= DEBUG_ONLY_THIS_PROCESS; | 
 |       if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, exec_file, real_path, | 
 | 			    __PMAX * sizeof (wchar_t)) < 0) | 
 | 	error (_("Error starting executable: %d"), errno); | 
 |       toexec = real_path; | 
 |       len = mbstowcs (NULL, allargs, 0) + 1; | 
 |       if (len == (size_t) -1) | 
 | 	error (_("Error starting executable: %d"), errno); | 
 |       cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t)); | 
 |       mbstowcs (cygallargs, allargs, len); | 
 |     } | 
 |   else | 
 |     { | 
 |       sh = get_shell (); | 
 |       if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, sh, shell, __PMAX) < 0) | 
 | 	error (_("Error starting executable via shell: %d"), errno); | 
 |       len = sizeof (L" -c 'exec  '") + mbstowcs (NULL, exec_file, 0) | 
 | 	    + mbstowcs (NULL, allargs, 0) + 2; | 
 |       cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t)); | 
 |       swprintf (cygallargs, len, L" -c 'exec %s %s'", exec_file, allargs); | 
 |       toexec = shell; | 
 |       flags |= DEBUG_PROCESS; | 
 |     } | 
 |  | 
 |   if (inferior_cwd != NULL | 
 |       && cygwin_conv_path (CCP_POSIX_TO_WIN_W, inferior_cwd, | 
 | 			   infcwd, strlen (inferior_cwd)) < 0) | 
 |     error (_("Error converting inferior cwd: %d"), errno); | 
 |  | 
 |   args = (wchar_t *) alloca ((wcslen (toexec) + wcslen (cygallargs) + 2) | 
 | 			     * sizeof (wchar_t)); | 
 |   wcscpy (args, toexec); | 
 |   wcscat (args, L" "); | 
 |   wcscat (args, cygallargs); | 
 |  | 
 | #ifdef CW_CVT_ENV_TO_WINENV | 
 |   /* First try to create a direct Win32 copy of the POSIX environment. */ | 
 |   w32_env = (PWCHAR) cygwin_internal (CW_CVT_ENV_TO_WINENV, in_env); | 
 |   if (w32_env != (PWCHAR) -1) | 
 |     flags |= CREATE_UNICODE_ENVIRONMENT; | 
 |   else | 
 |     /* If that fails, fall back to old method tweaking GDB's environment. */ | 
 | #endif	/* CW_CVT_ENV_TO_WINENV */ | 
 |     { | 
 |       /* Reset all Win32 environment variables to avoid leftover on next run. */ | 
 |       clear_win32_environment (environ); | 
 |       /* Prepare the environment vars for CreateProcess.  */ | 
 |       old_env = environ; | 
 |       environ = in_env; | 
 |       cygwin_internal (CW_SYNC_WINENV); | 
 |       w32_env = NULL; | 
 |     } | 
 |  | 
 |   if (inferior_tty.empty ()) | 
 |     tty = ostdin = ostdout = ostderr = -1; | 
 |   else | 
 |     { | 
 |       tty = open (inferior_tty.c_str (), O_RDWR | O_NOCTTY); | 
 |       if (tty < 0) | 
 | 	{ | 
 | 	  warning_filename_and_errno (inferior_tty.c_str (), errno); | 
 | 	  ostdin = ostdout = ostderr = -1; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  ostdin = dup (0); | 
 | 	  ostdout = dup (1); | 
 | 	  ostderr = dup (2); | 
 | 	  dup2 (tty, 0); | 
 | 	  dup2 (tty, 1); | 
 | 	  dup2 (tty, 2); | 
 | 	} | 
 |     } | 
 |  | 
 |   windows_init_thread_list (); | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       if (!create_process (nullptr, args, flags, w32_env, | 
 | 			   inferior_cwd != nullptr ? infcwd : nullptr, | 
 | 			   disable_randomization, | 
 | 			   &si, &pi)) | 
 | 	ret = (unsigned) GetLastError (); | 
 |       return true; | 
 |     }); | 
 |  | 
 |   if (w32_env) | 
 |     /* Just free the Win32 environment, if it could be created. */ | 
 |     free (w32_env); | 
 |   else | 
 |     { | 
 |       /* Reset all environment variables to avoid leftover on next run. */ | 
 |       clear_win32_environment (in_env); | 
 |       /* Restore normal GDB environment variables.  */ | 
 |       environ = old_env; | 
 |       cygwin_internal (CW_SYNC_WINENV); | 
 |     } | 
 |  | 
 |   if (tty >= 0) | 
 |     { | 
 |       ::close (tty); | 
 |       dup2 (ostdin, 0); | 
 |       dup2 (ostdout, 1); | 
 |       dup2 (ostderr, 2); | 
 |       ::close (ostdin); | 
 |       ::close (ostdout); | 
 |       ::close (ostderr); | 
 |     } | 
 | #else  /* !__CYGWIN__ */ | 
 |   allargs_len = strlen (allargs); | 
 |   allargs_copy = strcpy ((char *) alloca (allargs_len + 1), allargs); | 
 |   if (strpbrk (allargs_copy, "<>") != NULL) | 
 |     { | 
 |       int e = errno; | 
 |       errno = 0; | 
 |       redirected = | 
 | 	redirect_inferior_handles (allargs, allargs_copy, | 
 | 				   &fd_inp, &fd_out, &fd_err); | 
 |       if (errno) | 
 | 	warning (_("Error in redirection: %s."), safe_strerror (errno)); | 
 |       else | 
 | 	errno = e; | 
 |       allargs_len = strlen (allargs_copy); | 
 |     } | 
 |   /* If not all the standard streams are redirected by the command | 
 |      line, use INFERIOR_TTY for those which aren't.  */ | 
 |   if (!inferior_tty.empty () | 
 |       && !(fd_inp >= 0 && fd_out >= 0 && fd_err >= 0)) | 
 |     { | 
 |       SECURITY_ATTRIBUTES sa; | 
 |       sa.nLength = sizeof(sa); | 
 |       sa.lpSecurityDescriptor = 0; | 
 |       sa.bInheritHandle = TRUE; | 
 |       tty = CreateFileA (inferior_tty.c_str (), GENERIC_READ | GENERIC_WRITE, | 
 | 			 0, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | 
 |       if (tty == INVALID_HANDLE_VALUE) | 
 | 	{ | 
 | 	  unsigned err = (unsigned) GetLastError (); | 
 | 	  warning (_("Warning: Failed to open TTY %s, error %#x: %s"), | 
 | 		   inferior_tty.c_str (), err, strwinerror (err)); | 
 | 	} | 
 |     } | 
 |   if (redirected || tty != INVALID_HANDLE_VALUE) | 
 |     { | 
 |       if (fd_inp >= 0) | 
 | 	si.hStdInput = (HANDLE) _get_osfhandle (fd_inp); | 
 |       else if (tty != INVALID_HANDLE_VALUE) | 
 | 	si.hStdInput = tty; | 
 |       else | 
 | 	si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); | 
 |       if (fd_out >= 0) | 
 | 	si.hStdOutput = (HANDLE) _get_osfhandle (fd_out); | 
 |       else if (tty != INVALID_HANDLE_VALUE) | 
 | 	si.hStdOutput = tty; | 
 |       else | 
 | 	si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); | 
 |       if (fd_err >= 0) | 
 | 	si.hStdError = (HANDLE) _get_osfhandle (fd_err); | 
 |       else if (tty != INVALID_HANDLE_VALUE) | 
 | 	si.hStdError = tty; | 
 |       else | 
 | 	si.hStdError = GetStdHandle (STD_ERROR_HANDLE); | 
 |       si.dwFlags |= STARTF_USESTDHANDLES; | 
 |     } | 
 |  | 
 |   toexec = exec_file; | 
 |   /* Build the command line, a space-separated list of tokens where | 
 |      the first token is the name of the module to be executed. | 
 |      To avoid ambiguities introduced by spaces in the module name, | 
 |      we quote it.  */ | 
 |   args_len = strlen (toexec) + 2 /* quotes */ + allargs_len + 2; | 
 |   args = (char *) alloca (args_len); | 
 |   xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs_copy); | 
 |  | 
 |   flags |= DEBUG_ONLY_THIS_PROCESS; | 
 |  | 
 |   /* CreateProcess takes the environment list as a null terminated set of | 
 |      strings (i.e. two nulls terminate the list).  */ | 
 |  | 
 |   /* Get total size for env strings.  */ | 
 |   for (envlen = 0, i = 0; in_env[i] && *in_env[i]; i++) | 
 |     envlen += strlen (in_env[i]) + 1; | 
 |  | 
 |   envsize = sizeof (in_env[0]) * (i + 1); | 
 |   env = (char **) alloca (envsize); | 
 |   memcpy (env, in_env, envsize); | 
 |   /* Windows programs expect the environment block to be sorted.  */ | 
 |   qsort (env, i, sizeof (char *), envvar_cmp); | 
 |  | 
 |   w32env = (char *) alloca (envlen + 1); | 
 |  | 
 |   /* Copy env strings into new buffer.  */ | 
 |   for (temp = w32env, i = 0; env[i] && *env[i]; i++) | 
 |     { | 
 |       strcpy (temp, env[i]); | 
 |       temp += strlen (temp) + 1; | 
 |     } | 
 |  | 
 |   /* Final nil string to terminate new env.  */ | 
 |   *temp = 0; | 
 |  | 
 |   windows_init_thread_list (); | 
 |   do_synchronously ([&] () | 
 |     { | 
 |       if (!create_process (nullptr, /* image */ | 
 | 			   args,	/* command line */ | 
 | 			   flags,	/* start flags */ | 
 | 			   w32env,	/* environment */ | 
 | 			   inferior_cwd, /* current directory */ | 
 | 			   disable_randomization, | 
 | 			   &si, | 
 | 			   &pi)) | 
 | 	ret = (unsigned) GetLastError (); | 
 |       return true; | 
 |     }); | 
 |   if (tty != INVALID_HANDLE_VALUE) | 
 |     CloseHandle (tty); | 
 |   if (fd_inp >= 0) | 
 |     _close (fd_inp); | 
 |   if (fd_out >= 0) | 
 |     _close (fd_out); | 
 |   if (fd_err >= 0) | 
 |     _close (fd_err); | 
 | #endif	/* !__CYGWIN__ */ | 
 |  | 
 |   if (ret.has_value ()) | 
 |     { | 
 |       std::string msg = _("Error creating process ") + std::string (exec_file); | 
 |       throw_winerror_with_name (msg.c_str (), *ret); | 
 |     } | 
 |  | 
 | #ifdef __x86_64__ | 
 |   BOOL wow64; | 
 |   if (IsWow64Process (pi.hProcess, &wow64)) | 
 |     windows_process.wow64_process = wow64; | 
 | #endif | 
 |  | 
 |   CloseHandle (pi.hThread); | 
 |   CloseHandle (pi.hProcess); | 
 |  | 
 |   if (useshell && shell[0] != '\0') | 
 |     windows_process.saw_create = -1; | 
 |   else | 
 |     windows_process.saw_create = 0; | 
 |  | 
 |   do_initial_windows_stuff (pi.dwProcessId, 0); | 
 |  | 
 |   /* windows_continue (DBG_CONTINUE, -1, 0); */ | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::mourn_inferior () | 
 | { | 
 |   (void) windows_continue (DBG_CONTINUE, -1, 0, true); | 
 |   x86_cleanup_dregs(); | 
 |   if (windows_process.open_process_used) | 
 |     { | 
 |       CHECK (CloseHandle (windows_process.handle)); | 
 |       windows_process.open_process_used = 0; | 
 |     } | 
 |   windows_process.siginfo_er.ExceptionCode = 0; | 
 |   inf_child_target::mourn_inferior (); | 
 | } | 
 |  | 
 | /* Helper for windows_xfer_partial that handles memory transfers. | 
 |    Arguments are like target_xfer_partial.  */ | 
 |  | 
 | static enum target_xfer_status | 
 | windows_xfer_memory (gdb_byte *readbuf, const gdb_byte *writebuf, | 
 | 		     ULONGEST memaddr, ULONGEST len, ULONGEST *xfered_len) | 
 | { | 
 |   SIZE_T done = 0; | 
 |   BOOL success; | 
 |   DWORD lasterror = 0; | 
 |  | 
 |   if (writebuf != NULL) | 
 |     { | 
 |       DEBUG_MEM ("write target memory, %s bytes at %s", | 
 | 		 pulongest (len), core_addr_to_string (memaddr)); | 
 |       success = WriteProcessMemory (windows_process.handle, | 
 | 				    (LPVOID) (uintptr_t) memaddr, writebuf, | 
 | 				    len, &done); | 
 |       if (!success) | 
 | 	lasterror = GetLastError (); | 
 |       FlushInstructionCache (windows_process.handle, | 
 | 			     (LPCVOID) (uintptr_t) memaddr, len); | 
 |     } | 
 |   else | 
 |     { | 
 |       DEBUG_MEM ("read target memory, %s bytes at %s", | 
 | 		 pulongest (len), core_addr_to_string (memaddr)); | 
 |       success = ReadProcessMemory (windows_process.handle, | 
 | 				   (LPCVOID) (uintptr_t) memaddr, readbuf, | 
 | 				   len, &done); | 
 |       if (!success) | 
 | 	lasterror = GetLastError (); | 
 |     } | 
 |   *xfered_len = (ULONGEST) done; | 
 |   if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0) | 
 |     return TARGET_XFER_OK; | 
 |   else | 
 |     return success ? TARGET_XFER_OK : TARGET_XFER_E_IO; | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::kill () | 
 | { | 
 |   CHECK (TerminateProcess (windows_process.handle, 0)); | 
 |  | 
 |   for (;;) | 
 |     { | 
 |       if (!windows_continue (DBG_CONTINUE, -1, 1)) | 
 | 	break; | 
 |       wait_for_debug_event_main_thread (&windows_process.current_event); | 
 |       if (windows_process.current_event.dwDebugEventCode | 
 | 	  == EXIT_PROCESS_DEBUG_EVENT) | 
 | 	break; | 
 |     } | 
 |  | 
 |   target_mourn_inferior (inferior_ptid);	/* Or just windows_mourn_inferior?  */ | 
 | } | 
 |  | 
 | void | 
 | windows_nat_target::close () | 
 | { | 
 |   DEBUG_EVENTS ("inferior_ptid=%d\n", inferior_ptid.pid ()); | 
 |   async (false); | 
 | } | 
 |  | 
 | /* Convert pid to printable format.  */ | 
 | std::string | 
 | windows_nat_target::pid_to_str (ptid_t ptid) | 
 | { | 
 |   if (ptid.lwp () != 0) | 
 |     return string_printf ("Thread %d.0x%lx", ptid.pid (), ptid.lwp ()); | 
 |  | 
 |   return normal_pid_to_str (ptid); | 
 | } | 
 |  | 
 | static enum target_xfer_status | 
 | windows_xfer_shared_libraries (struct target_ops *ops, | 
 | 			       enum target_object object, const char *annex, | 
 | 			       gdb_byte *readbuf, const gdb_byte *writebuf, | 
 | 			       ULONGEST offset, ULONGEST len, | 
 | 			       ULONGEST *xfered_len) | 
 | { | 
 |   if (writebuf) | 
 |     return TARGET_XFER_E_IO; | 
 |  | 
 |   std::string xml = "<library-list>\n"; | 
 |   for (windows_solib &so : windows_process.solibs) | 
 |     windows_xfer_shared_library (so.name.c_str (), | 
 | 				 (CORE_ADDR) (uintptr_t) so.load_addr, | 
 | 				 &so.text_offset, | 
 | 				 current_inferior ()->arch (), xml); | 
 |   xml += "</library-list>\n"; | 
 |  | 
 |   ULONGEST len_avail = xml.size (); | 
 |   if (offset >= len_avail) | 
 |     len = 0; | 
 |   else | 
 |     { | 
 |       if (len > len_avail - offset) | 
 | 	len = len_avail - offset; | 
 |       memcpy (readbuf, xml.data () + offset, len); | 
 |     } | 
 |  | 
 |   *xfered_len = (ULONGEST) len; | 
 |   return len != 0 ? TARGET_XFER_OK : TARGET_XFER_EOF; | 
 | } | 
 |  | 
 | /* Helper for windows_nat_target::xfer_partial that handles signal info.  */ | 
 |  | 
 | static enum target_xfer_status | 
 | windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len, | 
 | 		      ULONGEST *xfered_len) | 
 | { | 
 |   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 (windows_process.siginfo_er.ExceptionCode == 0) | 
 |     return TARGET_XFER_E_IO; | 
 |  | 
 |   if (readbuf == nullptr) | 
 |     return TARGET_XFER_E_IO; | 
 |  | 
 |   if (offset > bufsize) | 
 |     return TARGET_XFER_E_IO; | 
 |  | 
 |   if (offset + len > bufsize) | 
 |     len = bufsize - offset; | 
 |  | 
 |   memcpy (readbuf, buf + offset, len); | 
 |   *xfered_len = len; | 
 |  | 
 |   return TARGET_XFER_OK; | 
 | } | 
 |  | 
 | enum target_xfer_status | 
 | windows_nat_target::xfer_partial (enum target_object object, | 
 | 				  const char *annex, gdb_byte *readbuf, | 
 | 				  const gdb_byte *writebuf, ULONGEST offset, | 
 | 				  ULONGEST len, ULONGEST *xfered_len) | 
 | { | 
 |   switch (object) | 
 |     { | 
 |     case TARGET_OBJECT_MEMORY: | 
 |       return windows_xfer_memory (readbuf, writebuf, offset, len, xfered_len); | 
 |  | 
 |     case TARGET_OBJECT_LIBRARIES: | 
 |       return windows_xfer_shared_libraries (this, object, annex, readbuf, | 
 | 					    writebuf, offset, len, xfered_len); | 
 |  | 
 |     case TARGET_OBJECT_SIGNAL_INFO: | 
 |       return windows_xfer_siginfo (readbuf, offset, len, xfered_len); | 
 |  | 
 |     default: | 
 |       if (beneath () == NULL) | 
 | 	{ | 
 | 	  /* This can happen when requesting the transfer of unsupported | 
 | 	     objects before a program has been started (and therefore | 
 | 	     with the current_target having no target beneath).  */ | 
 | 	  return TARGET_XFER_E_IO; | 
 | 	} | 
 |       return beneath ()->xfer_partial (object, annex, | 
 | 				       readbuf, writebuf, offset, len, | 
 | 				       xfered_len); | 
 |     } | 
 | } | 
 |  | 
 | /* Provide thread local base, i.e. Thread Information Block address. | 
 |    Returns 1 if ptid is found and sets *ADDR to thread_local_base.  */ | 
 |  | 
 | bool | 
 | windows_nat_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) | 
 | { | 
 |   windows_thread_info *th; | 
 |  | 
 |   th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT); | 
 |   if (th == NULL) | 
 |     return false; | 
 |  | 
 |   if (addr != NULL) | 
 |     *addr = th->thread_local_base; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | ptid_t | 
 | windows_nat_target::get_ada_task_ptid (long lwp, ULONGEST thread) | 
 | { | 
 |   return ptid_t (inferior_ptid.pid (), lwp, 0); | 
 | } | 
 |  | 
 | /* Implementation of the to_thread_name method.  */ | 
 |  | 
 | const char * | 
 | windows_nat_target::thread_name (struct thread_info *thr) | 
 | { | 
 |   windows_thread_info *th | 
 |     = windows_process.thread_rec (thr->ptid, | 
 | 				  DONT_INVALIDATE_CONTEXT); | 
 |   return th->thread_name (); | 
 | } | 
 |  | 
 |  | 
 | void _initialize_windows_nat (); | 
 | void | 
 | _initialize_windows_nat () | 
 | { | 
 |   x86_dr_low.set_control = cygwin_set_dr7; | 
 |   x86_dr_low.set_addr = cygwin_set_dr; | 
 |   x86_dr_low.get_addr = cygwin_get_dr; | 
 |   x86_dr_low.get_status = cygwin_get_dr6; | 
 |   x86_dr_low.get_control = cygwin_get_dr7; | 
 |  | 
 |   /* x86_dr_low.debug_register_length field is set by | 
 |      calling x86_set_debug_register_length function | 
 |      in processor windows specific native file.  */ | 
 |  | 
 |   /* The target is not a global specifically to avoid a C++ "static | 
 |      initializer fiasco" situation.  */ | 
 |   add_inf_child_target (new windows_nat_target); | 
 |  | 
 | #ifdef __CYGWIN__ | 
 |   cygwin_internal (CW_SET_DOS_FILE_WARNING, 0); | 
 | #endif | 
 |  | 
 |   add_com ("signal-event", class_run, signal_event_command, _("\ | 
 | Signal a crashed process with event ID, to allow its debugging.\n\ | 
 | This command is needed in support of setting up GDB as JIT debugger on \ | 
 | MS-Windows.  The command should be invoked from the GDB command line using \ | 
 | the '-ex' command-line option.  The ID of the event that blocks the \ | 
 | crashed process will be supplied by the Windows JIT debugging mechanism.")); | 
 |  | 
 | #ifdef __CYGWIN__ | 
 |   add_setshow_boolean_cmd ("shell", class_support, &useshell, _("\ | 
 | Set use of shell to start subprocess."), _("\ | 
 | Show use of shell to start subprocess."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("cygwin-exceptions", class_support, | 
 | 			   &cygwin_exceptions, _("\ | 
 | Break when an exception is detected in the Cygwin DLL itself."), _("\ | 
 | Show whether gdb breaks on exceptions in the Cygwin DLL itself."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 | #endif | 
 |  | 
 |   add_setshow_boolean_cmd ("new-console", class_support, &new_console, _("\ | 
 | Set creation of new console when creating child process."), _("\ | 
 | Show creation of new console when creating child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("new-group", class_support, &new_group, _("\ | 
 | Set creation of new group when creating child process."), _("\ | 
 | Show creation of new group when creating child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("debugexec", class_support, &debug_exec, _("\ | 
 | Set whether to display execution in child process."), _("\ | 
 | Show whether to display execution in child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("debugevents", class_support, &debug_events, _("\ | 
 | Set whether to display kernel events in child process."), _("\ | 
 | Show whether to display kernel events in child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("debugmemory", class_support, &debug_memory, _("\ | 
 | Set whether to display memory accesses in child process."), _("\ | 
 | Show whether to display memory accesses in child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   add_setshow_boolean_cmd ("debugexceptions", class_support, | 
 | 			   &debug_exceptions, _("\ | 
 | Set whether to display kernel exceptions in child process."), _("\ | 
 | Show whether to display kernel exceptions in child process."), NULL, | 
 | 			   NULL, | 
 | 			   NULL, /* FIXME: i18n: */ | 
 | 			   &setlist, &showlist); | 
 |  | 
 |   init_w32_command_list (); | 
 |  | 
 |   add_cmd ("selector", class_info, display_selectors, | 
 | 	   _("Display selectors infos."), | 
 | 	   &info_w32_cmdlist); | 
 |  | 
 |   if (!initialize_loadable ()) | 
 |     { | 
 |       /* This will probably fail on Windows 9x/Me.  Let the user know | 
 | 	 that we're missing some functionality.  */ | 
 |       warning(_("\ | 
 | cannot automatically find executable file or library to read symbols.\n\ | 
 | Use \"file\" or \"dll\" command to load executable/libraries directly.")); | 
 |     } | 
 | } | 
 |  | 
 | /* Hardware watchpoint support, adapted from go32-nat.c code.  */ | 
 |  | 
 | /* Pass the address ADDR to the inferior in the I'th debug register. | 
 |    Here we just store the address in dr array, the registers will be | 
 |    actually set up when windows_continue is called.  */ | 
 | static void | 
 | cygwin_set_dr (int i, CORE_ADDR addr) | 
 | { | 
 |   if (i < 0 || i > 3) | 
 |     internal_error (_("Invalid register %d in cygwin_set_dr.\n"), i); | 
 |   windows_process.dr[i] = addr; | 
 |  | 
 |   for (auto &th : windows_process.thread_list) | 
 |     th->debug_registers_changed = true; | 
 | } | 
 |  | 
 | /* Pass the value VAL to the inferior in the DR7 debug control | 
 |    register.  Here we just store the address in D_REGS, the watchpoint | 
 |    will be actually set up in windows_wait.  */ | 
 | static void | 
 | cygwin_set_dr7 (unsigned long val) | 
 | { | 
 |   windows_process.dr[7] = (CORE_ADDR) val; | 
 |  | 
 |   for (auto &th : windows_process.thread_list) | 
 |     th->debug_registers_changed = true; | 
 | } | 
 |  | 
 | /* Get the value of debug register I from the inferior.  */ | 
 |  | 
 | static CORE_ADDR | 
 | cygwin_get_dr (int i) | 
 | { | 
 |   return windows_process.dr[i]; | 
 | } | 
 |  | 
 | /* Get the value of the DR6 debug status register from the inferior. | 
 |    Here we just return the value stored in dr[6] | 
 |    by the last call to thread_rec for current_event.dwThreadId id.  */ | 
 | static unsigned long | 
 | cygwin_get_dr6 (void) | 
 | { | 
 |   return (unsigned long) windows_process.dr[6]; | 
 | } | 
 |  | 
 | /* Get the value of the DR7 debug status register from the inferior. | 
 |    Here we just return the value stored in dr[7] by the last call to | 
 |    thread_rec for current_event.dwThreadId id.  */ | 
 |  | 
 | static unsigned long | 
 | cygwin_get_dr7 (void) | 
 | { | 
 |   return (unsigned long) windows_process.dr[7]; | 
 | } | 
 |  | 
 | /* Determine if the thread referenced by "ptid" is alive | 
 |    by "polling" it.  If WaitForSingleObject returns WAIT_OBJECT_0 | 
 |    it means that the thread has died.  Otherwise it is assumed to be alive.  */ | 
 |  | 
 | bool | 
 | windows_nat_target::thread_alive (ptid_t ptid) | 
 | { | 
 |   gdb_assert (ptid.lwp () != 0); | 
 |  | 
 |   windows_thread_info *th | 
 |     = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT); | 
 |   return WaitForSingleObject (th->h, 0) != WAIT_OBJECT_0; | 
 | } | 
 |  | 
 | void _initialize_check_for_gdb_ini (); | 
 | void | 
 | _initialize_check_for_gdb_ini () | 
 | { | 
 |   char *homedir; | 
 |   if (inhibit_gdbinit) | 
 |     return; | 
 |  | 
 |   homedir = getenv ("HOME"); | 
 |   if (homedir) | 
 |     { | 
 |       char *p; | 
 |       char *oldini = (char *) alloca (strlen (homedir) + | 
 | 				      sizeof ("gdb.ini") + 1); | 
 |       strcpy (oldini, homedir); | 
 |       p = strchr (oldini, '\0'); | 
 |       if (p > oldini && !IS_DIR_SEPARATOR (p[-1])) | 
 | 	*p++ = '/'; | 
 |       strcpy (p, "gdb.ini"); | 
 |       if (access (oldini, 0) == 0) | 
 | 	{ | 
 | 	  int len = strlen (oldini); | 
 | 	  char *newini = (char *) alloca (len + 2); | 
 |  | 
 | 	  xsnprintf (newini, len + 2, "%.*s.gdbinit", | 
 | 		     (int) (len - (sizeof ("gdb.ini") - 1)), oldini); | 
 | 	  warning (_("obsolete '%s' found. Rename to '%s'."), oldini, newini); | 
 | 	} | 
 |     } | 
 | } |