/* when fork is done, various cleanup is needed in the child process. In particular, child must have its own connection to avoid stealing data from its parent */ static void gdbserver_cleanup_in_child_after_fork(ThreadId me) { dlog(1, "thread %d gdbserver_cleanup_in_child_after_fork pid %d\n", me, VG_(getpid) ()); /* finish connection inheritated from parent */ remote_finish(reset_after_fork); /* ensure next call to gdbserver will be considered as a brand new call that will initialize a fresh gdbserver. */ if (gdbserver_called) { gdbserver_called = 0; vg_assert (gs_addresses != NULL); vg_assert (gs_watches != NULL); clear_gdbserved_addresses(/* clear only jumps */ False); VG_(HT_destruct) (gs_addresses, VG_(free)); gs_addresses = NULL; clear_watched_addresses(); } else { vg_assert (gs_addresses == NULL); vg_assert (gs_watches == NULL); } if (VG_(clo_trace_children)) { VG_(gdbserver_prerun_action) (me); } }
Bool VG_(gdbserver_activity) (ThreadId tid) { Bool ret; busy++; if (!gdbserver_called) call_gdbserver (tid, init_reason); switch (remote_desc_activity("VG_(gdbserver_activity)")) { case 0: ret = False; break; case 1: ret = True; break; case 2: remote_finish(reset_after_error); call_gdbserver (tid, init_reason); ret = False; break; default: vg_assert (0); } busy--; return ret; }
void server_main (void) { static char status; static int zignal; char ch; int i = 0; unsigned int len; CORE_ADDR mem_addr; zignal = valgrind_wait (&status); if (VG_MINIMAL_SETJMP(toplevel)) { dlog(0, "error caused VG_MINIMAL_LONGJMP to server_main\n"); } while (1) { unsigned char sig; int packet_len; int new_packet_len = -1; if (resume_reply_packet_needed) { /* Send the resume reply to reply to last GDB resume request. */ resume_reply_packet_needed = False; prepare_resume_reply (own_buf, status, zignal); putpkt (own_buf); } /* If we our status is terminal (exit or fatal signal) get out as quickly as we can. We won't be able to handle any request anymore. */ if (status == 'W' || status == 'X') { return; } packet_len = getpkt (own_buf); if (packet_len <= 0) break; i = 0; ch = own_buf[i++]; switch (ch) { case 'Q': handle_set (own_buf, &new_packet_len); break; case 'q': handle_query (own_buf, &new_packet_len); break; case 'd': /* set/unset debugging is done through valgrind debug level. */ own_buf[0] = '\0'; break; case 'D': reset_valgrind_sink("gdb detaching from process"); /* When detaching or kill the process, gdb expects to get an packet OK back. Any other output will make gdb believes detach did not work. */ write_ok (own_buf); putpkt (own_buf); remote_finish (reset_after_error); remote_open (VG_(clo_vgdb_prefix)); myresume (0, 0); resume_reply_packet_needed = False; return; case '!': /* We can not use the extended protocol with valgrind, because we can not restart the running program. So return unrecognized. */ own_buf[0] = '\0'; break; case '?': prepare_resume_reply (own_buf, status, zignal); break; case 'H': if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') { unsigned long gdb_id, thread_id; gdb_id = strtoul (&own_buf[2], NULL, 16); thread_id = gdb_id_to_thread_id (gdb_id); if (thread_id == 0) { write_enn (own_buf); break; } if (own_buf[1] == 'g') { general_thread = thread_id; set_desired_inferior (1); } else if (own_buf[1] == 'c') { cont_thread = thread_id; } else if (own_buf[1] == 's') { step_thread = thread_id; } write_ok (own_buf); } else { /* Silently ignore it so that gdb can extend the protocol without compatibility headaches. */ own_buf[0] = '\0'; } break; case 'g': set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': set_desired_inferior (1); registers_from_string (&own_buf[1]); write_ok (own_buf); break; case 'P': { int regno; char *regbytes; Bool mod; ThreadState *tst; regno = strtol(&own_buf[1], NULL, 16); regbytes = strchr(&own_buf[0], '=') + 1; set_desired_inferior (1); tst = (ThreadState *) inferior_target_data (current_inferior); /* Only accept changing registers in "runnable state3. In fact, it would be ok to change most of the registers except a few "sensitive" registers such as the PC, SP, BP. We assume we do not need to very specific here, and that we can just refuse all of these. */ if (tst->status == VgTs_Runnable || tst->status == VgTs_Yielding) { supply_register_from_string (regno, regbytes, &mod); write_ok (own_buf); } else { /* at least from gdb 6.6 onwards, an E. error reply is shown to the user. So, we do an error msg which both is accepted by gdb as an error msg and is readable by the user. */ VG_(sprintf) (own_buf, "E.\n" "ERROR changing register %s regno %d\n" "gdb commands changing registers (pc, sp, ...) (e.g. 'jump',\n" "set pc, calling from gdb a function in the debugged process, ...)\n" "can only be accepted if the thread is VgTs_Runnable or VgTs_Yielding state\n" "Thread status is %s\n", find_register_by_number (regno)->name, regno, VG_(name_of_ThreadStatus)(tst->status)); if (VG_(clo_verbosity) > 1) VG_(umsg) ("%s\n", own_buf); } break; } case 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); if (valgrind_read_memory (mem_addr, mem_buf, len) == 0) convert_int_to_ascii (mem_buf, own_buf, len); else write_enn (own_buf); break; case 'M': decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (valgrind_write_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); break; case 'X': if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 || valgrind_write_memory (mem_addr, mem_buf, len) != 0) write_enn (own_buf); else write_ok (own_buf); break; case 'C': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) zignal = target_signal_to_host (sig); else zignal = 0; set_desired_inferior (0); myresume (0, zignal); return; // return control to valgrind case 'S': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) zignal = target_signal_to_host (sig); else zignal = 0; set_desired_inferior (0); myresume (1, zignal); return; // return control to valgrind case 'c': set_desired_inferior (0); myresume (0, 0); return; // return control to valgrind case 's': set_desired_inferior (0); myresume (1, 0); return; // return control to valgrind case 'Z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int zlen = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (type < '0' || type > '4') { /* Watchpoint command type unrecognized. */ own_buf[0] = '\0'; } else { int res; res = valgrind_insert_watchpoint (type, addr, zlen); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int zlen = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (type < '0' || type > '4') { /* Watchpoint command type unrecognized. */ own_buf[0] = '\0'; } else { int res; res = valgrind_remove_watchpoint (type, addr, zlen); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'k': kill_request("Gdb request to kill this process\n"); break; case 'T': { unsigned long gdb_id, thread_id; gdb_id = strtoul (&own_buf[1], NULL, 16); thread_id = gdb_id_to_thread_id (gdb_id); if (thread_id == 0) { write_enn (own_buf); break; } if (valgrind_thread_alive (thread_id)) write_ok (own_buf); else write_enn (own_buf); break; } case 'R': /* Restarting the inferior is only supported in the extended protocol. => It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this request. */ own_buf[0] = '\0'; break; case 'v': /* Extended (long) request. */ handle_v_requests (own_buf, &status, &zignal); break; default: /* It is a request we don't understand. Respond with an empty packet so that gdb knows that we don't support this request. */ own_buf[0] = '\0'; break; } if (new_packet_len != -1) putpkt_binary (own_buf, new_packet_len); else putpkt (own_buf); if (status == 'W') VG_(umsg) ("\nChild exited with status %d\n", zignal); if (status == 'X') VG_(umsg) ("\nChild terminated with signal = 0x%x (%s)\n", target_signal_to_host (zignal), target_signal_to_name (zignal)); if (status == 'W' || status == 'X') { VG_(umsg) ("Process exiting\n"); VG_(exit) (0); } } /* We come here when getpkt fails => close the connection, and re-open. Then return control to valgrind. We return the control to valgrind as we assume that the connection was closed due to vgdb having finished to execute a command. */ if (VG_(clo_verbosity) > 1) VG_(umsg) ("Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); remote_finish (reset_after_error); remote_open (VG_(clo_vgdb_prefix)); myresume (0, 0); resume_reply_packet_needed = False; return; }