mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-19 09:14:14 +08:00
Initial creation of sourceware repository
This commit is contained in:
241
gdb/gdbserver/Makefile.in
Normal file
241
gdb/gdbserver/Makefile.in
Normal file
@ -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".
|
127
gdb/gdbserver/README
Normal file
127
gdb/gdbserver/README
Normal file
@ -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.
|
100
gdb/gdbserver/configure.in
Normal file
100
gdb/gdbserver/configure.in
Normal file
@ -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
|
318
gdb/gdbserver/gdbreplay.c
Normal file
318
gdb/gdbserver/gdbreplay.c
Normal file
@ -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);
|
||||
}
|
||||
|
103
gdb/gdbserver/gdbserver.1
Normal file
103
gdb/gdbserver/gdbserver.1
Normal file
@ -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.
|
379
gdb/gdbserver/low-hppabsd.c
Normal file
379
gdb/gdbserver/low-hppabsd.c
Normal file
@ -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;
|
||||
}
|
451
gdb/gdbserver/low-linux.c
Normal file
451
gdb/gdbserver/low-linux.c
Normal file
@ -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;
|
||||
}
|
746
gdb/gdbserver/low-lynx.c
Normal file
746
gdb/gdbserver/low-lynx.c
Normal file
@ -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;
|
||||
}
|
289
gdb/gdbserver/low-sim.c
Normal file
289
gdb/gdbserver/low-sim.c
Normal file
@ -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
|
334
gdb/gdbserver/low-sparc.c
Normal file
334
gdb/gdbserver/low-sparc.c
Normal file
@ -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;
|
||||
}
|
313
gdb/gdbserver/low-sun3.c
Normal file
313
gdb/gdbserver/low-sun3.c
Normal file
@ -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;
|
||||
}
|
545
gdb/gdbserver/remote-utils.c
Normal file
545
gdb/gdbserver/remote-utils.c
Normal file
@ -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);
|
||||
}
|
258
gdb/gdbserver/server.c
Normal file
258
gdb/gdbserver/server.c
Normal file
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
72
gdb/gdbserver/server.h
Normal file
72
gdb/gdbserver/server.h
Normal file
@ -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));
|
114
gdb/gdbserver/utils.c
Normal file
114
gdb/gdbserver/utils.c
Normal file
@ -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);
|
||||
}
|
Reference in New Issue
Block a user