diff -purN gdb-5.2.1-orig/gdb/gdbserver/Makefile.in gdb-5.2.1/gdb/gdbserver/Makefile.in --- gdb-5.2.1-orig/gdb/gdbserver/Makefile.in Fri Mar 29 20:03:33 2002 +++ gdb-5.2.1/gdb/gdbserver/Makefile.in Tue Sep 2 14:04:40 2003 @@ -90,13 +90,15 @@ INCLUDE_CFLAGS = -I. -I${srcdir} -I$(src GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS} #PROFILE_CFLAGS = -pg +WARN_CFLAGS = -Wall + # CFLAGS is specifically reserved for setting from the command line # when running make. I.E. "make CFLAGS=-Wmissing-prototypes". CFLAGS = @CFLAGS@ # INTERNAL_CFLAGS is the aggregate of all other *CFLAGS macros. -INTERNAL_CFLAGS = ${CFLAGS} ${GLOBAL_CFLAGS} ${PROFILE_CFLAGS} \ - ${INCLUDE_CFLAGS} ${BFD_CFLAGS} +INTERNAL_CFLAGS = $(WARN_CFLAGS) ${CFLAGS} ${GLOBAL_CFLAGS} \ + ${PROFILE_CFLAGS} ${INCLUDE_CFLAGS} ${BFD_CFLAGS} # LDFLAGS is specifically reserved for setting from the command line # when running make. @@ -120,7 +122,11 @@ DEPFILES = @GDBSERVER_DEPFILES@ SOURCES = $(SFILES) TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} -OBS = utils.o $(DEPFILES) server.o remote-utils.o regcache.o signals.o +OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ + utils.o \ + mem-break.o \ + $(DEPFILES) +GDBSERVER_LIBS = @GDBSERVER_LIBS@ # 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. @@ -195,8 +201,12 @@ maintainer-clean realclean: clean STAGESTUFF=${OBS} ${TSOBS} ${NTSOBS} ${ADD_FILES} init.c init.o version.c gdb +config.h: stamp-h ; @true +stamp-h: config.in config.status + CONFIG_FILES="" $(SHELL) ./config.status + Makefile: Makefile.in config.status - $(SHELL) ./config.status + CONFIG_HEADERS="" $(SHELL) ./config.status config.status: configure configure.srv $(SHELL) ./config.status --recheck @@ -222,17 +232,24 @@ MAKEOVERRIDES= ## with no dependencies and no actions. unexport CHILLFLAGS CHILL_LIB CHILL_FOR_TARGET : -regdat_sh = $(srcdir)/../regformats/regdat.sh -regdef_h = $(srcdir)/../regformats/regdef.h +gdb_proc_service_h = $(srcdir)/../gdb_proc_service.h $(srcdir)/../gregset.h +regdat_sh = $(srcdir)/../regformats/regdat.sh +regdef_h = $(srcdir)/../regformats/regdef.h regcache_h = $(srcdir)/regcache.h -server_h = $(srcdir)/server.h $(regcache_h) config.h +server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \ + $(srcdir)/mem-break.h -server.o: server.c $(server_h) +inferiors.o: inferiors.c $(server_h) +mem-break.o: mem-break.c $(server_h) +proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h) +regcache.o: regcache.c $(server_h) $(regdef_h) remote-utils.o: remote-utils.c terminal.h $(server_h) +server.o: server.c $(server_h) +target.o: target.c $(server_h) +thread-db.o: thread-db.c $(server_h) $(gdb_proc_service_h) utils.o: utils.c $(server_h) -regcache.o: regcache.c $(server_h) $(regdef_h) -signals.o: ../signals/signals.c $(server_h) +signals.o: ../signals.c $(server_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER i387-fp.o: i387-fp.c $(server_h) @@ -240,6 +257,8 @@ i387-fp.o: i387-fp.c $(server_h) linux_low_h = $(srcdir)/linux-low.h linux-low.o: linux-low.c $(linux_low_h) $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< @USE_THREAD_DB@ + linux-arm-low.o: linux-arm-low.c $(linux_low_h) $(server_h) linux-i386-low.o: linux-i386-low.c $(linux_low_h) $(server_h) linux-ia64-low.o: linux-ia64-low.c $(linux_low_h) $(server_h) @@ -249,14 +268,6 @@ linux-s390-low.o: linux-s390-low.c $(lin linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h) linux-x86-64-low.o: linux-x86-64-low.c $(linux_low_h) $(server_h) -# OBSOLETE TARGETS -# OBSOLETE # low-lynx.o : ${srcdir}/low-lynx.c ${srcdir}/server.h -# OBSOLETE # low-nbsd.o : ${srcdir}/low-nbsd.c ${srcdir}/server.h -# OBSOLETE # low-sim.o : ${srcdir}/low-sim.c ${srcdir}/server.h -# OBSOLETE # low-sparc.o : $(srcdir)/low-sparc.c $(srcdir)/server.h -# OBSOLETE # low-sun3.o : $(srcdir)/low-sun3.c $(srcdir)/server.h -# OBSOLETE # low-hppabsd.o : $(srcdir)/low-hppabsd.c $(srcdir)/server.h - reg-arm.o : reg-arm.c $(regdef_h) reg-arm.c : $(srcdir)/../regformats/reg-arm.dat $(regdat_sh) sh $(regdat_sh) $(srcdir)/../regformats/reg-arm.dat reg-arm.c diff -purN gdb-5.2.1-orig/gdb/gdbserver/acconfig.h gdb-5.2.1/gdb/gdbserver/acconfig.h --- gdb-5.2.1-orig/gdb/gdbserver/acconfig.h Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/acconfig.h Tue Sep 2 14:04:40 2003 @@ -7,3 +7,18 @@ /* Define if the target supports PTRACE_GETFPXREGS for extended register access. */ #undef HAVE_PTRACE_GETFPXREGS + +/* Define if has prgregset_t. */ +#undef HAVE_PRGREGSET_T + +/* Define if has prfpregset_t. */ +#undef HAVE_PRFPREGSET_T + +/* Define if has lwpid_t. */ +#undef HAVE_LWPID_T + +/* Define if has psaddr_t. */ +#undef HAVE_PSADDR_T + +/* Define if the prfpregset_t type is broken. */ +#undef PRFPREGSET_T_BROKEN diff -purN gdb-5.2.1-orig/gdb/gdbserver/acinclude.m4 gdb-5.2.1/gdb/gdbserver/acinclude.m4 --- gdb-5.2.1-orig/gdb/gdbserver/acinclude.m4 Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/acinclude.m4 Tue Sep 2 14:04:40 2003 @@ -0,0 +1,41 @@ +dnl gdb/gdbserver/configure.in uses BFD_HAVE_SYS_PROCFS_TYPE. +sinclude(../../bfd/acinclude.m4) + +AC_DEFUN([SRV_CHECK_THREAD_DB], +[AC_CACHE_CHECK([for libthread_db],[srv_cv_thread_db], + [old_LIBS="$LIBS" + LIBS="$LIBS -lthread_db" + AC_TRY_LINK( + [void ps_pglobal_lookup() {} + void ps_pdread() {} + void ps_pdwrite() {} + void ps_lgetregs() {} + void ps_lsetregs() {} + void ps_lgetfpregs() {} + void ps_lsetfpregs() {} + void ps_getpid() {}], + [td_ta_new();], + [srv_cv_thread_db="-lthread_db"], + [srv_cv_thread_db=no + + if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then + thread_db="/lib/libthread_db.so.1" + else + thread_db='$prefix/lib/libthread_db.so.1' + fi + LIBS="$old_LIBS `eval echo "$thread_db"`" + AC_TRY_LINK( + [void ps_pglobal_lookup() {} + void ps_pdread() {} + void ps_pdwrite() {} + void ps_lgetregs() {} + void ps_lsetregs() {} + void ps_lgetfpregs() {} + void ps_lsetfpregs() {} + void ps_getpid() {}], + [td_ta_new();], + [srv_cv_thread_db="$thread_db"], + [srv_cv_thread_db=no]) + LIBS="$old_LIBS" + ]]) +)]) diff -purN gdb-5.2.1-orig/gdb/gdbserver/aclocal.m4 gdb-5.2.1/gdb/gdbserver/aclocal.m4 --- gdb-5.2.1-orig/gdb/gdbserver/aclocal.m4 Wed Feb 20 00:48:14 2002 +++ gdb-5.2.1/gdb/gdbserver/aclocal.m4 Tue Sep 2 14:04:40 2003 @@ -1,6 +1,6 @@ -dnl aclocal.m4 generated automatically by aclocal 1.4-p4 +dnl aclocal.m4 generated automatically by aclocal 1.4-p5 -dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -10,91 +10,45 @@ dnl but WITHOUT ANY WARRANTY, to the ext dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A dnl PARTICULAR PURPOSE. +dnl gdb/gdbserver/configure.in uses BFD_HAVE_SYS_PROCFS_TYPE. +sinclude(../../bfd/acinclude.m4) -# serial 1 - -# @defmac AC_PROG_CC_STDC -# @maindex PROG_CC_STDC -# @ovindex CC -# If the C compiler in not in ANSI C mode by default, try to add an option -# to output variable @code{CC} to make it so. This macro tries various -# options that select ANSI C on some system or another. It considers the -# compiler to be in ANSI C mode if it handles function prototypes correctly. -# -# If you use this macro, you should check after calling it whether the C -# compiler has been set to accept ANSI C; if not, the shell variable -# @code{am_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source -# code in ANSI C, you can make an un-ANSIfied copy of it by using the -# program @code{ansi2knr}, which comes with Ghostscript. -# @end defmac - -AC_DEFUN(AM_PROG_CC_STDC, -[AC_REQUIRE([AC_PROG_CC]) -AC_BEFORE([$0], [AC_C_INLINE]) -AC_BEFORE([$0], [AC_C_CONST]) -dnl Force this before AC_PROG_CPP. Some cpp's, eg on HPUX, require -dnl a magic option to avoid problems with ANSI preprocessor commands -dnl like #elif. -dnl FIXME: can't do this because then AC_AIX won't work due to a -dnl circular dependency. -dnl AC_BEFORE([$0], [AC_PROG_CPP]) -AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C) -AC_CACHE_VAL(am_cv_prog_cc_stdc, -[am_cv_prog_cc_stdc=no -ac_save_CC="$CC" -# Don't try gcc -ansi; that turns off useful extensions and -# breaks some systems' header files. -# AIX -qlanglvl=ansi -# Ultrix and OSF/1 -std1 -# HP-UX -Aa -D_HPUX_SOURCE -# SVR4 -Xc -D__EXTENSIONS__ -for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - AC_TRY_COMPILE( -[#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -], [ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; -], -[am_cv_prog_cc_stdc="$ac_arg"; break]) -done -CC="$ac_save_CC" -]) -if test -z "$am_cv_prog_cc_stdc"; then - AC_MSG_RESULT([none needed]) -else - AC_MSG_RESULT($am_cv_prog_cc_stdc) -fi -case "x$am_cv_prog_cc_stdc" in - x|xno) ;; - *) CC="$CC $am_cv_prog_cc_stdc" ;; -esac -]) +AC_DEFUN([SRV_CHECK_THREAD_DB], +[AC_CACHE_CHECK([for libthread_db],[srv_cv_thread_db], + [old_LIBS="$LIBS" + LIBS="$LIBS -lthread_db" + AC_TRY_LINK( + [void ps_pglobal_lookup() {} + void ps_pdread() {} + void ps_pdwrite() {} + void ps_lgetregs() {} + void ps_lsetregs() {} + void ps_lgetfpregs() {} + void ps_lsetfpregs() {} + void ps_getpid() {}], + [td_ta_new();], + [srv_cv_thread_db="-lthread_db"], + [srv_cv_thread_db=no + + if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then + thread_db="/lib/libthread_db.so.1" + else + thread_db='$prefix/lib/libthread_db.so.1' + fi + LIBS="$old_LIBS `eval echo "$thread_db"`" + AC_TRY_LINK( + [void ps_pglobal_lookup() {} + void ps_pdread() {} + void ps_pdwrite() {} + void ps_lgetregs() {} + void ps_lsetregs() {} + void ps_lgetfpregs() {} + void ps_lsetfpregs() {} + void ps_getpid() {}], + [td_ta_new();], + [srv_cv_thread_db="$thread_db"], + [srv_cv_thread_db=no]) + LIBS="$old_LIBS" + ]]) +)]) diff -purN gdb-5.2.1-orig/gdb/gdbserver/config.in gdb-5.2.1/gdb/gdbserver/config.in --- gdb-5.2.1-orig/gdb/gdbserver/config.in Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/config.in Tue Sep 2 14:04:40 2003 @@ -1,4 +1,4 @@ -/* config.in. Generated automatically from configure.in by autoheader 2.13. */ +/* config.in. Generated automatically from configure.in by autoheader. */ /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -13,9 +13,24 @@ register access. */ #undef HAVE_PTRACE_GETFPXREGS +/* Define if the prfpregset_t type is broken. */ +#undef PRFPREGSET_T_BROKEN + +/* Define if you have the header file. */ +#undef HAVE_LINUX_ELF_H + +/* Define if you have the header file. */ +#undef HAVE_PROC_SERVICE_H + /* Define if you have the header file. */ #undef HAVE_SGTTY_H +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_PROCFS_H + /* Define if you have the header file. */ #undef HAVE_SYS_REG_H @@ -24,3 +39,25 @@ /* Define if you have the header file. */ #undef HAVE_TERMIOS_H + +/* Define if you have the header file. */ +#undef HAVE_THREAD_DB_H + +/* Define if has lwpid_t. */ +#undef HAVE_LWPID_T + +/* Define if has psaddr_t. */ +#undef HAVE_PSADDR_T + +/* Define if has prgregset_t. */ +#undef HAVE_PRGREGSET_T + +/* Define if has prfpregset_t. */ +#undef HAVE_PRFPREGSET_T + +/* Define if has elf_fpregset_t. */ +#undef HAVE_ELF_FPREGSET_T + +#undef HAVE_TARGET_ATTACH + +#undef HAVE_TARGET_SHELL diff -purN gdb-5.2.1-orig/gdb/gdbserver/configure gdb-5.2.1/gdb/gdbserver/configure --- gdb-5.2.1-orig/gdb/gdbserver/configure Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/configure Tue Sep 2 14:04:41 2003 @@ -1105,7 +1105,7 @@ EOF fi -for ac_hdr in sgtty.h termio.h termios.h sys/reg.h +for ac_hdr in sgtty.h termio.h termios.h sys/reg.h string.h proc_service.h sys/procfs.h thread_db.h linux/elf.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 @@ -1223,7 +1223,333 @@ EOF fi fi -GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj" +if test "$ac_cv_header_sys_procfs_h" = yes; then + echo $ac_n "checking for lwpid_t in sys/procfs.h""... $ac_c" 1>&6 +echo "configure:1229: checking for lwpid_t in sys/procfs.h" >&5 + if eval "test \"`echo '$''{'bfd_cv_have_sys_procfs_type_lwpid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +lwpid_t avar +; return 0; } +EOF +if { (eval echo configure:1243: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + bfd_cv_have_sys_procfs_type_lwpid_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + bfd_cv_have_sys_procfs_type_lwpid_t=no + +fi +rm -f conftest* +fi + + if test $bfd_cv_have_sys_procfs_type_lwpid_t = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_LWPID_T 1 +EOF + + fi + echo "$ac_t""$bfd_cv_have_sys_procfs_type_lwpid_t" 1>&6 + + echo $ac_n "checking for psaddr_t in sys/procfs.h""... $ac_c" 1>&6 +echo "configure:1265: checking for psaddr_t in sys/procfs.h" >&5 + if eval "test \"`echo '$''{'bfd_cv_have_sys_procfs_type_psaddr_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +psaddr_t avar +; return 0; } +EOF +if { (eval echo configure:1279: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + bfd_cv_have_sys_procfs_type_psaddr_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + bfd_cv_have_sys_procfs_type_psaddr_t=no + +fi +rm -f conftest* +fi + + if test $bfd_cv_have_sys_procfs_type_psaddr_t = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_PSADDR_T 1 +EOF + + fi + echo "$ac_t""$bfd_cv_have_sys_procfs_type_psaddr_t" 1>&6 + + echo $ac_n "checking for prgregset_t in sys/procfs.h""... $ac_c" 1>&6 +echo "configure:1301: checking for prgregset_t in sys/procfs.h" >&5 + if eval "test \"`echo '$''{'bfd_cv_have_sys_procfs_type_prgregset_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +prgregset_t avar +; return 0; } +EOF +if { (eval echo configure:1315: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + bfd_cv_have_sys_procfs_type_prgregset_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + bfd_cv_have_sys_procfs_type_prgregset_t=no + +fi +rm -f conftest* +fi + + if test $bfd_cv_have_sys_procfs_type_prgregset_t = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_PRGREGSET_T 1 +EOF + + fi + echo "$ac_t""$bfd_cv_have_sys_procfs_type_prgregset_t" 1>&6 + + echo $ac_n "checking for prfpregset_t in sys/procfs.h""... $ac_c" 1>&6 +echo "configure:1337: checking for prfpregset_t in sys/procfs.h" >&5 + if eval "test \"`echo '$''{'bfd_cv_have_sys_procfs_type_prfpregset_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +prfpregset_t avar +; return 0; } +EOF +if { (eval echo configure:1351: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + bfd_cv_have_sys_procfs_type_prfpregset_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + bfd_cv_have_sys_procfs_type_prfpregset_t=no + +fi +rm -f conftest* +fi + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_PRFPREGSET_T 1 +EOF + + fi + echo "$ac_t""$bfd_cv_have_sys_procfs_type_prfpregset_t" 1>&6 + + + + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + echo $ac_n "checking whether prfpregset_t type is broken""... $ac_c" 1>&6 +echo "configure:1377: checking whether prfpregset_t type is broken" >&5 + if eval "test \"`echo '$''{'gdb_cv_prfpregset_t_broken'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + gdb_cv_prfpregset_t_broken=yes +else + cat > conftest.$ac_ext < + int main () + { + if (sizeof (prfpregset_t) == sizeof (void *)) + return 1; + return 0; + } +EOF +if { (eval echo configure:1395: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + gdb_cv_prfpregset_t_broken=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + gdb_cv_prfpregset_t_broken=yes +fi +rm -fr conftest* +fi + +fi + + echo "$ac_t""$gdb_cv_prfpregset_t_broken" 1>&6 + if test $gdb_cv_prfpregset_t_broken = yes; then + cat >> confdefs.h <<\EOF +#define PRFPREGSET_T_BROKEN 1 +EOF + + fi + fi + + echo $ac_n "checking for elf_fpregset_t in sys/procfs.h""... $ac_c" 1>&6 +echo "configure:1419: checking for elf_fpregset_t in sys/procfs.h" >&5 + if eval "test \"`echo '$''{'bfd_cv_have_sys_procfs_type_elf_fpregset_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +int main() { +elf_fpregset_t avar +; return 0; } +EOF +if { (eval echo configure:1433: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + bfd_cv_have_sys_procfs_type_elf_fpregset_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + bfd_cv_have_sys_procfs_type_elf_fpregset_t=no + +fi +rm -f conftest* +fi + + if test $bfd_cv_have_sys_procfs_type_elf_fpregset_t = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ELF_FPREGSET_T 1 +EOF + + fi + echo "$ac_t""$bfd_cv_have_sys_procfs_type_elf_fpregset_t" 1>&6 + +fi + +srv_thread_depfiles= +srv_libs= +USE_THREAD_DB= + +if test "$srv_linux_thread_db" = "yes"; then + echo $ac_n "checking for libthread_db""... $ac_c" 1>&6 +echo "configure:1462: checking for libthread_db" >&5 +if eval "test \"`echo '$''{'srv_cv_thread_db'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + old_LIBS="$LIBS" + LIBS="$LIBS -lthread_db" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + srv_cv_thread_db="-lthread_db" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + srv_cv_thread_db=no + + if test "$prefix" = "/usr" || test "$prefix" = "NONE"; then + thread_db="/lib/libthread_db.so.1" + else + thread_db='$prefix/lib/libthread_db.so.1' + fi + LIBS="$old_LIBS `eval echo "$thread_db"`" + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + srv_cv_thread_db="$thread_db" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + srv_cv_thread_db=no +fi +rm -f conftest* + LIBS="$old_LIBS" + +fi + +echo "$ac_t""$srv_cv_thread_db" 1>&6 + +fi +rm -f conftest* + if test "$srv_cv_thread_db" = no; then + echo "configure: warning: Could not find libthread_db." 1>&2 + echo "configure: warning: Disabling thread support in gdbserver." 1>&2 + srv_linux_thread_db=no + else + srv_libs="$srv_cv_thread_db" + fi +fi + +if test "$srv_linux_thread_db" = "yes"; then + srv_thread_depfiles="thread-db.o proc-service.o" + USE_THREAD_DB="-DUSE_THREAD_DB" +fi + +cat >> confdefs.h <<\EOF +#define HAVE_TARGET_ATTACH 1 +#define HAVE_TARGET_SHELL 1 +EOF + +GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles" +GDBSERVER_LIBS="$srv_libs" + + @@ -1381,6 +1707,8 @@ s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g s%@INSTALL_DATA@%$INSTALL_DATA%g s%@CPP@%$CPP%g s%@GDBSERVER_DEPFILES@%$GDBSERVER_DEPFILES%g +s%@GDBSERVER_LIBS@%$GDBSERVER_LIBS%g +s%@USE_THREAD_DB@%$USE_THREAD_DB%g CEOF EOF @@ -1593,6 +1921,10 @@ cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +case x$CONFIG_HEADERS in +xconfig.h:config.in) +echo > stamp-h ;; +esac exit 0 EOF diff -purN gdb-5.2.1-orig/gdb/gdbserver/configure.in gdb-5.2.1/gdb/gdbserver/configure.in --- gdb-5.2.1-orig/gdb/gdbserver/configure.in Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/configure.in Tue Sep 2 14:04:41 2003 @@ -30,7 +30,8 @@ AC_PROG_INSTALL AC_HEADER_STDC -AC_CHECK_HEADERS(sgtty.h termio.h termios.h sys/reg.h) +AC_CHECK_HEADERS(sgtty.h termio.h termios.h sys/reg.h string.h dnl + proc_service.h sys/procfs.h thread_db.h linux/elf.h) . ${srcdir}/configure.srv @@ -62,8 +63,71 @@ if test "${srv_linux_regsets}" = "yes"; fi fi -GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj" +if test "$ac_cv_header_sys_procfs_h" = yes; then + BFD_HAVE_SYS_PROCFS_TYPE(lwpid_t) + BFD_HAVE_SYS_PROCFS_TYPE(psaddr_t) + BFD_HAVE_SYS_PROCFS_TYPE(prgregset_t) + BFD_HAVE_SYS_PROCFS_TYPE(prfpregset_t) + + dnl Check for broken prfpregset_t type + + dnl For Linux/i386, glibc 2.1.3 was released with a bogus + dnl prfpregset_t type (it's a typedef for the pointer to a struct + dnl instead of the struct itself). We detect this here, and work + dnl around it in gdb_proc_service.h. + + if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then + AC_MSG_CHECKING(whether prfpregset_t type is broken) + AC_CACHE_VAL(gdb_cv_prfpregset_t_broken, + [AC_TRY_RUN([#include + int main () + { + if (sizeof (prfpregset_t) == sizeof (void *)) + return 1; + return 0; + }], + gdb_cv_prfpregset_t_broken=no, + gdb_cv_prfpregset_t_broken=yes, + gdb_cv_prfpregset_t_broken=yes)]) + AC_MSG_RESULT($gdb_cv_prfpregset_t_broken) + if test $gdb_cv_prfpregset_t_broken = yes; then + AC_DEFINE(PRFPREGSET_T_BROKEN) + fi + fi + + BFD_HAVE_SYS_PROCFS_TYPE(elf_fpregset_t) +fi + +srv_thread_depfiles= +srv_libs= +USE_THREAD_DB= + +if test "$srv_linux_thread_db" = "yes"; then + SRV_CHECK_THREAD_DB + if test "$srv_cv_thread_db" = no; then + AC_WARN([Could not find libthread_db.]) + AC_WARN([Disabling thread support in gdbserver.]) + srv_linux_thread_db=no + else + srv_libs="$srv_cv_thread_db" + fi +fi + +if test "$srv_linux_thread_db" = "yes"; then + srv_thread_depfiles="thread-db.o proc-service.o" + USE_THREAD_DB="-DUSE_THREAD_DB" +fi + +GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles" +GDBSERVER_LIBS="$srv_libs" AC_SUBST(GDBSERVER_DEPFILES) +AC_SUBST(GDBSERVER_LIBS) +AC_SUBST(USE_THREAD_DB) -AC_OUTPUT(Makefile) +AC_OUTPUT(Makefile, +[case x$CONFIG_HEADERS in +xconfig.h:config.in) +echo > stamp-h ;; +esac +]) diff -purN gdb-5.2.1-orig/gdb/gdbserver/configure.srv gdb-5.2.1/gdb/gdbserver/configure.srv --- gdb-5.2.1-orig/gdb/gdbserver/configure.srv Thu Feb 28 17:58:14 2002 +++ gdb-5.2.1/gdb/gdbserver/configure.srv Tue Sep 2 14:04:41 2003 @@ -21,11 +21,13 @@ case "${target}" in arm*-*-linux*) srv_regobj=reg-arm.o srv_tgtobj="linux-low.o linux-arm-low.o" srv_linux_usrregs=yes + srv_linux_thread_db=yes ;; i[3456]86-*-linux*) srv_regobj=reg-i386-linux.o srv_tgtobj="linux-low.o linux-i386-low.o i387-fp.o" srv_linux_usrregs=yes srv_linux_regsets=yes + srv_linux_thread_db=yes ;; ia64-*-linux*) srv_regobj=reg-ia64.o srv_tgtobj="linux-low.o linux-ia64-low.o" @@ -38,10 +40,12 @@ case "${target}" in mips*-*-linux*) srv_regobj=reg-mips.o srv_tgtobj="linux-low.o linux-mips-low.o" srv_linux_usrregs=yes + srv_linux_thread_db=yes ;; powerpc*-*-linux*) srv_regobj=reg-ppc.o srv_tgtobj="linux-low.o linux-ppc-low.o" srv_linux_usrregs=yes + srv_linux_thread_db=yes ;; s390-*-linux*) srv_regobj=reg-s390.o srv_tgtobj="linux-low.o linux-s390-low.o" @@ -54,6 +58,7 @@ case "${target}" in sh*-*-linux*) srv_regobj=reg-sh.o srv_tgtobj="linux-low.o linux-sh-low.o" srv_linux_usrregs=yes + srv_linux_thread_db=yes ;; x86_64-*-linux*) srv_regobj=reg-x86-64.o srv_tgtobj="linux-low.o linux-x86-64-low.o i387-fp.o" @@ -63,3 +68,4 @@ case "${target}" in exit 1 ;; esac + diff -purN gdb-5.2.1-orig/gdb/gdbserver/i387-fp.c gdb-5.2.1/gdb/gdbserver/i387-fp.c --- gdb-5.2.1-orig/gdb/gdbserver/i387-fp.c Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/i387-fp.c Tue Sep 2 14:04:41 2003 @@ -20,6 +20,7 @@ Boston, MA 02111-1307, USA. */ #include "server.h" +#include "i387-fp.h" int num_xmm_registers = 8; @@ -108,7 +109,7 @@ i387_cache_to_fsave (void *buf) } void -i387_fsave_to_cache (void *buf) +i387_fsave_to_cache (const void *buf) { struct i387_fsave *fp = (struct i387_fsave *) buf; int i; @@ -240,7 +241,7 @@ i387_ftag (struct i387_fxsave *fp, int r } void -i387_fxsave_to_cache (void *buf) +i387_fxsave_to_cache (const void *buf) { struct i387_fxsave *fp = (struct i387_fxsave *) buf; int i, top; @@ -287,4 +288,3 @@ i387_fxsave_to_cache (void *buf) val = (fp->fop) & 0x7FF; supply_register_by_name ("fop", &val); } - diff -purN gdb-5.2.1-orig/gdb/gdbserver/i387-fp.h gdb-5.2.1/gdb/gdbserver/i387-fp.h --- gdb-5.2.1-orig/gdb/gdbserver/i387-fp.h Wed Feb 27 08:07:38 2002 +++ gdb-5.2.1/gdb/gdbserver/i387-fp.h Tue Sep 2 14:04:41 2003 @@ -23,10 +23,10 @@ #define I387_FP_H void i387_cache_to_fsave (void *buf); -void i387_fsave_to_cache (void *buf); +void i387_fsave_to_cache (const void *buf); void i387_cache_to_fxsave (void *buf); -void i387_fxsave_to_cache (void *buf); +void i387_fxsave_to_cache (const void *buf); extern int num_xmm_registers; diff -purN gdb-5.2.1-orig/gdb/gdbserver/inferiors.c gdb-5.2.1/gdb/gdbserver/inferiors.c --- gdb-5.2.1-orig/gdb/gdbserver/inferiors.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/inferiors.c Tue Sep 2 14:04:41 2003 @@ -0,0 +1,214 @@ +/* Inferior process information for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 + +#include "server.h" + +struct thread_info +{ + struct inferior_list_entry entry; + void *target_data; + void *regcache_data; +}; + +struct inferior_list all_threads; + +struct thread_info *current_inferior; + +#define get_thread(inf) ((struct thread_info *)(inf)) +#define get_process(inf) ((struct process_info *)(inf)) + +void +add_inferior_to_list (struct inferior_list *list, + struct inferior_list_entry *new_inferior) +{ + new_inferior->next = NULL; + if (list->tail != NULL) + list->tail->next = new_inferior; + else + list->head = new_inferior; + list->tail = new_inferior; +} + +void +for_each_inferior (struct inferior_list *list, + void (*action) (struct inferior_list_entry *)) +{ + struct inferior_list_entry *cur = list->head, *next; + + while (cur != NULL) + { + next = cur->next; + (*action) (cur); + cur = next; + } +} + +void +change_inferior_id (struct inferior_list *list, + int new_id) +{ + if (list->head != list->tail) + error ("tried to change thread ID after multiple threads are created"); + + list->head->id = new_id; +} + + +void +remove_inferior (struct inferior_list *list, + struct inferior_list_entry *entry) +{ + struct inferior_list_entry **cur; + if (list->head == entry) + { + list->head = entry->next; + if (list->tail == entry) + list->tail = list->head; + return; + } + + cur = &list->head; + while (*cur && (*cur)->next != entry) + cur = &(*cur)->next; + + if (*cur == NULL) + return; + + (*cur)->next = entry->next; + + if (list->tail == entry) + list->tail = *cur; +} + +void +add_thread (int thread_id, void *target_data) +{ + struct thread_info *new_thread + = (struct thread_info *) malloc (sizeof (*new_thread)); + + memset (new_thread, 0, sizeof (*new_thread)); + + new_thread->entry.id = thread_id; + + add_inferior_to_list (&all_threads, & new_thread->entry); + + if (current_inferior == NULL) + current_inferior = new_thread; + + new_thread->target_data = target_data; + set_inferior_regcache_data (new_thread, new_register_cache ()); +} + +static void +free_one_thread (struct inferior_list_entry *inf) +{ + struct thread_info *thread = get_thread (inf); + free_register_cache (inferior_regcache_data (thread)); + free (thread); +} + + +static void +free_one_process (struct inferior_list_entry *inf) +{ + struct process_info *proc = get_process (inf); + free (proc); +} + + +void +remove_thread (struct thread_info *thread) +{ + remove_inferior (&all_threads, (struct inferior_list_entry *) thread); + free_one_thread (&thread->entry); +} + +void +clear_inferiors (void) +{ + extern struct inferior_list all_processes; + + for_each_inferior (&all_threads, free_one_thread); + + all_threads.head = all_threads.tail = NULL; +#ifdef HAVE_TARGET_ATTACH + for_each_inferior (&all_processes, free_one_process); + all_processes.head = all_processes.tail = NULL; +#endif +} + +struct inferior_list_entry * +find_inferior (struct inferior_list *list, + int (*func) (struct inferior_list_entry *, void *), void *arg) +{ + struct inferior_list_entry *inf = list->head; + + while (inf != NULL) + { + if ((*func) (inf, arg)) + return inf; + inf = inf->next; + } + + return NULL; +} + +struct inferior_list_entry * +find_inferior_id (struct inferior_list *list, int id) +{ + struct inferior_list_entry *inf = list->head; + + while (inf != NULL) + { + if (inf->id == id) + return inf; + inf = inf->next; + } + return NULL; +} + +void * +inferior_target_data (struct thread_info *inferior) +{ + return inferior->target_data; +} + +void +set_inferior_target_data (struct thread_info *inferior, void *data) +{ + inferior->target_data = data; +} + +void * +inferior_regcache_data (struct thread_info *inferior) +{ + return inferior->regcache_data; +} + +void +set_inferior_regcache_data (struct thread_info *inferior, void *data) +{ + inferior->regcache_data = data; +} diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-arm-low.c gdb-5.2.1/gdb/gdbserver/linux-arm-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-arm-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-arm-low.c Tue Sep 2 14:04:41 2003 @@ -26,22 +26,68 @@ #include #endif -int num_regs = 16; +#define arm_num_regs 16 -int regmap[] = { +static int arm_regmap[] = { 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, }; -int -cannot_store_register (int regno) +static int +arm_cannot_store_register (int regno) { - return (regno >= num_regs); + return (regno >= arm_num_regs); } -int -cannot_fetch_register (int regno) +static int +arm_cannot_fetch_register (int regno) { - return (regno >= num_regs); + return (regno >= arm_num_regs); } +static CORE_ADDR +arm_get_pc () +{ + unsigned long pc; + collect_register_by_name ("pc", &pc); + return pc; +} + +static void +arm_set_pc (CORE_ADDR pc) +{ + unsigned long newpc = pc; + supply_register_by_name ("pc", &newpc); +} + +/* Correct in either endianness. We do not support Thumb yet. */ +static const unsigned long arm_breakpoint = 0xef9f0001; +#define arm_breakpoint_len 4 + +static int +arm_breakpoint_at (CORE_ADDR where) +{ + unsigned long insn; + + (*the_target->read_memory) (where, (char *) &insn, 4); + if (insn == arm_breakpoint) + return 1; + + /* If necessary, recognize more trap instructions here. GDB only uses the + one. */ + return 0; +} + +struct linux_target_ops the_low_target = { + arm_num_regs, + arm_regmap, + arm_cannot_fetch_register, + arm_cannot_store_register, + arm_get_pc, + arm_set_pc, + (const char *) &arm_breakpoint, + arm_breakpoint_len, + NULL, + 0, + arm_breakpoint_at, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-i386-low.c gdb-5.2.1/gdb/gdbserver/linux-i386-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-i386-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-i386-low.c Tue Sep 2 14:04:41 2003 @@ -29,13 +29,13 @@ /* This module only supports access to the general purpose registers. */ -int num_regs = 16; +#define i386_num_regs 16 /* This stuff comes from i386-linux-nat.c. */ /* Mapping between the general-purpose registers in `struct user' format and GDB's register array layout. */ -int regmap[] = +static int i386_regmap[] = { EAX * 4, ECX * 4, EDX * 4, EBX * 4, UESP * 4, EBP * 4, ESI * 4, EDI * 4, @@ -43,16 +43,16 @@ int regmap[] = DS * 4, ES * 4, FS * 4, GS * 4 }; -int -cannot_store_register (int regno) +static int +i386_cannot_store_register (int regno) { - return (regno >= num_regs); + return (regno >= i386_num_regs); } -int -cannot_fetch_register (int regno) +static int +i386_cannot_fetch_register (int regno) { - return (regno >= num_regs); + return (regno >= i386_num_regs); } @@ -65,19 +65,19 @@ i386_fill_gregset (void *buf) { int i; - for (i = 0; i < num_regs; i++) - collect_register (i, ((char *) buf) + regmap[i]); + for (i = 0; i < i386_num_regs; i++) + collect_register (i, ((char *) buf) + i386_regmap[i]); collect_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4); } static void -i386_store_gregset (void *buf) +i386_store_gregset (const void *buf) { int i; - for (i = 0; i < num_regs; i++) - supply_register (i, ((char *) buf) + regmap[i]); + for (i = 0; i < i386_num_regs; i++) + supply_register (i, ((char *) buf) + i386_regmap[i]); supply_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4); } @@ -89,7 +89,7 @@ i386_fill_fpregset (void *buf) } static void -i386_store_fpregset (void *buf) +i386_store_fpregset (const void *buf) { i387_fsave_to_cache (buf); } @@ -101,7 +101,7 @@ i386_fill_fpxregset (void *buf) } static void -i386_store_fpxregset (void *buf) +i386_store_fpxregset (const void *buf) { i387_fxsave_to_cache (buf); } @@ -109,15 +109,68 @@ i386_store_fpxregset (void *buf) struct regset_info target_regsets[] = { { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t), + GENERAL_REGS, i386_fill_gregset, i386_store_gregset }, #ifdef HAVE_PTRACE_GETFPXREGS { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, sizeof (elf_fpxregset_t), + EXTENDED_REGS, i386_fill_fpxregset, i386_store_fpxregset }, #endif { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t), + FP_REGS, i386_fill_fpregset, i386_store_fpregset }, - { 0, 0, -1, NULL, NULL } + { 0, 0, -1, -1, NULL, NULL } }; #endif /* HAVE_LINUX_REGSETS */ +static const char i386_breakpoint[] = { 0xCC }; +#define i386_breakpoint_len 1 + +extern int debug_threads; + +static CORE_ADDR +i386_get_pc () +{ + unsigned long pc; + + collect_register_by_name ("eip", &pc); + + if (debug_threads) + fprintf (stderr, "stop pc (before any decrement) is %08lx\n", pc); + return pc; +} + +static void +i386_set_pc (CORE_ADDR newpc) +{ + if (debug_threads) + fprintf (stderr, "set pc to %08lx\n", (long) newpc); + supply_register_by_name ("eip", &newpc); +} + +static int +i386_breakpoint_at (CORE_ADDR pc) +{ + unsigned char c; + + read_inferior_memory (pc, &c, 1); + if (c == 0xCC) + return 1; + + return 0; +} + +struct linux_target_ops the_low_target = { + i386_num_regs, + i386_regmap, + i386_cannot_fetch_register, + i386_cannot_store_register, + i386_get_pc, + i386_set_pc, + i386_breakpoint, + i386_breakpoint_len, + NULL, + 1, + i386_breakpoint_at, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-ia64-low.c gdb-5.2.1/gdb/gdbserver/linux-ia64-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-ia64-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-ia64-low.c Tue Sep 2 14:04:41 2003 @@ -26,11 +26,11 @@ #include #endif -int num_regs = 590; +#define ia64_num_regs 590 #include -int regmap[] = +static int ia64_regmap[] = { /* general registers */ -1, /* gr0 not available; i.e, it's always zero */ @@ -283,15 +283,21 @@ int regmap[] = -1, -1, -1, -1, -1, -1, -1, -1, }; -int -cannot_store_register (int regno) +static int +ia64_cannot_store_register (int regno) { return 0; } -int -cannot_fetch_register (int regno) +static int +ia64_cannot_fetch_register (int regno) { return 0; } +struct linux_target_ops the_low_target = { + ia64_num_regs, + ia64_regmap, + ia64_cannot_fetch_register, + ia64_cannot_store_register, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-low.c gdb-5.2.1/gdb/gdbserver/linux-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-low.c Thu Mar 7 21:56:18 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-low.c Tue Sep 2 14:04:41 2003 @@ -35,6 +35,37 @@ #include #include +/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead, + however. This requires changing the ID in place when we go from !using_threads + to using_threads, immediately. + + ``all_processes'' is keyed by the process ID - which on Linux is (presently) + the same as the LWP ID. */ + +struct inferior_list all_processes; + +/* FIXME this is a bit of a hack, and could be removed. */ +int stopping_threads; + +/* FIXME make into a target method? */ +int using_threads; + +static void linux_resume_one_process (struct inferior_list_entry *entry, + int step, int signal); +static void linux_resume (int step, int signal); +static void stop_all_processes (void); +static int linux_wait_for_event (struct thread_info *child); +#ifdef HAVE_TARGET_ATTACH +static void +linux_detach_one_process (struct inferior_list_entry *entry); +#endif + +struct pending_signals +{ + int signal; + struct pending_signals *prev; +}; + #define PTRACE_ARG3_TYPE long #define PTRACE_XFER_TYPE long @@ -44,17 +75,72 @@ static int use_regsets_p = 1; extern int errno; -#ifdef HAVE_LINUX_USRREGS -extern int num_regs; -extern int regmap[]; -#endif +int debug_threads = 0; + +#define pid_of(proc) ((proc)->head.id) + +/* FIXME: Delete eventually. */ +#define inferior_pid (pid_of (get_thread_process (current_inferior))) + +/* This function should only be called if the process got a SIGTRAP. + The SIGTRAP could mean several things. + + On i386, where decr_pc_after_break is non-zero: + If we were single-stepping this process using PTRACE_SINGLESTEP, + we will get only the one SIGTRAP (even if the instruction we + stepped over was a breakpoint). The value of $eip will be the + next instruction. + If we continue the process using PTRACE_CONT, we will get a + SIGTRAP when we hit a breakpoint. The value of $eip will be + the instruction after the breakpoint (i.e. needs to be + decremented). If we report the SIGTRAP to GDB, we must also + report the undecremented PC. If we cancel the SIGTRAP, we + must resume at the decremented PC. + + (Presumably, not yet tested) On a non-decr_pc_after_break machine + with hardware or kernel single-step: + If we single-step over a breakpoint instruction, our PC will + point at the following instruction. If we continue and hit a + breakpoint instruction, our PC will point at the breakpoint + instruction. */ + +static CORE_ADDR +get_stop_pc (void) +{ + CORE_ADDR stop_pc = (*the_low_target.get_pc) (); + + if (get_thread_process (current_inferior)->stepping) + return stop_pc; + else + return stop_pc - the_low_target.decr_pc_after_break; +} + +static void * +add_process (int pid) +{ + struct process_info *process; + + process = (struct process_info *) malloc (sizeof (*process)); + memset (process, 0, sizeof (*process)); + + process->head.id = pid; + + /* Default to tid == lwpid == pid. */ + process->tid = pid; + process->lwpid = pid; + + add_inferior_to_list (&all_processes, &process->head); + + return process; +} /* Start an inferior process and returns its pid. ALLARGS is a vector of program-name and args. */ -int -create_inferior (char *program, char **allargs) +static int +linux_create_inferior (char *program, char **allargs) { + void *new_process; int pid; pid = fork (); @@ -65,6 +151,8 @@ create_inferior (char *program, char **a { ptrace (PTRACE_TRACEME, 0, 0, 0); + signal (SIGRTMIN + 1, SIG_DFL); + execv (program, allargs); fprintf (stderr, "Cannot exec %s: %s.\n", program, @@ -73,104 +161,767 @@ create_inferior (char *program, char **a _exit (0177); } - return pid; + new_process = add_process (pid); + add_thread (pid, new_process); + + return 0; } /* Attach to an inferior process. */ -int -myattach (int pid) +void +linux_attach_lwp (int pid, int tid) { + struct process_info *new_process; + if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) { fprintf (stderr, "Cannot attach to process %d: %s (%d)\n", pid, errno < sys_nerr ? sys_errlist[errno] : "unknown error", errno); fflush (stderr); - _exit (0177); + /* If we fail to attach to an LWP, just return. */ + if (!using_threads) + _exit (0177); + return; } + new_process = (struct process_info *) add_process (pid); + add_thread (tid, new_process); + + /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH + brings it to a halt. We should ignore that SIGSTOP and resume the process + (unless this is the first process, in which case the flag will be cleared + in linux_attach). + + On the other hand, if we are currently trying to stop all threads, we + should treat the new thread as if we had sent it a SIGSTOP. This works + because we are guaranteed that add_process added us to the end of the + list, and so the new thread has not yet reached wait_for_sigstop (but + will). */ + if (! stopping_threads) + new_process->stop_expected = 1; +} +int +linux_attach (int pid) +{ + struct process_info *process; + + linux_attach_lwp (pid, pid); + + /* Don't ignore the initial SIGSTOP if we just attached to this process. */ + process = (struct process_info *) find_inferior_id (&all_processes, pid); + process->stop_expected = 0; + + return 0; +} + +#ifdef HAVE_TARGET_ATTACH +int +linux_detach (int sig) +{ + if (using_threads) { + thread_db_deinit(); + using_threads = 0; + } + for_each_inferior (&all_threads, linux_detach_one_process); + clear_inferiors (); + current_inferior = NULL; return 0; } +static void +linux_detach_one_process (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct process_info *process = get_thread_process (thread); + + if (debug_threads) + fprintf(stderr, "detaching from pid %d...\n", + pid_of (process)); + errno = 0; + ptrace (PTRACE_DETACH, pid_of (process),0, 0); + if (errno) { + char *err = strerror(errno); + fprintf(stderr,"Error detaching %d: %s\n", pid_of (process), err); + } +} + +#endif + /* Kill the inferior process. Make us have no inferior. */ -void -kill_inferior (void) +static void +linux_kill_one_process (struct inferior_list_entry *entry) { - if (inferior_pid == 0) - return; - ptrace (PTRACE_KILL, inferior_pid, 0, 0); - wait (0); + struct thread_info *thread = (struct thread_info *) entry; + struct process_info *process = get_thread_process (thread); + int wstat; + + do + { + ptrace (PTRACE_KILL, pid_of (process), 0, 0); + + /* Make sure it died. The loop is most likely unnecessary. */ + wstat = linux_wait_for_event (thread); + } while (WIFSTOPPED (wstat)); } /* Return nonzero if the given thread is still alive. */ -int -mythread_alive (int pid) +static void +linux_kill (void) +{ + for_each_inferior (&all_threads, linux_kill_one_process); +} + +static int +linux_thread_alive (int tid) +{ + if (find_inferior_id (&all_threads, tid) != NULL) + return 1; + else + return 0; +} + +/* Return nonzero if this process stopped at a breakpoint which + no longer appears to be inserted. Also adjust the PC + appropriately to resume where the breakpoint used to be. */ +static int +check_removed_breakpoint (struct process_info *event_child) { + CORE_ADDR stop_pc; + struct thread_info *saved_inferior; + + if (event_child->pending_is_breakpoint == 0) + return 0; + + if (debug_threads) + fprintf (stderr, "Checking for breakpoint.\n"); + + saved_inferior = current_inferior; + current_inferior = get_process_thread (event_child); + + stop_pc = get_stop_pc (); + + /* If the PC has changed since we stopped, then we shouldn't do + anything. This happens if, for instance, GDB handled the + decr_pc_after_break subtraction itself. */ + if (stop_pc != event_child->pending_stop_pc) + { + if (debug_threads) + fprintf (stderr, "Ignoring, PC was changed.\n"); + + event_child->pending_is_breakpoint = 0; + current_inferior = saved_inferior; + return 0; + } + + /* If the breakpoint is still there, we will report hitting it. */ + if ((*the_low_target.breakpoint_at) (stop_pc)) + { + if (debug_threads) + fprintf (stderr, "Ignoring, breakpoint is still present.\n"); + current_inferior = saved_inferior; + return 0; + } + + if (debug_threads) + fprintf (stderr, "Removed breakpoint.\n"); + + /* For decr_pc_after_break targets, here is where we perform the + decrement. We go immediately from this function to resuming, + and can not safely call get_stop_pc () again. */ + if (the_low_target.set_pc != NULL) + (*the_low_target.set_pc) (stop_pc); + + /* We consumed the pending SIGTRAP. */ + event_child->status_pending_p = 0; + event_child->status_pending = 0; + + current_inferior = saved_inferior; return 1; } -/* Wait for process, returns status */ +/* Return 1 if this process has an interesting status pending. This function + may silently resume an inferior process. */ +static int +status_pending_p (struct inferior_list_entry *entry, void *dummy) +{ + struct process_info *process = (struct process_info *) entry; -unsigned char -mywait (char *status) + if (process->status_pending_p) + if (check_removed_breakpoint (process)) + { + /* This thread was stopped at a breakpoint, and the breakpoint + is now gone. We were told to continue (or step...) all threads, + so GDB isn't trying to single-step past this breakpoint. + So instead of reporting the old SIGTRAP, pretend we got to + the breakpoint just after it was removed instead of just + before; resume the process. */ + linux_resume_one_process (&process->head, 0, 0); + return 0; + } + + return process->status_pending_p; +} + +static void +linux_wait_for_process (struct process_info **childp, int *wstatp) +{ + int ret; + int to_wait_for = -1; + + if (*childp != NULL) + to_wait_for = (*childp)->lwpid; + + while (1) + { + ret = waitpid (to_wait_for, wstatp, WNOHANG); + + if (ret == -1) + { + if (errno != ECHILD) + perror_with_name ("waitpid"); + } + else if (ret > 0) + break; + + ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE); + if (ret == -1) + { + if (errno != ECHILD) + perror_with_name ("waitpid (WCLONE)"); + } + else if (ret > 0) + break; + + usleep (1000); + } + + if (debug_threads + && (!WIFSTOPPED (*wstatp) + || (WSTOPSIG (*wstatp) != 32 + && WSTOPSIG (*wstatp) != 33))) + fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp); + + if (to_wait_for == -1) + *childp = (struct process_info *) find_inferior_id (&all_processes, ret); + + (*childp)->stopped = 1; + (*childp)->pending_is_breakpoint = 0; + + if (debug_threads + && WIFSTOPPED (*wstatp)) + { + current_inferior = (struct thread_info *) + find_inferior_id (&all_threads, (*childp)->tid); + /* For testing only; i386_stop_pc prints out a diagnostic. */ + if (the_low_target.get_pc != NULL) + get_stop_pc (); + } +} +static int +linux_wait_for_event (struct thread_info *child) +{ + CORE_ADDR stop_pc; + struct process_info *event_child; + int wstat; + + /* Check for a process with a pending status. */ + /* It is possible that the user changed the pending task's registers since + it stopped. We correctly handle the change of PC if we hit a breakpoint + (in check_removed_breakpoints); signals should be reported anyway. */ + if (child == NULL) + { + event_child = (struct process_info *) + find_inferior (&all_processes, status_pending_p, NULL); + if (debug_threads && event_child) + fprintf (stderr, "Got a pending child %d\n", event_child->lwpid); + } + else + { + event_child = get_thread_process (child); + if (event_child->status_pending_p + && check_removed_breakpoint (event_child)) + event_child = NULL; + } + + if (event_child != NULL) + { + if (event_child->status_pending_p) + { + if (debug_threads) + fprintf (stderr, "Got an event from pending child %d (%04x)\n", + event_child->lwpid, event_child->status_pending); + wstat = event_child->status_pending; + event_child->status_pending_p = 0; + event_child->status_pending = 0; + current_inferior = get_process_thread (event_child); + return wstat; + } + } + + /* We only enter this loop if no process has a pending wait status. Thus + any action taken in response to a wait status inside this loop is + responding as soon as we detect the status, not after any pending + events. */ + while (1) + { + if (child == NULL) + event_child = NULL; + else + event_child = get_thread_process (child); + + linux_wait_for_process (&event_child, &wstat); + + if (event_child == NULL) + error ("event from unknown child"); + + current_inferior = (struct thread_info *) + find_inferior_id (&all_threads, event_child->tid); + + if (using_threads) + { + /* Check for thread exit. */ + if (! WIFSTOPPED (wstat)) + { + if (debug_threads) + fprintf (stderr, "Thread %d (LWP %d) exiting\n", + event_child->tid, event_child->head.id); + + /* If the last thread is exiting, just return. */ + if (all_threads.head == all_threads.tail) + return wstat; + + dead_thread_notify (event_child->tid); + + remove_inferior (&all_processes, &event_child->head); + free (event_child); + remove_thread (current_inferior); + current_inferior = (struct thread_info *) all_threads.head; + + /* If we were waiting for this particular child to do something... + well, it did something. */ + if (child != NULL) + return wstat; + + /* Wait for a more interesting event. */ + continue; + } + + if (WIFSTOPPED (wstat) + && WSTOPSIG (wstat) == SIGSTOP + && event_child->stop_expected) + { + if (debug_threads) + fprintf (stderr, "Expected stop.\n"); + event_child->stop_expected = 0; + linux_resume_one_process (&event_child->head, + event_child->stepping, 0); + continue; + } + + /* FIXME drow/2002-06-09: Get signal numbers from the inferior's + thread library? */ + if (WIFSTOPPED (wstat) + && (WSTOPSIG (wstat) == SIGRTMIN + || WSTOPSIG (wstat) == SIGRTMIN + 1)) + { + if (debug_threads) + fprintf (stderr, "Ignored signal %d for %d (LWP %d).\n", + WSTOPSIG (wstat), event_child->tid, + event_child->head.id); + linux_resume_one_process (&event_child->head, + event_child->stepping, + WSTOPSIG (wstat)); + continue; + } + } + + /* If this event was not handled above, and is not a SIGTRAP, report + it. */ + if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP) + return wstat; + + /* If this target does not support breakpoints, we simply report the + SIGTRAP; it's of no concern to us. */ + if (the_low_target.get_pc == NULL) + return wstat; + + stop_pc = get_stop_pc (); + + /* bp_reinsert will only be set if we were single-stepping. + Notice that we will resume the process after hitting + a gdbserver breakpoint; single-stepping to/over one + is not supported (yet). */ + if (event_child->bp_reinsert != 0) + { + if (debug_threads) + fprintf (stderr, "Reinserted breakpoint.\n"); + reinsert_breakpoint (event_child->bp_reinsert); + event_child->bp_reinsert = 0; + /* Clear the single-stepping flag and SIGTRAP as we resume. */ + linux_resume_one_process (&event_child->head, 0, 0); + continue; + } + + if (debug_threads) + fprintf (stderr, "Hit a (non-reinsert) breakpoint.\n"); + + if (check_breakpoints (stop_pc) != 0) + { + /* We hit one of our own breakpoints. We mark it as a pending + breakpoint, so that check_removed_breakpoints () will do the PC + adjustment for us at the appropriate time. */ + event_child->pending_is_breakpoint = 1; + event_child->pending_stop_pc = stop_pc; + + /* Now we need to put the breakpoint back. We continue in the event + loop instead of simply replacing the breakpoint right away, + in order to not lose signals sent to the thread that hit the + breakpoint. Unfortunately this increases the window where another + thread could sneak past the removed breakpoint. For the current + use of server-side breakpoints (thread creation) this is + acceptable; but it needs to be considered before this breakpoint + mechanism can be used in more general ways. For some breakpoints + it may be necessary to stop all other threads, but that should + be avoided where possible. + + If breakpoint_reinsert_addr is NULL, that means that we can + use PTRACE_SINGLESTEP on this platform. Uninsert the breakpoint, + mark it for reinsertion, and single-step. + + Otherwise, call the target function to figure out where we need + our temporary breakpoint, create it, and continue executing this + process. */ + if (the_low_target.breakpoint_reinsert_addr == NULL) + { + event_child->bp_reinsert = stop_pc; + uninsert_breakpoint (stop_pc); + linux_resume_one_process (&event_child->head, 1, 0); + } + else + { + reinsert_breakpoint_by_bp + (stop_pc, (*the_low_target.breakpoint_reinsert_addr) ()); + linux_resume_one_process (&event_child->head, 0, 0); + } + + continue; + } + + /* If we were single-stepping, we definitely want to report the + SIGTRAP. The single-step operation has completed, so also + clear the stepping flag; in general this does not matter, + because the SIGTRAP will be reported to the client, which + will give us a new action for this thread, but clear it for + consistency anyway. It's safe to clear the stepping flag + because the only consumer of get_stop_pc () after this point + is check_removed_breakpoints, and pending_is_breakpoint is not + set. It might be wiser to use a step_completed flag instead. */ + if (event_child->stepping) + { + event_child->stepping = 0; + return wstat; + } + + /* A SIGTRAP that we can't explain. It may have been a breakpoint. + Check if it is a breakpoint, and if so mark the process information + accordingly. This will handle both the necessary fiddling with the + PC on decr_pc_after_break targets and suppressing extra threads + hitting a breakpoint if two hit it at once and then GDB removes it + after the first is reported. Arguably it would be better to report + multiple threads hitting breakpoints simultaneously, but the current + remote protocol does not allow this. */ + if ((*the_low_target.breakpoint_at) (stop_pc)) + { + event_child->pending_is_breakpoint = 1; + event_child->pending_stop_pc = stop_pc; + } + + return wstat; + } + + /* NOTREACHED */ + return 0; +} + +/* Wait for process, returns status. */ + +static unsigned char +linux_wait (char *status) { - int pid; int w; + struct thread_info *child = NULL; + +retry: + /* If we were only supposed to resume one thread, only wait for + that thread - if it's still alive. If it died, however - which + can happen if we're coming from the thread death case below - + then we need to make sure we restart the other threads. We could + pick a thread at random or restart all; restarting all is less + arbitrary. */ + if (cont_thread > 0) + { + child = (struct thread_info *) find_inferior_id (&all_threads, + cont_thread); + + /* No stepping, no signal - unless one is pending already, of course. */ + if (child == NULL) + linux_resume (0, 0); + } enable_async_io (); - pid = waitpid (inferior_pid, &w, 0); + w = linux_wait_for_event (child); + stop_all_processes (); disable_async_io (); - if (pid != inferior_pid) - perror_with_name ("wait"); - if (WIFEXITED (w)) + /* If we are waiting for a particular child, and it exited, + linux_wait_for_event will return its exit status. Similarly if + the last child exited. If this is not the last child, however, + do not report it as exited until there is a 'thread exited' response + available in the remote protocol. Instead, just wait for another event. + This should be safe, because if the thread crashed we will already + have reported the termination signal to GDB; that should stop any + in-progress stepping operations, etc. + + Report the exit status of the last thread to exit. This matches + LinuxThreads' behavior. */ + + if (all_threads.head == all_threads.tail) { - fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); - *status = 'W'; - return ((unsigned char) WEXITSTATUS (w)); + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + clear_inferiors (); + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + clear_inferiors (); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } } - else if (!WIFSTOPPED (w)) + else { - fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); - *status = 'X'; - return ((unsigned char) WTERMSIG (w)); - } + if (!WIFSTOPPED (w)) + goto retry; - fetch_inferior_registers (0); + } *status = 'T'; return ((unsigned char) WSTOPSIG (w)); } +static void +send_sigstop (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + + if (process->stopped) + return; + + /* If we already have a pending stop signal for this process, don't + send another. */ + if (process->stop_expected) + { + process->stop_expected = 0; + return; + } + + if (debug_threads) + fprintf (stderr, "Sending sigstop to process %d\n", process->head.id); + + kill (process->head.id, SIGSTOP); + process->sigstop_sent = 1; +} + +static void +wait_for_sigstop (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + struct thread_info *saved_inferior, *thread; + int wstat, saved_tid; + + if (process->stopped) + return; + + saved_inferior = current_inferior; + saved_tid = ((struct inferior_list_entry *) saved_inferior)->id; + thread = (struct thread_info *) find_inferior_id (&all_threads, + process->tid); + wstat = linux_wait_for_event (thread); + + /* If we stopped with a non-SIGSTOP signal, save it for later + and record the pending SIGSTOP. If the process exited, just + return. */ + if (WIFSTOPPED (wstat) + && WSTOPSIG (wstat) != SIGSTOP) + { + if (debug_threads) + fprintf (stderr, "Stopped with non-sigstop signal\n"); + process->status_pending_p = 1; + process->status_pending = wstat; + process->stop_expected = 1; + } + + if (linux_thread_alive (saved_tid)) + current_inferior = saved_inferior; + else + { + if (debug_threads) + fprintf (stderr, "Previously current thread died.\n"); + + /* Set a valid thread as current. */ + set_desired_inferior (0); + } +} + +static void +stop_all_processes (void) +{ + stopping_threads = 1; + for_each_inferior (&all_processes, send_sigstop); + for_each_inferior (&all_processes, wait_for_sigstop); + stopping_threads = 0; +} + /* Resume execution of the inferior process. If STEP is nonzero, single-step it. If SIGNAL is nonzero, give it that signal. */ -void -myresume (int step, int signal) +static void +linux_resume_one_process (struct inferior_list_entry *entry, + int step, int signal) { + struct process_info *process = (struct process_info *) entry; + struct thread_info *saved_inferior; + + if (process->stopped == 0) + return; + + /* If we have pending signals or status, and a new signal, enqueue the + signal. Also enqueue the signal if we are waiting to reinsert a + breakpoint; it will be picked up again below. */ + if (signal != 0 + && (process->status_pending_p || process->pending_signals != NULL + || process->bp_reinsert != 0)) + { + struct pending_signals *p_sig; + p_sig = malloc (sizeof (*p_sig)); + p_sig->prev = process->pending_signals; + p_sig->signal = signal; + process->pending_signals = p_sig; + } + + if (process->status_pending_p) + return; + + saved_inferior = current_inferior; + current_inferior = get_process_thread (process); + + if (debug_threads) + fprintf (stderr, "Resuming process %d (%s, signal %d, stop %s)\n", inferior_pid, + step ? "step" : "continue", signal, + process->stop_expected ? "expected" : "not expected"); + + /* This bit needs some thinking about. If we get a signal that + we must report while a single-step reinsert is still pending, + we often end up resuming the thread. It might be better to + (ew) allow a stack of pending events; then we could be sure that + the reinsert happened right away and not lose any signals. + + Making this stack would also shrink the window in which breakpoints are + uninserted (see comment in linux_wait_for_process) but not enough for + complete correctness, so it won't solve that problem. It may be + worthwhile just to solve this one, however. */ + if (process->bp_reinsert != 0) + { + if (debug_threads) + fprintf (stderr, " pending reinsert at %08lx", (long)process->bp_reinsert); + if (step == 0) + fprintf (stderr, "BAD - reinserting but not stepping.\n"); + step = 1; + + /* Postpone any pending signal. It was enqueued above. */ + signal = 0; + } + + check_removed_breakpoint (process); + + if (debug_threads && the_low_target.get_pc != NULL) + { + fprintf (stderr, " "); + (long) (*the_low_target.get_pc) (); + } + + /* If we have pending signals, consume one unless we are trying to reinsert + a breakpoint. */ + if (process->pending_signals != NULL && process->bp_reinsert == 0) + { + struct pending_signals **p_sig; + + p_sig = &process->pending_signals; + while ((*p_sig)->prev != NULL) + p_sig = &(*p_sig)->prev; + + signal = (*p_sig)->signal; + free (*p_sig); + *p_sig = NULL; + } + + regcache_invalidate_one ((struct inferior_list_entry *) + get_process_thread (process)); + errno = 0; - ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, inferior_pid, 1, signal); + process->stopped = 0; + process->stepping = step; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, process->lwpid, 0, signal); + + current_inferior = saved_inferior; if (errno) perror_with_name ("ptrace"); } +/* This function is called once per process other than the first + one. The first process we are told the signal to continue + with, and whether to step or continue; for all others, any + existing signals will be marked in status_pending_p to be + reported momentarily, and we preserve the stepping flag. */ +static void +linux_continue_one_process (struct inferior_list_entry *entry) +{ + struct process_info *process; + process = (struct process_info *) entry; + linux_resume_one_process (entry, process->stepping, 0); +} +static void +linux_resume (int step, int signal) +{ + struct process_info *process; -#ifdef HAVE_LINUX_USRREGS + process = get_thread_process (current_inferior); -#define REGISTER_RAW_SIZE(regno) register_size((regno)) + /* If the current process has a status pending, this signal will + be enqueued and sent later. */ + linux_resume_one_process (&process->head, step, signal); + if (cont_thread == 0 || cont_thread == -1) + for_each_inferior (&all_processes, linux_continue_one_process); +} +#ifdef HAVE_LINUX_USRREGS int register_addr (int regnum) { int addr; - if (regnum < 0 || regnum >= num_regs) + if (regnum < 0 || regnum >= the_low_target.num_regs) error ("Invalid register number %d.", regnum); - addr = regmap[regnum]; + addr = the_low_target.regmap[regnum]; if (addr == -1) addr = 0; @@ -183,19 +934,21 @@ fetch_register (int regno) { CORE_ADDR regaddr; register int i; + char *buf; - if (regno >= num_regs) + if (regno >= the_low_target.num_regs) return; - if (cannot_fetch_register (regno)) + if ((*the_low_target.cannot_fetch_register) (regno)) return; regaddr = register_addr (regno); if (regaddr == -1) return; - for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE)) + buf = alloca (register_size (regno)); + for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - *(PTRACE_XFER_TYPE *) (register_data (regno) + i) = + *(PTRACE_XFER_TYPE *) (buf + i) = ptrace (PTRACE_PEEKUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, 0); regaddr += sizeof (PTRACE_XFER_TYPE); if (errno != 0) @@ -209,6 +962,8 @@ fetch_register (int regno) goto error_exit; } } + supply_register (regno, buf); + error_exit:; } @@ -217,7 +972,7 @@ static void usr_fetch_inferior_registers (int regno) { if (regno == -1 || regno == 0) - for (regno = 0; regno < num_regs; regno++) + for (regno = 0; regno < the_low_target.num_regs; regno++) fetch_register (regno); else fetch_register (regno); @@ -231,41 +986,45 @@ usr_store_inferior_registers (int regno) { CORE_ADDR regaddr; int i; + char *buf; if (regno >= 0) { - if (regno >= num_regs) + if (regno >= the_low_target.num_regs) return; - if (cannot_store_register (regno)) + if ((*the_low_target.cannot_store_register) (regno) == 1) return; regaddr = register_addr (regno); if (regaddr == -1) return; errno = 0; - for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE)) + buf = alloca (register_size (regno)); + collect_register (regno, buf); + for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE)) { errno = 0; ptrace (PTRACE_POKEUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, - *(int *) (register_data (regno) + i)); + *(int *) (buf + 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; + if ((*the_low_target.cannot_store_register) (regno) == 0) + { + 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); + for (regno = 0; regno < the_low_target.num_regs; regno++) + usr_store_inferior_registers (regno); } #endif /* HAVE_LINUX_USRREGS */ @@ -274,7 +1033,7 @@ usr_store_inferior_registers (int regno) #ifdef HAVE_LINUX_REGSETS static int -regsets_fetch_inferior_registers (void) +regsets_fetch_inferior_registers () { struct regset_info *regset; @@ -292,7 +1051,7 @@ regsets_fetch_inferior_registers (void) } buf = malloc (regset->size); - res = ptrace (regset->get_request, inferior_pid, 0, (int) buf); + res = ptrace (regset->get_request, inferior_pid, 0, buf); if (res < 0) { if (errno == EIO) @@ -312,16 +1071,21 @@ regsets_fetch_inferior_registers (void) } else { - perror ("Warning: ptrace(regsets_fetch_inferior_registers)"); + char s[256]; + sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d", + inferior_pid); + perror (s); + } } regset->store_function (buf); regset ++; } + return 0; } static int -regsets_store_inferior_registers (void) +regsets_store_inferior_registers () { struct regset_info *regset; @@ -340,7 +1104,7 @@ regsets_store_inferior_registers (void) buf = malloc (regset->size); regset->fill_function (buf); - res = ptrace (regset->set_request, inferior_pid, 0, (int) buf); + res = ptrace (regset->set_request, inferior_pid, 0, buf); if (res < 0) { if (errno == EIO) @@ -360,18 +1124,19 @@ regsets_store_inferior_registers (void) } else { - perror ("Warning: ptrace(regsets_fetch_inferior_registers)"); + perror ("Warning: ptrace(regsets_store_inferior_registers)"); } } regset ++; } + return 0; } #endif /* HAVE_LINUX_REGSETS */ void -fetch_inferior_registers (int regno) +linux_fetch_registers (int regno) { #ifdef HAVE_LINUX_REGSETS if (use_regsets_p) @@ -386,7 +1151,7 @@ fetch_inferior_registers (int regno) } void -store_inferior_registers (int regno) +linux_store_registers (int regno) { #ifdef HAVE_LINUX_REGSETS if (use_regsets_p) @@ -404,8 +1169,8 @@ store_inferior_registers (int regno) /* Copy LEN bytes from inferior's memory starting at MEMADDR to debugger memory starting at MYADDR. */ -void -read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len) +static void +linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len) { register int i; /* Round starting address down to longword boundary. */ @@ -433,8 +1198,8 @@ read_inferior_memory (CORE_ADDR memaddr, On failure (cannot write the inferior) returns the value of errno. */ -int -write_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len) +static int +linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len) { register int i; /* Round starting address down to longword boundary. */ @@ -446,6 +1211,11 @@ write_inferior_memory (CORE_ADDR memaddr register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); extern int errno; + if (debug_threads) + { + fprintf (stderr, "Writing %02x to %08lx\n", (unsigned)myaddr[0], (long)memaddr); + } + /* Fill start and end extra bytes of buffer with existing memory data. */ buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid, @@ -476,9 +1246,189 @@ write_inferior_memory (CORE_ADDR memaddr return 0; } - + +static void +linux_look_up_symbols (void) +{ +#ifdef USE_THREAD_DB + if (using_threads) + return; + + using_threads = thread_db_init (); +#endif +} + +/* Return 1 if this process is not stopped. */ +static int +unstopped_p (struct inferior_list_entry *entry, void *dummy) +{ + struct process_info *process = (struct process_info *) entry; + + if (process->stopped) + return 0; + + return 1; +} + +static int +linux_signal_pid () +{ + struct inferior_list_entry *process; + + process = find_inferior (&all_processes, unstopped_p, NULL); + + if (process == NULL) + { + warning ("no unstopped process"); + return inferior_pid; + } + + return pid_of ((struct process_info *) process); +} + + +static struct target_ops linux_target_ops = { + linux_create_inferior, + linux_attach, +#ifdef HAVE_TARGET_ATTACH + linux_detach, +#endif + linux_kill, + linux_thread_alive, + linux_resume, + linux_wait, + linux_fetch_registers, + linux_store_registers, + linux_read_memory, + linux_write_memory, + linux_look_up_symbols, + linux_signal_pid, +}; + +static void +linux_init_signals () +{ + /* FIXME drow/2002-06-09: As above, we should check with LinuxThreads + to find what the cancel signal actually is. */ + signal (SIGRTMIN+1, SIG_IGN); +} + + void initialize_low (void) { + using_threads = 0; + set_target_ops (&linux_target_ops); + set_breakpoint_data (the_low_target.breakpoint, + the_low_target.breakpoint_len); init_registers (); + linux_init_signals (); +} +#ifdef HAVE_TARGET_SHELL +extern int remote_desc; +static void +my_perror(char *s) +{ + const char *err; + + if (errno < sys_nerr) + err = sys_errlist[errno]; + else + err = "unknown error"; + fprintf(stderr, "%s: ", s); + error(err); +} + +void +exec_shellcmd(char *args) +{ + int fd[2]; + int pid, saved_pid; + int len; + char ch; + char buf[100]; /* must be <128 */ + char **shell, *argv[100], **argp, *p; + static char *shells[] = { + "/bin/sh", "/bin/bash", "/bin/tcsh", + "/usr/bin/sh", "/usr/bin/bash", "/usr/bin/tsch", (char *)0}; + + if (pipe(fd)) + perror_with_name("opening pipe"); + + if (current_inferior) + saved_pid = inferior_pid; + + switch (pid = fork()) + { + case -1: + perror_with_name("forking exec shell"); + break; + + case 0: + /* disable stdin */ + close(0); + open("/dev/null", O_RDONLY); + + /* redirect stdout and stderr to pipe */ + if (dup2(fd[1], 1) != 1 || dup2(fd[1], 2) != 2) + { + my_perror("dup in exec shell"); + exit(1); + } + /* which shell? */ + for (shell = shells; *shell; shell++) + if (access(*shell, X_OK) == 0) + break; + if (*shell) + execlp(*shell, *shell, "-c", args, (char *)0); + else + {/* No shell installed. Exec it directly */ + for (p = args, argp = argv; + *p && argp < &argv[sizeof(argv)/sizeof(argv[0])] - 1; ) + { + *argp++ = p; + while (*p && !isspace(*p)) + p++; /* skip word */ + if (!*p) + break; /* no more chars */ + *p++ = '\0'; + while (isspace(*p)) + p++; /* skip delimiting spaces */ + if (!*p) + break; /* no more chars */ + } + *argp = (char *)0; + execvp(*argv, argv); + } + + my_perror("exec shell"); + exit(1); + break; + + default: /* Parent - gdbserver */ + close(fd[1]); /* To get EOF properly */ + enable_async_io(); + /* To be able to stop the long output with ^C place + a fake process ID */ + if (current_inferior) + inferior_pid = pid; + while (1) + { + if ((len = read(fd[0], buf, sizeof buf)) > 0) + { + ch = (char)len; /* We know it fit */ + write(remote_desc, &ch, 1); + write(remote_desc, buf, len); + } + else if (len != -1 || errno != EINTR) + break; + + } + write(remote_desc, &len, 1); /* end mark (0) */ + waitpid(pid, (void *)0, 0); + if (current_inferior) + inferior_pid = saved_pid; + disable_async_io(); + } } +#endif diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-low.h gdb-5.2.1/gdb/gdbserver/linux-low.h --- gdb-5.2.1-orig/gdb/gdbserver/linux-low.h Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-low.h Tue Sep 2 14:04:41 2003 @@ -18,20 +18,112 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef HAVE_LINUX_USR_REGISTERS -extern int regmap[]; -extern int num_regs; -int cannot_fetch_register (int regno); -int cannot_store_register (int regno); -#endif - #ifdef HAVE_LINUX_REGSETS -typedef void (*regset_func) (void *); +typedef void (*regset_fill_func) (void *); +typedef void (*regset_store_func) (const void *); +enum regset_type { + GENERAL_REGS, + FP_REGS, + EXTENDED_REGS, +}; + struct regset_info { int get_request, set_request; int size; - regset_func fill_function, store_function; + enum regset_type type; + regset_fill_func fill_function; + regset_store_func store_function; }; extern struct regset_info target_regsets[]; #endif + +struct linux_target_ops +{ + int num_regs; + int *regmap; + int (*cannot_fetch_register) (int); + + /* Returns 0 if we can store the register, 1 if we can not + store the register, and 2 if failure to store the register + is acceptable. */ + int (*cannot_store_register) (int); + CORE_ADDR (*get_pc) (void); + void (*set_pc) (CORE_ADDR newpc); + const char *breakpoint; + int breakpoint_len; + CORE_ADDR (*breakpoint_reinsert_addr) (void); + + + int decr_pc_after_break; + int (*breakpoint_at) (CORE_ADDR pc); +}; + +extern struct linux_target_ops the_low_target; + +#define get_process(inf) ((struct process_info *)(inf)) +#define get_thread_process(thr) (get_process (inferior_target_data (thr))) +#define get_process_thread(proc) ((struct thread_info *) \ + find_inferior_id (&all_threads, \ + get_process (proc)->tid)) + +struct process_info +{ + struct inferior_list_entry head; + int thread_known; + int lwpid; + int tid; + + /* If this flag is set, the next SIGSTOP will be ignored (the process will + be immediately resumed). */ + int stop_expected; + + /* If this flag is set, the process is known to be stopped right now (stop + event already received in a wait()). */ + int stopped; + + /* If this flag is set, we have sent a SIGSTOP to this process and are + waiting for it to stop. */ + int sigstop_sent; + + /* If this flag is set, STATUS_PENDING is a waitstatus that has not yet + been reported. */ + int status_pending_p; + int status_pending; + + /* If this flag is set, the pending status is a (GDB-placed) breakpoint. */ + int pending_is_breakpoint; + CORE_ADDR pending_stop_pc; + + /* If this is non-zero, it is a breakpoint to be reinserted at our next + stop (SIGTRAP stops only). */ + CORE_ADDR bp_reinsert; + + /* If this flag is set, the last continue operation on this process + was a single-step. */ + int stepping; + + /* If this is non-zero, it points to a chain of signals which need to + be delivered to this process. */ + struct pending_signals *pending_signals; +}; +extern struct inferior_list all_processes; + +void linux_attach_lwp (int pid, int tid); +#ifdef HAVE_TARGET_ATTACH +void linux_detach_lwp (int pid, int tid); +int thread_db_deinit (void); +#endif +#ifdef HAVE_TARGET_SHELL +void exec_shellcmd(char *); +#endif + +int thread_db_init (void); + + + + + + + + diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-m68k-low.c gdb-5.2.1/gdb/gdbserver/linux-m68k-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-m68k-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-m68k-low.c Tue Sep 2 14:04:41 2003 @@ -26,10 +26,10 @@ #include #endif -int num_regs = 31; +#define m68k_num_regs 31 /* This table must line up with REGISTER_NAMES in tm-m68k.h */ -int regmap[] = +static int m68k_regmap[] = { #ifdef PT_D0 PT_D0 * 4, PT_D1 * 4, PT_D2 * 4, PT_D3 * 4, @@ -52,14 +52,21 @@ int regmap[] = #endif }; -int -cannot_store_register (int regno) +static int +m68k_cannot_store_register (int regno) { - return (regno >= num_regs); + return (regno >= m68k_num_regs); } -int -cannot_fetch_register (int regno) +static int +m68k_cannot_fetch_register (int regno) { - return (regno >= num_regs); + return (regno >= m68k_num_regs); } + +struct linux_target_ops the_low_target = { + m68k_num_regs, + m68k_regmap, + m68k_cannot_fetch_register, + m68k_cannot_store_register, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-mips-low.c gdb-5.2.1/gdb/gdbserver/linux-mips-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-mips-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-mips-low.c Tue Sep 2 14:04:41 2003 @@ -26,14 +26,14 @@ #include #endif -int num_regs = 90; +#define mips_num_regs 90 #include /* Return the ptrace ``address'' of register REGNO. */ /* Matches mips_generic32_regs */ -int regmap[] = { +static int mips_regmap[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, @@ -63,10 +63,10 @@ int regmap[] = { ZERO_REGNUM. We also can not set BADVADDR, CAUSE, or FCRIR via ptrace(). */ -int -cannot_fetch_register (int regno) +static int +mips_cannot_fetch_register (int regno) { - if (regmap[regno] == -1) + if (mips_regmap[regno] == -1) return 1; if (find_regno ("zero") == regno) @@ -75,10 +75,10 @@ cannot_fetch_register (int regno) return 0; } -int -cannot_store_register (int regno) +static int +mips_cannot_store_register (int regno) { - if (regmap[regno] == -1) + if (mips_regmap[regno] == -1) return 1; if (find_regno ("zero") == regno) @@ -95,3 +95,61 @@ cannot_store_register (int regno) return 0; } + +static CORE_ADDR +mips_get_pc () +{ + unsigned long pc; + collect_register_by_name ("pc", &pc); + return pc; +} + +static void +mips_set_pc (CORE_ADDR pc) +{ + unsigned long newpc = pc; + supply_register_by_name ("pc", &newpc); +} + +/* Correct in either endianness. */ +static const unsigned long mips_breakpoint = 0x0005000d; +#define mips_breakpoint_len 4 + +/* We only place breakpoints in empty marker functions, and thread locking + is outside of the function. So rather than importing software single-step, + we can just run until exit. */ +static CORE_ADDR +mips_reinsert_addr () +{ + unsigned long pc; + collect_register_by_name ("ra", &pc); + return pc; +} + +static int +mips_breakpoint_at (CORE_ADDR where) +{ + unsigned long insn; + + (*the_target->read_memory) (where, (char *) &insn, 4); + if (insn == mips_breakpoint) + return 1; + + /* If necessary, recognize more trap instructions here. GDB only uses the + one. */ + return 0; +} + +struct linux_target_ops the_low_target = { + mips_num_regs, + mips_regmap, + mips_cannot_fetch_register, + mips_cannot_store_register, + mips_get_pc, + mips_set_pc, + (const char *) &mips_breakpoint, + mips_breakpoint_len, + mips_reinsert_addr, + 0, + mips_breakpoint_at, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-ppc-low.c gdb-5.2.1/gdb/gdbserver/linux-ppc-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-ppc-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-ppc-low.c Tue Sep 2 14:04:41 2003 @@ -25,10 +25,10 @@ #include -int num_regs = 71; +#define ppc_num_regs 71 /* Currently, don't check/send MQ. */ -int regmap[] = +static int ppc_regmap[] = {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4, PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4, PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4, @@ -46,17 +46,71 @@ int regmap[] = PT_FPR0*4+192, PT_FPR0*4+200, PT_FPR0*4+208, PT_FPR0*4+216, PT_FPR0*4+224, PT_FPR0*4+232, PT_FPR0*4+240, PT_FPR0*4+248, PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4, - PT_CTR * 4, PT_XER * 4, -1, }; + PT_CTR * 4, PT_XER * 4, PT_FPSCR * 4, }; -int -cannot_store_register (int regno) +static int +ppc_cannot_store_register (int regno) +{ + /* Some kernels do not allow us to store fpscr. */ + if (regno == find_regno ("fpscr")) + return 2; + + return 0; +} + +static int +ppc_cannot_fetch_register (int regno) { return 0; } -int -cannot_fetch_register (int regno) +static CORE_ADDR +ppc_get_pc (void) { + unsigned long pc; + + collect_register_by_name ("pc", &pc); + return (CORE_ADDR) pc; +} + +static void +ppc_set_pc (CORE_ADDR pc) +{ + unsigned long newpc = pc; + + supply_register_by_name ("pc", &newpc); +} + +/* Correct in either endianness. Note that this file is + for PowerPC only, not PowerPC64. + This instruction is "twge r2, r2", which GDB uses as a software + breakpoint. */ +static const unsigned long ppc_breakpoint = 0x7d821008; +#define ppc_breakpoint_len 4 + +static int +ppc_breakpoint_at (CORE_ADDR where) +{ + unsigned long insn; + + (*the_target->read_memory) (where, (char *) &insn, 4); + if (insn == ppc_breakpoint) + return 1; + /* If necessary, recognize more trap instructions here. GDB only uses the + one. */ return 0; } +struct linux_target_ops the_low_target = { + ppc_num_regs, + ppc_regmap, + ppc_cannot_fetch_register, + ppc_cannot_store_register, + ppc_get_pc, + ppc_set_pc, + (const char *) &ppc_breakpoint, + ppc_breakpoint_len, + NULL, + 0, + ppc_breakpoint_at, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-s390-low.c gdb-5.2.1/gdb/gdbserver/linux-s390-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-s390-low.c Thu Feb 28 17:58:14 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-s390-low.c Tue Sep 2 14:04:41 2003 @@ -27,9 +27,9 @@ #include -int num_regs = 67; +#define s390_num_regs 67 -int regmap[] = { +static int s390_regmap[] = { PT_PSWMASK, PT_PSWADDR, PT_GPR0, PT_GPR1, PT_GPR2, PT_GPR3, @@ -62,20 +62,27 @@ int regmap[] = { #endif }; -int -cannot_fetch_register (int regno) +static int +s390_cannot_fetch_register (int regno) { - if (regmap[regno] == -1) + if (s390_regmap[regno] == -1) return 1; return 0; } -int -cannot_store_register (int regno) +static int +s390_cannot_store_register (int regno) { - if (regmap[regno] == -1) + if (s390_regmap[regno] == -1) return 1; return 0; } + +struct linux_target_ops the_low_target = { + s390_num_regs, + s390_regmap, + s390_cannot_fetch_register, + s390_cannot_store_register, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-sh-low.c gdb-5.2.1/gdb/gdbserver/linux-sh-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-sh-low.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-sh-low.c Tue Sep 2 14:04:41 2003 @@ -28,10 +28,10 @@ #include -int num_regs = 41; +#define sh_num_regs 41 /* Currently, don't check/send MQ. */ -int regmap[] = { +static int sh_regmap[] = { 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, @@ -45,15 +45,61 @@ int regmap[] = { REG_FPREG0+48, REG_FPREG0+52, REG_FPREG0+56, REG_FPREG0+60, }; -int -cannot_store_register (int regno) +static int +sh_cannot_store_register (int regno) { return 0; } -int -cannot_fetch_register (int regno) +static int +sh_cannot_fetch_register (int regno) { return 0; } +static CORE_ADDR +sh_get_pc () +{ + unsigned long pc; + collect_register_by_name ("pc", &pc); + return pc; +} + +static void +sh_set_pc (CORE_ADDR pc) +{ + unsigned long newpc = pc; + supply_register_by_name ("pc", &newpc); +} + +/* Correct in either endianness, obviously. */ +static const unsigned short sh_breakpoint = 0xc3c3; +#define sh_breakpoint_len 2 + +static int +sh_breakpoint_at (CORE_ADDR where) +{ + unsigned short insn; + + (*the_target->read_memory) (where, (char *) &insn, 2); + if (insn == sh_breakpoint) + return 1; + + /* If necessary, recognize more trap instructions here. GDB only uses the + one. */ + return 0; +} + +struct linux_target_ops the_low_target = { + sh_num_regs, + sh_regmap, + sh_cannot_fetch_register, + sh_cannot_store_register, + sh_get_pc, + sh_set_pc, + (const char *) &sh_breakpoint, + sh_breakpoint_len, + NULL, + 0, + sh_breakpoint_at, +}; diff -purN gdb-5.2.1-orig/gdb/gdbserver/linux-x86-64-low.c gdb-5.2.1/gdb/gdbserver/linux-x86-64-low.c --- gdb-5.2.1-orig/gdb/gdbserver/linux-x86-64-low.c Thu Mar 7 21:56:18 2002 +++ gdb-5.2.1/gdb/gdbserver/linux-x86-64-low.c Tue Sep 2 14:04:41 2003 @@ -28,12 +28,15 @@ #include #include -static int regmap[] = { - RAX, RDX, RCX, RBX, +#define X86_64_NUM_GREGS 22 + +static int x86_64_regmap[X86_64_NUM_GREGS] = { + RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14, R15, - RIP, EFLAGS + RIP, EFLAGS, + DS, ES, FS, GS }; static void @@ -41,8 +44,8 @@ x86_64_fill_gregset (void *buf) { int i; - for (i = 0; i < 18; i++) - collect_register (i, ((char *) buf) + regmap[i]); + for (i = 0; i < X86_64_NUM_GREGS; i++) + collect_register (i, ((char *) buf) + x86_64_regmap[i]); } static void @@ -50,8 +53,8 @@ x86_64_store_gregset (void *buf) { int i; - for (i = 0; i < 18; i++) - supply_register (i, ((char *) buf) + regmap[i]); + for (i = 0; i < X86_64_NUM_GREGS; i++) + supply_register (i, ((char *) buf) + x86_64_regmap[i]); } static void @@ -66,11 +69,19 @@ x86_64_store_fpregset (void *buf) i387_fxsave_to_cache (buf); } - struct regset_info target_regsets[] = { { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t), + GENERAL_REGS, x86_64_fill_gregset, x86_64_store_gregset }, { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t), + FP_REGS, x86_64_fill_fpregset, x86_64_store_fpregset }, - { 0, 0, -1, NULL, NULL } + { 0, 0, -1, -1, NULL, NULL } +}; + +struct linux_target_ops the_low_target = { + -1, + NULL, + NULL, + NULL, }; diff -purN gdb-5.2.1-orig/gdb/gdbserver/low-linux.c gdb-5.2.1/gdb/gdbserver/low-linux.c --- gdb-5.2.1-orig/gdb/gdbserver/low-linux.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/low-linux.c Tue Sep 2 14:04:40 2003 @@ -0,0 +1,957 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "server.h" +#include +#include "frame.h" +#include "inferior.h" +#include "nm.h" +#include "xm.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/***************Begin MY defs*********************/ +static char my_registers[REGISTER_BYTES]; +char *registers = my_registers; +/***************End MY defs*********************/ + +#ifdef HAVE_TARGET_ATTACH +#ifdef GDB_TARGET_POWERPC +#undef NUM_FREGS +#undef NUM_REGS +#define NUM_REGS 71 +#define NUM_FREGS 0 + +static int regmap[] = +{PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, + PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15, + PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23, + PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31, + PT_FPR0, PT_FPR0 + 2, PT_FPR0 + 4, PT_FPR0 + 6, PT_FPR0 + 8, PT_FPR0 + 10, PT_FPR0 + 12, PT_FPR0 + 14, + PT_FPR0 + 16, PT_FPR0 + 18, PT_FPR0 + 20, PT_FPR0 + 22, PT_FPR0 + 24, PT_FPR0 + 26, PT_FPR0 + 28, PT_FPR0 + 30, + PT_FPR0 + 32, PT_FPR0 + 34, PT_FPR0 + 36, PT_FPR0 + 38, PT_FPR0 + 40, PT_FPR0 + 42, PT_FPR0 + 44, PT_FPR0 + 46, + PT_FPR0 + 48, PT_FPR0 + 50, PT_FPR0 + 52, PT_FPR0 + 54, PT_FPR0 + 56, PT_FPR0 + 58, PT_FPR0 + 60, PT_FPR0 + 62, + PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ}; + +int +ppc_register_u_addr (int ustart, int regnum) +{ + return (ustart + 4 * regmap[regnum]); +} + +static void +initialize_arch () +{ + return; +} + +#elif defined(GDB_TARGET_POWERPC) +static int regmap[] = + {PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, + PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15, + PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23, + PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31, + PT_FPR0, PT_FPR0+2, PT_FPR0+4, PT_FPR0+6, PT_FPR0+8, PT_FPR0+10,PT_FPR0+12,PT_FPR0+14, + PT_FPR0+16,PT_FPR0+18,PT_FPR0+20,PT_FPR0+22,PT_FPR0+24,PT_FPR0+26,PT_FPR0+28,PT_FPR0+30, + PT_FPR0+32,PT_FPR0+34,PT_FPR0+36,PT_FPR0+38,PT_FPR0+40,PT_FPR0+42,PT_FPR0+44,PT_FPR0+46, + PT_FPR0+48,PT_FPR0+50,PT_FPR0+52,PT_FPR0+54,PT_FPR0+56,PT_FPR0+58,PT_FPR0+60,PT_FPR0+62, + PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ }; + +int ppc_register_u_addr(int ustart, int regnum) +{ + return (ustart + 4 * regmap[regnum]); +} +static void + initialize_arch () +{ + return; +} + +#undef HAVE_SYS_REG_H +#endif +#endif +#ifdef HAVE_SYS_REG_H +#include +#endif + +/* Default the type of the ptrace transfer to int. */ +#ifndef PTRACE_XFER_TYPE +#define PTRACE_XFER_TYPE int +#endif + +extern int errno; + +static void initialize_arch (void); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. */ + +int +create_inferior (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 (void) +{ + 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 (int pid) +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (char *status) +{ + int pid; + union wait w; + + enable_async_io (); +#ifdef HAVE_TARGET_ATTACH + pid = wait (&w); +#else + pid = waitpid (inferior_pid, &w, 0); +#endif + disable_async_io (); + if (pid != inferior_pid) +#ifdef HAVE_TARGET_ATTACH + { + if (errno) perror_with_name ("wait"); + inferior_pid=pid; + } +#else + perror_with_name ("wait"); +#endif + + 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 (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 + +#ifdef I386_GNULINUX_TARGET +/* This module only supports access to the general purpose registers. + Adjust the relevant constants accordingly. + + FIXME: kettenis/2001-03-28: We should really use PTRACE_GETREGS to + get at the registers. Better yet, we should try to share code with + i386-linux-nat.c. */ +#undef NUM_FREGS +#define NUM_FREGS 0 +#undef NUM_REGS +#define NUM_REGS NUM_GREGS + +/* This stuff comes from i386-tdep.c. */ + +/* i386_register_byte[i] is the offset into the register file of the + start of register number i. We initialize this from + i386_register_raw_size. */ +int i386_register_byte[MAX_NUM_REGS]; + +/* i386_register_raw_size[i] is the number of bytes of storage in + GDB's register array occupied by register i. */ +int i386_register_raw_size[MAX_NUM_REGS] = { + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 10, 10, 10, 10, + 10, 10, 10, 10, + 4, 4, 4, 4, + 4, 4, 4, 4, + 16, 16, 16, 16, + 16, 16, 16, 16, + 4 +}; + +static void +initialize_arch (void) +{ + /* Initialize the table saying where each register starts in the + register file. */ + { + int i, offset; + + offset = 0; + for (i = 0; i < MAX_NUM_REGS; i++) + { + i386_register_byte[i] = offset; + offset += i386_register_raw_size[i]; + } + } +} + +/* This stuff comes from i386-linux-nat.c. */ + +/* Mapping between the general-purpose registers in `struct user' + format and GDB's register array layout. */ +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS +}; + +/* Return the address of register REGNUM. BLOCKEND is the value of + u.u_ar0, which should point to the registers. */ + +CORE_ADDR +register_u_addr (CORE_ADDR blockend, int regnum) +{ + return (blockend + 4 * regmap[regnum]); +} +#elif defined(TARGET_M68K) +static void +initialize_arch (void) +{ + return; +} + +/* 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 (int blockend, int regnum) +{ + return (blockend + 4 * regmap[regnum]); +} +#elif defined(IA64_GNULINUX_TARGET) +#undef NUM_FREGS +#define NUM_FREGS 0 + +#include + +static int u_offsets[] = + { + /* general registers */ + -1, /* gr0 not available; i.e, it's always zero */ + PT_R1, + PT_R2, + PT_R3, + PT_R4, + PT_R5, + PT_R6, + PT_R7, + PT_R8, + PT_R9, + PT_R10, + PT_R11, + PT_R12, + PT_R13, + PT_R14, + PT_R15, + PT_R16, + PT_R17, + PT_R18, + PT_R19, + PT_R20, + PT_R21, + PT_R22, + PT_R23, + PT_R24, + PT_R25, + PT_R26, + PT_R27, + PT_R28, + PT_R29, + PT_R30, + PT_R31, + /* gr32 through gr127 not directly available via the ptrace interface */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* Floating point registers */ + -1, -1, /* f0 and f1 not available (f0 is +0.0 and f1 is +1.0) */ + PT_F2, + PT_F3, + PT_F4, + PT_F5, + PT_F6, + PT_F7, + PT_F8, + PT_F9, + PT_F10, + PT_F11, + PT_F12, + PT_F13, + PT_F14, + PT_F15, + PT_F16, + PT_F17, + PT_F18, + PT_F19, + PT_F20, + PT_F21, + PT_F22, + PT_F23, + PT_F24, + PT_F25, + PT_F26, + PT_F27, + PT_F28, + PT_F29, + PT_F30, + PT_F31, + PT_F32, + PT_F33, + PT_F34, + PT_F35, + PT_F36, + PT_F37, + PT_F38, + PT_F39, + PT_F40, + PT_F41, + PT_F42, + PT_F43, + PT_F44, + PT_F45, + PT_F46, + PT_F47, + PT_F48, + PT_F49, + PT_F50, + PT_F51, + PT_F52, + PT_F53, + PT_F54, + PT_F55, + PT_F56, + PT_F57, + PT_F58, + PT_F59, + PT_F60, + PT_F61, + PT_F62, + PT_F63, + PT_F64, + PT_F65, + PT_F66, + PT_F67, + PT_F68, + PT_F69, + PT_F70, + PT_F71, + PT_F72, + PT_F73, + PT_F74, + PT_F75, + PT_F76, + PT_F77, + PT_F78, + PT_F79, + PT_F80, + PT_F81, + PT_F82, + PT_F83, + PT_F84, + PT_F85, + PT_F86, + PT_F87, + PT_F88, + PT_F89, + PT_F90, + PT_F91, + PT_F92, + PT_F93, + PT_F94, + PT_F95, + PT_F96, + PT_F97, + PT_F98, + PT_F99, + PT_F100, + PT_F101, + PT_F102, + PT_F103, + PT_F104, + PT_F105, + PT_F106, + PT_F107, + PT_F108, + PT_F109, + PT_F110, + PT_F111, + PT_F112, + PT_F113, + PT_F114, + PT_F115, + PT_F116, + PT_F117, + PT_F118, + PT_F119, + PT_F120, + PT_F121, + PT_F122, + PT_F123, + PT_F124, + PT_F125, + PT_F126, + PT_F127, + /* predicate registers - we don't fetch these individually */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* branch registers */ + PT_B0, + PT_B1, + PT_B2, + PT_B3, + PT_B4, + PT_B5, + PT_B6, + PT_B7, + /* virtual frame pointer and virtual return address pointer */ + -1, -1, + /* other registers */ + PT_PR, + PT_CR_IIP, /* ip */ + PT_CR_IPSR, /* psr */ + PT_CFM, /* cfm */ + /* kernel registers not visible via ptrace interface (?) */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* hole */ + -1, -1, -1, -1, -1, -1, -1, -1, + PT_AR_RSC, + PT_AR_BSP, + PT_AR_BSPSTORE, + PT_AR_RNAT, + -1, + -1, /* Not available: FCR, IA32 floating control register */ + -1, -1, + -1, /* Not available: EFLAG */ + -1, /* Not available: CSD */ + -1, /* Not available: SSD */ + -1, /* Not available: CFLG */ + -1, /* Not available: FSR */ + -1, /* Not available: FIR */ + -1, /* Not available: FDR */ + -1, + PT_AR_CCV, + -1, -1, -1, + PT_AR_UNAT, + -1, -1, -1, + PT_AR_FPSR, + -1, -1, -1, + -1, /* Not available: ITC */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, + PT_AR_PFS, + PT_AR_LC, + -1, /* Not available: EC, the Epilog Count register */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, + /* nat bits - not fetched directly; instead we obtain these bits from + either rnat or unat or from memory. */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }; + +int +ia64_register_u_addr (int blockend, int regnum) +{ + int addr; + + if (regnum < 0 || regnum >= NUM_REGS) + error ("Invalid register number %d.", regnum); + + addr = u_offsets[regnum]; + if (addr == -1) + addr = 0; + + return addr; +} + +static void +initialize_arch (void) +{ + return; +} +#elif defined (ARM_GNULINUX_TARGET) +static void + initialize_arch() + { + return; + } +int arm_linux_register_u_addr(int blockend, int regnum) +{ + return blockend + REGISTER_BYTE (regnum); +} +#endif + +CORE_ADDR +register_addr (int regno, CORE_ADDR blockend) +{ + CORE_ADDR addr; + + if (regno < 0 || regno >= NUM_REGS) + error ("Invalid register number %d.", regno); + + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +/* Fetch one register. */ + +static void +fetch_register (int regno) +{ + CORE_ADDR 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 (PTRACE_XFER_TYPE)) + { + errno = 0; + *(PTRACE_XFER_TYPE *) ®isters[REGISTER_BYTE (regno) + i] = + ptrace (PTRACE_PEEKUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, 0); + regaddr += sizeof (PTRACE_XFER_TYPE); + 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 (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 (int regno) +{ + CORE_ADDR regaddr; + 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_POKEUSER, 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 (CORE_ADDR memaddr, char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) + / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer + = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) + { + buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 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 (CORE_ADDR memaddr, char *myaddr, int len) +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) / sizeof (PTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE)); + 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 (PTRACE_XFER_TYPE), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) + { + errno = 0; + ptrace (PTRACE_POKETEXT, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +void +initialize_low (void) +{ + initialize_arch (); +} +#ifdef HAVE_TARGET_ATTACH +int +process_attach (int pid) +{ + if (remote_debug) + printf("process_attach: pid=%d\n\r", pid); + + if (pid == getpid()) + return -1; + + errno = 0; + ptrace(PTRACE_ATTACH, pid, (PTRACE_ARG3_TYPE)0, 0); + return errno ? -1 : pid; +} + +int +process_detach(int sig) +{ + memset(registers, 0, sizeof(registers)); + + if (remote_debug) + printf("process_detach: pid=%d, signal = %d\n\r",inferior_pid,sig); + + if (!inferior_pid) + return -1; + fprintf(stderr, "Detaching process %d with signal %d...\n",inferior_pid, sig); + ptrace(PTRACE_DETACH, inferior_pid, (PTRACE_ARG3_TYPE)sig, 0); + inferior_pid = 0; + return 0; +} + +#endif + +#ifdef HAVE_TARGET_SHELL +extern int remote_desc; +static void +my_perror(char *s) +{ + const char *err; + + if (errno < sys_nerr) + err = sys_errlist[errno]; + else + err = "unknown error"; + fprintf(stderr, "%s: ", s); + error(err); +} + +void +exec_shellcmd(char *args) +{ + int fd[2]; + int pid, saved_pid = inferior_pid; + char len; + char buf[100]; /* must be <128 */ + char **shell, *argv[100], **argp, *p; + static char *shells[] = { + "/bin/sh", "/bin/bash", "/bin/tcsh", + "/usr/bin/sh", "/usr/bin/bash", "/usr/bin/tsch", (char *)0}; + + if (pipe(fd)) + perror_with_name("opening pipe"); + + switch (pid = fork()) + { + case -1: + perror_with_name("forking exec shell"); + break; + + case 0: + /* disable stdin */ + close(0); + open("/dev/null", O_RDONLY); + + /* redirect stdout and stderr to pipe */ + if (dup2(fd[1], 1) != 1 || dup2(fd[1], 2) != 2) + { + my_perror("dup in exec shell"); + exit(1); + } + + /* which shell? */ + for (shell = shells; *shell; shell++) + if (access(*shell, X_OK) == 0) + break; + if (*shell) + execlp(*shell, *shell, "-c", args, (char *)0); + else + {/* No shell installed. Exec it directly */ + for (p = args, argp = argv; + *p && argp < &argv[sizeof(argv)/sizeof(argv[0])] - 1; ) + { + *argp++ = p; + while (*p && !isspace(*p)) + p++; /* skip word */ + if (!*p) + break; /* no more chars */ + *p++ = '\0'; + while (isspace(*p)) + p++; /* skip delimiting spaces */ + if (!*p) + break; /* no more chars */ + } + *argp = (char *)0; + execvp(*argv, argv); + } + + my_perror("exec shell"); + exit(1); + break; + + default: /* Parent - gdbserver */ + close(fd[1]); /* To get EOF properly */ + enable_async_io(); + inferior_pid = pid; /* fake process ID */ + while (1) + { + if ((len = read(fd[0], buf, sizeof buf)) > 0) + { + write(remote_desc, &len, 1); + write(remote_desc, buf, len); + } + else if (len != -1 || errno != EINTR) + break; + } + write(remote_desc, &len, 1); /* end mark (0) */ + waitpid(pid, (void *)0, 0); + inferior_pid = saved_pid; + disable_async_io(); + } +} +#endif diff -purN gdb-5.2.1-orig/gdb/gdbserver/mem-break.c gdb-5.2.1/gdb/gdbserver/mem-break.c --- gdb-5.2.1-orig/gdb/gdbserver/mem-break.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/mem-break.c Tue Sep 2 14:04:41 2003 @@ -0,0 +1,280 @@ +/* Memory breakpoint operations for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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" + +const char *breakpoint_data; +int breakpoint_len; + +#define MAX_BREAKPOINT_LEN 8 + +struct breakpoint +{ + struct breakpoint *next; + CORE_ADDR pc; + unsigned char old_data[MAX_BREAKPOINT_LEN]; + + /* Non-zero iff we are stepping over this breakpoint. */ + int reinserting; + + /* Non-NULL iff this breakpoint was inserted to step over + another one. Points to the other breakpoint (which is also + in the *next chain somewhere). */ + struct breakpoint *breakpoint_to_reinsert; + + /* Function to call when we hit this breakpoint. */ + void (*handler) (CORE_ADDR); +}; + +struct breakpoint *breakpoints; + +void +set_breakpoint_at (CORE_ADDR where, void (*handler) (CORE_ADDR)) +{ + struct breakpoint *bp; + + if (breakpoint_data == NULL) + error ("Target does not support breakpoints."); + + bp = malloc (sizeof (struct breakpoint)); + memset (bp, 0, sizeof (struct breakpoint)); + + (*the_target->read_memory) (where, bp->old_data, + breakpoint_len); + (*the_target->write_memory) (where, breakpoint_data, + breakpoint_len); + + bp->pc = where; + bp->handler = handler; + + bp->next = breakpoints; + breakpoints = bp; +} + +void +delete_breakpoint (struct breakpoint *bp) +{ + struct breakpoint *cur; + + if (breakpoints == bp) + { + breakpoints = bp->next; + (*the_target->write_memory) (bp->pc, bp->old_data, + breakpoint_len); + free (bp); + return; + } + cur = breakpoints; + while (cur->next) + { + if (cur->next == bp) + { + cur->next = bp->next; + (*the_target->write_memory) (bp->pc, bp->old_data, + breakpoint_len); + free (bp); + return; + } + } + warning ("Could not find breakpoint in list."); +} + +struct breakpoint * +find_breakpoint_at (CORE_ADDR where) +{ + struct breakpoint *bp = breakpoints; + + while (bp != NULL) + { + if (bp->pc == where) + return bp; + bp = bp->next; + } + + return NULL; +} + +static void +reinsert_breakpoint_handler (CORE_ADDR stop_pc) +{ + struct breakpoint *stop_bp, *orig_bp; + + stop_bp = find_breakpoint_at (stop_pc); + if (stop_bp == NULL) + error ("lost the stopping breakpoint."); + + orig_bp = stop_bp->breakpoint_to_reinsert; + if (orig_bp == NULL) + error ("no breakpoint to reinsert"); + + (*the_target->write_memory) (orig_bp->pc, breakpoint_data, + breakpoint_len); + orig_bp->reinserting = 0; + delete_breakpoint (stop_bp); +} + +void +reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at) +{ + struct breakpoint *bp, *orig_bp; + + set_breakpoint_at (stop_at, reinsert_breakpoint_handler); + + orig_bp = find_breakpoint_at (stop_pc); + if (orig_bp == NULL) + error ("Could not find original breakpoint in list."); + + bp = find_breakpoint_at (stop_at); + if (bp == NULL) + error ("Could not find breakpoint in list (reinserting by breakpoint)."); + bp->breakpoint_to_reinsert = orig_bp; + + (*the_target->write_memory) (orig_bp->pc, orig_bp->old_data, + breakpoint_len); + orig_bp->reinserting = 1; +} + +void +uninsert_breakpoint (CORE_ADDR stopped_at) +{ + struct breakpoint *bp; + + bp = find_breakpoint_at (stopped_at); + if (bp == NULL) + error ("Could not find breakpoint in list (uninserting)."); + + (*the_target->write_memory) (bp->pc, bp->old_data, + breakpoint_len); + bp->reinserting = 1; +} + +void +reinsert_breakpoint (CORE_ADDR stopped_at) +{ + struct breakpoint *bp; + + bp = find_breakpoint_at (stopped_at); + if (bp == NULL) + error ("Could not find breakpoint in list (uninserting)."); + if (! bp->reinserting) + error ("Breakpoint already inserted at reinsert time."); + + (*the_target->write_memory) (bp->pc, breakpoint_data, + breakpoint_len); + bp->reinserting = 0; +} + +int +check_breakpoints (CORE_ADDR stop_pc) +{ + struct breakpoint *bp; + + bp = find_breakpoint_at (stop_pc); + if (bp == NULL) + return 0; + if (bp->reinserting) + { + warning ("Hit a removed breakpoint?"); + return 0; + } + + (*bp->handler) (bp->pc); + return 1; +} + +void +set_breakpoint_data (const char *bp_data, int bp_len) +{ + breakpoint_data = bp_data; + breakpoint_len = bp_len; +} + +void +check_mem_read (CORE_ADDR mem_addr, char *buf, int mem_len) +{ + struct breakpoint *bp = breakpoints; + CORE_ADDR mem_end = mem_addr + mem_len; + + for (; bp != NULL; bp = bp->next) + { + CORE_ADDR bp_end = bp->pc + breakpoint_len; + CORE_ADDR start, end; + int copy_offset, copy_len, buf_offset; + + if (mem_addr >= bp_end) + continue; + if (bp->pc >= mem_end) + continue; + + start = bp->pc; + if (mem_addr > start) + start = mem_addr; + + end = bp_end; + if (end > mem_end) + end = mem_end; + + copy_len = end - start; + copy_offset = start - bp->pc; + buf_offset = start - mem_addr; + + memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); + } +} + +void +check_mem_write (CORE_ADDR mem_addr, char *buf, int mem_len) +{ + struct breakpoint *bp = breakpoints; + CORE_ADDR mem_end = mem_addr + mem_len; + + for (; bp != NULL; bp = bp->next) + { + CORE_ADDR bp_end = bp->pc + breakpoint_len; + CORE_ADDR start, end; + int copy_offset, copy_len, buf_offset; + + if (mem_addr >= bp_end) + continue; + if (bp->pc >= mem_end) + continue; + + start = bp->pc; + if (mem_addr > start) + start = mem_addr; + + end = bp_end; + if (end > mem_end) + end = mem_end; + + copy_len = end - start; + copy_offset = start - bp->pc; + buf_offset = start - mem_addr; + + memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len); + if (bp->reinserting == 0) + memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len); + } +} + + diff -purN gdb-5.2.1-orig/gdb/gdbserver/mem-break.h gdb-5.2.1/gdb/gdbserver/mem-break.h --- gdb-5.2.1-orig/gdb/gdbserver/mem-break.h Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/mem-break.h Tue Sep 2 14:04:41 2003 @@ -0,0 +1,76 @@ +/* Memory breakpoint interfaces for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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. */ + +#ifndef MEM_BREAK_H +#define MEM_BREAK_H + +/* Breakpoints are opaque. */ + +/* Create a new breakpoint at WHERE, and call HANDLER when + it is hit. */ + +void set_breakpoint_at (CORE_ADDR where, + void (*handler) (CORE_ADDR)); + +#ifdef HAVE_TARGET_ATTACH +struct breakpoint *find_breakpoint_at (CORE_ADDR where); +void delete_breakpoint (struct breakpoint *bp); +#endif + +/* Create a reinsertion breakpoint at STOP_AT for the breakpoint + currently at STOP_PC (and temporarily remove the breakpoint at + STOP_PC). */ + +void reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at); + +/* Change the status of the breakpoint at WHERE to inserted. */ + +void reinsert_breakpoint (CORE_ADDR where); + +/* Change the status of the breakpoint at WHERE to uninserted. */ + +void uninsert_breakpoint (CORE_ADDR where); + +/* See if any breakpoint claims ownership of STOP_PC. Call the handler for + the breakpoint, if found. */ + +int check_breakpoints (CORE_ADDR stop_pc); + +/* See if any breakpoints shadow the target memory area from MEM_ADDR + to MEM_ADDR + MEM_LEN. Update the data already read from the target + (in BUF) if necessary. */ + +void check_mem_read (CORE_ADDR mem_addr, char *buf, int mem_len); + +/* See if any breakpoints shadow the target memory area from MEM_ADDR + to MEM_ADDR + MEM_LEN. Update the data to be written to the target + (in BUF) if necessary, as well as the original data for any breakpoints. */ + +void check_mem_write (CORE_ADDR mem_addr, char *buf, int mem_len); + +/* Set the byte pattern to insert for memory breakpoints. This function + must be called before any breakpoints are set. */ + +void set_breakpoint_data (const char *bp_data, int bp_len); + +#endif /* MEM_BREAK_H */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/proc-service.c gdb-5.2.1/gdb/gdbserver/proc-service.c --- gdb-5.2.1-orig/gdb/gdbserver/proc-service.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/proc-service.c Tue Sep 2 14:04:41 2003 @@ -0,0 +1,256 @@ +/* libthread_db helper functions for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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" + +/* This file is currently tied to GNU/Linux. It should scale well to + another libthread_db implementation, with the approriate gdbserver + hooks, but for now this means we can use GNU/Linux's target data. */ + +#include "linux-low.h" + +/* Correct for all GNU/Linux targets (for quite some time). */ +#define GDB_GREGSET_T elf_gregset_t +#define GDB_FPREGSET_T elf_fpregset_t + +#ifndef HAVE_ELF_FPREGSET_T +/* Make sure we have said types. Not all platforms bring in + via . */ +#ifdef HAVE_LINUX_ELF_H +#include +#endif +#endif + +#include "../gdb_proc_service.h" + +typedef struct ps_prochandle *gdb_ps_prochandle_t; +typedef void *gdb_ps_read_buf_t; +typedef const void *gdb_ps_write_buf_t; +typedef size_t gdb_ps_size_t; + +/* FIXME redo this right */ +#if 0 +#ifndef HAVE_LINUX_REGSETS +#error HAVE_LINUX_REGSETS required! +#else +static struct regset_info * +gregset_info(void) +{ + int i = 0; + + while (target_regsets[i].size != -1) + { + if (target_regsets[i].type == GENERAL_REGS) + break; + i++; + } + + return &target_regsets[i]; +} + +static struct regset_info * +fpregset_info(void) +{ + int i = 0; + + while (target_regsets[i].size != -1) + { + if (target_regsets[i].type == FP_REGS) + break; + i++; + } + + return &target_regsets[i]; +} +#endif +#endif + +/* Search for the symbol named NAME within the object named OBJ within + the target process PH. If the symbol is found the address of the + symbol is stored in SYM_ADDR. */ + +ps_err_e +ps_pglobal_lookup (gdb_ps_prochandle_t ph, const char *obj, + const char *name, paddr_t *sym_addr) +{ + CORE_ADDR addr; + + if (look_up_one_symbol (name, &addr) == 0) + return PS_NOSYM; + + *sym_addr = (paddr_t) (unsigned long) addr; + return PS_OK; +} + +/* Read SIZE bytes from the target process PH at address ADDR and copy + them into BUF. */ + +ps_err_e +ps_pdread (gdb_ps_prochandle_t ph, paddr_t addr, + gdb_ps_read_buf_t buf, gdb_ps_size_t size) +{ + read_inferior_memory (addr, buf, size); + return PS_OK; +} + +/* Write SIZE bytes from BUF into the target process PH at address ADDR. */ + +ps_err_e +ps_pdwrite (gdb_ps_prochandle_t ph, paddr_t addr, + gdb_ps_write_buf_t buf, gdb_ps_size_t size) +{ + return write_inferior_memory (addr, buf, size); +} + +/* Get the general registers of LWP LWPID within the target process PH + and store them in GREGSET. */ + +ps_err_e +ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset) +{ +#if 0 + struct thread_info *reg_inferior, *save_inferior; + void *regcache; + + reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, + lwpid); + if (reg_inferior == NULL) + return PS_ERR; + + save_inferior = current_inferior; + current_inferior = reg_inferior; + + regcache = new_register_cache (); + the_target->fetch_registers (0, regcache); + gregset_info()->fill_function (gregset, regcache); + free_register_cache (regcache); + + current_inferior = save_inferior; + return PS_OK; +#endif + /* FIXME */ + return PS_ERR; +} + +/* Set the general registers of LWP LWPID within the target process PH + from GREGSET. */ + +ps_err_e +ps_lsetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, const prgregset_t gregset) +{ +#if 0 + struct thread_info *reg_inferior, *save_inferior; + void *regcache; + + reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid); + if (reg_inferior == NULL) + return PS_ERR; + + save_inferior = current_inferior; + current_inferior = reg_inferior; + + regcache = new_register_cache (); + gregset_info()->store_function (gregset, regcache); + the_target->store_registers (0, regcache); + free_register_cache (regcache); + + current_inferior = save_inferior; + + return PS_OK; +#endif + /* FIXME */ + return PS_ERR; +} + +/* Get the floating-point registers of LWP LWPID within the target + process PH and store them in FPREGSET. */ + +ps_err_e +ps_lgetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, + gdb_prfpregset_t *fpregset) +{ +#if 0 + struct thread_info *reg_inferior, *save_inferior; + void *regcache; + + reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid); + if (reg_inferior == NULL) + return PS_ERR; + + save_inferior = current_inferior; + current_inferior = reg_inferior; + + regcache = new_register_cache (); + the_target->fetch_registers (0, regcache); + fpregset_info()->fill_function (fpregset, regcache); + free_register_cache (regcache); + + current_inferior = save_inferior; + + return PS_OK; +#endif + /* FIXME */ + return PS_ERR; +} + +/* Set the floating-point registers of LWP LWPID within the target + process PH from FPREGSET. */ + +ps_err_e +ps_lsetfpregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, + const gdb_prfpregset_t *fpregset) +{ +#if 0 + struct thread_info *reg_inferior, *save_inferior; + void *regcache; + + reg_inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid); + if (reg_inferior == NULL) + return PS_ERR; + + save_inferior = current_inferior; + current_inferior = reg_inferior; + + regcache = new_register_cache (); + fpregset_info()->store_function (fpregset, regcache); + the_target->store_registers (0, regcache); + free_register_cache (regcache); + + current_inferior = save_inferior; + + return PS_OK; +#endif + /* FIXME */ + return PS_ERR; +} + +/* Return overall process id of the target PH. Special for GNU/Linux + -- not used on Solaris. */ + +pid_t +ps_getpid (gdb_ps_prochandle_t ph) +{ + return ph->pid; +} + + diff -purN gdb-5.2.1-orig/gdb/gdbserver/regcache.c gdb-5.2.1/gdb/gdbserver/regcache.c --- gdb-5.2.1-orig/gdb/gdbserver/regcache.c Wed Feb 27 08:07:39 2002 +++ gdb-5.2.1/gdb/gdbserver/regcache.c Tue Sep 2 14:04:41 2003 @@ -25,7 +25,12 @@ #include #include -static char *registers; +struct inferior_regcache_data +{ + int registers_valid; + char *registers; +}; + static int register_bytes; static struct reg *reg_defs; @@ -33,12 +38,84 @@ static int num_registers; const char **gdbserver_expedite_regs; +static struct inferior_regcache_data * +get_regcache (struct thread_info *inf, int fetch) +{ + struct inferior_regcache_data *regcache; + + regcache = (struct inferior_regcache_data *) inferior_regcache_data (inf); + + if (regcache == NULL) + fatal ("no register cache"); + + /* FIXME - fetch registers for INF */ + if (fetch && regcache->registers_valid == 0) + { + fetch_inferior_registers (0); + regcache->registers_valid = 1; + } + + return regcache; +} + +void +regcache_invalidate_one (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + struct inferior_regcache_data *regcache; + + regcache = (struct inferior_regcache_data *) inferior_regcache_data (thread); + + if (regcache->registers_valid) + { + struct thread_info *saved_inferior = current_inferior; + + current_inferior = thread; + store_inferior_registers (-1); + current_inferior = saved_inferior; + } + + regcache->registers_valid = 0; +} + +void +regcache_invalidate () +{ + for_each_inferior (&all_threads, regcache_invalidate_one); +} + int registers_length (void) { return 2 * register_bytes; } +void * +new_register_cache (void) +{ + struct inferior_regcache_data *regcache; + + regcache = malloc (sizeof (*regcache)); + + regcache->registers = malloc (register_bytes); + if (regcache->registers == NULL) + fatal ("Could not allocate register cache."); + + regcache->registers_valid = 0; + + return regcache; +} + +void +free_register_cache (void *regcache_p) +{ + struct inferior_regcache_data *regcache + = (struct inferior_regcache_data *) regcache_p; + + free (regcache->registers); + free (regcache); +} + void set_register_cache (struct reg *regs, int n) { @@ -55,14 +132,13 @@ set_register_cache (struct reg *regs, in } register_bytes = offset / 8; - registers = malloc (offset / 8); - if (!registers) - fatal ("Could not allocate register cache."); } void registers_to_string (char *buf) { + char *registers = get_regcache (current_inferior, 1)->registers; + convert_int_to_ascii (registers, buf, register_bytes); } @@ -70,6 +146,7 @@ void registers_from_string (char *buf) { int len = strlen (buf); + char *registers = get_regcache (current_inferior, 1)->registers; if (len != register_bytes * 2) { @@ -116,32 +193,40 @@ register_size (int n) return reg_defs[n].size / 8; } -char * -register_data (int n) +static char * +register_data (int n, int fetch) { + char *registers = get_regcache (current_inferior, fetch)->registers; + return registers + (reg_defs[n].offset / 8); } void -supply_register (int n, const char *buf) +supply_register (int n, const void *buf) { - memcpy (register_data (n), buf, register_size (n)); + memcpy (register_data (n, 0), buf, register_size (n)); } void -supply_register_by_name (const char *name, const char *buf) +supply_register_by_name (const char *name, const void *buf) { supply_register (find_regno (name), buf); } void -collect_register (int n, char *buf) +collect_register (int n, void *buf) +{ + memcpy (buf, register_data (n, 1), register_size (n)); +} + +void +collect_register_as_string (int n, char *buf) { - memcpy (buf, register_data (n), register_size (n)); + convert_int_to_ascii (register_data (n, 1), buf, register_size (n)); } void -collect_register_by_name (const char *name, char *buf) +collect_register_by_name (const char *name, void *buf) { collect_register (find_regno (name), buf); } diff -purN gdb-5.2.1-orig/gdb/gdbserver/regcache.h gdb-5.2.1/gdb/gdbserver/regcache.h --- gdb-5.2.1-orig/gdb/gdbserver/regcache.h Thu Feb 14 07:21:22 2002 +++ gdb-5.2.1/gdb/gdbserver/regcache.h Tue Sep 2 14:04:41 2003 @@ -21,6 +21,21 @@ #ifndef REGCACHE_H #define REGCACHE_H +struct inferior_list_entry; + +/* Create a new register cache for INFERIOR. */ + +void *new_register_cache (void); + +/* Release all memory associated with the register cache for INFERIOR. */ + +void free_register_cache (void *regcache); + +/* Invalidate cached registers for one or all threads. */ + +void regcache_invalidate_one (struct inferior_list_entry *); +void regcache_invalidate (void); + /* Convert all registers to a string in the currently specified remote format. */ @@ -38,12 +53,20 @@ int registers_length (void); struct reg *find_register_by_number (int n); -char *register_data (int n); - int register_size (int n); int find_regno (const char *name); extern const char **gdbserver_expedite_regs; +void supply_register (int n, const void *buf); + +void supply_register_by_name (const char *name, const void *buf); + +void collect_register (int n, void *buf); + +void collect_register_as_string (int n, char *buf); + +void collect_register_by_name (const char *name, void *buf); + #endif /* REGCACHE_H */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/regdef.h gdb-5.2.1/gdb/gdbserver/regdef.h --- gdb-5.2.1-orig/gdb/gdbserver/regdef.h Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/regdef.h Tue Sep 2 14:04:41 2003 @@ -0,0 +1,46 @@ +/* Register protocol definition structures for the GNU Debugger + Copyright 2001, 2002 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. */ + +#ifndef REGDEF_H +#define REGDEF_H + +struct reg +{ + /* The name of this register - NULL for pad entries. */ + const char *name; + + /* At the moment, both of the following bit counts must be divisible + by eight (to match the representation as two hex digits) and divisible + by the size of a byte (to match the layout of each register in + memory). */ + + /* The offset (in bits) of the value of this register in the buffer. */ + int offset; + + /* The size (in bits) of the value of this register, as transmitted. */ + int size; +}; + +/* Set the current remote protocol and register cache according to the array + ``regs'', with ``n'' elements. */ + +void set_register_cache (struct reg *regs, int n); + +#endif /* REGDEF_H */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/remote-utils.c gdb-5.2.1/gdb/gdbserver/remote-utils.c --- gdb-5.2.1-orig/gdb/gdbserver/remote-utils.c Fri Mar 29 20:03:34 2002 +++ gdb-5.2.1/gdb/gdbserver/remote-utils.c Tue Sep 2 14:04:41 2003 @@ -35,11 +35,19 @@ #include #include #include +#include int remote_debug = 0; struct ui_file *gdb_stdlog; +#ifdef HAVE_TARGET_ATTACH +int remote_desc; +#else static int remote_desc; +#endif +/* FIXME headerize? */ +extern int using_threads; +extern int debug_threads; /* Open a connection to a remote debugger. NAME is the filename used for communication. */ @@ -185,6 +193,42 @@ fromhex (int a) return 0; } +int +unhexify (char *bin, const char *hex, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + if (hex[0] == 0 || hex[1] == 0) + { + /* Hex string is short, or of uneven length. + Return the count that has been converted so far. */ + return i; + } + *bin++ = fromhex (hex[0]) * 16 + fromhex (hex[1]); + hex += 2; + } + return i; +} + +static void +decode_address (CORE_ADDR *addrp, const char *start, int len) +{ + CORE_ADDR addr; + char ch; + int i; + + addr = 0; + for (i = 0; i < len; i++) + { + ch = start[i]; + addr = addr << 4; + addr = addr | (fromhex (ch) & 0x0f); + } + *addrp = addr; +} + /* Convert number NIB to a hex digit. */ static int @@ -196,6 +240,24 @@ tohex (int nib) return 'a' + nib - 10; } +int +hexify (char *hex, const char *bin, int count) +{ + int i; + + /* May use a length, or a nul-terminated string as input. */ + if (count == 0) + count = strlen (bin); + + for (i = 0; i < count; i++) + { + *hex++ = tohex ((*bin >> 4) & 0xf); + *hex++ = tohex (*bin++ & 0xf); + } + *hex = 0; + return i; +} + /* Send a packet to the remote machine, with error checking. The data of the packet is in BUF. Returns >= 0 on success, -1 otherwise. */ @@ -241,10 +303,18 @@ putpkt (char *buf) } if (remote_debug) - printf ("putpkt (\"%s\"); [looking for ack]\n", buf2); + { + fprintf (stderr, "putpkt (\"%s\"); [looking for ack]\n", buf2); + fflush (stderr); + } + cc = read (remote_desc, buf3, 1); if (remote_debug) - printf ("[received '%c' (0x%x)]\n", buf3[0], buf3[0]); + { + fprintf (stderr, "[received '%c' (0x%x)]\n", buf3[0], buf3[0]); + fflush (stderr); + } + if (cc <= 0) { if (cc == 0) @@ -255,6 +325,11 @@ putpkt (char *buf) free (buf2); return -1; } + + /* Check for an input interrupt while we're here. */ + if (buf3[0] == '\003') + kill ((*the_target->signal_pid) (), SIGINT); + } while (buf3[0] != '+'); @@ -291,7 +366,7 @@ input_interrupt (int unused) return; } - kill (inferior_pid, SIGINT); + kill ((*the_target->signal_pid) (), SIGINT); } } @@ -356,7 +431,11 @@ getpkt (char *buf) if (c == '$') break; if (remote_debug) - printf ("[getpkt: discarding char '%c']\n", c); + { + fprintf (stderr, "[getpkt: discarding char '%c']\n", c); + fflush (stderr); + } + if (c < 0) return -1; } @@ -386,12 +465,19 @@ getpkt (char *buf) } if (remote_debug) - printf ("getpkt (\"%s\"); [sending ack] \n", buf); + { + fprintf (stderr, "getpkt (\"%s\"); [sending ack] \n", buf); + fflush (stderr); + } write (remote_desc, "+", 1); if (remote_debug) - printf ("[sent ack]\n"); + { + fprintf (stderr, "[sent ack]\n"); + fflush (stderr); + } + return bp - buf; } @@ -444,8 +530,6 @@ convert_ascii_to_int (char *from, char * static char * outreg (int regno, char *buf) { - int regsize = register_size (regno); - if ((regno >> 12) != 0) *buf++ = tohex ((regno >> 12) & 0xf); if ((regno >> 8) != 0) @@ -453,14 +537,39 @@ outreg (int regno, char *buf) *buf++ = tohex ((regno >> 4) & 0xf); *buf++ = tohex (regno & 0xf); *buf++ = ':'; - convert_int_to_ascii (register_data (regno), buf, regsize); - buf += 2 * regsize; + collect_register_as_string (regno, buf); + buf += 2 * register_size (regno); *buf++ = ';'; return buf; } void +new_thread_notify (int id) +{ + char own_buf[256]; + + if (server_waiting == 0) + return; + + sprintf (own_buf, "n%x", id); + disable_async_io (); + putpkt (own_buf); + enable_async_io (); +} + +void +dead_thread_notify (int id) +{ + char own_buf[256]; + + sprintf (own_buf, "x%x", id); + disable_async_io (); + putpkt (own_buf); + enable_async_io (); +} + +void prepare_resume_reply (char *buf, char status, unsigned char signo) { int nib, sig; @@ -483,12 +592,23 @@ prepare_resume_reply (char *buf, char st regp ++; } - /* 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) + /* Formerly, if the debugger had not used any thread features we would not + burden it with a thread status response. This was for the benefit of + GDB 4.13 and older. However, in recent GDB versions the check + (``if (cont_thread != 0)'') does not have the desired effect because of + sillyness in the way that the remote protocol handles specifying a thread. + Since thread support relies on qSymbol support anyway, assume GDB can handle + threads. */ + + if (using_threads) { + /* FIXME right place to set this? */ + thread_from_wait = ((struct inferior_list_entry *)current_inferior)->id; + if (debug_threads) + fprintf (stderr, "Writing resume reply for %d\n\n", thread_from_wait); if (old_thread_from_wait != thread_from_wait) { + general_thread = thread_from_wait; sprintf (buf, "thread:%x;", thread_from_wait); buf += strlen (buf); old_thread_from_wait = thread_from_wait; @@ -543,3 +663,46 @@ decode_M_packet (char *from, CORE_ADDR * convert_ascii_to_int (&from[i++], to, *len_ptr); } + +int +look_up_one_symbol (const char *name, CORE_ADDR *addrp) +{ + char own_buf[266], *p, *q; + int len; + + /* Send the request. */ + strcpy (own_buf, "qSymbol:"); + hexify (own_buf + strlen ("qSymbol:"), name, strlen (name)); + if (putpkt (own_buf) < 0) + return -1; + + /* FIXME: Eventually add buffer overflow checking (to getpkt?) */ + len = getpkt (own_buf); + if (len < 0) + return -1; + + if (strncmp (own_buf, "qSymbol:", strlen ("qSymbol:")) != 0) + { + /* Malformed response. */ + if (remote_debug) + { + fprintf (stderr, "Malformed response to qSymbol, ignoring.\n"); + fflush (stderr); + } + + return -1; + } + + p = own_buf + strlen ("qSymbol:"); + q = p; + while (*q && *q != ':') + q++; + + /* Make sure we found a value for the symbol. */ + if (p == q || *q == '\0') + return 0; + + decode_address (addrp, p, q - p); + return 1; +} + diff -purN gdb-5.2.1-orig/gdb/gdbserver/server.c gdb-5.2.1/gdb/gdbserver/server.c --- gdb-5.2.1-orig/gdb/gdbserver/server.c Fri Mar 29 20:03:34 2002 +++ gdb-5.2.1/gdb/gdbserver/server.c Tue Sep 2 14:04:41 2003 @@ -23,20 +23,27 @@ int cont_thread; int general_thread; +int step_thread; int thread_from_wait; int old_thread_from_wait; int extended_protocol; +int server_waiting; + +static int active = 0; jmp_buf toplevel; -int inferior_pid; static unsigned char start_inferior (char *argv[], char *statusptr) { - inferior_pid = create_inferior (argv[0], argv); - fprintf (stderr, "Process %s created; pid = %d\n", argv[0], inferior_pid); + /* FIXME Check error? Or turn to void. */ + create_inferior (argv[0], argv); + + fprintf (stderr, "Process %s created; pid = %d\n", argv[0], + all_threads.head->id); + active = 1; /* Wait till we are at 1st instruction in program, return signal number. */ - return mywait (statusptr); + return mywait (statusptr, 0); } static int @@ -47,26 +54,105 @@ attach_inferior (int pid, char *statuspt if (myattach (pid) != 0) return -1; - inferior_pid = pid; - - *sigptr = mywait (statusptr); - + *sigptr = mywait (statusptr, 0); +#ifdef HAVE_TARGET_ATTACH + active = 1; +#endif return 0; } +#ifdef HAVE_TARGET_ATTACH +static int +detach_inferior (void) +{ + mydetach (1); + active = 0; + return 0; +} +#endif extern int remote_debug; +/* Handle all of the extended 'q' packets. */ +void +handle_query (char *own_buf) +{ + static struct inferior_list_entry *thread_ptr; + + if (strcmp ("qSymbol::", own_buf) == 0) + { + if (the_target->look_up_symbols != NULL) + (*the_target->look_up_symbols) (); + + strcpy (own_buf, "OK"); + return; + } + + if (strcmp ("qfThreadInfo", own_buf) == 0) + { + thread_ptr = all_threads.head; + sprintf (own_buf, "m%x", thread_ptr->id); + thread_ptr = thread_ptr->next; + return; + } + + if (strcmp ("qsThreadInfo", own_buf) == 0) + { + if (thread_ptr != NULL) + { + sprintf (own_buf, "m%x", thread_ptr->id); + thread_ptr = thread_ptr->next; + return; + } + else + { + sprintf (own_buf, "l"); + return; + } + } + + /* Otherwise we didn't know what packet it was. Say we didn't + understand it. */ + own_buf[0] = 0; +} + +static int attached; + +#ifdef HAVE_TARGET_ATTACH +static void +gdbserver_usage (void) +{ + error ("Usage:\tgdbserver [-d] COMM PROG [ARGS ...]\n" + "\tgdbserver [-d] COMM --attach PID\n" + "\tgdbserver [-d] COMM\n" + "\n" + "COMM may either be a tty device (for serial debugging), or \n" + "HOST:PORT to listen for a TCP connection.\n"); +} +#else +static void +gdbserver_usage (void) +{ + error ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" + "\tgdbserver COMM --attach PID\n" + "\n" + "COMM may either be a tty device (for serial debugging), or \n" + "HOST:PORT to listen for a TCP connection.\n"); +} +#endif + int main (int argc, char *argv[]) { char ch, status, *own_buf, mem_buf[2000]; int i = 0; +#ifdef HAVE_TARGET_ATTACH + int argmax = 3; +#endif unsigned char signal; unsigned int len; CORE_ADDR mem_addr; - int bad_attach = 0; - int pid = 0; - int attached = 0; + int bad_attach; + int pid; char *arg_end; if (setjmp (toplevel)) @@ -75,10 +161,71 @@ main (int argc, char *argv[]) exit (1); } + bad_attach = 0; + pid = 0; + attached = 0; +#ifdef HAVE_TARGET_ATTACH + if (argc >= 2) { + if (*argv[1] == '-') { + if (argc == 2) + gdbserver_usage(); + if (strcmp (argv[1], "-d") == 0) { + remote_debug = 1; + argmax++; + } else + gdbserver_usage(); + } + } else + gdbserver_usage(); + + if (argc >= argmax && strcmp (argv[argmax-1], "--attach") == 0) + { + if (argc == (argmax+1) + && argv[argmax] != '\0' + && (pid = strtoul (argv[argmax], &arg_end, 10)) != 0 + && *arg_end == '\0') + { + ; + } + else + gdbserver_usage(); + } + + initialize_low (); + own_buf = malloc (PBUFSIZ); + + if (argc < argmax) { + status = 'I'; /* idle */ + signal = 0; + fprintf (stderr, "Ready to attach to a process\n"); + goto loop; + } + + if (pid == 0) + { + /* Wait till we are at first instruction in program. */ + if (argc >= argmax && argv[argmax-1] != '\0') + signal = start_inferior (&argv[argmax-1], &status); + /* We are now stopped at the first instruction of the target process */ + } + else + { + switch (attach_inferior (pid, &status, &signal)) + { + case -1: + error ("Attaching not supported on this target"); + break; + default: + attached = 1; + break; + } + } + loop: +#else if (argc >= 3 && strcmp (argv[2], "--attach") == 0) { if (argc == 4 - && argv[3] != '\0' + && argv[3] != '\0' && (pid = strtoul (argv[3], &arg_end, 10)) != 0 && *arg_end == '\0') { @@ -89,8 +236,7 @@ main (int argc, char *argv[]) } if (argc < 3 || bad_attach) - error ("Usage:\tgdbserver tty prog [args ...]\n" - "\tgdbserver tty --attach pid"); + gdbserver_usage(); initialize_low (); @@ -115,10 +261,15 @@ main (int argc, char *argv[]) break; } } +#endif while (1) { +#ifdef HAVE_TARGET_ATTACH + remote_open (argv[argmax-2]); +#else remote_open (argv[1]); +#endif restart: setjmp (toplevel); @@ -129,6 +280,9 @@ main (int argc, char *argv[]) ch = own_buf[i++]; switch (ch) { + case 'q': + handle_query (own_buf); + break; case 'd': remote_debug = !remote_debug; break; @@ -147,20 +301,33 @@ main (int argc, char *argv[]) } break; case '?': +#ifdef HAVE_TARGET_ATTACH + if (!active) signal = 0; +#endif prepare_resume_reply (own_buf, status, signal); break; case 'H': switch (own_buf[1]) { case 'g': +#ifdef HAVE_TARGET_ATTACH + if (!active) { + write_enn (own_buf); + break; + } +#endif general_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); - fetch_inferior_registers (0); + set_desired_inferior (1); break; case 'c': cont_thread = strtol (&own_buf[2], NULL, 16); write_ok (own_buf); break; + case 's': + step_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. */ @@ -169,19 +336,32 @@ main (int argc, char *argv[]) } break; case 'g': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif + set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif + set_desired_inferior (1); registers_from_string (&own_buf[1]); - store_inferior_registers (-1); write_ok (own_buf); break; case 'm': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif 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': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif 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); @@ -189,33 +369,49 @@ main (int argc, char *argv[]) write_enn (own_buf); break; case 'C': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; + set_desired_inferior (0); myresume (0, signal); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'S': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); else signal = 0; + set_desired_inferior (0); myresume (1, signal); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'c': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif + set_desired_inferior (0); myresume (0, 0); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 's': +#ifdef HAVE_TARGET_ATTACH + if (!active) goto silent; /* no registers yet */ +#endif + set_desired_inferior (0); myresume (1, 0); - signal = mywait (&status); + signal = mywait (&status, 1); prepare_resume_reply (own_buf, status, signal); break; case 'k': @@ -230,7 +426,11 @@ main (int argc, char *argv[]) fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ +#ifdef HAVE_TARGET_ATTACH + signal = start_inferior (&argv[argmax-1], &status); +#else signal = start_inferior (&argv[2], &status); +#endif goto restart; break; } @@ -255,7 +455,11 @@ main (int argc, char *argv[]) fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ +#ifdef HAVE_TARGET_ATTACH + signal = start_inferior (&argv[argmax-1], &status); +#else signal = start_inferior (&argv[2], &status); +#endif goto restart; break; } @@ -267,10 +471,51 @@ main (int argc, char *argv[]) own_buf[0] = '\0'; break; } +#ifdef HAVE_TARGET_ATTACH + case 'U': /* attach a process */ + if (sscanf(&own_buf[i], "%x", &pid) == 1 && + (attach_inferior(pid, &status, &signal) == 0)) { + attached = 1; + set_desired_inferior (0); + prepare_resume_reply (own_buf, status, signal); + } + else + write_enn (own_buf); + break; + + case 'Z': /* Z-packet support - No*/ + own_buf[0]=0; + break; + + case 'X': /* binary download support */ + own_buf[0]=0; + break; + + case 'D': /* detach a process */ + if (detach_inferior() == -1) + write_enn (own_buf); + else { + status = 'I'; + write_ok (own_buf); + } + break; + + case 'K': /* close communication */ + goto comm_closed; + +#endif +#ifdef HAVE_TARGET_SHELL + case 'E': /* exec remote command */ + exec_shellcmd(&own_buf[1]); + continue; /* No replying packet */ +#endif 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. */ +#ifdef HAVE_TARGET_ATTACH + silent: +#endif own_buf[0] = '\0'; break; } @@ -292,11 +537,18 @@ main (int argc, char *argv[]) fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ +#ifdef HAVE_TARGET_ATTACH + signal = start_inferior (&argv[argmax-1], &status); +#else signal = start_inferior (&argv[2], &status); +#endif goto restart; break; } else +#ifdef HAVE_TARGET_ATTACH + if (!attached) +#endif { fprintf (stderr, "GDBserver exiting\n"); exit (0); @@ -311,6 +563,9 @@ main (int argc, char *argv[]) For the traditional remote protocol close the connection, and re-open it at the top of the loop. */ +#ifdef HAVE_TARGET_ATTACH +comm_closed: +#endif if (extended_protocol) { remote_close (); @@ -321,6 +576,9 @@ main (int argc, char *argv[]) fprintf (stderr, "Remote side has terminated connection. " "GDBserver will reopen the connection.\n"); remote_close (); +#ifdef HAVE_TARGET_ATTACH + status = 'I'; +#endif } } } diff -purN gdb-5.2.1-orig/gdb/gdbserver/server.h gdb-5.2.1/gdb/gdbserver/server.h --- gdb-5.2.1-orig/gdb/gdbserver/server.h Fri Mar 29 20:03:34 2002 +++ gdb-5.2.1/gdb/gdbserver/server.h Tue Sep 2 14:04:41 2003 @@ -23,48 +23,101 @@ #define SERVER_H #include "config.h" + #include #include #include #include +#include +#include /* For isspace */ +#ifdef HAVE_STRING_H +#include +#endif + +#ifndef ATTR_NORETURN +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)) +#define ATTR_NORETURN __attribute__ ((noreturn)) +#else +#define ATTR_NORETURN /* nothing */ +#endif +#endif + +#ifndef ATTR_FORMAT +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 4)) +#define ATTR_FORMAT(type, x, y) __attribute__ ((format(type, x, y))) +#else +#define ATTR_FORMAT(type, x, y) /* nothing */ +#endif +#endif -/* FIXME: Both of these should be autoconf'd for. */ -#define NORETURN +/* FIXME: This should probably be autoconf'd for. It's an integer type at + least the size of a (void *). */ typedef long long CORE_ADDR; +/* Generic information for tracking a list of ``inferiors'' - threads, + processes, etc. */ +struct inferior_list +{ + struct inferior_list_entry *head; + struct inferior_list_entry *tail; +}; +struct inferior_list_entry +{ + int id; + struct inferior_list_entry *next; +}; + +/* Opaque type for user-visible threads. */ +struct thread_info; + #include "regcache.h" -#include "gdb/signals.h" +#include "signals.h" -#include +#include "target.h" +#include "mem-break.h" /* Target-specific functions */ -int create_inferior (char *program, char **allargs); -void kill_inferior (void); -void fetch_inferior_registers (int regno); -void store_inferior_registers (int regno); -int mythread_alive (int pid); -void myresume (int step, int signo); -unsigned char mywait (char *status); -void read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len); -int write_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len); -int create_inferior (); void initialize_low (); -/* Target-specific variables */ +/* From inferiors.c. */ -extern char *registers; +extern struct inferior_list all_threads; +void add_inferior_to_list (struct inferior_list *list, + struct inferior_list_entry *new_inferior); +void for_each_inferior (struct inferior_list *list, + void (*action) (struct inferior_list_entry *)); +extern struct thread_info *current_inferior; +void remove_inferior (struct inferior_list *list, + struct inferior_list_entry *entry); +void remove_thread (struct thread_info *thread); +void add_thread (int thread_id, void *target_data); +void clear_inferiors (void); +struct inferior_list_entry *find_inferior + (struct inferior_list *, + int (*func) (struct inferior_list_entry *, + void *), + void *arg); +struct inferior_list_entry *find_inferior_id (struct inferior_list *list, + int id); +void *inferior_target_data (struct thread_info *); +void set_inferior_target_data (struct thread_info *, void *); +void *inferior_regcache_data (struct thread_info *); +void set_inferior_regcache_data (struct thread_info *, void *); +void change_inferior_id (struct inferior_list *list, + int new_id); /* Public variables in server.c */ extern int cont_thread; extern int general_thread; +extern int step_thread; extern int thread_from_wait; extern int old_thread_from_wait; +extern int server_waiting; extern jmp_buf toplevel; -extern int inferior_pid; /* Functions from remote-utils.c */ @@ -78,6 +131,8 @@ void enable_async_io (void); void disable_async_io (void); void convert_ascii_to_int (char *from, char *to, int n); void convert_int_to_ascii (char *from, char *to, int n); +void new_thread_notify (int id); +void dead_thread_notify (int id); void prepare_resume_reply (char *buf, char status, unsigned char sig); void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr, @@ -85,6 +140,11 @@ void decode_m_packet (char *from, CORE_A void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr, unsigned int *len_ptr, char *to); +int unhexify (char *bin, const char *hex, int count); +int hexify (char *hex, const char *bin, int count); + +int look_up_one_symbol (const char *name, CORE_ADDR *addrp); + /* Functions from ``signals.c''. */ enum target_signal target_signal_from_host (int hostsig); int target_signal_to_host_p (enum target_signal oursig); @@ -93,11 +153,18 @@ int target_signal_to_host (enum target_s /* Functions from utils.c */ void perror_with_name (char *string); -void error (const char *string,...); -void fatal (const char *string,...); +void error (const char *string,...) ATTR_NORETURN; +void fatal (const char *string,...) ATTR_NORETURN; void warning (const char *string,...); +/* Functions from the register cache definition. */ + +void init_registers (void); +#ifdef HAVE_TARGET_SHELL +/* Function from linux-low.c */ +void exec_shellcmd(char *); +#endif /* Maximum number of bytes to read/write at once. The value here is chosen to fill up a packet (the headers account for the 32). */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/signals.h gdb-5.2.1/gdb/gdbserver/signals.h --- gdb-5.2.1-orig/gdb/gdbserver/signals.h Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/signals.h Tue Sep 2 14:04:41 2003 @@ -0,0 +1,237 @@ +/* Target signal numbers for GDB and the GDB remote protocol. + Copyright 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1997, 1998, 1999, 2000, 2001, 2002 + 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. */ + +#ifndef GDB_SIGNALS_H +#define GDB_SIGNALS_H + +/* The numbering of these signals is chosen to match traditional unix + signals (insofar as various unices use the same numbers, anyway). + It is also the numbering of the GDB remote protocol. Other remote + protocols, if they use a different numbering, should make sure to + translate appropriately. + + Since these numbers have actually made it out into other software + (stubs, etc.), you mustn't disturb the assigned numbering. If you + need to add new signals here, add them to the end of the explicitly + numbered signals, at the comment marker. Add them unconditionally, + not within any #if or #ifdef. + + This is based strongly on Unix/POSIX signals for several reasons: + (1) This set of signals represents a widely-accepted attempt to + represent events of this sort in a portable fashion, (2) we want a + signal to make it from wait to child_wait to the user intact, (3) many + remote protocols use a similar encoding. However, it is + recognized that this set of signals has limitations (such as not + distinguishing between various kinds of SIGSEGV, or not + distinguishing hitting a breakpoint from finishing a single step). + So in the future we may get around this either by adding additional + signals for breakpoint, single-step, etc., or by adding signal + codes; the latter seems more in the spirit of what BSD, System V, + etc. are doing to address these issues. */ + +/* For an explanation of what each signal means, see + target_signal_to_string. */ + +enum target_signal + { + /* Used some places (e.g. stop_signal) to record the concept that + there is no signal. */ + TARGET_SIGNAL_0 = 0, + TARGET_SIGNAL_FIRST = 0, + TARGET_SIGNAL_HUP = 1, + TARGET_SIGNAL_INT = 2, + TARGET_SIGNAL_QUIT = 3, + TARGET_SIGNAL_ILL = 4, + TARGET_SIGNAL_TRAP = 5, + TARGET_SIGNAL_ABRT = 6, + TARGET_SIGNAL_EMT = 7, + TARGET_SIGNAL_FPE = 8, + TARGET_SIGNAL_KILL = 9, + TARGET_SIGNAL_BUS = 10, + TARGET_SIGNAL_SEGV = 11, + TARGET_SIGNAL_SYS = 12, + TARGET_SIGNAL_PIPE = 13, + TARGET_SIGNAL_ALRM = 14, + TARGET_SIGNAL_TERM = 15, + TARGET_SIGNAL_URG = 16, + TARGET_SIGNAL_STOP = 17, + TARGET_SIGNAL_TSTP = 18, + TARGET_SIGNAL_CONT = 19, + TARGET_SIGNAL_CHLD = 20, + TARGET_SIGNAL_TTIN = 21, + TARGET_SIGNAL_TTOU = 22, + TARGET_SIGNAL_IO = 23, + TARGET_SIGNAL_XCPU = 24, + TARGET_SIGNAL_XFSZ = 25, + TARGET_SIGNAL_VTALRM = 26, + TARGET_SIGNAL_PROF = 27, + TARGET_SIGNAL_WINCH = 28, + TARGET_SIGNAL_LOST = 29, + TARGET_SIGNAL_USR1 = 30, + TARGET_SIGNAL_USR2 = 31, + TARGET_SIGNAL_PWR = 32, + /* Similar to SIGIO. Perhaps they should have the same number. */ + TARGET_SIGNAL_POLL = 33, + TARGET_SIGNAL_WIND = 34, + TARGET_SIGNAL_PHONE = 35, + TARGET_SIGNAL_WAITING = 36, + TARGET_SIGNAL_LWP = 37, + TARGET_SIGNAL_DANGER = 38, + TARGET_SIGNAL_GRANT = 39, + TARGET_SIGNAL_RETRACT = 40, + TARGET_SIGNAL_MSG = 41, + TARGET_SIGNAL_SOUND = 42, + TARGET_SIGNAL_SAK = 43, + TARGET_SIGNAL_PRIO = 44, + TARGET_SIGNAL_REALTIME_33 = 45, + TARGET_SIGNAL_REALTIME_34 = 46, + TARGET_SIGNAL_REALTIME_35 = 47, + TARGET_SIGNAL_REALTIME_36 = 48, + TARGET_SIGNAL_REALTIME_37 = 49, + TARGET_SIGNAL_REALTIME_38 = 50, + TARGET_SIGNAL_REALTIME_39 = 51, + TARGET_SIGNAL_REALTIME_40 = 52, + TARGET_SIGNAL_REALTIME_41 = 53, + TARGET_SIGNAL_REALTIME_42 = 54, + TARGET_SIGNAL_REALTIME_43 = 55, + TARGET_SIGNAL_REALTIME_44 = 56, + TARGET_SIGNAL_REALTIME_45 = 57, + TARGET_SIGNAL_REALTIME_46 = 58, + TARGET_SIGNAL_REALTIME_47 = 59, + TARGET_SIGNAL_REALTIME_48 = 60, + TARGET_SIGNAL_REALTIME_49 = 61, + TARGET_SIGNAL_REALTIME_50 = 62, + TARGET_SIGNAL_REALTIME_51 = 63, + TARGET_SIGNAL_REALTIME_52 = 64, + TARGET_SIGNAL_REALTIME_53 = 65, + TARGET_SIGNAL_REALTIME_54 = 66, + TARGET_SIGNAL_REALTIME_55 = 67, + TARGET_SIGNAL_REALTIME_56 = 68, + TARGET_SIGNAL_REALTIME_57 = 69, + TARGET_SIGNAL_REALTIME_58 = 70, + TARGET_SIGNAL_REALTIME_59 = 71, + TARGET_SIGNAL_REALTIME_60 = 72, + TARGET_SIGNAL_REALTIME_61 = 73, + TARGET_SIGNAL_REALTIME_62 = 74, + TARGET_SIGNAL_REALTIME_63 = 75, + + /* Used internally by Solaris threads. See signal(5) on Solaris. */ + TARGET_SIGNAL_CANCEL = 76, + + /* Yes, this pains me, too. But LynxOS didn't have SIG32, and now + GNU/Linux does, and we can't disturb the numbering, since it's + part of the remote protocol. Note that in some GDB's + TARGET_SIGNAL_REALTIME_32 is number 76. */ + TARGET_SIGNAL_REALTIME_32, + /* Yet another pain, IRIX 6 has SIG64. */ + TARGET_SIGNAL_REALTIME_64, + /* Yet another pain, GNU/Linux MIPS might go up to 128. */ + TARGET_SIGNAL_REALTIME_65, + TARGET_SIGNAL_REALTIME_66, + TARGET_SIGNAL_REALTIME_67, + TARGET_SIGNAL_REALTIME_68, + TARGET_SIGNAL_REALTIME_69, + TARGET_SIGNAL_REALTIME_70, + TARGET_SIGNAL_REALTIME_71, + TARGET_SIGNAL_REALTIME_72, + TARGET_SIGNAL_REALTIME_73, + TARGET_SIGNAL_REALTIME_74, + TARGET_SIGNAL_REALTIME_75, + TARGET_SIGNAL_REALTIME_76, + TARGET_SIGNAL_REALTIME_77, + TARGET_SIGNAL_REALTIME_78, + TARGET_SIGNAL_REALTIME_79, + TARGET_SIGNAL_REALTIME_80, + TARGET_SIGNAL_REALTIME_81, + TARGET_SIGNAL_REALTIME_82, + TARGET_SIGNAL_REALTIME_83, + TARGET_SIGNAL_REALTIME_84, + TARGET_SIGNAL_REALTIME_85, + TARGET_SIGNAL_REALTIME_86, + TARGET_SIGNAL_REALTIME_87, + TARGET_SIGNAL_REALTIME_88, + TARGET_SIGNAL_REALTIME_89, + TARGET_SIGNAL_REALTIME_90, + TARGET_SIGNAL_REALTIME_91, + TARGET_SIGNAL_REALTIME_92, + TARGET_SIGNAL_REALTIME_93, + TARGET_SIGNAL_REALTIME_94, + TARGET_SIGNAL_REALTIME_95, + TARGET_SIGNAL_REALTIME_96, + TARGET_SIGNAL_REALTIME_97, + TARGET_SIGNAL_REALTIME_98, + TARGET_SIGNAL_REALTIME_99, + TARGET_SIGNAL_REALTIME_100, + TARGET_SIGNAL_REALTIME_101, + TARGET_SIGNAL_REALTIME_102, + TARGET_SIGNAL_REALTIME_103, + TARGET_SIGNAL_REALTIME_104, + TARGET_SIGNAL_REALTIME_105, + TARGET_SIGNAL_REALTIME_106, + TARGET_SIGNAL_REALTIME_107, + TARGET_SIGNAL_REALTIME_108, + TARGET_SIGNAL_REALTIME_109, + TARGET_SIGNAL_REALTIME_110, + TARGET_SIGNAL_REALTIME_111, + TARGET_SIGNAL_REALTIME_112, + TARGET_SIGNAL_REALTIME_113, + TARGET_SIGNAL_REALTIME_114, + TARGET_SIGNAL_REALTIME_115, + TARGET_SIGNAL_REALTIME_116, + TARGET_SIGNAL_REALTIME_117, + TARGET_SIGNAL_REALTIME_118, + TARGET_SIGNAL_REALTIME_119, + TARGET_SIGNAL_REALTIME_120, + TARGET_SIGNAL_REALTIME_121, + TARGET_SIGNAL_REALTIME_122, + TARGET_SIGNAL_REALTIME_123, + TARGET_SIGNAL_REALTIME_124, + TARGET_SIGNAL_REALTIME_125, + TARGET_SIGNAL_REALTIME_126, + TARGET_SIGNAL_REALTIME_127, + + TARGET_SIGNAL_INFO, + + /* Some signal we don't know about. */ + TARGET_SIGNAL_UNKNOWN, + + /* Use whatever signal we use when one is not specifically specified + (for passing to proceed and so on). */ + TARGET_SIGNAL_DEFAULT, + + /* Mach exceptions. In versions of GDB before 5.2, these were just before + TARGET_SIGNAL_INFO if you were compiling on a Mach host (and missing + otherwise). */ + TARGET_EXC_BAD_ACCESS, + TARGET_EXC_BAD_INSTRUCTION, + TARGET_EXC_ARITHMETIC, + TARGET_EXC_EMULATION, + TARGET_EXC_SOFTWARE, + TARGET_EXC_BREAKPOINT, + + /* If you are adding a new signal, add it just above this comment. */ + + /* Last and unused enum value, for sizing arrays, etc. */ + TARGET_SIGNAL_LAST + }; + +#endif /* #ifndef GDB_SIGNALS_H */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/target.c gdb-5.2.1/gdb/gdbserver/target.c --- gdb-5.2.1-orig/gdb/gdbserver/target.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/target.c Tue Sep 2 14:04:41 2003 @@ -0,0 +1,113 @@ +/* Target operations for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 "linux-low.h" +struct target_ops *the_target; + +void +set_desired_inferior (int use_general) +{ + struct thread_info *found; + + if (use_general == 1) + { + found = (struct thread_info *) find_inferior_id (&all_threads, + general_thread); + } + else + { + found = NULL; + + /* If we are continuing any (all) thread(s), use step_thread + to decide which thread to step and/or send the specified + signal to. */ + if (step_thread > 0 && (cont_thread == 0 || cont_thread == -1)) + found = (struct thread_info *) find_inferior_id (&all_threads, + step_thread); + + if (found == NULL) + found = (struct thread_info *) find_inferior_id (&all_threads, + cont_thread); + } + + if (found == NULL) { + /* Do not default it to initial thread, leave current. */ + if (!current_inferior) current_inferior = + (struct thread_info *) all_threads.head; + } + else + current_inferior = found; +} + +void +read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + (*the_target->read_memory) (memaddr, myaddr, len); + check_mem_read (memaddr, myaddr, len); +} + +int +write_inferior_memory (CORE_ADDR memaddr, const char *myaddr, int len) +{ + /* Lacking cleanups, there is some potential for a memory leak if the + write fails and we go through error(). Make sure that no more than + one buffer is ever pending by making BUFFER static. */ + static char *buffer = 0; + int res; + + if (buffer != NULL) + free (buffer); + + buffer = malloc (len); + memcpy (buffer, myaddr, len); + check_mem_write (memaddr, buffer, len); + res = (*the_target->write_memory) (memaddr, buffer, len); + free (buffer); + buffer = NULL; + + return res; +} + +unsigned char +mywait (char *statusp, int connected_wait) +{ + unsigned char ret; + + if (connected_wait) + server_waiting = 1; + + ret = (*the_target->wait) (statusp); + + if (connected_wait) + server_waiting = 0; + + return ret; +} + +void +set_target_ops (struct target_ops *target) +{ + the_target = (struct target_ops *) malloc (sizeof (*the_target)); + memcpy (the_target, target, sizeof (*the_target)); +} diff -purN gdb-5.2.1-orig/gdb/gdbserver/target.h gdb-5.2.1/gdb/gdbserver/target.h --- gdb-5.2.1-orig/gdb/gdbserver/target.h Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/target.h Tue Sep 2 14:04:41 2003 @@ -0,0 +1,156 @@ +/* Target operations for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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. */ + +#ifndef TARGET_H +#define TARGET_H + +struct target_ops +{ + /* Start a new process. + + PROGRAM is a path to the program to execute. + ARGS is a standard NULL-terminated array of arguments, + to be passed to the inferior as ``argv''. + + Returns 0 on success, -1 on failure. Registers the new + process with the process list. */ + + int (*create_inferior) (char *program, char **args); + + /* Attach to a running process. + + PID is the process ID to attach to, specified by the user + or a higher layer. */ + + int (*attach) (int pid); + +#ifdef HAVE_TARGET_ATTACH + int (*detach) (int pid); +#endif + + /* Kill all inferiors. */ + + void (*kill) (void); + + /* Return 1 iff the thread with process ID PID is alive. */ + + int (*thread_alive) (int pid); + + /* Resume the inferior process. + + If STEP is non-zero, we want to single-step. + + If SIGNAL is nonzero, send the process that signal as we resume it. + */ + + void (*resume) (int step, int signo); + + /* Wait for the inferior process to change state. + + STATUSP will be filled in with a response code to send to GDB. + + Returns the signal which caused the process to stop. */ + + unsigned char (*wait) (char *status); + + /* Fetch registers from the inferior process. + + If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ + + void (*fetch_registers) (int regno); + + /* Store registers to the inferior process. + + If REGNO is -1, store all registers; otherwise, store at least REGNO. */ + + void (*store_registers) (int regno); + + /* Read memory from the inferior process. This should generally be + called through read_inferior_memory, which handles breakpoint shadowing. + + Read LEN bytes at MEMADDR into a buffer at MYADDR. */ + + void (*read_memory) (CORE_ADDR memaddr, char *myaddr, int len); + + /* Write memory to the inferior process. This should generally be + called through write_inferior_memory, which handles breakpoint shadowing. + + Write LEN bytes from the buffer at MYADDR to MEMADDR. + + Returns 0 on success and errno on failure. */ + + int (*write_memory) (CORE_ADDR memaddr, const char *myaddr, int len); + + /* Query GDB for the values of any symbols we're interested in. + This function is called whenever we receive a "qSymbols::" + query, which corresponds to every time more symbols (might) + become available. NULL if we aren't interested in any + symbols. */ + + void (*look_up_symbols) (void); + + /* Return the PID we should send a signal to. Used for asynchronous + interrupts (user hitting Control-C). */ + + int (*signal_pid) (void); +}; + +extern struct target_ops *the_target; + +void set_target_ops (struct target_ops *); + +#define create_inferior(program, args) \ + (*the_target->create_inferior) (program, args) + +#define myattach(pid) \ + (*the_target->attach) (pid) + +#ifdef HAVE_TARGET_ATTACH +#define mydetach(pid) \ + (*the_target->detach) (pid) +#endif + +#define kill_inferior() \ + (*the_target->kill) () + +#define mythread_alive(pid) \ + (*the_target->thread_alive) (pid) + +#define myresume(step,signo) \ + (*the_target->resume) (step, signo) + +#define fetch_inferior_registers(regno) \ + (*the_target->fetch_registers) (regno) + +#define store_inferior_registers(regno) \ + (*the_target->store_registers) (regno) + +unsigned char mywait (char *statusp, int connected_wait); + +void read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len); + +int write_inferior_memory (CORE_ADDR memaddr, const char *myaddr, int len); + +void set_desired_inferior (int id); + +#endif /* TARGET_H */ diff -purN gdb-5.2.1-orig/gdb/gdbserver/thread-db.c gdb-5.2.1/gdb/gdbserver/thread-db.c --- gdb-5.2.1-orig/gdb/gdbserver/thread-db.c Thu Jan 1 01:00:00 1970 +++ gdb-5.2.1/gdb/gdbserver/thread-db.c Tue Sep 2 14:04:41 2003 @@ -0,0 +1,369 @@ +/* Thread management interface, for the remote server for GDB. + Copyright 2002 + Free Software Foundation, Inc. + + Contributed by MontaVista Software. + + 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 "linux-low.h" + +extern int debug_threads; +#ifdef HAVE_TARGET_ATTACH +static CORE_ADDR td_create_bp_addr; +#endif + +#ifdef HAVE_THREAD_DB_H +#include +#endif + +/* Correct for all GNU/Linux targets (for quite some time). */ +#define GDB_GREGSET_T elf_gregset_t +#define GDB_FPREGSET_T elf_fpregset_t + +#ifndef HAVE_ELF_FPREGSET_T +/* Make sure we have said types. Not all platforms bring in + via . */ +#ifdef HAVE_LINUX_ELF_H +#include +#endif +#endif + +#include "../gdb_proc_service.h" + +/* Structure that identifies the child process for the + interface. */ +static struct ps_prochandle proc_handle; + +/* Connection to the libthread_db library. */ +static td_thragent_t *thread_agent; + +static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); + +static char * +thread_db_err_str (td_err_e err) +{ + static char buf[64]; + + switch (err) + { + case TD_OK: + return "generic 'call succeeded'"; + case TD_ERR: + return "generic error"; + case TD_NOTHR: + return "no thread to satisfy query"; + case TD_NOSV: + return "no sync handle to satisfy query"; + case TD_NOLWP: + return "no LWP to satisfy query"; + case TD_BADPH: + return "invalid process handle"; + case TD_BADTH: + return "invalid thread handle"; + case TD_BADSH: + return "invalid synchronization handle"; + case TD_BADTA: + return "invalid thread agent"; + case TD_BADKEY: + return "invalid key"; + case TD_NOMSG: + return "no event message for getmsg"; + case TD_NOFPREGS: + return "FPU register set not available"; + case TD_NOLIBTHREAD: + return "application not linked with libthread"; + case TD_NOEVENT: + return "requested event is not supported"; + case TD_NOCAPAB: + return "capability not available"; + case TD_DBERR: + return "debugger service failed"; + case TD_NOAPLIC: + return "operation not applicable to"; + case TD_NOTSD: + return "no thread-specific data for this thread"; + case TD_MALLOC: + return "malloc failed"; + case TD_PARTIALREG: + return "only part of register set was written/read"; + case TD_NOXREGS: + return "X register set not available for this thread"; + default: + snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err); + return buf; + } +} + +#if 0 +static char * +thread_db_state_str (td_thr_state_e state) +{ + static char buf[64]; + + switch (state) + { + case TD_THR_STOPPED: + return "stopped by debugger"; + case TD_THR_RUN: + return "runnable"; + case TD_THR_ACTIVE: + return "active"; + case TD_THR_ZOMBIE: + return "zombie"; + case TD_THR_SLEEP: + return "sleeping"; + case TD_THR_STOPPED_ASLEEP: + return "stopped by debugger AND blocked"; + default: + snprintf (buf, sizeof (buf), "unknown thread_db state %d", state); + return buf; + } +} +#endif + +static void +thread_db_create_event (CORE_ADDR where) +{ + td_event_msg_t msg; + td_err_e err; + struct inferior_linux_data *tdata; + + if (debug_threads) + fprintf (stderr, "Thread creation event.\n"); + + tdata = inferior_target_data (current_inferior); + + /* FIXME: This assumes we don't get another event. + In the LinuxThreads implementation, this is safe, + because all events come from the manager thread + (except for its own creation, of course). */ + err = td_ta_event_getmsg (thread_agent, &msg); + if (err != TD_OK) + fprintf (stderr, "thread getmsg err: %s\n", + thread_db_err_str (err)); + + /* msg.event == TD_EVENT_CREATE */ + + find_new_threads_callback (msg.th_p, NULL); +} + +#if 0 +static void +thread_db_death_event (CORE_ADDR where) +{ + if (debug_threads) + fprintf (stderr, "Thread death event.\n"); +} +#endif + +static int +thread_db_enable_reporting () +{ + td_thr_events_t events; + td_notify_t notify; + td_err_e err; + + /* Set the process wide mask saying which events we're interested in. */ + td_event_emptyset (&events); + td_event_addset (&events, TD_CREATE); + +#if 0 + /* This is reported to be broken in glibc 2.1.3. A different approach + will be necessary to support that. */ + td_event_addset (&events, TD_DEATH); +#endif + + err = td_ta_set_event (thread_agent, &events); + if (err != TD_OK) + { + warning ("Unable to set global thread event mask: %s", + thread_db_err_str (err)); + return 0; + } + + /* Get address for thread creation breakpoint. */ + err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify); + if (err != TD_OK) + { + warning ("Unable to get location for thread creation breakpoint: %s", + thread_db_err_str (err)); + return 0; + } + set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, + thread_db_create_event); + +#ifdef HAVE_TARGET_ATTACH + td_create_bp_addr = (CORE_ADDR) (unsigned long) notify.u.bptaddr; +#endif +#if 0 + /* Don't concern ourselves with reported thread deaths, only + with actual thread deaths (via wait). */ + + /* Get address for thread death breakpoint. */ + err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify); + if (err != TD_OK) + { + warning ("Unable to get location for thread death breakpoint: %s", + thread_db_err_str (err)); + return; + } + set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, + thread_db_death_event); +#endif + + return 1; +} + +static void +maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) +{ + td_err_e err; + struct thread_info *inferior; + struct process_info *process; + + /* If we are attaching to our first thread, things are a little + different. */ + if (all_threads.head == all_threads.tail) + { + inferior = (struct thread_info *) all_threads.head; + process = get_thread_process (inferior); + if (process->thread_known == 0) + { + /* Switch to indexing the threads list by TID. */ + change_inferior_id (&all_threads, ti_p->ti_tid); + goto found; + } + } + + inferior = (struct thread_info *) find_inferior_id (&all_threads, + ti_p->ti_tid); + if (inferior != NULL) + return; + + if (debug_threads) + fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", + ti_p->ti_tid, ti_p->ti_lid); + linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid); + inferior = (struct thread_info *) find_inferior_id (&all_threads, + ti_p->ti_tid); + if (inferior == NULL) + { + warning ("Could not attach to thread %ld (LWP %d)\n", + ti_p->ti_tid, ti_p->ti_lid); + return; + } + + process = inferior_target_data (inferior); + +found: + new_thread_notify (ti_p->ti_tid); + + process->tid = ti_p->ti_tid; + process->lwpid = ti_p->ti_lid; + + process->thread_known = 1; + err = td_thr_event_enable (th_p, 1); + if (err != TD_OK) + error ("Cannot enable thread event reporting for %d: %s", + ti_p->ti_lid, thread_db_err_str (err)); +} + +static int +find_new_threads_callback (const td_thrhandle_t *th_p, void *data) +{ + td_thrinfo_t ti; + td_err_e err; + + err = td_thr_get_info (th_p, &ti); + if (err != TD_OK) + error ("Cannot get thread info: %s", thread_db_err_str (err)); + + /* Check for zombies. */ + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return 0; + + maybe_attach_thread (th_p, &ti); + + return 0; +} + +static void +thread_db_find_new_threads (void) +{ + td_err_e err; + + /* Iterate over all user-space threads to discover new threads. */ + err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); + if (err != TD_OK) + error ("Cannot find new threads: %s", thread_db_err_str (err)); +} + +int +thread_db_init () +{ + int err; + + proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id; + + err = td_ta_new (&proc_handle, &thread_agent); + switch (err) + { + case TD_NOLIBTHREAD: + /* No thread library was detected. */ + return 0; + + case TD_OK: + /* The thread library was detected. */ + + if (thread_db_enable_reporting () == 0) + return 0; + thread_db_find_new_threads (); + return 1; + + default: + warning ("error initializing thread_db library."); + } + + return 0; +} + +#ifdef HAVE_TARGET_ATTACH +static int +thread_db_disable_reporting () +{ + td_thr_events_t events; + struct breakpoint *thr_bp; + + td_event_emptyset (&events); + td_ta_set_event (thread_agent, &events); + /* delete thread event breakpoints */ + thr_bp = find_breakpoint_at (td_create_bp_addr); + delete_breakpoint (thr_bp); + return 0; +} + +int thread_db_deinit(void) +{ + return thread_db_disable_reporting(); +} +#endif diff -purN gdb-5.2.1-orig/gdb/gdbserver/utils.c gdb-5.2.1/gdb/gdbserver/utils.c --- gdb-5.2.1-orig/gdb/gdbserver/utils.c Thu Feb 14 07:21:22 2002 +++ gdb-5.2.1/gdb/gdbserver/utils.c Tue Sep 2 14:04:41 2003 @@ -57,7 +57,7 @@ perror_with_name (char *string) STRING is the error message, used as a fprintf string, and ARG is passed as an argument to it. */ -NORETURN void +void error (const char *string,...) { extern jmp_buf toplevel; @@ -74,7 +74,7 @@ error (const char *string,...) STRING and ARG are passed to fprintf. */ /* VARARGS */ -NORETURN void +void fatal (const char *string,...) { va_list args; diff -pu gdb-5.2.1-orig/gdb/regformats/reg-ppc.dat gdb-5.2.1/gdb/regformats/reg-ppc.dat --- gdb-5.2.1-orig/gdb/regformats/reg-ppc.dat Fri Feb 1 23:05:28 2002 +++ gdb-5.2.1/gdb/regformats/reg-ppc.dat Thu Sep 11 18:43:39 2003 @@ -73,4 +73,4 @@ expedite:r1,pc 32:lr 32:ctr 32:xer -0: +32:fpscr