// Scan a block of memory between [start, start+len). This range may // be bogus, inaccessable, or otherwise strange; we deal with it. For each // valid aligned word we assume it's a pointer to a chunk a push the chunk // onto the mark stack if so. static void lc_scan_memory(Addr start, SizeT len, Bool is_prior_definite, Int clique) { Addr ptr = VG_ROUNDUP(start, sizeof(Addr)); Addr end = VG_ROUNDDN(start+len, sizeof(Addr)); vki_sigset_t sigmask; if (VG_DEBUG_LEAKCHECK) VG_(printf)("scan %#lx-%#lx (%lu)\n", start, end, len); VG_(sigprocmask)(VKI_SIG_SETMASK, NULL, &sigmask); VG_(set_fault_catcher)(scan_all_valid_memory_catcher); // We might be in the middle of a page. Do a cheap check to see if // it's valid; if not, skip onto the next page. if (!VG_(am_is_valid_for_client)(ptr, sizeof(Addr), VKI_PROT_READ)) ptr = VG_PGROUNDUP(ptr+1); // First page is bad - skip it. while (ptr < end) { Addr addr; // Skip invalid chunks. if ( ! MC_(is_within_valid_secondary)(ptr) ) { ptr = VG_ROUNDUP(ptr+1, SM_SIZE); continue; } // Look to see if this page seems reasonable. if ((ptr % VKI_PAGE_SIZE) == 0) { if (!VG_(am_is_valid_for_client)(ptr, sizeof(Addr), VKI_PROT_READ)) { ptr += VKI_PAGE_SIZE; // Bad page - skip it. continue; } } if (VG_MINIMAL_SETJMP(memscan_jmpbuf) == 0) { if ( MC_(is_valid_aligned_word)(ptr) ) { lc_scanned_szB += sizeof(Addr); addr = *(Addr *)ptr; // If we get here, the scanned word is in valid memory. Now // let's see if its contents point to a chunk. lc_push_if_a_chunk_ptr(addr, clique, is_prior_definite); } else if (0 && VG_DEBUG_LEAKCHECK) { VG_(printf)("%#lx not valid\n", ptr); } ptr += sizeof(Addr); } else { // We need to restore the signal mask, because we were // longjmped out of a signal handler. VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL); ptr = VG_PGROUNDUP(ptr+1); // Bad page - skip it. } } VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL); VG_(set_fault_catcher)(NULL); }
void gdbserver_terminate (void) { /* last call to gdbserver is cleanup call */ if (VG_MINIMAL_SETJMP(toplevel)) { dlog(0, "error caused VG_MINIMAL_LONGJMP to gdbserver_terminate\n"); return; } remote_close(); }
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; }