diff options
Diffstat (limited to 'contrib/gdb/gdb/gdbserver/server.c')
-rw-r--r-- | contrib/gdb/gdb/gdbserver/server.c | 339 |
1 files changed, 317 insertions, 22 deletions
diff --git a/contrib/gdb/gdb/gdbserver/server.c b/contrib/gdb/gdb/gdbserver/server.c index adaabac..93e3ea4 100644 --- a/contrib/gdb/gdb/gdbserver/server.c +++ b/contrib/gdb/gdb/gdbserver/server.c @@ -1,5 +1,5 @@ /* Main code for remote server for GDB. - Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002 + Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -21,22 +21,44 @@ #include "server.h" +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> + int cont_thread; int general_thread; +int step_thread; int thread_from_wait; int old_thread_from_wait; int extended_protocol; +int server_waiting; + jmp_buf toplevel; -int inferior_pid; + +/* The PID of the originally created or attached inferior. Used to + send signals to the process when GDB sends us an asynchronous interrupt + (user hitting Control-C in the client), and to wait for the child to exit + when no longer debugging it. */ + +int signal_pid; static unsigned char start_inferior (char *argv[], char *statusptr) { - inferior_pid = create_inferior (argv[0], argv); - fprintf (stderr, "Process %s created; pid = %d\n", argv[0], inferior_pid); + signal (SIGTTOU, SIG_DFL); + signal (SIGTTIN, SIG_DFL); + + signal_pid = create_inferior (argv[0], argv); + + fprintf (stderr, "Process %s created; pid = %d\n", argv[0], + signal_pid); + + signal (SIGTTOU, SIG_IGN); + signal (SIGTTIN, SIG_IGN); + tcsetpgrp (fileno (stderr), signal_pid); /* Wait till we are at 1st instruction in program, return signal number. */ - return mywait (statusptr); + return mywait (statusptr, 0); } static int @@ -44,18 +66,249 @@ attach_inferior (int pid, char *statusptr, unsigned char *sigptr) { /* myattach should return -1 if attaching is unsupported, 0 if it succeeded, and call error() otherwise. */ + if (myattach (pid) != 0) return -1; - inferior_pid = pid; + fprintf (stderr, "Attached; pid = %d\n", pid); + + /* FIXME - It may be that we should get the SIGNAL_PID from the + attach function, so that it can be the main thread instead of + whichever we were told to attach to. */ + signal_pid = pid; - *sigptr = mywait (statusptr); + *sigptr = mywait (statusptr, 0); return 0; } extern int remote_debug; +/* 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; +} + +/* Parse vCont packets. */ +void +handle_v_cont (char *own_buf, char *status, unsigned char *signal) +{ + char *p, *q; + int n = 0, i = 0; + struct thread_resume *resume_info, default_action; + + /* Count the number of semicolons in the packet. There should be one + for every action. */ + p = &own_buf[5]; + while (p) + { + n++; + p++; + p = strchr (p, ';'); + } + /* Allocate room for one extra action, for the default remain-stopped + behavior; if no default action is in the list, we'll need the extra + slot. */ + resume_info = malloc ((n + 1) * sizeof (resume_info[0])); + + default_action.thread = -1; + default_action.leave_stopped = 1; + default_action.step = 0; + default_action.sig = 0; + + p = &own_buf[5]; + i = 0; + while (*p) + { + p++; + + resume_info[i].leave_stopped = 0; + + if (p[0] == 's' || p[0] == 'S') + resume_info[i].step = 1; + else if (p[0] == 'c' || p[0] == 'C') + resume_info[i].step = 0; + else + goto err; + + if (p[0] == 'S' || p[0] == 'C') + { + int sig; + sig = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + + if (!target_signal_to_host_p (sig)) + goto err; + resume_info[i].sig = target_signal_to_host (sig); + } + else + { + resume_info[i].sig = 0; + p = p + 1; + } + + if (p[0] == 0) + { + resume_info[i].thread = -1; + default_action = resume_info[i]; + + /* Note: we don't increment i here, we'll overwrite this entry + the next time through. */ + } + else if (p[0] == ':') + { + resume_info[i].thread = strtol (p + 1, &q, 16); + if (p == q) + goto err; + p = q; + if (p[0] != ';' && p[0] != 0) + goto err; + + i++; + } + } + + resume_info[i] = default_action; + + /* Still used in occasional places in the backend. */ + if (n == 1 && resume_info[0].thread != -1) + cont_thread = resume_info[0].thread; + else + cont_thread = -1; + set_desired_inferior (0); + + (*the_target->resume) (resume_info); + + free (resume_info); + + *signal = mywait (status, 1); + prepare_resume_reply (own_buf, *status, *signal); + return; + +err: + /* No other way to report an error... */ + strcpy (own_buf, ""); + free (resume_info); + return; +} + +/* Handle all of the extended 'v' packets. */ +void +handle_v_requests (char *own_buf, char *status, unsigned char *signal) +{ + if (strncmp (own_buf, "vCont;", 6) == 0) + { + handle_v_cont (own_buf, status, signal); + return; + } + + if (strncmp (own_buf, "vCont?", 6) == 0) + { + strcpy (own_buf, "vCont;c;C;s;S"); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; + return; +} + +void +myresume (int step, int sig) +{ + struct thread_resume resume_info[2]; + int n = 0; + + if (step || sig || cont_thread > 0) + { + resume_info[0].thread + = ((struct inferior_list_entry *) current_inferior)->id; + resume_info[0].step = step; + resume_info[0].sig = sig; + resume_info[0].leave_stopped = 0; + n++; + } + resume_info[n].thread = -1; + resume_info[n].step = 0; + resume_info[n].sig = 0; + resume_info[n].leave_stopped = (cont_thread > 0); + + (*the_target->resume) (resume_info); +} + +static int attached; + +static void +gdbserver_usage (void) +{ + error ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" + "\tgdbserver COMM --attach PID\n" + "\n" + "COMM may either be a tty device (for serial debugging), or \n" + "HOST:PORT to listen for a TCP connection.\n"); +} + int main (int argc, char *argv[]) { @@ -64,9 +317,8 @@ main (int argc, char *argv[]) unsigned char signal; unsigned int len; CORE_ADDR mem_addr; - int bad_attach = 0; - int pid = 0; - int attached = 0; + int bad_attach; + int pid; char *arg_end; if (setjmp (toplevel)) @@ -75,6 +327,9 @@ main (int argc, char *argv[]) exit (1); } + bad_attach = 0; + pid = 0; + attached = 0; if (argc >= 3 && strcmp (argv[2], "--attach") == 0) { if (argc == 4 @@ -89,8 +344,7 @@ main (int argc, char *argv[]) } if (argc < 3 || bad_attach) - error ("Usage:\tgdbserver tty prog [args ...]\n" - "\tgdbserver tty --attach pid"); + gdbserver_usage(); initialize_low (); @@ -129,9 +383,34 @@ main (int argc, char *argv[]) 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) { @@ -155,12 +434,16 @@ main (int argc, char *argv[]) case 'g': general_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); - fetch_inferior_registers (0); + 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. */ @@ -169,17 +452,20 @@ main (int argc, char *argv[]) } 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]); - 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); + 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); @@ -194,8 +480,9 @@ main (int argc, char *argv[]) signal = target_signal_to_host (sig); else signal = 0; + set_desired_inferior (0); myresume (0, signal); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'S': @@ -204,18 +491,21 @@ main (int argc, char *argv[]) signal = target_signal_to_host (sig); else signal = 0; + set_desired_inferior (0); myresume (1, signal); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'c': + set_desired_inferior (0); myresume (0, 0); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 's': + set_desired_inferior (0); myresume (1, 0); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'k': @@ -267,6 +557,10 @@ main (int argc, char *argv[]) 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 @@ -279,9 +573,10 @@ main (int argc, char *argv[]) if (status == 'W') fprintf (stderr, - "\nChild exited with status %d\n", sig); + "\nChild exited with status %d\n", signal); if (status == 'X') - fprintf (stderr, "\nChild terminated with signal = 0x%x\n", sig); + fprintf (stderr, "\nChild terminated with signal = 0x%x\n", + signal); if (status == 'W' || status == 'X') { if (extended_protocol) |