20000-05-18 H.J. Lu (hjl@gnu.org)
[binutils-gdb.git] / gdb / windows-nat.c
index 2be6fe8c90425d2037d719540e9c0f20c49a5237..869876ea40f4b0ee82459cf67912864b6295a2aa 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-vector operations for controlling win32 child processes, for GDB.
-   Copyright 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
-   Contributed by Cygnus Support.
+   Copyright 1995, 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Cygnus Solutions, A Red Hat Company.
 
    This file is part of GDB.
 
@@ -28,7 +28,6 @@
 #include "frame.h"             /* required by inferior.h */
 #include "inferior.h"
 #include "target.h"
-#include "gdb_wait.h"
 #include "gdbcore.h"
 #include "command.h"
 #include <signal.h>
@@ -80,6 +79,9 @@ static void child_stop PARAMS ((void));
 static int win32_child_thread_alive PARAMS ((int));
 void child_kill_inferior PARAMS ((void));
 
+static int last_sig = 0;       /* Set if a signal was received from the
+                                  debugged process */
+
 /* Thread information structure used to track information that is
    not available in gdb's thread structure. */
 typedef struct thread_info_struct
@@ -95,9 +97,6 @@ typedef struct thread_info_struct
 
 static thread_info thread_head = {NULL};
 
-/* The saved state for a continue after breaking back to gdb. */
-static DWORD continue_status;
-
 /* The process and thread handles for the above context. */
 
 static DEBUG_EVENT current_event;      /* The current debug event from
@@ -105,6 +104,8 @@ static DEBUG_EVENT current_event;   /* The current debug event from
 static HANDLE current_process_handle;  /* Currently executing process */
 static thread_info *current_thread;    /* Info on currently selected thread */
 static DWORD main_thread_id;   /* Thread ID of the main thread */
+static int ignore_first_first_chance = 0; /* True if we should ignore the
+                                            first first chance exception that we get. */
 
 /* Counts of things. */
 static int exception_count = 0;
@@ -202,7 +203,7 @@ thread_rec (DWORD id, int get_context)
       {
        if (!th->suspend_count && get_context)
          {
-           if (get_context > 0)
+           if (get_context > 0 && id != current_event.dwThreadId)
              th->suspend_count = SuspendThread (th->h) + 1;
            else if (get_context < 0)
              th->suspend_count = -1;
@@ -419,6 +420,55 @@ failed:
   return 0;
 }
 
+struct safe_symbol_file_add_args
+{
+  char *name;
+  int from_tty;
+  struct section_addr_info *addrs;
+  int mainline;
+  int flags;
+  struct objfile *ret;
+};
+
+static int
+safe_symbol_file_add_stub (void *argv)
+{
+#define p ((struct safe_symbol_file_add_args *)argv)
+  p->ret = symbol_file_add (p->name, p->from_tty, p->addrs, p->mainline, p->flags);
+  return !!p->ret;
+#undef p
+}
+
+static void
+safe_symbol_file_add_cleanup (void *gdb_stderrv)
+{
+  ui_file_delete (gdb_stderr);
+  gdb_stderr = (struct ui_file *)gdb_stderrv;
+}
+
+static struct objfile *
+safe_symbol_file_add (char *name, int from_tty,
+                     struct section_addr_info *addrs,
+                     int mainline, int flags)
+
+{
+  struct safe_symbol_file_add_args p;
+  struct cleanup *cleanup;
+
+  cleanup = make_cleanup (safe_symbol_file_add_cleanup, gdb_stderr);
+
+  gdb_stderr = ui_file_new ();
+  p.name = name;
+  p.from_tty = from_tty;
+  p.addrs = addrs;
+  p.mainline = mainline;
+  p.flags = flags;
+  catch_errors (safe_symbol_file_add_stub, &p, "", RETURN_MASK_ERROR);
+
+  do_cleanups (cleanup);
+  return p.ret;
+}
+
 /* Wait for child to do something.  Return pid of child, or -1 in case
    of error; store status through argument pointer OURSTATUS.  */
 
@@ -506,14 +556,12 @@ handle_load_dll (PTR dummy)
 
   /* The symbols in a dll are offset by 0x1000, which is the
      the offset from 0 of the first byte in an image - because
-     of the file header and the section alignment.
-
-     FIXME: Is this the real reason that we need the 0x1000 ? */
+     of the file header and the section alignment. */
 
-  printf_unfiltered ("%x:%s", event->lpBaseOfDll, dll_name);
-  section_addrs.text_addr = (int) event->lpBaseOfDll + 0x1000;
-  symbol_file_add (dll_name, 0, &section_addrs, 0, OBJF_SHARED);
-  printf_unfiltered ("\n");
+  section_addrs.other[0].name = ".text";
+  section_addrs.other[0].addr = (int) event->lpBaseOfDll + 0x1000;
+  safe_symbol_file_add (dll_name, 0, &section_addrs, 0, OBJF_SHARED);
+  printf_unfiltered ("%x:%s\n", event->lpBaseOfDll, dll_name);
 
   return 1;
 }
