From: Daniel Jacobowitz Date: Fri, 3 Mar 2006 20:46:38 +0000 (+0000) Subject: Initial version of available features support. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5fb2a6a2b5c61411239d88e09e05ef48bd8f5a78;p=thirdparty%2Fbinutils-gdb.git Initial version of available features support. --- diff --git a/Makefile.def b/Makefile.def index 3227a445602..3c87dce68dd 100644 --- a/Makefile.def +++ b/Makefile.def @@ -47,6 +47,7 @@ host_modules= { module= dejagnu; }; host_modules= { module= diff; }; host_modules= { module= dosutils; no_check= true; }; host_modules= { module= etc; }; +host_modules= { module= expat; }; host_modules= { module= fastjar; no_check_cross= true; }; host_modules= { module= fileutils; }; host_modules= { module= findutils; }; @@ -297,6 +298,7 @@ dependencies = { module=all-gdb; on=all-readline; }; dependencies = { module=all-gdb; on=all-build-bison; }; dependencies = { module=all-gdb; on=all-build-byacc; }; dependencies = { module=all-gdb; on=all-sim; }; +dependencies = { module=all-gdb; on=all-expat; }; dependencies = { module=configure-libgui; on=configure-tcl; }; dependencies = { module=configure-libgui; on=configure-tk; }; diff --git a/Makefile.in b/Makefile.in index 37d5510053b..15a98abc0b2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -566,6 +566,7 @@ configure-host: \ maybe-configure-diff \ maybe-configure-dosutils \ maybe-configure-etc \ + maybe-configure-expat \ maybe-configure-fastjar \ maybe-configure-fileutils \ maybe-configure-findutils \ @@ -694,6 +695,7 @@ all-host: maybe-all-dejagnu all-host: maybe-all-diff all-host: maybe-all-dosutils all-host: maybe-all-etc +all-host: maybe-all-expat all-host: maybe-all-fastjar all-host: maybe-all-fileutils all-host: maybe-all-findutils @@ -819,6 +821,7 @@ info-host: maybe-info-dejagnu info-host: maybe-info-diff info-host: maybe-info-dosutils info-host: maybe-info-etc +info-host: maybe-info-expat info-host: maybe-info-fastjar info-host: maybe-info-fileutils info-host: maybe-info-findutils @@ -923,6 +926,7 @@ dvi-host: maybe-dvi-dejagnu dvi-host: maybe-dvi-diff dvi-host: maybe-dvi-dosutils dvi-host: maybe-dvi-etc +dvi-host: maybe-dvi-expat dvi-host: maybe-dvi-fastjar dvi-host: maybe-dvi-fileutils dvi-host: maybe-dvi-findutils @@ -1027,6 +1031,7 @@ html-host: maybe-html-dejagnu html-host: maybe-html-diff html-host: maybe-html-dosutils html-host: maybe-html-etc +html-host: maybe-html-expat html-host: maybe-html-fastjar html-host: maybe-html-fileutils html-host: maybe-html-findutils @@ -1131,6 +1136,7 @@ TAGS-host: maybe-TAGS-dejagnu TAGS-host: maybe-TAGS-diff TAGS-host: maybe-TAGS-dosutils TAGS-host: maybe-TAGS-etc +TAGS-host: maybe-TAGS-expat TAGS-host: maybe-TAGS-fastjar TAGS-host: maybe-TAGS-fileutils TAGS-host: maybe-TAGS-findutils @@ -1235,6 +1241,7 @@ install-info-host: maybe-install-info-dejagnu install-info-host: maybe-install-info-diff install-info-host: maybe-install-info-dosutils install-info-host: maybe-install-info-etc +install-info-host: maybe-install-info-expat install-info-host: maybe-install-info-fastjar install-info-host: maybe-install-info-fileutils install-info-host: maybe-install-info-findutils @@ -1339,6 +1346,7 @@ installcheck-host: maybe-installcheck-dejagnu installcheck-host: maybe-installcheck-diff installcheck-host: maybe-installcheck-dosutils installcheck-host: maybe-installcheck-etc +installcheck-host: maybe-installcheck-expat installcheck-host: maybe-installcheck-fastjar installcheck-host: maybe-installcheck-fileutils installcheck-host: maybe-installcheck-findutils @@ -1443,6 +1451,7 @@ mostlyclean-host: maybe-mostlyclean-dejagnu mostlyclean-host: maybe-mostlyclean-diff mostlyclean-host: maybe-mostlyclean-dosutils mostlyclean-host: maybe-mostlyclean-etc +mostlyclean-host: maybe-mostlyclean-expat mostlyclean-host: maybe-mostlyclean-fastjar mostlyclean-host: maybe-mostlyclean-fileutils mostlyclean-host: maybe-mostlyclean-findutils @@ -1547,6 +1556,7 @@ clean-host: maybe-clean-dejagnu clean-host: maybe-clean-diff clean-host: maybe-clean-dosutils clean-host: maybe-clean-etc +clean-host: maybe-clean-expat clean-host: maybe-clean-fastjar clean-host: maybe-clean-fileutils clean-host: maybe-clean-findutils @@ -1651,6 +1661,7 @@ distclean-host: maybe-distclean-dejagnu distclean-host: maybe-distclean-diff distclean-host: maybe-distclean-dosutils distclean-host: maybe-distclean-etc +distclean-host: maybe-distclean-expat distclean-host: maybe-distclean-fastjar distclean-host: maybe-distclean-fileutils distclean-host: maybe-distclean-findutils @@ -1755,6 +1766,7 @@ maintainer-clean-host: maybe-maintainer-clean-dejagnu maintainer-clean-host: maybe-maintainer-clean-diff maintainer-clean-host: maybe-maintainer-clean-dosutils maintainer-clean-host: maybe-maintainer-clean-etc +maintainer-clean-host: maybe-maintainer-clean-expat maintainer-clean-host: maybe-maintainer-clean-fastjar maintainer-clean-host: maybe-maintainer-clean-fileutils maintainer-clean-host: maybe-maintainer-clean-findutils @@ -1912,6 +1924,7 @@ check-host: \ maybe-check-diff \ maybe-check-dosutils \ maybe-check-etc \ + maybe-check-expat \ maybe-check-fastjar \ maybe-check-fileutils \ maybe-check-findutils \ @@ -2043,6 +2056,7 @@ install-host-nogcc: \ maybe-install-diff \ maybe-install-dosutils \ maybe-install-etc \ + maybe-install-expat \ maybe-install-fastjar \ maybe-install-fileutils \ maybe-install-findutils \ @@ -2113,6 +2127,7 @@ install-host: \ maybe-install-diff \ maybe-install-dosutils \ maybe-install-etc \ + maybe-install-expat \ maybe-install-fastjar \ maybe-install-fileutils \ maybe-install-findutils \ @@ -8356,6 +8371,343 @@ maintainer-clean-etc: +.PHONY: configure-expat maybe-configure-expat +maybe-configure-expat: +@if expat +maybe-configure-expat: configure-expat +configure-expat: + @: $(MAKE); $(unstage) + @r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + test ! -f $(HOST_SUBDIR)/expat/Makefile || exit 0; \ + $(SHELL) $(srcdir)/mkinstalldirs $(HOST_SUBDIR)/expat ; \ + $(HOST_EXPORTS) \ + echo Configuring in $(HOST_SUBDIR)/expat; \ + cd "$(HOST_SUBDIR)/expat" || exit 1; \ + case $(srcdir) in \ + /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \ + *) topdir=`echo $(HOST_SUBDIR)/expat/ | \ + sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \ + esac; \ + srcdiroption="--srcdir=$${topdir}/expat"; \ + libsrcdir="$$s/expat"; \ + $(SHELL) $${libsrcdir}/configure \ + $(HOST_CONFIGARGS) $${srcdiroption} \ + || exit 1 +@endif expat + + + + + +.PHONY: all-expat maybe-all-expat +maybe-all-expat: +@if expat +TARGET-expat=all +maybe-all-expat: all-expat +all-expat: configure-expat + @: $(MAKE); $(unstage) + @r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(FLAGS_TO_PASS) $(TARGET-expat)) +@endif expat + + + + +.PHONY: check-expat maybe-check-expat +maybe-check-expat: +@if expat +maybe-check-expat: check-expat + +check-expat: + @: $(MAKE); $(unstage) + @r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(FLAGS_TO_PASS) check) + +@endif expat + +.PHONY: install-expat maybe-install-expat +maybe-install-expat: +@if expat +maybe-install-expat: install-expat + +install-expat: installdirs + @: $(MAKE); $(unstage) + @r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(FLAGS_TO_PASS) install) + +@endif expat + +# Other targets (info, dvi, etc.) + +.PHONY: maybe-info-expat info-expat +maybe-info-expat: +@if expat +maybe-info-expat: info-expat + +info-expat: \ + configure-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing info in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + info) \ + || exit 1 + +@endif expat + +.PHONY: maybe-dvi-expat dvi-expat +maybe-dvi-expat: +@if expat +maybe-dvi-expat: dvi-expat + +dvi-expat: \ + configure-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing dvi in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + dvi) \ + || exit 1 + +@endif expat + +.PHONY: maybe-html-expat html-expat +maybe-html-expat: +@if expat +maybe-html-expat: html-expat + +html-expat: \ + configure-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing html in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + html) \ + || exit 1 + +@endif expat + +.PHONY: maybe-TAGS-expat TAGS-expat +maybe-TAGS-expat: +@if expat +maybe-TAGS-expat: TAGS-expat + +TAGS-expat: \ + configure-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing TAGS in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + TAGS) \ + || exit 1 + +@endif expat + +.PHONY: maybe-install-info-expat install-info-expat +maybe-install-info-expat: +@if expat +maybe-install-info-expat: install-info-expat + +install-info-expat: \ + configure-expat \ + info-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing install-info in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + install-info) \ + || exit 1 + +@endif expat + +.PHONY: maybe-installcheck-expat installcheck-expat +maybe-installcheck-expat: +@if expat +maybe-installcheck-expat: installcheck-expat + +installcheck-expat: \ + configure-expat + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing installcheck in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + installcheck) \ + || exit 1 + +@endif expat + +.PHONY: maybe-mostlyclean-expat mostlyclean-expat +maybe-mostlyclean-expat: +@if expat +maybe-mostlyclean-expat: mostlyclean-expat + +mostlyclean-expat: + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing mostlyclean in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + mostlyclean) \ + || exit 1 + +@endif expat + +.PHONY: maybe-clean-expat clean-expat +maybe-clean-expat: +@if expat +maybe-clean-expat: clean-expat + +clean-expat: + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing clean in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + clean) \ + || exit 1 + +@endif expat + +.PHONY: maybe-distclean-expat distclean-expat +maybe-distclean-expat: +@if expat +maybe-distclean-expat: distclean-expat + +distclean-expat: + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing distclean in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + distclean) \ + || exit 1 + +@endif expat + +.PHONY: maybe-maintainer-clean-expat maintainer-clean-expat +maybe-maintainer-clean-expat: +@if expat +maybe-maintainer-clean-expat: maintainer-clean-expat + +maintainer-clean-expat: + @: $(MAKE); $(unstage) + @[ -f ./expat/Makefile ] || exit 0; \ + r=`${PWD_COMMAND}`; export r; \ + s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \ + $(HOST_EXPORTS) \ + for flag in $(EXTRA_HOST_FLAGS) ; do \ + eval `echo "$$flag" | sed -e "s|^\([^=]*\)=\(.*\)|\1='\2'; export \1|"`; \ + done; \ + echo "Doing maintainer-clean in expat" ; \ + (cd $(HOST_SUBDIR)/expat && \ + $(MAKE) $(BASE_FLAGS_TO_PASS) "AR=$${AR}" "AS=$${AS}" \ + "CC=$${CC}" "CXX=$${CXX}" "LD=$${LD}" "NM=$${NM}" \ + "RANLIB=$${RANLIB}" \ + "DLLTOOL=$${DLLTOOL}" "WINDRES=$${WINDRES}" \ + maintainer-clean) \ + || exit 1 + +@endif expat + + + .PHONY: configure-fastjar maybe-configure-fastjar maybe-configure-fastjar: @if fastjar @@ -38269,6 +38621,7 @@ all-gdb: maybe-all-readline all-gdb: maybe-all-build-bison all-gdb: maybe-all-build-byacc all-gdb: maybe-all-sim +all-gdb: maybe-all-expat configure-libgui: maybe-configure-tcl configure-libgui: maybe-configure-tk all-libgui: maybe-all-tcl diff --git a/configure b/configure index d74e6116d6a..3f7c5712a01 100755 --- a/configure +++ b/configure @@ -886,7 +886,7 @@ build_tools="build-texinfo build-byacc build-flex build-bison build-m4 build-fix # these libraries are used by various programs built for the host environment # -host_libs="intl mmalloc libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber" +host_libs="intl mmalloc libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber expat" # these tools are built for the host environment # Note, the powerpc-eabi build depends on sim occurring before gdb in order to diff --git a/configure.in b/configure.in index adb53b9468e..f7c50c194ef 100644 --- a/configure.in +++ b/configure.in @@ -123,7 +123,7 @@ build_tools="build-texinfo build-byacc build-flex build-bison build-m4 build-fix # these libraries are used by various programs built for the host environment # -host_libs="intl mmalloc libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber" +host_libs="intl mmalloc libiberty opcodes bfd readline tcl tk itcl libgui zlib libcpp libdecnumber expat" # these tools are built for the host environment # Note, the powerpc-eabi build depends on sim occurring before gdb in order to diff --git a/gdb/Makefile.in b/gdb/Makefile.in index d5abb4f682f..ae7d251cb21 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -127,6 +127,10 @@ READLINE = $(READLINE_DIR)/libreadline.a READLINE_SRC = $(srcdir)/$(READLINE_DIR) READLINE_CFLAGS = -I$(READLINE_SRC)/.. +# Where is the expat library? Typically in ../expat. +EXPAT = ../expat/.libs/libexpat.a +EXPAT_CFLAGS = -I$(srcdir)/expat/lib -I../expat + WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ GDB_WARN_CFLAGS = $(WARN_CFLAGS) @@ -350,6 +354,7 @@ INTERNAL_CFLAGS_BASE = \ $(CFLAGS) $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \ $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \ $(BFD_CFLAGS) $(INCLUDE_CFLAGS) \ + $(EXPAT_CFLAGS) \ $(INTL_CFLAGS) $(ENABLE_CFLAGS) INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS) INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS) @@ -378,9 +383,9 @@ INSTALLED_LIBS=-lbfd -lreadline -lopcodes -liberty \ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) \ $(XM_CLIBS) $(TM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ \ $(LIBICONV) \ - $(LIBIBERTY) $(WIN32LIBS) + $(LIBIBERTY) $(WIN32LIBS) $(EXPAT) CDEPS = $(XM_CDEPS) $(TM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE) \ - $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) + $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(EXPAT) ADD_FILES = $(XM_ADD_FILES) $(TM_ADD_FILES) $(NAT_ADD_FILES) ADD_DEPS = $(XM_ADD_FILES) $(TM_ADD_FILES) $(NAT_ADD_FILES) @@ -390,7 +395,7 @@ DIST=gdb LINT=/usr/5bin/lint LINTFLAGS= $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \ $(BFD_CFLAGS) $(INCLUDE_CFLAGS) \ - $(INTL_CFLAGS) + $(INTL_CFLAGS) $(EXPAT_CFLAGS) RUNTEST = runtest RUNTESTFLAGS= @@ -511,6 +516,7 @@ TARGET_FLAGS_TO_PASS = \ # SFILES is used in building the distribution archive. SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ + auxv.c available.c \ ax-general.c ax-gdb.c \ bcache.c \ bfd-target.c \ @@ -542,6 +548,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ objc-exp.y objc-lang.c \ objfiles.c osabi.c observer.c \ p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \ + parse-avail.c \ regcache.c reggroups.c remote.c remote-fileio.c \ scm-exp.c scm-lang.c scm-valprint.c \ sentinel-frame.c \ @@ -638,6 +645,7 @@ annotate_h = annotate.h $(symtab_h) $(gdbtypes_h) arch_utils_h = arch-utils.h arm_tdep_h = arm-tdep.h auxv_h = auxv.h +available_h = available.h ax_gdb_h = ax-gdb.h ax_h = ax.h $(doublest_h) bcache_h = bcache.h @@ -786,7 +794,7 @@ sparc_tdep_h = sparc-tdep.h srec_h = srec.h stabsread_h = stabsread.h stack_h = stack.h -symfile_h = symfile.h +symfile_h = symfile.h $(symtab_h) symtab_h = symtab.h target_h = target.h $(bfd_h) $(symtab_h) $(dcache_h) $(memattr_h) terminal_h = terminal.h @@ -902,6 +910,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ version.o \ annotate.o \ auxv.o \ + available.o parse-avail.o \ bfd-target.o \ blockframe.o breakpoint.o findvar.o regcache.o \ charset.o disasm.o dummy-frame.o \ @@ -1759,10 +1768,13 @@ arm-tdep.o: arm-tdep.c $(defs_h) $(frame_h) $(inferior_h) $(gdbcmd_h) \ $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) $(arm_tdep_h) \ $(gdb_sim_arm_h) $(elf_bfd_h) $(coff_internal_h) $(elf_arm_h) \ $(gdb_assert_h) $(bfd_in2_h) $(libcoff_h) $(objfiles_h) \ - $(dwarf2_frame_h) + $(dwarf2_frame_h) $(available_h) auxv.o: auxv.c $(defs_h) $(target_h) $(gdbtypes_h) $(command_h) \ $(inferior_h) $(valprint_h) $(gdb_assert_h) $(auxv_h) \ $(elf_common_h) +available.o: available.c $(defs_h) $(symfile_h) $(target_h) $(available_h) \ + $(arch_utils_h) $(gdbtypes_h) \ + $(gdb_string) $(gdb_assert) $(gdb_obstack_h) avr-tdep.o: avr-tdep.c $(defs_h) $(frame_h) $(frame_unwind_h) \ $(frame_base_h) $(trad_frame_h) $(gdbcmd_h) $(gdbcore_h) \ $(inferior_h) $(symfile_h) $(arch_utils_h) $(regcache_h) \ @@ -1982,7 +1994,8 @@ gcore.o: gcore.c $(defs_h) $(elf_bfd_h) $(infcall_h) $(inferior_h) \ $(gdb_assert_h) gdbarch.o: gdbarch.c $(defs_h) $(arch_utils_h) $(gdbcmd_h) $(inferior_h) \ $(symcat_h) $(floatformat_h) $(gdb_assert_h) $(gdb_string_h) \ - $(gdb_events_h) $(reggroups_h) $(osabi_h) $(gdb_obstack_h) + $(gdb_events_h) $(reggroups_h) $(osabi_h) $(gdb_obstack_h) \ + $(available_h) gdb.o: gdb.c $(defs_h) $(main_h) $(gdb_string_h) $(interps_h) gdb-events.o: gdb-events.c $(defs_h) $(gdb_events_h) $(gdbcmd_h) gdbtypes.o: gdbtypes.c $(defs_h) $(gdb_string_h) $(bfd_h) $(symtab_h) \ @@ -2126,7 +2139,7 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(symfile_h) $(gdbcore_h) $(target_h) $(language_h) $(symfile_h) \ $(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \ $(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \ - $(solib_h) $(gdb_assert_h) $(observer_h) + $(solib_h) $(gdb_assert_h) $(gdb_obstack_h) $(observer_h) inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \ $(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) inflow.o: inflow.c $(defs_h) $(frame_h) $(inferior_h) $(command_h) \ @@ -2376,6 +2389,8 @@ parse.o: parse.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(frame_h) $(expression_h) $(value_h) $(command_h) $(language_h) \ $(parser_defs_h) $(gdbcmd_h) $(symfile_h) $(inferior_h) \ $(doublest_h) $(gdb_assert_h) $(block_h) +parse-avail.o: parse-avail.c $(defs_h) $(available_h) \ + $(gdb_string) $(gdb_obstack_h) p-exp.o: p-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \ $(parser_defs_h) $(language_h) $(p_lang_h) $(bfd_h) $(symfile_h) \ $(objfiles_h) $(block_h) @@ -2450,7 +2465,7 @@ remote.o: remote.c $(defs_h) $(gdb_string_h) $(inferior_h) $(bfd_h) \ $(gdb_stabs_h) $(gdbthread_h) $(remote_h) $(regcache_h) $(value_h) \ $(gdb_assert_h) $(event_loop_h) $(event_top_h) $(inf_loop_h) \ $(serial_h) $(gdbcore_h) $(remote_fileio_h) $(solib_h) $(observer_h) \ - $(cli_decode_h) $(cli_setshow_h) + $(cli_decode_h) $(cli_setshow_h) $(available_h) remote-e7000.o: remote-e7000.c $(defs_h) $(gdbcore_h) $(gdbarch_h) \ $(inferior_h) $(target_h) $(value_h) $(command_h) $(gdb_string_h) \ $(exceptions_h) $(gdbcmd_h) $(serial_h) $(remote_utils_h) \ diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 652be4b7234..a831b653fe5 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -40,6 +40,7 @@ #include "trad-frame.h" #include "objfiles.h" #include "dwarf2-frame.h" +#include "available.h" #include "arm-tdep.h" #include "gdb/sim-arm.h" @@ -1343,6 +1344,12 @@ arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, static struct type * arm_register_type (struct gdbarch *gdbarch, int regnum) { + struct type *avail_type; + + avail_type = available_register_type (gdbarch, regnum); + if (avail_type) + return avail_type; + if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS) { if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) @@ -1354,23 +1361,6 @@ arm_register_type (struct gdbarch *gdbarch, int regnum) return builtin_type_int32; } -/* Index within `registers' of the first byte of the space for - register N. */ - -static int -arm_register_byte (int regnum) -{ - if (regnum < ARM_F0_REGNUM) - return regnum * INT_REGISTER_SIZE; - else if (regnum < ARM_PS_REGNUM) - return (NUM_GREGS * INT_REGISTER_SIZE - + (regnum - ARM_F0_REGNUM) * FP_REGISTER_SIZE); - else - return (NUM_GREGS * INT_REGISTER_SIZE - + NUM_FREGS * FP_REGISTER_SIZE - + (regnum - ARM_FPS_REGNUM) * STATUS_REGISTER_SIZE); -} - /* Map GDB internal REGNUM onto the Arm simulator register numbers. */ static int arm_register_sim_regno (int regnum) @@ -2466,6 +2456,12 @@ set_disassembly_style_sfunc (char *args, int from_tty, static const char * arm_register_name (int i) { + const char *avail_name; + + avail_name = available_register_name (current_gdbarch, i); + if (avail_name) + return avail_name; + return arm_register_names[i]; } @@ -2769,10 +2765,14 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM); /* ??? */ set_gdbarch_sp_regnum (gdbarch, ARM_SP_REGNUM); set_gdbarch_pc_regnum (gdbarch, ARM_PC_REGNUM); - set_gdbarch_deprecated_register_byte (gdbarch, arm_register_byte); set_gdbarch_num_regs (gdbarch, NUM_GREGS + NUM_FREGS + NUM_SREGS); + set_gdbarch_remote_num_g_packet_regs (gdbarch, + NUM_GREGS + NUM_FREGS + NUM_SREGS); set_gdbarch_register_type (gdbarch, arm_register_type); + if (info.feature_set) + record_available_features (gdbarch, info.feature_set); + /* Internal <-> external register number maps. */ set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno); @@ -2840,6 +2840,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) _("arm_gdbarch_init: bad byte order for float format")); } + set_gdbarch_available_features_support (gdbarch, 1); + return gdbarch; } diff --git a/gdb/auxv.c b/gdb/auxv.c index 557da3d416d..8a2c9fc2dde 100644 --- a/gdb/auxv.c +++ b/gdb/auxv.c @@ -76,43 +76,6 @@ procfs_xfer_auxv (struct target_ops *ops, return n; } -/* Read all the auxv data into a contiguous xmalloc'd buffer, - stored in *DATA. Return the size in bytes of this data. - If zero, there is no data and *DATA is null. - if < 0, there was an error and *DATA is null. */ -LONGEST -target_auxv_read (struct target_ops *ops, gdb_byte **data) -{ - size_t auxv_alloc = 512, auxv_pos = 0; - gdb_byte *auxv = xmalloc (auxv_alloc); - int n; - - while (1) - { - n = target_read_partial (ops, TARGET_OBJECT_AUXV, - NULL, &auxv[auxv_pos], 0, - auxv_alloc - auxv_pos); - if (n <= 0) - break; - auxv_pos += n; - if (auxv_pos < auxv_alloc) /* Read all there was. */ - break; - gdb_assert (auxv_pos == auxv_alloc); - auxv_alloc *= 2; - auxv = xrealloc (auxv, auxv_alloc); - } - - if (auxv_pos == 0) - { - xfree (auxv); - *data = NULL; - return n; - } - - *data = auxv; - return auxv_pos; -} - /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR. Return 0 if *READPTR is already at the end of the buffer. Return -1 if there is insufficient buffer for a whole entry. @@ -148,7 +111,7 @@ target_auxv_search (struct target_ops *ops, CORE_ADDR match, CORE_ADDR *valp) { CORE_ADDR type, val; gdb_byte *data; - int n = target_auxv_read (ops, &data); + LONGEST n = target_read_whole (ops, TARGET_OBJECT_AUXV, NULL, &data); gdb_byte *ptr = data; int ents = 0; @@ -184,7 +147,7 @@ fprint_target_auxv (struct ui_file *file, struct target_ops *ops) { CORE_ADDR type, val; gdb_byte *data; - int len = target_auxv_read (ops, &data); + LONGEST len = target_read_whole (ops, TARGET_OBJECT_AUXV, NULL, &data); gdb_byte *ptr = data; int ents = 0; diff --git a/gdb/auxv.h b/gdb/auxv.h index 92f7b541a6f..c3030b0cacd 100644 --- a/gdb/auxv.h +++ b/gdb/auxv.h @@ -31,12 +31,6 @@ struct target_ops; /* Forward declaration. */ -/* Read all the auxv data into a contiguous xmalloc'd buffer, - stored in *DATA. Return the size in bytes of this data. - If zero, there is no data and *DATA is null. - if < 0, there was an error and *DATA is null. */ -extern LONGEST target_auxv_read (struct target_ops *ops, gdb_byte **data); - /* Read one auxv entry from *READPTR, not reading locations >= ENDPTR. Return 0 if *READPTR is already at the end of the buffer. Return -1 if there is insufficient buffer for a whole entry. diff --git a/gdb/available.c b/gdb/available.c new file mode 100644 index 00000000000..b887d0977dd --- /dev/null +++ b/gdb/available.c @@ -0,0 +1,367 @@ +/* Support for runtime-defined target features for GDB. + + Copyright (C) 2006 + Free Software Foundation, Inc. + + Contributed by CodeSourcery. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "arch-utils.h" +#include "gdbtypes.h" +#include "symfile.h" +#include "target.h" + +#include "available.h" + +#include "gdb_assert.h" +#include "gdb_string.h" +#include "gdb_obstack.h" + +/* TODO: Remote target "guess" features from g packet size */ + +/* TODO: Clarify warning messages. Remember, they will appear to + the user with no context after a "target remote". The user + doesn't know how we got into this code. */ + +/* Architecture TODO to support this: + + - Call available_register_type from _register_type method. + - Handle unexpected changes to _num_regs. + - Call record_available_features from _gdbarch_init. + - Do not override the default _register_byte + - Provide gdbarch_remote_num_g_packet_regs +*/ + +/* FIXME: Everywhere we call internal_error from this file leads to a failure + to initialize a gdbarch, which leads to later failures when we expect + e.g. current_regcache to have been initialized. */ + +/* Read a string representation of available features from the target, + using TARGET_OBJECT_AVAILABLE_FEATURES. The returned string is + malloc allocated and NUL-terminated. If NAME is NULL, the overall + feature set is read; otherwise the specified name is read (e.g. + resolving xi:include). */ + +static char * +fetch_available_features_from_target (const char *name, void *ops_) +{ + struct target_ops *ops = ops_; + char *features_str; + gdb_byte *features_buf; + LONGEST len; + + struct gdb_feature_set *features; + struct gdb_available_feature **slot; + int ret; + + len = target_read_whole (ops, TARGET_OBJECT_AVAILABLE_FEATURES, + NULL, &features_buf); + if (len <= 0) + return NULL; + + /* Since we decode this object as a string, simplify processing by + making sure it is NUL terminated. */ + features_str = (char *) features_buf; + if (features_str[len - 1] != '\0') + { + features_str = xrealloc (features_str, len + 1); + features_str[len] = '\0'; + } + + return features_str; +} + +/* Standard method to convert a string representation of available features + to a binary representation. The string representation is fetched using + TARGET_OBJECT_AVAILABLE_FEATURES. */ + +struct gdb_feature_set * +available_features_from_target_object (struct target_ops *ops, + struct obstack *obstack) +{ + struct gdb_feature_set *features; + char *features_str, *cur; + gdb_byte *features_buf; + LONGEST len; + struct gdb_available_feature **slot; + int ret; + + features_str = fetch_available_features_from_target (NULL, ops); + + features = OBSTACK_ZALLOC (obstack, struct gdb_feature_set); + features->obstack = obstack; + ret = available_features_from_xml_string (&features->features, obstack, + features_str, + fetch_available_features_from_target, + ops, 0); + + if (ret < 0) + { + warning (_("Could not parse features XML from target")); + return NULL; + } + + return features; +} + +/* Return non-zero if LHS and RHS refer to compatible feature sets. */ + +int +features_same_p (const struct gdb_feature_set *lhs, + const struct gdb_feature_set *rhs) +{ + const struct gdb_available_feature *lhs_p, *rhs_p; + + lhs_p = lhs->features; + rhs_p = rhs->features; + while (lhs_p && rhs_p) + { + lhs_p = lhs_p->next; + rhs_p = rhs_p->next; + } + if (lhs_p || rhs_p) + return 0; + + /* FIXME: This checking assumes that there are no features with + duplicate names in either set; enforce that when creating sets. */ + + for (lhs_p = lhs->features; lhs_p; lhs_p = lhs_p->next) + { + for (rhs_p = rhs->features; rhs_p; rhs_p = rhs_p->next) + if (strcmp (lhs_p->name, rhs_p->name) == 0) + break; + if (rhs_p == NULL) + return 0; + + /* FIXME: Check feature contents, especially for "custom" features + which don't have a standard meaning! */ + } + + return 1; +} + +/* Switch the architecture (gdbarch) to one which supports FEATURES. */ + +void +arch_set_available_features (const struct gdb_feature_set *features) +{ + struct gdbarch_info info; + + gdbarch_info_init (&info); + info.feature_set = features; + if (!gdbarch_update_p (info)) + internal_error (__FILE__, __LINE__, "could not update architecture"); +} + +static struct gdb_feature_set * +copy_features_to_obstack (struct obstack *obstack, + const struct gdb_feature_set *features) +{ + struct gdb_feature_set *result; + struct gdb_available_feature **slot, *orig_feature; + + result = obstack_alloc (obstack, sizeof (struct gdb_feature_set)); + result->obstack = obstack; + + slot = &result->features; + for (orig_feature = features->features; + orig_feature; + orig_feature = orig_feature->next) + { + struct gdb_available_feature *feature; + struct gdb_available_register **reg_slot, *orig_reg; + + feature = OBSTACK_ZALLOC (obstack, struct gdb_available_feature); + *slot = feature; + slot = &feature->next; + + memcpy (feature, orig_feature, sizeof (struct gdb_available_feature)); + feature->name = obsavestring (feature->name, strlen (feature->name), + obstack); + if (feature->arch_data) + feature->arch_data = obsavestring (feature->arch_data, + strlen (feature->arch_data), + obstack); + + reg_slot = &feature->registers; + for (orig_reg = orig_feature->registers; + orig_reg; + orig_reg = orig_reg->next) + { + struct gdb_available_register *reg; + + reg = OBSTACK_ZALLOC (obstack, struct gdb_available_register); + *reg_slot = reg; + reg_slot = ®->next; + + memcpy (reg, orig_reg, sizeof (struct gdb_available_register)); + reg->name = obsavestring (reg->name, strlen (reg->name), obstack); + if (reg->arch_data) + reg->arch_data = obsavestring (reg->arch_data, + strlen (reg->arch_data), + obstack); + if (reg->group) + reg->group = obsavestring (reg->group, strlen (reg->group), + obstack); + if (reg->type) + reg->type = obsavestring (reg->type, strlen (reg->type), + obstack); + } + } + + return result; +} + +/* Set an architecture's feature set. Store BASE_FEATURES in GDBARCH, + and on the correct obstack. + + This function will update num_regs. It is the architecture's + responsibility to handle this if it has pseudo registers. + + FIXME: This interface may need to go away; what if we want to add + a single additional feature to that provided by the target? */ + +void +record_available_features (struct gdbarch *gdbarch, + const struct gdb_feature_set *base_features) +{ + struct gdb_available_feature *feature; + struct gdb_available_register *reg; + struct gdb_feature_set *features; + int gdb_regnum, protocol_number; + + features = copy_features_to_obstack (gdbarch_obstack (gdbarch), + base_features); + set_gdbarch_feature_set (gdbarch, features); + + gdb_regnum = gdbarch_num_regs (gdbarch); + + for (feature = features->features; feature; feature = feature->next) + { + protocol_number = feature->protocol_number; + for (reg = feature->registers; reg; reg = reg->next) + { + reg->gdb_regnum = gdb_regnum++; + reg->protocol_number = protocol_number++; + } + } + + set_gdbarch_num_regs (gdbarch, gdb_regnum); +} + +/* Search FEATURES for a register with GDB register number REGNUM. */ + +struct gdb_available_register * +find_register (const struct gdb_feature_set *features, int regnum) +{ + struct gdb_available_feature *feature; + struct gdb_available_register *reg; + + if (features == NULL) + return NULL; + + for (feature = features->features; feature; feature = feature->next) + for (reg = feature->registers; reg; reg = reg->next) + if (reg->gdb_regnum == regnum) + return reg; + + return NULL; +} + +/* Return the type of target-described register REGNUM, if the feature set + for GDBARCH describes that register. Otherwise return NULL. */ + +struct type * +available_register_type (struct gdbarch *gdbarch, int regnum) +{ + struct gdb_available_register *reg; + + reg = find_register (gdbarch_feature_set (gdbarch), regnum); + if (reg == NULL) + return NULL; + + if (reg->type && strcmp (reg->type, "float") == 0) + { + if (reg->bitsize == gdbarch_float_bit (gdbarch)) + return builtin_type_float; + else if (reg->bitsize == gdbarch_double_bit (gdbarch)) + return builtin_type_double; + else if (reg->bitsize == gdbarch_long_double_bit (gdbarch)) + return builtin_type_long_double; + } + + if (reg->type && strcmp (reg->type, "int") != 0) + { + /* FIXME: Warn the user about an unknown type + size? */ + } + + /* Use an integer type; default to "long". */ + if (reg->bitsize == gdbarch_long_bit (gdbarch)) + return builtin_type_long; + else if (reg->bitsize == TARGET_CHAR_BIT) + return builtin_type_signed_char; + else if (reg->bitsize == gdbarch_short_bit (gdbarch)) + return builtin_type_short; + else if (reg->bitsize == gdbarch_int_bit (gdbarch)) + return builtin_type_int; + else if (reg->bitsize == gdbarch_long_long_bit (gdbarch)) + return builtin_type_long_long; + else if (reg->bitsize == gdbarch_ptr_bit (gdbarch)) + /* A bit desperate by this point... */ + return builtin_type_void_data_ptr; + else + { + /* FIXME: Create a new integer type of the appropriate size? */ + internal_error (__FILE__, __LINE__, + _("GDB does not support %ld-bit registers on this target"), + reg->bitsize); + } +} + +/* Return the name of target-described register REGNUM, if the feature set + for GDBARCH describes that register. Otherwise return NULL. */ + +const char * +available_register_name (struct gdbarch *gdbarch, int regnum) +{ + struct gdb_available_register *reg; + + reg = find_register (gdbarch_feature_set (gdbarch), regnum); + if (reg == NULL) + return NULL; + + return reg->name; +} + +/* Return the target-supplied register of target-described register + REGNUM, if the feature set for GDBARCH describes that register. + Otherwise return REGNUM (the legacy 1:1 mapping). */ + +int +available_register_target_regnum (struct gdbarch *gdbarch, int regnum) +{ + struct gdb_available_register *reg; + + reg = find_register (gdbarch_feature_set (gdbarch), regnum); + if (reg == NULL) + return regnum; + + return reg->protocol_number; +} diff --git a/gdb/available.h b/gdb/available.h new file mode 100644 index 00000000000..600722d57cb --- /dev/null +++ b/gdb/available.h @@ -0,0 +1,171 @@ +/* Support for runtime-defined target features for GDB. + + Copyright (C) 2006 + Free Software Foundation, Inc. + + Contributed by CodeSourcery. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef AVAILABLE_H +#define AVAILABLE_H 1 + +struct obstack; + +/* A GDB target interface can use these types to communicate to the + architecture support (gdbarch) what new or optional features + it supports. + + Each individual gdb_available_feature describes a target-specific + unit of functionality, often including a group of registers, + possibly including other bits of which the debugger needs + explicit knowledge. GDB may recognize the feature by name, + or query the target for additional details. If it recognizes + the feature by name, it may take advantage of the feature's + presence in additional ways - e.g. by knowing the calling + conventions for functions using the new registers. + + If GDB does not recognize the feature by name, and the feature + requests explicit debugger support, GDB may suggest an upgrade + to the user. */ + +struct gdb_feature_set +{ + struct gdb_available_feature *features; + + struct obstack *obstack; +}; + +struct gdb_available_feature +{ + /* The name of this feature. For features, the name + is recognized by the architecture. */ + const char *name; + + /* The protocol number used by this target to provide this feature. + For instance, the base register number for a group of raw + registers included in a known feature. If none is necessary this + may be set to -1. */ + int protocol_number; + + /* Data private to the architecture associated with this feature. + This is a NUL-terminated string. */ + const char *arch_data; + + /* The registers associated with this feature. */ + struct gdb_available_register *registers; + + /* Chain to the next available feature in this set. */ + struct gdb_available_feature *next; +}; + +struct gdb_available_register +{ + /* The name of this feature. For registers, the name is + only used by the user interface. */ + const char *name; + + /* The protocol number used by this target to provide this + feature. For instance, the register number used for remote + p/P packets to access this register. */ + int protocol_number; + + /* Data private to the architecture associated with this feature. + This is a NUL-terminated string. */ + const char *arch_data; + + /* If this flag is set, GDB should never try to write to this + register. Otherwise, the user may modify the value in the + register. */ + int readonly; + + /* If this flag is set, GDB should save and restore this register + around calls to an inferior function. */ + /* FIXME: Richard Earnshaw proposed an alternate, more thourough + categorization. Should we use that instead? */ + int save_restore; + + /* The name of the register group containing this register. If this + is "general", "float", or "vector", the corresponding "info" command + should display this register's value. It can be an arbitrary + string, but should be limited to alphanumeric characters and internal + hyphens. */ + const char *group; + + /* The size of the register, in bits. */ + long bitsize; + + /* The type of the register. This is a target-supplied string, + corresponding to FIXME FIXME. */ + const char *type; + + /* GDB internal state for this register; this may vary per copy + of this code in each gdbarch. */ + + /* GDB register number for this register. */ + int gdb_regnum; + + /* Chain to the next available register in this feature. */ + struct gdb_available_register *next; +}; + +/* Standard method to convert a string representation of available features + to a binary representation. The string representation is fetched using + TARGET_OBJECT_AVAILABLE_FEATURES. */ + +struct gdb_feature_set *available_features_from_target_object + (struct target_ops *, struct obstack *); + +/* Standard method to update an architecture based on detected available + features. */ + +void arch_set_available_features (const struct gdb_feature_set *); + +/* Compare two sets of available features. */ + +int features_same_p (const struct gdb_feature_set *, + const struct gdb_feature_set *); + +/* Set an architecture's feature set. */ + +void record_available_features (struct gdbarch *, + const struct gdb_feature_set *); + +/* Find the type of a target-described register. */ + +struct type *available_register_type (struct gdbarch *, int); + +/* Find the name of a target-described register. */ + +const char *available_register_name (struct gdbarch *, int); + +/* Find the target-supplied register number of a target-described register. */ + +int available_register_target_regnum (struct gdbarch *, int); + + +/* Internal routines shared by available.c and parse-avail.c. */ + +typedef char *(*xml_fetch_another) (const char *, void *); + +int available_features_from_xml_string (struct gdb_available_feature **, + struct obstack *, const char *, + xml_fetch_another, void *, + int); + +#endif /* AVAILABLE_H */ diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c index 9c6566e9210..530caa60db1 100644 --- a/gdb/avr-tdep.c +++ b/gdb/avr-tdep.c @@ -1323,35 +1323,22 @@ static void avr_io_reg_read_command (char *args, int from_tty) { LONGEST bufsiz = 0; - char buf[400]; + gdb_byte *buf; char query[400]; char *p; unsigned int nreg = 0; unsigned int val; int i, j, k, step; - /* Just get the maximum buffer size. */ - bufsiz = target_read_partial (¤t_target, TARGET_OBJECT_AVR, - NULL, NULL, 0, 0); - if (bufsiz < 0) - { - fprintf_unfiltered (gdb_stderr, - _("ERR: info io_registers NOT supported " - "by current target\n")); - return; - } - if (bufsiz > sizeof (buf)) - bufsiz = sizeof (buf); - /* Find out how many io registers the target has. */ - strcpy (query, "avr.io_reg"); - target_read_partial (¤t_target, TARGET_OBJECT_AVR, query, buf, 0, - bufsiz); + bufsiz = target_read_whole (¤t_target, TARGET_OBJECT_AVR, + "avr.io_reg", &buf); - if (strncmp (buf, "", bufsiz) == 0) + if (bufsiz <= 0) { fprintf_unfiltered (gdb_stderr, - _("info io_registers NOT supported by target\n")); + _("ERR: info io_registers NOT supported " + "by current target\n")); return; } @@ -1359,9 +1346,12 @@ avr_io_reg_read_command (char *args, int from_tty) { fprintf_unfiltered (gdb_stderr, _("Error fetching number of io registers\n")); + xfree (buf); return; } + xfree (buf); + reinitialize_more_filter (); printf_unfiltered (_("Target has %u io registers:\n\n"), nreg); @@ -1377,8 +1367,8 @@ avr_io_reg_read_command (char *args, int from_tty) j = nreg - i; /* last block is less than 8 registers */ snprintf (query, sizeof (query) - 1, "avr.io_reg:%x,%x", i, j); - target_read_partial (¤t_target, TARGET_OBJECT_AVR, query, buf, - 0, bufsiz); + bufsiz = target_read_whole (¤t_target, TARGET_OBJECT_AVR, query, + &buf); p = buf; for (k = i; k < (i + j); k++) @@ -1393,6 +1383,8 @@ avr_io_reg_read_command (char *args, int from_tty) break; } } + + xfree (buf); } } diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd new file mode 100644 index 00000000000..54b865aa44a --- /dev/null +++ b/gdb/features/gdb-target.dtd @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 4fa5ea4a3ec..d5490845a87 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -41,6 +41,7 @@ #include "gdbcmd.h" #include "inferior.h" /* enum CALL_DUMMY_LOCATION et.al. */ #include "symcat.h" +#include "available.h" #include "floatformat.h" @@ -152,6 +153,7 @@ struct gdbarch gdbarch_pseudo_register_write_ftype *pseudo_register_write; int num_regs; int num_pseudo_regs; + int remote_num_g_packet_regs; int sp_regnum; int pc_regnum; int ps_regnum; @@ -235,6 +237,8 @@ struct gdbarch gdbarch_register_reggroup_p_ftype *register_reggroup_p; gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument; gdbarch_regset_from_core_section_ftype *regset_from_core_section; + int available_features_support; + const struct gdb_feature_set * feature_set; }; @@ -278,6 +282,7 @@ struct gdbarch startup_gdbarch = 0, /* pseudo_register_write */ 0, /* num_regs */ 0, /* num_pseudo_regs */ + 0, /* remote_num_g_packet_regs */ -1, /* sp_regnum */ -1, /* pc_regnum */ -1, /* ps_regnum */ @@ -361,6 +366,8 @@ struct gdbarch startup_gdbarch = default_register_reggroup_p, /* register_reggroup_p */ 0, /* fetch_pointer_argument */ 0, /* regset_from_core_section */ + 0, /* available_features_support */ + 0, /* feature_set */ /* startup_gdbarch() */ }; @@ -536,6 +543,7 @@ verify_gdbarch (struct gdbarch *current_gdbarch) if (current_gdbarch->num_regs == -1) fprintf_unfiltered (log, "\n\tnum_regs"); /* Skip verify of num_pseudo_regs, invalid_p == 0 */ + /* Skip verify of remote_num_g_packet_regs, has predicate */ /* Skip verify of sp_regnum, invalid_p == 0 */ /* Skip verify of pc_regnum, invalid_p == 0 */ /* Skip verify of ps_regnum, invalid_p == 0 */ @@ -719,6 +727,9 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: adjust_breakpoint_address = <0x%lx>\n", (long) current_gdbarch->adjust_breakpoint_address); + fprintf_unfiltered (file, + "gdbarch_dump: available_features_support = %s\n", + paddr_d (current_gdbarch->available_features_support)); #ifdef BELIEVE_PCC_PROMOTION fprintf_unfiltered (file, "gdbarch_dump: BELIEVE_PCC_PROMOTION # %s\n", @@ -1054,6 +1065,9 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: extract_return_value = <0x%lx>\n", (long) current_gdbarch->extract_return_value); + fprintf_unfiltered (file, + "gdbarch_dump: feature_set = %s\n", + paddr_nz ((long) current_gdbarch->feature_set)); #ifdef FETCH_POINTER_ARGUMENT_P fprintf_unfiltered (file, "gdbarch_dump: %s # %s\n", @@ -1474,6 +1488,12 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: regset_from_core_section = <0x%lx>\n", (long) current_gdbarch->regset_from_core_section); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_remote_num_g_packet_regs_p() = %d\n", + gdbarch_remote_num_g_packet_regs_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: remote_num_g_packet_regs = %s\n", + paddr_d (current_gdbarch->remote_num_g_packet_regs)); fprintf_unfiltered (file, "gdbarch_dump: remote_translate_xfer_address = <0x%lx>\n", (long) current_gdbarch->remote_translate_xfer_address); @@ -1640,6 +1660,14 @@ gdbarch_tdep (struct gdbarch *gdbarch) return gdbarch->tdep; } +struct obstack * +gdbarch_obstack (struct gdbarch *gdbarch) +{ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_obstack called\n"); + return gdbarch->obstack; +} + const struct bfd_arch_info * gdbarch_bfd_arch_info (struct gdbarch *gdbarch) @@ -2070,6 +2098,29 @@ set_gdbarch_num_pseudo_regs (struct gdbarch *gdbarch, gdbarch->num_pseudo_regs = num_pseudo_regs; } +int +gdbarch_remote_num_g_packet_regs_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->remote_num_g_packet_regs != 0; +} + +int +gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_remote_num_g_packet_regs called\n"); + return gdbarch->remote_num_g_packet_regs; +} + +void +set_gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch, + int remote_num_g_packet_regs) +{ + gdbarch->remote_num_g_packet_regs = remote_num_g_packet_regs; +} + int gdbarch_sp_regnum (struct gdbarch *gdbarch) { @@ -3683,6 +3734,38 @@ set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbarch->regset_from_core_section = regset_from_core_section; } +int +gdbarch_available_features_support (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_available_features_support called\n"); + return gdbarch->available_features_support; +} + +void +set_gdbarch_available_features_support (struct gdbarch *gdbarch, + int available_features_support) +{ + gdbarch->available_features_support = available_features_support; +} + +const struct gdb_feature_set * +gdbarch_feature_set (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_feature_set called\n"); + return gdbarch->feature_set; +} + +void +set_gdbarch_feature_set (struct gdbarch *gdbarch, + const struct gdb_feature_set * feature_set) +{ + gdbarch->feature_set = feature_set; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ @@ -4006,8 +4089,7 @@ register_gdbarch_init (enum bfd_architecture bfd_architecture, } -/* Look for an architecture using gdbarch_info. Base search on only - BFD_ARCH_INFO and BYTE_ORDER. */ +/* Look for an architecture using gdbarch_info. */ struct gdbarch_list * gdbarch_list_lookup_by_info (struct gdbarch_list *arches, @@ -4021,6 +4103,15 @@ gdbarch_list_lookup_by_info (struct gdbarch_list *arches, continue; if (info->osabi != arches->gdbarch->osabi) continue; + + if (info->feature_set && !arches->gdbarch->feature_set) + continue; + if (!info->feature_set && arches->gdbarch->feature_set) + continue; + if (info->feature_set + && !features_same_p (info->feature_set, arches->gdbarch->feature_set)) + continue; + return arches; } return NULL; diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 75ac81fd625..1d11fb70b06 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -49,6 +49,7 @@ struct regset; struct disassemble_info; struct target_ops; struct obstack; +struct gdb_feature_set; extern struct gdbarch *current_gdbarch; @@ -355,6 +356,16 @@ extern void set_gdbarch_num_pseudo_regs (struct gdbarch *gdbarch, int num_pseudo #define NUM_PSEUDO_REGS (gdbarch_num_pseudo_regs (current_gdbarch)) #endif +/* The number of registers fetched or stored using this target's + traditional g/G packet. + FIXME: Could we do without this by asking the target for a + g packet, and just seeing what's there? We surely could! */ + +extern int gdbarch_remote_num_g_packet_regs_p (struct gdbarch *gdbarch); + +extern int gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch); +extern void set_gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch, int remote_num_g_packet_regs); + /* GDB's standard (or well known) register numbers. These can map onto a real register or a pseudo (computed) register or not be defined at all (-1). @@ -1403,8 +1414,19 @@ typedef const struct regset * (gdbarch_regset_from_core_section_ftype) (struct g extern const struct regset * gdbarch_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size); extern void set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbarch_regset_from_core_section_ftype *regset_from_core_section); +/* Non-zero if the architecture supports target feature sets. */ + +extern int gdbarch_available_features_support (struct gdbarch *gdbarch); +extern void set_gdbarch_available_features_support (struct gdbarch *gdbarch, int available_features_support); + +/* The architecture's currently associated feature set. */ + +extern const struct gdb_feature_set * gdbarch_feature_set (struct gdbarch *gdbarch); +extern void set_gdbarch_feature_set (struct gdbarch *gdbarch, const struct gdb_feature_set * feature_set); + extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch); +extern struct obstack *gdbarch_obstack (struct gdbarch *gdbarch); /* Mechanism for co-ordinating the selection of a specific architecture. @@ -1487,6 +1509,9 @@ struct gdbarch_info /* Use default: GDB_OSABI_UNINITIALIZED (-1). */ enum gdb_osabi osabi; + + /* Use default: NULL. */ + const struct gdb_feature_set *feature_set; }; typedef struct gdbarch *(gdbarch_init_ftype) (struct gdbarch_info info, struct gdbarch_list *arches); @@ -1511,11 +1536,11 @@ extern const char **gdbarch_printable_names (void); /* Helper function. Search the list of ARCHES for a GDBARCH that matches the information provided by INFO. */ -extern struct gdbarch_list *gdbarch_list_lookup_by_info (struct gdbarch_list *arches, const struct gdbarch_info *info); +extern struct gdbarch_list *gdbarch_list_lookup_by_info (struct gdbarch_list *arches, const struct gdbarch_info *info); /* Helper function. Create a preliminary ``struct gdbarch''. Perform - basic initialization using values obtained from the INFO andTDEP + basic initialization using values obtained from the INFO and TDEP parameters. set_gdbarch_*() functions are called to complete the initialization of the object. */ diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index afcfd82b5d9..fb26e901d76 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -433,6 +433,11 @@ v:=:int:num_regs:::0:-1 # These pseudo-registers may be aliases for other registers, # combinations of other registers, or they may be computed by GDB. v:=:int:num_pseudo_regs:::0:0::0 +# The number of registers fetched or stored using this target's +# traditional g/G packet. +# FIXME: Could we do without this by asking the target for a +# g packet, and just seeing what's there? We surely could! +V::int:remote_num_g_packet_regs # GDB's standard (or well known) register numbers. These can map onto # a real register or a pseudo (computed) register or not be defined at @@ -662,6 +667,12 @@ F:=:CORE_ADDR:fetch_pointer_argument:struct frame_info *frame, int argi, struct # Return the appropriate register set for a core file section with # name SECT_NAME and size SECT_SIZE. M::const struct regset *:regset_from_core_section:const char *sect_name, size_t sect_size:sect_name, sect_size + +# Non-zero if the architecture supports target feature sets. +v::int:available_features_support + +# The architecture's currently associated feature set. +v::const struct gdb_feature_set *:feature_set:::::::paddr_nz ((long) current_gdbarch->feature_set) EOF } @@ -771,6 +782,7 @@ struct regset; struct disassemble_info; struct target_ops; struct obstack; +struct gdb_feature_set; extern struct gdbarch *current_gdbarch; EOF @@ -904,6 +916,7 @@ cat <tdep; } + +struct obstack * +gdbarch_obstack (struct gdbarch *gdbarch) +{ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_obstack called\\n"); + return gdbarch->obstack; +} EOF printf "\n" function_list | while do_read @@ -2024,8 +2049,7 @@ register_gdbarch_init (enum bfd_architecture bfd_architecture, } -/* Look for an architecture using gdbarch_info. Base search on only - BFD_ARCH_INFO and BYTE_ORDER. */ +/* Look for an architecture using gdbarch_info. */ struct gdbarch_list * gdbarch_list_lookup_by_info (struct gdbarch_list *arches, @@ -2039,6 +2063,15 @@ gdbarch_list_lookup_by_info (struct gdbarch_list *arches, continue; if (info->osabi != arches->gdbarch->osabi) continue; + + if (info->feature_set && !arches->gdbarch->feature_set) + continue; + if (!info->feature_set && arches->gdbarch->feature_set) + continue; + if (info->feature_set + && !features_same_p (info->feature_set, arches->gdbarch->feature_set)) + continue; + return arches; } return NULL; diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c index 4ec5a955cdb..f302142e9fe 100644 --- a/gdb/ia64-tdep.c +++ b/gdb/ia64-tdep.c @@ -2455,8 +2455,8 @@ ia64_access_mem (unw_addr_space_t as, } /* Call low-level function to access the kernel unwind table. */ -static int -getunwind_table (void *buf, size_t len) +static LONGEST +getunwind_table (gdb_byte **buf_p) { LONGEST x; @@ -2467,10 +2467,11 @@ getunwind_table (void *buf, size_t len) we want to preserve fall back to the running kernel's table, then we should find a way to override the corefile layer's xfer_partial method. */ - x = target_read_partial (¤t_target, TARGET_OBJECT_UNWIND_TABLE, NULL, - buf, 0, len); - return (int)x; + x = target_read_whole (¤t_target, TARGET_OBJECT_UNWIND_TABLE, + NULL, buf_p); + + return x; } /* Get the kernel unwind table. */ @@ -2481,14 +2482,15 @@ get_kernel_table (unw_word_t ip, unw_dyn_info_t *di) if (!ktab) { + gdb_byte *ktab_buf; size_t size; - size = getunwind_table (NULL, 0); - if ((int)size < 0) - return -UNW_ENOINFO; - ktab_size = size; - ktab = xmalloc (ktab_size); - getunwind_table (ktab, ktab_size); - + + ktab_size = getunwind_table (&ktab_buf); + if (ktab_size <= 0) + return -UNW_ENOINFO; + else + ktab = (struct ia64_table_entry *) ktab_buf; + for (etab = ktab; etab->start_offset; ++etab) etab->info_offset += KERNEL_START; } diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 12a305de2c3..09732ff4a46 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -48,6 +48,9 @@ #include #include "gdb_assert.h" #include "observer.h" +#include "available.h" + +#include "gdb_obstack.h" /* Functions exported for general use, in inferior.h: */ @@ -405,6 +408,25 @@ tty_command (char *file, int from_tty) void post_create_inferior (struct target_ops *target, int from_tty) { + /* The first thing we do after creating an inferior is update the + architecture with information provided by the target. + + FIXME: In some cases we could do this after target_open + instead; should we? */ + if (gdbarch_available_features_support (current_gdbarch)) + { + struct gdb_feature_set *features; + struct obstack tmp_obstack; + + obstack_init (&tmp_obstack); + features = target_available_features (target, &tmp_obstack); + + if (features) + arch_set_available_features (features); + + obstack_free (&tmp_obstack, NULL); + } + if (exec_bfd) { /* Sometimes the platform-specific hook loads initial shared diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 0710ac718ac..0c1dc1b81fe 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2849,7 +2849,8 @@ linux_nat_make_corefile_notes (bfd *obfd, int *note_size) note_data = thread_args.note_data; } - auxv_len = target_auxv_read (¤t_target, &auxv); + auxv_len = target_read_whole (¤t_target, TARGET_OBJECT_AUXV, NULL, + &auxv); if (auxv_len > 0) { note_data = elfcore_write_note (obfd, note_data, note_size, diff --git a/gdb/parse-avail.c b/gdb/parse-avail.c new file mode 100644 index 00000000000..b331d8a53be --- /dev/null +++ b/gdb/parse-avail.c @@ -0,0 +1,989 @@ +/* Support for runtime-defined target features for GDB. + + Copyright (C) 2006 + Free Software Foundation, Inc. + + Contributed by CodeSourcery. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" + +#include "available.h" + +#include "filenames.h" +#include "gdb_assert.h" +#include "gdb_string.h" +#include "gdb_obstack.h" + +#include + +/* General notes for this file: + + Do not call error while parsing supplied features. If something is + unrecognizable, fail gracefully. Calling error may lead to a + failure to initialize a gdbarch, and internal errors down the road. + Also, it's hard to predict with XML what's meaningless today but + might make sense tomorrow :-) + + Warnings are OK; so are true internal errors, e.g. impossible states + in the parsing finite state machine. Also, we will reach + internal_error if there is a memory allocation failure. */ + +/* Parse a field VALSTR that we expect to contain an integer value. + The integer is returned in *VALP. + + Returns 0 for success, -1 for error. */ + +static int +xml_parse_one_integer (const char *valstr, long *valp) +{ + char *endptr; + long result; + + if (*valstr == '\0') + return -1; + + result = strtol (valstr, &endptr, 0); + if (*endptr != '\0') + return -1; + + *valp = result; + return 0; +} + +/* Parse a field VALSTR that we expect to contain a boolean value. + The value is returned in *VALP. + + Returns 0 for success, -1 for error. */ + +static int +xml_parse_one_boolean (const char *valstr, int *valp) +{ + if (strcmp (valstr, "yes") == 0) + *valp = 1; + else if (strcmp (valstr, "no") == 0) + *valp = 0; + else + return -1; + + return 0; +} + +/* Save STR on OBSTACK, and return the new copy. */ + +static char * +obstrdup (struct obstack *obstack, const char *str) +{ + char *result; + size_t len; + + len = strlen (str); + result = obstack_alloc (obstack, len + 1); + memcpy (result, str, len); + result[len] = '\0'; + + return result; +} + +/* Duplicate a provided ORIG_FEATURE, allocating the new copy from + OBSTACK. Return the new copy. */ + +static struct gdb_available_feature * +copy_feature_to_obstack (struct obstack *obstack, + const struct gdb_available_feature *orig_feature) +{ + struct gdb_available_feature *feature; + struct gdb_available_register **reg_slot, *orig_reg; + + feature = OBSTACK_ZALLOC (obstack, struct gdb_available_feature); + + memcpy (feature, orig_feature, sizeof (struct gdb_available_feature)); + feature->name = obstrdup (obstack, feature->name); + if (feature->arch_data) + feature->arch_data = obstrdup (obstack, feature->arch_data); + + reg_slot = &feature->registers; + for (orig_reg = orig_feature->registers; + orig_reg; + orig_reg = orig_reg->next) + { + struct gdb_available_register *reg; + + reg = OBSTACK_ZALLOC (obstack, struct gdb_available_register); + *reg_slot = reg; + reg_slot = ®->next; + + memcpy (reg, orig_reg, sizeof (struct gdb_available_register)); + reg->name = obstrdup (obstack, reg->name); + if (reg->arch_data) + reg->arch_data = obstrdup (obstack, reg->arch_data); + if (reg->group) + reg->group = obstrdup (obstack, reg->group); + if (reg->type) + reg->type = obstrdup (obstack, reg->type); + } + + return feature; +} + + + +/* FIXME item: Warnings versus errors? Where to issue warnings? */ + +enum xml_phase { + PHASE_TOP, + PHASE_IN_TARGET, + PHASE_IN_FEATURE, + PHASE_IN_DESCRIPTION, + PHASE_IN_REG, + PHASE_IN_FEATURE_SET, + PHASE_IN_FEATURE_REF, + PHASE_IN_XINCLUDE, + PHASE_UNKNOWN +}; + +struct xml_state_stack +{ + enum xml_phase phase; + + union + { + struct + { + int seen_target; + int nested; + } top; + struct + { + int seen_feature_set; + int features_only; + } target; + struct + { + struct gdb_available_feature *feature; + int seen_reg; + } feature; + struct + { + struct gdb_available_register *reg; + } reg; + struct + { + int depth; + } unknown; + } u; + + struct xml_state_stack *prev; +}; + +struct xml_feature_parse_data +{ + /* The obstack to allocate new features from. */ + struct obstack *obstack; + + /* A function to call to obtain additional features, and its + baton. */ + xml_fetch_another fetcher; + void *fetcher_baton; + + struct gdb_available_feature *seen_features; + + struct gdb_available_feature *target_features; + + /* Rough count of unrecognized or invalid items found while + processing. These are non-fatal; however, some data from the + input has been skipped. */ + int unhandled; + + struct xml_state_stack *state; +}; + +static void +xml_start_xinclude (struct xml_feature_parse_data *data, + const XML_Char **attrs) +{ + struct gdb_available_feature *features, *f; + struct cleanup *back_to; + char *text; + int ret; + const char *href, *id, **p; + + href = id = NULL; + for (p = attrs; *p; p += 2) + { + const char *name = p[0], *val = p[1]; + + if (strcmp (name, "href") == 0) + href = val; + else if (strcmp (name, "xpointer") == 0) + id = val; + else + data->unhandled++; + } + + if (id == NULL) + { + data->unhandled++; + return; + } + + for (f = data->seen_features; f; f = f->next) + if (strcmp (f->name, id) == 0) + /* Success. We found a match already loaded; we don't need to + include the document. */ + return; + + if (href == NULL || data->fetcher == NULL) + { + data->unhandled++; + return; + } + + text = data->fetcher (href, data->fetcher_baton); + if (text == NULL) + { + data->unhandled++; + return; + } + + back_to = make_cleanup (xfree, text); + + ret = available_features_from_xml_string (&features, data->obstack, + text, data->fetcher, + data->fetcher_baton, 1); + if (ret == 0) + { + f = features; + while (f->next) + f = f->next; + f->next = data->seen_features; + data->seen_features = f; + } + else + /* Something went wrong parsing the document. */ + data->unhandled++; + + do_cleanups (back_to); +} + +static void +xml_start_feature (struct xml_feature_parse_data *data, + const XML_Char **attrs) +{ + struct gdb_available_feature *feature; + const XML_Char **p; + + feature = obstack_alloc (data->obstack, + sizeof (struct gdb_available_feature)); + memset (feature, 0, sizeof (struct gdb_available_feature)); + + feature->protocol_number = -1; + + for (p = attrs; *p; p += 2) + { + const char *name = p[0]; + const char *val = p[1]; + + if (strcmp (name, "name") == 0) + feature->name = obstrdup (data->obstack, val); + + else + data->unhandled++; + } + + /* Verify that mandatory fields were supplied. */ + if (feature->name == NULL) + { + data->unhandled++; + return; + } + + data->state->u.feature.feature = feature; +} + +static void +xml_start_reg (struct xml_feature_parse_data *data, + const XML_Char **attrs) +{ + struct gdb_available_register *reg; + const XML_Char **p; + + reg = obstack_alloc (data->obstack, + sizeof (struct gdb_available_register)); + memset (reg, 0, sizeof (struct gdb_available_register)); + + reg->bitsize = -1; + reg->readonly = -1; + reg->save_restore = -1; + + for (p = attrs; *p; p += 2) + { + const char *name = p[0]; + const char *val = p[1]; + + if (*val == 0) + data->unhandled++; + + else if (strcmp (name, "name") == 0) + reg->name = obstrdup (data->obstack, val); + + else if (strcmp (name, "bitsize") == 0) + { + if (xml_parse_one_integer (val, ®->bitsize) < 0) + data->unhandled++; + } + + else if (strcmp (name, "type") == 0) + reg->type = obstrdup (data->obstack, val); + + else if (strcmp (name, "group") == 0) + reg->group = obstrdup (data->obstack, val); + + else if (strcmp (name, "readonly") == 0) + { + if (xml_parse_one_boolean (val, ®->readonly) < 0) + data->unhandled++; + } + + else if (strcmp (name, "save-restore") == 0) + { + if (xml_parse_one_boolean (val, ®->save_restore) < 0) + data->unhandled++; + } + + else + data->unhandled++; + } + + /* Fill in optional fields with defaults. */ + /* FIXME: If we always provide the DTD, we don't need to do this. */ + if (reg->readonly == -1) + reg->readonly = 0; + if (reg->save_restore == -1) + reg->save_restore = 1; + if (reg->type == NULL) + reg->type = obstrdup (data->obstack, "int"); + + /* Verify that mandatory fields were supplied. */ + if (reg->name == NULL || reg->bitsize == -1) + { + data->unhandled++; + return; + } + + data->state->u.reg.reg = reg; +} + +static void +xml_start_feature_ref (struct xml_feature_parse_data *data, + const XML_Char **attrs) +{ + struct gdb_available_feature *feature, *new_feature; + struct gdb_available_register *reg; + const XML_Char **p; + const char *feature_name = NULL; + int i; + + for (p = attrs; *p; p += 2) + { + const char *name = p[0]; + const char *val = p[1]; + + if (strcmp (name, "name") == 0) + { + feature_name = val; + break; + } + } + + if (feature_name == NULL) + { + data->unhandled++; + return; + } + + for (feature = data->seen_features; feature; feature = feature->next) + if (strcmp (feature->name, feature_name) == 0) + break; + + /* TODO: Look for the feature elsewhere - on the target and in our + database. */ + + /* If we couldn't find the feature, stop. */ + if (feature == NULL) + { + data->unhandled++; + return; + } + + new_feature = copy_feature_to_obstack (data->obstack, feature); + + new_feature->protocol_number = -1; + + for (p = attrs; *p; p += 2) + { + const char *name = p[0]; + const char *val = p[1]; + + if (strcmp (name, "name") == 0) + continue; + else if (strcmp (name, "base-regnum") == 0) + new_feature->protocol_number = strtol (val, NULL, 0); + else + data->unhandled++; + } + + /* The protocol number is only optional if there are no registers. */ + if (new_feature->protocol_number == -1 && new_feature->registers != NULL) + { + data->unhandled++; + return; + } + + /* Set register numbers in the new feature. */ + for (i = new_feature->protocol_number, reg = new_feature->registers; + reg != NULL; + i++, reg = reg->next) + reg->protocol_number = i; + + new_feature->next = data->target_features; + data->target_features = new_feature; +} + +static void XMLCALL +xml_feature_start_element (void *data_, const XML_Char *name, + const XML_Char **attrs) +{ + struct xml_feature_parse_data *data = data_; + const XML_Char **p; + struct xml_state_stack *next_state; + enum xml_phase next_phase; + + if (data->state->phase == PHASE_UNKNOWN) + { +#if 1 + fprintf_unfiltered (gdb_stderr, "skipping, name %s\n", name); +#endif + data->state->u.unknown.depth++; + return; + } + +#if 1 + fprintf_unfiltered (gdb_stderr, "entering, name %s\n", name); + for (p = attrs; *p; p += 2) + fprintf_unfiltered (gdb_stderr, " attr %s=\"%s\"\n", p[0], p[1]); +#endif + + next_state = xmalloc (sizeof (struct xml_state_stack)); + memset (next_state, 0, sizeof (struct xml_state_stack)); + + /* Map the element we're entering to our known elements. */ + if (strcmp (name, "target") == 0) + next_phase = PHASE_IN_TARGET; + else if (strcmp (name, "feature") == 0) + next_phase = PHASE_IN_FEATURE; + else if (strcmp (name, "description") == 0) + next_phase = PHASE_IN_DESCRIPTION; + else if (strcmp (name, "reg") == 0) + next_phase = PHASE_IN_REG; + else if (strcmp (name, "feature-set") == 0) + next_phase = PHASE_IN_FEATURE_SET; + else if (strcmp (name, "feature-ref") == 0) + next_phase = PHASE_IN_FEATURE_REF; + else if (strcmp (name, "http://www.w3.org/2001/XInclude!include") == 0) + next_phase = PHASE_IN_XINCLUDE; + else + next_phase = PHASE_UNKNOWN; + + /* Make sure that the next phase is sensible. */ + switch (data->state->phase) + { + case PHASE_TOP: + if (next_phase == PHASE_IN_TARGET + && !data->state->u.top.seen_target) + { + data->state->u.top.seen_target = 1; + break; + } + if (next_phase == PHASE_IN_FEATURE + && !data->state->u.top.seen_target + && data->state->u.top.nested) + { + data->state->u.top.seen_target = 1; + break; + } + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_TARGET: + if (next_phase == PHASE_IN_FEATURE + && !data->state->u.target.seen_feature_set) + break; + if (next_phase == PHASE_IN_XINCLUDE + && !data->state->u.target.seen_feature_set) + break; + if (next_phase == PHASE_IN_FEATURE_SET + && !data->state->u.target.seen_feature_set) + { + if (data->state->u.target.features_only) + { + /* Skip parsing the feature set. This is not an + error, so counteract the increment of data->unhandled + below. */ + next_phase = PHASE_UNKNOWN; + data->unhandled--; + } + + data->state->u.target.seen_feature_set = 1; + break; + } + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_FEATURE: + if (next_phase == PHASE_IN_DESCRIPTION + && !data->state->u.feature.seen_reg) + break; + if (next_phase == PHASE_IN_REG) + { + data->state->u.feature.seen_reg = 1; + break; + } + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_DESCRIPTION: + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_REG: + if (next_phase == PHASE_IN_DESCRIPTION) + break; + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_FEATURE_SET: + if (next_phase == PHASE_IN_FEATURE_REF) + break; + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_IN_FEATURE_REF: + case PHASE_IN_XINCLUDE: + next_phase = PHASE_UNKNOWN; + break; + + case PHASE_UNKNOWN: + default: + internal_error (__FILE__, __LINE__, "unexpected current state"); + break; + } + + next_state->phase = next_phase; + next_state->prev = data->state; + data->state = next_state; + + /* FIXME: Check for unknown attributes in more elements, e.g. target. */ + /* FIXME: Check for missing mandatory elements more thoroughly. */ + + switch (next_phase) + { + case PHASE_TOP: + internal_error (__FILE__, __LINE__, "unexpected next state"); + break; + + case PHASE_IN_TARGET: + if (data->state->prev->u.top.nested) + data->state->u.target.features_only = 1; + break; + + case PHASE_IN_FEATURE: + xml_start_feature (data, attrs); + break; + + case PHASE_IN_DESCRIPTION: + /* FIXME: Currently, descriptions are ignored. */ + break; + + case PHASE_IN_REG: + xml_start_reg (data, attrs); + break; + + case PHASE_IN_FEATURE_SET: + /* Nothing needed. */ + break; + + case PHASE_IN_FEATURE_REF: + xml_start_feature_ref (data, attrs); + break; + + case PHASE_IN_XINCLUDE: + xml_start_xinclude (data, attrs); + break; + + case PHASE_UNKNOWN: + data->unhandled++; + next_state->u.unknown.depth++; + break; + + default: + internal_error (__FILE__, __LINE__, "unexpected next state"); + break; + } +} + +static void XMLCALL +xml_feature_end_element (void *data_, const XML_Char *name) +{ + struct xml_feature_parse_data *data = data_; + struct xml_state_stack *state; + struct gdb_available_feature *feature; + struct gdb_available_register *reg; + +#if 1 + fprintf_unfiltered (gdb_stderr, "leaving, name %s\n", name); +#endif + + switch (data->state->phase) + { + case PHASE_UNKNOWN: + data->state->u.unknown.depth--; + if (data->state->u.unknown.depth) + return; + break; + + case PHASE_IN_TARGET: + break; + + case PHASE_IN_FEATURE: + feature = data->state->u.feature.feature; + + /* Do nothing if a fatal error occured. */ + if (feature == NULL) + break; + + /* Record the feature. */ + feature->next = data->seen_features; + data->seen_features = feature; + + /* Reverse the list of registers. */ + if (feature->registers) + { + struct gdb_available_register *reg1, *reg2, *reg3; + + reg1 = NULL; + reg2 = feature->registers; + + while (reg2) + { + reg3 = reg2->next; + reg2->next = reg1; + reg1 = reg2; + reg2 = reg3; + } + + feature->registers = reg1; + } + + break; + + case PHASE_IN_DESCRIPTION: + /* FIXME: Currently, descriptions are ignored. */ + break; + + case PHASE_IN_REG: + gdb_assert (data->state->prev->phase == PHASE_IN_FEATURE); + + feature = data->state->prev->u.feature.feature; + reg = data->state->u.reg.reg; + + /* Do nothing if a fatal error occured, either here + or in the containing feature. */ + if (reg == NULL || feature == NULL) + break; + + reg->next = feature->registers; + feature->registers = reg; + break; + + case PHASE_IN_FEATURE_SET: + /* Reverse the list of features. */ + if (data->target_features) + { + struct gdb_available_feature *feat1, *feat2, *feat3; + + feat1 = NULL; + feat2 = data->target_features; + + while (feat2) + { + feat3 = feat2->next; + feat2->next = feat1; + feat1 = feat2; + feat2 = feat3; + } + + data->target_features = feat1; + } + break; + + case PHASE_IN_FEATURE_REF: + case PHASE_IN_XINCLUDE: + /* Nothing needed. */ + break; + + case PHASE_TOP: + default: + internal_error (__FILE__, __LINE__, "unexpected ending state"); + break; + } + + state = data->state; + data->state = data->state->prev; + xfree (state); +} + +static void +xml_parser_cleanup (void *parser) +{ + struct xml_feature_parse_data *data; + + data = XML_GetUserData (parser); + while (data->state) + { + struct xml_state_stack *prev; + + prev = data->state->prev; + xfree (data->state); + data->state = prev; + } + + XML_ParserFree (parser); +} + +static int XMLCALL +xml_feature_external_entity (XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId) +{ + struct xml_feature_parse_data *data; + XML_Parser entity_parser; + char *text; + struct cleanup *back_to; + + data = XML_GetUserData (parser); + if (data->fetcher == NULL) + return XML_STATUS_ERROR; + + text = data->fetcher (systemId, data->fetcher_baton); + if (text == NULL) + return XML_STATUS_ERROR; + + entity_parser = XML_ExternalEntityParserCreate (parser, context, NULL); + back_to = make_cleanup (xml_parser_cleanup, entity_parser); + + XML_SetElementHandler (entity_parser, NULL, NULL); + + if (XML_Parse (entity_parser, text, strlen (text), 1) != XML_STATUS_OK) + { + do_cleanups (back_to); + return XML_STATUS_ERROR; + } + + do_cleanups (back_to); + return XML_STATUS_OK; +} + +/* FIXME: Error check more XML_* calls. */ + +int +available_features_from_xml_string (struct gdb_available_feature **features_p, + struct obstack *obstack, + const char *text, xml_fetch_another fetcher, + void *fetcher_baton, + int nested) +{ + XML_Parser parser; + struct xml_feature_parse_data *data; + struct cleanup *back_to; + + parser = XML_ParserCreateNS (NULL, '!'); + if (parser == NULL) + return -1; + back_to = make_cleanup (xml_parser_cleanup, parser); + + data = XCALLOC (1, struct xml_feature_parse_data); + make_cleanup (xfree, data); + XML_SetUserData (parser, data); + + data->obstack = obstack; + data->state = XCALLOC (1, struct xml_state_stack); + data->state->phase = PHASE_TOP; + data->state->u.top.nested = nested; + + data->fetcher = fetcher; + data->fetcher_baton = fetcher_baton; + + XML_SetElementHandler (parser, xml_feature_start_element, + xml_feature_end_element); + + XML_SetParamEntityParsing (parser, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); + XML_SetExternalEntityRefHandler (parser, xml_feature_external_entity); + + /* FIXME: Enable this after we compile in the DTD? */ + /* XML_UseForeignDTD (parser, XML_TRUE); */ + + if (XML_Parse (parser, text, strlen (text), 1) != XML_STATUS_OK) + { + enum XML_Error err = XML_GetErrorCode (parser); + + warning (_("XML parsing error: %s"), XML_ErrorString (err)); + return -1; + } + + if (nested) + *features_p = data->seen_features; + else + *features_p = data->target_features; + + /* TODO: If data->unhandled, warn? */ + /* TODO: Can other errors be fatal? */ + /* TODO: Should *features_p == NULL be an error? */ + + do_cleanups (back_to); + + return 0; +} + +static void +do_cleanup_fclose (void *file) +{ + fclose (file); +} + +static char * +fetch_available_features_from_file (const char *filename, void *base_) +{ + const char *base = base_; + FILE *file; + struct cleanup *back_to; + char *text; + size_t len, offset; + + if (base && *base) + { + char *fullname = concat (base, "/", filename, NULL); + if (fullname == NULL) + nomem (0); + file = fopen (fullname, FOPEN_RT); + xfree (fullname); + } + else + file = fopen (filename, FOPEN_RT); + + if (file == NULL) + return NULL; + back_to = make_cleanup (do_cleanup_fclose, file); + + /* Read in the whole file. */ + len = 4096; + offset = 0; + text = xmalloc (len); + make_cleanup (free_current_contents, &text); + while (1) + { + size_t bytes_read; + + bytes_read = fread (text + offset, 1, len - offset, file); + if (ferror (file)) + { + warning (_("Could not read from \"%s\""), filename); + do_cleanups (back_to); + return NULL; + } + + if (feof (file)) + break; + + offset += bytes_read; + len += len; + text = xrealloc (text, len); + } + + fclose (file); + discard_cleanups (back_to); + + return text; +} + +int +available_features_from_xml_file (struct gdb_available_feature **feature_p, + struct obstack *obstack, + const char *filename) +{ + struct cleanup *back_to = make_cleanup (null_cleanup, NULL); + const char *p; + char *dirname, *text; + + text = fetch_available_features_from_file (filename, NULL); + if (text == NULL) + return -1; + + /* Simple, portable version of dirname that does not modify its + argument. */ + p = lbasename (filename); + while (p > filename && IS_DIR_SEPARATOR (p[-1])) + --p; + if (p > filename) + { + dirname = xmalloc (p - filename + 1); + memcpy (dirname, filename, p - filename); + dirname[p - filename] = '\0'; + make_cleanup (xfree, dirname); + } + else + dirname = NULL; + + available_features_from_xml_string (feature_p, obstack, text, + fetch_available_features_from_file, + dirname, 0); + + do_cleanups (back_to); + + return 0; +} + +/* For debugging. */ + +int +try_available_features_from_xml_file (const char *filename) +{ + struct obstack obstack; + struct gdb_available_feature *feature; + int ret; + + obstack_init (&obstack); + ret = available_features_from_xml_file (&feature, &obstack, filename); + obstack_free (&obstack, NULL); + + return ret; +} diff --git a/gdb/procfs.c b/gdb/procfs.c index 1fd45a3cfc3..143a63975dc 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -6122,7 +6122,8 @@ procfs_make_note_section (bfd *obfd, int *note_size) note_data = thread_args.note_data; } - auxv_len = target_auxv_read (¤t_target, &auxv); + auxv_len = target_read_whole (¤t_target, TARGET_OBJECT_AUXV, NULL, + &auxv); if (auxv_len > 0) { note_data = elfcore_write_note (obfd, note_data, note_size, diff --git a/gdb/remote.c b/gdb/remote.c index 5dc7ab43601..1a01cc0b748 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -45,6 +45,7 @@ #include "solib.h" #include "cli/cli-decode.h" #include "cli/cli-setshow.h" +#include "available.h" #include #include @@ -214,7 +215,7 @@ struct remote_state long sizeof_g_packet; /* Description of the remote protocol registers indexed by REGNUM - (making an array of NUM_REGS + NUM_PSEUDO_REGS in size). */ + (making an array NUM_REGS in size). */ struct packet_reg *regs; /* This is the size (in chars) of the first response to the ``g'' @@ -245,23 +246,29 @@ init_remote_state (struct gdbarch *gdbarch) { int regnum; struct remote_state *rs = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct remote_state); + int num_g_regs; + + if (gdbarch_remote_num_g_packet_regs_p (gdbarch)) + num_g_regs = gdbarch_remote_num_g_packet_regs (gdbarch); + else + num_g_regs = NUM_REGS; rs->sizeof_g_packet = 0; - /* Assume a 1:1 regnum<->pnum table. */ - rs->regs = GDBARCH_OBSTACK_CALLOC (gdbarch, NUM_REGS + NUM_PSEUDO_REGS, - struct packet_reg); - for (regnum = 0; regnum < NUM_REGS + NUM_PSEUDO_REGS; regnum++) + /* Assume a 1:1 regnum<->pnum table unless the available registers + interface informs us otherwise. */ + rs->regs = GDBARCH_OBSTACK_CALLOC (gdbarch, NUM_REGS, struct packet_reg); + for (regnum = 0; regnum < NUM_REGS; regnum++) { struct packet_reg *r = &rs->regs[regnum]; - r->pnum = regnum; + r->pnum = available_register_target_regnum (gdbarch, regnum); r->regnum = regnum; r->offset = DEPRECATED_REGISTER_BYTE (regnum); - r->in_g_packet = (regnum < NUM_REGS); + r->in_g_packet = (regnum < num_g_regs); /* ...name = REGISTER_NAME (regnum); */ /* Compute packet size by accumulating the size of all registers. */ - if (regnum < NUM_REGS) + if (r->in_g_packet) rs->sizeof_g_packet += register_size (current_gdbarch, regnum); } @@ -292,7 +299,7 @@ init_remote_state (struct gdbarch *gdbarch) static struct packet_reg * packet_reg_from_regnum (struct remote_state *rs, long regnum) { - if (regnum < 0 && regnum >= NUM_REGS + NUM_PSEUDO_REGS) + if (regnum < 0 && regnum >= NUM_REGS) return NULL; else { @@ -306,7 +313,7 @@ static struct packet_reg * packet_reg_from_pnum (struct remote_state *rs, LONGEST pnum) { int i; - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->pnum == pnum) @@ -741,6 +748,7 @@ enum { PACKET_Z3, PACKET_Z4, PACKET_qPart_auxv, + PACKET_qPart_features, PACKET_qGetTLSAddr, PACKET_MAX }; @@ -3031,60 +3039,64 @@ got_status: return inferior_ptid; } -/* Number of bytes of registers this stub implements. */ - -static int register_bytes_found; - -/* Read the remote registers into the block REGS. */ -/* Currently we just read all the registers, so we don't use regnum. */ +/* Fetch a single register using a 'p' packet. */ static int -fetch_register_using_p (int regnum) +fetch_register_using_p (struct packet_reg *reg) { struct remote_state *rs = get_remote_state (); char *buf = alloca (rs->remote_packet_size), *p; char regp[MAX_REGISTER_SIZE]; int i; + if (remote_protocol_packets[PACKET_p].support == PACKET_DISABLE) + return 0; + p = buf; *p++ = 'p'; - p += hexnumstr (p, regnum); + p += hexnumstr (p, reg->pnum); *p++ = '\0'; remote_send (buf, rs->remote_packet_size); - /* If the stub didn't recognize the packet, or if we got an error, - tell our caller. */ - if (buf[0] == '\0' || buf[0] == 'E') - return 0; + switch (packet_ok (buf, &remote_protocol_packets[PACKET_p])) + { + case PACKET_OK: + break; + case PACKET_UNKNOWN: + return 0; + case PACKET_ERROR: + error (_("Could not fetch register \"%s\""), + gdbarch_register_name (current_gdbarch, reg->regnum)); + } - /* If this register is unfetchable, tell the regcache. */ if (buf[0] == 'x') { - regcache_raw_supply (current_regcache, regnum, NULL); - set_register_cached (regnum, -1); + regcache_raw_supply (current_regcache, reg->regnum, NULL); + set_register_cached (reg->regnum, -1); return 1; } - /* Otherwise, parse and supply the value. */ p = buf; i = 0; while (p[0] != 0) { if (p[1] == 0) - { - error (_("fetch_register_using_p: early buf termination")); - return 0; - } - + error (_("fetch_register_using_p: early buf termination")); regp[i++] = fromhex (p[0]) * 16 + fromhex (p[1]); p += 2; } - regcache_raw_supply (current_regcache, regnum, regp); + regcache_raw_supply (current_regcache, reg->regnum, regp); return 1; } +/* Number of bytes of registers this stub implements. */ + +static int register_bytes_found; + +/* Fetch the registers included in the target's 'g' packet. */ + static void -remote_fetch_registers (int regnum) +fetch_registers_using_g (void) { struct remote_state *rs = get_remote_state (); char *buf = alloca (rs->remote_packet_size); @@ -3094,41 +3106,6 @@ remote_fetch_registers (int regnum) set_thread (PIDGET (inferior_ptid), 1); - if (regnum >= 0) - { - struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); - gdb_assert (reg != NULL); - if (!reg->in_g_packet) - internal_error (__FILE__, __LINE__, - _("Attempt to fetch a non G-packet register when this " - "remote.c does not support the p-packet.")); - } - switch (remote_protocol_packets[PACKET_p].support) - { - case PACKET_DISABLE: - break; - case PACKET_ENABLE: - if (fetch_register_using_p (regnum)) - return; - else - error (_("Protocol error: p packet not recognized by stub")); - case PACKET_SUPPORT_UNKNOWN: - if (fetch_register_using_p (regnum)) - { - /* The stub recognized the 'p' packet. Remember this. */ - remote_protocol_packets[PACKET_p].support = PACKET_ENABLE; - return; - } - else - { - /* The stub does not support the 'P' packet. Use 'G' - instead, and don't try using 'P' in the future (it - will just waste our time). */ - remote_protocol_packets[PACKET_p].support = PACKET_DISABLE; - break; - } - } - sprintf (buf, "g"); remote_send (buf, rs->remote_packet_size); @@ -3189,7 +3166,7 @@ remote_fetch_registers (int regnum) supply_them: { int i; - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->in_g_packet) @@ -3216,6 +3193,39 @@ remote_fetch_registers (int regnum) } } +static void +remote_fetch_registers (int regnum) +{ + struct remote_state *rs = get_remote_state (); + int i; + + set_thread (PIDGET (inferior_ptid), 1); + + if (regnum >= 0) + { + struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); + gdb_assert (reg != NULL); + + if (fetch_register_using_p (reg)) + return; + + if (!reg->in_g_packet) + error (_("Protocol error: register \"%s\" not supported by stub"), + gdbarch_register_name (current_gdbarch, reg->regnum)); + + fetch_registers_using_g (); + return; + } + + fetch_registers_using_g (); + + for (i = 0; i < NUM_REGS; i++) + if (!rs->regs[i].in_g_packet) + if (!fetch_register_using_p (&rs->regs[i])) + error (_("Protocol error: register \"%s\" not supported by stub"), + gdbarch_register_name (current_gdbarch, rs->regs[i].regnum)); +} + /* Prepare to store registers. Since we may send them all (using a 'G' request), we have to read out the ones we don't want to change first. */ @@ -3246,74 +3256,55 @@ remote_prepare_to_store (void) packet was not recognized. */ static int -store_register_using_P (int regnum) +store_register_using_P (struct packet_reg *reg) { struct remote_state *rs = get_remote_state (); - struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); /* Try storing a single register. */ char *buf = alloca (rs->remote_packet_size); gdb_byte regp[MAX_REGISTER_SIZE]; char *p; + if (remote_protocol_packets[PACKET_P].support == PACKET_DISABLE) + return 0; + xsnprintf (buf, rs->remote_packet_size, "P%s=", phex_nz (reg->pnum, 0)); p = buf + strlen (buf); regcache_raw_collect (current_regcache, reg->regnum, regp); bin2hex (regp, p, register_size (current_gdbarch, reg->regnum)); remote_send (buf, rs->remote_packet_size); - return buf[0] != '\0'; + switch (packet_ok (buf, &remote_protocol_packets[PACKET_P])) + { + case PACKET_OK: + return 1; + case PACKET_ERROR: + error (_("Could not write register \"%s\""), + gdbarch_register_name (current_gdbarch, reg->regnum)); + case PACKET_UNKNOWN: + return 0; + default: + internal_error (__FILE__, __LINE__, _("Bad result from packet_ok")); + } } - /* Store register REGNUM, or all registers if REGNUM == -1, from the contents of the register cache buffer. FIXME: ignores errors. */ static void -remote_store_registers (int regnum) +store_registers_using_G () { struct remote_state *rs = get_remote_state (); char *buf; gdb_byte *regs; char *p; - set_thread (PIDGET (inferior_ptid), 1); - - if (regnum >= 0) - { - switch (remote_protocol_packets[PACKET_P].support) - { - case PACKET_DISABLE: - break; - case PACKET_ENABLE: - if (store_register_using_P (regnum)) - return; - else - error (_("Protocol error: P packet not recognized by stub")); - case PACKET_SUPPORT_UNKNOWN: - if (store_register_using_P (regnum)) - { - /* The stub recognized the 'P' packet. Remember this. */ - remote_protocol_packets[PACKET_P].support = PACKET_ENABLE; - return; - } - else - { - /* The stub does not support the 'P' packet. Use 'G' - instead, and don't try using 'P' in the future (it - will just waste our time). */ - remote_protocol_packets[PACKET_P].support = PACKET_DISABLE; - break; - } - } - } - /* Extract all the registers in the regcache copying them into a local buffer. */ { int i; regs = alloca (rs->sizeof_g_packet); memset (regs, 0, rs->sizeof_g_packet); - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->in_g_packet) @@ -3330,6 +3321,42 @@ remote_store_registers (int regnum) bin2hex (regs, p, register_bytes_found); remote_send (buf, rs->remote_packet_size); } + +/* Store register REGNUM, or all registers if REGNUM == -1, from the contents + of the register cache buffer. FIXME: ignores errors. */ + +static void +remote_store_registers (int regnum) +{ + struct remote_state *rs = get_remote_state (); + int i; + + set_thread (PIDGET (inferior_ptid), 1); + + if (regnum >= 0) + { + struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); + gdb_assert (reg != NULL); + + if (store_register_using_P (reg)) + return; + + if (!reg->in_g_packet) + error (_("Protocol error: register \"%s\" not supported by stub"), + gdbarch_register_name (current_gdbarch, reg->regnum)); + + store_registers_using_G (); + return; + } + + store_registers_using_G (); + + for (i = 0; i < NUM_REGS; i++) + if (!rs->regs[i].in_g_packet) + if (!store_register_using_P (&rs->regs[i])) + error (_("Protocol error: register \"%s\" not supported by stub"), + gdbarch_register_name (current_gdbarch, rs->regs[i].regnum)); +} /* Return the number of hex digits in num. */ @@ -4763,6 +4790,49 @@ the loaded file\n")); printf_filtered (_("No loaded section named '%s'.\n"), args); } +/* Read OBJECT_NAME/ANNEX from the remote target using a qPart packet. + Data at OFFSET, of up to LEN bytes, is read into READBUF; the + number of bytes read is returned, or 0 for EOF, or -1 for error. + The number of bytes read may be less than LEN without indicating an + EOF. PACKET is checked and updated to indicate whether the remote + target supports this object. */ + +static LONGEST +remote_read_qpart (struct target_ops *ops, const char *object_name, + const char *annex, + gdb_byte *readbuf, ULONGEST offset, LONGEST len, + struct packet_config *packet) +{ + struct remote_state *rs = get_remote_state (); + char *buf2 = alloca (rs->remote_packet_size); + unsigned int total = 0; + LONGEST i, n; + + if (packet->support == PACKET_DISABLE) + return -1; + + n = min ((rs->remote_packet_size - 2) / 2, len); + snprintf (buf2, rs->remote_packet_size, "qPart:%s:read:%s:%s,%s", + object_name, annex ? annex : "", + phex_nz (offset, sizeof offset), + phex_nz (n, sizeof n)); + i = putpkt (buf2); + if (i < 0) + return -1; + + buf2[0] = '\0'; + getpkt (buf2, rs->remote_packet_size, 0); + if (packet_ok (buf2, packet) != PACKET_OK) + return -1; + + if (buf2[0] == 'O' && buf2[1] == 'K' && buf2[2] == '\0') + return 0; /* Got EOF indicator. */ + + /* Got some data. */ + i = hex2bin (buf2, readbuf, len); + return i; +} + static LONGEST remote_xfer_partial (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *readbuf, @@ -4770,8 +4840,7 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object, { struct remote_state *rs = get_remote_state (); int i; - char *buf2 = alloca (rs->remote_packet_size); - char *p2 = &buf2[0]; + char *buf2, *p2; char query_type; /* Handle memory using remote_xfer_memory. */ @@ -4815,39 +4884,13 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object, break; case TARGET_OBJECT_AUXV: - if (remote_protocol_packets[PACKET_qPart_auxv].support != PACKET_DISABLE) - { - unsigned int total = 0; - while (len > 0) - { - LONGEST n = min ((rs->remote_packet_size - 2) / 2, len); - snprintf (buf2, rs->remote_packet_size, - "qPart:auxv:read::%s,%s", - phex_nz (offset, sizeof offset), - phex_nz (n, sizeof n)); - i = putpkt (buf2); - if (i < 0) - return total > 0 ? total : i; - buf2[0] = '\0'; - getpkt (buf2, rs->remote_packet_size, 0); - if (packet_ok (buf2, &remote_protocol_packets[PACKET_qPart_auxv]) - != PACKET_OK) - return total > 0 ? total : -1; - if (buf2[0] == 'O' && buf2[1] == 'K' && buf2[2] == '\0') - break; /* Got EOF indicator. */ - /* Got some data. */ - i = hex2bin (buf2, readbuf, len); - if (i > 0) - { - readbuf = (void *) ((char *) readbuf + i); - offset += i; - len -= i; - total += i; - } - } - return total; - } - return -1; + gdb_assert (annex == NULL); + return remote_read_qpart (ops, "auxv", annex, readbuf, offset, len, + &remote_protocol_packets[PACKET_qPart_auxv]); + + case TARGET_OBJECT_AVAILABLE_FEATURES: + return remote_read_qpart (ops, "features", annex, readbuf, offset, len, + &remote_protocol_packets[PACKET_qPart_features]); default: return -1; @@ -4870,6 +4913,9 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object, gdb_assert (annex != NULL); gdb_assert (readbuf != NULL); + buf2 = alloca (rs->remote_packet_size); + p2 = &buf2[0]; + *p2++ = 'q'; *p2++ = query_type; @@ -5221,6 +5267,7 @@ Specify the serial device it is connected to\n\ remote_ops.to_xfer_partial = remote_xfer_partial; remote_ops.to_rcmd = remote_rcmd; remote_ops.to_get_thread_local_address = remote_get_thread_local_address; + remote_ops.to_available_features = available_features_from_target_object; remote_ops.to_stratum = process_stratum; remote_ops.to_has_all_memory = 1; remote_ops.to_has_memory = 1; @@ -5346,6 +5393,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya)."; remote_async_ops.to_stop = remote_stop; remote_async_ops.to_xfer_partial = remote_xfer_partial; remote_async_ops.to_rcmd = remote_rcmd; + remote_async_ops.to_available_features + = available_features_from_target_object; remote_async_ops.to_stratum = process_stratum; remote_async_ops.to_has_all_memory = 1; remote_async_ops.to_has_memory = 1; @@ -5635,6 +5684,13 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, &remote_set_cmdlist, &remote_show_cmdlist, 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qPart_features], + "qPart_features", "target-features", + set_remote_protocol_packet_cmd, + show_remote_protocol_packet_cmd, + &remote_set_cmdlist, &remote_show_cmdlist, + 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr], "qGetTLSAddr", "get-thread-local-storage-address", set_remote_protocol_packet_cmd, diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c index dc6c5347a55..aacdaa652e1 100644 --- a/gdb/sparc-tdep.c +++ b/gdb/sparc-tdep.c @@ -157,7 +157,7 @@ sparc_fetch_wcookie (void) gdb_byte buf[8]; int len; - len = target_read_partial (ops, TARGET_OBJECT_WCOOKIE, NULL, buf, 0, 8); + len = target_read (ops, TARGET_OBJECT_WCOOKIE, NULL, buf, 0, 8); if (len == -1) return 0; diff --git a/gdb/symfile.h b/gdb/symfile.h index 0cbd1fc5764..10361e795d9 100644 --- a/gdb/symfile.h +++ b/gdb/symfile.h @@ -25,6 +25,8 @@ /* This file requires that you first include "bfd.h". */ +#include "symtab.h" + /* Opaque declarations. */ struct section_table; struct objfile; diff --git a/gdb/target.c b/gdb/target.c index 3da3e651311..80b7b711142 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -457,6 +457,7 @@ update_current_target (void) INHERIT (to_find_memory_regions, t); INHERIT (to_make_corefile_notes, t); INHERIT (to_get_thread_local_address, t); + /* Do not inherit to_available_features. */ INHERIT (to_magic, t); } #undef INHERIT @@ -634,6 +635,7 @@ update_current_target (void) de_fault (to_async, (void (*) (void (*) (enum inferior_event_type, void*), void*)) tcomplain); + current_target.to_available_features = NULL; #undef de_fault /* Finally, position the target-stack beneath the squashed @@ -1378,8 +1380,9 @@ target_read (struct target_ops *ops, (gdb_byte *) buf + xfered, offset + xfered, len - xfered); /* Call an observer, notifying them of the xfer progress? */ - if (xfer <= 0) - /* Call memory_error? */ + if (xfer == 0) + return xfered; + if (xfer < 0) return -1; xfered += xfer; QUIT; @@ -1400,8 +1403,9 @@ target_write (struct target_ops *ops, (gdb_byte *) buf + xfered, offset + xfered, len - xfered); /* Call an observer, notifying them of the xfer progress? */ - if (xfer <= 0) - /* Call memory_error? */ + if (xfer == 0) + return xfered; + if (xfer < 0) return -1; xfered += xfer; QUIT; @@ -1409,6 +1413,45 @@ target_write (struct target_ops *ops, return len; } +/* Perform a full target read of unknown size. */ + +LONGEST +target_read_whole (struct target_ops *ops, + enum target_object object, + const char *annex, gdb_byte **buf_p) +{ + size_t buf_alloc = 512, buf_pos = 0; + gdb_byte *buf = xmalloc (buf_alloc); + LONGEST n, total; + + total = 0; + while (1) + { + n = target_read (ops, object, annex, &buf[buf_pos], + buf_pos, buf_alloc - buf_pos); + if (n < 0) + { + /* An error occurred. */ + xfree (buf); + return -1; + } + + buf_pos += n; + if (buf_pos < buf_alloc) + { + /* Read all there was. */ + if (buf_pos == 0) + xfree (buf); + else + *buf_p = buf; + return buf_pos; + } + + buf_alloc *= 2; + buf = xrealloc (buf, buf_alloc); + } +} + /* Memory transfer methods. */ void @@ -1526,6 +1569,28 @@ target_follow_fork (int follow_child) "could not find a target to follow fork"); } +/* Look for a target which can report architectural features, starting + from TARGET. If we find one, return its features, using OBSTACK + for any temporary allocation. */ + +struct gdb_feature_set * +target_available_features (struct target_ops *target, struct obstack *obstack) +{ + struct target_ops *t; + + for (t = target; t != NULL; t = t->beneath) + if (t->to_available_features != NULL) + { + struct gdb_feature_set *features; + + features = t->to_available_features (t, obstack); + if (features) + return features; + } + + return NULL; +} + /* Look through the list of possible targets for a target that can execute a run or attach command without any other data. This is used to locate the default process stratum. diff --git a/gdb/target.h b/gdb/target.h index b804b05df43..76d53d81c1a 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -30,6 +30,7 @@ struct objfile; struct ui_file; struct mem_attrib; struct target_ops; +struct gdb_feature_set; /* This include file defines the interface between the main part of the debugger, and the part which is target-specific, or @@ -230,7 +231,12 @@ enum target_object /* Transfer auxilliary vector. */ TARGET_OBJECT_AUXV, /* StackGhost cookie. See "sparc-tdep.c". */ - TARGET_OBJECT_WCOOKIE + TARGET_OBJECT_WCOOKIE, + /* Available target-specific features, e.g. registers and coprocessors. + See "available.c". With an empty ANNEX this fetches a list of + features; with a comma-separated list of features in ANNEX this + fetches the details of the listed features. */ + TARGET_OBJECT_AVAILABLE_FEATURES /* Possible future objects: TARGET_OBJECT_FILE, TARGET_OBJECT_PROC, ... */ }; @@ -245,7 +251,10 @@ extern LONGEST target_write_partial (struct target_ops *ops, const char *annex, const gdb_byte *buf, ULONGEST offset, LONGEST len); -/* Wrappers to perform the full transfer. */ +/* Wrappers to perform a full transfer. These functions take the + same arguments as the partial versions, above, but a return + value of a positive number less than LEN implies that no more + data can be read or written. */ extern LONGEST target_read (struct target_ops *ops, enum target_object object, const char *annex, gdb_byte *buf, @@ -256,6 +265,22 @@ extern LONGEST target_write (struct target_ops *ops, const char *annex, const gdb_byte *buf, ULONGEST offset, LONGEST len); +/* Wrappers to perform a full read of unknown size. OBJECT/ANNEX will + be read using OPS. The return value will be -1 if the transfer + fails or is not supported; 0 if the object is empty; and the length + of the object otherwise. If a positive value is returned, a + sufficiently large buffer will be allocated using xmalloc and + returned in *BUF_P containing the contents of the object. + + This method should be used for objects sufficiently small to store + in a single xmalloced buffer, when no fixed bound on the object's + size is known in advance. Don't try to read TARGET_OBJECT_MEMORY + through this function. */ + +extern LONGEST target_read_whole (struct target_ops *ops, + enum target_object object, + const char *annex, gdb_byte **buf_p); + /* Wrappers to target read/write that perform memory transfers. They throw an error if the memory transfer fails. @@ -423,6 +448,13 @@ struct target_ops gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len); + /* Describe the architecture-specific features of this target. + Returns the features found. All pointers refer either to + constants or temporary memory allocated on the provided + obstack. */ + struct gdb_feature_set *(*to_available_features) (struct target_ops *ops, + struct obstack *obstack); + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ @@ -1074,6 +1106,9 @@ extern int target_stopped_data_address_p (struct target_ops *); #define target_stopped_data_address_p(CURRENT_TARGET) (1) #endif +extern struct gdb_feature_set *target_available_features (struct target_ops *, + struct obstack *); + /* This will only be defined by a target that supports catching vfork events, such as HP-UX.