diff options
Diffstat (limited to 'contrib/gdb/gdb/remote-utils.c')
-rw-r--r-- | contrib/gdb/gdb/remote-utils.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/contrib/gdb/gdb/remote-utils.c b/contrib/gdb/gdb/remote-utils.c new file mode 100644 index 0000000..ba150e3 --- /dev/null +++ b/contrib/gdb/gdb/remote-utils.c @@ -0,0 +1,609 @@ +/* Generic support for remote debugging interfaces. + + Copyright 1993, 1994, 1995, 1996, 1998, 2000, 2001 + 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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. */ + +/* This file actually contains two distinct logical "packages". They + are packaged together in this one file because they are typically + used together. + + The first package is an addition to the serial package. The + addition provides reading and writing with debugging output and + timeouts based on user settable variables. These routines are + intended to support serial port based remote backends. These + functions are prefixed with sr_. + + The second package is a collection of more or less generic + functions for use by remote backends. They support user settable + variables for debugging, retries, and the like. + + Todo: + + * a pass through mode a la kermit or telnet. + * autobaud. + * ask remote to change his baud rate. + */ + +#include <ctype.h> + +#include "defs.h" +#include "gdb_string.h" +#include "gdbcmd.h" +#include "target.h" +#include "serial.h" +#include "gdbcore.h" /* for exec_bfd */ +#include "inferior.h" /* for generic_mourn_inferior */ +#include "remote-utils.h" +#include "regcache.h" + + +void _initialize_sr_support (void); + +struct _sr_settings sr_settings = +{ + 4, /* timeout: + remote-hms.c had 2 + remote-bug.c had "with a timeout of 2, we time out waiting for + the prompt after an s-record dump." + + remote.c had (2): This was 5 seconds, which is a long time to + sit and wait. Unless this is going though some terminal server + or multiplexer or other form of hairy serial connection, I + would think 2 seconds would be plenty. + */ + + 10, /* retries */ + NULL, /* device */ + NULL, /* descriptor */ +}; + +struct gr_settings *gr_settings = NULL; + +static void usage (char *, char *); +static void sr_com (char *, int); + +static void +usage (char *proto, char *junk) +{ + if (junk != NULL) + fprintf_unfiltered (gdb_stderr, "Unrecognized arguments: `%s'.\n", junk); + + error ("Usage: target %s [DEVICE [SPEED [DEBUG]]]\n\ +where DEVICE is the name of a device or HOST:PORT", proto); + + return; +} + +#define CHECKDONE(p, q) \ +{ \ + if (q == p) \ + { \ + if (*p == '\0') \ + return; \ + else \ + usage(proto, p); \ + } \ +} + +void +sr_scan_args (char *proto, char *args) +{ + int n; + char *p, *q; + + /* if no args, then nothing to do. */ + if (args == NULL || *args == '\0') + return; + + /* scan off white space. */ + for (p = args; isspace (*p); ++p);; + + /* find end of device name. */ + for (q = p; *q != '\0' && !isspace (*q); ++q);; + + /* check for missing or empty device name. */ + CHECKDONE (p, q); + sr_set_device (savestring (p, q - p)); + + /* look for baud rate. */ + n = strtol (q, &p, 10); + + /* check for missing or empty baud rate. */ + CHECKDONE (p, q); + baud_rate = n; + + /* look for debug value. */ + n = strtol (p, &q, 10); + + /* check for missing or empty debug value. */ + CHECKDONE (p, q); + sr_set_debug (n); + + /* scan off remaining white space. */ + for (p = q; isspace (*p); ++p);; + + /* if not end of string, then there's unrecognized junk. */ + if (*p != '\0') + usage (proto, p); + + return; +} + +void +gr_generic_checkin (void) +{ + sr_write_cr (""); + gr_expect_prompt (); +} + +void +gr_open (char *args, int from_tty, struct gr_settings *gr) +{ + target_preopen (from_tty); + sr_scan_args (gr->ops->to_shortname, args); + unpush_target (gr->ops); + + gr_settings = gr; + + if (sr_get_desc () != NULL) + gr_close (0); + + /* If no args are specified, then we use the device specified by a + previous command or "set remotedevice". But if there is no + device, better stop now, not dump core. */ + + if (sr_get_device () == NULL) + usage (gr->ops->to_shortname, NULL); + + sr_set_desc (serial_open (sr_get_device ())); + if (!sr_get_desc ()) + perror_with_name ((char *) sr_get_device ()); + + if (baud_rate != -1) + { + if (serial_setbaudrate (sr_get_desc (), baud_rate) != 0) + { + serial_close (sr_get_desc ()); + perror_with_name (sr_get_device ()); + } + } + + serial_raw (sr_get_desc ()); + + /* If there is something sitting in the buffer we might take it as a + response to a command, which would be bad. */ + serial_flush_input (sr_get_desc ()); + + /* default retries */ + if (sr_get_retries () == 0) + sr_set_retries (1); + + /* default clear breakpoint function */ + if (gr_settings->clear_all_breakpoints == NULL) + gr_settings->clear_all_breakpoints = remove_breakpoints; + + if (from_tty) + { + printf_filtered ("Remote debugging using `%s'", sr_get_device ()); + if (baud_rate != -1) + printf_filtered (" at baud rate of %d", + baud_rate); + printf_filtered ("\n"); + } + + push_target (gr->ops); + gr_checkin (); + gr_clear_all_breakpoints (); + return; +} + +/* Read a character from the remote system masking it down to 7 bits + and doing all the fancy timeout stuff. */ + +int +sr_readchar (void) +{ + int buf; + + buf = serial_readchar (sr_get_desc (), sr_get_timeout ()); + + if (buf == SERIAL_TIMEOUT) + error ("Timeout reading from remote system."); + + if (sr_get_debug () > 0) + printf_unfiltered ("%c", buf); + + return buf & 0x7f; +} + +int +sr_pollchar (void) +{ + int buf; + + buf = serial_readchar (sr_get_desc (), 0); + if (buf == SERIAL_TIMEOUT) + buf = 0; + if (sr_get_debug () > 0) + { + if (buf) + printf_unfiltered ("%c", buf); + else + printf_unfiltered ("<empty character poll>"); + } + + return buf & 0x7f; +} + +/* Keep discarding input from the remote system, until STRING is found. + Let the user break out immediately. */ +void +sr_expect (char *string) +{ + char *p = string; + + immediate_quit++; + while (1) + { + if (sr_readchar () == *p) + { + p++; + if (*p == '\0') + { + immediate_quit--; + return; + } + } + else + p = string; + } +} + +void +sr_write (char *a, int l) +{ + int i; + + if (serial_write (sr_get_desc (), a, l) != 0) + perror_with_name ("sr_write: Error writing to remote"); + + if (sr_get_debug () > 0) + for (i = 0; i < l; i++) + printf_unfiltered ("%c", a[i]); + + return; +} + +void +sr_write_cr (char *s) +{ + sr_write (s, strlen (s)); + sr_write ("\r", 1); + return; +} + +int +sr_timed_read (char *buf, int n) +{ + int i; + char c; + + i = 0; + while (i < n) + { + c = sr_readchar (); + + if (c == 0) + return i; + buf[i] = c; + i++; + + } + return i; +} + +/* Get a hex digit from the remote system & return its value. If + ignore_space is nonzero, ignore spaces (not newline, tab, etc). */ + +int +sr_get_hex_digit (int ignore_space) +{ + int ch; + + while (1) + { + ch = sr_readchar (); + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch != ' ' || !ignore_space) + { + gr_expect_prompt (); + error ("Invalid hex digit from remote system."); + } + } +} + +/* Get a byte from the remote and put it in *BYT. Accept any number + leading spaces. */ +void +sr_get_hex_byte (char *byt) +{ + int val; + + val = sr_get_hex_digit (1) << 4; + val |= sr_get_hex_digit (0); + *byt = val; +} + +/* Read a 32-bit hex word from the remote, preceded by a space */ +long +sr_get_hex_word (void) +{ + long val; + int j; + + val = 0; + for (j = 0; j < 8; j++) + val = (val << 4) + sr_get_hex_digit (j == 0); + return val; +} + +/* Put a command string, in args, out to the remote. The remote is assumed to + be in raw mode, all writing/reading done through desc. + Ouput from the remote is placed on the users terminal until the + prompt from the remote is seen. + FIXME: Can't handle commands that take input. */ + +static void +sr_com (char *args, int fromtty) +{ + sr_check_open (); + + if (!args) + return; + + /* Clear all input so only command relative output is displayed */ + + sr_write_cr (args); + sr_write ("\030", 1); + registers_changed (); + gr_expect_prompt (); +} + +void +gr_close (int quitting) +{ + gr_clear_all_breakpoints (); + + if (sr_is_open ()) + { + serial_close (sr_get_desc ()); + sr_set_desc (NULL); + } + + return; +} + +/* gr_detach() + takes a program previously attached to and detaches it. + We better not have left any breakpoints + in the program or it'll die when it hits one. + Close the open connection to the remote debugger. + Use this when you want to detach and do something else + with your gdb. */ + +void +gr_detach (char *args, int from_tty) +{ + if (args) + error ("Argument given to \"detach\" when remotely debugging."); + + if (sr_is_open ()) + gr_clear_all_breakpoints (); + + pop_target (); + if (from_tty) + puts_filtered ("Ending remote debugging.\n"); + + return; +} + +void +gr_files_info (struct target_ops *ops) +{ +#ifdef __GO32__ + printf_filtered ("\tAttached to DOS asynctsr\n"); +#else + printf_filtered ("\tAttached to %s", sr_get_device ()); + if (baud_rate != -1) + printf_filtered ("at %d baud", baud_rate); + printf_filtered ("\n"); +#endif + + if (exec_bfd) + { + printf_filtered ("\tand running program %s\n", + bfd_get_filename (exec_bfd)); + } + printf_filtered ("\tusing the %s protocol.\n", ops->to_shortname); +} + +void +gr_mourn (void) +{ + gr_clear_all_breakpoints (); + unpush_target (gr_get_ops ()); + generic_mourn_inferior (); +} + +void +gr_kill (void) +{ + return; +} + +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ +void +gr_create_inferior (char *execfile, char *args, char **env) +{ + int entry_pt; + + if (args && *args) + error ("Can't pass arguments to remote process."); + + if (execfile == 0 || exec_bfd == 0) + error ("No executable file specified"); + + entry_pt = (int) bfd_get_start_address (exec_bfd); + sr_check_open (); + + gr_kill (); + gr_clear_all_breakpoints (); + + init_wait_for_inferior (); + gr_checkin (); + + insert_breakpoints (); /* Needed to get correct instruction in cache */ + proceed (entry_pt, -1, 0); +} + +/* Given a null terminated list of strings LIST, read the input until we find one of + them. Return the index of the string found or -1 on error. '?' means match + any single character. Note that with the algorithm we use, the initial + character of the string cannot recur in the string, or we will not find some + cases of the string in the input. If PASSTHROUGH is non-zero, then + pass non-matching data on. */ + +int +gr_multi_scan (char *list[], int passthrough) +{ + char *swallowed = NULL; /* holding area */ + char *swallowed_p = swallowed; /* Current position in swallowed. */ + int ch; + int ch_handled; + int i; + int string_count; + int max_length; + char **plist; + + /* Look through the strings. Count them. Find the largest one so we can + allocate a holding area. */ + + for (max_length = string_count = i = 0; + list[i] != NULL; + ++i, ++string_count) + { + int length = strlen (list[i]); + + if (length > max_length) + max_length = length; + } + + /* if we have no strings, then something is wrong. */ + if (string_count == 0) + return (-1); + + /* otherwise, we will need a holding area big enough to hold almost two + copies of our largest string. */ + swallowed_p = swallowed = alloca (max_length << 1); + + /* and a list of pointers to current scan points. */ + plist = (char **) alloca (string_count * sizeof (*plist)); + + /* and initialize */ + for (i = 0; i < string_count; ++i) + plist[i] = list[i]; + + for (ch = sr_readchar (); /* loop forever */ ; ch = sr_readchar ()) + { + QUIT; /* Let user quit and leave process running */ + ch_handled = 0; + + for (i = 0; i < string_count; ++i) + { + if (ch == *plist[i] || *plist[i] == '?') + { + ++plist[i]; + if (*plist[i] == '\0') + return (i); + + if (!ch_handled) + *swallowed_p++ = ch; + + ch_handled = 1; + } + else + plist[i] = list[i]; + } + + if (!ch_handled) + { + char *p; + + /* Print out any characters which have been swallowed. */ + if (passthrough) + { + for (p = swallowed; p < swallowed_p; ++p) + fputc_unfiltered (*p, gdb_stdout); + + fputc_unfiltered (ch, gdb_stdout); + } + + swallowed_p = swallowed; + } + } +#if 0 + /* Never reached. */ + return (-1); +#endif +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +void +gr_prepare_to_store (void) +{ + /* Do nothing, since we assume we can store individual regs */ +} + +void +_initialize_sr_support (void) +{ +/* FIXME-now: if target is open... */ + add_show_from_set (add_set_cmd ("remotedevice", no_class, + var_filename, (char *) &sr_settings.device, + "Set device for remote serial I/O.\n\ +This device is used as the serial port when debugging using remote\n\ +targets.", &setlist), + &showlist); + + add_com ("remote <command>", class_obscure, sr_com, + "Send a command to the remote monitor."); + +} |