static void do_rmem(const char* line, uint8_t len) { uint16_t size; skip_ws(&line, &len); if (len != 4) goto on_error; if (string_to_uint16(line, &size)) goto on_error; if ((mem_addr + size) > DS2433_MEM_SIZE) goto on_error; if (ds2433_read_mem(mem_buf, mem_addr, size)) goto on_error; mem_addr += size; write_ok(); write_buf(mem_buf, size); return ; on_error: write_ko(); return ; }
static int sha_fs_get(struct request *r) { struct sha_fs_request *sp = (struct sha_fs_request *)r->open_data; int status = 0; blk_SHA_CTX ctx; unsigned char digest[20]; int fd; unsigned char chunk[CHUNK_SIZE]; int nread; blob_path(r, r->digest); /* * Open the file to the blob. */ switch (_open(r, sp->blob_path, &fd)) { case 0: break; case ENOENT: return 1; default: _panic(r, "_open(blob) failed"); } /* * Tell the client we have the blob. */ if (write_ok(r)) { _error(r, "write_ok() failed"); goto croak; } blk_SHA1_Init(&ctx); /* * Read a chunk from the file, write chunk to client, * update incremental digest. * * In principle, we ought to first scan the blob file * before sending "ok" to the requestor. */ while ((nread = _read(r, fd, chunk, sizeof chunk)) > 0) { if (blob_write(r, chunk, nread)) { _error(r, "blob_write(blob chunk) failed"); goto croak; } /* * Update the incremental digest. */ blk_SHA1_Update(&ctx, chunk, nread); } if (nread < 0) _panic(r, "_read(blob) failed"); /* * Finalize the digest. */ blk_SHA1_Final(digest, &ctx); /* * If the calculated digest does NOT match the stored digest, * then zap the blob from storage and get panicy. * A corrupt blob is a bad, bad thang. * * Note: unfortunately we've already deceived the client * by sending "ok". Probably need to improve for * the special case when the entire blob is read * in first chunk. */ if (memcmp(sp->digest, digest, 20)) { _error2(r, "PANIC: stored blob doesn't match digest", r->digest); if (zap_blob(r)) _panic(r, "zap_blob() failed"); goto croak; } goto cleanup; croak: status = -1; cleanup: if (_close(r, &fd)) _panic(r, "_close(blob) failed"); return status; }
/* Handle all of the extended 'q' packets. */ void handle_query (char *own_buf) { static struct inferior_list_entry *thread_ptr; if (strcmp ("qSymbol::", own_buf) == 0) { if (the_target->look_up_symbols != NULL) (*the_target->look_up_symbols) (); strcpy (own_buf, "OK"); return; } if (strcmp ("qfThreadInfo", own_buf) == 0) { thread_ptr = all_threads.head; sprintf (own_buf, "m%x", thread_ptr->id); thread_ptr = thread_ptr->next; return; } if (strcmp ("qsThreadInfo", own_buf) == 0) { if (thread_ptr != NULL) { sprintf (own_buf, "m%x", thread_ptr->id); thread_ptr = thread_ptr->next; return; } else { sprintf (own_buf, "l"); return; } } if (the_target->read_auxv != NULL && strncmp ("qPart:auxv:read::", own_buf, 17) == 0) { char data[(PBUFSIZ - 1) / 2]; CORE_ADDR ofs; unsigned int len; int n; decode_m_packet (&own_buf[17], &ofs, &len); /* "OFS,LEN" */ if (len > sizeof data) len = sizeof data; n = (*the_target->read_auxv) (ofs, data, len); if (n == 0) write_ok (own_buf); else if (n < 0) write_enn (own_buf); else convert_int_to_ascii (data, own_buf, n); return; } /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; }
int main (int argc, char *argv[]) { char ch, status, *own_buf, mem_buf[2000]; int i = 0; unsigned char signal; unsigned int len; CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end; if (setjmp (toplevel)) { fprintf (stderr, "Exiting\n"); exit (1); } bad_attach = 0; pid = 0; attached = 0; if (argc >= 3 && strcmp (argv[2], "--attach") == 0) { if (argc == 4 && argv[3] != '\0' && (pid = strtoul (argv[3], &arg_end, 10)) != 0 && *arg_end == '\0') { ; } else bad_attach = 1; } if (argc < 3 || bad_attach) gdbserver_usage(); initialize_low (); own_buf = malloc (PBUFSIZ); if (pid == 0) { /* Wait till we are at first instruction in program. */ signal = start_inferior (&argv[2], &status); /* We are now stopped at the first instruction of the target process */ } else { switch (attach_inferior (pid, &status, &signal)) { case -1: error ("Attaching not supported on this target"); break; default: attached = 1; break; } } while (1) { remote_open (argv[1]); restart: setjmp (toplevel); while (getpkt (own_buf) > 0) { unsigned char sig; i = 0; ch = own_buf[i++]; switch (ch) { case 'q': handle_query (own_buf); break; case 'd': remote_debug = !remote_debug; break; case 'D': fprintf (stderr, "Detaching from inferior\n"); detach_inferior (); write_ok (own_buf); putpkt (own_buf); remote_close (); /* If we are attached, then we can exit. Otherwise, we need to hang around doing nothing, until the child is gone. */ if (!attached) { int status, ret; do { ret = waitpid (signal_pid, &status, 0); if (WIFEXITED (status) || WIFSIGNALED (status)) break; } while (ret != -1 || errno != ECHILD); } exit (0); case '!': if (attached == 0) { extended_protocol = 1; prepare_resume_reply (own_buf, status, signal); } else { /* We can not use the extended protocol if we are attached, because we can not restart the running program. So return unrecognized. */ own_buf[0] = '\0'; } break; case '?': prepare_resume_reply (own_buf, status, signal); break; case 'H': switch (own_buf[1]) { case 'g': general_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); set_desired_inferior (1); break; case 'c': cont_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); break; case 's': step_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); break; default: /* Silently ignore it so that gdb can extend the protocol without compatibility headaches. */ own_buf[0] = '\0'; break; } 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 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_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 (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); break; case 'C': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (0, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'S': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (1, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'c': set_desired_inferior (0); myresume (0, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 's': set_desired_inferior (0); myresume (1, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'k': fprintf (stderr, "Killing inferior\n"); kill_inferior (); /* When using the extended protocol, we start up a new debugging session. The traditional protocol will exit instead. */ if (extended_protocol) { write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { exit (0); break; } case 'T': if (mythread_alive (strtol (&own_buf[1], NULL, 16))) write_ok (own_buf); else write_enn (own_buf); break; case 'R': /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { /* 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, &signal); 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; } putpkt (own_buf); if (status == 'W') fprintf (stderr, "\nChild exited with status %d\n", signal); if (status == 'X') fprintf (stderr, "\nChild terminated with signal = 0x%x\n", signal); if (status == 'W' || status == 'X') { if (extended_protocol) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } } /* We come here when getpkt fails. For the extended remote protocol we exit (and this is the only way we gracefully exit!). For the traditional remote protocol close the connection, and re-open it at the top of the loop. */ if (extended_protocol) { remote_close (); exit (0); } else { fprintf (stderr, "Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); 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; }
/* 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; }
int main (int argc, char *argv[]) { char ch, status, *own_buf; unsigned char *mem_buf; int i = 0; int signal; unsigned int len; CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end; my_stdout = stdout; my_stderr = stderr; myname = argv[0]; if (argc >= 2 && strcmp (argv[1], "--version") == 0) { gdbserver_version (); exit (0); } if (argc >= 2 && strcmp (argv[1], "--help") == 0) { gdbserver_usage (); exit (0); } if (setjmp (toplevel)) { warning ("Exiting"); exit (1); } bad_attach = 0; pid = 0; attached = 0; if (argc >= 3 && strcmp (argv[2], "--attach") == 0) { if (argc == 4 && argv[3][0] != '\0' && (pid = strtoul (argv[3], &arg_end, 10)) != 0 && *arg_end == '\0') { ; } else bad_attach = 1; } if (argc < 3 || bad_attach) { gdbserver_usage (); exit (1); } if (strcmp (argv[1], "pipe") == 0) { my_stdout = my_stderr = stderr; } initialize_low (); own_buf = malloc (PBUFSIZ + 1); mem_buf = malloc (PBUFSIZ); if (pid == 0) { /* Wait till we are at first instruction in program. */ signal = start_inferior (&argv[2], &status); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was successfully created. */ /* Don't report shared library events on the initial connection, even if some libraries are preloaded. */ dlls_changed = 0; } else { switch (attach_inferior (pid, &status, &signal)) { case -1: error ("Attaching not supported on this target"); break; default: attached = 1; break; } } if (setjmp (toplevel)) { warning ("Killing inferior"); kill_inferior (); exit (1); } if (status == 'W' || status == 'X') { warning ("No inferior, GDBserver exiting."); exit (1); } while (1) { remote_open (argv[1]); restart: if (setjmp (toplevel)) { if (remote_debug) printf_filtered ("gdbserver: error returned to main loop\n"); write_enn (own_buf); putpkt (own_buf); } while (1) { unsigned char sig; int packet_len; int new_packet_len = -1; packet_len = getpkt (own_buf, PBUFSIZ); if (packet_len <= 0) break; i = 0; ch = own_buf[i++]; switch (ch) { case 'q': handle_query (own_buf, packet_len, &new_packet_len); break; case 'Q': handle_general_set (own_buf); break; case 'D': warning ("Detaching from inferior"); if (detach_inferior () != 0) { write_enn (own_buf); putpkt (own_buf); } else { write_ok (own_buf); putpkt (own_buf); remote_close (); /* If we are attached, then we can exit. Otherwise, we need to hang around doing nothing, until the child is gone. */ if (!attached) join_inferior (); exit (0); } case '!': if (attached == 0) { extended_protocol = 1; prepare_resume_reply (own_buf, status, signal); } else { /* We can not use the extended protocol if we are attached, because we can not restart the running program. So return unrecognized. */ own_buf[0] = '\0'; } break; case '?': prepare_resume_reply (own_buf, status, signal); 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 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_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 (write_inferior_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 || write_inferior_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)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (0, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'S': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (1, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'c': set_desired_inferior (0); myresume (0, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 's': set_desired_inferior (0); myresume (1, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'z': case 'Z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->insert_watchpoint == NULL || the_target->remove_watchpoint == NULL || (type < '0' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; if (ch == 'z') res = (*the_target->remove_watchpoint) (type, addr, len); else res = (*the_target->insert_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'k': warning ("Killing inferior"); kill_inferior (); /* When using the extended protocol, we start up a new debugging session. The traditional protocol will exit instead. */ if (extended_protocol) { write_ok (own_buf); warning ("GDBserver restarting"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { exit (0); 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 (mythread_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. */ if (extended_protocol) { kill_inferior (); write_ok (own_buf); warning ("GDBserver restarting"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { /* 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, &signal); 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') warning ("\nChild exited with status %d", signal); if (status == 'X') warning ("\nChild terminated with signal = 0x%x (%s)", target_signal_to_host (signal), target_signal_to_name (signal)); if (status == 'W' || status == 'X') { if (extended_protocol) { warning ("Killing inferior"); kill_inferior (); write_ok (own_buf); warning ("GDBserver restarting"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { warning ("GDBserver exiting"); exit (0); } } } /* We come here when getpkt fails. For the extended remote protocol we exit (and this is the only way we gracefully exit!). For the traditional remote protocol close the connection, and re-open it at the top of the loop. */ if (extended_protocol) { remote_close (); exit (0); } else { warning ("Remote side has terminated connection. " "GDBserver will reopen the connection."); remote_close (); } } }
/* Handle all of the extended 'q' packets. */ void handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { static struct inferior_list_entry *thread_ptr; /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0) { thread_ptr = all_threads.head; if (thread_ptr == NULL) strcpy (own_buf, "unset"); else sprintf (own_buf, "QC%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); return; } if (strcmp ("qSymbol::", own_buf) == 0) { if (the_target->look_up_symbols != NULL) (*the_target->look_up_symbols) (); strcpy (own_buf, "OK"); return; } if (strcmp ("qfThreadInfo", own_buf) == 0) { thread_ptr = all_threads.head; sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } if (strcmp ("qsThreadInfo", own_buf) == 0) { if (thread_ptr != NULL) { sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; return; } else { sprintf (own_buf, "l"); return; } } if (the_target->read_offsets != NULL && strcmp ("qOffsets", own_buf) == 0) { CORE_ADDR text, data; if (the_target->read_offsets (&text, &data)) sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", (long)text, (long)data, (long)data); else write_enn (own_buf); return; } if (the_target->qxfer_spu != NULL && strncmp ("qXfer:spu:read:", own_buf, 15) == 0) { char *annex; int n; unsigned int len; CORE_ADDR ofs; unsigned char *spu_buf; strcpy (own_buf, "E00"); if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0) return; if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; spu_buf = malloc (len + 1); if (!spu_buf) return; n = (*the_target->qxfer_spu) (annex, spu_buf, NULL, ofs, len + 1); if (n < 0) write_enn (own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (own_buf, spu_buf, len, 1); else *new_packet_len_p = write_qxfer_response (own_buf, spu_buf, n, 0); free (spu_buf); return; } if (the_target->qxfer_spu != NULL && strncmp ("qXfer:spu:write:", own_buf, 16) == 0) { char *annex; int n; unsigned int len; CORE_ADDR ofs; unsigned char *spu_buf; strcpy (own_buf, "E00"); spu_buf = malloc (packet_len - 15); if (!spu_buf) return; if (decode_xfer_write (own_buf + 16, packet_len - 16, &annex, &ofs, &len, spu_buf) < 0) { free (spu_buf); return; } n = (*the_target->qxfer_spu) (annex, NULL, (unsigned const char *)spu_buf, ofs, len); if (n < 0) write_enn (own_buf); else sprintf (own_buf, "%x", n); free (spu_buf); return; } if (the_target->read_auxv != NULL && strncmp ("qXfer:auxv:read:", own_buf, 16) == 0) { unsigned char *data; int n; CORE_ADDR ofs; unsigned int len; char *annex; /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0 || annex[0] != '\0') { strcpy (own_buf, "E00"); return; } /* Read one extra byte, as an indicator of whether there is more. */ if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; data = malloc (len + 1); n = (*the_target->read_auxv) (ofs, data, len + 1); if (n < 0) write_enn (own_buf); else if (n > len) *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1); else *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0); free (data); return; } if (strncmp ("qXfer:features:read:", own_buf, 20) == 0) { CORE_ADDR ofs; unsigned int len, total_len; const char *document; char *annex; /* Check for support. */ document = get_features_xml ("target.xml"); if (document == NULL) { own_buf[0] = '\0'; return; } /* Grab the annex, offset, and length. */ if (decode_xfer_read (own_buf + 20, &annex, &ofs, &len) < 0) { strcpy (own_buf, "E00"); return; } /* Now grab the correct annex. */ document = get_features_xml (annex); if (document == NULL) { strcpy (own_buf, "E00"); return; } total_len = strlen (document); if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; if (ofs > total_len) write_enn (own_buf); else if (len < total_len - ofs) *new_packet_len_p = write_qxfer_response (own_buf, document + ofs, len, 1); else *new_packet_len_p = write_qxfer_response (own_buf, document + ofs, total_len - ofs, 0); return; } if (strncmp ("qXfer:libraries:read:", own_buf, 21) == 0) { CORE_ADDR ofs; unsigned int len, total_len; char *document, *p; struct inferior_list_entry *dll_ptr; char *annex; /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0 || annex[0] != '\0') { strcpy (own_buf, "E00"); return; } /* Over-estimate the necessary memory. Assume that every character in the library name must be escaped. */ total_len = 64; for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next) total_len += 128 + 6 * strlen (((struct dll_info *) dll_ptr)->name); document = malloc (total_len); strcpy (document, "<library-list>\n"); p = document + strlen (document); for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next) { struct dll_info *dll = (struct dll_info *) dll_ptr; char *name; strcpy (p, " <library name=\""); p = p + strlen (p); name = xml_escape_text (dll->name); strcpy (p, name); free (name); p = p + strlen (p); strcpy (p, "\"><segment address=\""); p = p + strlen (p); sprintf (p, "0x%lx", (long) dll->base_addr); p = p + strlen (p); strcpy (p, "\"/></library>\n"); p = p + strlen (p); } strcpy (p, "</library-list>\n"); total_len = strlen (document); if (len > PBUFSIZ - 2) len = PBUFSIZ - 2; if (ofs > total_len) write_enn (own_buf); else if (len < total_len - ofs) *new_packet_len_p = write_qxfer_response (own_buf, document + ofs, len, 1); else *new_packet_len_p = write_qxfer_response (own_buf, document + ofs, total_len - ofs, 0); free (document); return; } /* Protocol features query. */ if (strncmp ("qSupported", own_buf, 10) == 0 && (own_buf[10] == ':' || own_buf[10] == '\0')) { sprintf (own_buf, "PacketSize=%x", PBUFSIZ - 1); #if !defined (NO_PASS_SIGNALS) strcat (own_buf, ";QPassSignals+", PBUFSIZ - 1); #endif #if !defined (NO_LIBRARIES) /* We do not have any hook to indicate whether the target backend supports qXfer:libraries:read, so always report it. */ strcat (own_buf, ";qXfer:libraries:read+"); #endif strcat (own_buf, ";qXfer:memory map:read+"); if (the_target->read_auxv != NULL) strcat (own_buf, ";qXfer:auxv:read+"); if (the_target->qxfer_spu != NULL) strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+"); if (get_features_xml ("target.xml") != NULL) strcat (own_buf, ";qXfer:features:read+"); return; } /* Thread-local storage support. */ if (the_target->get_tls_address != NULL && strncmp ("qGetTLSAddr:", own_buf, 12) == 0) { char *p = own_buf + 12; CORE_ADDR parts[3], address = 0; int i, err; for (i = 0; i < 3; i++) { char *p2; int len; if (p == NULL) break; p2 = strchr (p, ','); if (p2) { len = p2 - p; p2++; } else { len = strlen (p); p2 = NULL; } decode_address (&parts[i], p, len); p = p2; } if (p != NULL || i < 3) err = 1; else { struct thread_info *thread = gdb_id_to_thread (parts[0]); if (thread == NULL) err = 2; else err = the_target->get_tls_address (thread, parts[1], parts[2], &address); } if (err == 0) { sprintf (own_buf, "%llx", address); return; } else if (err > 0) { write_enn (own_buf); return; } /* Otherwise, pretend we do not understand this packet. */ } /* Handle "monitor" commands. */ if (strncmp ("qRcmd,", own_buf, 6) == 0) { char *mon = malloc (PBUFSIZ); int len = strlen (own_buf + 6); if ((len % 1) != 0 || unhexify (mon, own_buf + 6, len / 2) != len / 2) { write_enn (own_buf); free (mon); return; } mon[len / 2] = '\0'; write_ok (own_buf); if (strcmp (mon, "set debug 1") == 0) { debug_threads = 1; monitor_output ("Debug output enabled.\n"); } else if (strcmp (mon, "set debug 0") == 0) { debug_threads = 0; monitor_output ("Debug output disabled.\n"); } else if (strcmp (mon, "set remote-debug 1") == 0) { remote_debug = 1; monitor_output ("Protocol debug output enabled.\n"); } else if (strcmp (mon, "set remote-debug 0") == 0) { remote_debug = 0; monitor_output ("Protocol debug output disabled.\n"); } else if (strcmp (mon, "help") == 0) monitor_show_help (); else { int ok = 0; if (the_target->commands) ok = (*the_target->commands) (mon, len); else monitor_output ("Unknown monitor command.\n\n"); if (!ok) { monitor_show_help (); write_enn (own_buf); } } free (mon); return; } /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; }
int main (int argc, char *argv[]) { char ch, status, *own_buf; unsigned char *mem_buf; int i = 0; int signal; unsigned int len; CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end, *port; char **next_arg = &argv[1]; int multi_mode = 0; int attach = 0; int was_running; while (*next_arg != NULL && **next_arg == '-') { if (strcmp (*next_arg, "--version") == 0) { gdbserver_version (); exit (0); } else if (strcmp (*next_arg, "--help") == 0) { gdbserver_usage (stdout); exit (0); } else if (strcmp (*next_arg, "--attach") == 0) attach = 1; else if (strcmp (*next_arg, "--multi") == 0) multi_mode = 1; else if (strcmp (*next_arg, "--wrapper") == 0) { next_arg++; wrapper_argv = next_arg; while (*next_arg != NULL && strcmp (*next_arg, "--") != 0) next_arg++; if (next_arg == wrapper_argv || *next_arg == NULL) { gdbserver_usage (stderr); exit (1); } /* Consume the "--". */ *next_arg = NULL; } else if (strcmp (*next_arg, "--debug") == 0) debug_threads = 1; else if (strcmp (*next_arg, "--disable-packet") == 0) { gdbserver_show_disableable (stdout); exit (0); } else if (strncmp (*next_arg, "--disable-packet=", sizeof ("--disable-packet=") - 1) == 0) { char *packets, *tok; packets = *next_arg += sizeof ("--disable-packet=") - 1; for (tok = strtok (packets, ","); tok != NULL; tok = strtok (NULL, ",")) { if (strcmp ("vCont", tok) == 0) disable_packet_vCont = 1; else if (strcmp ("Tthread", tok) == 0) disable_packet_Tthread = 1; else if (strcmp ("qC", tok) == 0) disable_packet_qC = 1; else if (strcmp ("qfThreadInfo", tok) == 0) disable_packet_qfThreadInfo = 1; else if (strcmp ("threads", tok) == 0) { disable_packet_vCont = 1; disable_packet_Tthread = 1; disable_packet_qC = 1; disable_packet_qfThreadInfo = 1; } else { fprintf (stderr, "Don't know how to disable \"%s\".\n\n", tok); gdbserver_show_disableable (stderr); exit (1); } } } else { fprintf (stderr, "Unknown argument: %s\n", *next_arg); exit (1); } next_arg++; continue; } if (setjmp (toplevel)) { fprintf (stderr, "Exiting\n"); exit (1); } port = *next_arg; next_arg++; if (port == NULL || (!attach && !multi_mode && *next_arg == NULL)) { gdbserver_usage (stderr); exit (1); } bad_attach = 0; pid = 0; /* --attach used to come after PORT, so allow it there for compatibility. */ if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0) { attach = 1; next_arg++; } if (attach && (*next_arg == NULL || (*next_arg)[0] == '\0' || (pid = strtoul (*next_arg, &arg_end, 0)) == 0 || *arg_end != '\0' || next_arg[1] != NULL)) bad_attach = 1; if (bad_attach) { gdbserver_usage (stderr); exit (1); } initialize_async_io (); initialize_low (); own_buf = malloc (PBUFSIZ + 1); mem_buf = malloc (PBUFSIZ); if (pid == 0 && *next_arg != NULL) { int i, n; n = argc - (next_arg - argv); program_argv = malloc (sizeof (char *) * (n + 1)); for (i = 0; i < n; i++) program_argv[i] = strdup (next_arg[i]); program_argv[i] = NULL; /* Wait till we are at first instruction in program. */ signal = start_inferior (program_argv, &status); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was successfully created. */ } else if (pid != 0) { if (attach_inferior (pid, &status, &signal) == -1) error ("Attaching not supported on this target"); /* Otherwise succeeded. */ } else { status = 'W'; signal = 0; } /* Don't report shared library events on the initial connection, even if some libraries are preloaded. Avoids the "stopped by shared library event" notice on gdb side. */ dlls_changed = 0; if (setjmp (toplevel)) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); exit (1); } if (status == 'W' || status == 'X') was_running = 0; else was_running = 1; if (!was_running && !multi_mode) { fprintf (stderr, "No program to debug. GDBserver exiting.\n"); exit (1); } while (1) { noack_mode = 0; remote_open (port); restart: if (setjmp (toplevel) != 0) { /* An error occurred. */ if (response_needed) { write_enn (own_buf); putpkt (own_buf); } } disable_async_io (); while (!exit_requested) { unsigned char sig; int packet_len; int new_packet_len = -1; response_needed = 0; packet_len = getpkt (own_buf); if (packet_len <= 0) break; response_needed = 1; i = 0; ch = own_buf[i++]; switch (ch) { case 'q': handle_query (own_buf, packet_len, &new_packet_len); break; case 'Q': handle_general_set (own_buf); break; case 'D': require_running (own_buf); fprintf (stderr, "Detaching from inferior\n"); if (detach_inferior () != 0) write_enn (own_buf); else { write_ok (own_buf); if (extended_protocol) { /* Treat this like a normal program exit. */ signal = 0; status = 'W'; } else { putpkt (own_buf); remote_close (); /* If we are attached, then we can exit. Otherwise, we need to hang around doing nothing, until the child is gone. */ if (!attached) join_inferior (); exit (0); } } break; case '!': extended_protocol = 1; write_ok (own_buf); break; case '?': prepare_resume_reply (own_buf, status, signal); break; case 'H': if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') { unsigned long gdb_id, thread_id; require_running (own_buf); gdb_id = strtoul (&own_buf[2], NULL, 16); if (gdb_id == 0 || gdb_id == -1) thread_id = gdb_id; else { 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': require_running (own_buf); set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': require_running (own_buf); set_desired_inferior (1); registers_from_string (&own_buf[1]); write_ok (own_buf); break; case 'm': require_running (own_buf); decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_memory (mem_addr, mem_buf, len) == 0) convert_int_to_ascii (mem_buf, own_buf, len); else write_enn (own_buf); break; case 'M': require_running (own_buf); decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); break; case 'X': require_running (own_buf); if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 || write_inferior_memory (mem_addr, mem_buf, len) != 0) write_enn (own_buf); else write_ok (own_buf); break; case 'C': require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; myresume (own_buf, 0, &signal, &status); break; case 'S': require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; myresume (own_buf, 1, &signal, &status); break; case 'c': require_running (own_buf); signal = 0; myresume (own_buf, 0, &signal, &status); break; case 's': require_running (own_buf); signal = 0; myresume (own_buf, 1, &signal, &status); break; case 'Z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->insert_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; require_running (own_buf); res = (*the_target->insert_watchpoint) (type, addr, len); 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 len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->remove_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; require_running (own_buf); res = (*the_target->remove_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'k': response_needed = 0; if (!target_running ()) /* The packet we received doesn't make sense - but we can't reply to it, either. */ goto restart; fprintf (stderr, "Killing inferior\n"); kill_inferior (); /* When using the extended protocol, we wait with no program running. The traditional protocol will exit instead. */ if (extended_protocol) { status = 'X'; signal = TARGET_SIGNAL_KILL; was_running = 0; goto restart; } else { exit (0); break; } case 'T': { unsigned long gdb_id, thread_id; require_running (own_buf); 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 (mythread_alive (thread_id)) write_ok (own_buf); else write_enn (own_buf); } break; case 'R': response_needed = 0; /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { if (target_running ()) kill_inferior (); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ if (program_argv != NULL) signal = start_inferior (program_argv, &status); else { status = 'X'; signal = TARGET_SIGNAL_KILL; } goto restart; } else { /* 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, &signal, packet_len, &new_packet_len); 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); response_needed = 0; if (was_running && (status == 'W' || status == 'X')) { was_running = 0; if (status == 'W') fprintf (stderr, "\nChild exited with status %d\n", signal); if (status == 'X') fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", target_signal_to_host (signal), target_signal_to_name (signal)); if (extended_protocol) goto restart; else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } if (status != 'W' && status != 'X') was_running = 1; } /* If an exit was requested (using the "monitor exit" command), terminate now. The only other way to get here is for getpkt to fail; close the connection and reopen it at the top of the loop. */ if (exit_requested) { remote_close (); if (attached && target_running ()) detach_inferior (); else if (target_running ()) kill_inferior (); exit (0); } else { fprintf (stderr, "Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); remote_close (); } } }
void gdbserver_main (void) { CORE_ADDR mem_addr; char *own_buf; unsigned char *mem_buf; int i = 0; unsigned int len; own_buf = malloc (PBUFSIZ + 1); mem_buf = malloc (PBUFSIZ); while (1) { remote_open (port); restart: #if 0 if (setjmp (toplevel) != 0) { /* An error occurred. */ if (response_needed) { write_enn (own_buf); putpkt (own_buf); } } #endif disable_async_io (); while (!exit_requested) { unsigned char sig; int packet_len; int new_packet_len = -1; response_needed = 0; packet_len = getpkt (own_buf); if (packet_len <= 0) break; response_needed = 1; i = 0; ch = own_buf[i++]; switch (ch) { case 'q': handle_query (own_buf, packet_len, &new_packet_len); break; case 'Q': handle_general_set (own_buf); break; case 'D': require_running (own_buf); fprintf (stderr, "Detaching from inferior\n"); if (detach_inferior () != 0) write_enn (own_buf); else { write_ok (own_buf); if (extended_protocol) { /* Treat this like a normal program exit. */ signal = 0; status = 'W'; } else { putpkt (own_buf); remote_close (); /* If we are attached, then we can exit. Otherwise, we need to hang around doing nothing, until the child is gone. */ if (!attached) join_inferior (); exit (0); } } break; case '!': extended_protocol = 1; write_ok (own_buf); break; case '?': prepare_resume_reply (own_buf, status, signal); break; case 'H': if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') { unsigned long gdb_id, thread_id; require_running (own_buf); gdb_id = strtoul (&own_buf[2], NULL, 16); if (gdb_id == 0 || gdb_id == -1) thread_id = gdb_id; else { 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': require_running (own_buf); set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': require_running (own_buf); set_desired_inferior (1); registers_from_string (&own_buf[1]); write_ok (own_buf); break; case 'm': require_running (own_buf); decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_memory (mem_addr, mem_buf, len) == 0) convert_int_to_ascii (mem_buf, own_buf, len); else write_enn (own_buf); break; case 'M': require_running (own_buf); decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); break; case 'X': require_running (own_buf); if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 || write_inferior_memory (mem_addr, mem_buf, len) != 0) write_enn (own_buf); else write_ok (own_buf); break; case 'C': require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; myresume (own_buf, 0, &signal, &status); break; case 'S': require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; myresume (own_buf, 1, &signal, &status); break; case 'c': require_running (own_buf); signal = 0; myresume (own_buf, 0, &signal, &status); break; case 's': require_running (own_buf); signal = 0; myresume (own_buf, 1, &signal, &status); break; case 'Z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->insert_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; require_running (own_buf); res = (*the_target->insert_watchpoint) (type, addr, len); 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 len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->remove_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; require_running (own_buf); res = (*the_target->remove_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'k': response_needed = 0; if (!target_running ()) /* The packet we received doesn't make sense - but we can't reply to it, either. */ goto restart; fprintf (stderr, "Killing inferior\n"); kill_inferior (); /* When using the extended protocol, we wait with no program running. The traditional protocol will exit instead. */ if (extended_protocol) { status = 'X'; signal = TARGET_SIGNAL_KILL; was_running = 0; goto restart; } else { exit (0); break; } case 'T': { unsigned long gdb_id, thread_id; require_running (own_buf); 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 (mythread_alive (thread_id)) write_ok (own_buf); else write_enn (own_buf); } break; case 'R': response_needed = 0; /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { if (target_running ()) kill_inferior (); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ if (program_argv != NULL) signal = start_inferior (program_argv, &status); else { status = 'X'; signal = TARGET_SIGNAL_KILL; } goto restart; } else { /* 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, &signal, packet_len, &new_packet_len); 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); response_needed = 0; if (was_running && (status == 'W' || status == 'X')) { was_running = 0; if (status == 'W') fprintf (stderr, "\nChild exited with status %d\n", signal); if (status == 'X') fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", target_signal_to_host (signal), target_signal_to_name (signal)); if (extended_protocol) goto restart; else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } if (status != 'W' && status != 'X') was_running = 1; } /* If an exit was requested (using the "monitor exit" command), terminate now. The only other way to get here is for getpkt to fail; close the connection and reopen it at the top of the loop. */ if (exit_requested) { remote_close (); if (attached && target_running ()) detach_inferior (); else if (target_running ()) kill_inferior (); exit (0); } else { fprintf (stderr, "Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); remote_close (); } } }
int main (int argc, char *argv[]) { char ch, status, *own_buf; unsigned char *mem_buf; int i = 0; int signal; unsigned int len; CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end; if (argc >= 2 && strcmp (argv[1], "--version") == 0) { gdbserver_version (); exit (0); } if (argc >= 2 && strcmp (argv[1], "--help") == 0) { gdbserver_usage (); exit (0); } if (setjmp (toplevel)) { fprintf (stderr, "Exiting\n"); exit (1); } bad_attach = 0; pid = 0; attached = 0; if (argc >= 3 && strcmp (argv[2], "--attach") == 0) { if (argc == 4 && argv[3] != '\0' && (pid = strtoul (argv[3], &arg_end, 10)) != 0 && *arg_end == '\0') { ; } else bad_attach = 1; } if (argc < 3 || bad_attach) { gdbserver_usage (); exit (1); } initialize_low (); own_buf = malloc (PBUFSIZ); mem_buf = malloc (PBUFSIZ); if (pid == 0) { /* Wait till we are at first instruction in program. */ signal = start_inferior (&argv[2], &status); /* start_inferior() returns an integer, but the wait * function returns an unsigned char. in the case of * of an error, the wait returns -1 which means 255. */ if (status == 'W' || status == 'X') { fprintf (stderr, "Aborting server; child exited with %i\n", signal); exit (signal); } /* We are now stopped at the first instruction of the target process */ } else { switch (attach_inferior (pid, &status, &signal)) { case -1: error ("Attaching not supported on this target"); break; default: attached = 1; break; } } while (1) { remote_open (argv[1]); restart: setjmp (toplevel); while (1) { unsigned char sig; int packet_len; int new_packet_len = -1; packet_len = getpkt (own_buf); if (packet_len <= 0) break; i = 0; ch = own_buf[i++]; switch (ch) { case 'q': handle_query (own_buf, &new_packet_len); break; case 'd': remote_debug = !remote_debug; break; #ifndef USE_WIN32API /* Skip "detach" support on mingw32, since we don't have waitpid. */ case 'D': fprintf (stderr, "Detaching from inferior\n"); detach_inferior (); write_ok (own_buf); putpkt (own_buf); remote_close (); /* If we are attached, then we can exit. Otherwise, we need to hang around doing nothing, until the child is gone. */ if (!attached) { int status, ret; do { ret = waitpid (signal_pid, &status, 0); if (WIFEXITED (status) || WIFSIGNALED (status)) break; } while (ret != -1 || errno != ECHILD); } exit (0); #endif case '!': if (attached == 0) { extended_protocol = 1; prepare_resume_reply (own_buf, status, signal); } else { /* We can not use the extended protocol if we are attached, because we can not restart the running program. So return unrecognized. */ own_buf[0] = '\0'; } break; case '?': prepare_resume_reply (own_buf, status, signal); 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 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_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 (write_inferior_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 || write_inferior_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)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (0, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'S': convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; set_desired_inferior (0); myresume (1, signal); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'c': set_desired_inferior (0); myresume (0, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 's': set_desired_inferior (0); myresume (1, 0); signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'Z': { char *lenptr; char *dataptr; CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); int len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->insert_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; res = (*the_target->insert_watchpoint) (type, addr, len); 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 len = strtol (lenptr + 1, &dataptr, 16); char type = own_buf[1]; if (the_target->remove_watchpoint == NULL || (type < '2' || type > '4')) { /* No watchpoint support or not a watchpoint command; unrecognized either way. */ own_buf[0] = '\0'; } else { int res; res = (*the_target->remove_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); else if (res == 1) /* Unsupported. */ own_buf[0] = '\0'; else write_enn (own_buf); } break; } case 'k': fprintf (stderr, "Killing inferior\n"); kill_inferior (); /* When using the extended protocol, we start up a new debugging session. The traditional protocol will exit instead. */ if (extended_protocol) { write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { exit (0); 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 (mythread_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. */ if (extended_protocol) { kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { /* 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, &signal); 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') fprintf (stderr, "\nChild exited with status %d\n", signal); if (status == 'X') fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", target_signal_to_host (signal), target_signal_to_name (signal)); if (status == 'W' || status == 'X') { if (extended_protocol) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } } /* We come here when getpkt fails. For the extended remote protocol we exit (and this is the only way we gracefully exit!). For the traditional remote protocol close the connection, and re-open it at the top of the loop. */ if (extended_protocol) { remote_close (); exit (0); } else { fprintf (stderr, "Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); remote_close (); } } }
/* 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; }
int main (int argc, char *argv[]) { char ch, status, own_buf[PBUFSIZ], mem_buf[2000]; int i = 0; unsigned char signal; unsigned int len; CORE_ADDR mem_addr; if (setjmp (toplevel)) { fprintf (stderr, "Exiting\n"); exit (1); } if (argc < 3) error ("Usage: gdbserver tty prog [args ...]"); initialize_low (); /* Wait till we are at first instruction in program. */ signal = start_inferior (&argv[2], &status); /* We are now stopped at the first instruction of the target process */ while (1) { remote_open (argv[1]); restart: setjmp (toplevel); while (getpkt (own_buf) > 0) { unsigned char sig; i = 0; ch = own_buf[i++]; switch (ch) { case 'd': remote_debug = !remote_debug; break; case '!': extended_protocol = 1; prepare_resume_reply (own_buf, status, signal); break; case '?': prepare_resume_reply (own_buf, status, signal); break; case 'H': switch (own_buf[1]) { case 'g': general_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); fetch_inferior_registers (0); break; case 'c': cont_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); break; default: /* Silently ignore it so that gdb can extend the protocol without compatibility headaches. */ own_buf[0] = '\0'; break; } break; case 'g': convert_int_to_ascii (registers, own_buf, REGISTER_BYTES); break; case 'G': convert_ascii_to_int (&own_buf[1], registers, REGISTER_BYTES); store_inferior_registers (-1); write_ok (own_buf); break; case 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); read_inferior_memory (mem_addr, mem_buf, len); convert_int_to_ascii (mem_buf, own_buf, len); break; case 'M': decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); break; case 'C': convert_ascii_to_int (own_buf + 1, &sig, 1); myresume (0, sig); signal = mywait (&status); prepare_resume_reply (own_buf, status, signal); break; case 'S': convert_ascii_to_int (own_buf + 1, &sig, 1); myresume (1, sig); signal = mywait (&status); prepare_resume_reply (own_buf, status, signal); break; case 'c': myresume (0, 0); signal = mywait (&status); prepare_resume_reply (own_buf, status, signal); break; case 's': myresume (1, 0); signal = mywait (&status); prepare_resume_reply (own_buf, status, signal); break; case 'k': fprintf (stderr, "Killing inferior\n"); kill_inferior (); /* When using the extended protocol, we start up a new debugging session. The traditional protocol will exit instead. */ if (extended_protocol) { write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { exit (0); break; } case 'T': if (mythread_alive (strtol (&own_buf[1], NULL, 16))) write_ok (own_buf); else write_enn (own_buf); break; case 'R': /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { /* 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; } 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; } putpkt (own_buf); if (status == 'W') fprintf (stderr, "\nChild exited with status %d\n", sig); if (status == 'X') fprintf (stderr, "\nChild terminated with signal = 0x%x\n", sig); if (status == 'W' || status == 'X') { if (extended_protocol) { fprintf (stderr, "Killing inferior\n"); kill_inferior (); write_ok (own_buf); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ signal = start_inferior (&argv[2], &status); goto restart; break; } else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } } /* We come here when getpkt fails. For the extended remote protocol we exit (and this is the only way we gracefully exit!). For the traditional remote protocol close the connection, and re-open it at the top of the loop. */ if (extended_protocol) { remote_close (); exit (0); } else { fprintf (stderr, "Remote side has terminated connection. GDBserver will reopen the connection.\n"); remote_close (); } } }