X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fserver.c;h=423427c356eadcdef61d1825bab225db5424b100;hb=e09875d41026beb03eae1a65510ca40ed3a5d6c1;hp=48a7e0f597a153e5a928e28033e7e31bb1b427bc;hpb=27e232885db363fb545fd2f450e72d929e59b8f6;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 48a7e0f597..423427c356 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1,11 +1,12 @@ /* Main code for remote server for GDB. - Copyright (C) 1989, 1993 Free Software Foundation, Inc. + Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, + 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -14,44 +15,1985 @@ GNU General Public License for more details. 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. */ + along with this program. If not, see . */ #include "server.h" -int cont_thread; -int general_thread; -int thread_from_wait; -int old_thread_from_wait; -int extended_protocol; +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SIGNAL_H +#include +#endif +#if HAVE_SYS_WAIT_H +#include +#endif +#if HAVE_MALLOC_H +#include +#endif + +ptid_t cont_thread; +ptid_t general_thread; +ptid_t step_thread; + +int server_waiting; + +static int extended_protocol; +static int response_needed; +static int exit_requested; + +int multi_process; +int non_stop; + +static char **program_argv, **wrapper_argv; + +/* Enable miscellaneous debugging output. The name is historical - it + was originally used to debug LinuxThreads support. */ +int debug_threads; + +int pass_signals[TARGET_SIGNAL_LAST]; + jmp_buf toplevel; -int inferior_pid; -static unsigned char -start_inferior (argv, statusptr) - char *argv[]; - char *statusptr; +const char *gdbserver_xmltarget; + +/* 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. */ + +unsigned long signal_pid; + +#ifdef SIGTTOU +/* A file descriptor for the controlling terminal. */ +int terminal_fd; + +/* TERMINAL_FD's original foreground group. */ +pid_t old_foreground_pgrp; + +/* Hand back terminal ownership to the original foreground group. */ + +static void +restore_old_foreground_pgrp (void) +{ + tcsetpgrp (terminal_fd, old_foreground_pgrp); +} +#endif + +/* Set if you want to disable optional thread related packets support + in gdbserver, for the sake of testing GDB against stubs that don't + support them. */ +int disable_packet_vCont; +int disable_packet_Tthread; +int disable_packet_qC; +int disable_packet_qfThreadInfo; + +/* Last status reported to GDB. */ +static struct target_waitstatus last_status; +static ptid_t last_ptid; + +static char *own_buf; +static unsigned char *mem_buf; + +/* Structure holding information relative to a single stop reply. We + keep a queue of these (really a singly-linked list) to push to GDB + in non-stop mode. */ +struct vstop_notif +{ + /* Pointer to next in list. */ + struct vstop_notif *next; + + /* Thread or process that got the event. */ + ptid_t ptid; + + /* Event info. */ + struct target_waitstatus status; +}; + +/* The pending stop replies list head. */ +static struct vstop_notif *notif_queue = NULL; + +/* Put a stop reply to the stop reply queue. */ + +static void +queue_stop_reply (ptid_t ptid, struct target_waitstatus *status) +{ + struct vstop_notif *new_notif; + + new_notif = malloc (sizeof (*new_notif)); + new_notif->next = NULL; + new_notif->ptid = ptid; + new_notif->status = *status; + + if (notif_queue) + { + struct vstop_notif *tail; + for (tail = notif_queue; + tail && tail->next; + tail = tail->next) + ; + tail->next = new_notif; + } + else + notif_queue = new_notif; + + if (remote_debug) + { + int i = 0; + struct vstop_notif *n; + + for (n = notif_queue; n; n = n->next) + i++; + + fprintf (stderr, "pending stop replies: %d\n", i); + } +} + +/* Place an event in the stop reply queue, and push a notification if + we aren't sending one yet. */ + +void +push_event (ptid_t ptid, struct target_waitstatus *status) +{ + queue_stop_reply (ptid, status); + + /* If this is the first stop reply in the queue, then inform GDB + about it, by sending a Stop notification. */ + if (notif_queue->next == NULL) + { + char *p = own_buf; + strcpy (p, "Stop:"); + p += strlen (p); + prepare_resume_reply (p, + notif_queue->ptid, ¬if_queue->status); + putpkt_notif (own_buf); + } +} + +/* Get rid of the currently pending stop replies for PID. If PID is + -1, then apply to all processes. */ + +static void +discard_queued_stop_replies (int pid) +{ + struct vstop_notif *prev = NULL, *reply, *next; + + for (reply = notif_queue; reply; reply = next) + { + next = reply->next; + + if (pid == -1 + || ptid_get_pid (reply->ptid) == pid) + { + if (reply == notif_queue) + notif_queue = next; + else + prev->next = reply->next; + + free (reply); + } + else + prev = reply; + } +} + +/* If there are more stop replies to push, push one now. */ + +static void +send_next_stop_reply (char *own_buf) +{ + if (notif_queue) + prepare_resume_reply (own_buf, + notif_queue->ptid, + ¬if_queue->status); + else + write_ok (own_buf); +} + +static int +target_running (void) +{ + return all_threads.head != NULL; +} + +static int +start_inferior (char **argv) +{ + char **new_argv = argv; + + if (wrapper_argv != NULL) + { + int i, count = 1; + + for (i = 0; wrapper_argv[i] != NULL; i++) + count++; + for (i = 0; argv[i] != NULL; i++) + count++; + new_argv = alloca (sizeof (char *) * count); + count = 0; + for (i = 0; wrapper_argv[i] != NULL; i++) + new_argv[count++] = wrapper_argv[i]; + for (i = 0; argv[i] != NULL; i++) + new_argv[count++] = argv[i]; + new_argv[count] = NULL; + } + +#ifdef SIGTTOU + signal (SIGTTOU, SIG_DFL); + signal (SIGTTIN, SIG_DFL); +#endif + + signal_pid = create_inferior (new_argv[0], new_argv); + + /* FIXME: we don't actually know at this point that the create + actually succeeded. We won't know that until we wait. */ + fprintf (stderr, "Process %s created; pid = %ld\n", argv[0], + signal_pid); + fflush (stderr); + +#ifdef SIGTTOU + signal (SIGTTOU, SIG_IGN); + signal (SIGTTIN, SIG_IGN); + terminal_fd = fileno (stderr); + old_foreground_pgrp = tcgetpgrp (terminal_fd); + tcsetpgrp (terminal_fd, signal_pid); + atexit (restore_old_foreground_pgrp); +#endif + + if (wrapper_argv != NULL) + { + struct thread_resume resume_info; + ptid_t ptid; + + resume_info.thread = pid_to_ptid (signal_pid); + resume_info.kind = resume_continue; + resume_info.sig = 0; + + ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + + if (last_status.kind != TARGET_WAITKIND_STOPPED) + return signal_pid; + + do + { + (*the_target->resume) (&resume_info, 1); + + mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + if (last_status.kind != TARGET_WAITKIND_STOPPED) + return signal_pid; + } + while (last_status.value.sig != TARGET_SIGNAL_TRAP); + + return signal_pid; + } + + /* Wait till we are at 1st instruction in program, return new pid + (assuming success). */ + last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + + return signal_pid; +} + +static int +attach_inferior (int pid) +{ + /* myattach should return -1 if attaching is unsupported, + 0 if it succeeded, and call error() otherwise. */ + + if (myattach (pid) != 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 + whichever we were told to attach to. */ + signal_pid = pid; + + if (!non_stop) + { + last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 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 (last_status.kind == TARGET_WAITKIND_STOPPED + && last_status.value.sig == TARGET_SIGNAL_STOP) + last_status.value.sig = 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 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, const void *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_general_set (char *own_buf) +{ + if (strncmp ("QPassSignals:", own_buf, strlen ("QPassSignals:")) == 0) + { + int numsigs = (int) TARGET_SIGNAL_LAST, i; + const char *p = own_buf + strlen ("QPassSignals:"); + CORE_ADDR cursig; + + p = decode_address_to_semicolon (&cursig, p); + for (i = 0; i < numsigs; i++) + { + if (i == cursig) + { + pass_signals[i] = 1; + if (*p == '\0') + /* Keep looping, to clear the remaining signals. */ + cursig = -1; + else + p = decode_address_to_semicolon (&cursig, p); + } + else + pass_signals[i] = 0; + } + strcpy (own_buf, "OK"); + return; + } + + if (strcmp (own_buf, "QStartNoAckMode") == 0) + { + if (remote_debug) + { + fprintf (stderr, "[noack mode enabled]\n"); + fflush (stderr); + } + + noack_mode = 1; + write_ok (own_buf); + return; + } + + if (strncmp (own_buf, "QNonStop:", 9) == 0) + { + char *mode = own_buf + 9; + int req = -1; + char *req_str; + + if (strcmp (mode, "0") == 0) + req = 0; + else if (strcmp (mode, "1") == 0) + req = 1; + else + { + /* We don't know what this mode is, so complain to + GDB. */ + fprintf (stderr, "Unknown non-stop mode requested: %s\n", + own_buf); + write_enn (own_buf); + return; + } + + req_str = req ? "non-stop" : "all-stop"; + if (start_non_stop (req) != 0) + { + fprintf (stderr, "Setting %s mode failed\n", req_str); + write_enn (own_buf); + return; + } + + non_stop = req; + + if (remote_debug) + fprintf (stderr, "[%s mode enabled]\n", req_str); + + write_ok (own_buf); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; +} + +static const char * +get_features_xml (const char *annex) +{ + /* gdbserver_xmltarget defines what to return when looking + for the "target.xml" file. Its contents can either be + verbatim XML code (prefixed with a '@') or else the name + of the actual XML file to be used in place of "target.xml". + + This variable is set up from the auto-generated + init_registers_... routine for the current target. */ + + if (gdbserver_xmltarget + && strcmp (annex, "target.xml") == 0) + { + if (*gdbserver_xmltarget == '@') + return gdbserver_xmltarget + 1; + else + annex = gdbserver_xmltarget; + } + +#ifdef USE_XML + { + extern const char *const xml_builtin[][2]; + int i; + + /* Look for the annex. */ + for (i = 0; xml_builtin[i][0] != NULL; i++) + if (strcmp (annex, xml_builtin[i][0]) == 0) + break; + + if (xml_builtin[i][0] != NULL) + return xml_builtin[i][1]; + } +#endif + + return NULL; +} + +void +monitor_show_help (void) +{ + monitor_output ("The following monitor commands are supported:\n"); + monitor_output (" set debug <0|1>\n"); + monitor_output (" Enable general debugging messages\n"); + monitor_output (" set remote-debug <0|1>\n"); + monitor_output (" Enable remote protocol debugging messages\n"); + monitor_output (" exit\n"); + monitor_output (" Quit GDBserver\n"); +} + +/* Subroutine of handle_search_memory to simplify it. */ + +static int +handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len, + gdb_byte *pattern, unsigned pattern_len, + gdb_byte *search_buf, + unsigned chunk_size, unsigned search_buf_size, + CORE_ADDR *found_addrp) +{ + /* Prime the search buffer. */ + + if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0) + { + warning ("Unable to access target memory at 0x%lx, halting search.", + (long) start_addr); + return -1; + } + + /* Perform the search. + + The loop is kept simple by allocating [N + pattern-length - 1] bytes. + When we've scanned N bytes we copy the trailing bytes to the start and + read in another N bytes. */ + + while (search_space_len >= pattern_len) + { + gdb_byte *found_ptr; + unsigned nr_search_bytes = (search_space_len < search_buf_size + ? search_space_len + : search_buf_size); + + found_ptr = memmem (search_buf, nr_search_bytes, pattern, pattern_len); + + if (found_ptr != NULL) + { + CORE_ADDR found_addr = start_addr + (found_ptr - search_buf); + *found_addrp = found_addr; + return 1; + } + + /* Not found in this chunk, skip to next chunk. */ + + /* Don't let search_space_len wrap here, it's unsigned. */ + if (search_space_len >= chunk_size) + search_space_len -= chunk_size; + else + search_space_len = 0; + + if (search_space_len >= pattern_len) + { + unsigned keep_len = search_buf_size - chunk_size; + CORE_ADDR read_addr = start_addr + keep_len; + int nr_to_read; + + /* Copy the trailing part of the previous iteration to the front + of the buffer for the next iteration. */ + memcpy (search_buf, search_buf + chunk_size, keep_len); + + nr_to_read = (search_space_len - keep_len < chunk_size + ? search_space_len - keep_len + : chunk_size); + + if (read_inferior_memory (read_addr, search_buf + keep_len, + nr_to_read) != 0) + { + warning ("Unable to access target memory at 0x%lx, halting search.", + (long) read_addr); + return -1; + } + + start_addr += chunk_size; + } + } + + /* Not found. */ + + return 0; +} + +/* Handle qSearch:memory packets. */ + +static void +handle_search_memory (char *own_buf, int packet_len) +{ + CORE_ADDR start_addr; + CORE_ADDR search_space_len; + gdb_byte *pattern; + unsigned int pattern_len; + /* NOTE: also defined in find.c testcase. */ +#define SEARCH_CHUNK_SIZE 16000 + const unsigned chunk_size = SEARCH_CHUNK_SIZE; + /* Buffer to hold memory contents for searching. */ + gdb_byte *search_buf; + unsigned search_buf_size; + int found; + CORE_ADDR found_addr; + int cmd_name_len = sizeof ("qSearch:memory:") - 1; + + pattern = malloc (packet_len); + if (pattern == NULL) + { + error ("Unable to allocate memory to perform the search"); + strcpy (own_buf, "E00"); + return; + } + if (decode_search_memory_packet (own_buf + cmd_name_len, + packet_len - cmd_name_len, + &start_addr, &search_space_len, + pattern, &pattern_len) < 0) + { + free (pattern); + error ("Error in parsing qSearch:memory packet"); + strcpy (own_buf, "E00"); + return; + } + + search_buf_size = chunk_size + pattern_len - 1; + + /* No point in trying to allocate a buffer larger than the search space. */ + if (search_space_len < search_buf_size) + search_buf_size = search_space_len; + + search_buf = malloc (search_buf_size); + if (search_buf == NULL) + { + free (pattern); + error ("Unable to allocate memory to perform the search"); + strcpy (own_buf, "E00"); + return; + } + + found = handle_search_memory_1 (start_addr, search_space_len, + pattern, pattern_len, + search_buf, chunk_size, search_buf_size, + &found_addr); + + if (found > 0) + sprintf (own_buf, "1,%lx", (long) found_addr); + else if (found == 0) + strcpy (own_buf, "0"); + else + strcpy (own_buf, "E00"); + + free (search_buf); + free (pattern); +} + +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + return; \ + } + +/* 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 && !disable_packet_qC) + { + ptid_t gdb_id; + require_running (own_buf); + + if (!ptid_equal (general_thread, null_ptid) + && !ptid_equal (general_thread, minus_one_ptid)) + gdb_id = general_thread; + else + { + thread_ptr = all_threads.head; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + } + + sprintf (own_buf, "QC"); + own_buf += 2; + own_buf = write_ptid (own_buf, gdb_id); + return; + } + + if (strcmp ("qSymbol::", own_buf) == 0) + { + if (target_running () && the_target->look_up_symbols != NULL) + (*the_target->look_up_symbols) (); + + strcpy (own_buf, "OK"); + return; + } + + if (!disable_packet_qfThreadInfo) + { + if (strcmp ("qfThreadInfo", own_buf) == 0) + { + ptid_t gdb_id; + + require_running (own_buf); + thread_ptr = all_threads.head; + + *own_buf++ = 'm'; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + write_ptid (own_buf, gdb_id); + thread_ptr = thread_ptr->next; + return; + } + + if (strcmp ("qsThreadInfo", own_buf) == 0) + { + ptid_t gdb_id; + + require_running (own_buf); + if (thread_ptr != NULL) + { + *own_buf++ = 'm'; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + write_ptid (own_buf, gdb_id); + 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; + + require_running (own_buf); + 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; + + require_running (own_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; + + require_running (own_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; + + require_running (own_buf); + + /* 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); + if (data == NULL) + { + write_enn (own_buf); + return; + } + 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; + + require_running (own_buf); + + /* 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; + + require_running (own_buf); + + /* 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); + if (document == NULL) + { + write_enn (own_buf); + return; + } + strcpy (document, "\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, " name); + strcpy (p, name); + free (name); + p = p + strlen (p); + strcpy (p, "\">base_addr); + p = p + strlen (p); + strcpy (p, "\"/>\n"); + p = p + strlen (p); + } + + strcpy (p, "\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; + } + + if (the_target->qxfer_osdata != NULL + && strncmp ("qXfer:osdata:read:", own_buf, 18) == 0) + { + char *annex; + int n; + unsigned int len; + CORE_ADDR ofs; + unsigned char *workbuf; + + strcpy (own_buf, "E00"); + if (decode_xfer_read (own_buf + 18, &annex, &ofs, &len) < 0) + return; + if (len > PBUFSIZ - 2) + len = PBUFSIZ - 2; + workbuf = malloc (len + 1); + if (!workbuf) + return; + + n = (*the_target->qxfer_osdata) (annex, workbuf, NULL, ofs, len + 1); + if (n < 0) + write_enn (own_buf); + else if (n > len) + *new_packet_len_p = write_qxfer_response (own_buf, workbuf, len, 1); + else + *new_packet_len_p = write_qxfer_response (own_buf, workbuf, n, 0); + + free (workbuf); + return; + } + + if (the_target->qxfer_siginfo != NULL + && strncmp ("qXfer:siginfo:read:", own_buf, 19) == 0) + { + unsigned char *data; + int n; + CORE_ADDR ofs; + unsigned int len; + char *annex; + + require_running (own_buf); + + /* Reject any annex; grab the offset and length. */ + if (decode_xfer_read (own_buf + 19, &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); + if (!data) + return; + n = (*the_target->qxfer_siginfo) (annex, data, NULL, ofs, 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 (the_target->qxfer_siginfo != NULL + && strncmp ("qXfer:siginfo:write:", own_buf, 20) == 0) + { + char *annex; + int n; + unsigned int len; + CORE_ADDR ofs; + unsigned char *data; + + require_running (own_buf); + + strcpy (own_buf, "E00"); + data = malloc (packet_len - 19); + if (!data) + return; + if (decode_xfer_write (own_buf + 20, packet_len - 20, &annex, + &ofs, &len, data) < 0) + { + free (data); + return; + } + + n = (*the_target->qxfer_siginfo) + (annex, NULL, (unsigned const char *)data, ofs, len); + if (n < 0) + write_enn (own_buf); + else + sprintf (own_buf, "%x", n); + + free (data); + return; + } + + /* Protocol features query. */ + if (strncmp ("qSupported", own_buf, 10) == 0 + && (own_buf[10] == ':' || own_buf[10] == '\0')) + { + char *p = &own_buf[10]; + + /* Process each feature being provided by GDB. The first + feature will follow a ':', and latter features will follow + ';'. */ + if (*p == ':') + for (p = strtok (p + 1, ";"); + p != NULL; + p = strtok (NULL, ";")) + { + /* Record if GDB knows about multiprocess support. */ + if (strcmp (p, "multiprocess+") == 0) + multi_process = 1; + } + + sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1); + + /* 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+"); + + 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 (the_target->qxfer_siginfo != NULL) + strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+"); + + /* We always report qXfer:features:read, as targets may + install XML files on a subsequent call to arch_setup. + If we reported to GDB on startup that we don't support + qXfer:feature:read at all, we will never be re-queried. */ + strcat (own_buf, ";qXfer:features:read+"); + + if (transport_is_reliable) + strcat (own_buf, ";QStartNoAckMode+"); + + if (the_target->qxfer_osdata != NULL) + strcat (own_buf, ";qXfer:osdata:read+"); + + strcat (own_buf, ";multiprocess+"); + + if (target_supports_non_stop ()) + strcat (own_buf, ";QNonStop+"); + + 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[2], address = 0; + int i, err; + ptid_t ptid = null_ptid; + + require_running (own_buf); + + 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; + } + + if (i == 0) + ptid = read_ptid (p, NULL); + else + decode_address (&parts[i - 1], p, len); + p = p2; + } + + if (p != NULL || i < 3) + err = 1; + else + { + struct thread_info *thread = find_thread_ptid (ptid); + + if (thread == NULL) + err = 2; + else + err = the_target->get_tls_address (thread, parts[0], parts[1], + &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 (mon == NULL) + { + write_enn (own_buf); + return; + } + + if ((len % 2) != 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 if (strcmp (mon, "exit") == 0) + exit_requested = 1; + else + { + monitor_output ("Unknown monitor command.\n\n"); + monitor_show_help (); + write_enn (own_buf); + } + + free (mon); + return; + } + + if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0) + { + require_running (own_buf); + handle_search_memory (own_buf, packet_len); + return; + } + + if (strcmp (own_buf, "qAttached") == 0 + || strncmp (own_buf, "qAttached:", sizeof ("qAttached:") - 1) == 0) + { + struct process_info *process; + + if (own_buf[sizeof ("qAttached") - 1]) + { + int pid = strtoul (own_buf + sizeof ("qAttached:") - 1, NULL, 16); + process = (struct process_info *) + find_inferior_id (&all_processes, pid_to_ptid (pid)); + } + else + { + require_running (own_buf); + process = current_process (); + } + + if (process == NULL) + { + write_enn (own_buf); + return; + } + + strcpy (own_buf, process->attached ? "1" : "0"); + 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 *p, *q; + int n = 0, i = 0; + struct thread_resume *resume_info; + struct thread_resume default_action = {{0}}; + + /* 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, ';'); + } + + resume_info = malloc (n * sizeof (resume_info[0])); + if (resume_info == NULL) + goto err; + + p = &own_buf[5]; + while (*p) + { + p++; + + if (p[0] == 's' || p[0] == 'S') + resume_info[i].kind = resume_step; + else if (p[0] == 'c' || p[0] == 'C') + resume_info[i].kind = resume_continue; + else if (p[0] == 't') + resume_info[i].kind = resume_stop; + 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 = minus_one_ptid; + 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] == ':') + { + ptid_t ptid = read_ptid (p + 1, &q); + + if (p == q) + goto err; + p = q; + if (p[0] != ';' && p[0] != 0) + goto err; + + resume_info[i].thread = ptid; + + i++; + } + } + + if (i < n) + resume_info[i] = default_action; + + /* Still used in occasional places in the backend. */ + if (n == 1 + && !ptid_equal (resume_info[0].thread, minus_one_ptid) + && resume_info[0].kind != resume_stop) + cont_thread = resume_info[0].thread; + else + cont_thread = minus_one_ptid; + set_desired_inferior (0); + + if (!non_stop) + enable_async_io (); + + (*the_target->resume) (resume_info, n); + + free (resume_info); + + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } + return; + +err: + write_enn (own_buf); + free (resume_info); + return; +} + +/* Attach to a new program. Return 1 if successful, 0 if failure. */ +int +handle_v_attach (char *own_buf) +{ + int pid; + + pid = strtol (own_buf + 8, NULL, 16); + if (pid != 0 && attach_inferior (pid) == 0) + { + /* Don't report shared library events after attaching, even if + some libraries are preloaded. GDB will always poll the + library list. Avoids the "stopped by shared library event" + notice on the GDB side. */ + dlls_changed = 0; + + if (non_stop) + { + /* In non-stop, we don't send a resume reply. Stop events + will follow up using the normal notification + mechanism. */ + write_ok (own_buf); + } + else + prepare_resume_reply (own_buf, last_ptid, &last_status); + + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + +/* Run a new program. Return 1 if successful, 0 if failure. */ +static int +handle_v_run (char *own_buf) +{ + char *p, *next_p, **new_argv; + int i, new_argc; + + new_argc = 0; + for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';')) + { + p++; + new_argc++; + } + + new_argv = calloc (new_argc + 2, sizeof (char *)); + if (new_argv == NULL) + { + write_enn (own_buf); + return 0; + } + + i = 0; + for (p = own_buf + strlen ("vRun;"); *p; p = next_p) + { + next_p = strchr (p, ';'); + if (next_p == NULL) + next_p = p + strlen (p); + + if (i == 0 && p == next_p) + new_argv[i] = NULL; + else + { + /* FIXME: Fail request if out of memory instead of dying. */ + new_argv[i] = xmalloc (1 + (next_p - p) / 2); + unhexify (new_argv[i], p, (next_p - p) / 2); + new_argv[i][(next_p - p) / 2] = '\0'; + } + + if (*next_p) + next_p++; + i++; + } + new_argv[i] = NULL; + + if (new_argv[0] == NULL) + { + /* GDB didn't specify a program to run. Use the program from the + last run with the new argument list. */ + + if (program_argv == NULL) + { + /* FIXME: new_argv memory leak */ + write_enn (own_buf); + return 0; + } + + new_argv[0] = strdup (program_argv[0]); + if (new_argv[0] == NULL) + { + /* FIXME: new_argv memory leak */ + write_enn (own_buf); + return 0; + } + } + + /* Free the old argv and install the new one. */ + freeargv (program_argv); + program_argv = new_argv; + + start_inferior (program_argv); + if (last_status.kind == TARGET_WAITKIND_STOPPED) + { + prepare_resume_reply (own_buf, last_ptid, &last_status); + + /* In non-stop, sending a resume reply doesn't set the general + thread, but GDB assumes a vRun sets it (this is so GDB can + query which is the main thread of the new inferior. */ + if (non_stop) + general_thread = last_ptid; + + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + +/* Kill process. Return 1 if successful, 0 if failure. */ +int +handle_v_kill (char *own_buf) +{ + int pid; + char *p = &own_buf[6]; + + pid = strtol (p, NULL, 16); + if (pid != 0 && kill_inferior (pid) == 0) + { + last_status.kind = TARGET_WAITKIND_SIGNALLED; + last_status.value.sig = TARGET_SIGNAL_KILL; + last_ptid = pid_to_ptid (pid); + discard_queued_stop_replies (pid); + write_ok (own_buf); + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + +/* Handle a 'vStopped' packet. */ +static void +handle_v_stopped (char *own_buf) +{ + /* If we're waiting for GDB to acknowledge a pending stop reply, + consider that done. */ + if (notif_queue) + { + struct vstop_notif *head; + + if (remote_debug) + fprintf (stderr, "vStopped: acking %s\n", + target_pid_to_str (notif_queue->ptid)); + + head = notif_queue; + notif_queue = notif_queue->next; + free (head); + } + + /* Push another stop reply, or if there are no more left, an OK. */ + send_next_stop_reply (own_buf); +} + +/* Handle all of the extended 'v' packets. */ +void +handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) +{ + if (!disable_packet_vCont) + { + if (strncmp (own_buf, "vCont;", 6) == 0) + { + require_running (own_buf); + handle_v_cont (own_buf); + return; + } + + if (strncmp (own_buf, "vCont?", 6) == 0) + { + strcpy (own_buf, "vCont;c;C;s;S;t"); + return; + } + } + + if (strncmp (own_buf, "vFile:", 6) == 0 + && handle_vFile (own_buf, packet_len, new_packet_len)) + return; + + if (strncmp (own_buf, "vAttach;", 8) == 0) + { + if (!multi_process && target_running ()) + { + fprintf (stderr, "Already debugging a process\n"); + write_enn (own_buf); + return; + } + handle_v_attach (own_buf); + return; + } + + if (strncmp (own_buf, "vRun;", 5) == 0) + { + if (!multi_process && target_running ()) + { + fprintf (stderr, "Already debugging a process\n"); + write_enn (own_buf); + return; + } + handle_v_run (own_buf); + return; + } + + if (strncmp (own_buf, "vKill;", 6) == 0) + { + if (!target_running ()) + { + fprintf (stderr, "No process to kill\n"); + write_enn (own_buf); + return; + } + handle_v_kill (own_buf); + return; + } + + if (strncmp (own_buf, "vStopped", 8) == 0) + { + handle_v_stopped (own_buf); + return; + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; + return; +} + +/* Resume inferior and wait for another event. In non-stop mode, + don't really wait here, but return immediatelly to the event + loop. */ +void +myresume (char *own_buf, int step, int sig) +{ + struct thread_resume resume_info[2]; + int n = 0; + int valid_cont_thread; + + set_desired_inferior (0); + + valid_cont_thread = (!ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)); + + if (step || sig || valid_cont_thread) + { + resume_info[0].thread + = ((struct inferior_list_entry *) current_inferior)->id; + if (step) + resume_info[0].kind = resume_step; + else + resume_info[0].kind = resume_continue; + resume_info[0].sig = sig; + n++; + } + + if (!valid_cont_thread) + { + resume_info[n].thread = minus_one_ptid; + resume_info[n].kind = resume_continue; + resume_info[n].sig = 0; + n++; + } + + if (!non_stop) + enable_async_io (); + + (*the_target->resume) (resume_info, n); + + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } +} + +/* Callback for for_each_inferior. Make a new stop reply for each + stopped thread. */ + +static int +queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) +{ + int pid = * (int *) arg; + + if (pid == -1 + || ptid_get_pid (entry->id) == pid) + { + struct target_waitstatus status; + + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; + + /* Pass the last stop reply back to GDB, but don't notify. */ + queue_stop_reply (entry->id, &status); + } + + return 0; +} + +/* Status handler for the '?' packet. */ + +static void +handle_status (char *own_buf) { - inferior_pid = create_inferior (argv[0], argv); - fprintf (stderr, "Process %s created; pid = %d\n", argv[0], inferior_pid); + struct target_waitstatus status; + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; - /* Wait till we are at 1st instruction in program, return signal number. */ - return mywait (statusptr); + /* In non-stop mode, we must send a stop reply for each stopped + thread. In all-stop mode, just send one for the first stopped + thread we find. */ + + if (non_stop) + { + int pid = -1; + discard_queued_stop_replies (pid); + find_inferior (&all_threads, queue_stop_reply_callback, &pid); + + /* The first is sent immediatly. OK is sent if there is no + stopped thread, which is the same handling of the vStopped + packet (by design). */ + send_next_stop_reply (own_buf); + } + else + { + if (all_threads.head) + prepare_resume_reply (own_buf, + all_threads.head->id, &status); + else + strcpy (own_buf, "W00"); + } } -extern int remote_debug; +static void +gdbserver_version (void) +{ + printf ("GNU gdbserver %s%s\n" + "Copyright (C) 2009 Free Software Foundation, Inc.\n" + "gdbserver is free software, covered by the GNU General Public License.\n" + "This gdbserver was configured as \"%s\"\n", + PKGVERSION, version, host_name); +} + +static void +gdbserver_usage (FILE *stream) +{ + fprintf (stream, "Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n" + "\tgdbserver [OPTIONS] --attach COMM PID\n" + "\tgdbserver [OPTIONS] --multi COMM\n" + "\n" + "COMM may either be a tty device (for serial debugging), or \n" + "HOST:PORT to listen for a TCP connection.\n" + "\n" + "Options:\n" + " --debug Enable general debugging output.\n" + " --remote-debug Enable remote protocol debugging output.\n" + " --version Display version information and exit.\n" + " --wrapper WRAPPER -- Run WRAPPER to start new programs.\n"); + if (REPORT_BUGS_TO[0] && stream == stdout) + fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO); +} + +static void +gdbserver_show_disableable (FILE *stream) +{ + fprintf (stream, "Disableable packets:\n" + " vCont \tAll vCont packets\n" + " qC \tQuerying the current thread\n" + " qfThreadInfo\tThread listing\n" + " Tthread \tPassing the thread specifier in the T stop reply packet\n" + " threads \tAll of the above\n"); +} + + +#undef require_running +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + break; \ + } + +static int +first_thread_of (struct inferior_list_entry *entry, void *args) +{ + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) == pid) + return 1; + + return 0; +} + +static void +kill_inferior_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + int pid = ptid_get_pid (process->head.id); + + kill_inferior (pid); + discard_queued_stop_replies (pid); +} + +/* Callback for for_each_inferior to detach or kill the inferior, + depending on whether we attached to it or not. + We inform the user whether we're detaching or killing the process + as this is only called when gdbserver is about to exit. */ + +static void +detach_or_kill_inferior_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + int pid = ptid_get_pid (process->head.id); + + if (process->attached) + detach_inferior (pid); + else + kill_inferior (pid); + + discard_queued_stop_replies (pid); +} + +/* for_each_inferior callback for detach_or_kill_for_exit to print + the pids of started inferiors. */ + +static void +print_started_pid (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + + if (! process->attached) + { + int pid = ptid_get_pid (process->head.id); + fprintf (stderr, " %d", pid); + } +} + +/* for_each_inferior callback for detach_or_kill_for_exit to print + the pids of attached inferiors. */ + +static void +print_attached_pid (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + + if (process->attached) + { + int pid = ptid_get_pid (process->head.id); + fprintf (stderr, " %d", pid); + } +} + +/* Call this when exiting gdbserver with possible inferiors that need + to be killed or detached from. */ + +static void +detach_or_kill_for_exit (void) +{ + /* First print a list of the inferiors we will be killing/detaching. + This is to assist the user, for example, in case the inferior unexpectedly + dies after we exit: did we screw up or did the inferior exit on its own? + Having this info will save some head-scratching. */ + + if (have_started_inferiors_p ()) + { + fprintf (stderr, "Killing process(es):"); + for_each_inferior (&all_processes, print_started_pid); + fprintf (stderr, "\n"); + } + if (have_attached_inferiors_p ()) + { + fprintf (stderr, "Detaching process(es):"); + for_each_inferior (&all_processes, print_attached_pid); + fprintf (stderr, "\n"); + } + + /* Now we can kill or detach the inferiors. */ + + for_each_inferior (&all_processes, detach_or_kill_inferior_callback); +} + +static void +join_inferiors_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + + /* If we are attached, then we can exit. Otherwise, we need to hang + around doing nothing, until the child is gone. */ + if (!process->attached) + join_inferior (ptid_get_pid (process->head.id)); +} int -main (argc, argv) - int argc; - char *argv[]; +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; + 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, "--remote-debug") == 0) + remote_debug = 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)) { @@ -59,203 +2001,573 @@ main (argc, argv) exit (1); } - if (argc < 3) - error ("Usage: gdbserver tty prog [args ...]"); + 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_inferiors (); + initialize_async_io (); initialize_low (); - /* Wait till we are at first instruction in program. */ - signal = start_inferior (&argv[2], &status); + own_buf = xmalloc (PBUFSIZ + 1); + mem_buf = xmalloc (PBUFSIZ); + + if (pid == 0 && *next_arg != NULL) + { + int i, n; + + n = argc - (next_arg - argv); + program_argv = xmalloc (sizeof (char *) * (n + 1)); + for (i = 0; i < n; i++) + program_argv[i] = xstrdup (next_arg[i]); + program_argv[i] = NULL; + + /* Wait till we are at first instruction in program. */ + start_inferior (program_argv); + + /* 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) == -1) + error ("Attaching not supported on this target"); + + /* Otherwise succeeded. */ + } + else + { + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.integer = 0; + last_ptid = minus_one_ptid; + } + + /* 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)) + { + detach_or_kill_for_exit (); + exit (1); + } + + if (last_status.kind == TARGET_WAITKIND_EXITED + || last_status.kind == TARGET_WAITKIND_SIGNALLED) + was_running = 0; + else + was_running = 1; - /* We are now stopped at the first instruction of the target process */ + if (!was_running && !multi_mode) + { + fprintf (stderr, "No program to debug. GDBserver exiting.\n"); + exit (1); + } while (1) { - remote_open (argv[1]); + noack_mode = 0; + multi_process = 0; + non_stop = 0; - restart: - setjmp (toplevel); - while (getpkt (own_buf) > 0) + remote_open (port); + + if (setjmp (toplevel) != 0) { - unsigned char sig; - i = 0; - ch = own_buf[i++]; - switch (ch) + /* An error occurred. */ + if (response_needed) { - 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"); + write_enn (own_buf); + putpkt (own_buf); + } + } - /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); - goto restart; - break; - } - else + /* Wait for events. This will return when all event sources are + removed from the event loop. */ + start_event_loop (); + + /* 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) + { + detach_or_kill_for_exit (); + exit (0); + } + else + fprintf (stderr, "Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + } +} + +/* Event loop callback that handles a serial event. The first byte in + the serial buffer gets us here. We expect characters to arrive at + a brisk pace, so we read the rest of the packet with a blocking + getpkt call. */ + +static void +process_serial_event (void) +{ + char ch; + int i = 0; + int signal; + unsigned int len; + CORE_ADDR mem_addr; + int pid; + unsigned char sig; + int packet_len; + int new_packet_len = -1; + + /* Used to decide when gdbserver should exit in + multi-mode/remote. */ + static int have_ran = 0; + + if (!have_ran) + have_ran = target_running (); + + disable_async_io (); + + response_needed = 0; + packet_len = getpkt (own_buf); + if (packet_len <= 0) + { + target_async (0); + remote_close (); + return; + } + 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); + + if (multi_process) + { + i++; /* skip ';' */ + pid = strtol (&own_buf[i], NULL, 16); + } + else + pid = + ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id); + + fprintf (stderr, "Detaching from process %d\n", pid); + if (detach_inferior (pid) != 0) + write_enn (own_buf); + else + { + discard_queued_stop_replies (pid); + write_ok (own_buf); + + if (extended_protocol) + { + /* Treat this like a normal program exit. */ + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.integer = 0; + last_ptid = pid_to_ptid (pid); + + current_inferior = NULL; + } + 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. */ + for_each_inferior (&all_processes, + join_inferiors_callback); + exit (0); + } + } + break; + case '!': + extended_protocol = 1; + write_ok (own_buf); + break; + case '?': + handle_status (own_buf); + break; + case 'H': + if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') + { + ptid_t gdb_id, thread_id; + int pid; + + require_running (own_buf); + + gdb_id = read_ptid (&own_buf[2], NULL); + + pid = ptid_get_pid (gdb_id); + + if (ptid_equal (gdb_id, null_ptid) + || ptid_equal (gdb_id, minus_one_ptid)) + thread_id = null_ptid; + else if (pid != 0 + && ptid_equal (pid_to_ptid (pid), + gdb_id)) + { + struct thread_info *thread = + (struct thread_info *) find_inferior (&all_threads, + first_thread_of, + &pid); + if (!thread) { - exit (0); + write_enn (own_buf); 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 + thread_id = ((struct inferior_list_entry *)thread)->id; + } + else + { + thread_id = gdb_id_to_thread_id (gdb_id); + if (ptid_equal (thread_id, null_ptid)) { - /* 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'; + write_enn (own_buf); 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 (own_buf[1] == 'g') { - 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 + if (ptid_equal (thread_id, null_ptid)) { - fprintf (stderr, "GDBserver exiting\n"); - exit (0); + /* GDB is telling us to choose any thread. Check if + the currently selected thread is still valid. If + it is not, select the first available. */ + struct thread_info *thread = + (struct thread_info *) find_inferior_id (&all_threads, + general_thread); + if (thread == NULL) + thread_id = all_threads.head->id; } + + 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); + 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); + break; + case 'c': + require_running (own_buf); + signal = 0; + myresume (own_buf, 0, signal); + break; + case 's': + require_running (own_buf); + signal = 0; + myresume (own_buf, 1, 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; + + 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]; - /* We come here when getpkt fails. + 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; - For the extended remote protocol we exit (and this is the only - way we gracefully exit!). + 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. */ + return; - For the traditional remote protocol close the connection, - and re-open it at the top of the loop. */ + fprintf (stderr, "Killing all inferiors\n"); + for_each_inferior (&all_processes, kill_inferior_callback); + + /* When using the extended protocol, we wait with no program + running. The traditional protocol will exit instead. */ if (extended_protocol) { - remote_close (); + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; + return; + } + else + { exit (0); + break; + } + case 'T': + { + ptid_t gdb_id, thread_id; + + require_running (own_buf); + + gdb_id = read_ptid (&own_buf[1], NULL); + thread_id = gdb_id_to_thread_id (gdb_id); + if (ptid_equal (thread_id, null_ptid)) + { + 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 ()) + for_each_inferior (&all_processes, + kill_inferior_callback); + fprintf (stderr, "GDBserver restarting\n"); + + /* Wait till we are at 1st instruction in prog. */ + if (program_argv != NULL) + start_inferior (program_argv); + else + { + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; + } + return; } else { - fprintf (stderr, "Remote side has terminated connection. GDBserver will reopen the connection.\n"); + /* 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, 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 (!extended_protocol && have_ran && !target_running ()) + { + /* In non-stop, defer exiting until GDB had a chance to query + the whole vStopped list (until it gets an OK). */ + if (!notif_queue) + { + fprintf (stderr, "GDBserver exiting\n"); remote_close (); + exit (0); } } } + +/* Event-loop callback for serial events. */ + +void +handle_serial_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible serial event\n"); + + /* Really handle it. */ + process_serial_event (); + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); +} + +/* Event-loop callback for target events. */ + +void +handle_target_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible target event\n"); + + last_ptid = mywait (minus_one_ptid, &last_status, + TARGET_WNOHANG, 1); + + if (last_status.kind != TARGET_WAITKIND_IGNORE) + { + /* Something interesting. Tell GDB about it. */ + push_event (last_ptid, &last_status); + } + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); +}