|  | /* 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 "exceptions.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 <atomic> | 
|  | #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 "cli/cli-cmds.h" | 
|  | #include "cli/cli-style.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; | 
|  |  | 
|  | 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__ | 
|  | /* 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 | 
|  |  | 
|  | #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); | 
|  |  | 
|  | /* Force the process_thread thread to return from WaitForDebugEvent. | 
|  | PROCESS_ALIVE is set to false if the inferior process exits while | 
|  | we're trying to break out the process_thread thread.  This can | 
|  | happen because this is called while all threads are running free, | 
|  | while we're trying to detach.  */ | 
|  | void break_out_process_thread (bool &process_alive); | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | /* True if we last called ContinueDebugEvent and the process_thread | 
|  | thread is now waiting for events.  False if WaitForDebugEvent | 
|  | already returned an event, and we need to ContinueDebugEvent | 
|  | again to restart the inferior.  */ | 
|  | bool m_continued = 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; | 
|  | }); | 
|  |  | 
|  | m_continued = 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 = windows_process.with_context (th, [] (auto *context) | 
|  | { | 
|  | return (char *) context; | 
|  | }); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | windows_process.with_context (th, [&] (auto *context) | 
|  | { | 
|  | context->ContextFlags = WindowsContext<decltype(context)>::all; | 
|  | CHECK (get_thread_context (th->h, 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] = context->Dr0; | 
|  | windows_process.dr[1] = context->Dr1; | 
|  | windows_process.dr[2] = context->Dr2; | 
|  | windows_process.dr[3] = context->Dr3; | 
|  | windows_process.dr[6] = context->Dr6; | 
|  | windows_process.dr[7] = 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 = windows_process.with_context (th, [] (auto *context) | 
|  | { | 
|  | return (char *) context; | 
|  | }); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | windows_solib *so = &windows_process.solibs.emplace_back (); | 
|  | so->load_addr = load_addr; | 
|  | so->original_name = name; | 
|  |  | 
|  | #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"); | 
|  | } | 
|  |  | 
|  | so->name = buf; | 
|  | #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"); | 
|  | } | 
|  | } | 
|  | if (buf[0]) | 
|  | { | 
|  | bool ok = false; | 
|  |  | 
|  | /* Check how big the output buffer has to be.  */ | 
|  | ssize_t size = cygwin_conv_path (CCP_WIN_W_TO_POSIX, buf, nullptr, 0); | 
|  | if (size > 0) | 
|  | { | 
|  | /* SIZE includes the null terminator.  */ | 
|  | so->name.resize (size - 1); | 
|  | if (cygwin_conv_path (CCP_WIN_W_TO_POSIX, buf, so->name.data (), | 
|  | size) == 0) | 
|  | ok = true; | 
|  | } | 
|  | if (!ok) | 
|  | so->name = so->original_name; | 
|  | } | 
|  | else | 
|  | { | 
|  | gdb::unique_xmalloc_ptr<char> rname = gdb_realpath (name); | 
|  | if (rname != nullptr) | 
|  | so->name = rname.get (); | 
|  | else | 
|  | { | 
|  | warning (_("dll path for \"%s\" 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 marker is | 
|  | followed by the signal number itself, and (since Cygwin 1.7) | 
|  | the thread id, and the address of a saved context in the | 
|  | inferior (That context has an IP which is the return address | 
|  | in "user" code of the cygwin internal signal handling code, | 
|  | but is not otherwise usable). | 
|  |  | 
|  | Tell gdb to treat this like the given thread issued 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); | 
|  | LPCVOID x = 0; | 
|  |  | 
|  | if (gotasig) | 
|  | { | 
|  | ourstatus->set_stopped (gotasig); | 
|  | retval = strtoul (p, &p, 0); | 
|  | if (!retval) | 
|  | retval = current_event.dwThreadId; | 
|  | else | 
|  | x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0); | 
|  | } | 
|  |  | 
|  | DEBUG_EVENTS ("gdb: cygwin signal %d, thread 0x%x, CONTEXT @ %p", | 
|  | gotasig, retval, x); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int | 
|  | display_selector (HANDLE thread, DWORD sel) | 
|  | { | 
|  | LDT_ENTRY info; | 
|  | BOOL ret = windows_process.with_context (nullptr, [&] (auto *context) | 
|  | { | 
|  | return get_thread_selector_entry (context, 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) | 
|  | { | 
|  | windows_process.with_context (current_windows_thread, [&] (auto *context) | 
|  | { | 
|  | gdb_puts ("Selector $cs\n"); | 
|  | display_selector (current_windows_thread->h, context->SegCs); | 
|  | gdb_puts ("Selector $ds\n"); | 
|  | display_selector (current_windows_thread->h, context->SegDs); | 
|  | gdb_puts ("Selector $es\n"); | 
|  | display_selector (current_windows_thread->h, context->SegEs); | 
|  | gdb_puts ("Selector $ss\n"); | 
|  | display_selector (current_windows_thread->h, context->SegSs); | 
|  | gdb_puts ("Selector $fs\n"); | 
|  | display_selector (current_windows_thread->h, context->SegFs); | 
|  | gdb_puts ("Selector $gs\n"); | 
|  | display_selector (current_windows_thread->h, 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) | 
|  | { | 
|  | windows_process.with_context (th.get (), [&] (auto *context) | 
|  | { | 
|  | if (th->debug_registers_changed) | 
|  | { | 
|  | context->ContextFlags | 
|  | |= WindowsContext<decltype(context)>::debug; | 
|  | context->Dr0 = windows_process.dr[0]; | 
|  | context->Dr1 = windows_process.dr[1]; | 
|  | context->Dr2 = windows_process.dr[2]; | 
|  | context->Dr3 = windows_process.dr[3]; | 
|  | context->Dr6 = DR6_CLEAR_VALUE; | 
|  | context->Dr7 = windows_process.dr[7]; | 
|  | th->debug_registers_changed = false; | 
|  | } | 
|  | if (context->ContextFlags) | 
|  | { | 
|  | DWORD ec = 0; | 
|  |  | 
|  | if (GetExitCodeThread (th->h, &ec) | 
|  | && ec == STILL_ACTIVE) | 
|  | { | 
|  | BOOL status = set_thread_context (th->h, context); | 
|  |  | 
|  | if (!killed) | 
|  | CHECK (status); | 
|  | } | 
|  | 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); | 
|  |  | 
|  | m_continued = !last_call; | 
|  |  | 
|  | 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, | 
|  | windows_process.current_event.dwThreadId, 0), | 
|  | 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) | 
|  | { | 
|  | windows_process.with_context (th, [&] (auto *context) | 
|  | { | 
|  | 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)); | 
|  | context->EFlags |= FLAG_TRACE_BIT; | 
|  | } | 
|  |  | 
|  | if (context->ContextFlags) | 
|  | { | 
|  | if (th->debug_registers_changed) | 
|  | { | 
|  | context->Dr0 = windows_process.dr[0]; | 
|  | context->Dr1 = windows_process.dr[1]; | 
|  | context->Dr2 = windows_process.dr[2]; | 
|  | context->Dr3 = windows_process.dr[3]; | 
|  | context->Dr6 = DR6_CLEAR_VALUE; | 
|  | context->Dr7 = windows_process.dr[7]; | 
|  | th->debug_registers_changed = false; | 
|  | } | 
|  | CHECK (set_thread_context (th->h, context)); | 
|  | 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 (); | 
|  |  | 
|  | 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 (current_program_space); | 
|  | 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) | 
|  | { | 
|  | /* Maybe PID was a Cygwin PID.  Try the corresponding native | 
|  | Windows PID.  */ | 
|  | DWORD winpid = cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid); | 
|  |  | 
|  | if (winpid != 0) | 
|  | { | 
|  | /* It was indeed a Cygwin PID.  Fully switch to the | 
|  | Windows PID from here on.  We don't do this | 
|  | unconditionally to avoid ending up with PID=0 in the | 
|  | error message below.  */ | 
|  | pid = winpid; | 
|  |  | 
|  | ok = DebugActiveProcess (winpid); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (!ok) | 
|  | err = (unsigned) GetLastError (); | 
|  |  | 
|  | return ok; | 
|  | }); | 
|  |  | 
|  | 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::break_out_process_thread (bool &process_alive) | 
|  | { | 
|  | /* This is called when the process_thread thread is blocked in | 
|  | WaitForDebugEvent (unless it already returned some event we | 
|  | haven't consumed yet), and we need to unblock it so that we can | 
|  | have it call DebugActiveProcessStop. | 
|  |  | 
|  | To make WaitForDebugEvent return, we need to force some event in | 
|  | the inferior.  Any method that lets us do that (without | 
|  | disturbing the other threads), injects a new thread in the | 
|  | inferior. | 
|  |  | 
|  | We don't use DebugBreakProcess for this, because that injects a | 
|  | thread that ends up executing a breakpoint instruction.  We can't | 
|  | let the injected thread hit that breakpoint _after_ we've | 
|  | detached.  Consuming events until we see a breakpoint trap isn't | 
|  | 100% reliable, because we can't distinguish it from some other | 
|  | thread itself deciding to call int3 while we're detaching, unless | 
|  | we temporarily suspend all threads.  It's just a lot of | 
|  | complication, and there's an easier way. | 
|  |  | 
|  | Important observation: the thread creation event for the newly | 
|  | injected thread is sufficient to unblock WaitForDebugEvent. | 
|  |  | 
|  | Instead of DebugBreakProcess, we can instead use | 
|  | CreateRemoteThread to control the code that the injected thread | 
|  | runs ourselves.  We could consider pointing the injected thread | 
|  | at some side-effect-free Win32 function as entry point.  However, | 
|  | finding the address of such a function requires having at least | 
|  | minimal symbols loaded for ntdll.dll.  Having a way that avoids | 
|  | that is better, so that detach always works correctly even when | 
|  | we don't have any symbols loaded. | 
|  |  | 
|  | So what we do is inject a thread that doesn't actually run ANY | 
|  | userspace code, because we force-terminate it as soon as we see | 
|  | its corresponding thread creation event.  CreateRemoteThread | 
|  | gives us the new thread's ID, which we can match with the thread | 
|  | associated with the CREATE_THREAD_DEBUG_EVENT event.  */ | 
|  |  | 
|  | DWORD injected_thread_id = 0; | 
|  | HANDLE injected_thread_handle | 
|  | = CreateRemoteThread (windows_process.handle, NULL, | 
|  | 0, (LPTHREAD_START_ROUTINE) 0, | 
|  | NULL, 0, &injected_thread_id); | 
|  |  | 
|  | if (injected_thread_handle == NULL) | 
|  | { | 
|  | DWORD err = GetLastError (); | 
|  |  | 
|  | DEBUG_EVENTS ("CreateRemoteThread failed with %u", err); | 
|  |  | 
|  | if (err == ERROR_ACCESS_DENIED) | 
|  | { | 
|  | /* Creating the remote thread fails with ERROR_ACCESS_DENIED | 
|  | if the process exited before we had a chance to inject | 
|  | the thread.  Continue with the loop below and consume the | 
|  | process exit event anyhow, so that our caller can always | 
|  | call windows_continue.  */ | 
|  | } | 
|  | else | 
|  | throw_winerror_with_name (_("Can't detach from running process.  " | 
|  | "Interrupt it first."), | 
|  | err); | 
|  | } | 
|  |  | 
|  | process_alive = true; | 
|  |  | 
|  | /* At this point, the user has declared that they want to detach, so | 
|  | any event that happens from this point on should be forwarded to | 
|  | the inferior.  */ | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | DEBUG_EVENT current_event; | 
|  | wait_for_debug_event_main_thread (¤t_event); | 
|  |  | 
|  | if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) | 
|  | { | 
|  | DEBUG_EVENTS ("got EXIT_PROCESS_DEBUG_EVENT"); | 
|  | process_alive = false; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (current_event.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT | 
|  | && current_event.dwThreadId == injected_thread_id) | 
|  | { | 
|  | DEBUG_EVENTS ("got CREATE_THREAD_DEBUG_EVENT for injected thread"); | 
|  |  | 
|  | /* Terminate the injected thread, so it doesn't run any code | 
|  | at all.  All we wanted was some event, and | 
|  | CREATE_THREAD_DEBUG_EVENT is sufficient.  */ | 
|  | CHECK (TerminateThread (injected_thread_handle, 0)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | DEBUG_EVENTS ("got unrelated event, code %u", | 
|  | current_event.dwDebugEventCode); | 
|  | windows_continue (DBG_CONTINUE, -1, 0); | 
|  | } | 
|  |  | 
|  | if (injected_thread_handle != NULL) | 
|  | CHECK (CloseHandle (injected_thread_handle)); | 
|  | } | 
|  |  | 
|  | void | 
|  | windows_nat_target::detach (inferior *inf, int from_tty) | 
|  | { | 
|  | /* If we see the process exit while unblocking the process_thread | 
|  | helper thread, then we should skip the actual | 
|  | DebugActiveProcessStop call.  But don't report an error.  Just | 
|  | pretend the process exited shortly after the detach.  */ | 
|  | bool process_alive = true; | 
|  |  | 
|  | /* The process_thread helper thread will be blocked in | 
|  | WaitForDebugEvent waiting for events if we've resumed the target | 
|  | before we get here, e.g., with "attach&" or "c&".  We need to | 
|  | unblock it so that we can have it call DebugActiveProcessStop | 
|  | below, in the do_synchronously block.  */ | 
|  | if (m_continued) | 
|  | break_out_process_thread (process_alive); | 
|  |  | 
|  | windows_continue (DBG_CONTINUE, -1, 0, true); | 
|  |  | 
|  | std::optional<unsigned> err; | 
|  | if (process_alive) | 
|  | 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 ([&] () | 
|  | { | 
|  | BOOL ok = create_process (nullptr, args, flags, w32_env, | 
|  | inferior_cwd != nullptr ? infcwd : nullptr, | 
|  | disable_randomization, | 
|  | &si, &pi); | 
|  |  | 
|  | if (!ok) | 
|  | ret = (unsigned) GetLastError (); | 
|  |  | 
|  | return ok; | 
|  | }); | 
|  |  | 
|  | 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 ([&] () | 
|  | { | 
|  | BOOL ok = create_process (nullptr, /* image */ | 
|  | args,	/* command line */ | 
|  | flags,	/* start flags */ | 
|  | w32env,	/* environment */ | 
|  | inferior_cwd, /* current directory */ | 
|  | disable_randomization, | 
|  | &si, | 
|  | &pi); | 
|  | if (!ok) | 
|  | ret = (unsigned) GetLastError (); | 
|  |  | 
|  | return ok; | 
|  | }); | 
|  | 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\n\ | 
|  | MS-Windows.  The command should be invoked from the GDB command line using\n\ | 
|  | the '-ex' command-line option.  The ID of the event that blocks the\n\ | 
|  | 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 \"%ps\" or \"%ps\" command to load executable/libraries directly."), | 
|  | styled_string (command_style.style (), "file"), | 
|  | styled_string (command_style.style (), "dll")); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  | } | 
|  | } |