/* 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 (¤t_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; } } }
/* 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 (¤t_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; }
/* 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 (¤t_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 (¤t_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; } } }
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 (¤t_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 (¤t_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 (¤t_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 (¤t_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 (¤t_event); current_inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); return 1; }
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; }