/* Wait for the inferior process to change state.
   STATUS will be filled in with a response code to send to GDB.
   Returns the signal which caused the process to stop. */
static ptid_t
win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
  struct regcache *regcache;

  while (1)
    {
      if (!get_child_debug_event (ourstatus))
	continue;

      switch (ourstatus->kind)
	{
	case TARGET_WAITKIND_EXITED:
	  OUTMSG2 (("Child exited with retcode = %x\n",
		    ourstatus->value.integer));
	  win32_clear_inferiors ();
	  return pid_to_ptid (current_event.dwProcessId);
	case TARGET_WAITKIND_STOPPED:
	case TARGET_WAITKIND_LOADED:
	  OUTMSG2 (("Child Stopped with signal = %d \n",
		    ourstatus->value.sig));

	  regcache = get_thread_regcache (current_inferior, 1);
	  child_fetch_inferior_registers (regcache, -1);

	  if (ourstatus->kind == TARGET_WAITKIND_LOADED
	      && !server_waiting)
	    {
	      /* When gdb connects, we want to be stopped at the
		 initial breakpoint, not in some dll load event.  */
	      child_continue (DBG_CONTINUE, -1);
	      break;
	    }

	  /* We don't expose _LOADED events to gdbserver core.  See
	     the `dlls_changed' global.  */
	  if (ourstatus->kind == TARGET_WAITKIND_LOADED)
	    ourstatus->kind = TARGET_WAITKIND_STOPPED;

	  return debug_event_ptid (&current_event);
	default:
	  OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
	  /* fall-through */
	case TARGET_WAITKIND_SPURIOUS:
	case TARGET_WAITKIND_EXECD:
	  /* do nothing, just continue */
	  child_continue (DBG_CONTINUE, -1);
	  break;
	}
    }
}
Exemple #2
0
/* Wait for the inferior process to change state.
   STATUS will be filled in with a response code to send to GDB.
   Returns the signal which caused the process to stop. */
static unsigned char
win32_wait (char *status)
{
  struct target_waitstatus our_status;

  *status = 'T';

  while (1)
    {
      if (!get_child_debug_event (&our_status))
	continue;

      switch (our_status.kind)
	{
	case TARGET_WAITKIND_EXITED:
	  OUTMSG2 (("Child exited with retcode = %x\n",
		    our_status.value.integer));

	  *status = 'W';
	  win32_clear_inferiors ();
	  return our_status.value.integer;
	case TARGET_WAITKIND_STOPPED:
 	case TARGET_WAITKIND_LOADED:
	  OUTMSG2 (("Child Stopped with signal = %d \n",
		    our_status.value.sig));

	  *status = 'T';

	  child_fetch_inferior_registers (-1);

	  if (our_status.kind == TARGET_WAITKIND_LOADED
	      && !server_waiting)
	    {
	      /* When gdb connects, we want to be stopped at the
		 initial breakpoint, not in some dll load event.  */
	      child_continue (DBG_CONTINUE, -1);
	      break;
	    }

	  return our_status.value.sig;
 	default:
	  OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind));
 	  /* fall-through */
 	case TARGET_WAITKIND_SPURIOUS:
 	case TARGET_WAITKIND_EXECD:
	  /* do nothing, just continue */
	  child_continue (DBG_CONTINUE, -1);
	  break;
	}
    }
}
/* Kill all inferiors.  */
static int
win32_kill (int pid)
{
  struct process_info *process;

  if (current_process_handle == NULL)
    return -1;

  TerminateProcess (current_process_handle, 0);
  for (;;)
    {
      if (!child_continue (DBG_CONTINUE, -1))
	break;
      if (!WaitForDebugEvent (&current_event, INFINITE))
	break;
      if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
	break;
      else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
	{
	  struct target_waitstatus our_status = { 0 };
	  handle_output_debug_string (&our_status);
	}
    }

  win32_clear_inferiors ();

  process = find_process_pid (pid);
  remove_process (process);
  return 0;
}
Exemple #4
0
/* Wait for the inferior process to change state.
   STATUS will be filled in with a response code to send to GDB.
   Returns the signal which caused the process to stop. */
