diff options
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r-- | gdb/gdbserver/Makefile.in | 241 | ||||
-rw-r--r-- | gdb/gdbserver/README | 127 | ||||
-rw-r--r-- | gdb/gdbserver/configure.in | 100 | ||||
-rw-r--r-- | gdb/gdbserver/gdbreplay.c | 318 | ||||
-rw-r--r-- | gdb/gdbserver/gdbserver.1 | 103 | ||||
-rw-r--r-- | gdb/gdbserver/low-hppabsd.c | 379 | ||||
-rw-r--r-- | gdb/gdbserver/low-linux.c | 451 | ||||
-rw-r--r-- | gdb/gdbserver/low-lynx.c | 746 | ||||
-rw-r--r-- | gdb/gdbserver/low-sim.c | 289 | ||||
-rw-r--r-- | gdb/gdbserver/low-sparc.c | 334 | ||||
-rw-r--r-- | gdb/gdbserver/low-sun3.c | 313 | ||||
-rw-r--r-- | gdb/gdbserver/remote-utils.c | 545 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 258 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 72 | ||||
-rw-r--r-- | gdb/gdbserver/utils.c | 114 |
15 files changed, 4390 insertions, 0 deletions
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in new file mode 100644 index 0000000..b016e13 --- /dev/null +++ b/gdb/gdbserver/Makefile.in @@ -0,0 +1,241 @@ +#Copyright 1989, 90, 91, 92, 93, 94, 95, 96, 1997 +#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. + +prefix = /usr/local + +program_transform_name = +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +tooldir = $(libdir)/$(target_alias) + +datadir = $(prefix)/share +mandir = $(prefix)/man +man1dir = $(mandir)/man1 +man2dir = $(mandir)/man2 +man3dir = $(mandir)/man3 +man4dir = $(mandir)/man4 +man5dir = $(mandir)/man5 +man6dir = $(mandir)/man6 +man7dir = $(mandir)/man7 +man8dir = $(mandir)/man8 +man9dir = $(mandir)/man9 +infodir = $(prefix)/info +includedir = $(prefix)/include + +SHELL = /bin/sh + +INSTALL = `cd $(srcdir)/../..;pwd`/install-sh -c +INSTALL_PROGRAM = $(INSTALL) +INSTALL_DATA = $(INSTALL) + +AR = ar +AR_FLAGS = qv +RANLIB = ranlib + +# If you are compiling with GCC, make sure that either 1) You use the +# -traditional flag, or 2) You have the fixed include files where GCC +# can reach them. Otherwise the ioctl calls in inflow.c +# will be incorrectly compiled. The "fixincludes" script in the gcc +# distribution will fix your include files up. +#CC=cc +#CC=gcc -traditional +GCC=gcc + +# Directory containing source files. Don't clean up the spacing, +# this exact string is matched for by the "configure" script. +srcdir = . + +# It is also possible that you will need to add -I/usr/include/sys to the +# CFLAGS section if your system doesn't have fcntl.h in /usr/include (which +# is where it should be according to Posix). + +# Set this up with gcc if you have gnu ld and the loader will print out +# line numbers for undefinded refs. +#CC-LD=gcc -static +CC-LD=${CC} + +# Where is the "include" directory? Traditionally ../include or ./include +INCLUDE_DIR = ${srcdir}/../../include +INCLUDE_DEP = $$(INCLUDE_DIR) + +# Where are the BFD library? +BFD_DIR = ../../bfd +BFD = $(BFD_DIR)/libbfd.a +BFD_SRC = $(srcdir)/$(BFD_DIR) +BFD_CFLAGS = -I$(BFD_DIR) -I$(BFD_SRC) + +# Where is the source dir for the READLINE library? Traditionally in .. or . +# (For the binary library built from it, we use ${READLINE_DIR}${subdir}.) +READLINE_DIR = ${srcdir}/../readline +READLINE_DEP = $$(READLINE_DIR) + +# All the includes used for CFLAGS and for lint. +# -I. for config files. +# -I${srcdir} possibly for regex.h also. +# -I${srcdir}/config for more generic config files. +INCLUDE_CFLAGS = -I. -I${srcdir} -I${srcdir}/.. -I${srcdir}/../config -I$(INCLUDE_DIR) + +# M{H,T}_CFLAGS, if defined, has host- and target-dependent CFLAGS +# from the config/ directory. +GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS} +#PROFILE_CFLAGS = -pg + +# CFLAGS is specifically reserved for setting from the command line +# when running make. I.E. "make CFLAGS=-Wmissing-prototypes". +CFLAGS = -g +# INTERNAL_CFLAGS is the aggregate of all other *CFLAGS macros. +INTERNAL_CFLAGS = ${CFLAGS} ${GLOBAL_CFLAGS} ${PROFILE_CFLAGS} \ + ${BFD_CFLAGS} ${INCLUDE_CFLAGS} + +# LDFLAGS is specifically reserved for setting from the command line +# when running make. + +# Perhaps should come from parent Makefile +VERSION = gdbserver-4.12.3 +DIST=gdb + +LINT=/usr/5bin/lint +LINTFLAGS= $(BFD_CFLAGS) + +# Host and target-dependent makefile fragments come in here. +#### +# End of host and target-dependent makefile fragments + +# All source files that go into linking GDB remote server. + +SFILES = $(srcdir)/low-lynx.c $(srcdir)/low-sparc.c $(srcdir)/low-sun3.c \ + $(srcdir)/low-hppabsd.c \ + $(srcdir)/utils.c $(srcdir)/server.c $(srcdir)/remote-utils.c + +DEPFILES = $(GDBSERVER_DEPFILES) + +SOURCES = $(SFILES) $(ALLDEPFILES) +TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} + +OBS = utils.o $(GDBSERVER_DEPFILES) server.o remote-utils.o + +# Prevent Sun make from putting in the machine type. Setting +# TARGET_ARCH to nothing works for SunOS 3, 4.0, but not for 4.1. +.c.o: + ${CC} -c ${INTERNAL_CFLAGS} $< + +all: gdbserver gdbreplay + +# Traditionally "install" depends on "all". But it may be useful +# not to; for example, if the user has made some trivial change to a +# source file and doesn't care about rebuilding or just wants to save the +# time it takes for make to check that all is up to date. +# install-only is intended to address that need. +install: all install-only +install-only: + n=`echo gdbserver | sed '$(program_transform_name)'`; \ + if [ x$$n = x ]; then n=gdbserver; else true; fi; \ + $(INSTALL_PROGRAM) gdbserver $(bindir)/$$n; \ + $(INSTALL_DATA) $(srcdir)/gdbserver.1 $(man1dir)/$$n.1 + +uninstall: force + n=`echo gdbserver | sed '$(program_transform_name)'`; \ + if [ x$$n = x ]; then n=gdbserver; else true; fi; \ + rm -f $(bindir)/$$n $(man1dir)/$$n.1 + +installcheck: +check: +info dvi: +install-info: +clean-info: + +gdbserver: $(OBS) ${ADD_DEPS} ${CDEPS} + rm -f gdbserver + ${CC-LD} $(GLOBAL_CFLAGS) $(LDFLAGS) -o gdbserver $(OBS) \ + $(GDBSERVER_LIBS) $(XM_CLIBS) + +gdbreplay: gdbreplay.o + rm -f gdbreplay + ${CC-LD} $(GLOBAL_CFLAGS) $(LDFLAGS) -o gdbreplay gdbreplay.o \ + $(XM_CLIBS) + +config.status: + @echo "You must configure gdbserver. Look at the README file for details." + @false + +# Put the proper machine-specific files first, so M-. on a machine +# specific routine gets the one for the correct machine. +# The xyzzy stuff below deals with empty DEPFILES +TAGS: ${TAGFILES} + etags `find ${srcdir}/../config -name $(TM_FILE) -print` \ + `find ${srcdir}/../config -name ${XM_FILE} -print` \ + `find ${srcdir}/../config -name ${NAT_FILE} -print` \ + `for i in yzzy ${DEPFILES}; do \ + if [ x$$i != xyzzy ]; then \ + echo ${srcdir}/$$i | sed -e 's/\.o$$/\.c/' ; \ + fi; \ + done` \ + ${TAGFILES} +tags: TAGS + +clean: + rm -f *.o ${ADD_FILES} *~ + rm -f gdbserver core make.log + +distclean: clean + rm -f nm.h tm.h xm.h config.status + rm -f Makefile + +maintainer-clean realclean: clean + rm -f nm.h tm.h xm.h config.status + rm -f Makefile + +STAGESTUFF=${OBS} ${TSOBS} ${NTSOBS} ${ADD_FILES} init.c init.o version.c gdb + +Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag) + $(SHELL) ./config.status + +force: + +version.c: Makefile + echo 'char *version = "$(VERSION)";' >version.c + +# GNU Make has an annoying habit of putting *all* the Makefile variables +# into the environment, unless you include this target as a circumvention. +# Rumor is that this will be fixed (and this target can be removed) +# in GNU Make 4.0. +.NOEXPORT: + +# GNU Make 3.63 has a different problem: it keeps tacking command line +# overrides onto the definition of $(MAKE). This variable setting +# will remove them. +MAKEOVERRIDES= + +## This is ugly, but I don't want GNU make to put these variables in +## the environment. Older makes will see this as a set of targets +## with no dependencies and no actions. +unexport CHILLFLAGS CHILL_LIB CHILL_FOR_TARGET : + +server.o : ${srcdir}/server.c ${srcdir}/server.h +remote-utils.o : ${srcdir}/remote-utils.c ${srcdir}/server.h +low-linux.o : ${srcdir}/low-linux.c ${srcdir}/server.h +low-lynx.o : ${srcdir}/low-lynx.c ${srcdir}/server.h +low-sim.o : ${srcdir}/low-sim.c ${srcdir}/server.h +low-sparc.o : $(srcdir)/low-sparc.c $(srcdir)/server.h +low-sun3.o : $(srcdir)/low-sun3.c $(srcdir)/server.h +low-hppabsd.o : $(srcdir)/low-hppabsd.c $(srcdir)/server.h +utils.o : ${srcdir}/utils.c ${srcdir}/server.h + +# This is the end of "Makefile.in". diff --git a/gdb/gdbserver/README b/gdb/gdbserver/README new file mode 100644 index 0000000..2281bf6 --- /dev/null +++ b/gdb/gdbserver/README @@ -0,0 +1,127 @@ + README for GDBserver & GDBreplay + by Stu Grossman and Fred Fish + +Introduction: + +This is GDBserver, a remote server for Un*x-like systems. It can be used to +control the execution of a program on a target system from a GDB on a different +host. GDB and GDBserver communicate using the standard remote serial protocol +implemented in remote.c, and various *-stub.c files. They communicate via +either a serial line or a TCP connection. + +Usage (server (target) side): + +First, you need to have a copy of the program you want to debug put onto +the target system. The program can be stripped to save space if needed, as +GDBserver doesn't care about symbols. All symbol handling is taken care of by +the GDB running on the host system. + +To use the server, you log on to the target system, and run the `gdbserver' +program. You must tell it (a) how to communicate with GDB, (b) the name of +your program, and (c) its arguments. The general syntax is: + + target> gdbserver COMM PROGRAM [ARGS ...] + +For example, using a serial port, you might say: + + target> gdbserver /dev/com1 emacs foo.txt + +This tells gdbserver to debug emacs with an argument of foo.txt, and to +communicate with GDB via /dev/com1. Gdbserver now waits patiently for the +host GDB to communicate with it. + +To use a TCP connection, you could say: + + target> gdbserver host:2345 emacs foo.txt + +This says pretty much the same thing as the last example, except that we are +going to communicate with the host GDB via TCP. The `host:2345' argument means +that we are expecting to see a TCP connection from `host' to local TCP port +2345. (Currently, the `host' part is ignored.) You can choose any number you +want for the port number as long as it does not conflict with any existing TCP +ports on the target system. This same port number must be used in the host +GDBs `target remote' command, which will be described shortly. Note that if +you chose a port number that conflicts with another service, gdbserver will +print an error message and exit. + +Usage (host side): + +You need an unstripped copy of the target program on your host system, since +GDB needs to examine it's symbol tables and such. Start up GDB as you normally +would, with the target program as the first argument. (You may need to use the +--baud option if the serial line is running at anything except 9600 baud.) +Ie: `gdb TARGET-PROG', or `gdb --baud BAUD TARGET-PROG'. After that, the only +new command you need to know about is `target remote'. It's argument is either +a device name (usually a serial device, like `/dev/ttyb'), or a HOST:PORT +descriptor. For example: + + (gdb) target remote /dev/ttyb + +communicates with the server via serial line /dev/ttyb, and: + + (gdb) target remote the-target:2345 + +communicates via a TCP connection to port 2345 on host `the-target', where +you previously started up gdbserver with the same port number. Note that for +TCP connections, you must start up gdbserver prior to using the `target remote' +command, otherwise you may get an error that looks something like +`Connection refused'. + +Building: + +Configuring gdbserver you should specify the same machine for host and +target (which are the machine that gdbserver is going to run on. This +is not the same as the machine that gdb is going to run on; building +gdbserver automatically as part of building a whole tree of tools does +not currently work if cross-compilation is involved (we don't get the +right CC in the Makefile, to start with)). + +gdbserver should work on sparc-sun-sunos4* or Lynx. The following +instructions pertain to Lynx. To build the server for Lynx, make a +new copy of the distribution onto a disk that is NFS shared with the +Lynx system. Lets say that's in a directory called xyzzy. Then, +follow these steps under the host system: + + 1) cd xyzzy/gdb/gdbserver + 2) ../../configure i386-none-lynx + +When that completes, do the following on the Lynx system: + + 3) cd xyzzy/gdb/gdbserver + 4) make CC=gcc + +It should build with only a minor complaint about NULL being redefined. That's +a LynxOS problem, and can be ignored. + +It's also possible that you may have a cross-compiler to Lynx. In that case, +you can skip the stuff about NFS. You would replace steps 3 & 4 with: + + make CC=lynx-target-compiler... + +Using GDBreplay: + +A special hacked down version of gdbserver can be used to replay remote +debug log files created by gdb. Before using the gdb "target" command to +initiate a remote debug session, use "set remotelogfile <filename>" to tell +gdb that you want to make a recording of the serial or tcp session. Note +that when replaying the session, gdb communicates with gdbreplay via tcp, +regardless of whether the original session was via a serial link or tcp. + +Once you are done with the remote debug session, start gdbreplay and +tell it the name of the log file and the host and port number that gdb +should connect to (typically the same as the host running gdb): + + $ gdbreplay logfile host:port + +Then start gdb (preferably in a different screen or window) and use the +"target" command to connect to gdbreplay: + + (gdb) target remote host:port + +Repeat the same sequence of user commands to gdb that you gave in the +original debug session. Gdb should not be able to tell that it is talking +to gdbreplay rather than a real target, all other things being equal. Note +that gdbreplay echos the command lines to stderr, as well as the contents of +the packets it sends and receives. The last command echoed by gdbreplay is +the next command that needs to be typed to gdb to continue the session in +sync with the original session. diff --git a/gdb/gdbserver/configure.in b/gdb/gdbserver/configure.in new file mode 100644 index 0000000..1bbb73c --- /dev/null +++ b/gdb/gdbserver/configure.in @@ -0,0 +1,100 @@ +srcname="Remote GDB server" +srctrigger=server.c +gdb_serial_driver=../ser-unix.c + +# per-host: + +. ${srcdir}/../configure.host + +echo "gdbserver/configure.in: host is $host, target is $target" + +if [ ! -f ${srcdir}/../config/${gdb_host_cpu}/${gdb_host}.mh ]; then + echo '***' "GDB remote does not support host ${host}" 1>&2 + exit 1 +fi + +# We really shouldn't depend on there being a space after XM_FILE= ... +hostfile=`awk '$1 == "XM_FILE=" { print $2 }' <${srcdir}/../config/${gdb_host_cpu}/${gdb_host}.mh` + +# per-target: + +. ${srcdir}/../configure.tgt + +echo "gdbserver/configure.in: host_cpu is $host_cpu, target_cpu is $target_cpu" + +if [ ! -f ${srcdir}/../config/${gdb_target_cpu}/${gdb_target}.mt ]; then + echo '***' "GDB remote does not support target ${target}" 1>&2 + exit 1 +fi + +if [ -z "${removing}" ] ; then + cat ${srcdir}/../config/${gdb_host_cpu}/${gdb_host}.mh ${srcdir}/../config/${gdb_target_cpu}/${gdb_target}.mt | awk '$1 == "#msg" { + print substr($0,6)}' +fi + +# We really shouldn't depend on there being a space after TM_FILE= ... +targetfile=`awk '$1 == "TM_FILE=" { print $2 }' <${srcdir}/../config/${gdb_target_cpu}/${gdb_target}.mt` + +if [ "${target}" = "${host}" ] ; then + nativefile=`awk '$1 == "NAT_FILE=" { print $2 }' <${srcdir}/../config/${gdb_host_cpu}/${gdb_host}.mh` +fi + +host_makefile_frag=../config/${gdb_host_cpu}/${gdb_host}.mh +target_makefile_frag=../config/${gdb_target_cpu}/${gdb_target}.mt + +# If hostfile (XM_FILE) and/or targetfile (TM_FILE) and/or nativefile +# (NAT_FILE) is not set in the ?config/* file, we don't make the +# corresponding links. But we have to remove the xm.h files and tm.h +# files anyway, e.g. when switching from "configure host" to +# "configure none". + +files= +links= +rm -f xm.h +rm -f ser-hardwire.c +if [ "${hostfile}" != "" ]; then + if [ -f ${srcdir}/../config/${hostfile} ]; then + files="${files} ../config/${hostfile}" + else + files="${files} ../config/${gdb_host_cpu}/${hostfile}" + fi + links="${links} xm.h" + +# files="${files} ${gdb_serial_driver}" +# links="${links} ser-hardwire.c" +fi +rm -f tm.h +if [ "${targetfile}" != "" ]; then + if [ -f ${srcdir}/../config/${targetfile} ]; then + files="${files} ../config/${targetfile}" + else + files="${files} ../config/${gdb_target_cpu}/${targetfile}" + fi + links="${links} tm.h" +fi +rm -f nm.h +if [ "${nativefile}" != "" ]; then + if [ -f ${srcdir}/../config/${nativefile} ]; then + files="${files} ../config/${nativefile}" + else + files="${files} ../config/${gdb_host_cpu}/${nativefile}" + fi + links="${links} nm.h" +# temporary scaffolding until all hosts have the host/target/native +# split in place. +else + files="${files} ../config/nm-empty.h" + links="${links} nm.h" +fi + +if [ ${target_cpu} = "sparclite" ]; then + configdirs="${configdirs} sparclite" +fi + +# post-target: + +if [ "${nativefile}" = "" ] ; then + sed -e '/^NATDEPFILES= /s//# NATDEPFILES= /' \ + < Makefile > Makefile.tem + mv -f Makefile.tem Makefile +fi diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c new file mode 100644 index 0000000..ebe06ba --- /dev/null +++ b/gdb/gdbserver/gdbreplay.c @@ -0,0 +1,318 @@ +/* Replay a remote debug session logfile for GDB. + Copyright (C) 1996 Free Software Foundation, Inc. + Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver. + +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. */ + +#include <stdio.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/tcp.h> +#include <signal.h> +#include <ctype.h> +#include <fcntl.h> + +/* Sort of a hack... */ +#define EOL (EOF - 1) + +static int remote_desc; + +/* Print the system error message for errno, and also mention STRING + as the file name for which the error was encountered. + Then return to command level. */ + +void +perror_with_name (string) + char *string; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + char *err; + char *combined; + + err = (errno < sys_nerr) ? sys_errlist[errno] : "unknown error"; + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + fprintf (stderr, "\n%s.\n", combined); + fflush (stderr); + exit (1); +} + +static void +sync_error (fp, desc, expect, got) + FILE *fp; + char *desc; + int expect; + int got; +{ + fprintf (stderr, "\n%s\n", desc); + fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n", + ftell (fp), expect, got); + fflush (stderr); + exit (1); +} + +void +remote_close() +{ + close (remote_desc); +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +void +remote_open (name) + char *name; +{ + extern char *strchr (); + + if (!strchr (name, ':')) + { + fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name); + fflush (stderr); + exit (1); + } + else + { + char *port_str; + int port; + struct sockaddr_in sockaddr; + int tmp; + struct protoent *protoent; + int tmp_desc; + + port_str = strchr (name, ':'); + + port = atoi (port_str + 1); + + tmp_desc = socket (PF_INET, SOCK_STREAM, 0); + if (tmp_desc < 0) + perror_with_name ("Can't open socket"); + + /* Allow rapid reuse of this port. */ + tmp = 1; + setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp, + sizeof(tmp)); + + sockaddr.sin_family = PF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + if (bind (tmp_desc, (struct sockaddr *)&sockaddr, sizeof (sockaddr)) + || listen (tmp_desc, 1)) + perror_with_name ("Can't bind address"); + + tmp = sizeof (sockaddr); + remote_desc = accept (tmp_desc, (struct sockaddr *)&sockaddr, &tmp); + if (remote_desc == -1) + perror_with_name ("Accept failed"); + + protoent = getprotobyname ("tcp"); + if (!protoent) + perror_with_name ("getprotobyname"); + + /* Enable TCP keep alive process. */ + tmp = 1; + setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp)); + + /* Tell TCP not to delay small packets. This greatly speeds up + interactive response. */ + tmp = 1; + setsockopt (remote_desc, protoent->p_proto, TCP_NODELAY, + (char *)&tmp, sizeof(tmp)); + + close (tmp_desc); /* No longer need this */ + + signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbreplay simply + exits when the remote side dies. */ + } + + fcntl (remote_desc, F_SETFL, FASYNC); + + fprintf (stderr, "Replay logfile using %s\n", name); + fflush (stderr); +} + +static int tohex (ch) + int ch; +{ + if (ch >= '0' && ch <= '9') + { + return (ch - '0'); + } + if (ch >= 'A' && ch <= 'F') + { + return (ch - 'A' + 10); + } + if (ch >= 'a' && ch <= 'f') + { + return (ch - 'a' + 10); + } + fprintf (stderr, "\nInvalid hex digit '%c'\n", ch); + fflush (stderr); + exit (1); +} + +static int +logchar (fp) + FILE *fp; +{ + int ch; + int ch2; + + ch = fgetc (fp); + fputc (ch, stdout); + fflush (stdout); + switch (ch) + { + case '\n': + ch = EOL; + break; + case '\\': + ch = fgetc (fp); + fputc (ch, stdout); + fflush (stdout); + switch (ch) + { + case '\\': break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case 'x': + ch2 = fgetc (fp); + fputc (ch2, stdout); + fflush (stdout); + ch = tohex (ch2) << 4; + ch2 = fgetc (fp); + fputc (ch2, stdout); + fflush (stdout); + ch |= tohex (ch2); + break; + default: + /* Treat any other char as just itself */ + break; + } + default: + break; + } + return (ch); +} + +/* Accept input from gdb and match with chars from fp (after skipping one + blank) up until a \n is read from fp (which is not matched) */ + +void +expect (fp) + FILE *fp; +{ + int fromlog; + unsigned char fromgdb; + + if ((fromlog = logchar (fp)) != ' ') + { + sync_error (fp, "Sync error during gdb read of leading blank", ' ', + fromlog); + } + do + { + fromlog = logchar (fp); + if (fromlog == EOL) + { + break; + } + read (remote_desc, &fromgdb, 1); + } while (fromlog == fromgdb); + if (fromlog != EOL) + { + sync_error (fp, "Sync error during read of gdb packet", fromlog, + fromgdb); + } +} + +/* Play data back to gdb from fp (after skipping leading blank) up until a + \n is read from fp (which is discarded and not sent to gdb). */ + +void +play (fp) + FILE *fp; +{ + int fromlog; + char ch; + + if ((fromlog = logchar (fp)) != ' ') + { + sync_error (fp, "Sync error skipping blank during write to gdb", ' ', + fromlog); + } + while ((fromlog = logchar (fp)) != EOL) + { + ch = fromlog; + write (remote_desc, &ch, 1); + } +} + +int +main (argc, argv) + int argc; + char *argv[]; +{ + FILE *fp; + int ch; + + if (argc < 3) + { + fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n"); + fflush (stderr); + exit (1); + } + fp = fopen (argv[1], "r"); + if (fp == NULL) + { + perror_with_name (argv[1]); + } + remote_open (argv[2]); + while ((ch = logchar (fp)) != EOF) + { + switch (ch) + { + case 'w': + /* data sent from gdb to gdbreplay, accept and match it */ + expect (fp); + break; + case 'r': + /* data sent from gdbreplay to gdb, play it */ + play (fp); + break; + case 'c': + /* Command executed by gdb */ + while ((ch = logchar (fp)) != EOL); + break; + } + } + remote_close (); + exit (0); +} + diff --git a/gdb/gdbserver/gdbserver.1 b/gdb/gdbserver/gdbserver.1 new file mode 100644 index 0000000..9d3fdcd --- /dev/null +++ b/gdb/gdbserver/gdbserver.1 @@ -0,0 +1,103 @@ +.\" Copyright (c) 1993 Free Software Foundation +.\" See section COPYING for conditions for redistribution +.TH gdbserver 1 "2 November 1993" "Cygnus Support" "GNU Development Tools" +.SH NAME +gdbserver \- Remote Server for the GNU Debugger +.SH SYNOPSIS +.na +.TP +.B gdbserver +.RB tty +.RB prog +.RB "[\|" args... "\|]" +.ad b +.SH DESCRIPTION +GDBSERVER is a program that allows you to run GDB on a different machine +than the one which is running the program being debugged. + +Usage (server (target) side): + +First, you need to have a copy of the program you want to debug put onto +the target system. The program can be stripped to save space if needed, as +GDBserver doesn't care about symbols. All symbol handling is taken care of by +the GDB running on the host system. + +To use the server, you log on to the target system, and run the `gdbserver' +program. You must tell it (a) how to communicate with GDB, (b) the name of +your program, and (c) its arguments. The general syntax is: + + target> gdbserver COMM PROGRAM [ARGS ...] + +For example, using a serial port, you might say: + + target> gdbserver /dev/com1 emacs foo.txt + +This tells gdbserver to debug emacs with an argument of foo.txt, and to +communicate with GDB via /dev/com1. Gdbserver now waits patiently for the +host GDB to communicate with it. + +To use a TCP connection, you could say: + + target> gdbserver host:2345 emacs foo.txt + +This says pretty much the same thing as the last example, except that we are +going to communicate with the host GDB via TCP. The `host:2345' argument means +that we are expecting to see a TCP connection from `host' to local TCP port +2345. (Currently, the `host' part is ignored.) You can choose any number you +want for the port number as long as it does not conflict with any existing TCP +ports on the target system. This same port number must be used in the host +GDBs `target remote' command, which will be described shortly. Note that if +you chose a port number that conflicts with another service, gdbserver will +print an error message and exit. + +Usage (host side): + +You need an unstripped copy of the target program on your host system, since +GDB needs to examine it's symbol tables and such. Start up GDB as you normally +would, with the target program as the first argument. (You may need to use the +--baud option if the serial line is running at anything except 9600 baud.) +Ie: `gdb TARGET-PROG', or `gdb --baud BAUD TARGET-PROG'. After that, the only +new command you need to know about is `target remote'. It's argument is either +a device name (usually a serial device, like `/dev/ttyb'), or a HOST:PORT +descriptor. For example: + + (gdb) target remote /dev/ttyb + +communicates with the server via serial line /dev/ttyb, and: + + (gdb) target remote the-target:2345 + +communicates via a TCP connection to port 2345 on host `the-target', where +you previously started up gdbserver with the same port number. Note that for +TCP connections, you must start up gdbserver prior to using the `target remote' +command, otherwise you may get an error that looks something like +`Connection refused'. +.SH OPTIONS +You have to supply the name of the program to debug +and the tty to communicate on; the remote GDB will do everything else. +Any remaining arguments will be passed to the program verbatim. +.SH "SEE ALSO" +.RB "`\|" gdb "\|'" +entry in +.B info\c +\&; +.I +Using GDB: A Guide to the GNU Source-Level Debugger\c +, Richard M. Stallman and Roland H. Pesch, July 1991. +.SH COPYING +Copyright (c) 1993 Free Software Foundation, Inc. +.PP +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +.PP +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +.PP +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. diff --git a/gdb/gdbserver/low-hppabsd.c b/gdb/gdbserver/low-hppabsd.c new file mode 100644 index 0000000..c784d0c --- /dev/null +++ b/gdb/gdbserver/low-hppabsd.c @@ -0,0 +1,379 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1995 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. */ + +#include "defs.h" +#include <sys/wait.h> +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> + +/***************Begin MY defs*********************/ +int quit_flag = 0; +char registers[REGISTER_BYTES]; + +/* Index within `registers' of the first byte of the space for + register N. */ + + +char buf2[MAX_REGISTER_RAW_SIZE]; +/***************End MY defs*********************/ + +#include <sys/ptrace.h> +#include <machine/reg.h> + +extern char **environ; +extern int errno; +extern int inferior_pid; +void quit (), perror_with_name (); +int query (); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PT_TRACE_ME, 0, 0, 0, 0); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0, 0); + wait (0); + /*************inferior_died ();****VK**************/ +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + union wait w; + + pid = wait (&w); + if (pid != inferior_pid) + perror_with_name ("wait"); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PT_STEP : PT_CONTINUE, inferior_pid, 1, signal, 0); + if (errno) + perror_with_name ("ptrace"); +} + + +#if !defined (offsetof) +#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER) +#endif + +/* U_REGS_OFFSET is the offset of the registers within the u area. */ +#if !defined (U_REGS_OFFSET) +#define U_REGS_OFFSET \ + ptrace (PT_READ_U, inferior_pid, \ + (PTRACE_ARG3_TYPE) (offsetof (struct user, u_ar0)), 0) \ + - KERNEL_U_ADDR +#endif + +CORE_ADDR +register_addr (regno, blockend) + int regno; + CORE_ADDR blockend; +{ + CORE_ADDR addr; + + if (regno < 0 || regno >= ARCH_NUM_REGS) + error ("Invalid register number %d.", regno); + + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +/* Fetch one register. */ + +static void +fetch_register (regno) + int regno; +{ + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + + /* Offset of registers within the u area. */ + unsigned int offset; + + offset = U_REGS_OFFSET; + + regaddr = register_addr (regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + errno = 0; + *(int *) ®isters[ regno * 4 + i] = ptrace (PT_RUREGS, inferior_pid, + (PTRACE_ARG3_TYPE) regaddr, 0, 0); + regaddr += sizeof (int); + if (errno != 0) + { + /* Warning, not error, in case we are attached; sometimes the + kernel doesn't let us at the registers. */ + char *err = strerror (errno); + char *msg = alloca (strlen (err) + 128); + sprintf (msg, "reading register %d: %s", regno, err); + error (msg); + goto error_exit; + } + } + error_exit:; +} + +/* Fetch all registers, or just one, from the child process. */ + +void +fetch_inferior_registers (regno) + int regno; +{ + if (regno == -1 || regno == 0) + for (regno = 0; regno < NUM_REGS; regno++) + fetch_register (regno); + else + fetch_register (regno); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (regno) + int regno; +{ + register unsigned int regaddr; + char buf[80]; + extern char registers[]; + register int i; + unsigned int offset = U_REGS_OFFSET; + int scratch; + + if (regno >= 0) + { + if (CANNOT_STORE_REGISTER (regno)) + return; + regaddr = register_addr (regno, offset); + errno = 0; + if (regno == PCOQ_HEAD_REGNUM || regno == PCOQ_TAIL_REGNUM) + { + scratch = *(int *) ®isters[REGISTER_BYTE (regno)] | 0x3; + ptrace (PT_WUREGS, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + scratch, 0); + if (errno != 0) + { + /* Error, even if attached. Failing to write these two + registers is pretty serious. */ + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } + else + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof(int)) + { + errno = 0; + ptrace (PT_WUREGS, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + *(int *) ®isters[REGISTER_BYTE (regno) + i], 0); + if (errno != 0) + { + /* Warning, not error, in case we are attached; sometimes the + kernel doesn't let us at the registers. */ + char *err = strerror (errno); + char *msg = alloca (strlen (err) + 128); + sprintf (msg, "writing register %d: %s", + regno, err); + error (msg); + return; + } + regaddr += sizeof(int); + } + } + else + for (regno = 0; regno < NUM_REGS; regno++) + store_inferior_registers (regno); +} + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (1, inferior_pid, addr, 0, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (1, inferior_pid, addr, 0, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0, 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (4, inferior_pid, addr, buffer[i], 0); + if (errno) + return errno; + } + + return 0; +} + +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} diff --git a/gdb/gdbserver/low-linux.c b/gdb/gdbserver/low-linux.c new file mode 100644 index 0000000..1060210 --- /dev/null +++ b/gdb/gdbserver/low-linux.c @@ -0,0 +1,451 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1995, 1996 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. */ + +#include "defs.h" +#include <sys/wait.h> +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#if 0 +#include <sgtty.h> +#endif +#include <fcntl.h> + +/***************Begin MY defs*********************/ +int quit_flag = 0; +char registers[REGISTER_BYTES]; + +/* Index within `registers' of the first byte of the space for + register N. */ + + +char buf2[MAX_REGISTER_RAW_SIZE]; +/***************End MY defs*********************/ + +#include <sys/ptrace.h> + +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) +#include <sys/reg.h> +#endif + +extern char **environ; +extern int errno; +extern int inferior_pid; +void quit (), perror_with_name (); +int query (); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PTRACE_TRACEME, 0, 0, 0); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (PTRACE_KILL, inferior_pid, 0, 0); + wait (0); + /*************inferior_died ();****VK**************/ +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + union wait w; + + pid = wait (&w); + if (pid != inferior_pid) + perror_with_name ("wait"); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + + +#if !defined (offsetof) +#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER) +#endif + +/* U_REGS_OFFSET is the offset of the registers within the u area. */ +#if !defined (U_REGS_OFFSET) +#define U_REGS_OFFSET \ + ptrace (PT_READ_U, inferior_pid, \ + (PTRACE_ARG3_TYPE) (offsetof (struct user, u_ar0)), 0) \ + - KERNEL_U_ADDR +#endif + +#ifndef TARGET_M68K +/* this table must line up with REGISTER_NAMES in tm-i386v.h */ +/* symbols like 'EAX' come from <sys/reg.h> */ +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS, +}; + +int +i386_register_u_addr (blockend, regnum) + int blockend; + int regnum; +{ +#if 0 + /* this will be needed if fp registers are reinstated */ + /* for now, you can look at them with 'info float' + * sys5 wont let you change them with ptrace anyway + */ + if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM) + { + int ubase, fpstate; + struct user u; + ubase = blockend + 4 * (SS + 1) - KSTKSZ; + fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u); + return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM)); + } + else +#endif + return (blockend + 4 * regmap[regnum]); + +} +#else /* TARGET_M68K */ +/* This table must line up with REGISTER_NAMES in tm-m68k.h */ +static int regmap[] = +{ +#ifdef PT_D0 + PT_D0, PT_D1, PT_D2, PT_D3, PT_D4, PT_D5, PT_D6, PT_D7, + PT_A0, PT_A1, PT_A2, PT_A3, PT_A4, PT_A5, PT_A6, PT_USP, + PT_SR, PT_PC, +#else + 14, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, + 17, 18, +#endif +#ifdef PT_FP0 + PT_FP0, PT_FP1, PT_FP2, PT_FP3, PT_FP4, PT_FP5, PT_FP6, PT_FP7, + PT_FPCR, PT_FPSR, PT_FPIAR +#else + 21, 24, 27, 30, 33, 36, 39, 42, 45, 46, 47 +#endif +}; + +/* BLOCKEND is the value of u.u_ar0, and points to the place where GS + is stored. */ + +int +m68k_linux_register_u_addr (blockend, regnum) + int blockend; + int regnum; +{ + return (blockend + 4 * regmap[regnum]); +} +#endif + +CORE_ADDR +register_addr (regno, blockend) + int regno; + CORE_ADDR blockend; +{ + CORE_ADDR addr; + + if (regno < 0 || regno >= ARCH_NUM_REGS) + error ("Invalid register number %d.", regno); + + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +/* Fetch one register. */ + +static void +fetch_register (regno) + int regno; +{ + register unsigned int regaddr; + register int i; + + /* Offset of registers within the u area. */ + unsigned int offset; + + offset = U_REGS_OFFSET; + + regaddr = register_addr (regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + errno = 0; + *(int *) ®isters[ regno * 4 + i] = ptrace (PTRACE_PEEKUSR, inferior_pid, + (PTRACE_ARG3_TYPE) regaddr, 0); + regaddr += sizeof (int); + if (errno != 0) + { + /* Warning, not error, in case we are attached; sometimes the + kernel doesn't let us at the registers. */ + char *err = strerror (errno); + char *msg = alloca (strlen (err) + 128); + sprintf (msg, "reading register %d: %s", regno, err); + error (msg); + goto error_exit; + } + } + error_exit:; +} + +/* Fetch all registers, or just one, from the child process. */ + +void +fetch_inferior_registers (regno) + int regno; +{ + if (regno == -1 || regno == 0) + for (regno = 0; regno < NUM_REGS-NUM_FREGS; regno++) + fetch_register (regno); + else + fetch_register (regno); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (regno) + int regno; +{ + register unsigned int regaddr; + register int i; + unsigned int offset = U_REGS_OFFSET; + + if (regno >= 0) + { +#if 0 + if (CANNOT_STORE_REGISTER (regno)) + return; +#endif + regaddr = register_addr (regno, offset); + errno = 0; +#if 0 + if (regno == PCOQ_HEAD_REGNUM || regno == PCOQ_TAIL_REGNUM) + { + scratch = *(int *) ®isters[REGISTER_BYTE (regno)] | 0x3; + ptrace (PT_WUREGS, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + scratch, 0); + if (errno != 0) + { + /* Error, even if attached. Failing to write these two + registers is pretty serious. */ + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } + else +#endif + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof(int)) + { + errno = 0; + ptrace (PTRACE_POKEUSR, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + *(int *) ®isters[REGISTER_BYTE (regno) + i]); + if (errno != 0) + { + /* Warning, not error, in case we are attached; sometimes the + kernel doesn't let us at the registers. */ + char *err = strerror (errno); + char *msg = alloca (strlen (err) + 128); + sprintf (msg, "writing register %d: %s", + regno, err); + error (msg); + return; + } + regaddr += sizeof(int); + } + } + else + for (regno = 0; regno < NUM_REGS-NUM_FREGS; regno++) + store_inferior_registers (regno); +} + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +void +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid, addr, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (PTRACE_PEEKTEXT, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (PTRACE_POKETEXT, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} diff --git a/gdb/gdbserver/low-lynx.c b/gdb/gdbserver/low-lynx.c new file mode 100644 index 0000000..3444f7a --- /dev/null +++ b/gdb/gdbserver/low-lynx.c @@ -0,0 +1,746 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1986, 1987, 1993 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. */ + +#include "server.h" +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#define LYNXOS +#include <sys/mem.h> +#include <sys/signal.h> +#include <sys/file.h> +#include <sys/kernel.h> +#ifndef __LYNXOS +#define __LYNXOS +#endif +#include <sys/itimer.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/fpp.h> + +char registers[REGISTER_BYTES]; + +#include <sys/ptrace.h> + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + int pgrp; + + /* Switch child to it's own process group so that signals won't + directly affect gdbserver. */ + + pgrp = getpid(); + setpgrp(0, pgrp); + ioctl (0, TIOCSPGRP, &pgrp); + + ptrace (PTRACE_TRACEME, 0, (PTRACE_ARG3_TYPE)0, 0); + + execv (program, allargs); + + fprintf (stderr, "GDBserver (process %d): Cannot exec %s: %s.\n", + getpid(), program, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (PTRACE_KILL, inferior_pid, 0, 0); + wait (0); + + inferior_pid = 0; +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + /* Arggh. Apparently pthread_kill only works for threads within + the process that calls pthread_kill. + + We want to avoid the lynx signal extensions as they simply don't + map well to the generic gdb interface we want to keep. + + All we want to do is determine if a particular thread is alive; + it appears as if we can just make a harmless thread specific + ptrace call to do that. */ + return (ptrace (PTRACE_THREADUSER, + BUILDPID (PIDGET (inferior_pid), pid), 0, 0) != -1); +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + union wait w; + + while (1) + { + enable_async_io(); + + pid = wait (&w); + + disable_async_io(); + + if (pid != PIDGET(inferior_pid)) + perror_with_name ("wait"); + + thread_from_wait = w.w_tid; + inferior_pid = BUILDPID (inferior_pid, w.w_tid); + + if (WIFSTOPPED(w) + && WSTOPSIG(w) == SIGTRAP) + { + int realsig; + + realsig = ptrace (PTRACE_GETTRACESIG, inferior_pid, + (PTRACE_ARG3_TYPE)0, 0); + + if (realsig == SIGNEWTHREAD) + { + /* It's a new thread notification. Nothing to do here since + the machine independent code in wait_for_inferior will + add the thread to the thread list and restart the thread + when pid != inferior_pid and pid is not in the thread list. + We don't even want to muck with realsig -- the code in + wait_for_inferior expects SIGTRAP. */ + ; + } + } + break; + } + + if (WIFEXITED (w)) + { + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PTRACE_SINGLESTEP_ONE : PTRACE_CONT, + BUILDPID (inferior_pid, cont_thread == -1 ? 0 : cont_thread), + 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER) + +/* Mapping between GDB register #s and offsets into econtext. Must be + consistent with REGISTER_NAMES macro in various tmXXX.h files. */ + +#define X(ENTRY)(offsetof(struct econtext, ENTRY)) + +#ifdef I386 +/* Mappings from tm-i386v.h */ + +static int regmap[] = +{ + X(eax), + X(ecx), + X(edx), + X(ebx), + X(esp), /* sp */ + X(ebp), /* fp */ + X(esi), + X(edi), + X(eip), /* pc */ + X(flags), /* ps */ + X(cs), + X(ss), + X(ds), + X(es), + X(ecode), /* Lynx doesn't give us either fs or gs, so */ + X(fault), /* we just substitute these two in the hopes + that they are useful. */ +}; +#endif + +#ifdef M68K +/* Mappings from tm-m68k.h */ + +static int regmap[] = +{ + X(regs[0]), /* d0 */ + X(regs[1]), /* d1 */ + X(regs[2]), /* d2 */ + X(regs[3]), /* d3 */ + X(regs[4]), /* d4 */ + X(regs[5]), /* d5 */ + X(regs[6]), /* d6 */ + X(regs[7]), /* d7 */ + X(regs[8]), /* a0 */ + X(regs[9]), /* a1 */ + X(regs[10]), /* a2 */ + X(regs[11]), /* a3 */ + X(regs[12]), /* a4 */ + X(regs[13]), /* a5 */ + X(regs[14]), /* fp */ + 0, /* sp */ + X(status), /* ps */ + X(pc), + + X(fregs[0*3]), /* fp0 */ + X(fregs[1*3]), /* fp1 */ + X(fregs[2*3]), /* fp2 */ + X(fregs[3*3]), /* fp3 */ + X(fregs[4*3]), /* fp4 */ + X(fregs[5*3]), /* fp5 */ + X(fregs[6*3]), /* fp6 */ + X(fregs[7*3]), /* fp7 */ + + X(fcregs[0]), /* fpcontrol */ + X(fcregs[1]), /* fpstatus */ + X(fcregs[2]), /* fpiaddr */ + X(ssw), /* fpcode */ + X(fault), /* fpflags */ +}; +#endif + +#ifdef SPARC +/* Mappings from tm-sparc.h */ + +#define FX(ENTRY)(offsetof(struct fcontext, ENTRY)) + +static int regmap[] = +{ + -1, /* g0 */ + X(g1), + X(g2), + X(g3), + X(g4), + -1, /* g5->g7 aren't saved by Lynx */ + -1, + -1, + + X(o[0]), + X(o[1]), + X(o[2]), + X(o[3]), + X(o[4]), + X(o[5]), + X(o[6]), /* sp */ + X(o[7]), /* ra */ + + -1,-1,-1,-1,-1,-1,-1,-1, /* l0 -> l7 */ + + -1,-1,-1,-1,-1,-1,-1,-1, /* i0 -> i7 */ + + FX(f.fregs[0]), /* f0 */ + FX(f.fregs[1]), + FX(f.fregs[2]), + FX(f.fregs[3]), + FX(f.fregs[4]), + FX(f.fregs[5]), + FX(f.fregs[6]), + FX(f.fregs[7]), + FX(f.fregs[8]), + FX(f.fregs[9]), + FX(f.fregs[10]), + FX(f.fregs[11]), + FX(f.fregs[12]), + FX(f.fregs[13]), + FX(f.fregs[14]), + FX(f.fregs[15]), + FX(f.fregs[16]), + FX(f.fregs[17]), + FX(f.fregs[18]), + FX(f.fregs[19]), + FX(f.fregs[20]), + FX(f.fregs[21]), + FX(f.fregs[22]), + FX(f.fregs[23]), + FX(f.fregs[24]), + FX(f.fregs[25]), + FX(f.fregs[26]), + FX(f.fregs[27]), + FX(f.fregs[28]), + FX(f.fregs[29]), + FX(f.fregs[30]), + FX(f.fregs[31]), + + X(y), + X(psr), + X(wim), + X(tbr), + X(pc), + X(npc), + FX(fsr), /* fpsr */ + -1, /* cpsr */ +}; +#endif + +#ifdef SPARC + +/* This routine handles some oddball cases for Sparc registers and LynxOS. + In partucular, it causes refs to G0, g5->7, and all fp regs to return zero. + It also handles knows where to find the I & L regs on the stack. */ + +void +fetch_inferior_registers (regno) + int regno; +{ +#if 0 + int whatregs = 0; + +#define WHATREGS_FLOAT 1 +#define WHATREGS_GEN 2 +#define WHATREGS_STACK 4 + + if (regno == -1) + whatregs = WHATREGS_FLOAT | WHATREGS_GEN | WHATREGS_STACK; + else if (regno >= L0_REGNUM && regno <= I7_REGNUM) + whatregs = WHATREGS_STACK; + else if (regno >= FP0_REGNUM && regno < FP0_REGNUM + 32) + whatregs = WHATREGS_FLOAT; + else + whatregs = WHATREGS_GEN; + + if (whatregs & WHATREGS_GEN) + { + struct econtext ec; /* general regs */ + char buf[MAX_REGISTER_RAW_SIZE]; + int retval; + int i; + + errno = 0; + retval = ptrace (PTRACE_GETREGS, + BUILDPID (inferior_pid, general_thread), + (PTRACE_ARG3_TYPE) &ec, + 0); + if (errno) + perror_with_name ("Sparc fetch_inferior_registers(ptrace)"); + + memset (buf, 0, REGISTER_RAW_SIZE (G0_REGNUM)); + supply_register (G0_REGNUM, buf); + supply_register (TBR_REGNUM, (char *)&ec.tbr); + + memcpy (®isters[REGISTER_BYTE (G1_REGNUM)], &ec.g1, + 4 * REGISTER_RAW_SIZE (G1_REGNUM)); + for (i = G1_REGNUM; i <= G1_REGNUM + 3; i++) + register_valid[i] = 1; + + supply_register (PS_REGNUM, (char *)&ec.psr); + supply_register (Y_REGNUM, (char *)&ec.y); + supply_register (PC_REGNUM, (char *)&ec.pc); + supply_register (NPC_REGNUM, (char *)&ec.npc); + supply_register (WIM_REGNUM, (char *)&ec.wim); + + memcpy (®isters[REGISTER_BYTE (O0_REGNUM)], ec.o, + 8 * REGISTER_RAW_SIZE (O0_REGNUM)); + for (i = O0_REGNUM; i <= O0_REGNUM + 7; i++) + register_valid[i] = 1; + } + + if (whatregs & WHATREGS_STACK) + { + CORE_ADDR sp; + int i; + + sp = read_register (SP_REGNUM); + + target_xfer_memory (sp + FRAME_SAVED_I0, + ®isters[REGISTER_BYTE(I0_REGNUM)], + 8 * REGISTER_RAW_SIZE (I0_REGNUM), 0); + for (i = I0_REGNUM; i <= I7_REGNUM; i++) + register_valid[i] = 1; + + target_xfer_memory (sp + FRAME_SAVED_L0, + ®isters[REGISTER_BYTE(L0_REGNUM)], + 8 * REGISTER_RAW_SIZE (L0_REGNUM), 0); + for (i = L0_REGNUM; i <= L0_REGNUM + 7; i++) + register_valid[i] = 1; + } + + if (whatregs & WHATREGS_FLOAT) + { + struct fcontext fc; /* fp regs */ + int retval; + int i; + + errno = 0; + retval = ptrace (PTRACE_GETFPREGS, BUILDPID (inferior_pid, general_thread), (PTRACE_ARG3_TYPE) &fc, + 0); + if (errno) + perror_with_name ("Sparc fetch_inferior_registers(ptrace)"); + + memcpy (®isters[REGISTER_BYTE (FP0_REGNUM)], fc.f.fregs, + 32 * REGISTER_RAW_SIZE (FP0_REGNUM)); + for (i = FP0_REGNUM; i <= FP0_REGNUM + 31; i++) + register_valid[i] = 1; + + supply_register (FPS_REGNUM, (char *)&fc.fsr); + } +#endif +} + +/* This routine handles storing of the I & L regs for the Sparc. The trick + here is that they actually live on the stack. The really tricky part is + that when changing the stack pointer, the I & L regs must be written to + where the new SP points, otherwise the regs will be incorrect when the + process is started up again. We assume that the I & L regs are valid at + this point. */ + +void +store_inferior_registers (regno) + int regno; +{ +#if 0 + int whatregs = 0; + + if (regno == -1) + whatregs = WHATREGS_FLOAT | WHATREGS_GEN | WHATREGS_STACK; + else if (regno >= L0_REGNUM && regno <= I7_REGNUM) + whatregs = WHATREGS_STACK; + else if (regno >= FP0_REGNUM && regno < FP0_REGNUM + 32) + whatregs = WHATREGS_FLOAT; + else if (regno == SP_REGNUM) + whatregs = WHATREGS_STACK | WHATREGS_GEN; + else + whatregs = WHATREGS_GEN; + + if (whatregs & WHATREGS_GEN) + { + struct econtext ec; /* general regs */ + int retval; + + ec.tbr = read_register (TBR_REGNUM); + memcpy (&ec.g1, ®isters[REGISTER_BYTE (G1_REGNUM)], + 4 * REGISTER_RAW_SIZE (G1_REGNUM)); + + ec.psr = read_register (PS_REGNUM); + ec.y = read_register (Y_REGNUM); + ec.pc = read_register (PC_REGNUM); + ec.npc = read_register (NPC_REGNUM); + ec.wim = read_register (WIM_REGNUM); + + memcpy (ec.o, ®isters[REGISTER_BYTE (O0_REGNUM)], + 8 * REGISTER_RAW_SIZE (O0_REGNUM)); + + errno = 0; + retval = ptrace (PTRACE_SETREGS, BUILDPID (inferior_pid, general_thread), (PTRACE_ARG3_TYPE) &ec, + 0); + if (errno) + perror_with_name ("Sparc fetch_inferior_registers(ptrace)"); + } + + if (whatregs & WHATREGS_STACK) + { + int regoffset; + CORE_ADDR sp; + + sp = read_register (SP_REGNUM); + + if (regno == -1 || regno == SP_REGNUM) + { + if (!register_valid[L0_REGNUM+5]) + abort(); + target_xfer_memory (sp + FRAME_SAVED_I0, + ®isters[REGISTER_BYTE (I0_REGNUM)], + 8 * REGISTER_RAW_SIZE (I0_REGNUM), 1); + + target_xfer_memory (sp + FRAME_SAVED_L0, + ®isters[REGISTER_BYTE (L0_REGNUM)], + 8 * REGISTER_RAW_SIZE (L0_REGNUM), 1); + } + else if (regno >= L0_REGNUM && regno <= I7_REGNUM) + { + if (!register_valid[regno]) + abort(); + if (regno >= L0_REGNUM && regno <= L0_REGNUM + 7) + regoffset = REGISTER_BYTE (regno) - REGISTER_BYTE (L0_REGNUM) + + FRAME_SAVED_L0; + else + regoffset = REGISTER_BYTE (regno) - REGISTER_BYTE (I0_REGNUM) + + FRAME_SAVED_I0; + target_xfer_memory (sp + regoffset, ®isters[REGISTER_BYTE (regno)], + REGISTER_RAW_SIZE (regno), 1); + } + } + + if (whatregs & WHATREGS_FLOAT) + { + struct fcontext fc; /* fp regs */ + int retval; + +/* We read fcontext first so that we can get good values for fq_t... */ + errno = 0; + retval = ptrace (PTRACE_GETFPREGS, BUILDPID (inferior_pid, general_thread), (PTRACE_ARG3_TYPE) &fc, + 0); + if (errno) + perror_with_name ("Sparc fetch_inferior_registers(ptrace)"); + + memcpy (fc.f.fregs, ®isters[REGISTER_BYTE (FP0_REGNUM)], + 32 * REGISTER_RAW_SIZE (FP0_REGNUM)); + + fc.fsr = read_register (FPS_REGNUM); + + errno = 0; + retval = ptrace (PTRACE_SETFPREGS, BUILDPID (inferior_pid, general_thread), (PTRACE_ARG3_TYPE) &fc, + 0); + if (errno) + perror_with_name ("Sparc fetch_inferior_registers(ptrace)"); + } +#endif +} +#endif /* SPARC */ + +#ifndef SPARC + +/* Return the offset relative to the start of the per-thread data to the + saved context block. */ + +static unsigned long +lynx_registers_addr() +{ + CORE_ADDR stblock; + int ecpoff = offsetof(st_t, ecp); + CORE_ADDR ecp; + + errno = 0; + stblock = (CORE_ADDR) ptrace (PTRACE_THREADUSER, BUILDPID (inferior_pid, general_thread), + (PTRACE_ARG3_TYPE)0, 0); + if (errno) + perror_with_name ("PTRACE_THREADUSER"); + + ecp = (CORE_ADDR) ptrace (PTRACE_PEEKTHREAD, BUILDPID (inferior_pid, general_thread), + (PTRACE_ARG3_TYPE)ecpoff, 0); + if (errno) + perror_with_name ("lynx_registers_addr(PTRACE_PEEKTHREAD)"); + + return ecp - stblock; +} + +/* Fetch one or more registers from the inferior. REGNO == -1 to get + them all. We actually fetch more than requested, when convenient, + marking them as valid so we won't fetch them again. */ + +void +fetch_inferior_registers (ignored) + int ignored; +{ + int regno; + unsigned long reg; + unsigned long ecp; + + ecp = lynx_registers_addr(); + + for (regno = 0; regno < NUM_REGS; regno++) + { + int ptrace_fun = PTRACE_PEEKTHREAD; + +#ifdef PTRACE_PEEKUSP + ptrace_fun = regno == SP_REGNUM ? PTRACE_PEEKUSP : PTRACE_PEEKTHREAD; +#endif + + errno = 0; + reg = ptrace (ptrace_fun, BUILDPID (inferior_pid, general_thread), + (PTRACE_ARG3_TYPE) (ecp + regmap[regno]), 0); + if (errno) + perror_with_name ("fetch_inferior_registers(PTRACE_PEEKTHREAD)"); + + *(unsigned long *)®isters[REGISTER_BYTE (regno)] = reg; + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (ignored) + int ignored; +{ + int regno; + unsigned long reg; + unsigned long ecp; + + ecp = lynx_registers_addr(); + + for (regno = 0; regno < NUM_REGS; regno++) + { + int ptrace_fun = PTRACE_POKEUSER; + +#ifdef PTRACE_POKEUSP + ptrace_fun = regno == SP_REGNUM ? PTRACE_POKEUSP : PTRACE_POKEUSER; +#endif + + reg = *(unsigned long *)®isters[REGISTER_BYTE (regno)]; + + errno = 0; + ptrace (ptrace_fun, BUILDPID (inferior_pid, general_thread), + (PTRACE_ARG3_TYPE) (ecp + regmap[regno]), reg); + if (errno) + perror_with_name ("PTRACE_POKEUSER"); + } +} + +#endif /* ! SPARC */ + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +void +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (PTRACE_PEEKTEXT, BUILDPID (inferior_pid, general_thread), addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (PTRACE_PEEKTEXT, BUILDPID (inferior_pid, general_thread), addr, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (PTRACE_PEEKTEXT, BUILDPID (inferior_pid, general_thread), + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + while (1) + { + errno = 0; + ptrace (PTRACE_POKETEXT, BUILDPID (inferior_pid, general_thread), addr, buffer[i]); + if (errno) + { + fprintf(stderr, "\ +ptrace (PTRACE_POKETEXT): errno=%d, pid=0x%x, addr=0x%x, buffer[i] = 0x%x\n", + errno, BUILDPID (inferior_pid, general_thread), + addr, buffer[i]); + fprintf(stderr, "Sleeping for 1 second\n"); + sleep(1); + } + else + break; + } + } + + return 0; +} diff --git a/gdb/gdbserver/low-sim.c b/gdb/gdbserver/low-sim.c new file mode 100644 index 0000000..8ad6e91 --- /dev/null +++ b/gdb/gdbserver/low-sim.c @@ -0,0 +1,289 @@ +/* Low level interface to simulators, for the remote server for GDB. + Copyright (C) 1995, 1996 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. */ + +#include "defs.h" +#include "bfd.h" +#include "server.h" +#include "callback.h" /* GDB simulator callback interface */ +#include "remote-sim.h" /* GDB simulator interface */ + +extern int remote_debug; + +extern host_callback default_callback; /* in sim/common/callback.c */ + +char registers[REGISTER_BYTES] __attribute__ ((aligned)); + +int target_byte_order; /* used by simulator */ + +/* We record the result of sim_open so we can pass it + back to the other sim_foo routines. */ +static SIM_DESC gdbsim_desc = 0; + +/* This version of "load" should be usable for any simulator that + does not support loading itself. */ + +static void +generic_load (loadfile_bfd) + bfd *loadfile_bfd; +{ + asection *s; + + for (s = loadfile_bfd->sections; s; s = s->next) + { + if (s->flags & SEC_LOAD) + { + bfd_size_type size; + + size = bfd_get_section_size_before_reloc (s); + if (size > 0) + { + char *buffer; + bfd_vma lma; /* use load address, not virtual address */ + + buffer = xmalloc (size); + lma = s->lma; + + /* Is this really necessary? I guess it gives the user something + to look at during a long download. */ + printf ("Loading section %s, size 0x%lx lma 0x%lx\n", + bfd_get_section_name (loadfile_bfd, s), + (unsigned long) size, + (unsigned long) lma); /* chops high 32 bits. FIXME!! */ + + bfd_get_section_contents (loadfile_bfd, s, buffer, 0, size); + + write_inferior_memory (lma, buffer, size); + free (buffer); + } + } + } + + printf ("Start address 0x%lx\n", + (unsigned long)loadfile_bfd->start_address); + + /* We were doing this in remote-mips.c, I suspect it is right + for other targets too. */ + /* write_pc (loadfile_bfd->start_address); */ /* FIXME!! */ +} + +int +create_inferior (program, argv) + char *program; + char **argv; +{ + bfd *abfd; + int pid = 0; +#ifdef TARGET_BYTE_ORDER_SELECTABLE + char **new_argv; + int nargs; +#endif + + abfd = bfd_openr (program, 0); + if (!abfd) + { + fprintf (stderr, "gdbserver: can't open %s: %s\n", + program, bfd_errmsg (bfd_get_error ())); + exit (1); + } + + if (!bfd_check_format (abfd, bfd_object)) + { + fprintf (stderr, "gdbserver: unknown load format for %s: %s\n", + program, bfd_errmsg (bfd_get_error ())); + exit (1); + } + +#ifdef TARGET_BYTE_ORDER_SELECTABLE + /* Add "-E big" or "-E little" to the argument list depending on the + endianness of the program to be loaded. */ + for (nargs = 0; argv[nargs] != NULL; nargs++) /* count the args */ + ; + new_argv = alloca (sizeof (char *) * (nargs + 3)); /* allocate new args */ + for (nargs = 0; argv[nargs] != NULL; nargs++) /* copy old to new */ + new_argv[nargs] = argv[nargs]; + new_argv[nargs] = "-E"; + new_argv[nargs + 1] = bfd_big_endian (abfd) ? "big" : "little"; + new_argv[nargs + 2] = NULL; + argv = new_argv; +#endif + + /* Create an instance of the simulator. */ + default_callback.init (&default_callback); + gdbsim_desc = sim_open (SIM_OPEN_STANDALONE, &default_callback, abfd, argv); + if (gdbsim_desc == 0) + exit (1); + + /* Load the program into the simulator. */ + if (abfd) + if (sim_load (gdbsim_desc, program, NULL, 0) == SIM_RC_FAIL) + generic_load (abfd); + + /* Create an inferior process in the simulator. This initializes SP. */ + sim_create_inferior (gdbsim_desc, abfd, argv, /* env */ NULL); + sim_resume (gdbsim_desc, 1, 0); /* execute one instr */ + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + sim_close (gdbsim_desc, 0); + default_callback.shutdown (&default_callback); +} + +/* Fetch one register. */ + +static void +fetch_register (regno) + int regno; +{ + sim_fetch_register (gdbsim_desc, regno, ®isters[REGISTER_BYTE (regno)], + REGISTER_RAW_SIZE (regno)); +} + +/* Fetch all registers, or just one, from the child process. */ + +void +fetch_inferior_registers (regno) + int regno; +{ + if (regno == -1 || regno == 0) + for (regno = 0; regno < NUM_REGS/*-NUM_FREGS*/; regno++) + fetch_register (regno); + else + fetch_register (regno); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (regno) + int regno; +{ + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + store_inferior_registers (regno); + } + else + sim_store_register (gdbsim_desc, regno, ®isters[REGISTER_BYTE (regno)], + REGISTER_RAW_SIZE (regno)); +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int sigrc; + enum sim_stop reason; + + sim_stop_reason (gdbsim_desc, &reason, &sigrc); + switch (reason) + { + case sim_exited: + if (remote_debug) + printf ("\nChild exited with retcode = %x \n", sigrc); + *status = 'W'; + return sigrc; + +#if 0 + case sim_stopped: + if (remote_debug) + printf ("\nChild terminated with signal = %x \n", sigrc); + *status = 'X'; + return sigrc; +#endif + + default: /* should this be sim_signalled or sim_stopped? FIXME!! */ + if (remote_debug) + printf ("\nChild received signal = %x \n", sigrc); + fetch_inferior_registers (0); + *status = 'T'; + return (unsigned char) sigrc; + } +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signo) + int step; + int signo; +{ + /* Should be using target_signal_to_host() or signal numbers in target.h + to convert GDB signal number to target signal number. */ + sim_resume (gdbsim_desc, step, signo); +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +void +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + sim_read (gdbsim_desc, memaddr, myaddr, len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + sim_write (gdbsim_desc, memaddr, myaddr, len); /* should check for error. FIXME!! */ + return 0; +} + +#if 0 +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} +#endif diff --git a/gdb/gdbserver/low-sparc.c b/gdb/gdbserver/low-sparc.c new file mode 100644 index 0000000..ddffaec --- /dev/null +++ b/gdb/gdbserver/low-sparc.c @@ -0,0 +1,334 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1986, 1987, 1993 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. */ + +#include "defs.h" +#include <sys/wait.h> +#include "frame.h" +#include "inferior.h" +/*************************** +#include "initialize.h" +****************************/ + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> + +/***************Begin MY defs*********************/ +int quit_flag = 0; +char registers[REGISTER_BYTES]; + +/* Index within `registers' of the first byte of the space for + register N. */ + + +char buf2[MAX_REGISTER_RAW_SIZE]; +/***************End MY defs*********************/ + +#include <sys/ptrace.h> +#include <sys/reg.h> + +extern int sys_nerr; +extern char **sys_errlist; +extern char **environ; +extern int errno; +extern int inferior_pid; +void quit (), perror_with_name (); +int query (); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PTRACE_TRACEME); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + /*************inferior_died ();****VK**************/ +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + union wait w; + + pid = wait (&w); + if (pid != inferior_pid) + perror_with_name ("wait"); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + +/* Fetch one or more registers from the inferior. REGNO == -1 to get + them all. We actually fetch more than requested, when convenient, + marking them as valid so we won't fetch them again. */ + +void +fetch_inferior_registers (ignored) + int ignored; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + int i; + + /* Global and Out regs are fetched directly, as well as the control + registers. If we're getting one of the in or local regs, + and the stack pointer has not yet been fetched, + we have to do that first, since they're found in memory relative + to the stack pointer. */ + + if (ptrace (PTRACE_GETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0)) + perror("ptrace_getregs"); + + registers[REGISTER_BYTE (0)] = 0; + memcpy (®isters[REGISTER_BYTE (1)], &inferior_registers.r_g1, + 15 * REGISTER_RAW_SIZE (G0_REGNUM)); + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = inferior_registers.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = inferior_registers.r_pc; + *(int *)®isters[REGISTER_BYTE (NPC_REGNUM)] = inferior_registers.r_npc; + *(int *)®isters[REGISTER_BYTE (Y_REGNUM)] = inferior_registers.r_y; + + /* Floating point registers */ + + if (ptrace (PTRACE_GETFPREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_fp_registers, + 0)) + perror("ptrace_getfpregs"); + memcpy (®isters[REGISTER_BYTE (FP0_REGNUM)], &inferior_fp_registers, + sizeof inferior_fp_registers.fpu_fr); + + /* These regs are saved on the stack by the kernel. Only read them + all (16 ptrace calls!) if we really need them. */ + + read_inferior_memory (*(CORE_ADDR*)®isters[REGISTER_BYTE (SP_REGNUM)], + ®isters[REGISTER_BYTE (L0_REGNUM)], + 16*REGISTER_RAW_SIZE (L0_REGNUM)); +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (ignored) + int ignored; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + CORE_ADDR sp = *(CORE_ADDR *)®isters[REGISTER_BYTE (SP_REGNUM)]; + + write_inferior_memory (sp, ®isters[REGISTER_BYTE (L0_REGNUM)], + 16*REGISTER_RAW_SIZE (L0_REGNUM)); + + memcpy (&inferior_registers.r_g1, ®isters[REGISTER_BYTE (G1_REGNUM)], + 15 * REGISTER_RAW_SIZE (G1_REGNUM)); + + inferior_registers.r_ps = + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)]; + inferior_registers.r_pc = + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)]; + inferior_registers.r_npc = + *(int *)®isters[REGISTER_BYTE (NPC_REGNUM)]; + inferior_registers.r_y = + *(int *)®isters[REGISTER_BYTE (Y_REGNUM)]; + + if (ptrace (PTRACE_SETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0)) + perror("ptrace_setregs"); + + memcpy (&inferior_fp_registers, ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof inferior_fp_registers.fpu_fr); + + if (ptrace (PTRACE_SETFPREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_fp_registers, 0)) + perror("ptrace_setfpregs"); +} + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (1, inferior_pid, addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} diff --git a/gdb/gdbserver/low-sun3.c b/gdb/gdbserver/low-sun3.c new file mode 100644 index 0000000..c84b79f --- /dev/null +++ b/gdb/gdbserver/low-sun3.c @@ -0,0 +1,313 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1986, 1987, 1993 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. */ + +#include "defs.h" +#include "<sys/wait.h>" +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> + +/***************Begin MY defs*********************/ +int quit_flag = 0; +char registers[REGISTER_BYTES]; + +/* Index within `registers' of the first byte of the space for + register N. */ + + +char buf2[MAX_REGISTER_RAW_SIZE]; +/***************End MY defs*********************/ + +#include <sys/ptrace.h> +#include <machine/reg.h> + +extern int sys_nerr; +extern char **sys_errlist; +extern char **environ; +extern int errno; +extern int inferior_pid; +void quit (), perror_with_name (); +int query (); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PTRACE_TRACEME); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + /*************inferior_died ();****VK**************/ +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + union wait w; + + pid = wait (&w); + if (pid != inferior_pid) + perror_with_name ("wait"); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + +/* Fetch one or more registers from the inferior. REGNO == -1 to get + them all. We actually fetch more than requested, when convenient, + marking them as valid so we won't fetch them again. */ + +void +fetch_inferior_registers (ignored) + int ignored; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + + ptrace (PTRACE_GETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers); +#ifdef FP0_REGNUM + ptrace (PTRACE_GETFPREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_fp_registers); +#endif + + memcpy (registers, &inferior_registers, 16 * 4); +#ifdef FP0_REGNUM + memcpy (®isters[REGISTER_BYTE (FP0_REGNUM)], &inferior_fp_registers, + sizeof inferior_fp_registers.fps_regs); +#endif + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = inferior_registers.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = inferior_registers.r_pc; +#ifdef FP0_REGNUM + memcpy + (®isters[REGISTER_BYTE (FPC_REGNUM)], + &inferior_fp_registers.fps_control, + sizeof inferior_fp_registers - sizeof inferior_fp_registers.fps_regs); +#endif +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (ignored) + int ignored; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + + memcpy (&inferior_registers, registers, 16 * 4); +#ifdef FP0_REGNUM + memcpy (&inferior_fp_registers, + ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof inferior_fp_registers.fps_regs); +#endif + inferior_registers.r_ps = *(int *)®isters[REGISTER_BYTE (PS_REGNUM)]; + inferior_registers.r_pc = *(int *)®isters[REGISTER_BYTE (PC_REGNUM)]; + +#ifdef FP0_REGNUM + memcpy (&inferior_fp_registers.fps_control, + ®isters[REGISTER_BYTE (FPC_REGNUM)], + (sizeof inferior_fp_registers + - sizeof inferior_fp_registers.fps_regs)); +#endif + + ptrace (PTRACE_SETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers); +#if FP0_REGNUM + ptrace (PTRACE_SETFPREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_fp_registers); +#endif +} + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (1, inferior_pid, addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c new file mode 100644 index 0000000..0a7e0fb --- /dev/null +++ b/gdb/gdbserver/remote-utils.c @@ -0,0 +1,545 @@ +/* Remote utility routines for the remote server for GDB. + Copyright (C) 1986, 1989, 1993 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. */ + +#include "server.h" +#include "terminal.h" +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/tcp.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <fcntl.h> + +int remote_debug = 0; + +static int remote_desc; + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +void +remote_open (name) + char *name; +{ + int save_fcntl_flags; + + if (!strchr (name, ':')) + { + remote_desc = open (name, O_RDWR); + if (remote_desc < 0) + perror_with_name ("Could not open remote device"); + +#ifdef HAVE_TERMIOS + { + struct termios termios; + tcgetattr(remote_desc, &termios); + + termios.c_iflag = 0; + termios.c_oflag = 0; + termios.c_lflag = 0; + termios.c_cflag &= ~(CSIZE|PARENB); + termios.c_cflag |= CLOCAL | CS8; + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 0; + + tcsetattr(remote_desc, TCSANOW, &termios); + } +#endif + +#ifdef HAVE_TERMIO + { + struct termio termio; + ioctl (remote_desc, TCGETA, &termio); + + termio.c_iflag = 0; + termio.c_oflag = 0; + termio.c_lflag = 0; + termio.c_cflag &= ~(CSIZE|PARENB); + termio.c_cflag |= CLOCAL | CS8; + termio.c_cc[VMIN] = 0; + termio.c_cc[VTIME] = 0; + + ioctl (remote_desc, TCSETA, &termio); + } +#endif + +#ifdef HAVE_SGTTY + { + struct sgttyb sg; + + ioctl (remote_desc, TIOCGETP, &sg); + sg.sg_flags = RAW; + ioctl (remote_desc, TIOCSETP, &sg); + } +#endif + + + } + else + { + char *port_str; + int port; + struct sockaddr_in sockaddr; + int tmp; + struct protoent *protoent; + int tmp_desc; + + port_str = strchr (name, ':'); + + port = atoi (port_str + 1); + + tmp_desc = socket (PF_INET, SOCK_STREAM, 0); + if (tmp_desc < 0) + perror_with_name ("Can't open socket"); + + /* Allow rapid reuse of this port. */ + tmp = 1; + setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp, + sizeof(tmp)); + + sockaddr.sin_family = PF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + if (bind (tmp_desc, (struct sockaddr *)&sockaddr, sizeof (sockaddr)) + || listen (tmp_desc, 1)) + perror_with_name ("Can't bind address"); + + tmp = sizeof (sockaddr); + remote_desc = accept (tmp_desc, (struct sockaddr *)&sockaddr, &tmp); + if (remote_desc == -1) + perror_with_name ("Accept failed"); + + protoent = getprotobyname ("tcp"); + if (!protoent) + perror_with_name ("getprotobyname"); + + /* Enable TCP keep alive process. */ + tmp = 1; + setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp)); + + /* Tell TCP not to delay small packets. This greatly speeds up + interactive response. */ + tmp = 1; + setsockopt (remote_desc, protoent->p_proto, TCP_NODELAY, + (char *)&tmp, sizeof(tmp)); + + close (tmp_desc); /* No longer need this */ + + signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbserver simply + exits when the remote side dies. */ + } + +#if defined(F_SETFL) && defined (FASYNC) + save_fcntl_flags = fcntl (remote_desc, F_GETFL, 0); + fcntl (remote_desc, F_SETFL, save_fcntl_flags | FASYNC); + disable_async_io (); +#endif /* FASYNC */ + fprintf (stderr, "Remote debugging using %s\n", name); +} + +void +remote_close() +{ + close (remote_desc); +} + +/* Convert hex digit A to a number. */ + +static int +fromhex (a) + int a; +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + error ("Reply contains invalid hex digit"); +} + +/* Convert number NIB to a hex digit. */ + +static int +tohex (nib) + int nib; +{ + if (nib < 10) + return '0' + nib; + else + return 'a' + nib - 10; +} + +/* Send a packet to the remote machine, with error checking. + The data of the packet is in BUF. Returns >= 0 on success, -1 otherwise. */ + +int +putpkt (buf) + char *buf; +{ + int i; + unsigned char csum = 0; + char buf2[2000]; + char buf3[1]; + int cnt = strlen (buf); + char *p; + + /* Copy the packet into buffer BUF2, encapsulating it + and giving it a checksum. */ + + p = buf2; + *p++ = '$'; + + for (i = 0; i < cnt; i++) + { + csum += buf[i]; + *p++ = buf[i]; + } + *p++ = '#'; + *p++ = tohex ((csum >> 4) & 0xf); + *p++ = tohex (csum & 0xf); + + *p = '\0'; + + /* Send it over and over until we get a positive ack. */ + + do + { + int cc; + + if (write (remote_desc, buf2, p - buf2) != p - buf2) + { + perror ("putpkt(write)"); + return -1; + } + + if (remote_debug) + printf ("putpkt (\"%s\"); [looking for ack]\n", buf2); + cc = read (remote_desc, buf3, 1); + if (remote_debug) + printf ("[received '%c' (0x%x)]\n", buf3[0], buf3[0]); + if (cc <= 0) + { + if (cc == 0) + fprintf (stderr, "putpkt(read): Got EOF\n"); + else + perror ("putpkt(read)"); + + return -1; + } + } + while (buf3[0] != '+'); + + return 1; /* Success! */ +} + +/* Come here when we get an input interrupt from the remote side. This + interrupt should only be active while we are waiting for the child to do + something. About the only thing that should come through is a ^C, which + will cause us to send a SIGINT to the child. */ + +static void +input_interrupt() +{ + int cc; + char c; + + cc = read (remote_desc, &c, 1); + + if (cc != 1 || c != '\003') + { + fprintf(stderr, "input_interrupt, cc = %d c = %d\n", cc, c); + return; + } + + kill (inferior_pid, SIGINT); +} + +void +enable_async_io() +{ + signal (SIGIO, input_interrupt); +} + +void +disable_async_io() +{ + signal (SIGIO, SIG_IGN); +} + +/* Returns next char from remote GDB. -1 if error. */ + +static int +readchar () +{ + static char buf[BUFSIZ]; + static int bufcnt = 0; + static char *bufp; + + if (bufcnt-- > 0) + return *bufp++ & 0x7f; + + bufcnt = read (remote_desc, buf, sizeof (buf)); + + if (bufcnt <= 0) + { + if (bufcnt == 0) + fprintf (stderr, "readchar: Got EOF\n"); + else + perror ("readchar"); + + return -1; + } + + bufp = buf; + bufcnt--; + return *bufp++ & 0x7f; +} + +/* Read a packet from the remote machine, with error checking, + and store it in BUF. Returns length of packet, or negative if error. */ + +int +getpkt (buf) + char *buf; +{ + char *bp; + unsigned char csum, c1, c2; + int c; + + while (1) + { + csum = 0; + + while (1) + { + c = readchar (); + if (c == '$') + break; + if (remote_debug) + printf ("[getpkt: discarding char '%c']\n", c); + if (c < 0) + return -1; + } + + bp = buf; + while (1) + { + c = readchar (); + if (c < 0) + return -1; + if (c == '#') + break; + *bp++ = c; + csum += c; + } + *bp = 0; + + c1 = fromhex (readchar ()); + c2 = fromhex (readchar ()); + + if (csum == (c1 << 4) + c2) + break; + + fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n", + (c1 << 4) + c2, csum, buf); + write (remote_desc, "-", 1); + } + + if (remote_debug) + printf ("getpkt (\"%s\"); [sending ack] \n", buf); + + write (remote_desc, "+", 1); + + if (remote_debug) + printf ("[sent ack]\n"); + return bp - buf; +} + +void +write_ok (buf) + char *buf; +{ + buf[0] = 'O'; + buf[1] = 'K'; + buf[2] = '\0'; +} + +void +write_enn (buf) + char *buf; +{ + buf[0] = 'E'; + buf[1] = 'N'; + buf[2] = 'N'; + buf[3] = '\0'; +} + +void +convert_int_to_ascii (from, to, n) + char *from, *to; + int n; +{ + int nib; + char ch; + while (n--) + { + ch = *from++; + nib = ((ch & 0xf0) >> 4) & 0x0f; + *to++ = tohex (nib); + nib = ch & 0x0f; + *to++ = tohex (nib); + } + *to++ = 0; +} + + +void +convert_ascii_to_int (from, to, n) + char *from, *to; + int n; +{ + int nib1, nib2; + while (n--) + { + nib1 = fromhex (*from++); + nib2 = fromhex (*from++); + *to++ = (((nib1 & 0x0f) << 4) & 0xf0) | (nib2 & 0x0f); + } +} + +static char * +outreg(regno, buf) + int regno; + char *buf; +{ + extern char registers[]; + int regsize = REGISTER_RAW_SIZE (regno); + + *buf++ = tohex (regno >> 4); + *buf++ = tohex (regno & 0xf); + *buf++ = ':'; + convert_int_to_ascii (®isters[REGISTER_BYTE (regno)], buf, regsize); + buf += 2 * regsize; + *buf++ = ';'; + + return buf; +} + +void +prepare_resume_reply (buf, status, signo) + char *buf; + char status; + unsigned char signo; +{ + int nib; + + *buf++ = status; + + /* FIXME! Should be converting this signal number (numbered + according to the signal numbering of the system we are running on) + to the signal numbers used by the gdb protocol (see enum target_signal + in gdb/target.h). */ + nib = ((signo & 0xf0) >> 4); + *buf++ = tohex (nib); + nib = signo & 0x0f; + *buf++ = tohex (nib); + + if (status == 'T') + { + buf = outreg (PC_REGNUM, buf); + buf = outreg (FP_REGNUM, buf); + buf = outreg (SP_REGNUM, buf); +#ifdef NPC_REGNUM + buf = outreg (NPC_REGNUM, buf); +#endif +#ifdef O7_REGNUM + buf = outreg (O7_REGNUM, buf); +#endif + + /* If the debugger hasn't used any thread features, don't burden it with + threads. If we didn't check this, GDB 4.13 and older would choke. */ + if (cont_thread != 0) + { + if (old_thread_from_wait != thread_from_wait) + { + sprintf (buf, "thread:%x;", thread_from_wait); + buf += strlen (buf); + old_thread_from_wait = thread_from_wait; + } + } + } + /* For W and X, we're done. */ + *buf++ = 0; +} + +void +decode_m_packet (from, mem_addr_ptr, len_ptr) + char *from; + CORE_ADDR *mem_addr_ptr; + unsigned int *len_ptr; +{ + int i = 0, j = 0; + char ch; + *mem_addr_ptr = *len_ptr = 0; + + while ((ch = from[i++]) != ',') + { + *mem_addr_ptr = *mem_addr_ptr << 4; + *mem_addr_ptr |= fromhex (ch) & 0x0f; + } + + for (j = 0; j < 4; j++) + { + if ((ch = from[i++]) == 0) + break; + *len_ptr = *len_ptr << 4; + *len_ptr |= fromhex (ch) & 0x0f; + } +} + +void +decode_M_packet (from, mem_addr_ptr, len_ptr, to) + char *from, *to; + CORE_ADDR *mem_addr_ptr; + unsigned int *len_ptr; +{ + int i = 0; + char ch; + *mem_addr_ptr = *len_ptr = 0; + + while ((ch = from[i++]) != ',') + { + *mem_addr_ptr = *mem_addr_ptr << 4; + *mem_addr_ptr |= fromhex (ch) & 0x0f; + } + + while ((ch = from[i++]) != ':') + { + *len_ptr = *len_ptr << 4; + *len_ptr |= fromhex (ch) & 0x0f; + } + + convert_ascii_to_int (&from[i++], to, *len_ptr); +} diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c new file mode 100644 index 0000000..cdec0f8 --- /dev/null +++ b/gdb/gdbserver/server.c @@ -0,0 +1,258 @@ +/* Main code for remote server for GDB. + Copyright (C) 1989, 1993 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. */ + +#include "server.h" + +int cont_thread; +int general_thread; +int thread_from_wait; +int old_thread_from_wait; +int extended_protocol; +jmp_buf toplevel; +int inferior_pid; + +static unsigned char +start_inferior (argv, statusptr) + char *argv[]; + char *statusptr; +{ + inferior_pid = create_inferior (argv[0], argv); + fprintf (stderr, "Process %s created; pid = %d\n", argv[0], inferior_pid); + + /* Wait till we are at 1st instruction in program, return signal number. */ + return mywait (statusptr); +} + +extern int remote_debug; + +int +main (argc, argv) + int argc; + char *argv[]; +{ + char ch, status, own_buf[2000], mem_buf[2000]; + int i = 0; + unsigned char signal; + unsigned int len; + CORE_ADDR mem_addr; + + if (setjmp(toplevel)) + { + fprintf(stderr, "Exiting\n"); + exit(1); + } + + if (argc < 3) + error("Usage: gdbserver tty prog [args ...]"); + + /* Wait till we are at first instruction in program. */ + signal = start_inferior (&argv[2], &status); + + /* We are now stopped at the first instruction of the target process */ + + while (1) + { + remote_open (argv[1]); + +restart: + setjmp(toplevel); + while (getpkt (own_buf) > 0) + { + unsigned char sig; + i = 0; + ch = own_buf[i++]; + switch (ch) + { + 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"); + + /* Wait till we are at 1st instruction in prog. */ + signal = start_inferior (&argv[2], &status); + goto restart; + break; + } + else + { + exit (0); + 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 + { + /* 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; + } + 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 (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 + { + fprintf (stderr, "GDBserver exiting\n"); + exit (0); + } + } + } + + /* We come here when getpkt fails. + + For the extended remote protocol we exit (and this is the only + way we gracefully exit!). + + For the traditional remote protocol close the connection, + and re-open it at the top of the loop. */ + if (extended_protocol) + { + remote_close (); + exit (0); + } + else + { + fprintf (stderr, "Remote side has terminated connection. GDBserver will reopen the connection.\n"); + + remote_close (); + } + } +} diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h new file mode 100644 index 0000000..e23c773 --- /dev/null +++ b/gdb/gdbserver/server.h @@ -0,0 +1,72 @@ +/* Common definitions for remote server for GDB. + Copyright (C) 1993 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. */ + +#include "defs.h" +#include <setjmp.h> + +/* Target-specific functions */ + +int create_inferior PARAMS ((char *program, char **allargs)); +void kill_inferior PARAMS ((void)); +void fetch_inferior_registers PARAMS ((int regno)); +void store_inferior_registers PARAMS ((int regno)); +int mythread_alive PARAMS ((int pid)); +void myresume PARAMS ((int step, int signo)); +unsigned char mywait PARAMS ((char *status)); +void read_inferior_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len)); +int write_inferior_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len)); +int create_inferior (); + +/* Target-specific variables */ + +extern char registers[]; + +/* Public variables in server.c */ + +extern int cont_thread; +extern int general_thread; +extern int thread_from_wait; +extern int old_thread_from_wait; + +extern jmp_buf toplevel; +extern int inferior_pid; + +/* Functions from remote-utils.c */ + +int putpkt PARAMS ((char *buf)); +int getpkt PARAMS ((char *buf)); +void remote_open PARAMS ((char *name)); +void remote_close PARAMS ((void)); +void write_ok PARAMS ((char *buf)); +void write_enn PARAMS ((char *buf)); +void enable_async_io PARAMS ((void)); +void disable_async_io PARAMS ((void)); +void convert_ascii_to_int PARAMS ((char *from, char *to, int n)); +void convert_int_to_ascii PARAMS ((char *from, char *to, int n)); +void prepare_resume_reply PARAMS ((char *buf, char status, unsigned char sig)); + +void decode_m_packet PARAMS ((char *from, CORE_ADDR *mem_addr_ptr, + unsigned int *len_ptr)); +void decode_M_packet PARAMS ((char *from, CORE_ADDR *mem_addr_ptr, + unsigned int *len_ptr, char *to)); + + +/* Functions from utils.c */ + +void perror_with_name PARAMS ((char *string)); diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c new file mode 100644 index 0000000..63d522c --- /dev/null +++ b/gdb/gdbserver/utils.c @@ -0,0 +1,114 @@ +/* General utility routines for the remote server for GDB. + Copyright (C) 1986, 1989, 1993 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. */ + +#include "server.h" +#include <stdio.h> +#include <string.h> + +/* Generally useful subroutines used throughout the program. */ + +/* Print the system error message for errno, and also mention STRING + as the file name for which the error was encountered. + Then return to command level. */ + +void +perror_with_name (string) + char *string; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + char *err; + char *combined; + + if (errno < sys_nerr) + err = sys_errlist[errno]; + else + err = "unknown error"; + + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + + error ("%s.", combined); +} + +/* Print an error message and return to command level. + STRING is the error message, used as a fprintf string, + and ARG is passed as an argument to it. */ + +#ifdef ANSI_PROTOTYPES +NORETURN void +error (const char *string, ...) +#else +void +error (va_alist) + va_dcl +#endif +{ + extern jmp_buf toplevel; + va_list args; +#ifdef ANSI_PROTOTYPES + va_start (args, string); +#else + va_start (args); +#endif + fflush (stdout); +#ifdef ANSI_PROTOTYPES + vfprintf (stderr, string, args); +#else + { + char *string1; + + string1 = va_arg (args, char *); + vfprintf (stderr, string1, args); + } +#endif + fprintf (stderr, "\n"); + longjmp(toplevel, 1); +} + +/* Print an error message and exit reporting failure. + This is for a error that we cannot continue from. + STRING and ARG are passed to fprintf. */ + +/* VARARGS */ +NORETURN void +#ifdef ANSI_PROTOTYPES +fatal (char *string, ...) +#else +fatal (va_alist) + va_dcl +#endif +{ + va_list args; +#ifdef ANSI_PROTOTYPES + va_start (args, string); +#else + char *string; + va_start (args); + string = va_arg (args, char *); +#endif + fprintf (stderr, "gdb: "); + vfprintf (stderr, string, args); + fprintf (stderr, "\n"); + va_end (args); + exit (1); +} |