/* Main code for remote server for GDB.
- Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004,
- 2005
+ Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004,
+ 2005, 2006
Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "server.h"
#include <unistd.h>
#include <signal.h>
+#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
+#endif
unsigned long cont_thread;
unsigned long general_thread;
static int
start_inferior (char *argv[], char *statusptr)
{
+#ifdef SIGTTOU
signal (SIGTTOU, SIG_DFL);
signal (SIGTTIN, SIG_DFL);
+#endif
signal_pid = create_inferior (argv[0], argv);
fprintf (stderr, "Process %s created; pid = %ld\n", argv[0],
signal_pid);
+ fflush (stderr);
+#ifdef SIGTTOU
signal (SIGTTOU, SIG_IGN);
signal (SIGTTIN, SIG_IGN);
tcsetpgrp (fileno (stderr), signal_pid);
+#endif
/* Wait till we are at 1st instruction in program, return signal number. */
return mywait (statusptr, 0);
return -1;
fprintf (stderr, "Attached; pid = %d\n", pid);
+ fflush (stderr);
/* 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
*sigptr = mywait (statusptr, 0);
+ /* GDB knows to ignore the first SIGSTOP after attaching to a running
+ process using the "attach" command, but this is different; it's
+ just using "target remote". Pretend it's just starting up. */
+ if (*statusptr == 'T' && *sigptr == TARGET_SIGNAL_STOP)
+ *sigptr = TARGET_SIGNAL_TRAP;
+
return 0;
}
extern int remote_debug;
+/* Decode a qXfer read request. Return 0 if everything looks OK,
+ or -1 otherwise. */
+
+static int
+decode_xfer_read (char *buf, char **annex, CORE_ADDR *ofs, unsigned int *len)
+{
+ /* Extract and NUL-terminate the annex. */
+ *annex = buf;
+ while (*buf && *buf != ':')
+ buf++;
+ if (*buf == '\0')
+ return -1;
+ *buf++ = 0;
+
+ /* After the read/write marker and annex, qXfer looks like a
+ traditional 'm' packet. */
+ decode_m_packet (buf, ofs, len);
+
+ return 0;
+}
+
+/* Write the response to a successful qXfer read. Returns the
+ length of the (binary) data stored in BUF, corresponding
+ to as much of DATA/LEN as we could fit. IS_MORE controls
+ the first character of the response. */
+static int
+write_qxfer_response (char *buf, unsigned char *data, int len, int is_more)
+{
+ int out_len;
+
+ if (is_more)
+ buf[0] = 'm';
+ else
+ buf[0] = 'l';
+
+ return remote_escape_output (data, len, (unsigned char *) buf + 1, &out_len,
+ PBUFSIZ - 2) + 1;
+}
+
/* Handle all of the extended 'q' packets. */
void
-handle_query (char *own_buf)
+handle_query (char *own_buf, int *new_packet_len_p)
{
static struct inferior_list_entry *thread_ptr;
if (strcmp ("qfThreadInfo", own_buf) == 0)
{
thread_ptr = all_threads.head;
- sprintf (own_buf, "m%lx", thread_ptr->id);
+ sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
thread_ptr = thread_ptr->next;
return;
}
{
if (thread_ptr != NULL)
{
- sprintf (own_buf, "m%lx", thread_ptr->id);
+ sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
thread_ptr = thread_ptr->next;
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->read_auxv != NULL
- && strncmp ("qPart:auxv:read::", own_buf, 17) == 0)
+ && strncmp ("qXfer:auxv:read:", own_buf, 16) == 0)
{
- char data[(PBUFSIZ - 1) / 2];
+ unsigned char *data;
+ int n;
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);
+ 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 > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
else
- convert_int_to_ascii (data, own_buf, n);
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+
+ 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 (the_target->read_auxv != NULL)
+ strcat (own_buf, ";qXfer:auxv:read+");
+
return;
}
}
else if (p[0] == ':')
{
- resume_info[i].thread = strtoul (p + 1, &q, 16);
+ unsigned int gdb_id = strtoul (p + 1, &q, 16);
+ unsigned long thread_id;
+
if (p == q)
goto err;
p = q;
if (p[0] != ';' && p[0] != 0)
goto err;
+ thread_id = gdb_id_to_thread_id (gdb_id);
+ if (thread_id)
+ resume_info[i].thread = thread_id;
+ else
+ goto err;
+
i++;
}
}
struct thread_resume resume_info[2];
int n = 0;
- if (step || sig || cont_thread > 0)
+ if (step || sig || (cont_thread != 0 && cont_thread != -1))
{
resume_info[0].thread
= ((struct inferior_list_entry *) current_inferior)->id;
resume_info[n].thread = -1;
resume_info[n].step = 0;
resume_info[n].sig = 0;
- resume_info[n].leave_stopped = (cont_thread > 0);
+ resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1);
(*the_target->resume) (resume_info);
}
static int attached;
+static void
+gdbserver_version (void)
+{
+ printf ("GNU gdbserver %s\n"
+ "Copyright (C) 2006 Free Software Foundation, Inc.\n"
+ "gdbserver is free software, covered by the GNU General Public License.\n"
+ "This gdbserver was configured as \"%s\"\n",
+ version, host_name);
+}
+
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");
+ printf ("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[])
{
- char ch, status, *own_buf, mem_buf[2000];
+ char ch, status, *own_buf;
+ unsigned char *mem_buf;
int i = 0;
int signal;
unsigned int len;
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");
}
if (argc < 3 || bad_attach)
- gdbserver_usage();
+ {
+ gdbserver_usage ();
+ exit (1);
+ }
initialize_low ();
own_buf = malloc (PBUFSIZ);
+ mem_buf = malloc (PBUFSIZ);
if (pid == 0)
{
restart:
setjmp (toplevel);
- while (getpkt (own_buf) > 0)
+ 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);
+ 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 ();
}
exit (0);
+#endif
case '!':
if (attached == 0)
prepare_resume_reply (own_buf, status, signal);
break;
case 'H':
- switch (own_buf[1])
+ if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's')
{
- case 'g':
- general_thread = strtoul (&own_buf[2], NULL, 16);
- write_ok (own_buf);
- set_desired_inferior (1);
- break;
- case 'c':
- cont_thread = strtoul (&own_buf[2], NULL, 16);
- write_ok (own_buf);
- break;
- case 's':
- step_thread = strtoul (&own_buf[2], NULL, 16);
+ 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);
- break;
- default:
+ }
+ else
+ {
/* Silently ignore it so that gdb can extend the protocol
without compatibility headaches. */
own_buf[0] = '\0';
- break;
}
break;
case 'g':
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))
break;
}
case 'T':
- if (mythread_alive (strtoul (&own_buf[1], NULL, 16)))
- write_ok (own_buf);
- else
- write_enn (own_buf);
+ {
+ 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
break;
}
- putpkt (own_buf);
+ 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\n",
- signal);
+ 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)