static ptid_t
win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
  struct regcache *regcache;

  if (cached_status.kind != TARGET_WAITKIND_IGNORE)
    {
      /* The core always does a wait after creating the inferior, and
	 do_initial_child_stuff already ran the inferior to the
	 initial breakpoint (or an exit, if creating the process
	 fails).  Report it now.  */
      *ourstatus = cached_status;
      cached_status.kind = TARGET_WAITKIND_IGNORE;
      return debug_event_ptid (&current_event);
    }

  while (1)
    {
      if (!get_child_debug_event (ourstatus))
	continue;

      switch (ourstatus->kind)
	{
	case TARGET_WAITKIND_EXITED:
	  OUTMSG2 (("Child exited with retcode = %x\n",
		    ourstatus->value.integer));
	  win32_clear_inferiors ();
	  return pid_to_ptid (current_event.dwProcessId);
	case TARGET_WAITKIND_STOPPED:
	case TARGET_WAITKIND_LOADED:
	  OUTMSG2 (("Child Stopped with signal = %d \n",
		    ourstatus->value.sig));

	  regcache = get_thread_regcache (current_thread, 1);
	  child_fetch_inferior_registers (regcache, -1);
	  return debug_event_ptid (&current_event);
	default:
	  OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
	  /* fall-through */
	case TARGET_WAITKIND_SPURIOUS:
	case TARGET_WAITKIND_EXECD:
	  /* do nothing, just continue */
	  child_continue (DBG_CONTINUE, -1);
	  break;
	}
    }
}
Exemple #5
0
int main(int argc, char **argv) {
  wshd_t *w;
  int rv;

  /* Continue child execution in the context of the container */
  if (argc > 1 && strcmp(argv[1], "--continue") == 0) {
    return child_continue(argc, argv);
  }

  w = calloc(1, sizeof(*w));
  assert(w != NULL);

  rv = wshd__getopt(w, argc, argv);
  if (rv == -1) {
    exit(1);
  }

  if (strlen(w->run_path) == 0) {
    strcpy(w->run_path, "run");
  }

  if (strlen(w->lib_path) == 0) {
    strcpy(w->lib_path, "lib");
  }

  if (strlen(w->root_path) == 0) {
    strcpy(w->root_path, "root");
  }

  assert_directory(w->run_path);
  assert_directory(w->lib_path);
  assert_directory(w->root_path);

  parent_run(w);

  return 0;
}
/* Resume the inferior process.  RESUME_INFO describes how we want
   to resume.  */
