/* Internal interfaces for the Windows code
- Copyright (C) 1995-2020 Free Software Foundation, Inc.
+ Copyright (C) 1995-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "nat/windows-nat.h"
#include "gdbsupport/common-debug.h"
-#define STATUS_WX86_BREAKPOINT 0x4000001F
-#define STATUS_WX86_SINGLE_STEP 0x4000001E
-
namespace windows_nat
{
DWORD main_thread_id;
enum gdb_signal last_sig = GDB_SIGNAL_0;
DEBUG_EVENT current_event;
-DEBUG_EVENT last_wait_event;
-windows_thread_info *current_windows_thread;
+
+/* The most recent event from WaitForDebugEvent. Unlike
+ current_event, this is guaranteed never to come from a pending
+ stop. This is important because only data from the most recent
+ event from WaitForDebugEvent can be used when calling
+ ContinueDebugEvent. */
+static DEBUG_EVENT last_wait_event;
+
DWORD desired_stop_thread_id = -1;
std::vector<pending_stop> pending_stops;
EXCEPTION_RECORD siginfo_er;
+#ifdef __x86_64__
+bool wow64_process = false;
+bool ignore_first_breakpoint = false;
+#endif
+
+AdjustTokenPrivileges_ftype *AdjustTokenPrivileges;
+DebugActiveProcessStop_ftype *DebugActiveProcessStop;
+DebugBreakProcess_ftype *DebugBreakProcess;
+DebugSetProcessKillOnExit_ftype *DebugSetProcessKillOnExit;
+EnumProcessModules_ftype *EnumProcessModules;
+#ifdef __x86_64__
+EnumProcessModulesEx_ftype *EnumProcessModulesEx;
+#endif
+GetModuleInformation_ftype *GetModuleInformation;
+GetModuleFileNameExA_ftype *GetModuleFileNameExA;
+GetModuleFileNameExW_ftype *GetModuleFileNameExW;
+LookupPrivilegeValueA_ftype *LookupPrivilegeValueA;
+OpenProcessToken_ftype *OpenProcessToken;
+GetCurrentConsoleFont_ftype *GetCurrentConsoleFont;
+GetConsoleFontSize_ftype *GetConsoleFontSize;
+#ifdef __x86_64__
+Wow64SuspendThread_ftype *Wow64SuspendThread;
+Wow64GetThreadContext_ftype *Wow64GetThreadContext;
+Wow64SetThreadContext_ftype *Wow64SetThreadContext;
+Wow64GetThreadSelectorEntry_ftype *Wow64GetThreadSelectorEntry;
+#endif
+GenerateConsoleCtrlEvent_ftype *GenerateConsoleCtrlEvent;
+
/* Note that 'debug_events' must be locally defined in the relevant
functions. */
-#define DEBUG_EVENTS(x) if (debug_events) debug_printf x
+#define DEBUG_EVENTS(fmt, ...) \
+ debug_prefixed_printf_cond (debug_events, "windows events", fmt, \
+ ## __VA_ARGS__)
windows_thread_info::~windows_thread_info ()
{
- CloseHandle (h);
}
void
suspended = 0;
}
-const char *
+/* Return the name of the DLL referenced by H at ADDRESS. UNICODE
+ determines what sort of string is read from the inferior. Returns
+ the name of the DLL, or NULL on error. If a name is returned, it
+ is stored in a static buffer which is valid until the next call to
+ get_image_name. */
+
+static const char *
get_image_name (HANDLE h, void *address, int unicode)
{
#ifdef __CYGWIN__
if (address == NULL)
return NULL;
-#ifdef _WIN32_WCE
- /* Windows CE reports the address of the image name,
- instead of an address of a pointer into the image name. */
- address_ptr = address;
-#else
/* See if we could read the address of a string, and that the
address isn't null. */
if (!ReadProcessMemory (h, address, &address_ptr,
|| done != sizeof (address_ptr)
|| !address_ptr)
return NULL;
-#endif
/* Find the length of the string. */
while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
case EXCEPTION_ACCESS_VIOLATION:
DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION");
ourstatus->value.sig = GDB_SIGNAL_SEGV;
-#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 HANDLE_EXCEPTION_UNHANDLED;
- }
-#endif
+ if (handle_access_violation (rec))
+ return HANDLE_EXCEPTION_UNHANDLED;
break;
case STATUS_STACK_OVERFLOW:
DEBUG_EXCEPTION_SIMPLE ("STATUS_STACK_OVERFLOW");
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
ignore_first_breakpoint = false;
}
+ else if (wow64_process)
+ {
+ /* This breakpoint exception is triggered for WOW64 processes when
+ reaching an int3 instruction in 64bit code.
+ gdb checks for int3 in case of SIGTRAP, this fails because
+ Wow64GetThreadContext can only report the pc of 32bit code, and
+ gdb lets the target process continue.
+ So handle it as SIGINT instead, then the target is stopped
+ unconditionally. */
+ DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_BREAKPOINT");
+ rec->ExceptionCode = DBG_CONTROL_C;
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ break;
+ }
#endif
/* FALLTHROUGH */
case STATUS_WX86_BREAKPOINT:
DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_BREAKPOINT");
ourstatus->value.sig = GDB_SIGNAL_TRAP;
-#ifdef _WIN32_WCE
- /* Remove the initial breakpoint. */
- check_breakpoints ((CORE_ADDR) (long) current_event
- .u.Exception.ExceptionRecord.ExceptionAddress);
-#endif
break;
case DBG_CONTROL_C:
DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_C");
#undef DEBUG_EXCEPTION_SIMPLE
}
+/* 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. */
+
+static void
+windows_add_dll (LPVOID load_addr)
+{
+ HMODULE dummy_hmodule;
+ DWORD cb_needed;
+ HMODULE *hmodules;
+ int i;
+
+#ifdef __x86_64__
+ if (wow64_process)
+ {
+ if (EnumProcessModulesEx (current_process_handle, &dummy_hmodule,
+ sizeof (HMODULE), &cb_needed,
+ LIST_MODULES_32BIT) == 0)
+ return;
+ }
+ else
+#endif
+ {
+ if (EnumProcessModules (current_process_handle, &dummy_hmodule,
+ sizeof (HMODULE), &cb_needed) == 0)
+ return;
+ }
+
+ if (cb_needed < 1)
+ return;
+
+ hmodules = (HMODULE *) alloca (cb_needed);
+#ifdef __x86_64__
+ if (wow64_process)
+ {
+ if (EnumProcessModulesEx (current_process_handle, hmodules,
+ cb_needed, &cb_needed,
+ LIST_MODULES_32BIT) == 0)
+ return;
+ }
+ else
+#endif
+ {
+ if (EnumProcessModules (current_process_handle, hmodules,
+ cb_needed, &cb_needed) == 0)
+ return;
+ }
+
+ char system_dir[MAX_PATH];
+ char syswow_dir[MAX_PATH];
+ size_t system_dir_len = 0;
+ bool convert_syswow_dir = false;
+#ifdef __x86_64__
+ if (wow64_process)
+#endif
+ {
+ /* This fails on 32bit Windows because it has no SysWOW64 directory,
+ and in this case a path conversion isn't necessary. */
+ UINT len = GetSystemWow64DirectoryA (syswow_dir, sizeof (syswow_dir));
+ if (len > 0)
+ {
+ /* Check that we have passed a large enough buffer. */
+ gdb_assert (len < sizeof (syswow_dir));
+
+ len = GetSystemDirectoryA (system_dir, sizeof (system_dir));
+ /* Error check. */
+ gdb_assert (len != 0);
+ /* Check that we have passed a large enough buffer. */
+ gdb_assert (len < sizeof (system_dir));
+
+ strcat (system_dir, "\\");
+ strcat (syswow_dir, "\\");
+ system_dir_len = strlen (system_dir);
+
+ convert_syswow_dir = true;
+ }
+
+ }
+ for (i = 1; i < (int) (cb_needed / sizeof (HMODULE)); i++)
+ {
+ MODULEINFO mi;
+#ifdef __USEWIDE
+ wchar_t dll_name[MAX_PATH];
+ char dll_name_mb[MAX_PATH];
+#else
+ char dll_name[MAX_PATH];
+#endif
+ const char *name;
+ if (GetModuleInformation (current_process_handle, hmodules[i],
+ &mi, sizeof (mi)) == 0)
+ continue;
+
+ if (GetModuleFileNameEx (current_process_handle, hmodules[i],
+ dll_name, sizeof (dll_name)) == 0)
+ continue;
+#ifdef __USEWIDE
+ wcstombs (dll_name_mb, dll_name, MAX_PATH);
+ name = dll_name_mb;
+#else
+ name = dll_name;
+#endif
+ /* Convert the DLL path of 32bit processes returned by
+ GetModuleFileNameEx from the 64bit system directory to the
+ 32bit syswow64 directory if necessary. */
+ std::string syswow_dll_path;
+ if (convert_syswow_dir
+ && strncasecmp (name, system_dir, system_dir_len) == 0
+ && strchr (name + system_dir_len, '\\') == nullptr)
+ {
+ syswow_dll_path = syswow_dir;
+ syswow_dll_path += name + system_dir_len;
+ name = syswow_dll_path.c_str();
+ }
+
+ /* Record the DLL if either LOAD_ADDR is NULL or the address
+ at which the DLL was loaded is equal to LOAD_ADDR. */
+ if (!(load_addr != nullptr && mi.lpBaseOfDll != load_addr))
+ {
+ handle_load_dll (name, mi.lpBaseOfDll);
+ if (load_addr != nullptr)
+ return;
+ }
+ }
+}
+
+/* See nat/windows-nat.h. */
+
+void
+dll_loaded_event ()
+{
+ gdb_assert (current_event.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT);
+
+ LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
+ const char *dll_name;
+
+ /* Try getting the DLL name via the lpImageName field of the event.
+ Note that Microsoft documents this fields as strictly optional,
+ in the sense that it might be NULL. And the first DLL event in
+ particular is explicitly documented as "likely not pass[ed]"
+ (source: MSDN LOAD_DLL_DEBUG_INFO structure). */
+ dll_name = get_image_name (current_process_handle,
+ event->lpImageName, event->fUnicode);
+ /* If the DLL name could not be gleaned via lpImageName, try harder
+ by enumerating all the DLLs loaded into the inferior, looking for
+ one that is loaded at base address = lpBaseOfDll. */
+ if (dll_name != nullptr)
+ handle_load_dll (dll_name, event->lpBaseOfDll);
+ else if (event->lpBaseOfDll != nullptr)
+ windows_add_dll (event->lpBaseOfDll);
+}
+
+/* See nat/windows-nat.h. */
+
+void
+windows_add_all_dlls ()
+{
+ windows_add_dll (nullptr);
+}
+
/* See nat/windows-nat.h. */
bool
if (desired_stop_thread_id == -1
|| desired_stop_thread_id == item.thread_id)
{
- DEBUG_EVENTS (("windows_continue - pending stop anticipated, "
- "desired=0x%x, item=0x%x\n",
- desired_stop_thread_id, item.thread_id));
+ DEBUG_EVENTS ("pending stop anticipated, desired=0x%x, item=0x%x",
+ desired_stop_thread_id, item.thread_id);
return true;
}
}
/* See nat/windows-nat.h. */
+gdb::optional<pending_stop>
+fetch_pending_stop (bool debug_events)
+{
+ gdb::optional<pending_stop> result;
+ for (auto iter = pending_stops.begin ();
+ iter != pending_stops.end ();
+ ++iter)
+ {
+ if (desired_stop_thread_id == -1
+ || desired_stop_thread_id == iter->thread_id)
+ {
+ result = *iter;
+ current_event = iter->event;
+
+ DEBUG_EVENTS ("pending stop found in 0x%x (desired=0x%x)",
+ iter->thread_id, desired_stop_thread_id);
+
+ pending_stops.erase (iter);
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* See nat/windows-nat.h. */
+
BOOL
continue_last_debug_event (DWORD continue_status, bool debug_events)
{
- DEBUG_EVENTS (("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s);\n",
- (unsigned) last_wait_event.dwProcessId,
- (unsigned) last_wait_event.dwThreadId,
- continue_status == DBG_CONTINUE ?
- "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED"));
+ DEBUG_EVENTS ("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s)",
+ (unsigned) last_wait_event.dwProcessId,
+ (unsigned) last_wait_event.dwThreadId,
+ continue_status == DBG_CONTINUE ?
+ "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED");
return ContinueDebugEvent (last_wait_event.dwProcessId,
last_wait_event.dwThreadId,
continue_status);
}
+/* See nat/windows-nat.h. */
+
+BOOL
+wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout)
+{
+ BOOL result = WaitForDebugEvent (event, timeout);
+ if (result)
+ last_wait_event = *event;
+ return result;
+}
+
+/* Define dummy functions which always return error for the rare cases where
+ these functions could not be found. */
+template<typename... T>
+BOOL WINAPI
+bad (T... args)
+{
+ return FALSE;
+}
+
+template<typename... T>
+DWORD WINAPI
+bad (T... args)
+{
+ return 0;
+}
+
+static BOOL WINAPI
+bad_GetCurrentConsoleFont (HANDLE w, BOOL bMaxWindow, CONSOLE_FONT_INFO *f)
+{
+ f->nFont = 0;
+ return 1;
+}
+
+static COORD WINAPI
+bad_GetConsoleFontSize (HANDLE w, DWORD nFont)
+{
+ COORD size;
+ size.X = 8;
+ size.Y = 12;
+ return size;
+}
+
+/* See windows-nat.h. */
+
+bool
+initialize_loadable ()
+{
+ bool result = true;
+ HMODULE hm = NULL;
+
+#define GPA(m, func) \
+ func = (func ## _ftype *) GetProcAddress (m, #func)
+
+ hm = LoadLibrary (TEXT ("kernel32.dll"));
+ if (hm)
+ {
+ GPA (hm, DebugActiveProcessStop);
+ GPA (hm, DebugBreakProcess);
+ GPA (hm, DebugSetProcessKillOnExit);
+ GPA (hm, GetConsoleFontSize);
+ GPA (hm, DebugActiveProcessStop);
+ GPA (hm, GetCurrentConsoleFont);
+#ifdef __x86_64__
+ GPA (hm, Wow64SuspendThread);
+ GPA (hm, Wow64GetThreadContext);
+ GPA (hm, Wow64SetThreadContext);
+ GPA (hm, Wow64GetThreadSelectorEntry);
+#endif
+ GPA (hm, GenerateConsoleCtrlEvent);
+ }
+
+ /* Set variables to dummy versions of these processes if the function
+ wasn't found in kernel32.dll. */
+ if (!DebugBreakProcess)
+ DebugBreakProcess = bad;
+ if (!DebugActiveProcessStop || !DebugSetProcessKillOnExit)
+ {
+ DebugActiveProcessStop = bad;
+ DebugSetProcessKillOnExit = bad;
+ }
+ if (!GetConsoleFontSize)
+ GetConsoleFontSize = bad_GetConsoleFontSize;
+ if (!GetCurrentConsoleFont)
+ GetCurrentConsoleFont = bad_GetCurrentConsoleFont;
+
+ /* Load optional functions used for retrieving filename information
+ associated with the currently debugged process or its dlls. */
+ hm = LoadLibrary (TEXT ("psapi.dll"));
+ if (hm)
+ {
+ GPA (hm, EnumProcessModules);
+#ifdef __x86_64__
+ GPA (hm, EnumProcessModulesEx);
+#endif
+ GPA (hm, GetModuleInformation);
+ GPA (hm, GetModuleFileNameExA);
+ GPA (hm, GetModuleFileNameExW);
+ }
+
+ if (!EnumProcessModules || !GetModuleInformation
+ || !GetModuleFileNameExA || !GetModuleFileNameExW)
+ {
+ /* Set variables to dummy versions of these processes if the function
+ wasn't found in psapi.dll. */
+ EnumProcessModules = bad;
+ GetModuleInformation = bad;
+ GetModuleFileNameExA = bad;
+ GetModuleFileNameExW = bad;
+
+ result = false;
+ }
+
+ hm = LoadLibrary (TEXT ("advapi32.dll"));
+ if (hm)
+ {
+ GPA (hm, OpenProcessToken);
+ GPA (hm, LookupPrivilegeValueA);
+ GPA (hm, AdjustTokenPrivileges);
+ /* Only need to set one of these since if OpenProcessToken fails nothing
+ else is needed. */
+ if (!OpenProcessToken || !LookupPrivilegeValueA
+ || !AdjustTokenPrivileges)
+ OpenProcessToken = bad;
+ }
+
+#undef GPA
+
+ return result;
+}
}