@@ -557,19 +605,24 @@ handle_exception (struct target_waitstatus *ourstatus)
   int i;
   int done = 0;
   thread_info *th;
+  int fc = ignore_first_first_chance;
 
   ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
+  ignore_first_first_chance = 0;
+
   /* Record the context of the current thread */
   th = thread_rec (current_event.dwThreadId, -1);
 
+  last_sig = 0;
+
   switch (current_event.u.Exception.ExceptionRecord.ExceptionCode)
     {
     case EXCEPTION_ACCESS_VIOLATION:
       DEBUG_EXCEPT (("gdb: Target exception ACCESS_VIOLATION at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_SEGV;
-      continue_status = DBG_EXCEPTION_NOT_HANDLED;
+      last_sig = SIGSEGV;
       break;
     case STATUS_FLOAT_UNDERFLOW:
     case STATUS_FLOAT_DIVIDE_BY_ZERO:
@@ -578,15 +631,19 @@ handle_exception (struct target_waitstatus *ourstatus)
       DEBUG_EXCEPT (("gdb: Target exception STACK_OVERFLOW at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_FPE;
-      continue_status = DBG_EXCEPTION_NOT_HANDLED;
       break;
     case STATUS_STACK_OVERFLOW:
       DEBUG_EXCEPT (("gdb: Target exception STACK_OVERFLOW at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_SEGV;
-      continue_status = DBG_EXCEPTION_NOT_HANDLED;
       break;
     case EXCEPTION_BREAKPOINT:
+      if (fc && current_event.u.Exception.dwFirstChance &&
+         ((DWORD) current_event.u.Exception.ExceptionRecord.ExceptionAddress & 0xc0000000))
+       {
+         last_sig = -1;
+         return 0;
+       }
       DEBUG_EXCEPT (("gdb: Target exception BREAKPOINT at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_TRAP;
@@ -595,27 +652,24 @@ handle_exception (struct target_waitstatus *ourstatus)
       DEBUG_EXCEPT (("gdb: Target exception CONTROL_C at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_INT;
-      continue_status = DBG_EXCEPTION_NOT_HANDLED;
+      last_sig = SIGINT;       /* FIXME - should check pass state */
       break;
     case EXCEPTION_SINGLE_STEP:
       DEBUG_EXCEPT (("gdb: Target exception SINGLE_STEP at 0x%08x\n",
               current_event.u.Exception.ExceptionRecord.ExceptionAddress));
       ourstatus->value.sig = TARGET_SIGNAL_TRAP;
       break;
+    case EXCEPTION_ILLEGAL_INSTRUCTION:
+      DEBUG_EXCEPT (("gdb: Target exception SINGLE_ILL at 0x%08x\n",
+              current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+      ourstatus->value.sig = TARGET_SIGNAL_ILL;
+      last_sig = SIGILL;
+      break;
     default:
-      /* This may be a structured exception handling exception.  In
-         that case, we want to let the program try to handle it, and
-         only break if we see the exception a second time.
-      if (current_event.u.Exception.dwFirstChance)
-
-       return 0;
-*/
-
       printf_unfiltered ("gdb: unknown target exception 0x%08x at 0x%08x\n",
                    current_event.u.Exception.ExceptionRecord.ExceptionCode,
                current_event.u.Exception.ExceptionRecord.ExceptionAddress);
       ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
-      continue_status = DBG_EXCEPTION_NOT_HANDLED;
       break;
     }
   exception_count++;
@@ -625,7 +679,7 @@ handle_exception (struct target_waitstatus *ourstatus)
 /* Resume all artificially suspended threads if we are continuing
    execution */
 static BOOL
-child_continue (int id)
+child_continue (DWORD continue_status, int id)
 {
   int i;
   thread_info *th;
@@ -649,16 +703,22 @@ child_continue (int id)
   return res;
 }
 
+/* Get the next event from the child.  Return 1 if the event requires
+   handling by WFI (or whatever).
+ */
 static int
 get_child_debug_event (int pid, struct target_waitstatus *ourstatus,
-                      DWORD *event_code, int *retval)
+                      DWORD target_event_code, int *retval)
 {
+  int breakout = 0;
   BOOL debug_event;
-  int breakout = 1;
+  DWORD continue_status, event_code;
+  thread_info *th = NULL;
+  static thread_info dummy_thread_info;
 
-  if (!(debug_event = WaitForDebugEvent (&current_event, 20)))
+  if (!(debug_event = WaitForDebugEvent (&current_event, 1000)))
     {
-      breakout = *retval = *event_code = 0;
+      *retval = 0;
       goto out;
     }
 
@@ -666,91 +726,95 @@ get_child_debug_event (int pid, struct target_waitstatus *ourstatus,
   continue_status = DBG_CONTINUE;
   *retval = 0;
 
-  switch (*event_code = current_event.dwDebugEventCode)
+  event_code = current_event.dwDebugEventCode;
+  breakout = event_code == target_event_code;
+
+  switch (event_code)
     {
     case CREATE_THREAD_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%x code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "CREATE_THREAD_DEBUG_EVENT"));
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "CREATE_THREAD_DEBUG_EVENT"));
       /* Record the existence of this thread */
-      child_add_thread (current_event.dwThreadId,
-                       current_event.u.CreateThread.hThread);
+      th = child_add_thread (current_event.dwThreadId,
+                            current_event.u.CreateThread.hThread);
       if (info_verbose)
        printf_unfiltered ("[New %s]\n",
-                     target_pid_to_str (current_event.dwThreadId));
+                          target_pid_to_str (current_event.dwThreadId));
       break;
 
     case EXIT_THREAD_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "EXIT_THREAD_DEBUG_EVENT"));
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "EXIT_THREAD_DEBUG_EVENT"));
       child_delete_thread (current_event.dwThreadId);
+      th = &dummy_thread_info;
       break;
 
     case CREATE_PROCESS_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "CREATE_PROCESS_DEBUG_EVENT"));
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "CREATE_PROCESS_DEBUG_EVENT"));
       current_process_handle = current_event.u.CreateProcessInfo.hProcess;
 
       main_thread_id = inferior_pid = current_event.dwThreadId;
       /* Add the main thread */
-      current_thread = child_add_thread (inferior_pid,
-                        current_event.u.CreateProcessInfo.hThread);
+      th = child_add_thread (inferior_pid,
+                            current_event.u.CreateProcessInfo.hThread);
       break;
 
     case EXIT_PROCESS_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "EXIT_PROCESS_DEBUG_EVENT"));
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "EXIT_PROCESS_DEBUG_EVENT"));
       ourstatus->kind = TARGET_WAITKIND_EXITED;
       ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
       CloseHandle (current_process_handle);
       *retval = current_event.dwProcessId;
