/* Resume all artificially suspended threads if we are continuing execution. */ static int continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) { struct thread_info *thread = (struct thread_info *) this_thread; int thread_id = * (int *) id_ptr; win32_thread_info *th = inferior_target_data (thread); if ((thread_id == -1 || thread_id == th->tid) && th->suspended) { if (th->context.ContextFlags) { win32_set_thread_context (th); th->context.ContextFlags = 0; } if (ResumeThread (th->h) == (DWORD) -1) { DWORD err = GetLastError (); OUTMSG (("warning: ResumeThread failed in continue_one_thread, " "(error %d): %s\n", (int) err, strwinerror (err))); } th->suspended = 0; } return 0; }
/* Resume all artificially suspended threads if we are continuing execution. */ static int continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) { struct thread_info *thread = (struct thread_info *) this_thread; int thread_id = * (int *) id_ptr; thread_info *th = inferior_target_data (thread); int i; if ((thread_id == -1 || thread_id == th->tid) && th->suspend_count) { for (i = 0; i < th->suspend_count; i++) (void) ResumeThread (th->h); th->suspend_count = 0; if (debug_registers_changed) { /* Only change the value of the debug registers. */ th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; th->context.Dr0 = dr[0]; th->context.Dr1 = dr[1]; th->context.Dr2 = dr[2]; th->context.Dr3 = dr[3]; /* th->context.Dr6 = dr[6]; FIXME: should we set dr6 also ?? */ th->context.Dr7 = dr[7]; SetThreadContext (th->h, &th->context); th->context.ContextFlags = 0; } } return 0; }
void set_desired_inferior (int use_general) { struct thread_info *found; if (use_general == 1) { found = (struct thread_info *) find_inferior_id (&all_threads, general_thread); } else { found = NULL; /* If we are continuing any (all) thread(s), use step_thread to decide which thread to step and/or send the specified signal to. */ if ((step_thread != 0 && step_thread != -1) && (cont_thread == 0 || cont_thread == -1)) found = (struct thread_info *) find_inferior_id (&all_threads, step_thread); if (found == NULL) found = (struct thread_info *) find_inferior_id (&all_threads, cont_thread); } if (found == NULL) current_inferior = (struct thread_info *) all_threads.head; else current_inferior = found; { ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); ThreadId tid = tst->tid; dlog(1, "set_desired_inferior use_general %d found %p tid %d lwpid %d\n", use_general, found, tid, tst->os_state.lwpid); } }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (ptid_t ptid, int get_context) { struct thread_info *thread; win32_thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (get_context && th->context.ContextFlags == 0) { if (!th->suspended) { if (SuspendThread (th->h) == (DWORD) -1) { DWORD err = GetLastError (); OUTMSG (("warning: SuspendThread failed in thread_rec, " "(error %d): %s\n", (int) err, strwinerror (err))); } else th->suspended = 1; } win32_get_thread_context (th); } return th; }
/* Fetch one register from valgrind VEX guest state. */ static void fetch_register (int regno) { int size; ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); ThreadId tid = tst->tid; if (regno >= the_low_target.num_regs) { dlog(0, "error fetch_register regno %d max %d\n", regno, the_low_target.num_regs); return; } size = register_size (regno); if (size > 0) { Bool mod; char buf [size]; VG_(memset) (buf, 0, size); // registers not fetched will be seen as 0. (*the_low_target.transfer_register) (tid, regno, buf, valgrind_to_gdbserver, size, &mod); // Note: the *mod received from transfer_register is not interesting. // We are interested to see if the register data in the register cache is modified. supply_register (regno, buf, &mod); if (mod && VG_(debugLog_getLevel)() > 1) { char bufimage [2*size + 1]; heximage (bufimage, buf, size); dlog(2, "fetched register %d size %d name %s value %s tid %d status %s\n", regno, size, the_low_target.reg_defs[regno].name, bufimage, tid, VG_(name_of_ThreadStatus) (tst->status)); } } }
static void thread_db_create_event (CORE_ADDR where) { td_event_msg_t msg; td_err_e err; struct inferior_linux_data *tdata; if (debug_threads) fprintf (stderr, "Thread creation event.\n"); tdata = inferior_target_data (current_inferior); /* FIXME: This assumes we don't get another event. In the LinuxThreads implementation, this is safe, because all events come from the manager thread (except for its own creation, of course). */ err = td_ta_event_getmsg (thread_agent, &msg); if (err != TD_OK) fprintf (stderr, "thread getmsg err: %s\n", thread_db_err_str (err)); /* msg.event == TD_EVENT_CREATE */ find_new_threads_callback (msg.th_p, NULL); }
static DWORD64 win32_get_current_dr (int dr) { win32_thread_info *th = inferior_target_data (current_thread); win32_require_context (th); #define RET_DR(DR) \ case DR: \ return th->context.Dr ## DR switch (dr) { RET_DR (0); RET_DR (1); RET_DR (2); RET_DR (3); RET_DR (6); RET_DR (7); } #undef RET_DR gdb_assert_not_reached ("unhandled dr"); }
int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) { Bool is_valid_client_memory; void *targetaddr = C2v (memaddr); dlog(2, "writing memory %p size %d\n", targetaddr, len); is_valid_client_memory = VG_(am_is_valid_for_client) ((Addr)targetaddr, len, VKI_PROT_WRITE); if (is_valid_client_memory || (hostvisibility && VG_(am_is_valid_for_valgrind) ((Addr) targetaddr, len, VKI_PROT_READ))) { if (len > 0) { VG_(memcpy) (targetaddr, myaddr, len); if (is_valid_client_memory && VG_(tdict).track_post_mem_write) { /* Inform the tool of the post memwrite. Note that we do the minimum necessary to avoid complains from e.g. memcheck. The idea is that the debugger is as least intrusive as possible. So, we do not inform of the pre mem write (and in any case, this would cause problems with memcheck that does not like our CorePart in pre_mem_write. */ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); ThreadId tid = tst->tid; VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid, (Addr) targetaddr, len ); } } return 0; } else { dlog(1, "error writing memory %p size %d\n", targetaddr, len); return -1; } }
/* Delete a thread from the list of threads. */ static void delete_thread_info (struct inferior_list_entry *thread) { win32_thread_info *th = inferior_target_data ((struct thread_info *) thread); remove_thread ((struct thread_info *) thread); CloseHandle (th->h); free (th); }
static void maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) { td_err_e err; struct thread_info *inferior; struct process_info *process; /* If we are attaching to our first thread, things are a little different. */ if (all_threads.head == all_threads.tail) { inferior = (struct thread_info *) all_threads.head; process = get_thread_process (inferior); if (process->thread_known == 0) { /* Switch to indexing the threads list by TID. */ change_inferior_id (&all_threads, ti_p->ti_tid); goto found; } } inferior = (struct thread_info *) find_inferior_id (&all_threads, ti_p->ti_tid); if (inferior != NULL) return; if (debug_threads) fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ti_p->ti_tid); if (inferior == NULL) { warning ("Could not attach to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); return; } process = inferior_target_data (inferior); found: new_thread_notify (ti_p->ti_tid); process->tid = ti_p->ti_tid; process->lwpid = ti_p->ti_lid; process->thread_known = 1; process->th = *th_p; err = td_thr_event_enable (th_p, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti_p->ti_lid, thread_db_err_str (err)); }
int valgrind_thread_alive (unsigned long tid) { struct thread_info *ti = gdb_id_to_thread(tid); ThreadState *tst; if (ti != NULL) { tst = (ThreadState *) inferior_target_data (ti); return tst->status != VgTs_Zombie; } else { return 0; } }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (ptid_t ptid, int get_context) { struct thread_info *thread; win32_thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (get_context) win32_require_context (th); return th; }
static int update_debug_registers_callback (struct inferior_list_entry *entry, void *pid_p) { struct thread_info *thr = (struct thread_info *) entry; win32_thread_info *th = inferior_target_data (thr); int pid = *(int *) pid_p; /* Only update the threads of this process. */ if (pid_of (thr) == pid) { /* The actual update is done later just before resuming the lwp, we just mark that the registers need updating. */ th->debug_registers_changed = 1; } return 0; }
static void suspend_one_thread (struct inferior_list_entry *entry) { struct thread_info *thread = (struct thread_info *) entry; win32_thread_info *th = inferior_target_data (thread); if (!th->suspended) { if (SuspendThread (th->h) == (DWORD) -1) { DWORD err = GetLastError (); OUTMSG (("warning: SuspendThread failed in suspend_one_thread, " "(error %d): %s\n", (int) err, strwinerror (err))); } else th->suspended = 1; } }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static thread_info * thread_rec (DWORD id, int get_context) { struct thread_info *thread; thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, id); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (!th->suspend_count && get_context) { if (get_context > 0 && id != current_event.dwThreadId) th->suspend_count = SuspendThread (th->h) + 1; else if (get_context < 0) th->suspend_count = -1; th->context.ContextFlags = CONTEXT_DEBUGGER_DR; GetThreadContext (th->h, &th->context); if (id == current_event.dwThreadId) { /* Copy dr values from that thread. */ dr[0] = th->context.Dr0; dr[1] = th->context.Dr1; dr[2] = th->context.Dr2; dr[3] = th->context.Dr3; dr[6] = th->context.Dr6; dr[7] = th->context.Dr7; } } return th; }
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; }
/* Handle all of the extended 'q' packets. */ static void handle_query (char *arg_own_buf, int *new_packet_len_p) { static struct inferior_list_entry *thread_ptr; /* thread local storage query */ if (strncmp ("qGetTLSAddr:", arg_own_buf, 12) == 0) { char *from, *to; char *end = arg_own_buf + strlen(arg_own_buf); unsigned long gdb_id; CORE_ADDR lm; CORE_ADDR offset; struct thread_info *ti; from = arg_own_buf + 12; to = strchr(from, ','); *to = 0; gdb_id = strtoul (from, NULL, 16); from = to + 1; to = strchr(from, ','); decode_address (&offset, from, to - from); from = to + 1; to = end; decode_address (&lm, from, to - from); dlog(2, "qGetTLSAddr thread %lu offset %p lm %p\n", gdb_id, (void*)offset, (void*)lm); ti = gdb_id_to_thread (gdb_id); if (ti != NULL) { ThreadState *tst; Addr tls_addr; tst = (ThreadState *) inferior_target_data (ti); if (valgrind_get_tls_addr(tst, offset, lm, &tls_addr)) { VG_(sprintf) (arg_own_buf, "%lx", tls_addr); return; } // else we will report we do not support qGetTLSAddr } else { write_enn (arg_own_buf); return; } } /* qRcmd, monitor command handling. */ if (strncmp ("qRcmd,", arg_own_buf, 6) == 0) { char *p = arg_own_buf + 6; int cmdlen = strlen(p)/2; char cmd[cmdlen+1]; if (unhexify (cmd, p, cmdlen) != cmdlen) { write_enn (arg_own_buf); return; } cmd[cmdlen] = '\0'; if (handle_gdb_monitor_command (cmd)) { write_ok (arg_own_buf); return; } else { /* cmd not recognised */ VG_(gdb_printf) ("command '%s' not recognised\n" "In gdb, try 'monitor help'\n" "In a shell, try 'vgdb help'\n", cmd); write_ok (arg_own_buf); return; } } /* provide some valgrind specific info in return to qThreadExtraInfo. */ if (strncmp ("qThreadExtraInfo,", arg_own_buf, 17) == 0) { unsigned long gdb_id; struct thread_info *ti; ThreadState *tst; gdb_id = strtoul (&arg_own_buf[17], NULL, 16); ti = gdb_id_to_thread (gdb_id); if (ti != NULL) { tst = (ThreadState *) inferior_target_data (ti); /* Additional info is the tid, the thread status and the thread's name, if any. */ SizeT len = strlen(VG_(name_of_ThreadStatus)(tst->status)) + 20; if (tst->thread_name) len += strlen(tst->thread_name); /* As the string will be hexified and copied into own_buf we need to limit the length to avoid buffer overflow. */ if (len * 2 > (PBUFSIZ + POVERHSIZ)) len = (PBUFSIZ + POVERHSIZ) / 2; char status[len]; if (tst->thread_name) { VG_(snprintf) (status, sizeof(status), "tid %d %s %s", tst->tid, VG_(name_of_ThreadStatus)(tst->status), tst->thread_name); } else { VG_(snprintf) (status, sizeof(status), "tid %d %s", tst->tid, VG_(name_of_ThreadStatus)(tst->status)); } hexify (arg_own_buf, status, strlen(status)); return; } else { write_enn (arg_own_buf); return; } } if (strcmp ("qAttached", arg_own_buf) == 0) { /* tell gdb to always detach, never kill the process */ arg_own_buf[0] = '1'; arg_own_buf[1] = 0; return; } if (strcmp ("qSymbol::", arg_own_buf) == 0) { /* We have no symbol to read. */ write_ok (arg_own_buf); return; } if (strcmp ("qfThreadInfo", arg_own_buf) == 0) { thread_ptr = all_threads.head; VG_(sprintf) (arg_own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } if (strcmp ("qsThreadInfo", arg_own_buf) == 0) { if (thread_ptr != NULL) { VG_(sprintf) (arg_own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } else { VG_(sprintf) (arg_own_buf, "l"); return; } } if (valgrind_target_xml(VG_(clo_vgdb_shadow_registers)) != NULL && strncmp ("qXfer:features:read:", arg_own_buf, 20) == 0) { CORE_ADDR ofs; unsigned int len, doc_len; const char *annex = NULL; // First, the annex is extracted from the packet received. // Then, it is replaced by the corresponding file name. int fd; /* Grab the annex, offset, and length. */ if (decode_xfer_read (arg_own_buf + 20, &annex, &ofs, &len) < 0) { strcpy (arg_own_buf, "E00"); return; } if (strcmp (annex, "target.xml") == 0) { annex = valgrind_target_xml(VG_(clo_vgdb_shadow_registers)); if (annex != NULL && VG_(clo_vgdb_shadow_registers)) { /* Ensure the shadow registers are initialized. */ initialize_shadow_low(True); } if (annex == NULL) { strcpy (arg_own_buf, "E00"); return; } } { char doc[VG_(strlen)(VG_(libdir)) + 1 + VG_(strlen)(annex) + 1]; struct vg_stat stat_doc; char toread[len]; int len_read; VG_(sprintf)(doc, "%s/%s", VG_(libdir), annex); fd = VG_(fd_open) (doc, VKI_O_RDONLY, 0); if (fd == -1) { strcpy (arg_own_buf, "E00"); return; } if (VG_(fstat) (fd, &stat_doc) != 0) { VG_(close) (fd); strcpy (arg_own_buf, "E00"); return; } doc_len = stat_doc.size; if (len > PBUFSIZ - POVERHSIZ) len = PBUFSIZ - POVERHSIZ; if (ofs > doc_len) { write_enn (arg_own_buf); VG_(close) (fd); return; } VG_(lseek) (fd, ofs, VKI_SEEK_SET); len_read = VG_(read) (fd, toread, len); *new_packet_len_p = write_qxfer_response (arg_own_buf, (unsigned char *)toread, len_read, ofs + len_read < doc_len); VG_(close) (fd); return; } } if (strncmp ("qXfer:auxv:read:", arg_own_buf, 16) == 0) { unsigned char *data; int n; CORE_ADDR ofs; unsigned int len; const char *annex; /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (arg_own_buf + 16, &annex, &ofs, &len) < 0 || annex[0] != '\0') { strcpy (arg_own_buf, "E00"); return; } if (len > PBUFSIZ - POVERHSIZ) len = PBUFSIZ - POVERHSIZ; data = malloc (len); { UWord *client_auxv = VG_(client_auxv); unsigned int client_auxv_len = 0; while (*client_auxv != 0) { dlog(4, "auxv %lld %llx\n", (ULong)*client_auxv, (ULong)*(client_auxv+1)); client_auxv++; client_auxv++; client_auxv_len += 2 * sizeof(UWord); } client_auxv_len += 2 * sizeof(UWord); dlog(4, "auxv len %d\n", client_auxv_len); if (ofs >= client_auxv_len) n = -1; else { n = client_auxv_len - ofs; VG_(memcpy) (data, (unsigned char *) VG_(client_auxv), n); } } if (n < 0) write_enn (arg_own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (arg_own_buf, data, len, 1); else *new_packet_len_p = write_qxfer_response (arg_own_buf, data, n, 0); free (data); return; } if (strncmp ("qXfer:exec-file:read:", arg_own_buf, 21) == 0) { unsigned char *data; int n; CORE_ADDR ofs; unsigned int len; const char *annex; unsigned long pid; const HChar *name; /* grab the annex, offset and length. */ if (decode_xfer_read (arg_own_buf + 21, &annex, &ofs, &len) < 0) { strcpy (arg_own_buf, "E00"); return; } /* Reject any annex with invalid/unexpected pid */ if (strlen(annex) > 0) pid = strtoul (annex, NULL, 16); else pid = 0; if ((int)pid != VG_(getpid)() && pid != 0) { VG_(sprintf) (arg_own_buf, "E.Valgrind gdbserver pid is %d." " Cannot give info for pid %d", VG_(getpid)(), (int) pid); return; } if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; data = malloc (len); if (!VG_(resolve_filename)(VG_(cl_exec_fd), &name)) { VG_(sprintf) (arg_own_buf, "E.Valgrind gdbserver could not" " resolve pid %d exec filename.", VG_(getpid)()); return; } if (ofs >= strlen(name)) n = -1; else { n = strlen(name) - ofs; VG_(memcpy) (data, name, n); } if (n < 0) write_enn (arg_own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (arg_own_buf, data, len, 1); else *new_packet_len_p = write_qxfer_response (arg_own_buf, data, n, 0); free (data); return; } if (strncmp ("qXfer:siginfo:read:", arg_own_buf, 19) == 0) { vki_siginfo_t info; int n; CORE_ADDR ofs; unsigned int len; const char *annex; /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (arg_own_buf + 19, &annex, &ofs, &len) < 0 || annex[0] != '\0') { strcpy (arg_own_buf, "E00"); return; } if (len > PBUFSIZ - POVERHSIZ) len = PBUFSIZ - POVERHSIZ; gdbserver_pending_signal_to_report(&info); if (ofs >= sizeof(info)) n = -1; else n = sizeof(info) - ofs; if (n < 0) write_enn (arg_own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (arg_own_buf, (unsigned char *)&info, len, 1); else *new_packet_len_p = write_qxfer_response (arg_own_buf, (unsigned char *)&info, n, 0); return; } /* Protocol features query. */ if (strncmp ("qSupported", arg_own_buf, 10) == 0 && (arg_own_buf[10] == ':' || arg_own_buf[10] == '\0')) { VG_(sprintf) (arg_own_buf, "PacketSize=%x", PBUFSIZ - 1); /* Note: max packet size including frame and checksum, but without trailing null byte, which is not sent/received. */ strcat (arg_own_buf, ";QStartNoAckMode+"); strcat (arg_own_buf, ";QPassSignals+"); if (VG_(client_auxv)) strcat (arg_own_buf, ";qXfer:auxv:read+"); if (valgrind_target_xml(VG_(clo_vgdb_shadow_registers)) != NULL) { strcat (arg_own_buf, ";qXfer:features:read+"); /* if a new gdb connects to us, we have to reset the register set to the normal register sets to allow this new gdb to decide to use or not the shadow registers. Note that the reset is only done for gdb that are sending qSupported packets. If a user first connected with a recent gdb using shadow registers and then with a very old gdb that does not use qSupported packet, then the old gdb will not properly connect. */ initialize_shadow_low(False); } strcat (arg_own_buf, ";qXfer:exec-file:read+"); strcat (arg_own_buf, ";qXfer:siginfo:read+"); return; } /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ arg_own_buf[0] = 0; }
/* Get the thread ID from the current selected inferior (the current thread). */ static DWORD current_inferior_tid (void) { thread_info *th = inferior_target_data (current_inferior); return th->tid; }
/* Handle all of the extended 'q' packets. */ static void handle_query (char *arg_own_buf, int *new_packet_len_p) { static struct inferior_list_entry *thread_ptr; /* qRcmd, monitor command handling. */ if (strncmp ("qRcmd,", arg_own_buf, 6) == 0) { char *p = arg_own_buf + 6; int cmdlen = strlen(p)/2; char cmd[cmdlen+1]; if (unhexify (cmd, p, cmdlen) != cmdlen) { write_enn (arg_own_buf); return; } cmd[cmdlen] = '\0'; if (handle_gdb_monitor_command (cmd)) { /* In case the command is from a standalone vgdb, connection will be closed soon => flush the output. */ VG_(message_flush) (); write_ok (arg_own_buf); return; } else { /* cmd not recognised */ VG_(gdb_printf) ("command '%s' not recognised\n" "In gdb, try 'monitor help'\n" "In a shell, try 'vgdb help'\n", cmd); write_ok (arg_own_buf); return; } } /* provide some valgrind specific info in return to qThreadExtraInfo. */ if (strncmp ("qThreadExtraInfo,", arg_own_buf, 17) == 0) { unsigned long gdb_id; struct thread_info *ti; ThreadState *tst; char status[100]; gdb_id = strtoul (&arg_own_buf[17], NULL, 16); ti = gdb_id_to_thread (gdb_id); if (ti != NULL) { tst = (ThreadState *) inferior_target_data (ti); /* Additional info is the tid and the thread status. */ VG_(snprintf) (status, sizeof(status), "tid %d %s", tst->tid, VG_(name_of_ThreadStatus)(tst->status)); hexify (arg_own_buf, status, strlen(status)); return; } else { write_enn (arg_own_buf); return; } } if (strcmp ("qAttached", arg_own_buf) == 0) { /* tell gdb to always detach, never kill the process */ arg_own_buf[0] = '1'; arg_own_buf[1] = 0; return; } if (strcmp ("qSymbol::", arg_own_buf) == 0) { /* We have no symbol to read. */ write_ok (arg_own_buf); return; } if (strcmp ("qfThreadInfo", arg_own_buf) == 0) { thread_ptr = all_threads.head; VG_(sprintf) (arg_own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } if (strcmp ("qsThreadInfo", arg_own_buf) == 0) { if (thread_ptr != NULL) { VG_(sprintf) (arg_own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } else { VG_(sprintf) (arg_own_buf, "l"); return; } } if (valgrind_target_xml(VG_(clo_vgdb_shadow_registers)) != NULL && strncmp ("qXfer:features:read:", arg_own_buf, 20) == 0) { CORE_ADDR ofs; unsigned int len, doc_len; const char *annex = NULL; // First, the annex is extracted from the packet received. // Then, it is replaced by the corresponding file name. int fd; /* Grab the annex, offset, and length. */ if (decode_xfer_read (arg_own_buf + 20, &annex, &ofs, &len) < 0) { strcpy (arg_own_buf, "E00"); return; } if (strcmp (annex, "target.xml") == 0) { annex = valgrind_target_xml(VG_(clo_vgdb_shadow_registers)); if (annex != NULL && VG_(clo_vgdb_shadow_registers)) { /* Ensure the shadow registers are initialized. */ initialize_shadow_low(True); } if (annex == NULL) { strcpy (arg_own_buf, "E00"); return; } } { char doc[VG_(strlen)(VG_(libdir)) + 1 + VG_(strlen)(annex) + 1]; struct vg_stat stat_doc; char toread[len]; int len_read; VG_(sprintf)(doc, "%s/%s", VG_(libdir), annex); fd = VG_(fd_open) (doc, VKI_O_RDONLY, 0); if (fd == -1) { strcpy (arg_own_buf, "E00"); return; } if (VG_(fstat) (fd, &stat_doc) != 0) { VG_(close) (fd); strcpy (arg_own_buf, "E00"); return; } doc_len = stat_doc.size; if (len > PBUFSIZ - POVERHSIZ) len = PBUFSIZ - POVERHSIZ; if (ofs > doc_len) { write_enn (arg_own_buf); VG_(close) (fd); return; } VG_(lseek) (fd, ofs, VKI_SEEK_SET); len_read = VG_(read) (fd, toread, len); *new_packet_len_p = write_qxfer_response (arg_own_buf, (unsigned char *)toread, len_read, ofs + len_read < doc_len); VG_(close) (fd); return; } } if (strncmp ("qXfer:auxv:read:", arg_own_buf, 16) == 0) { unsigned char *data; int n; CORE_ADDR ofs; unsigned int len; const char *annex; /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (arg_own_buf + 16, &annex, &ofs, &len) < 0 || annex[0] != '\0') { strcpy (arg_own_buf, "E00"); return; } if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; data = malloc (len); { UWord *client_auxv = VG_(client_auxv); unsigned int client_auxv_len = 0; while (*client_auxv != 0) { dlog(4, "auxv %lld %llx\n", (ULong)*client_auxv, (ULong)*(client_auxv+1)); client_auxv++; client_auxv++; client_auxv_len += 2 * sizeof(UWord); } client_auxv_len += 2 * sizeof(UWord); dlog(4, "auxv len %d\n", client_auxv_len); if (ofs >= client_auxv_len) n = -1; else { n = client_auxv_len - ofs; VG_(memcpy) (data, (unsigned char *) VG_(client_auxv), n); } } if (n < 0) write_enn (arg_own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (arg_own_buf, data, len, 1); else *new_packet_len_p = write_qxfer_response (arg_own_buf, data, n, 0); free (data); return; } /* Protocol features query. */ if (strncmp ("qSupported", arg_own_buf, 10) == 0 && (arg_own_buf[10] == ':' || arg_own_buf[10] == '\0')) { VG_(sprintf) (arg_own_buf, "PacketSize=%x", PBUFSIZ - 1); /* Note: max packet size including frame and checksum, but without trailing null byte, which is not sent/received. */ strcat (arg_own_buf, ";QStartNoAckMode+"); strcat (arg_own_buf, ";QPassSignals+"); if (VG_(client_auxv)) strcat (arg_own_buf, ";qXfer:auxv:read+"); if (valgrind_target_xml(VG_(clo_vgdb_shadow_registers)) != NULL) { strcat (arg_own_buf, ";qXfer:features:read+"); /* if a new gdb connects to us, we have to reset the register set to the normal register sets to allow this new gdb to decide to use or not the shadow registers. Note that the reset is only done for gdb that are sending qSupported packets. If a user first connected with a recent gdb using shadow registers and then with a very old gdb that does not use qSupported packet, then the old gdb will not properly connect. */ initialize_shadow_low(False); } return; } /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ arg_own_buf[0] = 0; }
/* Store our register values back into the inferior. If REGNO is -1, do this for all registers. Otherwise, REGNO specifies which register (so we can save time). */ static void usr_store_inferior_registers (int regno) { int size; ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); ThreadId tid = tst->tid; if (regno >= 0) { if (regno >= the_low_target.num_regs) { dlog(0, "error store_register regno %d max %d\n", regno, the_low_target.num_regs); return; } size = register_size (regno); if (size > 0) { Bool mod; Addr old_SP, new_SP; char buf[size]; if (regno == the_low_target.stack_pointer_regno) { /* When the stack pointer register is changed such that the stack is extended, we better inform the tool of the stack increase. This is needed in particular to avoid spurious Memcheck errors during Inferior calls. So, we save in old_SP the SP before the change. A change of stack pointer is also assumed to have initialised this new stack space. For the typical example of an inferior call, gdb writes arguments on the stack, and then changes the stack pointer. As the stack increase tool function might mark it as undefined, we have to call it at the good moment. */ VG_(memset) ((void *) &old_SP, 0, size); (*the_low_target.transfer_register) (tid, regno, (void *) &old_SP, valgrind_to_gdbserver, size, &mod); } VG_(memset) (buf, 0, size); collect_register (regno, buf); (*the_low_target.transfer_register) (tid, regno, buf, gdbserver_to_valgrind, size, &mod); if (mod && VG_(debugLog_getLevel)() > 1) { char bufimage [2*size + 1]; heximage (bufimage, buf, size); dlog(2, "stored register %d size %d name %s value %s " "tid %d status %s\n", regno, size, the_low_target.reg_defs[regno].name, bufimage, tid, VG_(name_of_ThreadStatus) (tst->status)); } if (regno == the_low_target.stack_pointer_regno) { VG_(memcpy) (&new_SP, buf, size); if (old_SP > new_SP) { Word delta = (Word)new_SP - (Word)old_SP; dlog(1, " stack increase by stack pointer changed from %p to %p " "delta %ld\n", (void*) old_SP, (void *) new_SP, delta); VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, 0 ); VG_TRACK( new_mem_stack, new_SP, -delta ); VG_TRACK( post_mem_write, Vg_CoreClientReq, tid, new_SP, -delta); } } } } else { for (regno = 0; regno < the_low_target.num_regs; regno++) usr_store_inferior_registers (regno); } }