X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fserver.c;h=6bb36d87c3761c6f045bbb5ff5dacae27652e5af;hb=9accd112a61b0eaee2724185171761707b4f53e1;hp=92541216c706291171318ceedf6362a4f38acf3d;hpb=4c38e0a4fcb69f8586d8db0b9cdb8dbab5980811;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 92541216c7..6bb36d87c3 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1,6 +1,5 @@ /* Main code for remote server for GDB. - Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, - 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 1989-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -18,6 +17,9 @@ along with this program. If not, see . */ #include "server.h" +#include "gdbthread.h" +#include "agent.h" +#include "notif.h" #if HAVE_UNISTD_H #include @@ -25,16 +27,23 @@ #if HAVE_SIGNAL_H #include #endif -#if HAVE_SYS_WAIT_H -#include -#endif -#if HAVE_MALLOC_H -#include -#endif - +#include "gdb_wait.h" +#include "btrace-common.h" + +/* The thread set with an `Hc' packet. `Hc' is deprecated in favor of + `vCont'. Note the multi-process extensions made `vCont' a + requirement, so `Hc pPID.TID' is pretty much undefined. So + CONT_THREAD can be null_ptid for no `Hc' thread, minus_one_ptid for + resuming all threads of the process (again, `Hc' isn't used for + multi-process), or a specific thread ptid_t. + + We also set this when handling a single-thread `vCont' resume, as + some places in the backends check it to know when (and for which + thread) single-thread scheduler-locking is in effect. */ ptid_t cont_thread; + +/* The thread set with an `Hg' packet. */ ptid_t general_thread; -ptid_t step_thread; int server_waiting; @@ -42,9 +51,16 @@ static int extended_protocol; static int response_needed; static int exit_requested; +/* --once: Exit after the first connection has closed. */ +int run_once; + int multi_process; int non_stop; +/* Whether we should attempt to disable the operating system's address + space randomization feature before starting an inferior. */ +int disable_randomization = 1; + static char **program_argv, **wrapper_argv; /* Enable miscellaneous debugging output. The name is historical - it @@ -54,7 +70,9 @@ int debug_threads; /* Enable debugging of h/w breakpoint/watchpoint support. */ int debug_hw_points; -int pass_signals[TARGET_SIGNAL_LAST]; +int pass_signals[GDB_SIGNAL_LAST]; +int program_signals[GDB_SIGNAL_LAST]; +int program_signals_p; jmp_buf toplevel; @@ -98,13 +116,13 @@ 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. */ +/* A sub-class of 'struct notif_event' for stop, holding information + relative to a single stop reply. We keep a queue of these to + push to GDB in non-stop mode. */ + struct vstop_notif { - /* Pointer to next in list. */ - struct vstop_notif *next; + struct notif_event base; /* Thread or process that got the event. */ ptid_t ptid; @@ -113,64 +131,39 @@ struct vstop_notif struct target_waitstatus status; }; -/* The pending stop replies list head. */ -static struct vstop_notif *notif_queue = NULL; +DEFINE_QUEUE_P (notif_event_p); /* 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; + struct vstop_notif *new_notif = xmalloc (sizeof (*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); - } + notif_event_enque (¬if_stop, (struct notif_event *) new_notif); } -/* 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) +static int +remove_all_on_match_pid (QUEUE (notif_event_p) *q, + QUEUE_ITER (notif_event_p) *iter, + struct notif_event *event, + void *data) { - queue_stop_reply (ptid, status); + int *pid = data; - /* 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) + if (*pid == -1 + || ptid_get_pid (((struct vstop_notif *) event)->ptid) == *pid) { - char *p = own_buf; - strcpy (p, "Stop:"); - p += strlen (p); - prepare_resume_reply (p, - notif_queue->ptid, ¬if_queue->status); - putpkt_notif (own_buf); + if (q->free_func != NULL) + q->free_func (event); + + QUEUE_remove_elem (notif_event_p, q, iter); } + + return 1; } /* Get rid of the currently pending stop replies for PID. If PID is @@ -179,40 +172,23 @@ push_event (ptid_t ptid, struct target_waitstatus *status) 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; - } + QUEUE_iterate (notif_event_p, notif_stop.queue, + remove_all_on_match_pid, &pid); } -/* If there are more stop replies to push, push one now. */ - static void -send_next_stop_reply (char *own_buf) +vstop_notif_reply (struct notif_event *event, char *own_buf) { - if (notif_queue) - prepare_resume_reply (own_buf, - notif_queue->ptid, - ¬if_queue->status); - else - write_ok (own_buf); + struct vstop_notif *vstop = (struct vstop_notif *) event; + + prepare_resume_reply (own_buf, vstop->ptid, &vstop->status); } +struct notif_server notif_stop = +{ + "vStopped", "Stop", NULL, vstop_notif_reply, +}; + static int target_running (void) { @@ -241,11 +217,23 @@ start_inferior (char **argv) new_argv[count] = NULL; } + if (debug_threads) + { + int i; + for (i = 0; new_argv[i]; ++i) + fprintf (stderr, "new_argv[%d] = \"%s\"\n", i, new_argv[i]); + fflush (stderr); + } + #ifdef SIGTTOU signal (SIGTTOU, SIG_DFL); signal (SIGTTIN, SIG_DFL); #endif + /* Clear this so the backend doesn't get confused, thinking + CONT_THREAD died, and it needs to resume all threads. */ + cont_thread = null_ptid; + signal_pid = create_inferior (new_argv[0], new_argv); /* FIXME: we don't actually know at this point that the create @@ -266,13 +254,12 @@ start_inferior (char **argv) 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); + last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); if (last_status.kind != TARGET_WAITKIND_STOPPED) return signal_pid; @@ -281,11 +268,14 @@ start_inferior (char **argv) { (*the_target->resume) (&resume_info, 1); - mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); if (last_status.kind != TARGET_WAITKIND_STOPPED) return signal_pid; + + current_inferior->last_resume_kind = resume_stop; + current_inferior->last_status = last_status; } - while (last_status.value.sig != TARGET_SIGNAL_TRAP); + while (last_status.value.sig != GDB_SIGNAL_TRAP); return signal_pid; } @@ -294,6 +284,13 @@ start_inferior (char **argv) (assuming success). */ last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + if (last_status.kind != TARGET_WAITKIND_EXITED + && last_status.kind != TARGET_WAITKIND_SIGNALLED) + { + current_inferior->last_resume_kind = resume_stop; + current_inferior->last_status = last_status; + } + return signal_pid; } @@ -314,6 +311,10 @@ attach_inferior (int pid) whichever we were told to attach to. */ signal_pid = pid; + /* Clear this so the backend doesn't get confused, thinking + CONT_THREAD died, and it needs to resume all threads. */ + cont_thread = null_ptid; + if (!non_stop) { last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 0); @@ -322,8 +323,11 @@ attach_inferior (int pid) 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; + && last_status.value.sig == GDB_SIGNAL_STOP) + last_status.value.sig = GDB_SIGNAL_TRAP; + + current_inferior->last_resume_kind = resume_stop; + current_inferior->last_status = last_status; } return 0; @@ -335,8 +339,34 @@ extern int remote_debug; or -1 otherwise. */ static int -decode_xfer_read (char *buf, char **annex, CORE_ADDR *ofs, unsigned int *len) +decode_xfer_read (char *buf, CORE_ADDR *ofs, unsigned int *len) +{ + /* After the read marker and annex, qXfer looks like a + traditional 'm' packet. */ + decode_m_packet (buf, ofs, len); + + return 0; +} + +static int +decode_xfer (char *buf, char **object, char **rw, char **annex, char **offset) { + /* Extract and NUL-terminate the object. */ + *object = buf; + while (*buf && *buf != ':') + buf++; + if (*buf == '\0') + return -1; + *buf++ = 0; + + /* Extract and NUL-terminate the read/write action. */ + *rw = buf; + while (*buf && *buf != ':') + buf++; + if (*buf == '\0') + return -1; + *buf++ = 0; + /* Extract and NUL-terminate the annex. */ *annex = buf; while (*buf && *buf != ':') @@ -345,10 +375,7 @@ decode_xfer_read (char *buf, char **annex, CORE_ADDR *ofs, unsigned int *len) return -1; *buf++ = 0; - /* After the read marker and annex, qXfer looks like a - traditional 'm' packet. */ - decode_m_packet (buf, ofs, len); - + *offset = buf; return 0; } @@ -370,13 +397,96 @@ write_qxfer_response (char *buf, const void *data, int len, int is_more) PBUFSIZ - 2) + 1; } +/* Handle btrace enabling. */ + +static const char * +handle_btrace_enable (struct thread_info *thread) +{ + if (thread->btrace != NULL) + return "E.Btrace already enabled."; + + thread->btrace = target_enable_btrace (thread->entry.id); + if (thread->btrace == NULL) + return "E.Could not enable btrace."; + + return NULL; +} + +/* Handle btrace disabling. */ + +static const char * +handle_btrace_disable (struct thread_info *thread) +{ + + if (thread->btrace == NULL) + return "E.Branch tracing not enabled."; + + if (target_disable_btrace (thread->btrace) != 0) + return "E.Could not disable branch tracing."; + + thread->btrace = NULL; + return NULL; +} + +/* Handle the "Qbtrace" packet. */ + +static int +handle_btrace_general_set (char *own_buf) +{ + struct thread_info *thread; + const char *err; + char *op; + + if (strncmp ("Qbtrace:", own_buf, strlen ("Qbtrace:")) != 0) + return 0; + + op = own_buf + strlen ("Qbtrace:"); + + if (!target_supports_btrace ()) + { + strcpy (own_buf, "E.Target does not support branch tracing."); + return -1; + } + + if (ptid_equal (general_thread, null_ptid) + || ptid_equal (general_thread, minus_one_ptid)) + { + strcpy (own_buf, "E.Must select a single thread."); + return -1; + } + + thread = find_thread_ptid (general_thread); + if (thread == NULL) + { + strcpy (own_buf, "E.No such thread."); + return -1; + } + + err = NULL; + + if (strcmp (op, "bts") == 0) + err = handle_btrace_enable (thread); + else if (strcmp (op, "off") == 0) + err = handle_btrace_disable (thread); + else + err = "E.Bad Qbtrace operation. Use bts or off."; + + if (err != 0) + strcpy (own_buf, err); + else + write_ok (own_buf); + + return 1; +} + /* Handle all of the extended 'Q' packets. */ -void + +static void handle_general_set (char *own_buf) { if (strncmp ("QPassSignals:", own_buf, strlen ("QPassSignals:")) == 0) { - int numsigs = (int) TARGET_SIGNAL_LAST, i; + int numsigs = (int) GDB_SIGNAL_LAST, i; const char *p = own_buf + strlen ("QPassSignals:"); CORE_ADDR cursig; @@ -399,6 +509,33 @@ handle_general_set (char *own_buf) return; } + if (strncmp ("QProgramSignals:", own_buf, strlen ("QProgramSignals:")) == 0) + { + int numsigs = (int) GDB_SIGNAL_LAST, i; + const char *p = own_buf + strlen ("QProgramSignals:"); + CORE_ADDR cursig; + + program_signals_p = 1; + + p = decode_address_to_semicolon (&cursig, p); + for (i = 0; i < numsigs; i++) + { + if (i == cursig) + { + program_signals[i] = 1; + if (*p == '\0') + /* Keep looping, to clear the remaining signals. */ + cursig = -1; + else + p = decode_address_to_semicolon (&cursig, p); + } + else + program_signals[i] = 0; + } + strcpy (own_buf, "OK"); + return; + } + if (strcmp (own_buf, "QStartNoAckMode") == 0) { if (remote_debug) @@ -449,6 +586,58 @@ handle_general_set (char *own_buf) return; } + if (strncmp ("QDisableRandomization:", own_buf, + strlen ("QDisableRandomization:")) == 0) + { + char *packet = own_buf + strlen ("QDisableRandomization:"); + ULONGEST setting; + + unpack_varlen_hex (packet, &setting); + disable_randomization = setting; + + if (remote_debug) + { + if (disable_randomization) + fprintf (stderr, "[address space randomization disabled]\n"); + else + fprintf (stderr, "[address space randomization enabled]\n"); + } + + write_ok (own_buf); + return; + } + + if (target_supports_tracepoints () + && handle_tracepoint_general_set (own_buf)) + return; + + if (strncmp ("QAgent:", own_buf, strlen ("QAgent:")) == 0) + { + char *mode = own_buf + strlen ("QAgent:"); + int req = 0; + + if (strcmp (mode, "0") == 0) + req = 0; + else if (strcmp (mode, "1") == 0) + req = 1; + else + { + /* We don't know what this value is, so complain to GDB. */ + sprintf (own_buf, "E.Unknown QAgent value"); + return; + } + + /* Update the flag. */ + use_agent = req; + if (remote_debug) + fprintf (stderr, "[%s agent]\n", req ? "Enable" : "Disable"); + write_ok (own_buf); + return; + } + + if (handle_btrace_general_set (own_buf)) + return; + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -506,6 +695,72 @@ monitor_show_help (void) monitor_output (" Quit GDBserver\n"); } +/* Read trace frame or inferior memory. Returns the number of bytes + actually read, zero when no further transfer is possible, and -1 on + error. Return of a positive value smaller than LEN does not + indicate there's no more to be read, only the end of the transfer. + E.g., when GDB reads memory from a traceframe, a first request may + be served from a memory block that does not cover the whole request + length. A following request gets the rest served from either + another block (of the same traceframe) or from the read-only + regions. */ + +static int +gdb_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int res; + + if (current_traceframe >= 0) + { + ULONGEST nbytes; + ULONGEST length = len; + + if (traceframe_read_mem (current_traceframe, + memaddr, myaddr, len, &nbytes)) + return EIO; + /* Data read from trace buffer, we're done. */ + if (nbytes > 0) + return nbytes; + if (!in_readonly_region (memaddr, length)) + return -1; + /* Otherwise we have a valid readonly case, fall through. */ + /* (assume no half-trace half-real blocks for now) */ + } + + res = prepare_to_access_memory (); + if (res == 0) + { + res = read_inferior_memory (memaddr, myaddr, len); + done_accessing_memory (); + + return res == 0 ? len : -1; + } + else + return -1; +} + +/* Write trace frame or inferior memory. Actually, writing to trace + frames is forbidden. */ + +static int +gdb_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) +{ + if (current_traceframe >= 0) + return EIO; + else + { + int ret; + + ret = prepare_to_access_memory (); + if (ret == 0) + { + ret = write_inferior_memory (memaddr, myaddr, len); + done_accessing_memory (); + } + return ret; + } +} + /* Subroutine of handle_search_memory to simplify it. */ static int @@ -517,10 +772,12 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len, { /* Prime the search buffer. */ - if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0) + if (gdb_read_memory (start_addr, search_buf, search_buf_size) + != search_buf_size) { - warning ("Unable to access target memory at 0x%lx, halting search.", - (long) start_addr); + warning ("Unable to access %ld bytes of target " + "memory at 0x%lx, halting search.", + (long) search_buf_size, (long) start_addr); return -1; } @@ -568,11 +825,12 @@ handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len, ? search_space_len - keep_len : chunk_size); - if (read_inferior_memory (read_addr, search_buf + keep_len, - nr_to_read) != 0) + if (gdb_read_memory (read_addr, search_buf + keep_len, + nr_to_read) != search_buf_size) { - warning ("Unable to access target memory at 0x%lx, halting search.", - (long) read_addr); + warning ("Unable to access %ld bytes of target memory " + "at 0x%lx, halting search.", + (long) nr_to_read, (long) read_addr); return -1; } @@ -663,7 +921,7 @@ handle_search_memory (char *own_buf, int packet_len) /* Handle monitor commands not handled by target-specific handlers. */ static void -handle_monitor_command (char *mon) +handle_monitor_command (char *mon, char *own_buf) { if (strcmp (mon, "set debug 1") == 0) { @@ -707,408 +965,722 @@ handle_monitor_command (char *mon) } } -/* Handle all of the extended 'q' packets. */ -void -handle_query (char *own_buf, int packet_len, int *new_packet_len_p) +/* Associates a callback with each supported qXfer'able object. */ + +struct qxfer { - static struct inferior_list_entry *thread_ptr; + /* The object this handler handles. */ + const char *object; + + /* Request that the target transfer up to LEN 8-bit bytes of the + target's OBJECT. The OFFSET, for a seekable object, specifies + the starting point. The ANNEX can be used to provide additional + data-specific information to the target. + + Return the number of bytes actually transfered, zero when no + further transfer is possible, -1 on error, -2 when the transfer + is not supported, and -3 on a verbose error message that should + be preserved. Return of a positive value smaller than LEN does + not indicate the end of the object, only the end of the transfer. + + One, and only one, of readbuf or writebuf must be non-NULL. */ + int (*xfer) (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len); +}; - /* Reply the current thread id. */ - if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC) - { - ptid_t gdb_id; - require_running (own_buf); +/* Handle qXfer:auxv:read. */ - 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); - } +static int +handle_qxfer_auxv (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (the_target->read_auxv == NULL || writebuf != NULL) + return -2; - sprintf (own_buf, "QC"); - own_buf += 2; - own_buf = write_ptid (own_buf, gdb_id); - return; - } + if (annex[0] != '\0' || !target_running ()) + return -1; - if (strcmp ("qSymbol::", own_buf) == 0) - { - if (target_running () && the_target->look_up_symbols != NULL) - (*the_target->look_up_symbols) (); + return (*the_target->read_auxv) (offset, readbuf, len); +} - strcpy (own_buf, "OK"); - return; - } +/* Handle qXfer:features:read. */ - if (!disable_packet_qfThreadInfo) - { - if (strcmp ("qfThreadInfo", own_buf) == 0) - { - ptid_t gdb_id; +static int +handle_qxfer_features (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + const char *document; + size_t total_len; - require_running (own_buf); - thread_ptr = all_threads.head; + if (writebuf != NULL) + return -2; - *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 (!target_running ()) + return -1; - if (strcmp ("qsThreadInfo", own_buf) == 0) - { - ptid_t gdb_id; + /* Grab the correct annex. */ + document = get_features_xml (annex); + if (document == NULL) + return -1; - 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; - } - } - } + total_len = strlen (document); - if (the_target->read_offsets != NULL - && strcmp ("qOffsets", own_buf) == 0) - { - CORE_ADDR text, data; + if (offset > total_len) + return -1; - 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); + if (offset + len > total_len) + len = total_len - offset; - return; - } + memcpy (readbuf, document + offset, len); + return len; +} + +/* Handle qXfer:libraries:read. */ + +static int +handle_qxfer_libraries (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + unsigned int total_len; + char *document, *p; + struct inferior_list_entry *dll_ptr; + + if (writebuf != NULL) + return -2; + + if (annex[0] != '\0' || !target_running ()) + return -1; + + /* 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); - if (the_target->qxfer_spu != NULL - && strncmp ("qXfer:spu:read:", own_buf, 15) == 0) + document = malloc (total_len); + if (document == NULL) + return -1; + + strcpy (document, "\n"); + p = document + strlen (document); + + for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next) { - char *annex; - int n; - unsigned int len; - CORE_ADDR ofs; - unsigned char *spu_buf; + struct dll_info *dll = (struct dll_info *) dll_ptr; + char *name; - 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); + 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); + } - free (spu_buf); - return; + strcpy (p, "\n"); + + total_len = strlen (document); + + if (offset > total_len) + { + free (document); + return -1; } - if (the_target->qxfer_spu != NULL - && strncmp ("qXfer:spu:write:", own_buf, 16) == 0) + if (offset + len > total_len) + len = total_len - offset; + + memcpy (readbuf, document + offset, len); + free (document); + return len; +} + +/* Handle qXfer:libraries-svr4:read. */ + +static int +handle_qxfer_libraries_svr4 (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (writebuf != NULL) + return -2; + + if (annex[0] != '\0' || !target_running () + || the_target->qxfer_libraries_svr4 == NULL) + return -1; + + return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len); +} + +/* Handle qXfer:osadata:read. */ + +static int +handle_qxfer_osdata (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (the_target->qxfer_osdata == NULL || writebuf != NULL) + return -2; + + return (*the_target->qxfer_osdata) (annex, readbuf, NULL, offset, len); +} + +/* Handle qXfer:siginfo:read and qXfer:siginfo:write. */ + +static int +handle_qxfer_siginfo (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (the_target->qxfer_siginfo == NULL) + return -2; + + if (annex[0] != '\0' || !target_running ()) + return -1; + + return (*the_target->qxfer_siginfo) (annex, readbuf, writebuf, offset, len); +} + +/* Handle qXfer:spu:read and qXfer:spu:write. */ + +static int +handle_qxfer_spu (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + if (the_target->qxfer_spu == NULL) + return -2; + + if (!target_running ()) + return -1; + + return (*the_target->qxfer_spu) (annex, readbuf, writebuf, offset, len); +} + +/* Handle qXfer:statictrace:read. */ + +static int +handle_qxfer_statictrace (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + ULONGEST nbytes; + + if (writebuf != NULL) + return -2; + + if (annex[0] != '\0' || !target_running () || current_traceframe == -1) + return -1; + + if (traceframe_read_sdata (current_traceframe, offset, + readbuf, len, &nbytes)) + return -1; + return nbytes; +} + +/* Helper for handle_qxfer_threads. */ + +static void +handle_qxfer_threads_proper (struct buffer *buffer) +{ + struct inferior_list_entry *thread; + + buffer_grow_str (buffer, "\n"); + + for (thread = all_threads.head; thread; thread = thread->next) { - char *annex; - int n; - unsigned int len; - CORE_ADDR ofs; - unsigned char *spu_buf; + ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread); + char ptid_s[100]; + int core = target_core_of_thread (ptid); + char core_s[21]; - 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) + write_ptid (ptid_s, ptid); + + if (core != -1) { - free (spu_buf); - return; + sprintf (core_s, "%d", core); + buffer_xml_printf (buffer, "\n", + ptid_s, core_s); } - - 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); + { + buffer_xml_printf (buffer, "\n", + ptid_s); + } + } - free (spu_buf); - return; + buffer_grow_str0 (buffer, "\n"); +} + +/* Handle qXfer:threads:read. */ + +static int +handle_qxfer_threads (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + static char *result = 0; + static unsigned int result_length = 0; + + if (writebuf != NULL) + return -2; + + if (!target_running () || annex[0] != '\0') + return -1; + + if (offset == 0) + { + struct buffer buffer; + /* When asked for data at offset 0, generate everything and store into + 'result'. Successive reads will be served off 'result'. */ + if (result) + free (result); + + buffer_init (&buffer); + + handle_qxfer_threads_proper (&buffer); + + result = buffer_finish (&buffer); + result_length = strlen (result); + buffer_free (&buffer); } - if (the_target->read_auxv != NULL - && strncmp ("qXfer:auxv:read:", own_buf, 16) == 0) + if (offset >= result_length) { - unsigned char *data; - int n; - CORE_ADDR ofs; - unsigned int len; - char *annex; + /* We're out of data. */ + free (result); + result = NULL; + result_length = 0; + return 0; + } - require_running (own_buf); + if (len > result_length - offset) + len = result_length - offset; - /* 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; - } + memcpy (readbuf, result + offset, len); - /* 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); + return len; +} - free (data); +/* Handle qXfer:traceframe-info:read. */ - return; +static int +handle_qxfer_traceframe_info (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + static char *result = 0; + static unsigned int result_length = 0; + + if (writebuf != NULL) + return -2; + + if (!target_running () || annex[0] != '\0' || current_traceframe == -1) + return -1; + + if (offset == 0) + { + struct buffer buffer; + + /* When asked for data at offset 0, generate everything and + store into 'result'. Successive reads will be served off + 'result'. */ + free (result); + + buffer_init (&buffer); + + traceframe_read_info (current_traceframe, &buffer); + + result = buffer_finish (&buffer); + result_length = strlen (result); + buffer_free (&buffer); } - if (strncmp ("qXfer:features:read:", own_buf, 20) == 0) + if (offset >= result_length) { - CORE_ADDR ofs; - unsigned int len, total_len; - const char *document; - char *annex; + /* We're out of data. */ + free (result); + result = NULL; + result_length = 0; + return 0; + } - require_running (own_buf); + if (len > result_length - offset) + len = result_length - offset; - /* Grab the annex, offset, and length. */ - if (decode_xfer_read (own_buf + 20, &annex, &ofs, &len) < 0) - { - strcpy (own_buf, "E00"); - return; - } + memcpy (readbuf, result + offset, len); + return len; +} - /* Now grab the correct annex. */ - document = get_features_xml (annex); - if (document == NULL) - { - strcpy (own_buf, "E00"); - return; - } +/* Handle qXfer:fdpic:read. */ - total_len = strlen (document); - if (len > PBUFSIZ - 2) - len = PBUFSIZ - 2; +static int +handle_qxfer_fdpic (const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, LONGEST len) +{ + if (the_target->read_loadmap == NULL) + return -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); + if (!target_running ()) + return -1; - return; + return (*the_target->read_loadmap) (annex, offset, readbuf, len); +} + +/* Handle qXfer:btrace:read. */ + +static int +handle_qxfer_btrace (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + static struct buffer cache; + struct thread_info *thread; + int type; + + if (the_target->read_btrace == NULL || writebuf != NULL) + return -2; + + if (!target_running ()) + return -1; + + if (ptid_equal (general_thread, null_ptid) + || ptid_equal (general_thread, minus_one_ptid)) + { + strcpy (own_buf, "E.Must select a single thread."); + return -3; } - if (strncmp ("qXfer:libraries:read:", own_buf, 21) == 0) + thread = find_thread_ptid (general_thread); + if (thread == NULL) { - CORE_ADDR ofs; - unsigned int len, total_len; - char *document, *p; - struct inferior_list_entry *dll_ptr; - char *annex; + strcpy (own_buf, "E.No such thread."); + return -3; + } - require_running (own_buf); + if (thread->btrace == NULL) + { + strcpy (own_buf, "E.Btrace not enabled."); + return -3; + } + + if (strcmp (annex, "all") == 0) + type = btrace_read_all; + else if (strcmp (annex, "new") == 0) + type = btrace_read_new; + else + { + strcpy (own_buf, "E.Bad annex."); + return -3; + } + + if (offset == 0) + { + buffer_free (&cache); + + target_read_btrace (thread->btrace, &cache, type); + } + else if (offset > cache.used_size) + { + buffer_free (&cache); + return -3; + } + + if (len > cache.used_size - offset) + len = cache.used_size - offset; + + memcpy (readbuf, cache.buffer + offset, len); - /* Reject any annex; grab the offset and length. */ - if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0 - || annex[0] != '\0') + return len; +} + +static const struct qxfer qxfer_packets[] = + { + { "auxv", handle_qxfer_auxv }, + { "btrace", handle_qxfer_btrace }, + { "fdpic", handle_qxfer_fdpic}, + { "features", handle_qxfer_features }, + { "libraries", handle_qxfer_libraries }, + { "libraries-svr4", handle_qxfer_libraries_svr4 }, + { "osdata", handle_qxfer_osdata }, + { "siginfo", handle_qxfer_siginfo }, + { "spu", handle_qxfer_spu }, + { "statictrace", handle_qxfer_statictrace }, + { "threads", handle_qxfer_threads }, + { "traceframe-info", handle_qxfer_traceframe_info }, + }; + +static int +handle_qxfer (char *own_buf, int packet_len, int *new_packet_len_p) +{ + int i; + char *object; + char *rw; + char *annex; + char *offset; + + if (strncmp (own_buf, "qXfer:", 6) != 0) + return 0; + + /* Grab the object, r/w and annex. */ + if (decode_xfer (own_buf + 6, &object, &rw, &annex, &offset) < 0) + { + write_enn (own_buf); + return 1; + } + + for (i = 0; + i < sizeof (qxfer_packets) / sizeof (qxfer_packets[0]); + i++) + { + const struct qxfer *q = &qxfer_packets[i]; + + if (strcmp (object, q->object) == 0) { - strcpy (own_buf, "E00"); - return; + if (strcmp (rw, "read") == 0) + { + unsigned char *data; + int n; + CORE_ADDR ofs; + unsigned int len; + + /* Grab the offset and length. */ + if (decode_xfer_read (offset, &ofs, &len) < 0) + { + write_enn (own_buf); + return 1; + } + + /* 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 1; + } + n = (*q->xfer) (annex, data, NULL, ofs, len + 1); + if (n == -2) + { + free (data); + return 0; + } + else if (n == -3) + { + /* Preserve error message. */ + } + else 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 1; + } + else if (strcmp (rw, "write") == 0) + { + int n; + unsigned int len; + CORE_ADDR ofs; + unsigned char *data; + + strcpy (own_buf, "E00"); + data = malloc (packet_len - (offset - own_buf)); + if (data == NULL) + { + write_enn (own_buf); + return 1; + } + if (decode_xfer_write (offset, packet_len - (offset - own_buf), + &ofs, &len, data) < 0) + { + free (data); + write_enn (own_buf); + return 1; + } + + n = (*q->xfer) (annex, NULL, data, ofs, len); + if (n == -2) + { + free (data); + return 0; + } + else if (n == -3) + { + /* Preserve error message. */ + } + else if (n < 0) + write_enn (own_buf); + else + sprintf (own_buf, "%x", n); + + free (data); + return 1; + } + + return 0; } + } + + return 0; +} - /* 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); +/* Table used by the crc32 function to calcuate the checksum. */ - document = malloc (total_len); - if (document == NULL) +static unsigned int crc32_table[256] = +{0, 0}; + +/* Compute 32 bit CRC from inferior memory. + + On success, return 32 bit CRC. + On failure, return (unsigned long long) -1. */ + +static unsigned long long +crc32 (CORE_ADDR base, int len, unsigned int crc) +{ + if (!crc32_table[1]) + { + /* Initialize the CRC table and the decoding table. */ + int i, j; + unsigned int c; + + for (i = 0; i < 256; i++) { - write_enn (own_buf); - return; + for (c = i << 24, j = 8; j > 0; --j) + c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1); + crc32_table[i] = c; } - 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; + while (len--) + { + unsigned char byte = 0; - 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); - } + /* Return failure if memory read fails. */ + if (read_inferior_memory (base, &byte, 1) != 0) + return (unsigned long long) -1; - strcpy (p, "\n"); + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ byte) & 255]; + base++; + } + return (unsigned long long) crc; +} - total_len = strlen (document); - if (len > PBUFSIZ - 2) - len = PBUFSIZ - 2; +/* Handle all of the extended 'q' packets. */ - 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); +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 - *new_packet_len_p = write_qxfer_response (own_buf, document + ofs, - total_len - ofs, 0); + { + thread_ptr = all_threads.head; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + } - free (document); + sprintf (own_buf, "QC"); + own_buf += 2; + write_ptid (own_buf, gdb_id); return; } - if (the_target->qxfer_osdata != NULL - && strncmp ("qXfer:osdata:read:", own_buf, 18) == 0) + if (strcmp ("qSymbol::", own_buf) == 0) { - char *annex; - int n; - unsigned int len; - CORE_ADDR ofs; - unsigned char *workbuf; + /* GDB is suggesting new symbols have been loaded. This may + mean a new shared library has been detected as loaded, so + take the opportunity to check if breakpoints we think are + inserted, still are. Note that it isn't guaranteed that + we'll see this when a shared library is loaded, and nor will + we see this for unloads (although breakpoints in unloaded + libraries shouldn't trigger), as GDB may not find symbols for + the library at all. We also re-validate breakpoints when we + see a second GDB breakpoint for the same address, and or when + we access breakpoint shadows. */ + validate_breakpoints (); + + if (target_supports_tracepoints ()) + tracepoint_look_up_symbols (); - 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); + if (target_running () && the_target->look_up_symbols != NULL) + (*the_target->look_up_symbols) (); - free (workbuf); + strcpy (own_buf, "OK"); return; } - if (the_target->qxfer_siginfo != NULL - && strncmp ("qXfer:siginfo:read:", own_buf, 19) == 0) + if (!disable_packet_qfThreadInfo) { - unsigned char *data; - int n; - CORE_ADDR ofs; - unsigned int len; - char *annex; + if (strcmp ("qfThreadInfo", own_buf) == 0) + { + ptid_t gdb_id; - require_running (own_buf); + require_running (own_buf); + thread_ptr = all_threads.head; - /* 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"); + *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; } - /* 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); + if (strcmp ("qsThreadInfo", own_buf) == 0) + { + ptid_t gdb_id; - free (data); - return; + 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->qxfer_siginfo != NULL - && strncmp ("qXfer:siginfo:write:", own_buf, 20) == 0) + if (the_target->read_offsets != NULL + && strcmp ("qOffsets", own_buf) == 0) { - char *annex; - int n; - unsigned int len; - CORE_ADDR ofs; - unsigned char *data; + CORE_ADDR text, 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); + if (the_target->read_offsets (&text, &data)) + sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", + (long)text, (long)data, (long)data); else - sprintf (own_buf, "%x", n); + write_enn (own_buf); - free (data); return; } @@ -1117,29 +1689,67 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) && (own_buf[10] == ':' || own_buf[10] == '\0')) { char *p = &own_buf[10]; + int gdb_supports_qRelocInsn = 0; + + /* Start processing qSupported packet. */ + target_process_qsupported (NULL); /* 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, ";")) - { - if (strcmp (p, "multiprocess+") == 0) - { - /* GDB supports and wants multi-process support if - possible. */ - if (target_supports_multi_process ()) - multi_process = 1; - } - } + { + char **qsupported = NULL; + int count = 0; + int i; + + /* Two passes, to avoid nested strtok calls in + target_process_qsupported. */ + for (p = strtok (p + 1, ";"); + p != NULL; + p = strtok (NULL, ";")) + { + count++; + qsupported = xrealloc (qsupported, count * sizeof (char *)); + qsupported[count - 1] = xstrdup (p); + } + + for (i = 0; i < count; i++) + { + p = qsupported[i]; + if (strcmp (p, "multiprocess+") == 0) + { + /* GDB supports and wants multi-process support if + possible. */ + if (target_supports_multi_process ()) + multi_process = 1; + } + else if (strcmp (p, "qRelocInsn+") == 0) + { + /* GDB supports relocate instruction requests. */ + gdb_supports_qRelocInsn = 1; + } + else + target_process_qsupported (p); + + free (p); + } + + free (qsupported); + } - sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1); + sprintf (own_buf, + "PacketSize=%x;QPassSignals+;QProgramSignals+", + 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->qxfer_libraries_svr4 != NULL) + strcat (own_buf, ";qXfer:libraries-svr4:read+"); + else + { + /* We do not have any hook to indicate whether the non-SVR4 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+"); @@ -1150,6 +1760,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->qxfer_siginfo != NULL) strcat (own_buf, ";qXfer:siginfo:read+;qXfer:siginfo:write+"); + if (the_target->read_loadmap != NULL) + strcat (own_buf, ";qXfer:fdpic:read+"); + /* 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 @@ -1168,6 +1781,42 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_non_stop ()) strcat (own_buf, ";QNonStop+"); + if (target_supports_disable_randomization ()) + strcat (own_buf, ";QDisableRandomization+"); + + strcat (own_buf, ";qXfer:threads:read+"); + + if (target_supports_tracepoints ()) + { + strcat (own_buf, ";ConditionalTracepoints+"); + strcat (own_buf, ";TraceStateVariables+"); + strcat (own_buf, ";TracepointSource+"); + strcat (own_buf, ";DisconnectedTracing+"); + if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ()) + strcat (own_buf, ";FastTracepoints+"); + strcat (own_buf, ";StaticTracepoints+"); + strcat (own_buf, ";InstallInTrace+"); + strcat (own_buf, ";qXfer:statictrace:read+"); + strcat (own_buf, ";qXfer:traceframe-info:read+"); + strcat (own_buf, ";EnableDisableTracepoints+"); + strcat (own_buf, ";QTBuffer:size+"); + strcat (own_buf, ";tracenz+"); + } + + /* Support target-side breakpoint conditions and commands. */ + strcat (own_buf, ";ConditionalBreakpoints+"); + strcat (own_buf, ";BreakpointCommands+"); + + if (target_supports_agent ()) + strcat (own_buf, ";QAgent+"); + + if (target_supports_btrace ()) + { + strcat (own_buf, ";Qbtrace:bts+"); + strcat (own_buf, ";Qbtrace:off+"); + strcat (own_buf, ";qXfer:btrace:read+"); + } + return; } @@ -1224,7 +1873,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (err == 0) { - sprintf (own_buf, "%llx", address); + strcpy (own_buf, paddress(address)); return; } else if (err > 0) @@ -1236,6 +1885,29 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* Otherwise, pretend we do not understand this packet. */ } + /* Windows OS Thread Information Block address support. */ + if (the_target->get_tib_address != NULL + && strncmp ("qGetTIBAddr:", own_buf, 12) == 0) + { + char *annex; + int n; + CORE_ADDR tlb; + ptid_t ptid = read_ptid (own_buf + 12, &annex); + + n = (*the_target->get_tib_address) (ptid, &tlb); + if (n == 1) + { + strcpy (own_buf, paddress(tlb)); + return; + } + else if (n == 0) + { + write_enn (own_buf); + return; + } + return; + } + /* Handle "monitor" commands. */ if (strncmp ("qRcmd,", own_buf, 6) == 0) { @@ -1261,13 +1933,14 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (the_target->handle_monitor_command == NULL || (*the_target->handle_monitor_command) (mon) == 0) /* Default processing. */ - handle_monitor_command (mon); + handle_monitor_command (mon, own_buf); free (mon); return; } - if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0) + if (strncmp ("qSearch:memory:", own_buf, + sizeof ("qSearch:memory:") - 1) == 0) { require_running (own_buf); handle_search_memory (own_buf, packet_len); @@ -1301,11 +1974,46 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) return; } + if (strncmp ("qCRC:", own_buf, 5) == 0) + { + /* CRC check (compare-section). */ + char *comma; + ULONGEST base; + int len; + unsigned long long crc; + + require_running (own_buf); + comma = unpack_varlen_hex (own_buf + 5, &base); + if (*comma++ != ',') + { + write_enn (own_buf); + return; + } + len = strtoul (comma, NULL, 16); + crc = crc32 (base, len, 0xffffffff); + /* Check for memory failure. */ + if (crc == (unsigned long long) -1) + { + write_enn (own_buf); + return; + } + sprintf (own_buf, "C%lx", (unsigned long) crc); + return; + } + + if (handle_qxfer (own_buf, packet_len, new_packet_len_p)) + return; + + if (target_supports_tracepoints () && handle_tracepoint_query (own_buf)) + return; + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; } +static void gdb_wants_all_threads_stopped (void); + /* Parse vCont packets. */ void handle_v_cont (char *own_buf) @@ -1351,9 +2059,9 @@ handle_v_cont (char *own_buf) goto err; p = q; - if (!target_signal_to_host_p (sig)) + if (!gdb_signal_to_host_p (sig)) goto err; - resume_info[i].sig = target_signal_to_host (sig); + resume_info[i].sig = gdb_signal_to_host (sig); } else { @@ -1388,9 +2096,13 @@ handle_v_cont (char *own_buf) if (i < n) resume_info[i] = default_action; - /* Still used in occasional places in the backend. */ + /* `cont_thread' is still used in occasional places in the backend, + to implement single-thread scheduler-locking. Doesn't make sense + to set it if we see a stop request, or a wildcard action (one + with '-1' (all threads), or 'pPID.-1' (all threads of PID)). */ if (n == 1 - && !ptid_equal (resume_info[0].thread, minus_one_ptid) + && !(ptid_equal (resume_info[0].thread, minus_one_ptid) + || ptid_get_lwp (resume_info[0].thread) == -1) && resume_info[0].kind != resume_stop) cont_thread = resume_info[0].thread; else @@ -1409,8 +2121,22 @@ handle_v_cont (char *own_buf) else { last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + + if (last_status.kind != TARGET_WAITKIND_EXITED + && last_status.kind != TARGET_WAITKIND_SIGNALLED) + current_inferior->last_status = last_status; + + /* From the client's perspective, all-stop mode always stops all + threads implicitly (and the target backend has already done + so by now). Tag all threads as "want-stopped", so we don't + resume them implicitly without the client telling us to. */ + gdb_wants_all_threads_stopped (); prepare_resume_reply (own_buf, last_ptid, &last_status); disable_async_io (); + + if (last_status.kind == TARGET_WAITKIND_EXITED + || last_status.kind == TARGET_WAITKIND_SIGNALLED) + mourn_inferior (find_process_pid (ptid_get_pid (last_ptid))); } return; @@ -1505,16 +2231,16 @@ handle_v_run (char *own_buf) if (program_argv == NULL) { - /* FIXME: new_argv memory leak */ write_enn (own_buf); + freeargv (new_argv); return 0; } new_argv[0] = strdup (program_argv[0]); if (new_argv[0] == NULL) { - /* FIXME: new_argv memory leak */ write_enn (own_buf); + freeargv (new_argv); return 0; } } @@ -1556,7 +2282,7 @@ handle_v_kill (char *own_buf) if (pid != 0 && kill_inferior (pid) == 0) { last_status.kind = TARGET_WAITKIND_SIGNALLED; - last_status.value.sig = TARGET_SIGNAL_KILL; + last_status.value.sig = GDB_SIGNAL_KILL; last_ptid = pid_to_ptid (pid); discard_queued_stop_replies (pid); write_ok (own_buf); @@ -1569,29 +2295,6 @@ handle_v_kill (char *own_buf) } } -/* 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) @@ -1618,7 +2321,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) if (strncmp (own_buf, "vAttach;", 8) == 0) { - if (!multi_process && target_running ()) + if ((!extended_protocol || !multi_process) && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); @@ -1630,7 +2333,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) if (strncmp (own_buf, "vRun;", 5) == 0) { - if (!multi_process && target_running ()) + if ((!extended_protocol || !multi_process) && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); @@ -1652,11 +2355,8 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) return; } - if (strncmp (own_buf, "vStopped", 8) == 0) - { - handle_v_stopped (own_buf); - return; - } + if (handle_notif_ack (own_buf, packet_len)) + return; /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ @@ -1667,7 +2367,7 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) /* Resume inferior and wait for another event. In non-stop mode, don't really wait here, but return immediatelly to the event loop. */ -void +static void myresume (char *own_buf, int step, int sig) { struct thread_resume resume_info[2]; @@ -1681,8 +2381,7 @@ myresume (char *own_buf, int step, int sig) if (step || sig || valid_cont_thread) { - resume_info[0].thread - = ((struct inferior_list_entry *) current_inferior)->id; + resume_info[0].thread = current_ptid; if (step) resume_info[0].kind = resume_step; else @@ -1709,8 +2408,20 @@ myresume (char *own_buf, int step, int sig) else { last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + + if (last_status.kind != TARGET_WAITKIND_EXITED + && last_status.kind != TARGET_WAITKIND_SIGNALLED) + { + current_inferior->last_resume_kind = resume_stop; + current_inferior->last_status = last_status; + } + prepare_resume_reply (own_buf, last_ptid, &last_status); disable_async_io (); + + if (last_status.kind == TARGET_WAITKIND_EXITED + || last_status.kind == TARGET_WAITKIND_SIGNALLED) + mourn_inferior (find_process_pid (ptid_get_pid (last_ptid))); } } @@ -1720,31 +2431,87 @@ myresume (char *own_buf, int step, int sig) static int queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) { - int pid = * (int *) arg; + struct thread_info *thread = (struct thread_info *) entry; - if (pid == -1 - || ptid_get_pid (entry->id) == pid) + /* For now, assume targets that don't have this callback also don't + manage the thread's last_status field. */ + if (the_target->thread_stopped == NULL) + { + struct vstop_notif *new_notif = xmalloc (sizeof (*new_notif)); + + new_notif->ptid = entry->id; + new_notif->status = thread->last_status; + /* Pass the last stop reply back to GDB, but don't notify + yet. */ + notif_event_enque (¬if_stop, + (struct notif_event *) new_notif); + } + else { - struct target_waitstatus status; + if (thread_stopped (thread)) + { + if (debug_threads) + fprintf (stderr, + "Reporting thread %s as already stopped with %s\n", + target_pid_to_str (entry->id), + target_waitstatus_to_string (&thread->last_status)); - status.kind = TARGET_WAITKIND_STOPPED; - status.value.sig = TARGET_SIGNAL_TRAP; + gdb_assert (thread->last_status.kind != TARGET_WAITKIND_IGNORE); - /* Pass the last stop reply back to GDB, but don't notify. */ - queue_stop_reply (entry->id, &status); + /* Pass the last stop reply back to GDB, but don't notify + yet. */ + queue_stop_reply (entry->id, &thread->last_status); + } } return 0; } +/* Set this inferior threads's state as "want-stopped". We won't + resume this thread until the client gives us another action for + it. */ + +static void +gdb_wants_thread_stopped (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + + thread->last_resume_kind = resume_stop; + + if (thread->last_status.kind == TARGET_WAITKIND_IGNORE) + { + /* Most threads are stopped implicitly (all-stop); tag that with + signal 0. */ + thread->last_status.kind = TARGET_WAITKIND_STOPPED; + thread->last_status.value.sig = GDB_SIGNAL_0; + } +} + +/* Set all threads' states as "want-stopped". */ + +static void +gdb_wants_all_threads_stopped (void) +{ + for_each_inferior (&all_threads, gdb_wants_thread_stopped); +} + +/* Clear the gdb_detached flag of every process. */ + +static void +gdb_reattached_process (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + + process->gdb_detached = 0; +} + /* Status handler for the '?' packet. */ static void handle_status (char *own_buf) { - struct target_waitstatus status; - status.kind = TARGET_WAITKIND_STOPPED; - status.value.sig = TARGET_SIGNAL_TRAP; + /* GDB is connected, don't forward events to the target anymore. */ + for_each_inferior (&all_processes, gdb_reattached_process); /* 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 @@ -1752,20 +2519,29 @@ handle_status (char *own_buf) if (non_stop) { - int pid = -1; - discard_queued_stop_replies (pid); - find_inferior (&all_threads, queue_stop_reply_callback, &pid); + discard_queued_stop_replies (-1); + find_inferior (&all_threads, queue_stop_reply_callback, NULL); /* 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); + notif_write_event (¬if_stop, own_buf); } else { + pause_all (0); + stabilize_threads (); + gdb_wants_all_threads_stopped (); + if (all_threads.head) - prepare_resume_reply (own_buf, - all_threads.head->id, &status); + { + struct target_waitstatus status; + + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = GDB_SIGNAL_TRAP; + prepare_resume_reply (own_buf, + all_threads.head->id, &status); + } else strcpy (own_buf, "W00"); } @@ -1775,8 +2551,9 @@ static void gdbserver_version (void) { printf ("GNU gdbserver %s%s\n" - "Copyright (C) 2010 Free Software Foundation, Inc.\n" - "gdbserver is free software, covered by the GNU General Public License.\n" + "Copyright (C) 2013 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); } @@ -1795,7 +2572,9 @@ gdbserver_usage (FILE *stream) " --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"); + " --wrapper WRAPPER -- Run WRAPPER to start new programs.\n" + " --once Exit after the first connection has " + "closed.\n"); if (REPORT_BUGS_TO[0] && stream == stdout) fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO); } @@ -1807,7 +2586,8 @@ gdbserver_show_disableable (FILE *stream) " 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" + " Tthread \tPassing the thread specifier in the " + "T stop reply packet\n" " threads \tAll of the above\n"); } @@ -1919,17 +2699,6 @@ detach_or_kill_for_exit (void) 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 (int argc, char *argv[]) { @@ -1937,8 +2706,8 @@ main (int argc, char *argv[]) int pid; char *arg_end, *port; char **next_arg = &argv[1]; - int multi_mode = 0; - int attach = 0; + volatile int multi_mode = 0; + volatile int attach = 0; int was_running; while (*next_arg != NULL && **next_arg == '-') @@ -2018,6 +2787,19 @@ main (int argc, char *argv[]) } } } + else if (strcmp (*next_arg, "-") == 0) + { + /* "-" specifies a stdio connection and is a form of port + specification. */ + *next_arg = STDIO_CONNECTION_NAME; + break; + } + else if (strcmp (*next_arg, "--disable-randomization") == 0) + disable_randomization = 1; + else if (strcmp (*next_arg, "--no-disable-randomization") == 0) + disable_randomization = 0; + else if (strcmp (*next_arg, "--once") == 0) + run_once = 1; else { fprintf (stderr, "Unknown argument: %s\n", *next_arg); @@ -2042,6 +2824,12 @@ main (int argc, char *argv[]) exit (1); } + /* We need to know whether the remote connection is stdio before + starting the inferior. Inferiors created in this scenario have + stdin,stdout redirected. So do this here before we call + start_inferior. */ + remote_prepare (port); + bad_attach = 0; pid = 0; @@ -2067,9 +2855,11 @@ main (int argc, char *argv[]) exit (1); } - initialize_inferiors (); initialize_async_io (); initialize_low (); + initialize_event_loop (); + if (target_supports_tracepoints ()) + initialize_tracepoint (); own_buf = xmalloc (PBUFSIZ + 1); mem_buf = xmalloc (PBUFSIZ); @@ -2105,6 +2895,8 @@ main (int argc, char *argv[]) last_ptid = minus_one_ptid; } + initialize_notif (); + /* 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. */ @@ -2112,7 +2904,14 @@ main (int argc, char *argv[]) if (setjmp (toplevel)) { - detach_or_kill_for_exit (); + /* If something fails and longjmps while detaching or killing + inferiors, we'd end up here again, stuck in an infinite loop + trap. Be sure that if that happens, we exit immediately + instead. */ + if (setjmp (toplevel) == 0) + detach_or_kill_for_exit (); + else + fprintf (stderr, "Detach or kill failed. Exiting\n"); exit (1); } @@ -2132,7 +2931,8 @@ main (int argc, char *argv[]) { noack_mode = 0; multi_process = 0; - non_stop = 0; + /* Be sure we're out of tfind mode. */ + current_traceframe = -1; remote_open (port); @@ -2147,7 +2947,7 @@ main (int argc, char *argv[]) } /* Wait for events. This will return when all event sources are - removed from the event loop. */ + removed from the event loop. */ start_event_loop (); /* If an exit was requested (using the "monitor exit" command), @@ -2155,15 +2955,104 @@ main (int argc, char *argv[]) getpkt to fail; close the connection and reopen it at the top of the loop. */ - if (exit_requested) + if (exit_requested || run_once) { - detach_or_kill_for_exit (); - exit (0); + /* If something fails and longjmps while detaching or + killing inferiors, we'd end up here again, stuck in an + infinite loop trap. Be sure that if that happens, we + exit immediately instead. */ + if (setjmp (toplevel) == 0) + { + detach_or_kill_for_exit (); + exit (0); + } + else + { + fprintf (stderr, "Detach or kill failed. Exiting\n"); + exit (1); + } + } + + fprintf (stderr, + "Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + + if (tracing) + { + if (disconnected_tracing) + { + /* Try to enable non-stop/async mode, so we we can both + wait for an async socket accept, and handle async + target events simultaneously. There's also no point + either in having the target always stop all threads, + when we're going to pass signals down without + informing GDB. */ + if (!non_stop) + { + if (start_non_stop (1)) + non_stop = 1; + + /* Detaching implicitly resumes all threads; simply + disconnecting does not. */ + } + } + else + { + fprintf (stderr, + "Disconnected tracing disabled; stopping trace run.\n"); + stop_tracing (); + } + } + } +} + +/* Process options coming from Z packets for *point at address + POINT_ADDR. PACKET is the packet buffer. *PACKET is updated + to point to the first char after the last processed option. */ + +static void +process_point_options (CORE_ADDR point_addr, char **packet) +{ + char *dataptr = *packet; + int persist; + + /* Check if data has the correct format. */ + if (*dataptr != ';') + return; + + dataptr++; + + while (*dataptr) + { + if (*dataptr == ';') + ++dataptr; + + if (*dataptr == 'X') + { + /* Conditional expression. */ + if (debug_threads) + fprintf (stderr, "Found breakpoint condition.\n"); + add_breakpoint_condition (point_addr, &dataptr); + } + else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0) + { + dataptr += strlen ("cmds:"); + if (debug_threads) + fprintf (stderr, "Found breakpoint commands %s.\n", dataptr); + persist = (*dataptr == '1'); + dataptr += 2; + add_breakpoint_commands (point_addr, &dataptr, persist); } else - fprintf (stderr, "Remote side has terminated connection. " - "GDBserver will reopen the connection.\n"); + { + fprintf (stderr, "Unknown token %c, ignoring.\n", + *dataptr); + /* Skip tokens until we find one that we recognize. */ + while (*dataptr && *dataptr != ';') + dataptr++; + } } + *packet = dataptr; } /* Event loop callback that handles a serial event. The first byte in @@ -2171,13 +3060,14 @@ main (int argc, char *argv[]) a brisk pace, so we read the rest of the packet with a blocking getpkt call. */ -static void +static int process_serial_event (void) { char ch; int i = 0; int signal; unsigned int len; + int res; CORE_ADDR mem_addr; int pid; unsigned char sig; @@ -2197,9 +3087,9 @@ process_serial_event (void) packet_len = getpkt (own_buf); if (packet_len <= 0) { - target_async (0); remote_close (); - return; + /* Force an event loop break. */ + return -1; } response_needed = 1; @@ -2222,10 +3112,57 @@ process_serial_event (void) pid = strtol (&own_buf[i], NULL, 16); } else - pid = - ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id); + pid = ptid_get_pid (current_ptid); + + if ((tracing && disconnected_tracing) || any_persistent_commands ()) + { + struct thread_resume resume_info; + struct process_info *process = find_process_pid (pid); + + if (process == NULL) + { + write_enn (own_buf); + break; + } + + if (tracing && disconnected_tracing) + fprintf (stderr, + "Disconnected tracing in effect, " + "leaving gdbserver attached to the process\n"); + + if (any_persistent_commands ()) + fprintf (stderr, + "Persistent commands are present, " + "leaving gdbserver attached to the process\n"); + + /* Make sure we're in non-stop/async mode, so we we can both + wait for an async socket accept, and handle async target + events simultaneously. There's also no point either in + having the target stop all threads, when we're going to + pass signals down without informing GDB. */ + if (!non_stop) + { + if (debug_threads) + fprintf (stderr, "Forcing non-stop mode\n"); + + non_stop = 1; + start_non_stop (1); + } + + process->gdb_detached = 1; + + /* Detaching implicitly resumes all threads. */ + resume_info.thread = minus_one_ptid; + resume_info.kind = resume_continue; + resume_info.sig = 0; + (*the_target->resume) (&resume_info, 1); + + write_ok (own_buf); + break; /* from switch/case */ + } fprintf (stderr, "Detaching from process %d\n", pid); + stop_tracing (); if (detach_inferior (pid) != 0) write_enn (own_buf); else @@ -2250,8 +3187,7 @@ process_serial_event (void) /* 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); + join_inferior (pid); exit (0); } } @@ -2323,8 +3259,6 @@ process_serial_event (void) } else if (own_buf[1] == 'c') cont_thread = thread_id; - else if (own_buf[1] == 's') - step_thread = thread_id; write_ok (own_buf); } @@ -2337,27 +3271,53 @@ process_serial_event (void) break; case 'g': require_running (own_buf); - set_desired_inferior (1); - registers_to_string (own_buf); + if (current_traceframe >= 0) + { + struct regcache *regcache = new_register_cache (); + + if (fetch_traceframe_registers (current_traceframe, + regcache, -1) == 0) + registers_to_string (regcache, own_buf); + else + write_enn (own_buf); + free_register_cache (regcache); + } + else + { + struct regcache *regcache; + + set_desired_inferior (1); + regcache = get_thread_regcache (current_inferior, 1); + registers_to_string (regcache, own_buf); + } break; case 'G': require_running (own_buf); - set_desired_inferior (1); - registers_from_string (&own_buf[1]); - write_ok (own_buf); + if (current_traceframe >= 0) + write_enn (own_buf); + else + { + struct regcache *regcache; + + set_desired_inferior (1); + regcache = get_thread_regcache (current_inferior, 1); + registers_from_string (regcache, &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 + res = gdb_read_memory (mem_addr, mem_buf, len); + if (res < 0) write_enn (own_buf); + else + convert_int_to_ascii (mem_buf, own_buf, res); 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) + decode_M_packet (&own_buf[1], &mem_addr, &len, &mem_buf); + if (gdb_write_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); else write_enn (own_buf); @@ -2365,8 +3325,8 @@ process_serial_event (void) 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) + &mem_addr, &len, &mem_buf) < 0 + || gdb_write_memory (mem_addr, mem_buf, len) != 0) write_enn (own_buf); else write_ok (own_buf); @@ -2374,8 +3334,8 @@ process_serial_event (void) 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); + if (gdb_signal_to_host_p (sig)) + signal = gdb_signal_to_host (sig); else signal = 0; myresume (own_buf, 0, signal); @@ -2383,8 +3343,8 @@ process_serial_event (void) 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); + if (gdb_signal_to_host_p (sig)) + signal = gdb_signal_to_host (sig); else signal = 0; myresume (own_buf, 1, signal); @@ -2403,13 +3363,16 @@ process_serial_event (void) /* Fallthrough. */ case 'z': /* remove_ ... */ { - char *lenptr; char *dataptr; - CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16); - int len = strtol (lenptr + 1, &dataptr, 16); + ULONGEST addr; + int len; char type = own_buf[1]; int res; const int insert = ch == 'Z'; + char *p = &own_buf[3]; + + p = unpack_varlen_hex (p, &addr); + len = strtol (p + 1, &dataptr, 16); /* Default to unrecognized/unsupported. */ res = 1; @@ -2422,7 +3385,22 @@ process_serial_event (void) case '4': /* access watchpoint */ require_running (own_buf); if (insert && the_target->insert_point != NULL) - res = (*the_target->insert_point) (type, addr, len); + { + /* Insert the breakpoint. If it is already inserted, nothing + will take place. */ + res = (*the_target->insert_point) (type, addr, len); + + /* GDB may have sent us a list of *point parameters to be + evaluated on the target's side. Read such list here. If we + already have a list of parameters, GDB is telling us to drop + that list and use this one instead. */ + if (!res && (type == '0' || type == '1')) + { + /* Remove previous conditions. */ + clear_gdb_breakpoint_conditions (addr); + process_point_options (addr, &dataptr); + } + } else if (!insert && the_target->remove_point != NULL) res = (*the_target->remove_point) (type, addr, len); break; @@ -2444,7 +3422,7 @@ process_serial_event (void) if (!target_running ()) /* The packet we received doesn't make sense - but we can't reply to it, either. */ - return; + return 0; fprintf (stderr, "Killing all inferiors\n"); for_each_inferior (&all_processes, kill_inferior_callback); @@ -2454,14 +3432,12 @@ process_serial_event (void) if (extended_protocol) { last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = TARGET_SIGNAL_KILL; - return; + last_status.value.sig = GDB_SIGNAL_KILL; + return 0; } else - { - exit (0); - break; - } + exit (0); + case 'T': { ptid_t gdb_id, thread_id; @@ -2500,9 +3476,9 @@ process_serial_event (void) else { last_status.kind = TARGET_WAITKIND_EXITED; - last_status.value.sig = TARGET_SIGNAL_KILL; + last_status.value.sig = GDB_SIGNAL_KILL; } - return; + return 0; } else { @@ -2536,34 +3512,42 @@ process_serial_event (void) { /* In non-stop, defer exiting until GDB had a chance to query the whole vStopped list (until it gets an OK). */ - if (!notif_queue) + if (QUEUE_is_empty (notif_event_p, notif_stop.queue)) { fprintf (stderr, "GDBserver exiting\n"); remote_close (); exit (0); } } + + if (exit_requested) + return -1; + + return 0; } /* Event-loop callback for serial events. */ -void +int 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 (); + if (process_serial_event () < 0) + return -1; /* Be sure to not change the selected inferior behind GDB's back. Important in the non-stop mode asynchronous protocol. */ set_desired_inferior (1); + + return 0; } /* Event-loop callback for target events. */ -void +int handle_target_event (int err, gdb_client_data client_data) { if (debug_threads) @@ -2574,11 +3558,72 @@ handle_target_event (int err, gdb_client_data client_data) if (last_status.kind != TARGET_WAITKIND_IGNORE) { - /* Something interesting. Tell GDB about it. */ - push_event (last_ptid, &last_status); + int pid = ptid_get_pid (last_ptid); + struct process_info *process = find_process_pid (pid); + int forward_event = !gdb_connected () || process->gdb_detached; + + if (last_status.kind == TARGET_WAITKIND_EXITED + || last_status.kind == TARGET_WAITKIND_SIGNALLED) + { + mark_breakpoints_out (process); + mourn_inferior (process); + } + else + { + /* We're reporting this thread as stopped. Update its + "want-stopped" state to what the client wants, until it + gets a new resume action. */ + current_inferior->last_resume_kind = resume_stop; + current_inferior->last_status = last_status; + } + + if (forward_event) + { + if (!target_running ()) + { + /* The last process exited. We're done. */ + exit (0); + } + + if (last_status.kind == TARGET_WAITKIND_STOPPED) + { + /* A thread stopped with a signal, but gdb isn't + connected to handle it. Pass it down to the + inferior, as if it wasn't being traced. */ + struct thread_resume resume_info; + + if (debug_threads) + fprintf (stderr, + "GDB not connected; forwarding event %d for [%s]\n", + (int) last_status.kind, + target_pid_to_str (last_ptid)); + + resume_info.thread = last_ptid; + resume_info.kind = resume_continue; + resume_info.sig = gdb_signal_to_host (last_status.value.sig); + (*the_target->resume) (&resume_info, 1); + } + else if (debug_threads) + fprintf (stderr, "GDB not connected; ignoring event %d for [%s]\n", + (int) last_status.kind, + target_pid_to_str (last_ptid)); + } + else + { + struct vstop_notif *vstop_notif + = xmalloc (sizeof (struct vstop_notif)); + + vstop_notif->status = last_status; + vstop_notif->ptid = last_ptid; + /* Push Stop notification. */ + notif_push (¬if_stop, + (struct notif_event *) vstop_notif); + } } /* Be sure to not change the selected inferior behind GDB's back. Important in the non-stop mode asynchronous protocol. */ set_desired_inferior (1); + + return 0; }