-      goto out;
+      breakout = 1;
+      break;
 
     case LOAD_DLL_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "LOAD_DLL_DEBUG_EVENT"));
-      catch_errors (handle_load_dll, NULL, "", RETURN_MASK_ALL);
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "LOAD_DLL_DEBUG_EVENT"));
+      catch_errors (handle_load_dll, NULL, (char *) "", RETURN_MASK_ALL);
       registers_changed ();    /* mark all regs invalid */
       break;
 
     case UNLOAD_DLL_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "UNLOAD_DLL_DEBUG_EVENT"));
-      break;           /* FIXME: don't know what to do here */
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "UNLOAD_DLL_DEBUG_EVENT"));
+      break;                   /* FIXME: don't know what to do here */
 
     case EXCEPTION_DEBUG_EVENT:
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "EXCEPTION_DEBUG_EVENT"));
-      if (handle_exception (ourstatus))        /* sets continue_status */
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "EXCEPTION_DEBUG_EVENT"));
+      if (handle_exception (ourstatus))
+       *retval = current_event.dwThreadId;
+      else
        {
-         *retval = current_event.dwThreadId;
-         goto out;
+         if (last_sig >= 0)
+           continue_status = DBG_EXCEPTION_NOT_HANDLED;
+         breakout = 0;
        }
       break;
 
