Share handle_exception
[deliverable/binutils-gdb.git] / gdb / nat / windows-nat.c
index 767ed8c192fa84534ebd22286e4772757291a295..6bbf41c7b131bce92eaeffa1d3a01afef0d5669b 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal interfaces for the Windows code
-   Copyright (C) 1995-2019 Free Software Foundation, Inc.
+   Copyright (C) 1995-2020 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
 #include "gdbsupport/common-defs.h"
 #include "nat/windows-nat.h"
+#include "gdbsupport/common-debug.h"
+
+#define STATUS_WX86_BREAKPOINT 0x4000001F
+#define STATUS_WX86_SINGLE_STEP 0x4000001E
+
+namespace windows_nat
+{
+
+HANDLE current_process_handle;
+DWORD current_process_id;
+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;
+DWORD desired_stop_thread_id = -1;
+std::vector<pending_stop> pending_stops;
+EXCEPTION_RECORD siginfo_er;
+
+windows_thread_info::~windows_thread_info ()
+{
+  CloseHandle (h);
+}
 
 void
 windows_thread_info::suspend ()
@@ -60,3 +83,233 @@ windows_thread_info::resume ()
     }
   suspended = 0;
 }
+
+const char *
+get_image_name (HANDLE h, void *address, int unicode)
+{
+#ifdef __CYGWIN__
+  static char buf[MAX_PATH];
+#else
+  static char buf[(2 * MAX_PATH) + 1];
+#endif
+  DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
+  char *address_ptr;
+  int len = 0;
+  char b[2];
+  SIZE_T done;
+
+  /* Attempt to read the name of the dll that was detected.
+     This is documented to work only when actively debugging
+     a program.  It will not work for attached processes.  */
+  if (address == NULL)
+    return NULL;
+
+#ifdef _WIN32_WCE
+  /* Windows CE reports the address of the image name,
+     instead of an address of a pointer into the image name.  */
+  address_ptr = address;
+#else
+  /* See if we could read the address of a string, and that the
+     address isn't null.  */
+  if (!ReadProcessMemory (h, address,  &address_ptr,
+                         sizeof (address_ptr), &done)
+      || done != sizeof (address_ptr)
+      || !address_ptr)
+    return NULL;
+#endif
+
+  /* Find the length of the string.  */
+  while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
+        && (b[0] != 0 || b[size - 1] != 0) && done == size)
+    continue;
+
+  if (!unicode)
+    ReadProcessMemory (h, address_ptr, buf, len, &done);
+  else
+    {
+      WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR));
+      ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
+                        &done);
+#ifdef __CYGWIN__
+      wcstombs (buf, unicode_address, MAX_PATH);
+#else
+      WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, sizeof buf,
+                          0, 0);
+#endif
+    }
+
+  return buf;
+}
+
+/* The exception thrown by a program to tell the debugger the name of
+   a thread.  The exception record contains an ID of a thread and a
+   name to give it.  This exception has no documented name, but MSDN
+   dubs it "MS_VC_EXCEPTION" in one code example.  */
+#define MS_VC_EXCEPTION 0x406d1388
+
+handle_exception_result
+handle_exception (struct target_waitstatus *ourstatus, bool debug_exceptions)
+{
+#define DEBUG_EXCEPTION_SIMPLE(x)       if (debug_exceptions) \
+  debug_printf ("gdb: Target exception %s at %s\n", x, \
+    host_address_to_string (\
+      current_event.u.Exception.ExceptionRecord.ExceptionAddress))
+
+  EXCEPTION_RECORD *rec = &current_event.u.Exception.ExceptionRecord;
+  DWORD code = rec->ExceptionCode;
+  handle_exception_result result = HANDLE_EXCEPTION_HANDLED;
+
+  memcpy (&siginfo_er, rec, sizeof siginfo_er);
+
+  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+  /* Record the context of the current thread.  */
+  thread_rec (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0),
+             DONT_SUSPEND);
+
+  switch (code)
+    {
+    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
+      break;
+    case STATUS_STACK_OVERFLOW:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_STACK_OVERFLOW");
+      ourstatus->value.sig = GDB_SIGNAL_SEGV;
+      break;
+    case STATUS_FLOAT_DENORMAL_OPERAND:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DENORMAL_OPERAND");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+      DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_INEXACT_RESULT:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INEXACT_RESULT");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_INVALID_OPERATION:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INVALID_OPERATION");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_OVERFLOW:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_OVERFLOW");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_STACK_CHECK:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_STACK_CHECK");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_UNDERFLOW:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_UNDERFLOW");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_FLOAT_DIVIDE_BY_ZERO:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DIVIDE_BY_ZERO");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_INTEGER_DIVIDE_BY_ZERO:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_DIVIDE_BY_ZERO");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case STATUS_INTEGER_OVERFLOW:
+      DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_OVERFLOW");
+      ourstatus->value.sig = GDB_SIGNAL_FPE;
+      break;
+    case EXCEPTION_BREAKPOINT:
+#ifdef __x86_64__
+      if (ignore_first_breakpoint)
+       {
+         /* For WOW64 processes, there are always 2 breakpoint exceptions
+            on startup, first a BREAKPOINT for the 64bit ntdll.dll,
+            then a WX86_BREAKPOINT for the 32bit ntdll.dll.
+            Here we only care about the WX86_BREAKPOINT's.  */
+         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+         ignore_first_breakpoint = false;
+       }
+#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");
+      ourstatus->value.sig = GDB_SIGNAL_INT;
+      break;
+    case DBG_CONTROL_BREAK:
+      DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_BREAK");
+      ourstatus->value.sig = GDB_SIGNAL_INT;
+      break;
+    case EXCEPTION_SINGLE_STEP:
+    case STATUS_WX86_SINGLE_STEP:
+      DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_SINGLE_STEP");
+      ourstatus->value.sig = GDB_SIGNAL_TRAP;
+      break;
+    case EXCEPTION_ILLEGAL_INSTRUCTION:
+      DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ILLEGAL_INSTRUCTION");
+      ourstatus->value.sig = GDB_SIGNAL_ILL;
+      break;
+    case EXCEPTION_PRIV_INSTRUCTION:
+      DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_PRIV_INSTRUCTION");
+      ourstatus->value.sig = GDB_SIGNAL_ILL;
+      break;
+    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+      DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_NONCONTINUABLE_EXCEPTION");
+      ourstatus->value.sig = GDB_SIGNAL_ILL;
+      break;
+    case MS_VC_EXCEPTION:
+      DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION");
+      if (handle_ms_vc_exception (rec))
+       {
+         ourstatus->value.sig = GDB_SIGNAL_TRAP;
+         result = HANDLE_EXCEPTION_IGNORED;
+         break;
+       }
+       /* treat improperly formed exception as unknown */
+       /* FALLTHROUGH */
+    default:
+      /* Treat unhandled first chance exceptions specially.  */
+      if (current_event.u.Exception.dwFirstChance)
+       return HANDLE_EXCEPTION_UNHANDLED;
+      debug_printf ("gdb: unknown target exception 0x%08x at %s\n",
+       (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
+       host_address_to_string (
+         current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+      ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
+      break;
+    }
+
+  last_sig = ourstatus->value.sig;
+  return result;
+
+#undef DEBUG_EXCEPTION_SIMPLE
+}
+
+}
This page took 0.025862 seconds and 4 git commands to generate.