| /* Internal interfaces for the Windows code |
| Copyright (C) 1995-2024 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef NAT_WINDOWS_NAT_H |
| #define NAT_WINDOWS_NAT_H |
| |
| #include <windows.h> |
| #include <psapi.h> |
| #include <vector> |
| |
| #include <optional> |
| #include "target/waitstatus.h" |
| |
| #define STATUS_WX86_BREAKPOINT 0x4000001F |
| #define STATUS_WX86_SINGLE_STEP 0x4000001E |
| |
| namespace windows_nat |
| { |
| |
| /* Thread information structure used to track extra information about |
| each thread. */ |
| struct windows_thread_info |
| { |
| windows_thread_info (DWORD tid_, HANDLE h_, CORE_ADDR tlb) |
| : tid (tid_), |
| h (h_), |
| thread_local_base (tlb) |
| { |
| } |
| |
| DISABLE_COPY_AND_ASSIGN (windows_thread_info); |
| |
| /* Ensure that this thread has been suspended. */ |
| void suspend (); |
| |
| /* Resume the thread if it has been suspended. */ |
| void resume (); |
| |
| /* Return the thread's name, or nullptr if not known. The name is |
| stored in this thread and is guaranteed to live until at least |
| the next call. */ |
| const char *thread_name (); |
| |
| /* The Win32 thread identifier. */ |
| DWORD tid; |
| |
| /* The handle to the thread. */ |
| HANDLE h; |
| |
| /* Thread Information Block address. */ |
| CORE_ADDR thread_local_base; |
| |
| /* This keeps track of whether SuspendThread was called on this |
| thread. -1 means there was a failure or that the thread was |
| explicitly not suspended, 1 means it was called, and 0 means it |
| was not. */ |
| int suspended = 0; |
| |
| /* The context of the thread, including any manipulations. */ |
| union |
| { |
| CONTEXT context {}; |
| #ifdef __x86_64__ |
| WOW64_CONTEXT wow64_context; |
| #endif |
| }; |
| |
| /* Whether debug registers changed since we last set CONTEXT back to |
| the thread. */ |
| bool debug_registers_changed = false; |
| |
| /* Nonzero if CONTEXT is invalidated and must be re-read from the |
| inferior thread. */ |
| bool reload_context = false; |
| |
| /* True if this thread is currently stopped at a software |
| breakpoint. This is used to offset the PC when needed. */ |
| bool stopped_at_software_breakpoint = false; |
| |
| /* True if we've adjusted the PC after hitting a software |
| breakpoint, false otherwise. This lets us avoid multiple |
| adjustments if the registers are read multiple times. */ |
| bool pc_adjusted = false; |
| |
| /* The name of the thread. */ |
| gdb::unique_xmalloc_ptr<char> name; |
| }; |
| |
| |
| /* Possible values to pass to 'thread_rec'. */ |
| enum thread_disposition_type |
| { |
| /* Do not invalidate the thread's context, and do not suspend the |
| thread. */ |
| DONT_INVALIDATE_CONTEXT, |
| /* Invalidate the context, but do not suspend the thread. */ |
| DONT_SUSPEND, |
| /* Invalidate the context and suspend the thread. */ |
| INVALIDATE_CONTEXT |
| }; |
| |
| /* A single pending stop. See "pending_stops" for more |
| information. */ |
| struct pending_stop |
| { |
| /* The thread id. */ |
| DWORD thread_id; |
| |
| /* The target waitstatus we computed. */ |
| target_waitstatus status; |
| |
| /* The event. A few fields of this can be referenced after a stop, |
| and it seemed simplest to store the entire event. */ |
| DEBUG_EVENT event; |
| }; |
| |
| enum handle_exception_result |
| { |
| HANDLE_EXCEPTION_UNHANDLED = 0, |
| HANDLE_EXCEPTION_HANDLED, |
| HANDLE_EXCEPTION_IGNORED |
| }; |
| |
| /* A single Windows process. An object of this type (or subclass) is |
| created by the client. Some methods must be provided by the client |
| as well. */ |
| |
| struct windows_process_info |
| { |
| /* The process handle */ |
| HANDLE handle = 0; |
| DWORD main_thread_id = 0; |
| enum gdb_signal last_sig = GDB_SIGNAL_0; |
| |
| /* The current debug event from WaitForDebugEvent or from a pending |
| stop. */ |
| DEBUG_EVENT current_event {}; |
| |
| /* The ID of the thread for which we anticipate a stop event. |
| Normally this is -1, meaning we'll accept an event in any |
| thread. */ |
| DWORD desired_stop_thread_id = -1; |
| |
| /* A vector of pending stops. Sometimes, Windows will report a stop |
| on a thread that has been ostensibly suspended. We believe what |
| happens here is that two threads hit a breakpoint simultaneously, |
| and the Windows kernel queues the stop events. However, this can |
| result in the strange effect of trying to single step thread A -- |
| leaving all other threads suspended -- and then seeing a stop in |
| thread B. To handle this scenario, we queue all such "pending" |
| stops here, and then process them once the step has completed. See |
| PR gdb/22992. */ |
| std::vector<pending_stop> pending_stops; |
| |
| /* Contents of $_siginfo */ |
| EXCEPTION_RECORD siginfo_er {}; |
| |
| #ifdef __x86_64__ |
| /* The target is a WOW64 process */ |
| bool wow64_process = false; |
| /* Ignore first breakpoint exception of WOW64 process */ |
| bool ignore_first_breakpoint = false; |
| #endif |
| |
| |
| /* Find a thread record given a thread id. THREAD_DISPOSITION |
| controls whether the thread is suspended, and whether the context |
| is invalidated. |
| |
| This function must be supplied by the embedding application. */ |
| virtual windows_thread_info *thread_rec (ptid_t ptid, |
| thread_disposition_type disposition) = 0; |
| |
| /* Handle OUTPUT_DEBUG_STRING_EVENT from child process. Updates |
| OURSTATUS and returns the thread id if this represents a thread |
| change (this is specific to Cygwin), otherwise 0. |
| |
| Cygwin prepends its messages with a "cygwin:". Interpret this as |
| a Cygwin signal. Otherwise just print the string as a warning. |
| |
| This function must be supplied by the embedding application. */ |
| virtual int handle_output_debug_string (struct target_waitstatus *ourstatus) = 0; |
| |
| /* Handle a DLL load event. |
| |
| This function assumes that the current event did not occur during |
| inferior initialization. |
| |
| DLL_NAME is the name of the library. BASE is the base load |
| address. |
| |
| This function must be supplied by the embedding application. */ |
| |
| virtual void handle_load_dll (const char *dll_name, LPVOID base) = 0; |
| |
| /* Handle a DLL unload event. |
| |
| This function assumes that this event did not occur during inferior |
| initialization. |
| |
| This function must be supplied by the embedding application. */ |
| |
| virtual void handle_unload_dll () = 0; |
| |
| /* When EXCEPTION_ACCESS_VIOLATION is processed, we give the embedding |
| application a chance to change it to be considered "unhandled". |
| This function must be supplied by the embedding application. If it |
| returns true, then the exception is "unhandled". */ |
| |
| virtual bool handle_access_violation (const EXCEPTION_RECORD *rec) = 0; |
| |
| handle_exception_result handle_exception |
| (struct target_waitstatus *ourstatus, bool debug_exceptions); |
| |
| /* Call to indicate that a DLL was loaded. */ |
| |
| void dll_loaded_event (); |
| |
| /* Iterate over all DLLs currently mapped by our inferior, and |
| add them to our list of solibs. */ |
| |
| void add_all_dlls (); |
| |
| /* Return true if there is a pending stop matching |
| desired_stop_thread_id. If DEBUG_EVENTS is true, logging will be |
| enabled. */ |
| |
| bool matching_pending_stop (bool debug_events); |
| |
| /* See if a pending stop matches DESIRED_STOP_THREAD_ID. If so, |
| remove it from the list of pending stops, set 'current_event', and |
| return it. Otherwise, return an empty optional. */ |
| |
| std::optional<pending_stop> fetch_pending_stop (bool debug_events); |
| |
| const char *pid_to_exec_file (int); |
| |
| private: |
| |
| /* Handle MS_VC_EXCEPTION when processing a stop. MS_VC_EXCEPTION is |
| somewhat undocumented but is used to tell the debugger the name of |
| a thread. |
| |
| Return true if the exception was handled; return false otherwise. */ |
| |
| bool handle_ms_vc_exception (const EXCEPTION_RECORD *rec); |
| |
| /* Iterate over all DLLs currently mapped by our inferior, looking for |
| a DLL which is loaded at LOAD_ADDR. If found, add the DLL to our |
| list of solibs; otherwise do nothing. LOAD_ADDR NULL means add all |
| DLLs to the list of solibs; this is used when the inferior finishes |
| its initialization, and all the DLLs it statically depends on are |
| presumed loaded. */ |
| |
| void add_dll (LPVOID load_addr); |
| |
| /* Try to determine the executable filename. |
| |
| EXE_NAME_RET is a pointer to a buffer whose size is EXE_NAME_MAX_LEN. |
| |
| Upon success, the filename is stored inside EXE_NAME_RET, and |
| this function returns nonzero. |
| |
| Otherwise, this function returns zero and the contents of |
| EXE_NAME_RET is undefined. */ |
| |
| int get_exec_module_filename (char *exe_name_ret, size_t exe_name_max_len); |
| }; |
| |
| /* A simple wrapper for ContinueDebugEvent that continues the last |
| waited-for event. If DEBUG_EVENTS is true, logging will be |
| enabled. */ |
| |
| extern BOOL continue_last_debug_event (DWORD continue_status, |
| bool debug_events); |
| |
| /* A simple wrapper for WaitForDebugEvent that also sets the internal |
| 'last_wait_event' on success. */ |
| |
| extern BOOL wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout); |
| |
| /* Wrappers for CreateProcess. These exist primarily so that the |
| "disable randomization" feature can be implemented in a single |
| place. */ |
| |
| extern BOOL create_process (const char *image, char *command_line, |
| DWORD flags, void *environment, |
| const char *cur_dir, |
| bool no_randomization, |
| STARTUPINFOA *startup_info, |
| PROCESS_INFORMATION *process_info); |
| #ifdef __CYGWIN__ |
| extern BOOL create_process (const wchar_t *image, wchar_t *command_line, |
| DWORD flags, void *environment, |
| const wchar_t *cur_dir, |
| bool no_randomization, |
| STARTUPINFOW *startup_info, |
| PROCESS_INFORMATION *process_info); |
| #endif /* __CYGWIN__ */ |
| |
| #define AdjustTokenPrivileges dyn_AdjustTokenPrivileges |
| #define DebugActiveProcessStop dyn_DebugActiveProcessStop |
| #define DebugBreakProcess dyn_DebugBreakProcess |
| #define DebugSetProcessKillOnExit dyn_DebugSetProcessKillOnExit |
| #undef EnumProcessModules |
| #define EnumProcessModules dyn_EnumProcessModules |
| #undef EnumProcessModulesEx |
| #define EnumProcessModulesEx dyn_EnumProcessModulesEx |
| #undef GetModuleInformation |
| #define GetModuleInformation dyn_GetModuleInformation |
| #undef GetModuleFileNameExA |
| #define GetModuleFileNameExA dyn_GetModuleFileNameExA |
| #undef GetModuleFileNameExW |
| #define GetModuleFileNameExW dyn_GetModuleFileNameExW |
| #define LookupPrivilegeValueA dyn_LookupPrivilegeValueA |
| #define OpenProcessToken dyn_OpenProcessToken |
| #define GetConsoleFontSize dyn_GetConsoleFontSize |
| #define GetCurrentConsoleFont dyn_GetCurrentConsoleFont |
| #define Wow64SuspendThread dyn_Wow64SuspendThread |
| #define Wow64GetThreadContext dyn_Wow64GetThreadContext |
| #define Wow64SetThreadContext dyn_Wow64SetThreadContext |
| #define Wow64GetThreadSelectorEntry dyn_Wow64GetThreadSelectorEntry |
| #define GenerateConsoleCtrlEvent dyn_GenerateConsoleCtrlEvent |
| #define InitializeProcThreadAttributeList dyn_InitializeProcThreadAttributeList |
| #define UpdateProcThreadAttribute dyn_UpdateProcThreadAttribute |
| #define DeleteProcThreadAttributeList dyn_DeleteProcThreadAttributeList |
| |
| typedef BOOL WINAPI (AdjustTokenPrivileges_ftype) (HANDLE, BOOL, |
| PTOKEN_PRIVILEGES, |
| DWORD, PTOKEN_PRIVILEGES, |
| PDWORD); |
| extern AdjustTokenPrivileges_ftype *AdjustTokenPrivileges; |
| |
| typedef BOOL WINAPI (DebugActiveProcessStop_ftype) (DWORD); |
| extern DebugActiveProcessStop_ftype *DebugActiveProcessStop; |
| |
| typedef BOOL WINAPI (DebugBreakProcess_ftype) (HANDLE); |
| extern DebugBreakProcess_ftype *DebugBreakProcess; |
| |
| typedef BOOL WINAPI (DebugSetProcessKillOnExit_ftype) (BOOL); |
| extern DebugSetProcessKillOnExit_ftype *DebugSetProcessKillOnExit; |
| |
| typedef BOOL WINAPI (EnumProcessModules_ftype) (HANDLE, HMODULE *, DWORD, |
| LPDWORD); |
| extern EnumProcessModules_ftype *EnumProcessModules; |
| |
| #ifdef __x86_64__ |
| typedef BOOL WINAPI (EnumProcessModulesEx_ftype) (HANDLE, HMODULE *, DWORD, |
| LPDWORD, DWORD); |
| extern EnumProcessModulesEx_ftype *EnumProcessModulesEx; |
| #endif |
| |
| typedef BOOL WINAPI (GetModuleInformation_ftype) (HANDLE, HMODULE, |
| LPMODULEINFO, DWORD); |
| extern GetModuleInformation_ftype *GetModuleInformation; |
| |
| typedef DWORD WINAPI (GetModuleFileNameExA_ftype) (HANDLE, HMODULE, LPSTR, |
| DWORD); |
| extern GetModuleFileNameExA_ftype *GetModuleFileNameExA; |
| |
| typedef DWORD WINAPI (GetModuleFileNameExW_ftype) (HANDLE, HMODULE, |
| LPWSTR, DWORD); |
| extern GetModuleFileNameExW_ftype *GetModuleFileNameExW; |
| |
| typedef BOOL WINAPI (LookupPrivilegeValueA_ftype) (LPCSTR, LPCSTR, PLUID); |
| extern LookupPrivilegeValueA_ftype *LookupPrivilegeValueA; |
| |
| typedef BOOL WINAPI (OpenProcessToken_ftype) (HANDLE, DWORD, PHANDLE); |
| extern OpenProcessToken_ftype *OpenProcessToken; |
| |
| typedef BOOL WINAPI (GetCurrentConsoleFont_ftype) (HANDLE, BOOL, |
| CONSOLE_FONT_INFO *); |
| extern GetCurrentConsoleFont_ftype *GetCurrentConsoleFont; |
| |
| typedef COORD WINAPI (GetConsoleFontSize_ftype) (HANDLE, DWORD); |
| extern GetConsoleFontSize_ftype *GetConsoleFontSize; |
| |
| #ifdef __x86_64__ |
| typedef DWORD WINAPI (Wow64SuspendThread_ftype) (HANDLE); |
| extern Wow64SuspendThread_ftype *Wow64SuspendThread; |
| |
| typedef BOOL WINAPI (Wow64GetThreadContext_ftype) (HANDLE, PWOW64_CONTEXT); |
| extern Wow64GetThreadContext_ftype *Wow64GetThreadContext; |
| |
| typedef BOOL WINAPI (Wow64SetThreadContext_ftype) (HANDLE, |
| const WOW64_CONTEXT *); |
| extern Wow64SetThreadContext_ftype *Wow64SetThreadContext; |
| |
| typedef BOOL WINAPI (Wow64GetThreadSelectorEntry_ftype) (HANDLE, DWORD, |
| PLDT_ENTRY); |
| extern Wow64GetThreadSelectorEntry_ftype *Wow64GetThreadSelectorEntry; |
| #endif |
| |
| typedef BOOL WINAPI (GenerateConsoleCtrlEvent_ftype) (DWORD, DWORD); |
| extern GenerateConsoleCtrlEvent_ftype *GenerateConsoleCtrlEvent; |
| |
| /* We use a local typedef for this type to avoid depending on |
| Windows 8. */ |
| typedef void *gdb_lpproc_thread_attribute_list; |
| |
| typedef BOOL WINAPI (InitializeProcThreadAttributeList_ftype) |
| (gdb_lpproc_thread_attribute_list lpAttributeList, |
| DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize); |
| extern InitializeProcThreadAttributeList_ftype *InitializeProcThreadAttributeList; |
| |
| typedef BOOL WINAPI (UpdateProcThreadAttribute_ftype) |
| (gdb_lpproc_thread_attribute_list lpAttributeList, |
| DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, |
| PVOID lpPreviousValue, PSIZE_T lpReturnSize); |
| extern UpdateProcThreadAttribute_ftype *UpdateProcThreadAttribute; |
| |
| typedef void WINAPI (DeleteProcThreadAttributeList_ftype) |
| (gdb_lpproc_thread_attribute_list lpAttributeList); |
| extern DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList; |
| |
| /* Return true if it's possible to disable randomization on this |
| host. */ |
| |
| extern bool disable_randomization_available (); |
| |
| /* Load any functions which may not be available in ancient versions |
| of Windows. */ |
| |
| extern bool initialize_loadable (); |
| |
| } |
| |
| #endif |