-    case OUTPUT_DEBUG_STRING_EVENT:
+    case OUTPUT_DEBUG_STRING_EVENT:    /* message from the kernel */
       DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
-                   (unsigned) current_event.dwProcessId,
-                   (unsigned) current_event.dwThreadId,
-                   "OUTPUT_DEBUG_STRING_EVENT"));
-      if (handle_output_debug_string (ourstatus))
-       {
-         *retval = main_thread_id;
-         goto out;
-       }
+                    (unsigned) current_event.dwProcessId,
+                    (unsigned) current_event.dwThreadId,
+                    "OUTPUT_DEBUG_STRING_EVENT"));
+      handle_output_debug_string ( ourstatus);
       break;
     default:
       printf_unfiltered ("gdb: kernel event for pid=%d tid=%d\n",
@@ -761,15 +825,15 @@ get_child_debug_event (int pid, struct target_waitstatus *ourstatus,
       break;
     }
 
-  breakout = 0;
-  CHECK (child_continue (-1));
-  continue_status = 0;
+  if (breakout)
+    current_thread = th ?: thread_rec (current_event.dwThreadId, TRUE);
+  else
+    CHECK (child_continue (continue_status, -1));
 
 out:
   return breakout;
 }
 
-
 /* Wait for interesting events to occur in the target process. */
 static int
 child_wait (int pid, struct target_waitstatus *ourstatus)
@@ -784,22 +848,18 @@ child_wait (int pid, struct target_waitstatus *ourstatus)
      isn't necessarily what you think it is. */
 
   while (1)
-    {
-      if (continue_status != 0)
-       CHECK (child_continue (-1));
-      if (get_child_debug_event (pid, ourstatus, &event_code, &retval))
-       return retval;
-      else
-       {
-         int detach = 0;
+    if (get_child_debug_event (pid, ourstatus, EXCEPTION_DEBUG_EVENT, &retval))
+      return retval;
+    else
+      {
+       int detach = 0;
 
-         if (ui_loop_hook != NULL)
-           detach = ui_loop_hook (0);
+       if (ui_loop_hook != NULL)
+         detach = ui_loop_hook (0);
 
-         if (detach)
-           child_kill_inferior ();
-       }
-    }
+       if (detach)
+         child_kill_inferior ();
+      }
 }
 
 /* Attach to process PID, then initialize for debugging it.  */
@@ -1027,22 +1087,21 @@ child_create_inferior (exec_file, allargs, env)
   target_terminal_init ();
   target_terminal_inferior ();
 
-  /* Run until process and threads are loaded */
-  do
-    get_child_debug_event (inferior_pid, &dummy, &event_code, &ret);
-  while (event_code != EXCEPTION_DEBUG_EVENT);
+  ignore_first_first_chance = 1;
 
-  SymSetOptions (SYMOPT_DEFERRED_LOADS);
-  SymInitialize (current_process_handle, NULL, TRUE);
+  /* Run until process and threads are loaded */
+  while (!get_child_debug_event (inferior_pid, &dummy,
+                                CREATE_PROCESS_DEBUG_EVENT, &ret))
+    continue;
 