static void
win32_resume (struct thread_resume *resume_info, size_t n)
{
  DWORD tid;
  enum target_signal sig;
  int step;
  win32_thread_info *th;
  DWORD continue_status = DBG_CONTINUE;
  ptid_t ptid;

  /* This handles the very limited set of resume packets that GDB can
     currently produce.  */

  if (n == 1 && ptid_equal (resume_info[0].thread, minus_one_ptid))
    tid = -1;
  else if (n > 1)
    tid = -1;
  else
    /* Yes, we're ignoring resume_info[0].thread.  It'd be tricky to make
       the Windows resume code do the right thing for thread switching.  */
    tid = current_event.dwThreadId;

  if (!ptid_equal (resume_info[0].thread, minus_one_ptid))
    {
      sig = resume_info[0].sig;
      step = resume_info[0].kind == resume_step;
    }
  else
    {
      sig = 0;
      step = 0;
    }

  if (sig != TARGET_SIGNAL_0)
    {
      if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
	{
	  OUTMSG (("Cannot continue with signal %d here.\n", sig));
	}
      else if (sig == last_sig)
	continue_status = DBG_EXCEPTION_NOT_HANDLED;
      else
	OUTMSG (("Can only continue with recieved signal %d.\n", last_sig));
    }

  last_sig = TARGET_SIGNAL_0;

  /* Get context for the currently selected thread.  */
  ptid = debug_event_ptid (&current_event);
  th = thread_rec (ptid, FALSE);
  if (th)
    {
      if (th->context.ContextFlags)
	{
	  /* Move register values from the inferior into the thread
	     context structure.  */
	  regcache_invalidate ();

	  if (step)
	    {
	      if (the_low_target.single_step != NULL)
		(*the_low_target.single_step) (th);
	      else
		error ("Single stepping is not supported "
		       "in this configuration.\n");
	    }

	  win32_set_thread_context (th);
	  th->context.ContextFlags = 0;
	}
    }

  /* Allow continuing with the same signal that interrupted us.
     Otherwise complain.  */

  child_continue (continue_status, tid);
}
static int
get_child_debug_event (struct target_waitstatus *ourstatus)
{
  ptid_t ptid;

  last_sig = TARGET_SIGNAL_0;
  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;

  /* Check if GDB sent us an interrupt request.  */
  check_remote_input_interrupt_request ();

  if (soft_interrupt_requested)
    {
      soft_interrupt_requested = 0;
      fake_breakpoint_event ();
      goto gotevent;
    }

#ifndef _WIN32_WCE
  attaching = 0;
#else
  if (attaching)
    {
      /* WinCE doesn't set an initial breakpoint automatically.  To
	 stop the inferior, we flush all currently pending debug
	 events -- the thread list and the dll list are always
	 reported immediatelly without delay, then, we suspend all
	 threads and pretend we saw a trap at the current PC of the
	 main thread.

	 Contrary to desktop Windows, Windows CE *does* report the dll
	 names on LOAD_DLL_DEBUG_EVENTs resulting from a
	 DebugActiveProcess call.  This limits the way we can detect
	 if all the dlls have already been reported.  If we get a real
	 debug event before leaving attaching, the worst that will
	 happen is the user will see a spurious breakpoint.  */

      current_event.dwDebugEventCode = 0;
      if (!WaitForDebugEvent (&current_event, 0))
	{
	  OUTMSG2(("no attach events left\n"));
	  fake_breakpoint_event ();
	  attaching = 0;
	}
      else
	OUTMSG2(("got attach event\n"));
    }
  else
#endif
    {
      /* Keep the wait time low enough for confortable remote
	 interruption, but high enough so gdbserver doesn't become a
	 bottleneck.  */
      if (!WaitForDebugEvent (&current_event, 250))
        {
	  DWORD e  = GetLastError();

	  if (e == ERROR_PIPE_NOT_CONNECTED)
	    {
	      /* This will happen if the loader fails to succesfully
		 load the application, e.g., if the main executable
		 tries to pull in a non-existing export from a
		 DLL.  */
	      ourstatus->kind = TARGET_WAITKIND_EXITED;
	      ourstatus->value.integer = 1;
	      return 1;
	    }

	  return 0;
        }
    }

 gotevent:

  switch (current_event.dwDebugEventCode)
    {
    case CREATE_THREAD_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT "
		"for pid=%d tid=%x)\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));

      /* Record the existence of this thread.  */
      child_add_thread (current_event.dwProcessId,
			current_event.dwThreadId,
			current_event.u.CreateThread.hThread,
			current_event.u.CreateThread.lpThreadLocalBase);
      break;

    case EXIT_THREAD_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      child_delete_thread (current_event.dwProcessId,
			   current_event.dwThreadId);

      current_inferior = (struct thread_info *) all_threads.head;
      return 1;

    case CREATE_PROCESS_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      CloseHandle (current_event.u.CreateProcessInfo.hFile);

      current_process_handle = current_event.u.CreateProcessInfo.hProcess;
      main_thread_id = current_event.dwThreadId;

      ourstatus->kind = TARGET_WAITKIND_EXECD;
      ourstatus->value.execd_pathname = "Main executable";

      /* Add the main thread.  */
      child_add_thread (current_event.dwProcessId,
			main_thread_id,
			current_event.u.CreateProcessInfo.hThread,
			current_event.u.CreateProcessInfo.lpThreadLocalBase);

      ourstatus->value.related_pid = debug_event_ptid (&current_event);
#ifdef _WIN32_WCE
      if (!attaching)
	{
	  /* Windows CE doesn't set the initial breakpoint
	     automatically like the desktop versions of Windows do.
	     We add it explicitly here.	 It will be removed as soon as
	     it is hit.	 */
	  set_breakpoint_at ((CORE_ADDR) (long) current_event.u
			     .CreateProcessInfo.lpStartAddress,
			     auto_delete_breakpoint);
	}
#endif
      break;

    case EXIT_PROCESS_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      ourstatus->kind = TARGET_WAITKIND_EXITED;
      ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
      child_continue (DBG_CONTINUE, -1);
      CloseHandle (current_process_handle);
      current_process_handle = NULL;
      break;

    case LOAD_DLL_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      CloseHandle (current_event.u.LoadDll.hFile);
      handle_load_dll ();

      ourstatus->kind = TARGET_WAITKIND_LOADED;
      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
      break;

    case UNLOAD_DLL_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      handle_unload_dll ();
      ourstatus->kind = TARGET_WAITKIND_LOADED;
      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
      break;

    case EXCEPTION_DEBUG_EVENT:
      OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      handle_exception (ourstatus);
      break;

    case OUTPUT_DEBUG_STRING_EVENT:
      /* A message from the kernel (or Cygwin).  */
      OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT "
		"for pid=%d tid=%x\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId));
      handle_output_debug_string (ourstatus);
      break;

    default:
      OUTMSG2 (("gdbserver: kernel event unknown "
		"for pid=%d tid=%x code=%ld\n",
		(unsigned) current_event.dwProcessId,
		(unsigned) current_event.dwThreadId,
		current_event.dwDebugEventCode));
      break;
    }

  ptid = debug_event_ptid (&current_event);
  current_inferior =
    (struct thread_info *) find_inferior_id (&all_threads, ptid);
  return 1;
}
Exemple #8
0
int main(int argc, char *argv[]) {
  int              backlog          = 10;
  muxer_t         *muxers[2]        = {NULL, NULL};
  status_writer_t *sw               = NULL;
  child_t         *child            = NULL;
  int              child_status     = -1;
  int              ring_buffer_size = 65535;
  int              fds[3]           = {-1, -1, -1};
  int              ii               = 0, exit_status = 0, nwritten = 0;
  pthread_t        sw_thread, muxer_threads[2];
  char             socket_paths[3][PATH_MAX + 1];
  char             *socket_names[3] = { "stdout.sock", "stderr.sock", "status.sock" };
  barrier_t        *barrier = NULL;

  if (argc < 3) {
    fprintf(stderr, "Usage: %s <socket directory> <cmd>\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  /* Setup listeners on domain sockets */
  for (ii = 0; ii < 3; ++ii) {
    memset(socket_paths[ii], 0, sizeof(socket_paths[ii]));
    nwritten = snprintf(socket_paths[ii], sizeof(socket_paths[ii]),
                        "%s/%s", argv[1], socket_names[ii]);
    if (nwritten >= sizeof(socket_paths[ii])) {
      fprintf(stderr, "Socket path too long\n");
      exit_status = 1;
      goto cleanup;
    }

    fds[ii] = create_unix_domain_listener(socket_paths[ii], backlog);

    DLOG("created listener, path=%s fd=%d", socket_paths[ii], fds[ii]);

    if (-1 == fds[ii]) {
      perrorf("Failed creating socket at %s:", socket_paths[ii]);
      exit_status = 1;
      goto cleanup;
    }

    set_cloexec(fds[ii]);
  }

  child = child_create(argv + 2, argc - 2);

  printf("child_pid=%d\n", child->pid);
  fflush(stdout);

  /* Muxers for stdout/stderr */
  muxers[0] = muxer_alloc(fds[0], child->stdout[0], ring_buffer_size);
  muxers[1] = muxer_alloc(fds[1], child->stderr[0], ring_buffer_size);
  for (ii = 0; ii < 2; ++ii) {
    if (pthread_create(&muxer_threads[ii], NULL, run_muxer, muxers[ii])) {
      perrorf("Failed creating muxer thread:");
      exit_status = 1;
      goto cleanup;
    }

    DLOG("created muxer thread for socket=%s", socket_paths[ii]);
  }

  /* Status writer */
  barrier = barrier_alloc();
  sw = status_writer_alloc(fds[2], barrier);
  if (pthread_create(&sw_thread, NULL, run_status_writer, sw)) {
    perrorf("Failed creating muxer thread:");
    exit_status = 1;
    goto cleanup;
  }

  /* Wait for clients on stdout, stderr, and status */
  for (ii = 0; ii < 2; ++ii) {
    muxer_wait_for_client(muxers[ii]);
  }
  barrier_wait(barrier);

  child_continue(child);

  printf("child active\n");
  fflush(stdout);

  if (-1 == waitpid(child->pid, &child_status, 0)) {
    perrorf("Waitpid for child failed: ");
    exit_status = 1;
    goto cleanup;
  }

  DLOG("child exited, status = %d", WEXITSTATUS(child_status));

  /* Wait for status writer */
  status_writer_finish(sw, child_status);
  pthread_join(sw_thread, NULL);

  /* Wait for muxers */
  for (ii = 0; ii < 2; ++ii) {
    muxer_stop(muxers[ii]);
    pthread_join(muxer_threads[ii], NULL);
  }

  DLOG("all done, cleaning up and exiting");

cleanup:
  if (NULL != child) {
    child_free(child);
  }

  if (NULL != barrier) {
    barrier_free(barrier);
  }

  if (NULL != sw) {
    status_writer_free(sw);
  }

  for (ii = 0; ii < 2; ++ii) {
    if (NULL != muxers[ii]) {
      muxer_free(muxers[ii]);
    }
  }

  /* Close accept sockets and clean up paths */
  for (ii = 0; ii < 3; ++ii) {
    if (-1 != fds[ii]) {
      close(fds[ii]);
      unlink(socket_paths[ii]);
    }
  }

  return exit_status;
}