+  /* child_continue (DBG_CONTINUE, -1);*/
   proceed ((CORE_ADDR) - 1, TARGET_SIGNAL_0, 0);
 }
 
 static void
 child_mourn_inferior ()
 {
-  continue_status = DBG_CONTINUE;
-  (void) child_continue (-1);
+  (void) child_continue (DBG_CONTINUE, -1);
   unpush_target (&child_ops);
   generic_mourn_inferior ();
 }
@@ -1088,8 +1147,7 @@ child_kill_inferior (void)
 
   for (;;)
     {
-      continue_status = DBG_CONTINUE;
-      if (!child_continue (-1))
+      if (!child_continue (DBG_CONTINUE, -1))
        break;
       if (!WaitForDebugEvent (&current_event, INFINITE))
        break;
@@ -1107,8 +1165,11 @@ child_kill_inferior (void)
 void
 child_resume (int pid, int step, enum target_signal sig)
 {
-  int i;
   thread_info *th;
+  DWORD continue_status = last_sig > 0 && last_sig < NSIG ?
+                         DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE;
+
+  last_sig = 0;
 
   DEBUG_EXEC (("gdb: child_resume (pid=%d, step=%d, sig=%d);\n",
               pid, step, sig));
@@ -1133,7 +1194,7 @@ child_resume (int pid, int step, enum target_signal sig)
   /* Allow continuing with the same signal that interrupted us.
      Otherwise complain. */
 
-  child_continue (pid);
+  child_continue (continue_status, pid);
 }
 
 static void
@@ -1205,7 +1266,6 @@ init_child_ops (void)
 void
 _initialize_inftarg ()
 {
-  struct cmd_list_element *c;
   init_child_ops ();
 
   add_show_from_set
@@ -1274,60 +1334,3 @@ cygwin_pid_to_str (int pid)
     sprintf (buf, "thread %d.0x%x", current_event.dwProcessId, pid);
   return buf;
 }
-
-static LPVOID __stdcall
-sfta(HANDLE h, DWORD d)
-{
-  return NULL;
-}
-
-static DWORD __stdcall
-sgmb(HANDLE h, DWORD d)
-{
-#if 0
-  return 4;
-#else
-  return SymGetModuleBase (h, d) ?: 4;
-#endif
-}
-
-CORE_ADDR
-child_frame_chain(struct frame_info *f)
-{
-  STACKFRAME *sf = (STACKFRAME *) f->extra_info;
-  if (!StackWalk (IMAGE_FILE_MACHINE_I386, current_process_handle,
-                 current_thread->h, sf, NULL, NULL, SymFunctionTableAccess, sgmb, NULL) ||
-      !sf->AddrReturn.Offset)
-    return 0;
-  return sf->AddrFrame.Offset;
-}
-
-CORE_ADDR
-child_frame_saved_pc(struct frame_info *f)
-{
-  STACKFRAME *sf = (STACKFRAME *) f->extra_info;
-  return sf->AddrReturn.Offset;
-}
-
-void
-child_init_frame(int leaf, struct frame_info *f)
-{
-  STACKFRAME *sf;
-
-  if (f->next && f->next->extra_info)
-    f->extra_info = f->next->extra_info;
-  else if (f->prev && f->prev->extra_info)
-    f->extra_info = f->prev->extra_info;
-  else
-    {
-      sf = (STACKFRAME *) frame_obstack_alloc (sizeof (*sf));
-      f->extra_info = (struct frame_extra_info *) sf;
-      memset (sf, 0, sizeof(*sf));
-      sf->AddrPC.Offset = f->pc;
-      sf->AddrPC.Mode = AddrModeFlat;
-      sf->AddrStack.Offset = current_thread->context.Esp;
-      sf->AddrStack.Mode = AddrModeFlat;
-      sf->AddrFrame.Offset = f->frame;
-      sf->AddrFrame.Mode = AddrModeFlat;
-    }
-}