diff options
author | gdb-3.1 <gdb@fsf.org> | 1989-01-31 17:56:40 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2012-06-03 15:36:31 +0100 |
commit | e91b87a36830d061ef87d67be5f309e4d4ed918f (patch) | |
tree | 3408ea913a9cccd51c9b7d0b3bc7d7897cac8a5b /gdb/RCS | |
parent | bb7592f01006b09c846831a9fb9c306307ba34f6 (diff) | |
download | gdb-e91b87a36830d061ef87d67be5f309e4d4ed918f.zip gdb-e91b87a36830d061ef87d67be5f309e4d4ed918f.tar.gz gdb-e91b87a36830d061ef87d67be5f309e4d4ed918f.tar.bz2 |
gdb-3.1
Diffstat (limited to 'gdb/RCS')
-rw-r--r-- | gdb/RCS/Makefile,v | 367 | ||||
-rw-r--r-- | gdb/RCS/blockframe.c,v | 606 | ||||
-rw-r--r-- | gdb/RCS/coffread.c,v | 2047 | ||||
-rwxr-xr-x | gdb/RCS/config.gdb,v | 229 | ||||
-rw-r--r-- | gdb/RCS/core.c,v | 651 | ||||
-rw-r--r-- | gdb/RCS/dbxread.c,v | 4610 | ||||
-rw-r--r-- | gdb/RCS/default-dep.c,v | 731 | ||||
-rw-r--r-- | gdb/RCS/findvar.c,v | 584 | ||||
-rw-r--r-- | gdb/RCS/gdb.texinfo,v | 3009 | ||||
-rw-r--r-- | gdb/RCS/gdbcore.h,v | 105 | ||||
-rw-r--r-- | gdb/RCS/inflow.c,v | 636 | ||||
-rw-r--r-- | gdb/RCS/infrun.c,v | 1855 | ||||
-rw-r--r-- | gdb/RCS/m-aux.h,v | 591 | ||||
-rw-r--r-- | gdb/RCS/m-hp9k320.h,v | 607 | ||||
-rw-r--r-- | gdb/RCS/m-sparc.h,v | 747 | ||||
-rw-r--r-- | gdb/RCS/m68k-pinsn.c,v | 824 | ||||
-rw-r--r-- | gdb/RCS/main.c,v | 1348 | ||||
-rwxr-xr-x | gdb/RCS/munch,v | 75 | ||||
-rw-r--r-- | gdb/RCS/printcmd.c,v | 1707 | ||||
-rw-r--r-- | gdb/RCS/remote.c,v | 662 | ||||
-rw-r--r-- | gdb/RCS/source.c,v | 990 | ||||
-rw-r--r-- | gdb/RCS/sparc-dep.c,v | 1091 | ||||
-rw-r--r-- | gdb/RCS/stack.c,v | 882 | ||||
-rw-r--r-- | gdb/RCS/utils.c,v | 726 | ||||
-rw-r--r-- | gdb/RCS/valprint.c,v | 1117 | ||||
-rw-r--r-- | gdb/RCS/values.c,v | 1047 |
26 files changed, 27844 insertions, 0 deletions
diff --git a/gdb/RCS/Makefile,v b/gdb/RCS/Makefile,v new file mode 100644 index 0000000..fd71c1c --- /dev/null +++ b/gdb/RCS/Makefile,v @@ -0,0 +1,367 @@ +head 1.4; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.4 +date 89.03.27.21.28.33; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 89.03.27.18.33.31; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.03.13.19.02.58; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.03.15.53; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.4 +log +@More general support for ALLOCA and other local library routines; +other minor cleanup. +@ +text +@# On HPUX, you need to add -Ihp-include to CFLAGS. +# The headers in the directory hp-include override system headers +# and tell GDB to use BSD executable file format. +# You must also define REGEX & REGEX1 below and ALLOCA & ALLOCA1 (get +# alloca.c from the emacs distribution) to the CLIBS. +# If you compile GDB with GCC on HPUX, you must make sure that the "nm" used +# in "munch" is GNU's nm. This is because gcc uses a different .o +# file format than the native HPUX compiler. + +# On USG (System V) machines, you must make sure to setup REGEX & +# REGEX1 to point at regex.o and use the USG version of CLIBS. +# If your system has a broken alloca() -- most do -- then get +# alloca.c from the GNU Emacs distribution and set ALLOCA & ALLOCA1. +# Also, if you compile gdb with a compiler which uses the coff +# encapsulation feature (this is a function of the compiler used, NOT +# of the m-?.h file selected by config.gdb), you must make sure that +# the GNU nm is the one that is used by munch. + +# On Sunos 4.0 machines, make sure to compile *without* shared +# libraries if you want to run gdb on itself. Make sure to compile +# any program on which you want to run gdb without shared libraries. + +# If you are compiling with GCC, make sure that either 1) You use the +# -traditional flag, or 2) You have the fixed include files where GCC +# can reach them. Otherwise the ioctl calls in inflow.c will be +# incorrectly compiled. The "fixincludes" script in the gcc +# distribution will probably fix your include files up. + +CC=cc +SHELL=/bin/sh + +# Set this up with gcc if you have gnu ld and the loader will print out +# line numbers for undefinded refs. +CC-LD=${CC} + +# -I. for "#include <obstack.h>". Possibly regex.h also. +#CFLAGS = -g -pg -I. -O +CFLAGS = -I. -g +#LDFLAGS = -pg -g +LDFLAGS = -g + +# define this to be "obstack.o" if you don't have the obstack library installed +# you must at the same time define OBSTACK1 as "obstack.o" +# so that the dependencies work right. Similarly with REGEX and "regex.o". +# You must define REGEX and REGEX1 on USG machines. +# If your system is missing alloca(), or, more likely, it's there but it +# doesn't work, define ALLOCA and ALLOCA1. +OBSTACK = obstack.o +OBSTACK1 = obstack.o +REGEX = regex.o +REGEX1 = regex.o +ALLOCA = alloca.o +ALLOCA1 = alloca.o +ADD_FILES = $(OBSTACK) $(REGEX) $(ALLOCA) $(GNU_MALLOC) +ADD_DEPS = $(OBSTACK1) $(REGEX1) $(ALLOCA1) $(GNU_MALLOC) + +# +# define this to be "malloc.o" if you want to use the gnu malloc routine +# (useful for debugging memory allocation problems in gdb). Otherwise, leave +# it blank. +GNU_MALLOC = +#GNU_MALLOC = malloc.o + +# Flags to be used in compiling malloc.o +# Specify range checking for storage allocation. +MALLOC_FLAGS = +#MALLOC_FLAGS = ${CFLAGS} -Drcheck -Dbotch=fatal -DMSTATS + +# for BSD +CLIBS = $(ADD_FILES) +# for USG +#CLIBS= $(ADD_FILES) -lPW + +SFILES = blockframe.c breakpoint.c coffread.c command.c core.c dbxread.c \ + environ.c eval.c expprint.c findvar.c infcmd.c inflow.c infrun.c \ + kdb-start.c main.c printcmd.c \ + remote.c source.c stack.c standalone.c stuff.c symmisc.c symtab.c \ + utils.c valarith.c valops.c valprint.c values.c version.c expread.y \ + xgdb.c + +DEPFILES = convex-dep.c umax-dep.c gould-dep.c default-dep.c sun3-dep.c \ + sparc-dep.c hp9k320-dep.c news-dep.c i386-dep.c + +PINSNS = gld-pinsn.c i386-pinsn.c sparc-pinsn.c vax-pinsn.c m68k-pinsn.c \ + ns32k-pinsn.c + +HFILES = command.h defs.h environ.h expression.h frame.h getpagesize.h \ + inferior.h symseg.h symtab.h value.h wait.h \ + a.out.encap.h a.out.gnu.h stab.gnu.h + +OPCODES = m68k-opcode.h pn-opcode.h sparc-opcode.h npl-opcode.h vax-opcode.h \ + ns32k-opcode.h + +MFILES = m-hp9k320.h m-i386.h m-i386gas.h m-isi.h m-merlin.h m-news.h \ + m-npl.h m-pn.h m-sparc.h m-sun2.h m-sun3.h m-sun2os4.h \ + m-sun3os4.h m-sun4os4.h m-umax.h m-vax.h + +POSSLIBS = obstack.h obstack.c regex.c regex.h malloc.c + +TESTS = testbpt.c testfun.c testrec.c testreg.c testregs.c + +OTHERS = Makefile createtags munch config.gdb ChangeLog README TAGS \ + gdb.texinfo .gdbinit COPYING expread.tab.c stab.def hp-include + +TAGFILES = ${SFILES} ${DEPFILES} ${PINSNS} ${HFILES} ${OPCODES} ${MFILES} \ + ${POSSLIBS} +TARFILES = ${TAGFILES} ${OTHERS} + +OBS = main.o blockframe.o breakpoint.o findvar.o stack.o source.o \ + values.o eval.o valops.o valarith.o valprint.o printcmd.o \ + symtab.o symmisc.o coffread.o dbxread.o infcmd.o infrun.o remote.o \ + command.o utils.o expread.o expprint.o pinsn.o environ.o version.o + +TSOBS = core.o inflow.o dep.o + +NTSOBS = standalone.o + +TSSTART = /lib/crt0.o + +NTSSTART = kdb-start.o + +gdb : $(OBS) $(TSOBS) $(ADD_DEPS) + -rm -f init.c + ./munch $(OBS) $(TSOBS) > init.c + ${CC-LD} $(LDFLAGS) -o gdb init.c $(OBS) $(TSOBS) $(CLIBS) + +xgdb : $(OBS) $(TSOBS) xgdb.o $(ADD_DEPS) + -rm -f init.c + ./munch $(OBS) $(TSOBS) xgdb.o > init.c + $(CC-LD) $(LDFLAGS) -o xgdb init.c $(OBS) $(TSOBS) xgdb.o \ + -lXaw -lXt -lX11 $(CLIBS) + +kdb : $(NTSSTART) $(OBS) $(NTSOBS) $(ADD_DEPS) + -rm -f init.c + ./munch $(OBS) $(NTSOBS) > init.c + $(CC-LD) $(LDFLAGS) -c init.c $(CLIBS) + ld -o kdb $(NTSSTART) $(OBS) $(NTSOBS) init.o -lc $(CLIBS) + +# If it can figure out the appropriate order, createtags will make sure +# that the proper m-*, *-dep, *-pinsn, and *-opcode files come first +# in the tags list. It will attempt to do the same for dbxread.c and +# coffread.c. This makes using M-. on machine dependent routines much +# easier. +# +TAGS: ${TAGFILES} + createtags ${TAGFILES} +tags: TAGS + +gdb.tar: ${TARFILES} + rm -f gdb.tar + mkdir dist-gdb + cd dist-gdb ; for i in ${TARFILES} ; do ln -s ../$$i . ; done + tar chf gdb.tar dist-gdb + rm -rf dist-gdb + +gdb.tar.Z: gdb.tar + compress gdb.tar + +clean: + -rm -f ${OBS} ${TSOBS} ${NTSOBS} ${OBSTACK} ${REGEX} + -rm -f init.c init.o + -rm -f gdb + +realclean: clean + -rm -f expread.tab.c tags TAGS + +xgdb.o : xgdb.c defs.h param.h symtab.h frame.h + $(CC) -c $(CFLAGS) xgdb.c -o $@@ + +expread.tab.c : expread.y + @@echo 'Expect 101 shift/reduce conflicts and 1 reduce/reduce conflict.' + yacc expread.y + mv y.tab.c expread.tab.c + +expread.o : expread.tab.c defs.h param.h symtab.h frame.h expression.h + $(CC) -c ${CFLAGS} expread.tab.c + mv expread.tab.o expread.o + +# +# Only useful if you are using the gnu malloc routines. +# +malloc.o : malloc.c + ${CC} -c ${MALLOC_FLAGS} malloc.c + +# +# dep.o depends on ALL the dep files since we don't know which one +# is really being used. +# +dep.o : ${DEPFILES} defs.h param.h frame.h inferior.h obstack.h \ + a.out.encap.h + +# pinsn.o depends on ALL the opcode printers +# since we don't know which one is really being used. +pinsn.o : ${PINSNS} defs.h param.h symtab.h obstack.h symseg.h frame.h \ + ${OPCODES} + +# +# The rest of this is a standard dependencies list (hand edited output of +# cpp -M). It does not include dependencies of .o files on .c files. +# +blockframe.o : defs.h param.h symtab.h obstack.h symseg.h frame.h +breakpoint.o : defs.h param.h symtab.h obstack.h symseg.h frame.h +coffread.o : defs.h param.h +command.o : command.h defs.h +core.o : defs.h param.h a.out.encap.h +dbxread.o : param.h defs.h symtab.h obstack.h symseg.h a.out.encap.h \ + stab.gnu.h +environ.o : environ.h +eval.o : defs.h param.h symtab.h obstack.h symseg.h value.h expression.h +expprint.o : defs.h symtab.h obstack.h symseg.h param.h expression.h +findvar.o : defs.h param.h symtab.h obstack.h symseg.h frame.h value.h +infcmd.o : defs.h param.h symtab.h obstack.h symseg.h frame.h inferior.h \ + environ.h value.h +inflow.o : defs.h param.h frame.h inferior.h +infrun.o : defs.h param.h symtab.h obstack.h symseg.h frame.h inferior.h \ + wait.h +kdb-start.o : defs.h param.h +main.o : defs.h command.h param.h +malloc.o : getpagesize.h +obstack.o : obstack.h +printcmd.o : defs.h param.h frame.h symtab.h obstack.h symseg.h value.h \ + expression.h +regex.o : regex.h +remote.o : defs.h param.h frame.h inferior.h wait.h +source.o : defs.h symtab.h obstack.h symseg.h param.h +stack.o : defs.h param.h symtab.h obstack.h symseg.h frame.h +standalone.o : defs.h param.h symtab.h obstack.h symseg.h frame.h \ + inferior.h wait.h +symmisc.o : defs.h symtab.h obstack.h symseg.h obstack.h +symtab.o : defs.h symtab.h obstack.h symseg.h param.h obstack.h +utils.o : defs.h param.h +valarith.o : defs.h param.h symtab.h obstack.h symseg.h value.h expression.h +valops.o : defs.h param.h symtab.h obstack.h symseg.h value.h frame.h \ + inferior.h +valprint.o : defs.h param.h symtab.h obstack.h symseg.h value.h +values.o : defs.h param.h symtab.h obstack.h symseg.h value.h + +robotussin.h : getpagesize.h +symtab.h : obstack.h symseg.h +a.out.encap.h : a.out.gnu.h +@ + + +1.3 +log +@A/UX changes. Use cc, use local regex.o, use local alloca.o +@ +text +@d4 2 +a5 2 +# You must also define REGEX & REGEX1 below and add alloca.o (from +# the emacs distribution) to the CLIBS. +d12 2 +d26 2 +a27 1 +# incorrectly compiled. +d46 2 +d52 4 +d70 1 +a70 1 +#CLIBS = $(OBSTACK) $(REGEX) $(GNU_MALLOC) +d72 1 +a72 1 +CLIBS= $(OBSTACK) $(REGEX) $(GNU_MALLOC) alloca.o +d122 1 +a122 1 +gdb : $(OBS) $(TSOBS) $(OBSTACK1) $(REGEX1) ${GNU_MALLOC} +d127 1 +a127 1 +xgdb : $(OBS) $(TSOBS) xgdb.o $(OBSTACK1) $(REGEX1) ${GNU_MALLOC} +d133 1 +a133 1 +kdb : $(NTSSTART) $(OBS) $(NTSOBS) $(OBSTACK1) $(REGEX1) ${GNU_MALLOC} +d161 1 +a161 1 + -rm -f init.c +d165 1 +a165 1 + -rm -f expread.tab.c +@ + + +1.2 +log +@All rm's to rm -f's. +@ +text +@d26 1 +a26 1 +CC=gcc +d45 2 +a46 2 +REGEX = +REGEX1 = +d61 1 +a61 1 +CLIBS = $(OBSTACK) $(REGEX) $(GNU_MALLOC) +d63 1 +a63 1 +#CLIBS= $(OBSTACK) $(REGEX) $(GNU_MALLOC) -lPW +@ + + +1.1 +log +@Initial revision +@ +text +@d26 1 +a26 1 +CC=/bin/cc +d114 1 +a114 1 + -rm init.c +d119 1 +a119 1 + -rm init.c +d125 1 +a125 1 + -rm init.c +d151 3 +a153 3 + -rm ${OBS} ${TSOBS} ${NTSOBS} ${OBSTACK} ${REGEX} + -rm init.c + -rm gdb +d156 1 +a156 1 + -rm expread.tab.c +@ diff --git a/gdb/RCS/blockframe.c,v b/gdb/RCS/blockframe.c,v new file mode 100644 index 0000000..fc64e2f --- /dev/null +++ b/gdb/RCS/blockframe.c,v @@ -0,0 +1,606 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.03.16.21.09.52; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.09.23.21.53; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.15.15.16; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@Don't stop the stack trace until the "next frame pointer" is zero. +@ +text +@/* Get info from stack frames; + convert between frames, blocks, functions and pc values. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" + +/* Address of end of first object file. + This file is assumed to be a startup file + and frames with pc's inside it + are treated as nonexistent. */ + +CORE_ADDR first_object_file_end; + +/* Address of innermost stack frame (contents of FP register) */ + +static FRAME current_frame; + +struct block *block_for_pc (); +CORE_ADDR get_pc_function_start (); + +/* + * Cache for frame addresses already read by gdb. Valid only while + * inferior is stopped. Control variables for the frame cache should + * be local to this module. + */ +struct obstack frame_cache_obstack; + +/* Return the innermost (currently executing) stack frame. */ + +FRAME +get_current_frame () +{ + /* We assume its address is kept in a general register; + param.h says which register. */ + + return current_frame; +} + +void +set_current_frame (frame) + FRAME frame; +{ + current_frame = frame; +} + +FRAME +create_new_frame (addr, pc) + FRAME_ADDR addr; + CORE_ADDR pc; +{ + struct frame_info *fci; /* Same type as FRAME */ + + fci = (struct frame_info *) + obstack_alloc (&frame_cache_obstack, + sizeof (struct frame_info)); + + /* Arbitrary frame */ + fci->next = (struct frame_info *) 0; + fci->prev = (struct frame_info *) 0; + fci->frame = addr; + fci->next_frame = 0; /* Since arbitrary */ + fci->pc = pc; + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO (fci); +#endif + + return fci; +} + +/* Return the frame that called FRAME. + If FRAME is the original frame (it has no caller), return 0. */ + +FRAME +get_prev_frame (frame) + FRAME frame; +{ + /* We're allowed to know that FRAME and "struct frame_info *" are + the same */ + return get_prev_frame_info (frame); +} + +/* + * Flush the entire frame cache. + */ +void +flush_cached_frames () +{ + /* Since we can't really be sure what the first object allocated was */ + obstack_free (&frame_cache_obstack, 0); + obstack_init (&frame_cache_obstack); + + current_frame = (struct frame_info *) 0; /* Invalidate cache */ +} + +/* Return a structure containing various interesting information + about a specified stack frame. */ +/* How do I justify including this function? Well, the FRAME + identifier format has gone through several changes recently, and + it's not completely inconceivable that it could happen again. If + it does, have this routine around will help */ + +struct frame_info * +get_frame_info (frame) + FRAME frame; +{ + return frame; +} + +/* Return a structure containing various interesting information + about the frame that called NEXT_FRAME. */ + +struct frame_info * +get_prev_frame_info (next_frame) + FRAME next_frame; +{ + FRAME_ADDR address; + struct frame_info *prev; + int fromleaf = 0; + + /* If we are within "start" right now, don't go any higher. */ + /* This truncates stack traces of things at sigtramp() though, + because sigtramp() doesn't have a normal return PC, it has + garbage or a small value (seen: 3) in the return PC slot. + It's VITAL to see where the signal occurred, so punt this. */ +#if 0 + if (next_frame && next_frame->pc < first_object_file_end) + return 0; +#endif + + /* If the requested entry is in the cache, return it. + Otherwise, figure out what the address should be for the entry + we're about to add to the cache. */ + + if (!next_frame) + { + if (!current_frame) + error ("No frame is currently selected."); + + return current_frame; + } + else + { + /* If we have the prev one, return it */ + if (next_frame->prev) + return next_frame->prev; + + /* There is a questionable, but probably always correct + assumption being made here. The assumption is that if + functions on a specific machine has a FUNCTION_START_OFFSET, + then this is used by the function call instruction for some + purpose. If the function call instruction has this much hair + in it, it probably also sets up the frame pointer + automatically (ie. we'll never have what I am calling a + "leaf node", one which shares a frame pointer with it's + calling function). This is true on a vax. The only other + way to find this out would be to setup a seperate macro + "FUNCTION_HAS_FRAME_POINTER", which would often be equivalent + to SKIP_PROLOGUE modifying a pc value. */ + +#if FUNCTION_START_OFFSET == 0 + if (!(next_frame->next)) + { + /* Innermost */ + CORE_ADDR func_start, after_prologue; + + func_start = (get_pc_function_start (next_frame->pc) + + FUNCTION_START_OFFSET); + after_prologue = func_start; + SKIP_PROLOGUE (after_prologue); + if (after_prologue == func_start) + { + fromleaf = 1; + address = next_frame->frame; + } + } +#endif + + if (!fromleaf) + { + /* Two macros defined in param.h specify the machine-dependent + actions to be performed here. */ + /* First, get the frame's chain-pointer. + If that is zero, the frame is the outermost frame. */ + address = FRAME_CHAIN (next_frame); + if (!FRAME_CHAIN_VALID (address, next_frame)) + return 0; + + /* If frame has a caller, combine the chain pointer and + the frame's own address to get the address of the caller. */ + address = FRAME_CHAIN_COMBINE (address, next_frame); + } + } + + prev = (struct frame_info *) + obstack_alloc (&frame_cache_obstack, + sizeof (struct frame_info)); + + if (next_frame) + next_frame->prev = prev; + prev->next = next_frame; + prev->prev = (struct frame_info *) 0; + prev->frame = address; + prev->next_frame = prev->next ? prev->next->frame : 0; + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO(prev); +#endif + + /* This entry is in the frame queue now, which is good since + FRAME_SAVED_PC may use that queue to figure out it's value + (see m-sparc.h). We want the pc saved in the inferior frame. */ + prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (next_frame) : + next_frame ? FRAME_SAVED_PC (next_frame) : read_pc ()); + + return prev; +} + +CORE_ADDR +get_frame_pc (frame) + FRAME frame; +{ + struct frame_info *fi; + fi = get_frame_info (frame); + return fi->pc; +} + +/* Find the addresses in which registers are saved in FRAME. */ + +void +get_frame_saved_regs (frame_info_addr, saved_regs_addr) + struct frame_info *frame_info_addr; + struct frame_saved_regs *saved_regs_addr; +{ +#if 1 + FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr); +#else + { + register int regnum; + register int regmask; + register CORE_ADDR next_addr; + register CORE_ADDR pc; + int nextinsn; + bzero (&*saved_regs_addr, sizeof *saved_regs_addr); + if ((frame_info_addr)->pc >= ((frame_info_addr)->frame + - CALL_DUMMY_LENGTH - FP_REGNUM*4 - 8*12 - 4) + && (frame_info_addr)->pc <= (frame_info_addr)->frame) + { + next_addr = (frame_info_addr)->frame; + pc = (frame_info_addr)->frame - CALL_DUMMY_LENGTH - FP_REGNUM * 4 - 8*12 - 4; + } + else + { + pc = get_pc_function_start ((frame_info_addr)->pc); + /* Verify we have a link a6 instruction next; + if not we lose. If we win, find the address above the saved + regs using the amount of storage from the link instruction. */ + if (044016 == read_memory_integer (pc, 2)) + { + next_addr = (frame_info_addr)->frame + read_memory_integer (pc += 2, 4); + pc += 4; + } + else if (047126 == read_memory_integer (pc, 2)) + { + next_addr = (frame_info_addr)->frame + read_memory_integer (pc += 2, 2); + pc+=2; + } + else goto lose; + + /* If have an addal #-n, sp next, adjust next_addr. */ + if ((0177777 & read_memory_integer (pc, 2)) == 0157774) + { + next_addr += read_memory_integer (pc += 2, 4); + pc += 4; + } + } + /* next should be a moveml to (sp) or -(sp) or a movl r,-(sp) */ + regmask = read_memory_integer (pc + 2, 2); + + /* But before that can come an fmovem. Check for it. */ + nextinsn = 0xffff & read_memory_integer (pc, 2); + if (0xf227 == nextinsn + && (regmask & 0xff00) == 0xe000) + { + pc += 4; /* Regmask's low bit is for register fp7, the first pushed */ + for (regnum = FP0_REGNUM + 7; + regnum >= FP0_REGNUM; + regnum--, regmask >>= 1) + if (regmask & 1) + (*saved_regs_addr).regs[regnum] = (next_addr -= 12); + regmask = read_memory_integer (pc + 2, 2); + } + if (0044327 == read_memory_integer (pc, 2)) + { + pc += 4; /* Regmask's low bit is for register 0, the first written */ + for (regnum = 0; regnum < 16; regnum++, regmask >>= 1) + if (regmask & 1) + (*saved_regs_addr).regs[regnum] = (next_addr += 4) - 4; + } + else if (0044347 == read_memory_integer (pc, 2)) + { pc += 4; /* Regmask's low bit is for register 15, the first pushed */ + for (regnum = 15; regnum >= 0; regnum--, regmask >>= 1) + if (regmask & 1) + (*saved_regs_addr).regs[regnum] = (next_addr -= 4); } + else if (0x2f00 == (0xfff0 & read_memory_integer (pc, 2))) + { regnum = 0xf & read_memory_integer (pc, 2); pc += 2; + (*saved_regs_addr).regs[regnum] = (next_addr -= 4); } + /* fmovemx to index of sp may follow. */ + regmask = read_memory_integer (pc + 2, 2); + nextinsn = 0xffff & read_memory_integer (pc, 2); + if (0xf236 == nextinsn + && (regmask & 0xff00) == 0xf000) + { + pc += 10; /* Regmask's low bit is for register fp0, the first written */ + for (regnum = FP0_REGNUM + 7; + regnum >= FP0_REGNUM; + regnum--, regmask >>= 1) + if (regmask & 1) + (*saved_regs_addr).regs[regnum] = (next_addr += 12) - 12; + regmask = read_memory_integer (pc + 2, 2); + } + /* clrw -(sp); movw ccr,-(sp) may follow. */ + if (0x426742e7 == read_memory_integer (pc, 4)) + (*saved_regs_addr).regs[PS_REGNUM] = (next_addr -= 4); + lose: ; + (*saved_regs_addr).regs[SP_REGNUM] = (frame_info_addr)->frame + 8; + (*saved_regs_addr).regs[FP_REGNUM] = (frame_info_addr)->frame; + (*saved_regs_addr).regs[PC_REGNUM] = (frame_info_addr)->frame + 4; + } +#endif +} + +/* Return the innermost lexical block in execution + in a specified stack frame. The frame address is assumed valid. */ + +struct block * +get_frame_block (frame) + FRAME frame; +{ + struct frame_info *fi; + + fi = get_frame_info (frame); + return block_for_pc (fi->pc); +} + +struct block * +get_current_block () +{ + return block_for_pc (read_pc ()); +} + +CORE_ADDR +get_pc_function_start (pc) + CORE_ADDR pc; +{ + register struct block *bl = block_for_pc (pc); + register struct symbol *symbol; + if (bl == 0 || (symbol = block_function (bl)) == 0) + { + register int misc_index = find_pc_misc_function (pc); + if (misc_index >= 0) + return misc_function_vector[misc_index].address; + return 0; + } + bl = SYMBOL_BLOCK_VALUE (symbol); + return BLOCK_START (bl); +} + +/* Return the symbol for the function executing in frame FRAME. */ + +struct symbol * +get_frame_function (frame) + FRAME frame; +{ + register struct block *bl = get_frame_block (frame); + if (bl == 0) + return 0; + return block_function (bl); +} + +/* Return the innermost lexical block containing the specified pc value, + or 0 if there is none. */ + +extern struct symtab *psymtab_to_symtab (); + +struct block * +block_for_pc (pc) + register CORE_ADDR pc; +{ + register struct block *b; + register int bot, top, half; + register struct symtab *s; + register struct partial_symtab *ps; + struct blockvector *bl; + + /* First search all symtabs for one whose file contains our pc */ + + for (s = symtab_list; s; s = s->next) + { + bl = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bl, 0); + if (BLOCK_START (b) <= pc + && BLOCK_END (b) > pc) + break; + } + + if (s == 0) + for (ps = partial_symtab_list; ps; ps = ps->next) + { + if (ps->textlow <= pc + && ps->texthigh > pc) + { + s = psymtab_to_symtab (ps); + bl = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bl, 0); + break; + } + } + + if (s == 0) + return 0; + + /* Then search that symtab for the smallest block that wins. */ + /* Use binary search to find the last block that starts before PC. */ + + bot = 0; + top = BLOCKVECTOR_NBLOCKS (bl); + + while (top - bot > 1) + { + half = (top - bot + 1) >> 1; + b = BLOCKVECTOR_BLOCK (bl, bot + half); + if (BLOCK_START (b) <= pc) + bot += half; + else + top = bot + half; + } + + /* Now search backward for a block that ends after PC. */ + + while (bot >= 0) + { + b = BLOCKVECTOR_BLOCK (bl, bot); + if (BLOCK_END (b) > pc) + return b; + bot--; + } + + return 0; +} + +/* Return the function containing pc value PC. + Returns 0 if function is not known. */ + +struct symbol * +find_pc_function (pc) + CORE_ADDR pc; +{ + register struct block *b = block_for_pc (pc); + if (b == 0) + return 0; + return block_function (b); +} + +/* Find the misc function whose address is the largest + while being less than PC. Return its index in misc_function_vector. + Returns -1 if PC is not in suitable range. */ + +int +find_pc_misc_function (pc) + register CORE_ADDR pc; +{ + register int lo = 0; + register int hi = misc_function_count-1; + register int new; + register int distance; + + /* Note that the last thing in the vector is always _etext. */ + + /* Above statement is not *always* true - fix for case where there are */ + /* no misc functions at all (ie no symbol table has been read). */ + if (hi < 0) return -1; /* no misc functions recorded */ + + /* trivial reject range test */ + if (pc < misc_function_vector[0].address || + pc > misc_function_vector[hi].address) + return -1; + + do { + new = (lo + hi) >> 1; + distance = misc_function_vector[new].address - pc; + if (distance == 0) + return new; /* an exact match */ + else if (distance > 0) + hi = new; + else + lo = new; + } while (hi-lo != 1); + + /* if here, we had no exact match, so return the lower choice */ + return lo; +} + +/* Return the innermost stack frame executing inside of the specified block, + or zero if there is no such frame. */ + +FRAME +block_innermost_frame (block) + struct block *block; +{ + struct frame_info *fi; + register FRAME frame; + register CORE_ADDR start = BLOCK_START (block); + register CORE_ADDR end = BLOCK_END (block); + + frame = 0; + while (1) + { + frame = get_prev_frame (frame); + if (frame == 0) + return 0; + fi = get_frame_info (frame); + if (fi->pc >= start && fi->pc < end) + return frame; + } +} + +void +_initialize_blockframe () +{ + obstack_init (&frame_cache_obstack); +} +@ + + +1.2 +log +@Avoid fatal error for simple user error +@ +text +@d142 5 +d149 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d152 1 +a152 1 + fatal ("get_prev_frame_info: Called before cache primed"); +@ diff --git a/gdb/RCS/coffread.c,v b/gdb/RCS/coffread.c,v new file mode 100644 index 0000000..9c45395 --- /dev/null +++ b/gdb/RCS/coffread.c,v @@ -0,0 +1,2047 @@ +head 1.5; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.5 +date 89.03.27.18.38.25; author gnu; state Exp; +branches ; +next 1.4; + +1.4 +date 89.03.27.18.37.20; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 89.03.27.18.36.15; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.10.01.38.05; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.10.01.32.45; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.5 +log +@Remake A/UX changes. +@ +text +@/* Read coff symbol tables and convert to internal format, for GDB. + Design and support routines derived from dbxread.c, and UMAX COFF + specific routines written 9/1/87 by David D. Johnson, Brown University. + Revised 11/27/87 ddj@@cs.brown.edu + Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#ifdef COFF_FORMAT +#include "symtab.h" + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#endif + +#include <a.out.h> +#include <stdio.h> +#include <obstack.h> +#include <sys/param.h> +#include <sys/file.h> + +/* Avoid problems with A/UX predefine */ +#undef aux + +static void add_symbol_to_list (); +static void read_coff_symtab (); +static void patch_opaque_types (); +static struct type *decode_function_type (); +static struct type *decode_type (); +static struct type *decode_base_type (); +static struct type *read_enum_type (); +static struct type *read_struct_type (); +static void finish_block (); +static struct blockvector *make_blockvector (); +static struct symbol *process_coff_symbol (); +static int init_stringtab (); +static void free_stringtab (); +static char *getfilename (); +static char *getsymname (); +static int init_lineno (); +static void enter_linenos (); + +extern void free_all_symtabs (); +extern void free_all_psymtabs (); + + +/* Name of source file whose symbol data we are now processing. + This comes from a symbol named ".file". */ + +static char *last_source_file; + +/* Core address of start and end of text of current source file. + This comes from a ".text" symbol where x_nlinno > 0. */ + +static CORE_ADDR cur_src_start_addr; +static CORE_ADDR cur_src_end_addr; + +/* End of the text segment of the executable file, + as found in the symbol _etext. */ + +static CORE_ADDR end_of_text_addr; + +/* The addresses of the symbol table stream and number of symbols + of the object file we are reading (as copied into core). */ + +static FILE *nlist_stream_global; +static int nlist_nsyms_global; + +/* The file and text section headers of the symbol file */ + +static FILHDR file_hdr; +static SCNHDR text_hdr; + +/* The index in the symbol table of the last coff symbol that was processed. */ + +static int symnum; + +/* Vector of types defined so far, indexed by their coff symnum. */ + +static struct typevector *type_vector; + +/* Number of elements allocated for type_vector currently. */ + +static int type_vector_length; + +/* Vector of line number information. */ + +static struct linetable *line_vector; + +/* Index of next entry to go in line_vector_index. */ + +static int line_vector_index; + +/* Last line number recorded in the line vector. */ + +static int prev_line_number; + +/* Number of elements allocated for line_vector currently. */ + +static int line_vector_length; + +/* Chain of typedefs of pointers to empty struct/union types. + They are chained thru the SYMBOL_VALUE. */ + +#define HASHSIZE 127 +static struct symbol *opaque_type_chain[HASHSIZE]; + +/* Record the symbols defined for each context in a list. + We don't create a struct block for the context until we + know how long to make it. */ + +struct pending +{ + struct pending *next; + struct symbol *symbol; +}; + +/* Here are the three lists that symbols are put on. */ + +struct pending *file_symbols; /* static at top level, and types */ + +struct pending *global_symbols; /* global functions and variables */ + +struct pending *local_symbols; /* everything local to lexical context */ + +/* List of unclosed lexical contexts + (that will become blocks, eventually). */ + +struct context_stack +{ + struct context_stack *next; + struct pending *locals; + struct pending_block *old_blocks; + struct symbol *name; + CORE_ADDR start_addr; + int depth; +}; + +struct context_stack *context_stack; + +/* Nonzero if within a function (so symbols should be local, + if nothing says specifically). */ + +int within_function; + +/* List of blocks already made (lexical contexts already closed). + This is used at the end to make the blockvector. */ + +struct pending_block +{ + struct pending_block *next; + struct block *block; +}; + +struct pending_block *pending_blocks; + +extern CORE_ADDR first_object_file_end; /* From blockframe.c */ + +/* File name symbols were loaded from. */ + +static char *symfile; + +/* Look up a coff type-number index. Return the address of the slot + where the type for that index is stored. + The type-number is in INDEX. + + This can be used for finding the type associated with that index + or for associating a new type with the index. */ + +static struct type ** +coff_lookup_type (index) + register int index; +{ + if (index >= type_vector_length) + { + int old_vector_length = type_vector_length; + + type_vector_length *= 2; + if (type_vector_length < index) { + type_vector_length = index * 2; + } + type_vector = (struct typevector *) + xrealloc (type_vector, sizeof (struct typevector) + + type_vector_length * sizeof (struct type *)); + bzero (&type_vector->type[ old_vector_length ], + (type_vector_length - old_vector_length) * sizeof(struct type *)); + } + return &type_vector->type[index]; +} + +/* Make sure there is a type allocated for type number index + and return the type object. + This can create an empty (zeroed) type object. */ + +static struct type * +coff_alloc_type (index) + int index; +{ + register struct type **type_addr = coff_lookup_type (index); + register struct type *type = *type_addr; + + /* If we are referring to a type not known at all yet, + allocate an empty type for it. + We will fill it in later if we find out how. */ + if (type == 0) + { + type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + bzero (type, sizeof (struct type)); + *type_addr = type; + } + return type; +} + +/* maintain the lists of symbols and blocks */ + +/* Add a symbol to one of the lists of symbols. */ +static void +add_symbol_to_list (symbol, listhead) + struct symbol *symbol; + struct pending **listhead; +{ + register struct pending *link + = (struct pending *) xmalloc (sizeof (struct pending)); + + link->next = *listhead; + link->symbol = symbol; + *listhead = link; +} + +/* Take one of the lists of symbols and make a block from it. + Put the block on the list of pending blocks. */ + +static void +finish_block (symbol, listhead, old_blocks, start, end) + struct symbol *symbol; + struct pending **listhead; + struct pending_block *old_blocks; + CORE_ADDR start, end; +{ + register struct pending *next, *next1; + register struct block *block; + register struct pending_block *pblock; + struct pending_block *opblock; + register int i; + + /* Count the length of the list of symbols. */ + + for (next = *listhead, i = 0; next; next = next->next, i++); + + block = (struct block *) + obstack_alloc (symbol_obstack, sizeof (struct block) + (i - 1) * sizeof (struct symbol *)); + + /* Copy the symbols into the block. */ + + BLOCK_NSYMS (block) = i; + for (next = *listhead; next; next = next->next) + BLOCK_SYM (block, --i) = next->symbol; + + BLOCK_START (block) = start; + BLOCK_END (block) = end; + BLOCK_SUPERBLOCK (block) = 0; /* Filled in when containing block is made */ + + /* Put the block in as the value of the symbol that names it. */ + + if (symbol) + { + SYMBOL_BLOCK_VALUE (symbol) = block; + BLOCK_FUNCTION (block) = symbol; + } + else + BLOCK_FUNCTION (block) = 0; + + /* Now free the links of the list, and empty the list. */ + + for (next = *listhead; next; next = next1) + { + next1 = next->next; + free (next); + } + *listhead = 0; + + /* Install this block as the superblock + of all blocks made since the start of this scope + that don't have superblocks yet. */ + + opblock = 0; + for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next) + { + if (BLOCK_SUPERBLOCK (pblock->block) == 0) + BLOCK_SUPERBLOCK (pblock->block) = block; + opblock = pblock; + } + + /* Record this block on the list of all blocks in the file. + Put it after opblock, or at the beginning if opblock is 0. + This puts the block in the list after all its subblocks. */ + + pblock = (struct pending_block *) xmalloc (sizeof (struct pending_block)); + pblock->block = block; + if (opblock) + { + pblock->next = opblock->next; + opblock->next = pblock; + } + else + { + pblock->next = pending_blocks; + pending_blocks = pblock; + } +} + +static struct blockvector * +make_blockvector () +{ + register struct pending_block *next, *next1; + register struct blockvector *blockvector; + register int i; + + /* Count the length of the list of blocks. */ + + for (next = pending_blocks, i = 0; next; next = next->next, i++); + + blockvector = (struct blockvector *) + obstack_alloc (symbol_obstack, sizeof (struct blockvector) + (i - 1) * sizeof (struct block *)); + + /* Copy the blocks into the blockvector. + This is done in reverse order, which happens to put + the blocks into the proper order (ascending starting address). + finish_block has hair to insert each block into the list + after its subblocks in order to make sure this is true. */ + + BLOCKVECTOR_NBLOCKS (blockvector) = i; + for (next = pending_blocks; next; next = next->next) + BLOCKVECTOR_BLOCK (blockvector, --i) = next->block; + + /* Now free the links of the list, and empty the list. */ + + for (next = pending_blocks; next; next = next1) + { + next1 = next->next; + free (next); + } + pending_blocks = 0; + + return blockvector; +} + +/* Manage the vector of line numbers. */ + +static +record_line (line, pc) + int line; + CORE_ADDR pc; +{ + struct linetable_entry *e; + /* Make sure line vector is big enough. */ + + if (line_vector_index + 2 >= line_vector_length) + { + line_vector_length *= 2; + line_vector = (struct linetable *) + xrealloc (line_vector, sizeof (struct linetable) + + (line_vector_length + * sizeof (struct linetable_entry))); + } + + e = line_vector->item + line_vector_index++; + e->line = line; e->pc = pc; +} + +/* Start a new symtab for a new source file. + This is called when a COFF ".file" symbol is seen; + it indicates the start of data for one original source file. */ + +static void +start_symtab () +{ + file_symbols = 0; + global_symbols = 0; + context_stack = 0; + within_function = 0; + last_source_file = 0; + + /* Initialize the source file information for this file. */ + + line_vector_index = 0; + line_vector_length = 1000; + prev_line_number = -2; /* Force first line number to be explicit */ + line_vector = (struct linetable *) + xmalloc (sizeof (struct linetable) + + line_vector_length * sizeof (struct linetable_entry)); +} + +/* Save the vital information for use when closing off the current file. + NAME is the file name the symbols came from, START_ADDR is the first + text address for the file, and SIZE is the number of bytes of text. */ + +static void +complete_symtab (name, start_addr, size) + char *name; + CORE_ADDR start_addr; + unsigned int size; +{ + last_source_file = savestring (name, strlen (name)); + cur_src_start_addr = start_addr; + cur_src_end_addr = start_addr + size; +} + +/* Finish the symbol definitions for one main source file, + close off all the lexical contexts for that file + (creating struct block's for them), then make the + struct symtab for that file and put it in the list of all such. */ + +static void +end_symtab () +{ + register struct symtab *symtab; + register struct context_stack *cstk; + register struct blockvector *blockvector; + register struct linetable *lv; + + /* Finish the lexical context of the last function in the file. */ + + if (context_stack) + { + cstk = context_stack; + context_stack = 0; + /* Make a block for the local symbols within. */ + finish_block (cstk->name, &local_symbols, cstk->old_blocks, + cstk->start_addr, cur_src_end_addr); + free (cstk); + } + + /* Ignore a file that has no functions with real debugging info. */ + if (pending_blocks == 0 && file_symbols == 0 && global_symbols == 0) + { + free (line_vector); + line_vector = 0; + line_vector_length = -1; + last_source_file = 0; + return; + } + + /* Create the two top-level blocks for this file. */ + finish_block (0, &file_symbols, 0, cur_src_start_addr, cur_src_end_addr); + finish_block (0, &global_symbols, 0, cur_src_start_addr, cur_src_end_addr); + + /* Create the blockvector that points to all the file's blocks. */ + blockvector = make_blockvector (); + + /* Now create the symtab object for this source file. */ + symtab = (struct symtab *) xmalloc (sizeof (struct symtab)); + symtab->free_ptr = 0; + + /* Fill in its components. */ + symtab->blockvector = blockvector; + symtab->free_code = free_linetable; + symtab->filename = last_source_file; + lv = line_vector; + lv->nitems = line_vector_index; + symtab->linetable = (struct linetable *) + xrealloc (lv, (sizeof (struct linetable) + + lv->nitems * sizeof (struct linetable_entry))); + symtab->nlines = 0; + symtab->line_charpos = 0; + + /* Link the new symtab into the list of such. */ + symtab->next = symtab_list; + symtab_list = symtab; + + /* Reinitialize for beginning of new file. */ + line_vector = 0; + line_vector_length = -1; + last_source_file = 0; +} + +/* Accumulate the misc functions in bunches of 127. + At the end, copy them all into one newly allocated structure. */ + +#define MISC_BUNCH_SIZE 127 + +struct misc_bunch +{ + struct misc_bunch *next; + struct misc_function contents[MISC_BUNCH_SIZE]; +}; + +/* Bunch currently being filled up. + The next field points to chain of filled bunches. */ + +static struct misc_bunch *misc_bunch; + +/* Number of slots filled in current bunch. */ + +static int misc_bunch_index; + +/* Total number of misc functions recorded so far. */ + +static int misc_count; + +static void +init_misc_functions () +{ + misc_count = 0; + misc_bunch = 0; + misc_bunch_index = MISC_BUNCH_SIZE; +} + +static void +record_misc_function (name, address) + char *name; + CORE_ADDR address; +{ + register struct misc_bunch *new; + + if (misc_bunch_index == MISC_BUNCH_SIZE) + { + new = (struct misc_bunch *) xmalloc (sizeof (struct misc_bunch)); + misc_bunch_index = 0; + new->next = misc_bunch; + misc_bunch = new; + } + misc_bunch->contents[misc_bunch_index].name = savestring (name, strlen (name)); + misc_bunch->contents[misc_bunch_index].address = address; + misc_bunch_index++; + misc_count++; +} + +/* if we see a function symbol, we do record_misc_function. + * however, if it turns out the next symbol is '.bf', then + * we call here to undo the misc definition + */ +static void +unrecord_misc_function () +{ + if (misc_bunch_index == 0) + error ("Internal error processing symbol table, at symbol %d.", + symnum); + misc_bunch_index--; + misc_count--; +} + + +static int +compare_misc_functions (fn1, fn2) + struct misc_function *fn1, *fn2; +{ + /* Return a signed result based on unsigned comparisons + so that we sort into unsigned numeric order. */ + if (fn1->address < fn2->address) + return -1; + if (fn1->address > fn2->address) + return 1; + return 0; +} + +static void +discard_misc_bunches () +{ + register struct misc_bunch *next; + + while (misc_bunch) + { + next = misc_bunch->next; + free (misc_bunch); + misc_bunch = next; + } +} + +static void +condense_misc_bunches () +{ + register int i, j; + register struct misc_bunch *bunch; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + misc_function_vector + = (struct misc_function *) + xmalloc (misc_count * sizeof (struct misc_function)); + + j = 0; + bunch = misc_bunch; + while (bunch) + { + for (i = 0; i < misc_bunch_index; i++) + { + register char *tmp; + + misc_function_vector[j] = bunch->contents[i]; + tmp = misc_function_vector[j].name; + misc_function_vector[j].name = (tmp[0] == '_' ? tmp + offset : tmp); + j++; + } + bunch = bunch->next; + misc_bunch_index = MISC_BUNCH_SIZE; + } + + misc_function_count = j; + + /* Sort the misc functions by address. */ + + qsort (misc_function_vector, j, sizeof (struct misc_function), + compare_misc_functions); +} + +/* Call sort_syms to sort alphabetically + the symbols of each block of each symtab. */ + +static int +compare_symbols (s1, s2) + struct symbol **s1, **s2; +{ + /* Names that are less should come first. */ + register int namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2)); + if (namediff != 0) return namediff; + /* For symbols of the same name, registers should come first. */ + return ((SYMBOL_CLASS (*s2) == LOC_REGISTER) + - (SYMBOL_CLASS (*s1) == LOC_REGISTER)); +} + +static void +sort_syms () +{ + register struct symtab *s; + register int i, nbl; + register struct blockvector *bv; + register struct block *b; + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + nbl = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < nbl; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + if (BLOCK_SHOULD_SORT (b)) + qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b), + sizeof (struct symbol *), compare_symbols); + } + } +} + +/* This is the symbol-file command. Read the file, analyze its symbols, + and add a struct symtab to symtab_list. */ + +void +symbol_file_command (name) + char *name; +{ + int desc; + int num_symbols; + int num_sections; + int symtab_offset; + extern void close (); + register int val; + struct cleanup *old_chain; + + dont_repeat (); + + if (name == 0) + { + if (symtab_list && !query ("Discard symbol table? ", 0)) + error ("Not confirmed."); + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + return; + } + + if (symtab_list && !query ("Load new symbol table from \"%s\"? ", name)) + error ("Not confirmed."); + + if (symfile) + free (symfile); + symfile = 0; + + { + char *absolute_name; + + desc = openp (getenv ("PATH"), 1, name, O_RDONLY, 0, &absolute_name); + if (desc < 0) + perror_with_name (name); + else + name = absolute_name; + } + + old_chain = make_cleanup (close, desc); + make_cleanup (free_current_contents, &name); + + if ((num_symbols = read_file_hdr (desc, &file_hdr)) < 0) + error ("File \"%s\" not in executable format.", name); + + if (num_symbols == 0) + { + free_all_symtabs (); + printf ("%s does not have a symbol-table.\n", name); + fflush (stdout); + return; + } + + printf ("Reading symbol data from %s...", name); + fflush (stdout); + + /* Throw away the old symbol table. */ + + free_all_symtabs (); + free_all_psymtabs (); /* Make sure that partial_symtab_list */ + /* is 0 also. */ + + num_sections = file_hdr.f_nscns; + symtab_offset = file_hdr.f_symptr; + + if (read_section_hdr (desc, _TEXT, &text_hdr, num_sections) < 0) + error ("\"%s\": can't read text section header", name); + + /* Read the line number table, all at once. */ + + val = init_lineno (desc, text_hdr.s_lnnoptr, text_hdr.s_nlnno); + if (val < 0) + error ("\"%s\": error reading line numbers\n", name); + + /* Now read the string table, all at once. */ + + val = init_stringtab (desc, symtab_offset + num_symbols * SYMESZ); + if (val < 0) + { + free_all_symtabs (); + printf ("\"%s\": can't get string table", name); + fflush (stdout); + return; + } + make_cleanup (free_stringtab, 0); + + /* Position to read the symbol table. Do not read it all at once. */ + val = lseek (desc, (long)symtab_offset, 0); + if (val < 0) + perror_with_name (name); + + init_misc_functions (); + make_cleanup (discard_misc_bunches, 0); + + /* Now that the executable file is positioned at symbol table, + process it and define symbols accordingly. */ + + read_coff_symtab (desc, num_symbols); + + patch_opaque_types (); + + /* Sort symbols alphabetically within each block. */ + + sort_syms (); + + /* Go over the misc functions and install them in vector. */ + + condense_misc_bunches (); + + /* Don't allow char * to have a typename (else would get caddr_t.) */ + + TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0; + + /* Make a default for file to list. */ + + select_source_symtab (symtab_list); + + symfile = savestring (name, strlen (name)); + + do_cleanups (old_chain); + + printf ("done.\n"); + fflush (stdout); +} + +/* Return name of file symbols were loaded from, or 0 if none.. */ + +char * +get_sym_file () +{ + return symfile; +} + +/* Simplified internal version of coff symbol table information */ + +struct coff_symbol { + char *c_name; + int c_symnum; /* symbol number of this entry */ + int c_nsyms; /* 1 if syment only, 2 if syment + auxent */ + long c_value; + int c_sclass; + int c_secnum; + unsigned int c_type; +}; + +/* Given pointers to a symbol table in coff style exec file, + analyze them and create struct symtab's describing the symbols. + NSYMS is the number of symbols in the symbol table. + We read them one at a time using read_one_sym (). */ + +static void +read_coff_symtab (desc, nsyms) + int desc; + int nsyms; +{ + int newfd; /* Avoid multiple closes on same desc */ + FILE *stream; + register struct context_stack *new; + struct coff_symbol coff_symbol; + register struct coff_symbol *cs = &coff_symbol; + static SYMENT main_sym; + static AUXENT main_aux; + struct coff_symbol fcn_cs_saved; + static SYMENT fcn_sym_saved; + static AUXENT fcn_aux_saved; + + int num_object_files = 0; + int next_file_symnum = -1; + char *filestring; + int depth; + int fcn_first_line; + int fcn_last_line; + int fcn_start_addr; + long fcn_line_ptr; + struct cleanup *old_chain; + int fclose(); + + newfd = dup (desc); + if (newfd == -1) + fatal ("Too many open files"); + stream = fdopen (newfd, "r"); + + old_chain = make_cleanup (free_all_symtabs, 0); + make_cleanup (fclose, stream); + nlist_stream_global = stream; + nlist_nsyms_global = nsyms; + last_source_file = 0; + bzero (opaque_type_chain, sizeof opaque_type_chain); + + type_vector_length = 160; + type_vector = (struct typevector *) + xmalloc (sizeof (struct typevector) + + type_vector_length * sizeof (struct type *)); + bzero (type_vector->type, type_vector_length * sizeof (struct type *)); + + start_symtab (); + + symnum = 0; + while (symnum < nsyms) + { + QUIT; /* Make this command interruptable. */ + read_one_sym (cs, &main_sym, &main_aux); + + if (cs->c_symnum == next_file_symnum && cs->c_sclass != C_FILE) + { + CORE_ADDR last_file_end = cur_src_end_addr; + + if (last_source_file) + end_symtab (); + + start_symtab (); + complete_symtab ("_globals_", 0, first_object_file_end); + /* done with all files, everything from here on out is globals */ + } + + /* Special case for file with type declarations only, no text. */ + if (!last_source_file && cs->c_type != T_NULL && cs->c_secnum == N_DEBUG) + complete_symtab (filestring, 0, 0); + + /* Typedefs should not be treated as symbol definitions. */ + if (ISFCN (cs->c_type) && cs->c_sclass != C_TPDEF) + { + /* record as misc function. if we get '.bf' next, + * then we undo this step + */ + record_misc_function (cs->c_name, cs->c_value); + + fcn_line_ptr = main_aux.x_sym.x_fcnary.x_fcn.x_lnnoptr; + fcn_start_addr = cs->c_value; + fcn_cs_saved = *cs; + fcn_sym_saved = main_sym; + fcn_aux_saved = main_aux; + continue; + } + + switch (cs->c_sclass) + { + case C_EFCN: + case C_EXTDEF: + case C_ULABEL: + case C_USTATIC: + case C_LINE: + case C_ALIAS: + case C_HIDDEN: + printf ("Bad n_sclass = %d\n", cs->c_sclass); + break; + + case C_FILE: + /* + * c_value field contains symnum of next .file entry in table + * or symnum of first global after last .file. + */ + next_file_symnum = cs->c_value; + filestring = getfilename (&main_aux); + /* + * Complete symbol table for last object file + * containing debugging information. + */ + if (last_source_file) + { + end_symtab (); + start_symtab (); + } + num_object_files++; + break; + + case C_STAT: + if (cs->c_name[0] == '.') { + if (strcmp (cs->c_name, _TEXT) == 0) { + if (num_object_files == 1) { + /* last address of startup file */ + first_object_file_end = cs->c_value + + main_aux.x_scn.x_scnlen; + } + /* for some reason the old code didn't do + * this if this section entry had + * main_aux.x_scn.x_nlinno equal to 0 + */ + complete_symtab (filestring, cs->c_value, + main_aux.x_scn.x_scnlen); + } + /* flush rest of '.' symbols */ + break; + } + /* fall in for static symbols that don't start with '.' */ + case C_EXT: + if (cs->c_sclass == C_EXT && + cs->c_secnum == N_ABS && + strcmp (cs->c_name, _ETEXT) == 0) + end_of_text_addr = cs->c_value; + if (cs->c_type == T_NULL) { + if (cs->c_secnum <= 1) { /* text or abs */ + record_misc_function (cs->c_name, cs->c_value); + break; + } else { + cs->c_type = T_INT; + } + } + (void) process_coff_symbol (cs, &main_aux); + break; + + case C_FCN: + if (strcmp (cs->c_name, ".bf") == 0) + { + unrecord_misc_function (); + + within_function = 1; + + /* value contains address of first non-init type code */ + /* main_aux.x_sym.x_misc.x_lnsz.x_lnno + contains line number of '{' } */ + fcn_first_line = main_aux.x_sym.x_misc.x_lnsz.x_lnno; + + new = (struct context_stack *) + xmalloc (sizeof (struct context_stack)); + new->depth = depth = 0; + new->next = 0; + context_stack = new; + new->locals = 0; + new->old_blocks = pending_blocks; + new->start_addr = fcn_start_addr; + fcn_cs_saved.c_name = getsymname (&fcn_sym_saved); + new->name = process_coff_symbol (&fcn_cs_saved, + &fcn_aux_saved); + } + else if (strcmp (cs->c_name, ".ef") == 0) + { + /* the value of .ef is the address of epilogue code; + * not useful for gdb + */ + /* { main_aux.x_sym.x_misc.x_lnsz.x_lnno + contains number of lines to '}' */ + fcn_last_line = main_aux.x_sym.x_misc.x_lnsz.x_lnno; + enter_linenos (fcn_line_ptr, fcn_first_line, fcn_last_line); + new = context_stack; + + if (new == 0) + error ("Invalid symbol data; .bf/.ef/.bb/.eb symbol mismatch, at symbol %d.", + symnum); + + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, + fcn_cs_saved.c_value + + fcn_aux_saved.x_sym.x_misc.x_fsize); + context_stack = 0; + within_function = 0; + free (new); + } + break; + + case C_BLOCK: + if (strcmp (cs->c_name, ".bb") == 0) + { + new = (struct context_stack *) + xmalloc (sizeof (struct context_stack)); + depth++; + new->depth = depth; + new->next = context_stack; + context_stack = new; + new->locals = local_symbols; + new->old_blocks = pending_blocks; + new->start_addr = cs->c_value; + new->name = 0; + local_symbols = 0; + } + else if (strcmp (cs->c_name, ".eb") == 0) + { + new = context_stack; + if (new == 0 || depth != new->depth) + error ("Invalid symbol data: .bb/.eb symbol mismatch at symbol %d.", + symnum); + if (local_symbols && context_stack->next) + { + /* Make a block for the local symbols within. */ + finish_block (0, &local_symbols, new->old_blocks, + new->start_addr, cs->c_value); + } + depth--; + local_symbols = new->locals; + context_stack = new->next; + free (new); + } + break; + + default: + (void) process_coff_symbol (cs, &main_aux); + break; + } + } + + if (last_source_file) + end_symtab (); + fclose (stream); + discard_cleanups (old_chain); +} + +/* Routines for reading headers and symbols from executable. */ + +/* Read COFF file header, check magic number, + and return number of symbols. */ +read_file_hdr (chan, file_hdr) + int chan; + FILHDR *file_hdr; +{ + lseek (chan, 0L, 0); + if (myread (chan, (char *)file_hdr, FILHSZ) < 0) + return -1; + + switch (file_hdr->f_magic) + { +#ifdef NS32GMAGIC + case NS32GMAGIC: + case NS32SMAGIC: +#endif +#ifdef I386MAGIC + case I386MAGIC: +#endif + return file_hdr->f_nsyms; + + + default: +#ifdef BADMAG + if (BADMAG(file_hdr)) + return -1; + else + return file_hdr->f_nsyms; +#else + return -1; +#endif + } +} + +read_aout_hdr (chan, aout_hdr, size) + int chan; + AOUTHDR *aout_hdr; + int size; +{ + lseek (chan, (long)FILHSZ, 0); + if (size != sizeof (AOUTHDR)) + return -1; + if (myread (chan, (char *)aout_hdr, size) != size) + return -1; + return 0; +} + +read_section_hdr (chan, section_name, section_hdr, nsects) + register int chan; + register char *section_name; + SCNHDR *section_hdr; + register int nsects; +{ + register int i; + + if (lseek (chan, FILHSZ + sizeof (AOUTHDR), 0) < 0) + return -1; + + for (i = 0; i < nsects; i++) + { + if (myread (chan, (char *)section_hdr, SCNHSZ) < 0) + return -1; + if (strncmp (section_hdr->s_name, section_name, 8) == 0) + return 0; + } + return -1; +} + +read_one_sym (cs, sym, aux) + register struct coff_symbol *cs; + register SYMENT *sym; + register AUXENT *aux; +{ + cs->c_symnum = symnum; + fread ((char *)sym, SYMESZ, 1, nlist_stream_global); + cs->c_nsyms = (sym->n_numaux & 0xff) + 1; + if (cs->c_nsyms == 2) + { + /* doc for coff says there is either no aux entry or just one */ + fread ((char *)aux, AUXESZ, 1, nlist_stream_global); + } + else if (cs->c_nsyms > 2) + error ("more than one aux symbol table entry at symnum=%d\n", symnum); + + cs->c_name = getsymname (sym); + cs->c_value = sym->n_value; + cs->c_sclass = (sym->n_sclass & 0xff); + cs->c_secnum = sym->n_scnum; + cs->c_type = (unsigned) sym->n_type; + + symnum += cs->c_nsyms; +} + +/* Support for string table handling */ + +static char *stringtab = NULL; + +static int +init_stringtab (chan, offset) + int chan; + long offset; +{ + long buffer; + int val; + + if (stringtab) + { + free (stringtab); + stringtab = NULL; + } + + if (lseek (chan, offset, 0) < 0) + return -1; + + val = myread (chan, (char *)&buffer, sizeof buffer); + + /* If no string table is needed, then the file may end immediately + after the symbols. Just return with `stringtab' set to null. */ + if (val != sizeof buffer || buffer == 0) + return 0; + + stringtab = (char *) xmalloc (buffer); + if (stringtab == NULL) + return -1; + + bcopy (&buffer, stringtab, sizeof buffer); + + val = myread (chan, stringtab + sizeof buffer, buffer - sizeof buffer); + if (val != buffer - sizeof buffer || stringtab[buffer - 1] != '\0') + return -1; + + return 0; +} + +static void +free_stringtab () +{ + if (stringtab) + free (stringtab); + stringtab = NULL; +} + +static char * +getsymname (symbol_entry) + SYMENT *symbol_entry; +{ + static char buffer[SYMNMLEN+1]; + char *result; + + if (symbol_entry->n_zeroes == 0) + { + result = stringtab + symbol_entry->n_offset; + } + else + { + strncpy (buffer, symbol_entry->n_name, SYMNMLEN); + buffer[SYMNMLEN] = '\0'; + result = buffer; + } + return result; +} + +static char * +getfilename (aux_entry) + AUXENT *aux_entry; +{ + static char buffer[BUFSIZ]; + register char *temp; + char *result; + extern char *rindex (); + +#ifndef COFF_NO_LONG_FILE_NAMES + if (aux_entry->x_file.x_foff != 0) + strcpy (buffer, stringtab + aux_entry->x_file.x_foff); + else +#endif + { + strncpy (buffer, aux_entry->x_file.x_fname, FILNMLEN); + buffer[FILNMLEN] = '\0'; + } + result = buffer; + if ((temp = rindex (result, '/')) != NULL) + result = temp + 1; + return (result); +} + +/* Support for line number handling */ +static char *linetab = NULL; +static long linetab_offset; +static int linetab_count; + +static int +init_lineno (chan, offset, count) + int chan; + long offset; + int count; +{ + int val; + + if (lseek (chan, offset, 0) < 0) + return -1; + + if (linetab) + free (linetab); + linetab = (char *) xmalloc (count * LINESZ); + + val = myread (chan, linetab, count * LINESZ); + if (val != count * LINESZ) + return -1; + + linetab_offset = offset; + linetab_count = count; + return 0; +} + +static void +enter_linenos (file_offset, first_line, last_line) + long file_offset; + register int first_line; + register int last_line; +{ + register char *rawptr = &linetab[file_offset - linetab_offset]; + register struct lineno *lptr; + + /* skip first line entry for each function */ + rawptr += LINESZ; + /* line numbers start at one for the first line of the function */ + first_line--; + + for (lptr = (struct lineno *)rawptr; + lptr->l_lnno && lptr->l_lnno <= last_line; + rawptr += LINESZ, lptr = (struct lineno *)rawptr) + { + record_line (first_line + lptr->l_lnno, lptr->l_addr.l_paddr); + } +} + +static int +hashname (name) + char *name; +{ + register char *p = name; + register int total = p[0]; + register int c; + + c = p[1]; + total += c << 2; + if (c) + { + c = p[2]; + total += c << 4; + if (c) + total += p[3] << 6; + } + + return total % HASHSIZE; +} + +static void +patch_type (type, real_type) + struct type *type; + struct type *real_type; +{ + register struct type *target = TYPE_TARGET_TYPE (type); + register struct type *real_target = TYPE_TARGET_TYPE (real_type); + int field_size = TYPE_NFIELDS (real_target) * sizeof (struct field); + + TYPE_LENGTH (target) = TYPE_LENGTH (real_target); + TYPE_NFIELDS (target) = TYPE_NFIELDS (real_target); + TYPE_FIELDS (target) = (struct field *) + obstack_alloc (symbol_obstack, field_size); + + bcopy (TYPE_FIELDS (real_target), TYPE_FIELDS (target), field_size); + + if (TYPE_NAME (real_target)) + { + if (TYPE_NAME (target)) + free (TYPE_NAME (target)); + TYPE_NAME (target) = concat (TYPE_NAME (real_target), "", ""); + } +} + +/* Patch up all appropriate typdef symbols in the opaque_type_chains + so that they can be used to print out opaque data structures properly */ + +static void +patch_opaque_types () +{ + struct symtab *s; + + /* Look at each symbol in the per-file block of each symtab. */ + for (s = symtab_list; s; s = s->next) + { + register struct block *b; + register int i; + + /* Go through the per-file symbols only */ + b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1); + for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--) + { + register struct symbol *real_sym; + + /* Find completed typedefs to use to fix opaque ones. + Remove syms from the chain when their types are stored, + but search the whole chain, as there may be several syms + from different files with the same name. */ + real_sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (real_sym) == LOC_TYPEDEF && + SYMBOL_NAMESPACE (real_sym) == VAR_NAMESPACE && + TYPE_CODE (SYMBOL_TYPE (real_sym)) == TYPE_CODE_PTR && + TYPE_LENGTH (TYPE_TARGET_TYPE (SYMBOL_TYPE (real_sym))) != 0) + { + register char *name = SYMBOL_NAME (real_sym); + register int hash = hashname (name); + register struct symbol *sym, *prev; + + prev = 0; + for (sym = opaque_type_chain[hash]; sym;) + { + if (name[0] == SYMBOL_NAME (sym)[0] && + !strcmp (name + 1, SYMBOL_NAME (sym) + 1)) + { + if (prev) + SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym); + else + opaque_type_chain[hash] + = (struct symbol *) SYMBOL_VALUE (sym); + + patch_type (SYMBOL_TYPE (sym), SYMBOL_TYPE (real_sym)); + + if (prev) + sym = (struct symbol *) SYMBOL_VALUE (prev); + else + sym = opaque_type_chain[hash]; + } + else + { + prev = sym; + sym = (struct symbol *) SYMBOL_VALUE (sym); + } + } + } + } + } +} + +static struct symbol * +process_coff_symbol (cs, aux) + register struct coff_symbol *cs; + register AUXENT *aux; +{ + register struct symbol *sym + = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol)); + char *name; + char *dot; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + bzero (sym, sizeof (struct symbol)); + name = cs->c_name; + name = (name[0] == '_' ? name + offset : name); + SYMBOL_NAME (sym) = obstack_copy0 (symbol_obstack, name, strlen (name)); + + /* default assumptions */ + SYMBOL_VALUE (sym) = cs->c_value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + + if (ISFCN (cs->c_type)) + { + SYMBOL_TYPE (sym) = + lookup_function_type (decode_function_type (cs, cs->c_type, aux)); + SYMBOL_CLASS (sym) = LOC_BLOCK; + if (cs->c_sclass == C_STAT) + add_symbol_to_list (sym, &file_symbols); + else if (cs->c_sclass == C_EXT) + add_symbol_to_list (sym, &global_symbols); + } + else + { + SYMBOL_TYPE (sym) = decode_type (cs, cs->c_type, aux); + switch (cs->c_sclass) + { + case C_NULL: + break; + + case C_AUTO: + SYMBOL_CLASS (sym) = LOC_LOCAL; + add_symbol_to_list (sym, &local_symbols); + break; + + case C_EXT: + SYMBOL_CLASS (sym) = LOC_STATIC; + add_symbol_to_list (sym, &global_symbols); + break; + + case C_STAT: + SYMBOL_CLASS (sym) = LOC_STATIC; + if (within_function) { + /* Static symbol of local scope */ + add_symbol_to_list (sym, &local_symbols); + } + else { + /* Static symbol at top level of file */ + add_symbol_to_list (sym, &file_symbols); + } + break; + + case C_REG: + case C_REGPARM: + SYMBOL_CLASS (sym) = LOC_REGISTER; + add_symbol_to_list (sym, &local_symbols); + break; + + case C_LABEL: + break; + + case C_ARG: + SYMBOL_CLASS (sym) = LOC_ARG; + add_symbol_to_list (sym, &local_symbols); + /* If PCC says a parameter is a short or a char, + it is really an int. */ + if (SYMBOL_TYPE (sym) == builtin_type_char + || SYMBOL_TYPE (sym) == builtin_type_short) + SYMBOL_TYPE (sym) = builtin_type_int; + else if (SYMBOL_TYPE (sym) == builtin_type_unsigned_char + || SYMBOL_TYPE (sym) == builtin_type_unsigned_short) + SYMBOL_TYPE (sym) = builtin_type_unsigned_int; + break; + + case C_TPDEF: + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + + /* If type has no name, give it one */ + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) + = concat (SYMBOL_NAME (sym), "", ""); + + /* Keep track of any type which points to empty structured type, + so it can be filled from a definition from another file */ + if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_PTR && + TYPE_LENGTH (TYPE_TARGET_TYPE (SYMBOL_TYPE (sym))) == 0) + { + register int i = hashname (SYMBOL_NAME (sym)); + + SYMBOL_VALUE (sym) = (int) opaque_type_chain[i]; + opaque_type_chain[i] = sym; + } + add_symbol_to_list (sym, &file_symbols); + break; + + case C_STRTAG: + case C_UNTAG: + case C_ENTAG: + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE; + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) + = concat ("", + (cs->c_sclass == C_ENTAG + ? "enum " + : (cs->c_sclass == C_STRTAG + ? "struct " : "union ")), + SYMBOL_NAME (sym)); + add_symbol_to_list (sym, &file_symbols); + break; + + default: + break; + } + } + return sym; +} + +/* Decode a coff type specifier; + return the type that is meant. */ + +static +struct type * +decode_type (cs, c_type, aux) + register struct coff_symbol *cs; + unsigned int c_type; + register AUXENT *aux; +{ + register struct type *type = 0; + register int n; + unsigned int new_c_type; + + if (c_type & ~N_BTMASK) + { + new_c_type = DECREF (c_type); + if (ISPTR (c_type)) + { + type = decode_type (cs, new_c_type, aux); + type = lookup_pointer_type (type); + } + else if (ISFCN (c_type)) + { + type = decode_type (cs, new_c_type, aux); + type = lookup_function_type (type); + } + else if (ISARY (c_type)) + { + int i, n; + register unsigned short *dim; + struct type *base_type; + + /* Define an array type. */ + /* auxent refers to array, not base type */ + if (aux->x_sym.x_tagndx == 0) + cs->c_nsyms = 1; + + /* shift the indices down */ + dim = &aux->x_sym.x_fcnary.x_ary.x_dimen[0]; + i = 1; + n = dim[0]; + for (i = 0; *dim && i < DIMNUM - 1; i++, dim++) + *dim = *(dim + 1); + *dim = 0; + + type = (struct type *) + obstack_alloc (symbol_obstack, sizeof (struct type)); + bzero (type, sizeof (struct type)); + + base_type = decode_type (cs, new_c_type, aux); + + TYPE_CODE (type) = TYPE_CODE_ARRAY; + TYPE_TARGET_TYPE (type) = base_type; + TYPE_LENGTH (type) = n * TYPE_LENGTH (base_type); + } + return type; + } + + /* Reference to existing type */ + if (cs->c_nsyms > 1 && aux->x_sym.x_tagndx != 0) + { + type = coff_alloc_type (aux->x_sym.x_tagndx); + return type; + } + + return decode_base_type (cs, BTYPE (c_type), aux); +} + +/* Decode a coff type specifier for function definition; + return the type that the function returns. */ + +static +struct type * +decode_function_type (cs, c_type, aux) + register struct coff_symbol *cs; + unsigned int c_type; + register AUXENT *aux; +{ + if (aux->x_sym.x_tagndx == 0) + cs->c_nsyms = 1; /* auxent refers to function, not base type */ + + return decode_type (cs, DECREF (cs->c_type), aux); +} + +/* basic C types */ + +static +struct type * +decode_base_type (cs, c_type, aux) + register struct coff_symbol *cs; + unsigned int c_type; + register AUXENT *aux; +{ + struct type *type; + + switch (c_type) + { + case T_NULL: + /* shows up with "void (*foo)();" structure members */ + return builtin_type_void; + + case T_ARG: + /* shouldn't show up here */ + break; + + case T_CHAR: + return builtin_type_char; + + case T_SHORT: + return builtin_type_short; + + case T_INT: + return builtin_type_int; + + case T_LONG: + return builtin_type_long; + + case T_FLOAT: + return builtin_type_float; + + case T_DOUBLE: + return builtin_type_double; + + case T_STRUCT: + if (cs->c_nsyms != 2) + { + /* anonymous structure type */ + type = coff_alloc_type (cs->c_symnum); + TYPE_CODE (type) = TYPE_CODE_STRUCT; + TYPE_NAME (type) = concat ("struct ", "<opaque>", ""); + TYPE_LENGTH (type) = 0; + TYPE_FIELDS (type) = 0; + TYPE_NFIELDS (type) = 0; + } + else + { + type = read_struct_type (cs->c_symnum, + aux->x_sym.x_misc.x_lnsz.x_size, + aux->x_sym.x_fcnary.x_fcn.x_endndx); + } + return type; + + case T_UNION: + if (cs->c_nsyms != 2) + { + /* anonymous union type */ + type = coff_alloc_type (cs->c_symnum); + TYPE_NAME (type) = concat ("union ", "<opaque>", ""); + TYPE_LENGTH (type) = 0; + TYPE_FIELDS (type) = 0; + TYPE_NFIELDS (type) = 0; + } + else + { + type = read_struct_type (cs->c_symnum, + aux->x_sym.x_misc.x_lnsz.x_size, + aux->x_sym.x_fcnary.x_fcn.x_endndx); + } + TYPE_CODE (type) = TYPE_CODE_UNION; + return type; + + case T_ENUM: + return read_enum_type (cs->c_symnum, + aux->x_sym.x_misc.x_lnsz.x_size, + aux->x_sym.x_fcnary.x_fcn.x_endndx); + + case T_MOE: + /* shouldn't show up here */ + break; + + case T_UCHAR: + return builtin_type_unsigned_char; + + case T_USHORT: + return builtin_type_unsigned_short; + + case T_UINT: + return builtin_type_unsigned_int; + + case T_ULONG: + return builtin_type_unsigned_long; + } + printf ("unexpected type %d at symnum %d\n", c_type, cs->c_symnum); + return builtin_type_void; +} + +/* This page contains subroutines of read_type. */ + +/* Read the description of a structure (or union type) + and return an object describing the type. */ + +static struct type * +read_struct_type (index, length, lastsym) + int index; + int length; + int lastsym; +{ + struct nextfield + { + struct nextfield *next; + struct field field; + }; + + register struct type *type; + register struct nextfield *list = 0; + struct nextfield *new; + int nfields = 0; + register int n; + char *name; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + struct coff_symbol member_sym; + register struct coff_symbol *ms = &member_sym; + SYMENT sub_sym; + AUXENT sub_aux; + int done = 0; + + type = coff_alloc_type (index); + TYPE_CODE (type) = TYPE_CODE_STRUCT; + TYPE_LENGTH (type) = length; + + while (!done && symnum < lastsym && symnum < nlist_nsyms_global) + { + read_one_sym (ms, &sub_sym, &sub_aux); + name = ms->c_name; + name = (name[0] == '_' ? name + offset : name); + + switch (ms->c_sclass) + { + case C_MOS: + case C_MOU: + + /* Get space to record the next field's data. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + /* Save the data. */ + list->field.name = savestring (name, strlen (name)); + list->field.type = decode_type (ms, ms->c_type, &sub_aux); + list->field.bitpos = 8 * ms->c_value; + list->field.bitsize = 0; + nfields++; + break; + + case C_FIELD: + + /* Get space to record the next field's data. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + /* Save the data. */ + list->field.name = savestring (name, strlen (name)); + list->field.type = decode_type (ms, ms->c_type, &sub_aux); + list->field.bitpos = ms->c_value; + list->field.bitsize = sub_aux.x_sym.x_misc.x_lnsz.x_size; + nfields++; + break; + + case C_EOS: + done = 1; + break; + } + } + /* Now create the vector of fields, and record how big it is. */ + + TYPE_NFIELDS (type) = nfields; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (symbol_obstack, sizeof (struct field) * nfields); + + /* Copy the saved-up fields into the field vector. */ + + for (n = nfields; list; list = list->next) + TYPE_FIELD (type, --n) = list->field; + + return type; +} + +/* Read a definition of an enumeration type, + and create and return a suitable type object. + Also defines the symbols that represent the values of the type. */ + +static struct type * +read_enum_type (index, length, lastsym) + int index; + int length; + int lastsym; +{ + register struct symbol *sym; + register struct type *type; + int nsyms = 0; + struct pending **symlist; + struct coff_symbol member_sym; + register struct coff_symbol *ms = &member_sym; + SYMENT sub_sym; + AUXENT sub_aux; + struct pending *osyms, *syms; + register int n; + char *name; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + type = coff_alloc_type (index); + if (within_function) + symlist = &local_symbols; + else + symlist = &file_symbols; + osyms = *symlist; + + while (symnum < lastsym && symnum < nlist_nsyms_global) + { + read_one_sym (ms, &sub_sym, &sub_aux); + name = ms->c_name; + name = (name[0] == '_' ? name + offset : name); + + switch (ms->c_sclass) + { + case C_MOE: + sym = (struct symbol *) xmalloc (sizeof (struct symbol)); + bzero (sym, sizeof (struct symbol)); + + SYMBOL_NAME (sym) = savestring (name, strlen (name)); + SYMBOL_CLASS (sym) = LOC_CONST; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_VALUE (sym) = ms->c_value; + add_symbol_to_list (sym, symlist); + nsyms++; + break; + + case C_EOS: + break; + } + } + + /* Now fill in the fields of the type-structure. */ + + TYPE_LENGTH (type) = sizeof (int); + TYPE_CODE (type) = TYPE_CODE_ENUM; + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (symbol_obstack, sizeof (struct field) * nsyms); + + /* Find the symbols for the values and put them into the type. + The symbols can be found in the symlist that we put them on + to cause them to be defined. osyms contains the old value + of that symlist; everything up to there was defined by us. */ + + for (syms = *symlist, n = nsyms; syms != osyms; syms = syms->next) + { + SYMBOL_TYPE (syms->symbol) = type; + TYPE_FIELD_NAME (type, --n) = SYMBOL_NAME (syms->symbol); + TYPE_FIELD_VALUE (type, n) = 0; + TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (syms->symbol); + TYPE_FIELD_BITSIZE (type, n) = 0; + } + return type; +} + +/* This function is really horrible, but to avoid it, there would need + to be more filling in of forward references. THIS SHOULD BE MOVED + OUT OF COFFREAD.C AND DBXREAD.C TO SOME PLACE WHERE IT CAN BE SHARED. */ +int +fill_in_vptr_fieldno (type) + struct type *type; +{ + if (TYPE_VPTR_FIELDNO (type) < 0) + TYPE_VPTR_FIELDNO (type) = + fill_in_vptr_fieldno (TYPE_BASECLASS (type, 1)); + return TYPE_VPTR_FIELDNO (type); +} + +/* partial symbol tables are not implemented in coff, therefore + block_for_pc() (and others) will never decide to call this. */ + +extern struct symtab * +psymtab_to_symtab () +{ + fatal ("error: Someone called psymtab_to_symtab\n"); +} + +/* These will stay zero all the time */ +struct partial_symbol *global_psymbols, *static_psymbols; + +_initialize_coff () +{ + symfile = 0; + + static_psymbols = global_psymbols = (struct partial_symbol *) 0; + + add_com ("symbol-file", class_files, symbol_file_command, + "Load symbol table (in coff format) from executable file FILE."); +} + + +#endif /* COFF_FORMAT */ + +@ + + +1.4 +log +@Avoid A/UX change (#undef aux) for sending in to FSF. +@ +text +@d40 3 +@ + + +1.3 +log +@A/UX and USG changes. If BADMAG defined, use it. Avoid <sys/fcntl.h>. +Declare fclose(). #undef aux which we use as a var. +@ +text +@a39 3 +/* Avoid problems with A/UX predefine */ +#undef aux + +@ + + +1.2 +log +@If discarding the symbol table, discard its name too, so "info files" +will give the right answer. +@ +text +@d31 1 +a31 1 +#include <sys/fcntl.h> +d40 3 +d847 1 +a847 1 + +d1093 6 +d1100 1 +@ + + +1.1 +log +@Initial revision +@ +text +@d5 1 +a5 1 + Copyright (C) 1987, 1988 Free Software Foundation, Inc. +d684 3 +@ diff --git a/gdb/RCS/config.gdb,v b/gdb/RCS/config.gdb,v new file mode 100755 index 0000000..b569f62 --- /dev/null +++ b/gdb/RCS/config.gdb,v @@ -0,0 +1,229 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @@; + + +1.2 +date 89.03.27.18.38.55; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.13.19.14.24; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Add A/UX option (config.gdb aux). +@ +text +@#!/bin/sh + +# +# Shell script to create proper links to machine-dependent files in +# preparation for compiling gdb. +# +# Usage: config.gdb machine [operating-system] +# +# If config.gdb succeeds, it leaves its status in config.status. +# If config.gdb fails after disturbing the status quo, +# config.status is removed. +# + +progname=$0 + +case $# in +1) + machine=$1 + os="none" + ;; +2) + machine=$1 + os=$2 + ;; +*) + echo "Usage: $progname machine [operating-system]" + echo "Available machine types:" + echo m-*.h | sed 's/m-//g' | sed 's/\.h//g' + if [ -r config.status ] + then + cat config.status + fi + exit 1 + ;; +esac + +paramfile=m-${machine}.h +pinsnfile=${machine}-pinsn.c +opcodefile=${machine}-opcode.h +if [ -r ${machine}-dep.c ] +then + depfile=${machine}-dep.c +else + depfile=default-dep.c +fi + +# +# Special cases. +# If a file is not needed, set the filename to 'skip' and it will be +# ignored. +# +case $machine in +aux) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +vax) + pinsnfile=vax-pinsn.c + opcodefile=vax-opcode.h + ;; +hp9k320) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +isi) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +i386) + echo "Note: i386 users need to modify \`CLIBS' & \`REGEX*' in the Makefile" + opcodefile=skip + ;; +i386gas) + echo "Note: i386 users need to modify \`CLIBS' & \`REGEX*' in the Makefile" + echo "Use of the coff encapsulation features also requires the GNU binutils utilities" + echo "to be ahead of their System V counterparts in your path." + pinsnfile=i386-pinsn.c + depfile=i386-dep.c + opcodefile=skip + ;; +merlin) + pinsnfile=ns32k-pinsn.c + opcodefile=ns32k-opcode.h + ;; +news) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +npl) + pinsnfile=gld-pinsn.c + ;; +pn) + pinsnfile=gld-pinsn.c + ;; +sun2) + case $os in + os4|sunos4) + paramfile=m-sun2os4.h + ;; + os2|sunos2) + paramfile=m-sun2os2.h + esac + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +sun2os2) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +sun2os4) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +sun3) + case $os in + os4|sunos4) + paramfile=m-sun3os4.h + esac + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + ;; +sun3os4) + pinsnfile=m68k-pinsn.c + opcodefile=m68k-opcode.h + depfile=sun3-dep.c + ;; +sun4os4) + pinsnfile=sparc-pinsn.c + opcodefile=sparc-opcode.h + depfile=sparc-dep.c + ;; +umax) + pinsnfile=ns32k-pinsn.c + opcodefile=ns32k-opcode.h + ;; +sparc|sun4) + case $os in + os4|sunos4) + paramfile=m-sun4os4.h + esac + pinsnfile=sparc-pinsn.c + opcodefile=sparc-opcode.h + depfile=sparc-dep.c + paramfile=m-sparc.h + ;; +test) + paramfile=one + pinsnfile=three + opcodefile=four + ;; +*) + echo "Unknown machine type: \`$machine'" + echo "Available types:" + echo m-*.h | sed 's/m-//g' | sed 's/\.h//g' + exit 1 +esac + +files="$paramfile $pinsnfile $opcodefile $depfile" +links="param.h pinsn.c opcode.h dep.c" + +while [ -n "$files" ] +do + # set file to car of files, files to cdr of files + set $files; file=$1; shift; files=$* + set $links; link=$1; shift; links=$* + + if [ "$file" != skip ] + then + if [ ! -r $file ] + then + echo "$progname: cannot create a link \`$link'," + echo "since the file \`$file' does not exist." + exit 1 + fi + + rm -f $link config.status + # Make a symlink if possible, otherwise try a hard link + ln -s $file $link 2>/dev/null || ln $file $link + + if [ ! -r $link ] + then + echo "$progname: unable to link \`$link' to \`$file'." + exit 1 + fi + echo "Linked \`$link' to \`$file'." + fi +done + +echo "Links are now set up for use with a $machine." \ + | tee config.status +exit 0 + +@ + + +1.1 +log +@Initial revision +@ +text +@d53 4 +@ diff --git a/gdb/RCS/core.c,v b/gdb/RCS/core.c,v new file mode 100644 index 0000000..f63081d --- /dev/null +++ b/gdb/RCS/core.c,v @@ -0,0 +1,651 @@ +head 1.4; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.4 +date 89.03.27.18.39.18; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 89.02.10.01.39.45; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.09.23.22.33; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.22.49.56; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.4 +log +@Unisoft Assholes changes for user.ps. Avoid sys/fcntl.h. +@ +text +@/* Work with core dump and executable files, for GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#include "gdbcore.h" + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#endif + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif + +#ifndef N_MAGIC +#ifdef COFF_FORMAT +#define N_MAGIC(exec) ((exec).magic) +#else +#define N_MAGIC(exec) ((exec).a_magic) +#endif +#endif + +#include <stdio.h> +#include <signal.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/file.h> +#include <sys/stat.h> + +#ifdef UNISOFT_ASSHOLES +#define PMMU +#define NEW_PMMU +#include <sys/seg.h> /* Required for user.ps */ +#include <sys/time.h> /* '' */ +#include <sys/mmu.h> /* '' */ +#include <sys/reg.h> +#define mc68881 /* Required to get float in user.ps */ +#endif + +#ifdef UMAX_CORE +#include <sys/ptrace.h> +#else +#include <sys/user.h> +#endif + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +#ifndef COFF_FORMAT +#define AOUTHDR struct exec +#endif + +extern char *sys_siglist[]; + +extern core_file_command (), exec_file_command (); + +/* Hook for `exec_file_command' command to call. */ + +void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +char *corefile; +char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +int corechan; +int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +CORE_ADDR data_start; +CORE_ADDR data_end; +CORE_ADDR stack_start; +CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +CORE_ADDR text_start; +CORE_ADDR text_end; + +CORE_ADDR exec_data_start; +CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +int text_offset; + +/* Address in executable file of start of data area data. */ + +int exec_data_offset; + +/* Address in core file of start of data area data. */ + +int data_offset; + +/* Address in core file of start of stack area data. */ + +int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +FILHDR file_hdr; +SCNHDR text_hdr; +SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +AOUTHDR exec_aouthdr; + +void validate_files (); +unsigned int register_addr (); + +/* Call this to specify the hook for exec_file_command to call back. + This is called from the x-window display code. */ + +void +specify_exec_file_hook (hook) + void (*hook) (); +{ + exec_file_display_hook = hook; +} + +/* The exec file must be closed before running an inferior. + If it is needed again after the inferior dies, it must + be reopened. */ + +void +close_exec_file () +{ + if (execchan >= 0) + close (execchan); + execchan = -1; +} + +void +reopen_exec_file () +{ + if (execchan < 0 && execfile != 0) + { + char *filename = concat (execfile, "", ""); + exec_file_command (filename, 0); + free (filename); + } +} + +/* If we have both a core file and an exec file, + print a warning if they don't go together. + This should really check that the core file came + from that exec file, but I don't know how to do it. */ + +void +validate_files () +{ + if (execfile != 0 && corefile != 0) + { + struct stat st_core; + + fstat (corechan, &st_core); + + if (N_MAGIC (core_aouthdr) != 0 + && bcmp (&core_aouthdr, &exec_aouthdr, sizeof core_aouthdr)) + printf ("Warning: core file does not match specified executable file.\n"); + else if (exec_mtime > st_core.st_mtime) + printf ("Warning: exec file is newer than core file.\n"); + } +} + +/* Return the name of the executable file as a string. + ERR nonzero means get error if there is none specified; + otherwise return 0 in that case. */ + +char * +get_exec_file (err) + int err; +{ + if (err && execfile == 0) + error ("No executable file specified.\n\ +Use the \"exec-file\" and \"symbol-file\" commands."); + return execfile; +} + +int +have_core_file_p () +{ + return corefile != 0; +} + +static void +files_info () +{ + char *symfile; + extern char *get_sym_file (); + + if (execfile) + printf ("Executable file \"%s\".\n", execfile); + else + printf ("No executable file\n"); + + if (corefile) + printf ("Core dump file \"%s\".\n", corefile); + else + printf ("No core dump file\n"); + + if (have_inferior_p ()) + printf ("Using the running image of the program, rather than these files.\n"); + + symfile = get_sym_file (); + if (symfile != 0) + printf ("Symbols from \"%s\".\n", symfile); + + if (! have_inferior_p ()) + { + if (execfile) + { + printf ("Text segment in executable from 0x%x to 0x%x.\n", + text_start, text_end); + printf ("Data segment in executable from 0x%x to 0x%x.\n", + exec_data_start, exec_data_end); + if (corefile) + printf("(But since we have a core file, we're using...)\n"); + } + if (corefile) + { + printf ("Data segment in core file from 0x%x to 0x%x.\n", + data_start, data_end); + printf ("Stack segment in core file from 0x%x to 0x%x.\n", + stack_start, stack_end); + } + } +} + +/* Read "memory data" from core file and/or executable file. + Returns zero if successful, 1 if xfer_core_file failed, errno value if + ptrace failed. */ + +int +read_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + if (have_inferior_p ()) + return read_inferior_memory (memaddr, myaddr, len); + else + return xfer_core_file (memaddr, myaddr, len); +} + +/* Write LEN bytes of data starting at address MYADDR + into debugged program memory at address MEMADDR. + Returns zero if successful, or an errno value if ptrace failed. */ + +int +write_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + if (have_inferior_p ()) + return write_inferior_memory (memaddr, myaddr, len); + else + error ("Can write memory only when program being debugged is running."); +} + +/* Read from the program's memory (except for inferior processes). + This function is misnamed, since it only reads, never writes; and + since it will use the core file and/or executable file as necessary. + + It should be extended to write as well as read, FIXME, for patching files. + + Return 0 if address could be read, 1 if not. */ + +int +xfer_core_file (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + register int val; + int xferchan; + char **xferfile; + int fileptr; + int returnval = 0; + + while (len > 0) + { + xferfile = 0; + xferchan = 0; + + /* Determine which file the next bunch of addresses reside in, + and where in the file. Set the file's read/write pointer + to point at the proper place for the desired address + and set xferfile and xferchan for the correct file. + + If desired address is nonexistent, leave them zero. + + i is set to the number of bytes that can be handled + along with the next address. + + We put the most likely tests first for efficiency. */ + + /* Note that if there is no core file + data_start and data_end are equal. */ + if (memaddr >= data_start && memaddr < data_end) + { + i = min (len, data_end - memaddr); + fileptr = memaddr - data_start + data_offset; + xferfile = &corefile; + xferchan = corechan; + } + /* Note that if there is no core file + stack_start and stack_end are equal. */ + else if (memaddr >= stack_start && memaddr < stack_end) + { + i = min (len, stack_end - memaddr); + fileptr = memaddr - stack_start + stack_offset; + xferfile = &corefile; + xferchan = corechan; + } + else if (corechan < 0 + && memaddr >= exec_data_start && memaddr < exec_data_end) + { + i = min (len, exec_data_end - memaddr); + fileptr = memaddr - exec_data_start + exec_data_offset; + xferfile = &execfile; + xferchan = execchan; + } + else if (memaddr >= text_start && memaddr < text_end) + { + i = min (len, text_end - memaddr); + fileptr = memaddr - text_start + text_offset; + xferfile = &execfile; + xferchan = execchan; + } + else if (memaddr < text_start) + { + i = min (len, text_start - memaddr); + } + else if (memaddr >= text_end + && memaddr < (corechan >= 0? data_start : exec_data_start)) + { + i = min (len, data_start - memaddr); + } + else if (memaddr >= (corechan >= 0 ? data_end : exec_data_end) + && memaddr < stack_start) + { + i = min (len, stack_start - memaddr); + } + else if (memaddr >= stack_end && stack_end != 0) + { + i = min (len, - memaddr); + } + else + { + /* Address did not classify into one of the known ranges. + This could be because data_start != exec_data_start + or data_end similarly. */ + abort(); + } + + /* Now we know which file to use. + Set up its pointer and transfer the data. */ + if (xferfile) + { + if (*xferfile == 0) + if (xferfile == &execfile) + error ("No program file to examine."); + else + error ("No core dump file or running program to examine."); + val = lseek (xferchan, fileptr, 0); + if (val < 0) + perror_with_name (*xferfile); + val = myread (xferchan, myaddr, i); + if (val < 0) + perror_with_name (*xferfile); + } + /* If this address is for nonexistent memory, + read zeros if reading, or do nothing if writing. + (FIXME we never write.) */ + else + { + bzero (myaddr, i); + returnval = 1; + } + + memaddr += i; + myaddr += i; + len -= i; + } + return returnval; +} + +/* My replacement for the read system call. + Used like `read' but keeps going if `read' returns too soon. */ + +int +myread (desc, addr, len) + int desc; + char *addr; + int len; +{ + register int val; + int orglen = len; + + while (len > 0) + { + val = read (desc, addr, len); + if (val < 0) + return val; + if (val == 0) + return orglen - len; + len -= val; + addr += val; + } + return orglen; +} + +#ifdef REGISTER_U_ADDR + +/* Return the address in the core dump or inferior of register REGNO. + BLOCKEND is the address of the end of the user structure. */ + +unsigned int +register_addr (regno, blockend) + int regno; + int blockend; +{ + int addr; + + if (regno < 0 || regno >= NUM_REGS) + error ("Invalid register number %d.", regno); + + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +#endif /* REGISTER_U_ADDR */ + +void +_initialize_core() +{ + corechan = -1; + execchan = -1; + corefile = 0; + execfile = 0; + exec_file_display_hook = 0; + + text_start = 0; + text_end = 0; + data_start = 0; + data_end = 0; + exec_data_start = 0; + exec_data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + add_com ("core-file", class_files, core_file_command, + "Use FILE as core dump for examining memory and registers.\n\ +No arg means have no core file."); + add_com ("exec-file", class_files, exec_file_command, + "Use FILE as program for getting contents of pure memory.\n\ +If FILE cannot be found as specified, your execution directory path\n\ +is searched for a command of that name.\n\ +No arg means have no executable file."); + add_info ("files", files_info, "Names of files being debugged."); +} + +@ + + +1.3 +log +@Fix up "info files" some more, to give more information. +Rearrange the tests in xfer_core_file to avoid dependencies +between data_start and exec_data_start, and for efficiency +and add an abort() to test correctness. (If you take out +never mind...) +@ +text +@d27 1 +a27 1 +#include <sys/fcntl.h> +d50 10 +@ + + +1.2 +log +@Create gdbcore.h for externally visible variables; +spiff up the "info files" output to make it easier to read and more +informative. +@ +text +@d250 4 +d257 4 +a260 7 + printf ("Data segment in core file from 0x%x to 0x%x.\nStack segment in core file from 0x%x to 0x%x.\n", + data_start, data_end, stack_start, stack_end); + } + else if (execfile) + { + printf ("Data segment in executable from 0x%x to 0x%x.\n", + exec_data_start, exec_data_end); +d297 3 +a299 1 +/* Return 0 if address could be read, 1 if not. */ +d301 4 +d327 1 +d329 1 +d331 3 +a333 1 + along with the next address. */ +a334 17 + if (memaddr < text_start) + { + i = min (len, text_start - memaddr); + } + else if (memaddr >= text_end && memaddr < data_start) + { + i = min (len, data_start - memaddr); + } + else if (memaddr >= (corechan >= 0 ? data_end : exec_data_end) + && memaddr < stack_start) + { + i = min (len, stack_start - memaddr); + } + else if (memaddr >= stack_end && stack_end != 0) + { + i = min (len, - memaddr); + } +d337 1 +a337 1 + else if (memaddr >= data_start && memaddr < data_end) +d368 25 +d411 2 +a412 1 + read zeros if reading, or do nothing if writing. */ +@ + + +1.1 +log +@Initial revision +@ +text +@d2 1 +a2 1 + Copyright (C) 1986, 1987 Free Software Foundation, Inc. +d23 1 +d35 1 +d43 1 +d231 4 +a234 1 + if (corefile == 0) +a235 2 + else + printf ("Core dump file \"%s\".\n", corefile); +d242 1 +a242 1 + printf ("Symbols loaded from \"%s\".\n", symfile); +d248 1 +a248 1 + printf ("Text segment from 0x%x to 0x%x.\n", +d253 1 +a253 1 + printf ("Data segment from 0x%x to 0x%x.\nStack segment from 0x%x to 0x%x.\n", +d256 1 +a256 1 + else +@ diff --git a/gdb/RCS/dbxread.c,v b/gdb/RCS/dbxread.c,v new file mode 100644 index 0000000..eb9d1cd --- /dev/null +++ b/gdb/RCS/dbxread.c,v @@ -0,0 +1,4610 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.03.27.18.41.32; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.10.01.38.34; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.10.01.30.11; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@Avoid sys/fcntl.h. +@ +text +@/* Read dbx symbol tables and convert to internal format, for GDB. + Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "param.h" + +#ifdef READ_DBX_FORMAT + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#define L_SET 0 +#define L_INCR 1 +#endif + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#include "stab.gnu.h" +#else +#include <a.out.h> +#include <stab.h> +#endif + +/* + * Define specifically gnu symbols here. + */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +#ifndef N_WARNING +#define N_WARNING 0x1E /* Warning message to print if file included */ +#endif /* This is input to ld */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ + +#include <stdio.h> +#include <obstack.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include "defs.h" +#include "symtab.h" + +#ifndef COFF_FORMAT +#define AOUTHDR struct exec +#endif + +static void add_symbol_to_list (); +static void read_dbx_symtab (); +static void process_one_symbol (); +static void free_all_psymbols (); +static struct type *read_type (); +static struct type *read_range_type (); +static struct type *read_enum_type (); +static struct type *read_struct_type (); +static struct type *read_array_type (); +static long read_number (); +static void finish_block (); +static struct blockvector *make_blockvector (); +static struct symbol *define_symbol (); +static void start_subfile (); +static int hashname (); +static void hash_symsegs (); + +extern struct symtab *read_symsegs (); +extern void free_all_symtabs (); +extern void free_all_psymtabs (); +extern void free_inclink_symtabs (); + +/* C++ */ +static struct type **read_args(); + +/* Macro to determine which symbols to ignore when reading the first symbol + of a file. Some machines override this definition. */ +#ifdef N_NSYMS +#ifndef IGNORE_SYMBOL +/* This code is used on Ultrix systems. Ignore it */ +#define IGNORE_SYMBOL(type) (type == N_NSYMS) +#endif +#else +#ifndef IGNORE_SYMBOL +/* Don't ignore any symbols. */ +#define IGNORE_SYMBOL(type) (0) +#endif +#endif /* not N_NSYMS */ + +/* Macro for number of symbol table entries (in usual a.out format). + Some machines override this definition. */ +#ifndef NUMBER_OF_SYMBOLS +#ifdef COFF_HEADER +#define NUMBER_OF_SYMBOLS \ + ((COFF_HEADER(hdr) ? hdr.coffhdr.filehdr.f_nsyms : hdr.a_syms) / \ + sizeof (struct nlist)) +#else +#define NUMBER_OF_SYMBOLS (hdr.a_syms / sizeof (struct nlist)) +#endif +#endif + +/* Macro for file-offset of symbol table (in usual a.out format). */ +#ifndef SYMBOL_TABLE_OFFSET +#define SYMBOL_TABLE_OFFSET N_SYMOFF (hdr) +#endif + +/* Macro for file-offset of string table (in usual a.out format). */ +#ifndef STRING_TABLE_OFFSET +#define STRING_TABLE_OFFSET (N_SYMOFF (hdr) + hdr.a_syms) +#endif + +/* Macro to store the length of the string table data in INTO. */ +#ifndef READ_STRING_TABLE_SIZE +#define READ_STRING_TABLE_SIZE(INTO) \ +{ val = myread (desc, &INTO, sizeof INTO); \ + if (val < 0) perror_with_name (name); } +#endif + +/* Macro to declare variables to hold the file's header data. */ +#ifndef DECLARE_FILE_HEADERS +#define DECLARE_FILE_HEADERS AOUTHDR hdr +#endif + +/* Macro to read the header data from descriptor DESC and validate it. + NAME is the file name, for error messages. */ +#ifndef READ_FILE_HEADERS +#ifdef HEADER_SEEK_FD +#define READ_FILE_HEADERS(DESC, NAME) \ +{ HEADER_SEEK_FD (DESC); \ + val = myread (DESC, &hdr, sizeof hdr); \ + if (val < 0) perror_with_name (NAME); \ + if (N_BADMAG (hdr)) \ + error ("File \"%s\" not in executable format.", NAME); } +#else +#define READ_FILE_HEADERS(DESC, NAME) \ +{ val = myread (DESC, &hdr, sizeof hdr); \ + if (val < 0) perror_with_name (NAME); \ + if (N_BADMAG (hdr)) \ + error ("File \"%s\" not in executable format.", NAME); } +#endif +#endif + +/* Macro for size of text segment */ +#ifndef SIZE_OF_TEXT_SEGMENT +#define SIZE_OF_TEXT_SEGMENT hdr.a_text +#endif + +/* Macro for name of symbol to indicate a file compiled with gcc. */ +#ifndef GCC_COMPILED_FLAG_SYMBOL +#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled." +#endif + +/* Chain of symtabs made from reading the file's symsegs. + These symtabs do not go into symtab_list themselves, + but the information is copied from them when appropriate + to make the symtabs that will exist permanently. */ + +static struct symtab *symseg_chain; + +/* Symseg symbol table for the file whose data we are now processing. + It is one of those in symseg_chain. Or 0, for a compilation that + has no symseg. */ + +static struct symtab *current_symseg; + +/* Name of source file whose symbol data we are now processing. + This comes from a symbol of type N_SO. */ + +static char *last_source_file; + +/* Core address of start of text of current source file. + This too comes from the N_SO symbol. */ + +static CORE_ADDR last_source_start_addr; + +/* End of the text segment of the executable file, + as found in the symbol _etext. */ + +static CORE_ADDR end_of_text_addr; + +/* The list of sub-source-files within the current individual compilation. + Each file gets its own symtab with its own linetable and associated info, + but they all share one blockvector. */ + +struct subfile +{ + struct subfile *next; + char *name; + struct linetable *line_vector; + int line_vector_length; + int line_vector_index; + int prev_line_number; +}; + +static struct subfile *subfiles; + +static struct subfile *current_subfile; + +/* Count symbols as they are processed, for error messages. */ + +static int symnum; + +/* Vector of types defined so far, indexed by their dbx type numbers. + (In newer sun systems, dbx uses a pair of numbers in parens, + as in "(SUBFILENUM,NUMWITHINSUBFILE)". Then these numbers must be + translated through the type_translations hash table to get + the index into the type vector.) */ + +static struct typevector *type_vector; + +/* Number of elements allocated for type_vector currently. */ + +static int type_vector_length; + +/* Vector of line number information. */ + +static struct linetable *line_vector; + +/* Index of next entry to go in line_vector_index. */ + +static int line_vector_index; + +/* Last line number recorded in the line vector. */ + +static int prev_line_number; + +/* Number of elements allocated for line_vector currently. */ + +static int line_vector_length; + +/* Hash table of global symbols whose values are not known yet. + They are chained thru the SYMBOL_VALUE, since we don't + have the correct data for that slot yet. */ + +#define HASHSIZE 127 +static struct symbol *global_sym_chain[HASHSIZE]; + +/* Record the symbols defined for each context in a list. + We don't create a struct block for the context until we + know how long to make it. */ + +#define PENDINGSIZE 100 + +struct pending +{ + struct pending *next; + int nsyms; + struct symbol *symbol[PENDINGSIZE]; +}; + +/* List of free `struct pending' structures for reuse. */ +struct pending *free_pendings; + +/* Here are the three lists that symbols are put on. */ + +struct pending *file_symbols; /* static at top level, and types */ + +struct pending *global_symbols; /* global functions and variables */ + +struct pending *local_symbols; /* everything local to lexical context */ + +/* Stack representing unclosed lexical contexts + (that will become blocks, eventually). */ + +struct context_stack +{ + struct pending *locals; + struct pending_block *old_blocks; + struct symbol *name; + CORE_ADDR start_addr; + int depth; +}; + +struct context_stack *context_stack; + +/* Index of first unused entry in context stack. */ +int context_stack_depth; + +/* Currently allocated size of context stack. */ + +int context_stack_size; + +/* Nonzero if within a function (so symbols should be local, + if nothing says specifically). */ + +int within_function; + +/* List of blocks already made (lexical contexts already closed). + This is used at the end to make the blockvector. */ + +struct pending_block +{ + struct pending_block *next; + struct block *block; +}; + +struct pending_block *pending_blocks; + +extern CORE_ADDR first_object_file_end; /* From blockframe.c */ + +/* File name symbols were loaded from. */ + +static char *symfile; + +/* Low and high symbol values (inclusive) for the global variable + entries in the symbol file. */ + +static int first_global_sym, last_global_sym; + +/* Partial symbol list for all of the global and static symbols found + in a file */ + +struct partial_symbol *global_psymbols, *static_psymbols; +int global_psymbols_allocated, static_psymbols_allocated; + +/* Position for next psymbol to be added */ + +struct partial_symbol *next_ps_global, *next_ps_static; + +/* Global variable which, when set, indicates that we are processing a + .o file compiled with gcc */ + +static unsigned char processing_gcc_compilation; + +static int +xxmalloc (n) +{ + int v = malloc (n); + if (v == 0) + abort (); + return v; +} + +/* Make a copy of the string at PTR with SIZE characters in the symbol obstack + (and add a null character at the end in the copy). + Returns the address of the copy. */ + +static char * +obsavestring (ptr, size) + char *ptr; + int size; +{ + register char *p = (char *) obstack_alloc (symbol_obstack, size + 1); + /* Open-coded bcopy--saves function call time. + These strings are usually short. */ + { + register char *p1 = ptr; + register char *p2 = p; + char *end = ptr + size; + while (p1 != end) + *p2++ = *p1++; + } + p[size] = 0; + return p; +} + +/* Concatenate strings S1, S2 and S3; return the new string. + Space is found in the symbol_obstack. */ + +static char * +obconcat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1; + register char *val = (char *) obstack_alloc (symbol_obstack, len); + strcpy (val, s1); + strcat (val, s2); + strcat (val, s3); + return val; +} + +/* Support for Sun changes to dbx symbol format */ + +/* For each identified header file, we have a table of types defined + in that header file. + + header_files maps header file names to their type tables. + It is a vector of n_header_files elements. + Each element describes one header file. + It contains a vector of types. + + Sometimes it can happen that the same header file produces + different results when included in different places. + This can result from conditionals or from different + things done before including the file. + When this happens, there are multiple entries for the file in this table, + one entry for each distinct set of results. + The entries are distinguished by the INSTANCE field. + The INSTANCE field appears in the N_BINCL and N_EXCL symbol table and is + used to match header-file references to their corresponding data. */ + +struct header_file +{ + char *name; /* Name of header file */ + int instance; /* Numeric code distinguishing instances + of one header file that produced + different results when included. + It comes from the N_BINCL or N_EXCL. */ + struct type **vector; /* Pointer to vector of types */ + int length; /* Allocated length (# elts) of that vector */ +}; + +static struct header_file *header_files; + +static int n_header_files; + +static int n_allocated_header_files; + +/* During initial symbol readin, we need to have a structure to keep + track of which psymtabs have which bincls in them. This structure + is used during readin to setup the list of dependencies within each + partial symbol table. */ + +struct header_file_location +{ + char *name; /* Name of header file */ + int instance; /* See above */ + struct partial_symtab *pst; /* Partial symtab that has the + BINCL/EINCL defs for this file */ +}; + +/* The actual list and controling variables */ +static struct header_file_location *bincl_list, *next_bincl; +static int bincls_allocated; + +/* Within each object file, various header files are assigned numbers. + A type is defined or referred to with a pair of numbers + (FILENUM,TYPENUM) where FILENUM is the number of the header file + and TYPENUM is the number within that header file. + TYPENUM is the index within the vector of types for that header file. + + FILENUM == 1 is special; it refers to the main source of the object file, + and not to any header file. FILENUM != 1 is interpreted by looking it up + in the following table, which contains indices in header_files. */ + +static int *this_object_header_files; + +static int n_this_object_header_files; + +static int n_allocated_this_object_header_files; + +/* When a header file is getting special overriding definitions + for one source file, record here the header_files index + of its normal definition vector. + At other times, this is -1. */ + +static int header_file_prev_index; + +/* At the start of reading dbx symbols, allocate our tables. */ + +static void +init_header_files () +{ + n_allocated_header_files = 10; + header_files = (struct header_file *) xxmalloc (10 * sizeof (struct header_file)); + n_header_files = 0; + + n_allocated_this_object_header_files = 10; + this_object_header_files = (int *) xxmalloc (10 * sizeof (int)); +} + +/* At the end of reading dbx symbols, free our tables. */ + +static void +free_header_files () +{ + register int i; + for (i = 0; i < n_header_files; i++) + free (header_files[i].name); + if (header_files) free (header_files); + if (this_object_header_files) + free (this_object_header_files); +} + +/* Called at the start of each object file's symbols. + Clear out the mapping of header file numbers to header files. */ + +static void +new_object_header_files () +{ + /* Leave FILENUM of 0 free for builtin types and this file's types. */ + n_this_object_header_files = 1; + header_file_prev_index = -1; +} + +/* Add header file number I for this object file + at the next successive FILENUM. */ + +static void +add_this_object_header_file (i) + int i; +{ + if (n_this_object_header_files == n_allocated_this_object_header_files) + { + n_allocated_this_object_header_files *= 2; + this_object_header_files + = (int *) xrealloc (this_object_header_files, + n_allocated_this_object_header_files * sizeof (int)); + } + + this_object_header_files[n_this_object_header_files++] = i; +} + +/* Add to this file an "old" header file, one already seen in + a previous object file. NAME is the header file's name. + INSTANCE is its instance code, to select among multiple + symbol tables for the same header file. */ + +static void +add_old_header_file (name, instance) + char *name; + int instance; +{ + register struct header_file *p = header_files; + register int i; + + for (i = 0; i < n_header_files; i++) + if (!strcmp (p[i].name, name) && instance == p[i].instance) + { + add_this_object_header_file (i); + return; + } + error ("Invalid symbol data: \"repeated\" header file that hasn't been seen before, at symtab pos %d.", + symnum); +} + +/* Add to this file a "new" header file: definitions for its types follow. + NAME is the header file's name. + Most often this happens only once for each distinct header file, + but not necessarily. If it happens more than once, INSTANCE has + a different value each time, and references to the header file + use INSTANCE values to select among them. + + dbx output contains "begin" and "end" markers for each new header file, + but at this level we just need to know which files there have been; + so we record the file when its "begin" is seen and ignore the "end". */ + +static void +add_new_header_file (name, instance) + char *name; + int instance; +{ + register int i; + register struct header_file *p = header_files; + header_file_prev_index = -1; + +#if 0 + /* This code was used before I knew about the instance codes. + My first hypothesis is that it is not necessary now + that instance codes are handled. */ + + /* Has this header file a previous definition? + If so, make a new entry anyway so that this use in this source file + gets a separate entry. Later source files get the old entry. + Record here the index of the old entry, so that any type indices + not previously defined can get defined in the old entry as + well as in the new one. */ + + for (i = 0; i < n_header_files; i++) + if (!strcmp (p[i].name, name)) + { + header_file_prev_index = i; + } + +#endif + + /* Make sure there is room for one more header file. */ + + if (n_header_files == n_allocated_header_files) + { + n_allocated_header_files *= 2; + header_files = (struct header_file *) + xrealloc (header_files, + (n_allocated_header_files + * sizeof (struct header_file))); + } + + /* Create an entry for this header file. */ + + i = n_header_files++; + header_files[i].name = savestring (name, strlen(name)); + header_files[i].instance = instance; + header_files[i].length = 10; + header_files[i].vector + = (struct type **) xxmalloc (10 * sizeof (struct type *)); + bzero (header_files[i].vector, 10 * sizeof (struct type *)); + + add_this_object_header_file (i); +} + +/* Look up a dbx type-number pair. Return the address of the slot + where the type for that number-pair is stored. + The number-pair is in TYPENUMS. + + This can be used for finding the type associated with that pair + or for associating a new type with the pair. */ + +static struct type ** +dbx_lookup_type (typenums) + int typenums[2]; +{ + register int filenum = typenums[0], index = typenums[1]; + + if (filenum < 0 || filenum >= n_this_object_header_files) + error ("Invalid symbol data: type number (%d,%d) out of range at symtab pos %d.", + filenum, index, symnum); + + if (filenum == 0) + { + /* Type is defined outside of header files. + Find it in this object file's type vector. */ + if (index >= type_vector_length) + { + type_vector_length *= 2; + type_vector = (struct typevector *) + xrealloc (type_vector, + (sizeof (struct typevector) + + type_vector_length * sizeof (struct type *))); + bzero (&type_vector->type[type_vector_length / 2], + type_vector_length * sizeof (struct type *) / 2); + } + return &type_vector->type[index]; + } + else + { + register int real_filenum = this_object_header_files[filenum]; + register struct header_file *f; + + if (real_filenum >= n_header_files) + abort (); + + f = &header_files[real_filenum]; + + if (index >= f->length) + { + f->length *= 2; + f->vector = (struct type **) + xrealloc (f->vector, f->length * sizeof (struct type *)); + bzero (&f->vector[f->length / 2], + f->length * sizeof (struct type *) / 2); + } + return &f->vector[index]; + } +} + +/* Make sure there is a type allocated for type numbers TYPENUMS + and return the type object. + This can create an empty (zeroed) type object. */ + +static struct type * +dbx_alloc_type (typenums) + int typenums[2]; +{ + register struct type **type_addr = dbx_lookup_type (typenums); + register struct type *type = *type_addr; + + /* If we are referring to a type not known at all yet, + allocate an empty type for it. + We will fill it in later if we find out how. */ + if (type == 0) + { + type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + bzero (type, sizeof (struct type)); + TYPE_VPTR_FIELDNO (type) = -1; + *type_addr = type; + } + return type; +} + +#if 0 +static struct type ** +explicit_lookup_type (real_filenum, index) + int real_filenum, index; +{ + register struct header_file *f = &header_files[real_filenum]; + + if (index >= f->length) + { + f->length *= 2; + f->vector = (struct type **) + xrealloc (f->vector, f->length * sizeof (struct type *)); + bzero (&f->vector[f->length / 2], + f->length * sizeof (struct type *) / 2); + } + return &f->vector[index]; +} +#endif + +/* maintain the lists of symbols and blocks */ + +/* Add a symbol to one of the lists of symbols. */ +static void +add_symbol_to_list (symbol, listhead) + struct symbol *symbol; + struct pending **listhead; +{ + /* We keep PENDINGSIZE symbols in each link of the list. + If we don't have a link with room in it, add a new link. */ + if (*listhead == 0 || (*listhead)->nsyms == PENDINGSIZE) + { + register struct pending *link; + if (free_pendings) + { + link = free_pendings; + free_pendings = link->next; + } + else + link = (struct pending *) xxmalloc (sizeof (struct pending)); + + link->next = *listhead; + *listhead = link; + link->nsyms = 0; + } + + (*listhead)->symbol[(*listhead)->nsyms++] = symbol; +} + +/* At end of reading syms, or in case of quit, + really free as many `struct pending's as we can easily find. */ + +static void +really_free_pendings () +{ + struct pending *next, *next1; + struct pending_block *bnext, *bnext1; + + for (next = free_pendings; next; next = next1) + { + next1 = next->next; + free (next); + } + free_pendings = 0; + + for (bnext = pending_blocks; bnext; bnext = bnext1) + { + bnext1 = bnext->next; + free (bnext); + } + pending_blocks = 0; + + for (next = file_symbols; next; next = next1) + { + next1 = next->next; + free (next); + } + for (next = global_symbols; next; next = next1) + { + next1 = next->next; + free (next); + } +} + +/* Take one of the lists of symbols and make a block from it. + Keep the order the symbols have in the list (reversed from the input file). + Put the block on the list of pending blocks. */ + +static void +finish_block (symbol, listhead, old_blocks, start, end) + struct symbol *symbol; + struct pending **listhead; + struct pending_block *old_blocks; + CORE_ADDR start, end; +{ + register struct pending *next, *next1; + register struct block *block; + register struct pending_block *pblock; + struct pending_block *opblock; + register int i; + + /* Count the length of the list of symbols. */ + + for (next = *listhead, i = 0; next; i += next->nsyms, next = next->next); + + block = (struct block *) obstack_alloc (symbol_obstack, + (sizeof (struct block) + + ((i - 1) + * sizeof (struct symbol *)))); + + /* Copy the symbols into the block. */ + + BLOCK_NSYMS (block) = i; + for (next = *listhead; next; next = next->next) + { + register int j; + for (j = next->nsyms - 1; j >= 0; j--) + BLOCK_SYM (block, --i) = next->symbol[j]; + } + + BLOCK_START (block) = start; + BLOCK_END (block) = end; + BLOCK_SUPERBLOCK (block) = 0; /* Filled in when containing block is made */ + BLOCK_GCC_COMPILED (block) = processing_gcc_compilation; + + /* Put the block in as the value of the symbol that names it. */ + + if (symbol) + { + SYMBOL_BLOCK_VALUE (symbol) = block; + BLOCK_FUNCTION (block) = symbol; + } + else + BLOCK_FUNCTION (block) = 0; + + /* Now "free" the links of the list, and empty the list. */ + + for (next = *listhead; next; next = next1) + { + next1 = next->next; + next->next = free_pendings; + free_pendings = next; + } + *listhead = 0; + + /* Install this block as the superblock + of all blocks made since the start of this scope + that don't have superblocks yet. */ + + opblock = 0; + for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next) + { + if (BLOCK_SUPERBLOCK (pblock->block) == 0) + BLOCK_SUPERBLOCK (pblock->block) = block; + opblock = pblock; + } + + /* Record this block on the list of all blocks in the file. + Put it after opblock, or at the beginning if opblock is 0. + This puts the block in the list after all its subblocks. */ + + /* Allocate in the symbol_obstack to save time. + It wastes a little space. */ + pblock = (struct pending_block *) + obstack_alloc (symbol_obstack, + sizeof (struct pending_block)); + pblock->block = block; + if (opblock) + { + pblock->next = opblock->next; + opblock->next = pblock; + } + else + { + pblock->next = pending_blocks; + pending_blocks = pblock; + } +} + +static struct blockvector * +make_blockvector () +{ + register struct pending_block *next, *next1; + register struct blockvector *blockvector; + register int i; + + /* Count the length of the list of blocks. */ + + for (next = pending_blocks, i = 0; next; next = next->next, i++); + + blockvector = (struct blockvector *) + obstack_alloc (symbol_obstack, + (sizeof (struct blockvector) + + (i - 1) * sizeof (struct block *))); + + /* Copy the blocks into the blockvector. + This is done in reverse order, which happens to put + the blocks into the proper order (ascending starting address). + finish_block has hair to insert each block into the list + after its subblocks in order to make sure this is true. */ + + BLOCKVECTOR_NBLOCKS (blockvector) = i; + for (next = pending_blocks; next; next = next->next) + BLOCKVECTOR_BLOCK (blockvector, --i) = next->block; + +#if 0 /* Now we make the links in the obstack, so don't free them. */ + /* Now free the links of the list, and empty the list. */ + + for (next = pending_blocks; next; next = next1) + { + next1 = next->next; + free (next); + } +#endif + pending_blocks = 0; + + return blockvector; +} + +/* Manage the vector of line numbers. */ + +static void +record_line (line, pc) + int line; + CORE_ADDR pc; +{ + struct linetable_entry *e; + /* Ignore the dummy line number in libg.o */ + + if (line == 0xffff) + return; + + /* Make sure line vector is big enough. */ + + if (line_vector_index + 1 >= line_vector_length) + { + line_vector_length *= 2; + line_vector = (struct linetable *) + xrealloc (line_vector, + (sizeof (struct linetable) + + line_vector_length * sizeof (struct linetable_entry))); + current_subfile->line_vector = line_vector; + } + + e = line_vector->item + line_vector_index++; + e->line = line; e->pc = pc; +} + +/* Start a new symtab for a new source file. + This is called when a dbx symbol of type N_SO is seen; + it indicates the start of data for one original source file. */ + +static void +start_symtab (name, start_addr) + char *name; + CORE_ADDR start_addr; +{ + register struct symtab *s; + + last_source_file = name; + last_source_start_addr = start_addr; + file_symbols = 0; + global_symbols = 0; + within_function = 0; + + /* Context stack is initially empty, with room for 10 levels. */ + context_stack + = (struct context_stack *) xxmalloc (10 * sizeof (struct context_stack)); + context_stack_size = 10; + context_stack_depth = 0; + + new_object_header_files (); + + for (s = symseg_chain; s; s = s->next) + if (s->ldsymoff == symnum * sizeof (struct nlist)) + break; + current_symseg = s; + if (s != 0) + return; + + type_vector_length = 160; + type_vector = (struct typevector *) + xxmalloc (sizeof (struct typevector) + + type_vector_length * sizeof (struct type *)); + bzero (type_vector->type, type_vector_length * sizeof (struct type *)); + + /* Initialize the list of sub source files with one entry + for this file (the top-level source file). */ + + subfiles = 0; + current_subfile = 0; + start_subfile (name); + + /* Set default for compiler to pcc; assume that we aren't processing + a gcc compiled file until proved otherwise. */ + + processing_gcc_compilation = 0; +} + +/* Handle an N_SOL symbol, which indicates the start of + code that came from an included (or otherwise merged-in) + source file with a different name. */ + +static void +start_subfile (name) + char *name; +{ + register struct subfile *subfile; + + /* Save the current subfile's line vector data. */ + + if (current_subfile) + { + current_subfile->line_vector_index = line_vector_index; + current_subfile->line_vector_length = line_vector_length; + current_subfile->prev_line_number = prev_line_number; + } + + /* See if this subfile is already known as a subfile of the + current main source file. */ + + for (subfile = subfiles; subfile; subfile = subfile->next) + { + if (!strcmp (subfile->name, name)) + { + line_vector = subfile->line_vector; + line_vector_index = subfile->line_vector_index; + line_vector_length = subfile->line_vector_length; + prev_line_number = subfile->prev_line_number; + current_subfile = subfile; + return; + } + } + + /* This subfile is not known. Add an entry for it. */ + + line_vector_index = 0; + line_vector_length = 1000; + prev_line_number = -2; /* Force first line number to be explicit */ + line_vector = (struct linetable *) + xxmalloc (sizeof (struct linetable) + + line_vector_length * sizeof (struct linetable_entry)); + + /* Make an entry for this subfile in the list of all subfiles + of the current main source file. */ + + subfile = (struct subfile *) xxmalloc (sizeof (struct subfile)); + subfile->next = subfiles; + subfile->name = savestring (name, strlen (name)); + subfile->line_vector = line_vector; + subfiles = subfile; + current_subfile = subfile; +} + +/* Finish the symbol definitions for one main source file, + close off all the lexical contexts for that file + (creating struct block's for them), then make the struct symtab + for that file and put it in the list of all such. + + END_ADDR is the address of the end of the file's text. */ + +static void +end_symtab (end_addr) + CORE_ADDR end_addr; +{ + register struct symtab *symtab; + register struct blockvector *blockvector; + register struct subfile *subfile; + register struct linetable *lv; + struct subfile *nextsub; + + if (current_symseg != 0) + { + last_source_file = 0; + current_symseg = 0; + return; + } + + /* Finish the lexical context of the last function in the file; + pop the context stack. */ + + if (context_stack_depth > 0) + { + register struct context_stack *cstk; + context_stack_depth--; + cstk = &context_stack[context_stack_depth]; + /* Make a block for the local symbols within. */ + finish_block (cstk->name, &local_symbols, cstk->old_blocks, + cstk->start_addr, end_addr); + } + + /* Finish defining all the blocks of this symtab. */ + finish_block (0, &file_symbols, 0, last_source_start_addr, end_addr); + finish_block (0, &global_symbols, 0, last_source_start_addr, end_addr); + blockvector = make_blockvector (); + + current_subfile->line_vector_index = line_vector_index; + + /* Now create the symtab objects proper, one for each subfile. */ + /* (The main file is one of them.) */ + + for (subfile = subfiles; subfile; subfile = nextsub) + { + symtab = (struct symtab *) xxmalloc (sizeof (struct symtab)); + symtab->free_ptr = 0; + + /* Fill in its components. */ + symtab->blockvector = blockvector; + type_vector->length = type_vector_length; + symtab->typevector = type_vector; + symtab->free_code = free_linetable; + if (subfile->next == 0) + symtab->free_ptr = (char *) type_vector; + + symtab->filename = subfile->name; + lv = subfile->line_vector; + lv->nitems = subfile->line_vector_index; + symtab->linetable = (struct linetable *) + xrealloc (lv, (sizeof (struct linetable) + + lv->nitems * sizeof (struct linetable_entry))); + symtab->nlines = 0; + symtab->line_charpos = 0; + + /* Link the new symtab into the list of such. */ + symtab->next = symtab_list; + symtab_list = symtab; + + nextsub = subfile->next; + free (subfile); + } + + type_vector = 0; + type_vector_length = -1; + line_vector = 0; + line_vector_length = -1; + last_source_file = 0; +} + +#ifdef N_BINCL + +/* Handle the N_BINCL and N_EINCL symbol types + that act like N_SOL for switching source files + (different subfiles, as we call them) within one object file, + but using a stack rather than in an arbitrary order. */ + +struct subfile_stack +{ + struct subfile_stack *next; + char *name; + int prev_index; +}; + +struct subfile_stack *subfile_stack; + +static void +push_subfile () +{ + register struct subfile_stack *tem + = (struct subfile_stack *) xxmalloc (sizeof (struct subfile_stack)); + + tem->next = subfile_stack; + subfile_stack = tem; + if (current_subfile == 0 || current_subfile->name == 0) + abort (); + tem->name = current_subfile->name; + tem->prev_index = header_file_prev_index; +} + +static char * +pop_subfile () +{ + register char *name; + register struct subfile_stack *link = subfile_stack; + + if (link == 0) + abort (); + + name = link->name; + subfile_stack = link->next; + header_file_prev_index = link->prev_index; + free (link); + + return name; +} +#endif /* Have N_BINCL */ + +/* Accumulate the misc functions in bunches of 127. + At the end, copy them all into one newly allocated structure. */ + +#define MISC_BUNCH_SIZE 127 + +struct misc_bunch +{ + struct misc_bunch *next; + struct misc_function contents[MISC_BUNCH_SIZE]; +}; + +/* Bunch currently being filled up. + The next field points to chain of filled bunches. */ + +static struct misc_bunch *misc_bunch; + +/* Number of slots filled in current bunch. */ + +static int misc_bunch_index; + +/* Total number of misc functions recorded so far. */ + +static int misc_count; + +static void +init_misc_functions () +{ + misc_count = 0; + misc_bunch = 0; + misc_bunch_index = MISC_BUNCH_SIZE; +} + +static void +record_misc_function (name, address) + char *name; + CORE_ADDR address; +{ + register struct misc_bunch *new; + + if (misc_bunch_index == MISC_BUNCH_SIZE) + { + new = (struct misc_bunch *) xxmalloc (sizeof (struct misc_bunch)); + misc_bunch_index = 0; + new->next = misc_bunch; + misc_bunch = new; + } + misc_bunch->contents[misc_bunch_index].name = name; + misc_bunch->contents[misc_bunch_index].address = address; + misc_bunch_index++; + misc_count++; +} + +static int +compare_misc_functions (fn1, fn2) + struct misc_function *fn1, *fn2; +{ + /* Return a signed result based on unsigned comparisons + so that we sort into unsigned numeric order. */ + if (fn1->address < fn2->address) + return -1; + if (fn1->address > fn2->address) + return 1; + return 0; +} + +static void +discard_misc_bunches () +{ + register struct misc_bunch *next; + + while (misc_bunch) + { + next = misc_bunch->next; + free (misc_bunch); + misc_bunch = next; + } +} + +/* INCLINK nonzero means bunches are from an incrementally-linked file. + Add them to the existing bunches. + Otherwise INCLINK is zero, and we start from scratch. */ +static void +condense_misc_bunches (inclink) + int inclink; +{ + register int i, j; + register struct misc_bunch *bunch; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + if (inclink) + { + misc_function_vector + = (struct misc_function *) + xrealloc (misc_function_vector, (misc_count + misc_function_count) + * sizeof (struct misc_function)); + j = misc_function_count; + } + else + { + misc_function_vector + = (struct misc_function *) + xxmalloc (misc_count * sizeof (struct misc_function)); + j = 0; + } + + bunch = misc_bunch; + while (bunch) + { + for (i = 0; i < misc_bunch_index; i++) + { + misc_function_vector[j] = bunch->contents[i]; + misc_function_vector[j].name + = obconcat (misc_function_vector[j].name + + (misc_function_vector[j].name[0] == '_' ? offset : 0), + "", ""); + j++; + } + bunch = bunch->next; + misc_bunch_index = MISC_BUNCH_SIZE; + } + + if (inclink) + misc_function_count += misc_count; + else + misc_function_count = j; + + /* Sort the misc functions by address. */ + + qsort (misc_function_vector, misc_function_count, + sizeof (struct misc_function), + compare_misc_functions); +} + +/* Call sort_syms to sort alphabetically + the symbols of each block of each symtab. */ + +static int +compare_symbols (s1, s2) + struct symbol **s1, **s2; +{ + register int namediff; + + /* Compare the initial characters. */ + namediff = SYMBOL_NAME (*s1)[0] - SYMBOL_NAME (*s2)[0]; + if (namediff != 0) return namediff; + + /* If they match, compare the rest of the names. */ + namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2)); + if (namediff != 0) return namediff; + + /* For symbols of the same name, registers should come first. */ + return ((SYMBOL_CLASS (*s2) == LOC_REGISTER) + - (SYMBOL_CLASS (*s1) == LOC_REGISTER)); +} + +static void sort_symtab_syms (); + +static void +sort_syms () +{ + register struct symtab *s; + + for (s = symtab_list; s; s = s->next) + sort_symtab_syms (s); +} + +static void +sort_symtab_syms (s) + register struct symtab *s; +{ + register struct blockvector *bv = BLOCKVECTOR (s); + int nbl = BLOCKVECTOR_NBLOCKS (bv); + int i; + register struct block *b; + + /* Note that in the following sort, we always make sure that + register debug symbol declarations always come before regular + debug symbol declarations (as might happen when parameters are + then put into registers by the compiler). We do this by a + correct compare in compare_symbols, and by the reversal of the + symbols if we don't sort. This works as long as a register debug + symbol always comes after a parameter debug symbol. */ + + /* This is no longer necessary; lookup_block_symbol now always + prefers some other declaration over a parameter declaration. We + still sort the thing (that is necessary), but we don't reverse it + if we shouldn't sort it. */ + + for (i = 0; i < nbl; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + if (BLOCK_SHOULD_SORT (b)) + qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b), + sizeof (struct symbol *), compare_symbols); + } +} + + +extern struct symtab *psymtab_to_symtab (); + +/* This is the symbol-file command. Read the file, analyze its symbols, + and add a struct symtab to symtab_list. */ + +void +symbol_file_command (name) + char *name; +{ + register int desc; + DECLARE_FILE_HEADERS; + struct nlist *nlist; + char *stringtab; + long buffer; + register int val; + extern void close (); + struct cleanup *old_chain; + struct symtab *symseg; + struct stat statbuf; + + dont_repeat (); + + if (name == 0) + { + if ((symtab_list || partial_symtab_list) + && !query ("Discard symbol table? ", 0)) + error ("Not confirmed."); + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + return; + } + + if ((symtab_list || partial_symtab_list) + && !query ("Load new symbol table from \"%s\"? ", name)) + error ("Not confirmed."); + + { + char *absolute_name; + desc = openp (getenv ("PATH"), 1, name, O_RDONLY, 0, &absolute_name); + if (desc < 0) + perror_with_name (name); + else + name = absolute_name; + } + + old_chain = make_cleanup (close, desc); + make_cleanup (free_current_contents, &name); + + READ_FILE_HEADERS (desc, name); + + if (NUMBER_OF_SYMBOLS == 0) + { + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + printf ("%s has no symbol-table; symbols discarded.\n", name); + fflush (stdout); + do_cleanups (old_chain); + return; + } + + printf ("Reading symbol data from %s...", name); + fflush (stdout); + + /* Now read the string table, all at once. */ + val = lseek (desc, STRING_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + stat (name, &statbuf); + READ_STRING_TABLE_SIZE (buffer); + if (buffer >= 0 && buffer < statbuf.st_size) + stringtab = (char *) alloca (buffer); + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", name, buffer); + + bcopy (&buffer, stringtab, sizeof buffer); + val = myread (desc, stringtab + sizeof buffer, buffer - sizeof buffer); + if (val < 0) + perror_with_name (name); + + /* Throw away the old symbol table. */ + + if (symfile) + free (symfile); + symfile = 0; + free_all_symtabs (); + free_all_psymtabs (); + + /* Empty the hash table of global syms looking for values. */ + bzero (global_sym_chain, sizeof global_sym_chain); + +#ifdef READ_GDB_SYMSEGS + /* That puts us at the symsegs. Read them. */ + symseg_chain = read_symsegs (desc, name); + hash_symsegs (); + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + for (symseg = symseg_chain; symseg; symseg = symseg->next) + { + int i; + struct sourcevector *sv = (struct sourcevector *) symseg->linetable; + + for (i = 0; i < sv->length; i++) + { + int j; + struct source *source = sv->source[i]; + struct symtab *sp1 + = (struct symtab *) xxmalloc (sizeof (struct symtab)); + + bcopy (symseg, sp1, sizeof (struct symtab)); + sp1->filename = savestring (source->name, strlen (source->name)); + sp1->linetable = &source->contents; + sp1->free_code = free_nothing; + sp1->free_ptr = (i == 0) ? (char *) symseg : 0; + + sp1->next = symtab_list; + symtab_list = sp1; + } + } +#else + /* Where people are using the 4.2 ld program, must not check for + symsegs, because that ld puts randonm garbage at the end of + the output file and that would trigger an error message. */ + symseg_chain = 0; +#endif + + /* Position to read the symbol table. Do not read it all at once. */ + val = lseek (desc, SYMBOL_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + + /* Don't put these on the cleanup chain; they need to stick around + until the next call to symbol_file_command. *Then* we'll free + them. */ + free_header_files (); + init_header_files (); + + init_misc_functions (); + make_cleanup (discard_misc_bunches, 0); + + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + make_cleanup (really_free_pendings, 0); + + /* Now that the symbol table data of the executable file are all in core, + process them and define symbols accordingly. Closes desc. */ + + read_dbx_symtab (desc, stringtab, NUMBER_OF_SYMBOLS, 0, 0, 0); + + /* Go over the misc functions and install them in vector. */ + + condense_misc_bunches (0); + + /* Don't allow char * to have a typename (else would get caddr_t.) */ + + TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0; + + /* Make a default for file to list. */ + + symfile = savestring (name, strlen (name)); + + /* Call to select_source_symtab used to be here; it was using too + much time. I'll make sure that list_sources can handle the lack + of current_source_symtab */ + + do_cleanups (old_chain); /* Descriptor closed here */ + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + while (symseg_chain) + { + register struct symtab *s = symseg_chain->next; + free (symseg_chain); + symseg_chain = s; + } + + if (!partial_symtab_list) + printf ("\n(no debugging symbols found)..."); + + printf ("done.\n"); + fflush (stdout); +} + +/* Return name of file symbols were loaded from, or 0 if none.. */ + +char * +get_sym_file () +{ + return symfile; +} + +/* Buffer for reading the symbol table entries. */ +static struct nlist symbuf[4096]; +static int symbuf_idx; +static int symbuf_end; + +/* I/O descriptor for reading the symbol table. */ +static int symtab_input_desc; + +/* The address of the string table + of the object file we are reading (as copied into core). */ +static char *stringtab_global; + +/* Refill the symbol table input buffer + and set the variables that control fetching entries from it. + Reports an error if no data available. + This function can read past the end of the symbol table + (into the string table) but this does no harm. */ + +static int +fill_symbuf () +{ + int nbytes = myread (symtab_input_desc, symbuf, sizeof (symbuf)); + if (nbytes <= 0) + error ("error or end of file reading symbol table"); + symbuf_end = nbytes / sizeof (struct nlist); + symbuf_idx = 0; + return 1; +} + +/* dbx allows the text of a symbol name to be continued into the + next symbol name! When such a continuation is encountered + (a \ at the end of the text of a name) + call this function to get the continuation. */ + +static char * +next_symbol_text () +{ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + symnum++; + return symbuf[symbuf_idx++].n_un.n_strx + stringtab_global; +} + +/* + * Initializes storage for all of the partial symbols that will be + * created by read_dbx_symtab and subsidiaries. + */ +void +init_psymbol_list (total_symbols) + int total_symbols; +{ + /* Current best guess is that there are approximately a twentieth + of the total symbols (in a debugging file) are global or static + oriented symbols */ + global_psymbols_allocated = total_symbols / 10; + static_psymbols_allocated = total_symbols / 10; + next_ps_global = global_psymbols = (struct partial_symbol *) + xmalloc (global_psymbols_allocated * sizeof (struct partial_symbol)); + next_ps_static = static_psymbols = (struct partial_symbol *) + xmalloc (static_psymbols_allocated * sizeof (struct partial_symbol)); +} + +/* + * Initialize the list of bincls to contain none and have some + * allocated. + */ +static void +init_bincl_list (number) + int number; +{ + bincls_allocated = number; + next_bincl = bincl_list = (struct header_file_location *) + xmalloc (bincls_allocated * sizeof(struct header_file_location)); +} + +/* + * Add a bincl to the list. + */ +static void +add_bincl_to_list (pst, name, instance) + struct partial_symtab *pst; + char *name; + int instance; +{ + if (next_bincl >= bincl_list + bincls_allocated) + { + int offset = next_bincl - bincl_list; + bincls_allocated *= 2; + bincl_list = (struct header_file_location *) + xrealloc (bincl_list, + bincls_allocated * sizeof (struct header_file_location)); + next_bincl = bincl_list + offset; + } + next_bincl->pst = pst; + next_bincl->instance = instance; + next_bincl++->name = name; +} + +/* + * Given a name, value pair, find the corresponding + * bincl in the list. Return the partial symtab associated + * with that header_file_location. + */ +struct partial_symtab * +find_corresponding_bincl_psymtab (name, instance) + char *name; + int instance; +{ + struct header_file_location *bincl; + + for (bincl = bincl_list; bincl < next_bincl; bincl++) + if (bincl->instance == instance + && !strcmp (name, bincl->name)) + return bincl->pst; + + return (struct partial_symtab *) 0; +} + +/* + * Free the storage allocated for the bincl list. + */ +static void +free_bincl_list () +{ + free (bincl_list); + bincls_allocated = 0; +} + +static struct partial_symtab *start_psymtab (); +static void add_psymtab_dependency (); +static void end_psymtab(); + +/* Given pointers to an a.out symbol table in core containing dbx + style data, setup partial_symtab's describing each source file for + which debugging information is available. NLISTLEN is the number + of symbols in the symbol table. All symbol names are given as + offsets relative to STRINGTAB. + + I have no idea whether or not this routine should be setup to deal + with inclinks. It seems reasonable to me that they be dealt with + standardly, so I am not going to make a strong effort to deal with + them here. + */ + +static void process_symbol_for_psymtab (); + +static void +read_dbx_symtab (desc, stringtab, nlistlen, inclink, text_addr, text_size) + int desc; + register char *stringtab; + register int nlistlen; + int inclink; + unsigned text_addr; + int text_size; +{ + register char *namestring; + register struct symbol *sym, *prev; + int hash; + int num_object_files = 0; + int past_first_source_file = 0; + struct cleanup *old_chain; + int current_text_start, current_file_symbol_start; + struct pending *global_symbols, *static_symbols; + int nsl; /* Length of namestring, when needed */ + + /* Current partial symtab */ + struct partial_symtab *pst; + + /* List of current psymtab's include files */ + char **psymtab_include_list; + int includes_allocated; + int includes_used; + + /* Index within current psymtab dependency list */ + struct partial_symtab **dependency_list; + int dependencies_used, dependencies_allocated; + + /* Setup a define to deal cleanly with the underscore problem */ + +#ifdef NAMES_HAVE_UNDERSCORE +#define HASH_OFFSET 1 +#else +#define HASH_OFFSET 0 +#endif + + global_symbols = static_symbols = + (struct pending *) 0; + pst = (struct partial_symtab *) 0; + + includes_allocated = 30; + includes_used = 0; + psymtab_include_list = (char **) alloca (includes_allocated * + sizeof (char *)); + + dependencies_allocated = 30; + dependencies_used = 0; + dependency_list = + (struct partial_symtab **) alloca (dependencies_allocated * + sizeof (struct partial_symtab *)); + + old_chain = make_cleanup (free_all_psymtabs, 0); + + /* Init bincl list */ + init_bincl_list (20); + make_cleanup (free_bincl_list, 0); + + /* Setup global partial symbol list */ + init_psymbol_list (nlistlen); + + last_source_file = 0; + +#ifdef END_OF_TEXT_DEFAULT + end_of_text_addr = END_OF_TEXT_DEFAULT; +#endif + + symtab_input_desc = desc; /* This is needed for fill_symbuf below */ + symbuf_end = symbuf_idx = 0; + + for (symnum = 0; symnum < nlistlen; symnum++) + { + struct nlist *bufp; + unsigned char type; + + /* Get the symbol for this run and pull out some info */ + QUIT; /* allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type; + + /* + * Special cases to speed up readin. + */ + if (type == N_SLINE) continue; + + namestring = bufp->n_un.n_strx ? bufp->n_un.n_strx + stringtab : ""; + + switch (type) + { + /* + * Standard, non-debugger, symbols + */ + + case N_TEXT | N_EXT: + /* Catch etext */ + + if (!strcmp (namestring, "_etext")) + end_of_text_addr = bufp->n_value; + /* Fall through */ + +#ifdef N_NBTEXT + case N_NBTEXT | N_EXT: +#endif +#ifdef N_NBDATA + case N_NBDATA | N_EXT: +#endif +#ifdef N_NBBSS + case N_NBBSS | N_EXT: +#endif + case N_ABS | N_EXT: + case N_DATA | N_EXT: + case N_BSS | N_EXT: + /* Figure out beginning and end of global linker symbol + section and put non-debugger specified symbols on + tmp_symchain */ + + last_global_sym = symnum; + if (!first_global_sym) first_global_sym = symnum; + + record_misc_function (namestring, bufp->n_value); /* Always */ + + continue; + +#ifdef N_NBTEXT + case N_NBTEXT: +#endif + case N_TEXT: + if (!strcmp (namestring + strlen (namestring) - 2, ".o") + || !strncmp (namestring, "-l", 2)) + { + if (num_object_files++ == 1) + first_object_file_end = bufp->n_value; + if (past_first_source_file && pst) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), bufp->n_value, + dependency_list, dependencies_used, + next_ps_global, next_ps_static); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + } + else + past_first_source_file = 1; + } + continue; + + case N_UNDF: + case N_UNDF | N_EXT: + case N_ABS: + case N_DATA: + case N_BSS: +#ifdef N_NBDATA + case N_NBDATA: +#endif +#ifdef N_NBBSS + case N_NBBSS: +#endif + case N_FN: + /* Keep going . . .*/ + + /* + * Special symbol types for GNU + */ +#ifdef N_INDR + case N_INDR: + case N_INDR | N_EXT: +#endif +#ifdef N_SETA + case N_SETA: + case N_SETA | N_EXT: + case N_SETT: + case N_SETT | N_EXT: + case N_SETD: + case N_SETD | N_EXT: + case N_SETB: + case N_SETB | N_EXT: + case N_SETV: + case N_SETV | N_EXT: +#endif + continue; + + /* + * Debugger symbols + */ + + case N_SO: + /* End the current partial symtab and start a new one */ + + if (past_first_source_file && pst) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), bufp->n_value, + dependency_list, dependencies_used, + next_ps_global, next_ps_static); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + } + else + past_first_source_file = 1; + + pst = start_psymtab (namestring, bufp->n_value, + symnum * sizeof (struct nlist), + next_ps_global, next_ps_static); + + continue; + +#ifdef N_BINCL + case N_BINCL: + /* Add this bincl to the bincl_list for future EXCLs. No + need to save the string; it'll be around until + read_dbx_symtab function return */ + add_bincl_to_list (pst, namestring, bufp->n_value); + + /* Fall through */ +#endif + + case N_SOL: + /* Mark down an include file in the current psymtab */ + + psymtab_include_list[includes_used++] = namestring; + if (includes_used >= includes_allocated) + { + char **orig = psymtab_include_list; + + psymtab_include_list = (char **) + alloca ((includes_allocated *= 2) * + sizeof (char *)); + bcopy (orig, psymtab_include_list, + includes_used * sizeof (char *)); +#ifdef DEBUG_INFO + fprintf (stderr, "Had to realloc includes. New size: %d\n", + includes_allocated); +#endif + } + continue; + + case N_FUN: + case N_SSYM: + case N_GSYM: + case N_LSYM: + case N_STSYM: + case N_LCSYM: + case N_ENTRY: +#ifdef N_MAIN + case N_MAIN: +#endif +#ifdef N_BSLINE + case N_BSLINE: +#endif + case N_PC: +#ifdef N_M2C + case N_M2C: + case N_SCOPE: +#endif + /* Process a symbol as appropriate for the type (this + information is contained in the name of the symbol) */ + + if (namestring[0] != '\0') +#if 1 + process_symbol_for_psymtab (namestring); +#else + process_symbol_for_psymtab (namestring, tmp_symchain); +#endif + continue; + +#ifdef N_BINCL + case N_EXCL: + /* Find the corresponding bincl and mark that psymtab on the + psymtab dependency list */ + { + struct partial_symtab *needed_pst = + find_corresponding_bincl_psymtab (namestring, bufp->n_value); + + /* If this include file was defined earlier in this file, + leave it alone. */ + if (needed_pst == pst) continue; + + if (needed_pst) + { + int i; + int found = 0; + + for (i = 0; i < dependencies_used; i++) + if (dependency_list[i] == needed_pst) + { + found = 1; + break; + } + + /* If it's already in the list, skip the rest. */ + if (found) continue; + + dependency_list[dependencies_used++] = needed_pst; + if (dependencies_used >= dependencies_allocated) + { + struct partial_symtab **orig = dependency_list; + dependency_list = + (struct partial_symtab **) + alloca ((dependencies_allocated *= 2) + * sizeof (struct partial_symtab *)); + bcopy (orig, dependency_list, + (dependencies_used + * sizeof (struct partial_symtab *))); +#ifdef DEBUG_INFO + fprintf (stderr, "Had to reallocate dependency list.\n"); + fprintf (stderr, "New dependencies allocated: %d\n", + dependencies_allocated); +#endif + } + } + else + error ("Invalid symbol data: \"repeated\" header file not previously seen, at symtab pos %d.", + symnum); + } + continue; + + case N_EINCL: +#endif +#ifdef N_DSLINE + case N_DSLINE: +#endif + case N_LENG: + case N_BCOMM: + case N_ECOMM: + case N_ECOML: + case N_FNAME: + case N_SLINE: + case N_RSYM: + case N_PSYM: + case N_LBRAC: + case N_RBRAC: + /* These symbols aren't interesting; don't worry about them */ + + continue; + + default: + /* If we haven't found it yet, we've got problems */ + + if (IGNORE_SYMBOL (type)) + continue; + + fatal ("Bad symbol type 0x%x encountered in gdb scan", type); + } + } + + if (last_source_file) + { + end_psymtab (pst, psymtab_include_list, includes_used, + symnum * sizeof (struct nlist), end_of_text_addr, + dependency_list, dependencies_used, + next_ps_global, next_ps_static); + includes_used = 0; + dependencies_used = 0; + pst = (struct partial_symtab *) 0; + } + + free_bincl_list (); + discard_cleanups (old_chain); +} + +/* + * Take a single symbol (name: NAME) and process it (add it to the + * app psymbol list or not). + */ +static void +process_symbol_for_psymtab (name) + char *name; +{ + char *p = (char *) index(name, ':') + 1; + int deftype; + struct partial_symbol *sym; + enum { T_IGNORE, T_STATIC, T_GLOBAL } symbol_type; + enum namespace ns = UNDEF_NAMESPACE; + enum address_class class; + int hash; + + if (p == (char *) 0x1) + /* No ":" ; I guess it's not a debuggging symbol */ + return; + + if ((*p >= '0' && *p <= '9') || *p == '(') + deftype = 'l'; + else + deftype = *p; + + /* Figure out how to handle this symbol */ + switch (deftype) + { + /* T is a struct/union/enum, t is a typedef */ + case 'T': + symbol_type = T_STATIC; + ns = STRUCT_NAMESPACE; + class = LOC_TYPEDEF; + break; + case 't': + symbol_type = T_STATIC; + ns = VAR_NAMESPACE; + class = LOC_TYPEDEF; + break; + case 'c': + symbol_type = T_STATIC; + ns = VAR_NAMESPACE; + class = LOC_CONST; + break; + case 'S': + symbol_type = T_STATIC; + ns = VAR_NAMESPACE; + class = LOC_STATIC; + break; + case 'f': + symbol_type = T_STATIC; + ns = VAR_NAMESPACE; + class = LOC_BLOCK; + break; + case 'F': + symbol_type = T_GLOBAL; + ns = VAR_NAMESPACE; + class = LOC_BLOCK; + break; + case 'G': + symbol_type = T_GLOBAL; + ns = VAR_NAMESPACE; + class = LOC_STATIC; + break; + default: + return; + } + + /* Create the symbol and store it on the list */ + /* There's a better algorithm possible for the allocation; figure + out how far through the symbol table we are and do a reestimate */ + if (symbol_type == T_STATIC) + { + if (next_ps_static >= static_psymbols + static_psymbols_allocated) + { + static_psymbols = (struct partial_symbol *) + xrealloc (static_psymbols, + (static_psymbols_allocated * 2 + * sizeof (struct partial_symbol))); + /* Next assumes we only went one over. Should be good if + program works correctly */ + next_ps_static = static_psymbols + static_psymbols_allocated; + static_psymbols_allocated *= 2; +#ifdef DEBUGINFO + fprintf(stderr, "debuginfo: Had to realloc statics\n"); +#endif + } + sym = next_ps_static++; + } + else + { + if (next_ps_global >= global_psymbols + global_psymbols_allocated) + { + global_psymbols = (struct partial_symbol *) + xrealloc (global_psymbols, + (global_psymbols_allocated * 2 + * sizeof (struct partial_symbol))); + next_ps_global = global_psymbols + global_psymbols_allocated; + global_psymbols_allocated *= 2; +#ifdef DEBUGINFO + fprintf(stderr, "debuginfo: Had to realloc globals\n"); +#endif + } + sym = next_ps_global++; + } + + SYMBOL_NAME(sym) = (char *) obstack_alloc (psymbol_obstack, + p - name); + strncpy(SYMBOL_NAME(sym), name, p - name - 1); + SYMBOL_NAME(sym)[p - name - 1] = '\0'; + SYMBOL_NAMESPACE(sym) = ns; + SYMBOL_CLASS(sym) = class; +} +#undef HASH_OFFSET + +/* + * Allocate and partially fill a partial symtab. It will be + * completely filled at the end of the symbol list. + */ +static struct partial_symtab * +start_psymtab (filename, textlow, ldsymoff, global_syms, static_syms) + char *filename; + int textlow; + int ldsymoff; + struct partial_symbol *global_syms; + struct partial_symbol *static_syms; +{ + struct partial_symtab *result = + (struct partial_symtab *) obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab)); + + result->filename = + (char *) obstack_alloc (psymbol_obstack, + strlen (filename) + 1); + strcpy (result->filename, filename); + + result->textlow = textlow; + result->ldsymoff = ldsymoff; + + result->readin = 0; + + result->globals_offset = global_syms - global_psymbols; + result->statics_offset = static_syms - static_psymbols; + + result->n_global_syms = 0; + result->n_static_syms = 0; + + return result; +} + +static int +compare_psymbols (s1, s2) + register struct partial_symbol *s1, *s2; +{ + register char + *st1 = SYMBOL_NAME (s1), + *st2 = SYMBOL_NAME (s2); + + return (st1[0] - st2[0] ? st1[0] - st2[0] : + strcmp (st1 + 1, st2 + 1)); +} + + +/* Close off the current usage of a partial_symbol table entry. This + involves setting the correct number of includes (with a realloc), + setting the high text mark, setting the symbol length in the + executable, and setting the length of the global and static lists + of psymbols. + + The global symbols and static symbols are then seperately sorted. + + Then the partial symtab is put on the global list. + *** List variables and peculiarities of same. *** + */ +static void +end_psymtab (pst, include_list, num_includes, capping_symbol_offset, + capping_text, dependency_list, number_dependencies, + capping_global, capping_static) + struct partial_symtab *pst; + char **include_list; + int num_includes; + int capping_symbol_offset; + int capping_text; + struct partial_symtab **dependency_list; + int number_dependencies; + struct partial_symbol *capping_global, *capping_static; +{ + int i; + + pst->ldsymlen = capping_symbol_offset - pst->ldsymoff; + pst->texthigh = capping_text; + + pst->n_global_syms = + capping_global - (global_psymbols + pst->globals_offset); + pst->n_static_syms = + capping_static - (static_psymbols + pst->statics_offset); + + pst->dependencies = (struct partial_symtab **) + obstack_alloc (psymbol_obstack, + number_dependencies * sizeof (struct partial_symtab *)); + bcopy (dependency_list, pst->dependencies, + number_dependencies * sizeof (struct partial_symtab *)); + pst->number_of_dependencies = number_dependencies; + + for (i = 0; i < num_includes; i++) + { + /* Eventually, put this on obstack */ + struct partial_symtab *subpst = + (struct partial_symtab *) + obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab)); + + subpst->filename = + (char *) obstack_alloc (psymbol_obstack, + strlen (include_list[i]) + 1); + strcpy (subpst->filename, include_list[i]); + + subpst->ldsymoff = + subpst->ldsymlen = + subpst->textlow = + subpst->texthigh = 0; + subpst->readin = 0; + + subpst->dependencies = (struct partial_symtab **) + obstack_alloc (psymbol_obstack, + sizeof (struct partial_symtab *)); + subpst->dependencies[0] = pst; + subpst->number_of_dependencies = 1; + + subpst->globals_offset = + subpst->n_global_syms = + subpst->statics_offset = + subpst->n_static_syms = 0; + + subpst->next = partial_symtab_list; + partial_symtab_list = subpst; + } + + /* Sort the global list; don't sort the static list */ + qsort (global_psymbols + pst->globals_offset, pst->n_global_syms, + sizeof (struct partial_symbol), compare_psymbols); + + /* Put the psymtab on the psymtab list */ + pst->next = partial_symtab_list; + partial_symtab_list = pst; +} + +/* + * Read in all of the symbols for a given psymtab for real. Return + * the value of the symtab you create. Do not free the storage + * allocated to the psymtab; it may have pointers to it. + */ +static void scan_file_globals (); +static void read_ofile_symtab (); + +struct symtab * +psymtab_to_symtab(pst) + struct partial_symtab *pst; +{ + int desc; + DECLARE_FILE_HEADERS; + char *stringtab; + struct partial_symtab **list_patch; + int stsize, val; + struct stat statbuf; + struct cleanup *old_chain; + extern void close (); + int i; + struct symtab *result; + char *name = symfile; /* Some of the macros require the */ + /* variable "name" to be defined in */ + /* the context in which they execute */ + /* (Yech!) */ + + if (!pst) + return 0; + + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return 0; + } + + if (!name) + error("No symbol file currently specified; use command symbol-file"); + + /* Read in all partial symbtabs on which this one is dependent */ + for (i = 0; i < pst->number_of_dependencies; i++) + if (!pst->dependencies[i]->readin) + psymtab_to_symtab (pst->dependencies[i]); + + if (pst->ldsymlen) /* Otherwise it's a dummy */ + { + /* Open symbol file and read in string table */ + stat (name, &statbuf); + desc = open(name, O_RDONLY, 0); /* symbol_file_command + guarrantees that the symbol file name + will be absolute, so there is no + need for openp */ + + old_chain = make_cleanup (close, desc); + + if (desc < 0) + error("Symbol file not readable"); + + READ_FILE_HEADERS (desc, name); + + /* Read in the string table */ + lseek (desc, STRING_TABLE_OFFSET, L_SET); + READ_STRING_TABLE_SIZE (stsize); + if (stsize >= 0 && stsize < statbuf.st_size) + stringtab = (char *) alloca (stsize); + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", name, stsize); + + bcopy (&stsize, stringtab, sizeof stsize); + val = myread (desc, stringtab + sizeof stsize, stsize - sizeof stsize); + if (val < 0) + perror_with_name (name); + + /* Init stuff necessary for reading in symbols */ + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + make_cleanup (really_free_pendings, 0); + + /* Read in this files symbols */ + lseek (desc, SYMBOL_TABLE_OFFSET, L_SET); + read_ofile_symtab (desc, stringtab, pst->ldsymoff, + pst->ldsymlen, pst->textlow, + pst->texthigh - pst->textlow, 0); + sort_symtab_syms (symtab_list); /* At beginning since just added */ + + /* Match with global symbols */ + lseek (desc, SYMBOL_TABLE_OFFSET, L_SET); + scan_file_globals (desc, stringtab, + first_global_sym * sizeof(struct nlist), + last_global_sym - first_global_sym + 1); + + do_cleanups (old_chain); + } + + /* Find pst in list, prune it, and free it's storage */ + for (list_patch = &partial_symtab_list; + *list_patch && *list_patch != pst; + list_patch = &((*list_patch)->next)) + ; + + if (!(*list_patch)) /* pst not in list. Don't worry about it? */ + fatal ("internal: psymtab_to_symtab called with non-listed pst"); + + *list_patch = (*list_patch)->next; /* Prune */ + + pst->readin = 1; /* Mark as read in */ + + /* It's the last one if we actually read something in */ + if (pst->ldsymlen) + return symtab_list; + else + /* Search through list for correct name. */ + for (result = symtab_list; result; result = result->next) + if (!strcmp (result->filename, pst->filename)) + return result; + + return 0; +} + +/* + * Scan through all of the global symbols defined in the object file, + * assigning values to the debugging symbols that need to be assigned + * to. + * + * DESC is the file descriptor of the symbol file, with the seek + * pointer pointing at the beginning of the symbol table. + * STRINGTAB is the file's string table, already read in. + * OFFSET is the offset (in bytes) of the beginning of the global + * symbols from the beginning of the symbol table. + * NUMSYMS is the number of symbols that have to be checked. + */ +static void +scan_file_globals (desc, stringtab, offset, numsyms) + int desc; + char *stringtab; + int offset; + int numsyms; +{ + int hash; + + lseek(desc, offset, L_INCR); + symtab_input_desc = desc; + symbuf_end = symbuf_idx = 0; + + for (symnum = 0; symnum < numsyms; symnum++) + { + struct nlist *bufp; + unsigned char type; + char *namestring; + + QUIT; + if (symbuf_idx == symbuf_end) + fill_symbuf (); + + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type; + + if (type & N_EXT && type != N_EXT) + { + struct symbol *sym, *prev; + + namestring = bufp->n_un.n_strx ? + bufp->n_un.n_strx + stringtab : ""; + prev = (struct symbol *) 0; + + /* Get the hash index and check all the symbols + under that hash index. */ + +#ifdef NAMES_HAVE_UNDERSCORE + hash = hashname (namestring + 1); +#else /* ! NAMES_HAVE_UNDERSCORE */ + hash = hashname (namestring); +#endif /* ! NAMES_HAVE_UNDERSCORE */ + for (sym = global_sym_chain[hash]; sym;) + { + if ( +#ifdef NAMES_HAVE_UNDERSCORE + *namestring == '_' + && namestring[1] == SYMBOL_NAME (sym)[0] + && !strcmp(namestring + 2, SYMBOL_NAME (sym) + 1) +#else /* ! NAMES_HAVE_UNDERSCORE */ + namestring[0] == SYMBOL_NAME (sym) [0] + && !strcmp(namestring + 1, SYMBOL_NAME(sym) + 1) +#endif /* ! NAMES_HAVE_UNDERSCORE */ + ) + { + /* Splice this symbol out of the hash chain and + assign the value we have to it. */ + if (prev) + SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym); + else + global_sym_chain[hash] + = (struct symbol *) SYMBOL_VALUE (sym); + SYMBOL_VALUE (sym) = bufp->n_value; + if (prev) + sym = (struct symbol *) SYMBOL_VALUE (prev); + else + sym = global_sym_chain[hash]; + break; /* Only one reference per file */ + } + else + { + prev = sym; + sym = (struct symbol *) SYMBOL_VALUE (sym); + } + } + } + } + /* There shouldn't be anything left on the hash list at this point. + If there is, we have done something wrong. For right now it's + worth checking, until I get the bugs out. */ + /* Sigh. Unfortunately, the above is not true. If an extern + variable is mentioned in an include file (or a program) and the + variable is never either referenced or defined, there will be a + debugger symbol with no "real" symbol. Oh well. */ +} + +/* + * Read in a defined section of a specific object file's symbols. + * + * DESC is the file descriptor for the file, positioned at the + * beginning of the symtab + * STRINGTAB is a pointer to the files string + * table, already read in + * SYM_OFFSET is the offset within the file of + * the beginning of the symbols we want to read, NUM_SUMBOLS is the + * number of symbols to read + * TEXT_OFFSET is the offset to be added to + * all values of symbols coming in and + * TEXT_SIZE is the size of the text segment read in. + * OFFSET is a flag which indicates that the value of all of the + * symbols should be offset by TEXT_OFFSET (for the purposes of + * incremental linking). + */ + +static void +read_ofile_symtab (desc, stringtab, sym_offset, + sym_size, text_offset, text_size, offset) + int desc; + register char *stringtab; + int sym_offset; + int sym_size; + int text_offset; + int text_size; + int offset; +{ + register char *namestring; + register struct symbol *sym, *prev; + int hash; + struct cleanup *old_chain; + struct nlist *bufp; + unsigned char type; +#ifdef N_BINCL + subfile_stack = 0; +#endif + + stringtab_global = stringtab; + last_source_file = 0; + + symtab_input_desc = desc; + symbuf_end = symbuf_idx = 0; + lseek(desc, sym_offset, L_INCR); + + fill_symbuf(); + bufp = &symbuf[symbuf_idx]; + if ((unsigned char) bufp->n_type != N_SO) + fatal("First symbol in segment of executable not a source symbol"); + + for (symnum = 0; + symnum < sym_size / sizeof(struct nlist); + symnum++) + { + QUIT; /* Allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf(); + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type; + + if (offset && + (type == N_TEXT || type == N_DATA || type == N_BSS)) + bufp->n_value += text_offset; + + namestring = bufp->n_un.n_strx ? bufp->n_un.n_strx + stringtab : ""; + + if (type & N_STAB) + process_one_symbol(type, bufp->n_desc, + bufp->n_value, namestring); + /* We skip checking for a new .o or -l file; that should never + happen in this routine. */ + else if (type == N_TEXT + && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL)) + processing_gcc_compilation = 1; + else if (type & N_EXT || type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + /* Global symbol: see if we came across a dbx defintion for + a corresponding symbol. If so, store the value. Remove + syms from the chain when their values are stored, but + search the whole chain, as there may be several syms from + different files with the same name. */ + /* This is probably not true. Since the files will be read + in one at a time, each reference to a global symbol will + be satisfied in each file as it appears. So we skip this + section. */ + &stringtab_global; /* For debugger; am I right? */ + } + end_symtab (text_offset + text_size); +} + +static int +hashname (name) + char *name; +{ + register char *p = name; + register int total = p[0]; + register int c; + + c = p[1]; + total += c << 2; + if (c) + { + c = p[2]; + total += c << 4; + if (c) + total += p[3] << 6; + } + + /* Ensure result is positive. */ + if (total < 0) total += (1000 << 6); + return total % HASHSIZE; +} + +/* Put all appropriate global symbols in the symseg data + onto the hash chains so that their addresses will be stored + when seen later in loader global symbols. */ + +static void +hash_symsegs () +{ + /* Look at each symbol in each block in each symseg symtab. */ + struct symtab *s; + for (s = symseg_chain; s; s = s->next) + { + register int n; + for (n = BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (s)) - 1; n >= 0; n--) + { + register struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), n); + register int i; + for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--) + { + register struct symbol *sym = BLOCK_SYM (b, i); + + /* Put the symbol on a chain if its value is an address + that is figured out by the loader. */ + + if (SYMBOL_CLASS (sym) == LOC_EXTERNAL) + { + register int hash = hashname (SYMBOL_NAME (sym)); + SYMBOL_VALUE (sym) = (int) global_sym_chain[hash]; + global_sym_chain[hash] = sym; + SYMBOL_CLASS (sym) = LOC_STATIC; + } + } + } + } +} + +static void +process_one_symbol (type, desc, value, name) + int type, desc; + CORE_ADDR value; + char *name; +{ + register struct context_stack *new; + + /* Something is wrong if we see real data before + seeing a source file name. */ + + if (last_source_file == 0 && type != N_SO) + { + /* Currently this ignores N_ENTRY on Gould machines, N_NSYM on machines + where that code is defined, and all symbols on the Convex. */ + if (IGNORE_SYMBOL (type)) + return; + + error ("Invalid symbol data: does not start by identifying a source file."); + } + + switch (type) + { + case N_FUN: + case N_FNAME: + /* Either of these types of symbols indicates the start of + a new function. We must process its "name" normally for dbx, + but also record the start of a new lexical context, and possibly + also the end of the lexical context for the previous function. */ + + within_function = 1; + if (context_stack_depth > 0) + { + new = &context_stack[--context_stack_depth]; + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, value); + } + /* Stack must be empty now. */ + if (context_stack_depth != 0) + error ("Invalid symbol data: unmatched N_LBRAC before symtab pos %d.", + symnum); + + new = &context_stack[context_stack_depth++]; + new->old_blocks = pending_blocks; + new->start_addr = value; + new->name = define_symbol (value, name, desc); + local_symbols = 0; + break; + + case N_LBRAC: + /* This "symbol" just indicates the start of an inner lexical + context within a function. */ + + if (context_stack_depth == context_stack_size) + { + context_stack_size *= 2; + context_stack = (struct context_stack *) + xrealloc (context_stack, + (context_stack_size + * sizeof (struct context_stack))); + } + + new = &context_stack[context_stack_depth++]; + new->depth = desc; + new->locals = local_symbols; + new->old_blocks = pending_blocks; + new->start_addr = value; + new->name = 0; + local_symbols = 0; + break; + + case N_RBRAC: + /* This "symbol" just indicates the end of an inner lexical + context that was started with N_RBRAC. */ + new = &context_stack[--context_stack_depth]; + if (desc != new->depth) + error ("Invalid symbol data: N_LBRAC/N_RBRAC symbol mismatch, symtab pos %d.", symnum); + local_symbols = new->locals; + + /* If this is not the outermost LBRAC...RBRAC pair in the + function, its local symbols preceded it, and are the ones + just recovered from the context stack. Defined the block for them. + + If this is the outermost LBRAC...RBRAC pair, there is no + need to do anything; leave the symbols that preceded it + to be attached to the function's own block. */ + if (local_symbols && context_stack_depth > 1) + { + /* Muzzle a compiler bug that makes end > start. */ + if (new->start_addr > value) + new->start_addr = value; + /* Make a block for the local symbols within. */ + finish_block (0, &local_symbols, new->old_blocks, + new->start_addr + last_source_start_addr, + value + last_source_start_addr); + } + break; + + case N_FN: + /* This kind of symbol supposedly indicates the start + of an object file. In fact this type does not appear. */ + break; + + case N_SO: + /* This type of symbol indicates the start of data + for one source file. + Finish the symbol table of the previous source file + (if any) and start accumulating a new symbol table. */ + if (last_source_file) + end_symtab (value); + start_symtab (name, value); + break; + + case N_SOL: + /* This type of symbol indicates the start of data for + a sub-source-file, one whose contents were copied or + included in the compilation of the main source file + (whose name was given in the N_SO symbol.) */ + start_subfile (name); + break; + +#ifdef N_BINCL + case N_BINCL: + push_subfile (); + add_new_header_file (name, value); + start_subfile (name); + break; + + case N_EINCL: + start_subfile (pop_subfile ()); + break; + + case N_EXCL: + add_old_header_file (name, value); + break; +#endif /* have N_BINCL */ + + case N_SLINE: + /* This type of "symbol" really just records + one line-number -- core-address correspondence. + Enter it in the line list for this symbol table. */ + record_line (desc, value); + break; + + case N_BCOMM: + case N_ECOMM: + case N_ECOML: + case N_LENG: + break; + + default: + if (name) + define_symbol (value, name, desc); + } +} + +/* This function was added for C++ functionality. I presume that it + condenses the bunches formed by reading in an additional .o file + (incremental linking). */ + +static void +condense_addl_misc_bunches () +{ + register int i, j; + register struct misc_bunch *bunch; +#ifdef NAMES_HAVE_UNDERSCORE + int offset = 1; +#else + int offset = 0; +#endif + + misc_function_vector + = (struct misc_function *) xrealloc (misc_function_vector, + (misc_count + misc_function_count) * sizeof (struct misc_function)); + + j = misc_function_count; + bunch = misc_bunch; + while (bunch) + { + for (i = 0; i < misc_bunch_index; i++) + { + misc_function_vector[j] = bunch->contents[i]; + misc_function_vector[j].name + = concat (misc_function_vector[j].name + + (misc_function_vector[j].name[0] == '_' ? offset : 0), + "", ""); + j++; + } + bunch = bunch->next; + misc_bunch_index = MISC_BUNCH_SIZE; + } + + misc_function_count += misc_count; + + /* Sort the misc functions by address. */ + + qsort (misc_function_vector, misc_function_count, + sizeof (struct misc_function), compare_misc_functions); +} + + +/* Read in another .o file and create a symtab entry for it.*/ + +static void +read_addl_syms (desc, stringtab, nlistlen, text_addr, text_size) + int desc; + register char *stringtab; + register int nlistlen; + unsigned text_addr; + int text_size; +{ + FILE *stream = fdopen (desc, "r"); + register char *namestring; + register struct symbol *sym, *prev; + int hash; + int num_object_files = 0; + +#ifdef N_BINCL + subfile_stack = 0; +#endif + + last_source_file = 0; + bzero (global_sym_chain, sizeof global_sym_chain); + symtab_input_desc = desc; + stringtab_global = stringtab; + fill_symbuf (); + + for (symnum = 0; symnum < nlistlen; symnum++) + { + struct nlist *bufp; + unsigned char type; + + QUIT; /* allow this to be interruptable */ + if (symbuf_idx == symbuf_end) + fill_symbuf (); + bufp = &symbuf[symbuf_idx++]; + type = bufp->n_type & N_TYPE; + namestring = bufp->n_un.n_strx ? bufp->n_un.n_strx + stringtab : ""; + + if( (type == N_TEXT) || (type == N_DATA) || (type == N_BSS) ) + { + /* Relocate this file's symbol table information + to the address it has been loaded into. */ + bufp->n_value += text_addr; + } + + type = bufp->n_type; + + if (type & N_STAB) + process_one_symbol (type, bufp->n_desc, + bufp->n_value, namestring); + /* A static text symbol whose name ends in ".o" + can only mean the start of another object file. + So end the symtab of the source file we have been processing. + This is how we avoid counting the libraries as part + or the last source file. + Also this way we find end of first object file (crt0). */ + else if ((type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + && (!strcmp (namestring + strlen (namestring) - 2, ".o")) + || ! strcmp (namestring, "-l", 2)) + { + if (num_object_files++ == 1) + first_object_file_end = bufp->n_value; + if (last_source_file) + end_symtab (bufp->n_value); + } + else if (type & N_EXT || type == N_TEXT +#ifdef N_NBTEXT + || type == N_NBTEXT +#endif + ) + { + int used_up = 0; + + /* Record the location of _etext. */ + if (type == (N_TEXT | N_EXT) + && !strcmp (namestring, "_etext")) + end_of_text_addr = bufp->n_value; + + /* Global symbol: see if we came across a dbx definition + for a corresponding symbol. If so, store the value. + Remove syms from the chain when their values are stored, + but search the whole chain, as there may be several syms + from different files with the same name. */ + if (type & N_EXT) + { + prev = 0; +#ifdef NAMES_HAVE_UNDERSCORE + hash = hashname (namestring + 1); +#else /* not NAMES_HAVE_UNDERSCORE */ + hash = hashname (namestring); +#endif /* not NAMES_HAVE_UNDERSCORE */ + for (sym = global_sym_chain[hash]; + sym;) + { + if ( +#ifdef NAMES_HAVE_UNDERSCORE + *namestring == '_' + && namestring[1] == SYMBOL_NAME (sym)[0] + && + !strcmp (namestring + 2, SYMBOL_NAME (sym) + 1) +#else /* NAMES_HAVE_UNDERSCORE */ + namestring[0] == SYMBOL_NAME (sym)[0] + && + !strcmp (namestring + 1, SYMBOL_NAME (sym) + 1) +#endif /* NAMES_HAVE_UNDERSCORE */ + ) + { + if (prev) + SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym); + else + global_sym_chain[hash] + = (struct symbol *) SYMBOL_VALUE (sym); + SYMBOL_VALUE (sym) = bufp->n_value; + if (prev) + sym = (struct symbol *) SYMBOL_VALUE (prev); + else + sym = global_sym_chain[hash]; + + used_up = 1; + } + else + { + prev = sym; + sym = (struct symbol *) SYMBOL_VALUE (sym); + } + } + } + + /* Defined global or text symbol: record as a misc function + if it didn't give its address to a debugger symbol above. */ + if (type <= (N_TYPE | N_EXT) + && type != N_EXT + && ! used_up) + record_misc_function (namestring, bufp->n_value); + } + } + + if (last_source_file) + end_symtab (text_addr + text_size); + + fclose (stream); +} + +/* C++: + This function allows the addition of incrementally linked object files. + Since this has a fair amount of code in common with symbol_file_command, + it might be worthwhile to consolidate things, as was done with + read_dbx_symtab and condense_misc_bunches. */ + +void +add_file_command (arg_string) + char* arg_string; +{ + register int desc; + DECLARE_FILE_HEADERS; + struct nlist *nlist; + char *stringtab; + long buffer; + register int val; + extern void close (); + struct cleanup *old_chain; + struct symtab *symseg; + struct stat statbuf; + char *name; + unsigned text_addr; + + if (arg_string == 0) + error ("add-file takes a file name and an address"); + + for( ; *arg_string == ' '; arg_string++ ); + name = arg_string; + for( ; *arg_string && *arg_string != ' ' ; arg_string++ ); + *arg_string++ = (char) 0; + + if (name[0] == 0) + error ("add-file takes a file name and an address"); + + text_addr = parse_and_eval_address (arg_string); + + dont_repeat (); + + if (!query ("add symbol table from filename \"%s\" at text_addr = 0x%x\n", + name, text_addr)) + error ("Not confirmed."); + + desc = open (name, O_RDONLY); + if (desc < 0) + perror_with_name (name); + + old_chain = make_cleanup (close, desc); + make_cleanup (free_current_contents, &name); + + READ_FILE_HEADERS (desc, name); + + if (NUMBER_OF_SYMBOLS == 0) + { + printf ("%s does not have a symbol-table.\n", name); + fflush (stdout); + return; + } + + printf ("Reading symbol data from %s...", name); + fflush (stdout); + + /* Now read the string table, all at once. */ + val = lseek (desc, STRING_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + stat (name, &statbuf); + READ_STRING_TABLE_SIZE (buffer); + if (buffer >= 0 && buffer < statbuf.st_size) + stringtab = (char *) alloca (buffer); + else + stringtab = NULL; + if (stringtab == NULL) + error ("ridiculous string table size: %d bytes", name, buffer); + + bcopy (&buffer, stringtab, sizeof buffer); + val = myread (desc, stringtab + sizeof buffer, buffer - sizeof buffer); + if (val < 0) + perror_with_name (name); + +#ifdef READ_GDB_SYMSEGS + /* That puts us at the symsegs. Read them. */ + symseg_chain = read_symsegs (desc, name); + hash_symsegs (); + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + for (symseg = symseg_chain; symseg; symseg = symseg->next) + { + int i; + struct sourcevector *sv = (struct sourcevector *) symseg->linetable; + + for (i = 0; i < sv->length; i++) + { + int j; + struct source *source = sv->source[i]; + struct symtab *sp1 + = (struct symtab *) xxmalloc (sizeof (struct symtab)); + + bcopy (symseg, sp1, sizeof (struct symtab)); + sp1->filename = savestring (source->name, strlen (source->name)); + sp1->linetable = &source->contents; + sp1->free_code = free_nothing; + sp1->free_ptr = (i == 0) ? (char *) symseg : 0; + + sp1->next = symtab_list; + symtab_list = sp1; + } + } +#else + /* Where people are using the 4.2 ld program, must not check for + symsegs, because that ld puts randonm garbage at the end of + the output file and that would trigger an error message. */ + symseg_chain = 0; +#endif + + /* Position to read the symbol table. Do not read it all at once. */ + val = lseek (desc, SYMBOL_TABLE_OFFSET, 0); + if (val < 0) + perror_with_name (name); + + init_misc_functions (); + make_cleanup (discard_misc_bunches, 0); + init_header_files (); + make_cleanup (free_header_files, 0); + free_pendings = 0; + pending_blocks = 0; + file_symbols = 0; + global_symbols = 0; + make_cleanup (really_free_pendings, 0); + + read_addl_syms (desc, stringtab, NUMBER_OF_SYMBOLS, text_addr, + SIZE_OF_TEXT_SEGMENT); + + + /* Sort symbols alphabetically within each block. */ + + sort_syms (); + + /* Go over the misc functions and install them in vector. */ + + condense_addl_misc_bunches (1); + + /* Don't allow char * to have a typename (else would get caddr_t.) */ + + TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0; + + /* Make a default for file to list. */ + /* Hmmm. I'd say we don't want this in add_file_command, but . . . */ + + select_source_symtab (symtab_list); + + do_cleanups (old_chain); + + /* Free the symtabs made by read_symsegs, but not their contents, + which have been copied into symtabs on symtab_list. */ + while (symseg_chain) + { + register struct symtab *s = symseg_chain->next; + free (symseg_chain); + symseg_chain = s; + } + + printf ("done.\n"); + fflush (stdout); +} + +static struct symbol * +define_symbol (value, string, desc) + int value; + char *string; + int desc; +{ + register struct symbol *sym + = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol)); + char *p = (char *) index (string, ':'); + int deftype; + register int i; + + /* Ignore syms with empty names. */ + if (string[0] == 0) + return 0; + + SYMBOL_NAME (sym) + = (char *) obstack_alloc (symbol_obstack, ((p - string) + 1)); + /* Open-coded bcopy--saves function call time. */ + { + register char *p1 = string; + register char *p2 = SYMBOL_NAME (sym); + while (p1 != p) + *p2++ = *p1++; + *p2++ = '\0'; + } + p++; + /* Determine the type of name being defined. */ + if ((*p >= '0' && *p <= '9') || *p == '(') + deftype = 'l'; + else + deftype = *p++; + + /* c is a special case, not followed by a type-number. + SYMBOL:c=iVALUE for an integer constant symbol. + SYMBOL:c=rVALUE for a floating constant symbol. */ + if (deftype == 'c') + { + if (*p++ != '=') + error ("Invalid symbol data at symtab pos %d.", symnum); + switch (*p++) + { + case 'r': + { + double d = atof (p); + char *value; + + SYMBOL_TYPE (sym) = builtin_type_double; + value = (char *) obstack_alloc (symbol_obstack, sizeof (double)); + bcopy (&d, value, sizeof (double)); + SYMBOL_VALUE_BYTES (sym) = value; + SYMBOL_CLASS (sym) = LOC_CONST; + } + break; + case 'i': + { + SYMBOL_TYPE (sym) = builtin_type_int; + SYMBOL_VALUE (sym) = atoi (p); + SYMBOL_CLASS (sym) = LOC_CONST_BYTES; + } + break; + default: + error ("Invalid symbol data at symtab pos %d.", symnum); + } + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + return sym; + } + + /* Now usually comes a number that says which data type, + and possibly more stuff to define the type + (all of which is handled by read_type) */ + + if (deftype == 'p' && *p == 'F') + /* pF is a two-letter code that means a function parameter in Fortran. + The type-number specifies the type of the return value. + Translate it into a pointer-to-function type. */ + { + p++; + SYMBOL_TYPE (sym) + = lookup_pointer_type (lookup_function_type (read_type (&p))); + } + else + { + struct type *type = read_type (&p); + + if ((deftype == 'F' || deftype == 'f') + && TYPE_CODE (type) != TYPE_CODE_FUNC) + SYMBOL_TYPE (sym) = lookup_function_type (type); + else + SYMBOL_TYPE (sym) = type; + } + + switch (deftype) + { + case 'f': + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + break; + + case 'F': + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &global_symbols); + break; + + case 'G': + /* For a class G (global) symbol, it appears that the + value is not correct. It is necessary to search for the + corresponding linker definition to find the value. + These definitions appear at the end of the namelist. */ + i = hashname (SYMBOL_NAME (sym)); + SYMBOL_VALUE (sym) = (int) global_sym_chain[i]; + global_sym_chain[i] = sym; + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &global_symbols); + break; + + /* This case is faked by a conditional above, + when there is no code letter in the dbx data. + Dbx data never actually contains 'l'. */ + case 'l': + SYMBOL_CLASS (sym) = LOC_LOCAL; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'p': + SYMBOL_CLASS (sym) = LOC_ARG; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + /* DESC == 0 implies compiled with GCC. + In this case, if it says `short', believe it. */ + if (desc == 0) + break; + /* If PCC says a parameter is a short or a char, + it is really an int. */ + if (SYMBOL_TYPE (sym) == builtin_type_char + || SYMBOL_TYPE (sym) == builtin_type_short) + SYMBOL_TYPE (sym) = builtin_type_int; + else if (SYMBOL_TYPE (sym) == builtin_type_unsigned_char + || SYMBOL_TYPE (sym) == builtin_type_unsigned_short) + SYMBOL_TYPE (sym) = builtin_type_unsigned_int; + break; + + case 'P': + SYMBOL_CLASS (sym) = LOC_REGPARM; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'r': + SYMBOL_CLASS (sym) = LOC_REGISTER; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + case 'S': + /* Static symbol at top level of file */ + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &file_symbols); + break; + + case 't': + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) = + obsavestring (SYMBOL_NAME (sym), + strlen (SYMBOL_NAME (sym))); + /* C++ vagaries: we may have a type which is derived from + a base type which did not have its name defined when the + derived class was output. We fill in the derived class's + base part member's name here in that case. */ + else if ((TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT + || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION) + && TYPE_N_BASECLASSES (SYMBOL_TYPE (sym))) + { + int i; + for (i = TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)); i > 0; i--) + if (TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) == 0) + TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) = + TYPE_NAME (TYPE_BASECLASS (SYMBOL_TYPE (sym), i)); + } + + add_symbol_to_list (sym, &file_symbols); + break; + + case 'T': + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE; + if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0 + && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0) + TYPE_NAME (SYMBOL_TYPE (sym)) + = obconcat ("", + (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_ENUM + ? "enum " + : (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT + ? "struct " : "union ")), + SYMBOL_NAME (sym)); + add_symbol_to_list (sym, &file_symbols); + break; + + case 'V': + case 'v': + /* Static symbol of local scope */ + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) = value; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &local_symbols); + break; + + default: + error ("Invalid symbol data: unknown symbol-type code `%c' at symtab pos %d.", deftype, symnum); + } + return sym; +} + +/* Read a number by which a type is referred to in dbx data, + or perhaps read a pair (FILENUM, TYPENUM) in parentheses. + Just a single number N is equivalent to (0,N). + Return the two numbers by storing them in the vector TYPENUMS. + TYPENUMS will then be used as an argument to dbx_lookup_type. */ + +static void +read_type_number (pp, typenums) + register char **pp; + register int *typenums; +{ + if (**pp == '(') + { + (*pp)++; + typenums[0] = read_number (pp, ','); + typenums[1] = read_number (pp, ')'); + } + else + { + typenums[0] = 0; + typenums[1] = read_number (pp, 0); + } +} + +/* Read a dbx type reference or definition; + return the type that is meant. + This can be just a number, in which case it references + a type already defined and placed in type_vector. + Or the number can be followed by an =, in which case + it means to define a new type according to the text that + follows the =. */ + +static +struct type * +read_type (pp) + register char **pp; +{ + register struct type *type = 0; + register int n; + struct type *type1; + int typenums[2]; + int xtypenums[2]; + + read_type_number (pp, typenums); + + /* Detect random reference to type not yet defined. + Allocate a type object but leave it zeroed. */ + if (**pp != '=') + return dbx_alloc_type (typenums); + + *pp += 2; + switch ((*pp)[-1]) + { + case 'x': + type = dbx_alloc_type (typenums); + /* Set the type code according to the following letter. */ + switch ((*pp)[0]) + { + case 's': + TYPE_CODE (type) = TYPE_CODE_STRUCT; + break; + case 'u': + TYPE_CODE (type) = TYPE_CODE_UNION; + break; + case 'e': + TYPE_CODE (type) = TYPE_CODE_ENUM; + break; + } + /* Skip the name the cross-ref points to. */ + /* Note: for C++, the cross reference may be to a base type which + has not yet been seen. In this case, we skip to the comma, + which will mark the end of the base class name. (The ':' + at the end of the base class name will be skipped as well.) */ + *pp = (char *) index (*pp, ','); + /* Just allocate the type and leave it zero if nothing known */ + return dbx_alloc_type (typenums); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '(': + (*pp)--; + read_type_number (pp, xtypenums); + type = *dbx_lookup_type (xtypenums); + if (type == 0) + type = builtin_type_void; + *dbx_lookup_type (typenums) = type; + break; + + case '*': + type1 = read_type (pp); + if (TYPE_POINTER_TYPE (type1)) + { + type = TYPE_POINTER_TYPE (type1); + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_pointer_type (type, type1); + } + break; + + case '@@': + { + struct type *domain = read_type (pp); + char c; + struct type *memtype; + + if (*(*pp)++ != ',') + error ("invalid member type data format, at symtab pos %d.", + symnum); + + memtype = read_type (pp); + type = dbx_alloc_type (typenums); + smash_to_member_type (type, domain, memtype); + } + break; + + case '&': + type1 = read_type (pp); + if (TYPE_REFERENCE_TYPE (type1)) + { + type = TYPE_REFERENCE_TYPE (type1); + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_reference_type (type, type1); + } + break; + + case 'f': + type1 = read_type (pp); + if (TYPE_FUNCTION_TYPE (type1)) + { + type = TYPE_FUNCTION_TYPE (type1); + *dbx_lookup_type (typenums) = type; + } + else + { + type = dbx_alloc_type (typenums); + smash_to_function_type (type, type1); + } + break; + + case 'r': + type = read_range_type (pp, typenums); + *dbx_lookup_type (typenums) = type; + break; + + case 'e': + type = dbx_alloc_type (typenums); + type = read_enum_type (pp, type); + *dbx_lookup_type (typenums) = type; + break; + + case 's': + type = dbx_alloc_type (typenums); + type = read_struct_type (pp, type); + break; + + case 'u': + type = dbx_alloc_type (typenums); + type = read_struct_type (pp, type); + TYPE_CODE (type) = TYPE_CODE_UNION; + break; + + case 'a': + if (*(*pp)++ != 'r') + error ("Invalid symbol data: unrecognized type-code `a%c' %s %d.", + (*pp)[-1], "at symtab position", symnum); + + type = dbx_alloc_type (typenums); + type = read_array_type (pp, type); + break; + +#if 0 + /* Format of an array type: + "ar<index type>;lower;upper;<array_contents_type>". Put code + in to handle this. */ + + /* dbx expresses array types in terms of a range type for the index, + and that range type is specified right inside the array type spec + making ar1;MIN;MAX;VALTYPE */ + if (!strncmp (*pp, "r1;0;", 5)) + (*pp) += 5; + else if (!strncmp (*pp, "r(0,1);0;", 9)) + (*pp) += 9; + else break; + + TYPE_CODE (type) = TYPE_CODE_ARRAY; + /* In Fortran, an upper bound may be T... meaning a parameter specifies + the length of the data. In this case, just pretend the bound is 1. + This happens only for array parameters, which are really passed + as pointers anyway, and we will translate them into such. */ + if (**pp == 'T') + { + n = 1; + while (**pp != ';') + (*pp)++; + } + else + n = read_number (pp, ';') + 1; + TYPE_TARGET_TYPE (type) = read_type (pp); + TYPE_LENGTH (type) = TYPE_LENGTH (TYPE_TARGET_TYPE (type)) * n; + break; +#endif + + default: + error ("Invalid symbol data: unrecognized type-code `%c' at symtab pos %d.", + (*pp)[-1], symnum); + } + + if (type == 0) + abort (); + +#if 0 + /* If this is an overriding temporary alteration for a header file's + contents, and this type number is unknown in the global definition, + put this type into the global definition at this type number. */ + if (header_file_prev_index >= 0) + { + register struct type **tp + = explicit_lookup_type (header_file_prev_index, typenums[1]); + if (*tp == 0) + *tp = type; + } +#endif + return type; +} + +/* This page contains subroutines of read_type. */ + +/* Read the description of a structure (or union type) + and return an object describing the type. */ + +static struct type * +read_struct_type (pp, type) + char **pp; + register struct type *type; +{ + struct nextfield + { + struct nextfield *next; + int visibility; + struct field field; + }; + + struct next_fnfield + { + struct next_fnfield *next; + int visibility; + struct fn_field fn_field; + }; + + struct next_fnfieldlist + { + struct next_fnfieldlist *next; + struct fn_fieldlist fn_fieldlist; + }; + + register struct nextfield *list = 0; + struct nextfield *new; + int totalsize; + char *name; + register char *p; + int nfields = 0; + register int n; + + register struct next_fnfieldlist *mainlist = 0; + int nfn_fields = 0; + struct type *baseclass = NULL; + int read_possible_virtual_info = 0; + + TYPE_CODE (type) = TYPE_CODE_STRUCT; + + /* First comes the total size in bytes. */ + + TYPE_LENGTH (type) = read_number (pp, 0); + + /* C++: Now, if the class is a derived class, then the next character + will be a '!', followed by the number of base classes derived from. + Each element in the list contains visibility information, + the offset of this base class in the derived structure, + and then the base type. */ + if (**pp == '!') + { + int i, n_baseclasses, offset; + struct type **baseclass_vec; + struct type *baseclass; + int via_public, via_virtual; + + *pp += 1; + + n_baseclasses = read_number (pp, ','); + baseclass_vec = (struct type **) + obstack_alloc (symbol_obstack, + (n_baseclasses) * sizeof (struct type **)) - 1; + + for (i = 1; i <= n_baseclasses; i++) + { + if (**pp == '\\') + *pp = next_symbol_text (); + + switch (*(*pp)++) + { + case '0': + via_virtual = 0; + break; + case '1': + via_virtual = 1; + break; + default: + error ("Invalid symbol data: bad visibility format at symtab pos %d", + symnum); + } + + switch (*(*pp)++) + { + case '0': + via_public = 0; + break; + case '2': + via_public = 1; + break; + default: + error ("Invalid symbol data: bad visibility format at symtab pos %d.", + symnum); + } + offset = read_number (pp, ','); + baseclass = read_type (pp); + *pp += 1; /* skip trailing ';' */ + baseclass_vec[i] = lookup_basetype_type (baseclass, offset, via_virtual, via_public); + + /* Make this baseclass visible for structure-printing purposes. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + list->field.type = baseclass_vec[i]; + list->field.name = TYPE_NAME (baseclass_vec[i]); + list->field.bitpos = offset; + list->field.bitsize = 0; /* this should be an unpacked field! */ + nfields++; + } + TYPE_N_BASECLASSES (type) = n_baseclasses; + TYPE_BASECLASSES (type) = baseclass_vec; + } + + /* Now come the fields, as NAME:?TYPENUM,BITPOS,BITSIZE; for each one. + At the end, we see a semicolon instead of a field. + + In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for + a static field. + + The `?' is a placeholder for one of '+' (public visibility), + '0' (protected visibility), and '-' (private visibility). */ + + while (**pp != ';') + { + int visibility; + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + /* Get space to record the next field's data. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + /* Read the data. */ + p = *pp; + while (*p != ':') p++; + list->field.name = obsavestring (*pp, p - *pp); + + /* C++: Check to see if we have hit the methods yet. */ + if (p[1] == ':') + break; + + *pp = p + 1; + + /* This means we have a visibility for a field coming. */ + if (**pp == '/') + { + switch (*++*pp) + { + case '0': + visibility = 0; + *pp += 1; + break; + + case '1': + visibility = 1; + *pp += 1; + break; + + case '2': + visibility = 2; + *pp += 1; + break; + } + } + /* else normal dbx-style format. */ + + list->field.type = read_type (pp); + if (**pp == ':') + { + list->field.bitpos = (long)-1; + p = ++(*pp); + while (*p != ';') p++; + list->field.bitsize = (long) savestring (*pp, p - *pp); + *pp = p + 1; + nfields++; + continue; + } + else if (**pp != ',') + error ("Invalid symbol data: bad structure-type format at symtab pos %d.", + symnum); + (*pp)++; /* Skip the comma. */ + list->field.bitpos = read_number (pp, ','); + list->field.bitsize = read_number (pp, ';'); + /* Detect an unpacked field and mark it as such. + dbx gives a bit size for all fields. + Note that forward refs cannot be packed, + and treat enums as if they had the width of ints. */ + if (TYPE_CODE (list->field.type) != TYPE_CODE_INT + && TYPE_CODE (list->field.type) != TYPE_CODE_ENUM) + list->field.bitsize = 0; + if ((list->field.bitsize == 8 * TYPE_LENGTH (list->field.type) + || (TYPE_CODE (list->field.type) == TYPE_CODE_ENUM + && list->field.bitsize == 8 * TYPE_LENGTH (builtin_type_int))) + && + list->field.bitpos % 8 == 0) + list->field.bitsize = 0; + nfields++; + } + + /* Now come the method fields, as NAME::methods + where each method is of the form TYPENUM,ARGS,...:PHYSNAME; + At the end, we see a semicolon instead of a field. + + For the case of overloaded operators, the format is + OPERATOR::*.methods, where OPERATOR is the string "operator", + `*' holds the place for an operator name (such as `+=') + and `.' marks the end of the operator name. */ + if (p[1] == ':') + { + /* Now, read in the methods. To simplify matters, we + "unread" the name that has been read, so that we can + start from the top. */ + + p = *pp; + + /* chill the list of fields: the last entry (at the head) + is a partially constructed entry which we now scrub. */ + list = list->next; + + /* For each list of method lists... */ + do + { + int i; + struct next_fnfield *sublist = 0; + struct fn_field *fn_fields = 0; + int length = 0; + struct next_fnfieldlist *new_mainlist = + (struct next_fnfieldlist *)alloca (sizeof (struct next_fnfieldlist)); + + /* read in the name. */ + while (*p != ':') p++; + if ((*pp)[0] == 'o' && (*pp)[1] == 'p' && (*pp)[2] == '$') + { + static char opname[32] = "operator "; + char *o = opname + 9; + + /* Skip past '::'. */ + p += 2; + while (*p != '.') + *o++ = *p++; + new_mainlist->fn_fieldlist.name = savestring (opname, o - opname); + /* Skip past '.' */ + *pp = p + 1; + } + else + { + i = 0; + new_mainlist->fn_fieldlist.name = savestring (*pp, p - *pp); + /* Skip past '::'. */ + *pp = p + 2; + } + + do + { + struct next_fnfield *new_sublist = + (struct next_fnfield *)alloca (sizeof (struct next_fnfield)); + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + new_sublist->fn_field.type = read_type (pp); + new_sublist->fn_field.args = read_args (pp, ':'); + p = *pp; + while (*p != ';') p++; + new_sublist->fn_field.physname = savestring (*pp, p - *pp); + *pp = p + 1; + new_sublist->visibility = *(*pp)++ - '0'; + if (**pp == '\\') *pp = next_symbol_text (); + + if (*(*pp)++ == '*') + new_sublist->fn_field.voffset = read_number (pp, ';') + 1; + else + new_sublist->fn_field.voffset = 0; + + new_sublist->next = sublist; + sublist = new_sublist; + length++; + } + while (**pp != ';'); + + *pp += 1; + + new_mainlist->fn_fieldlist.fn_fields = + (struct fn_field *) obstack_alloc (symbol_obstack, + sizeof (struct fn_field) * length); + TYPE_FN_PRIVATE_BITS (new_mainlist->fn_fieldlist) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (length >> 5))); + + TYPE_FN_PROTECTED_BITS (new_mainlist->fn_fieldlist) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (length >> 5))); + + for (i = length; sublist; sublist = sublist->next) + { + new_mainlist->fn_fieldlist.fn_fields[--i] = sublist->fn_field; + if (sublist->visibility == 0) + B_SET (new_mainlist->fn_fieldlist.private_fn_field_bits, i); + else if (sublist->visibility == 1) + B_SET (new_mainlist->fn_fieldlist.protected_fn_field_bits, i); + } + + new_mainlist->fn_fieldlist.length = length; + new_mainlist->next = mainlist; + mainlist = new_mainlist; + nfn_fields++; + } + while (**pp != ';'); + } + + *pp += 1; + + /* Now create the vector of fields, and record how big it is. */ + + TYPE_NFIELDS (type) = nfields; + TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, + sizeof (struct field) * nfields); + TYPE_FIELD_PRIVATE_BITS (type) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (nfields >> 5))); + TYPE_FIELD_PROTECTED_BITS (type) = + (int *) obstack_alloc (symbol_obstack, + sizeof (int) * (1 + (nfields >> 5))); + + TYPE_NFN_FIELDS (type) = nfn_fields; + TYPE_NFN_FIELDS_TOTAL (type) = nfn_fields; + if (baseclass) + TYPE_NFN_FIELDS_TOTAL (type) += TYPE_NFN_FIELDS_TOTAL (baseclass); + + TYPE_FN_FIELDLISTS (type) = + (struct fn_fieldlist *) obstack_alloc (symbol_obstack, + sizeof (struct fn_fieldlist) * nfn_fields); + + /* Copy the saved-up fields into the field vector. */ + + for (n = nfields; list; list = list->next) + { + TYPE_FIELD (type, --n) = list->field; + if (list->visibility == 0) + SET_TYPE_FIELD_PRIVATE (type, n); + else if (list->visibility == 1) + SET_TYPE_FIELD_PROTECTED (type, n); + } + + for (n = nfn_fields; mainlist; mainlist = mainlist->next) + TYPE_FN_FIELDLISTS (type)[--n] = mainlist->fn_fieldlist; + + if (**pp == '~') + { + *pp += 1; + + if (**pp == '=') + { + TYPE_FLAGS (type) + |= TYPE_FLAG_HAS_CONSTRUCTOR | TYPE_FLAG_HAS_DESTRUCTOR; + *pp += 1; + } + else if (**pp == '+') + { + TYPE_FLAGS (type) |= TYPE_FLAG_HAS_CONSTRUCTOR; + *pp += 1; + } + else if (**pp == '-') + { + TYPE_FLAGS (type) |= TYPE_FLAG_HAS_DESTRUCTOR; + *pp += 1; + } + + /* Read either a '%' or the final ';'. */ + if (*(*pp)++ == '%') + { + /* Now we must record the virtual function table pointer's + field information. */ + + struct type *t; + int i; + + t = read_type (pp); + p = (*pp)++; + while (*p != ';') p++; + TYPE_VPTR_BASETYPE (type) = t; + if (type == t) + { + if (TYPE_FIELD_NAME (t, 0) == 0) + TYPE_VPTR_FIELDNO (type) = i = 0; + else for (i = TYPE_NFIELDS (t) - 1; i >= 0; --i) + if (! strncmp (TYPE_FIELD_NAME (t, i), *pp, + strlen (TYPE_FIELD_NAME (t, i)))) + { + TYPE_VPTR_FIELDNO (type) = i; + break; + } + if (i < 0) + error ("virtual function table field not found"); + } + else + TYPE_VPTR_FIELDNO (type) = TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, 1)); + *pp = p + 1; + } + else + { + TYPE_VPTR_BASETYPE (type) = 0; + TYPE_VPTR_FIELDNO (type) = -1; + } + } + else + { + TYPE_VPTR_BASETYPE (type) = 0; + TYPE_VPTR_FIELDNO (type) = -1; + } + + return type; +} + +/* Read a definition of an enumberation type, + and create and return a suitable type object. + Also creates a range type which represents the bounds of that + array. */ +static struct type * +read_array_type (pp, type) + register char **pp; + register struct type *type; +{ + struct type *index_type, *element_type, *range_type; + int lower, upper; + + /* Format of an array type: + "ar<index type>;lower;upper;<array_contents_type>". Put code in + to handle this. */ + + index_type = read_type (pp); + if (*(*pp)++ != ';') + error ("Invalid symbol data; improper format of array type decl."); + lower = read_number (pp, ';'); + upper = read_number (pp, ';'); + element_type = read_type (pp); + + { + /* Create range type. */ + range_type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + TYPE_CODE (range_type) = TYPE_CODE_RANGE; + TYPE_TARGET_TYPE (range_type) = index_type; + + /* This should never be needed. */ + TYPE_LENGTH (range_type) = sizeof (int); + + TYPE_NFIELDS (range_type) = 2; + TYPE_FIELDS (range_type) = + (struct field *) obstack_alloc (symbol_obstack, + 2 * sizeof (struct field)); + TYPE_FIELD_BITPOS (range_type, 0) = lower; + TYPE_FIELD_BITPOS (range_type, 1) = upper; + } + + TYPE_CODE (type) = TYPE_CODE_ARRAY; + TYPE_TARGET_TYPE (type) = element_type; + TYPE_LENGTH (type) = (upper - lower + 1) * TYPE_LENGTH (element_type); + TYPE_NFIELDS (type) = 1; + TYPE_FIELDS (type) = + (struct field *) obstack_alloc (symbol_obstack, + sizeof (struct field)); + TYPE_FIELD_TYPE (type, 0) = range_type; + + return type; +} + + +/* Read a definition of an enumeration type, + and create and return a suitable type object. + Also defines the symbols that represent the values of the type. */ + +static struct type * +read_enum_type (pp, type) + register char **pp; + register struct type *type; +{ + register char *p; + char *name; + register long n; + register struct symbol *sym; + int nsyms = 0; + struct pending **symlist; + struct pending *osyms, *syms; + int o_nsyms; + + if (within_function) + symlist = &local_symbols; + else + symlist = &file_symbols; + osyms = *symlist; + o_nsyms = osyms ? osyms->nsyms : 0; + + /* Read the value-names and their values. + The input syntax is NAME:VALUE,NAME:VALUE, and so on. + A semicolon instead of a NAME means the end. */ + while (**pp && **pp != ';') + { + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') *pp = next_symbol_text (); + + p = *pp; + while (*p != ':') p++; + name = obsavestring (*pp, p - *pp); + *pp = p + 1; + n = read_number (pp, ','); + + sym = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol)); + bzero (sym, sizeof (struct symbol)); + SYMBOL_NAME (sym) = name; + SYMBOL_CLASS (sym) = LOC_CONST; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_VALUE (sym) = n; + add_symbol_to_list (sym, symlist); + nsyms++; + } + + (*pp)++; /* Skip the semicolon. */ + + /* Now fill in the fields of the type-structure. */ + + TYPE_LENGTH (type) = sizeof (int); + TYPE_CODE (type) = TYPE_CODE_ENUM; + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field) * nsyms); + + /* Find the symbols for the values and put them into the type. + The symbols can be found in the symlist that we put them on + to cause them to be defined. osyms contains the old value + of that symlist; everything up to there was defined by us. */ + + for (syms = *symlist, n = nsyms; syms; syms = syms->next) + { + int j = 0; + if (syms == osyms) + j = o_nsyms; + for (; j < syms->nsyms; j++) + { + struct symbol *sym = syms->symbol[j]; + SYMBOL_TYPE (sym) = type; + TYPE_FIELD_NAME (type, --n) = SYMBOL_NAME (sym); + TYPE_FIELD_VALUE (type, n) = 0; + TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (sym); + TYPE_FIELD_BITSIZE (type, n) = 0; + } + if (syms == osyms) + break; + } + + return type; +} + +#define MAX_OF_TYPE(t) ((1 << (sizeof (t) - 1)) - 1) +#define MIN_OF_TYPE(t) (-(1 << (sizeof (t) - 1))) + +static struct type * +read_range_type (pp, typenums) + char **pp; + int typenums[2]; +{ + char *errp = *pp; + int rangenums[2]; + int n2, n3; + int self_subrange; + struct type *result_type; + + /* First comes a type we are a subrange of. + In C it is usually 0, 1 or the type being defined. */ + read_type_number (pp, rangenums); + self_subrange = (rangenums[0] == typenums[0] && + rangenums[1] == typenums[1]); + + /* A semicolon should now follow; skip it. */ + if (**pp == ';') + (*pp)++; + + /* The remaining two operands are usually lower and upper bounds + of the range. But in some special cases they mean something else. */ + n2 = read_number (pp, ';'); + n3 = read_number (pp, ';'); + + /* A type defined as a subrange of itself, with bounds both 0, is void. */ + if (self_subrange && n2 == 0 && n3 == 0) + return builtin_type_void; + + /* If n3 is zero and n2 is not, we want a floating type, + and n2 is the width in bytes. + + Fortran programs appear to use this for complex types also, + and they give no way to distinguish between double and single-complex! + We don't have complex types, so we would lose on all fortran files! + So return type `double' for all of those. It won't work right + for the complex values, but at least it makes the file loadable. */ + + if (n3 == 0 && n2 > 0) + { + if (n2 == sizeof (float)) + return builtin_type_float; + return builtin_type_double; + } + + /* If the upper bound is -1, it must really be an unsigned int. */ + + else if (n2 == 0 && n3 == -1) + { + if (sizeof (int) == sizeof (long)) + return builtin_type_unsigned_int; + else + return builtin_type_unsigned_long; + } + + /* Special case: char is defined (Who knows why) as a subrange of + itself with range 0-127. */ + else if (self_subrange && n2 == 0 && n3 == 127) + return builtin_type_char; + + /* Assumptions made here: Subrange of self is equivalent to subrange + of int. */ + else if (n2 == 0 + && (self_subrange || + *dbx_lookup_type (rangenums) == builtin_type_int)) + { + /* an unsigned type */ + if (n3 == (1 << (8 * sizeof (int))) - 1) + return builtin_type_unsigned_int; + if (n3 == (1 << (8 * sizeof (short))) - 1) + return builtin_type_unsigned_short; + if (n3 == (1 << (8 * sizeof (char))) - 1) + return builtin_type_unsigned_char; + } + else if (n2 == -n3 -1) + { + /* a signed type */ + if (n3 == (1 << (8 * sizeof (int) - 1)) - 1) + return builtin_type_int; + if (n3 == (1 << (8 * sizeof (long) - 1)) - 1) + return builtin_type_long; + if (n3 == (1 << (8 * sizeof (short) - 1)) - 1) + return builtin_type_short; + if (n3 == (1 << (8 * sizeof (char) - 1)) - 1) + return builtin_type_char; + } + + /* We have a real range type on our hands. Allocate space and + return a real pointer. */ + + /* At this point I don't have the faintest idea how to deal with + a self_subrange type; I'm going to assume that this is used + as an idiom, and that all of them are special cases. So . . . */ + if (self_subrange) + error ("Type defined as subrange of itself."); + + result_type = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + bzero (result_type, sizeof (struct type)); + + TYPE_TARGET_TYPE (result_type) = (self_subrange ? + builtin_type_int : + *dbx_lookup_type(rangenums)); + + /* We have to figure out how many bytes it takes to hold this + range type. I'm going to assume that anything that is pushing + the bounds of a long was taken care of above. */ + if (n2 >= MIN_OF_TYPE(char) && n3 <= MAX_OF_TYPE(char)) + TYPE_LENGTH (result_type) = 1; + else if (n2 >= MIN_OF_TYPE(short) && n3 <= MAX_OF_TYPE(short)) + TYPE_LENGTH (result_type) = sizeof (short); + else if (n2 >= MIN_OF_TYPE(int) && n3 <= MAX_OF_TYPE(int)) + TYPE_LENGTH (result_type) = sizeof (int); + else if (n2 >= MIN_OF_TYPE(long) && n3 <= MAX_OF_TYPE(long)) + TYPE_LENGTH (result_type) = sizeof (long); + else + error ("Ranged type doesn't fit within known sizes."); + + TYPE_LENGTH (result_type) = TYPE_LENGTH (TYPE_TARGET_TYPE (result_type)); + TYPE_CODE (result_type) = TYPE_CODE_RANGE; + TYPE_NFIELDS (result_type) = 2; + TYPE_FIELDS (result_type) = + (struct field *) obstack_alloc (symbol_obstack, + 2 * sizeof (struct field)); + bzero (TYPE_FIELDS (result_type), 2 * sizeof (struct field)); + TYPE_FIELD_BITPOS (result_type, 0) = n2; + TYPE_FIELD_BITPOS (result_type, 1) = n3; + + return result_type; +} + +/* Read a number from the string pointed to by *PP. + The value of *PP is advanced over the number. + If END is nonzero, the character that ends the + number must match END, or an error happens; + and that character is skipped if it does match. + If END is zero, *PP is left pointing to that character. */ + +static long +read_number (pp, end) + char **pp; + int end; +{ + register char *p = *pp; + register long n = 0; + register int c; + int sign = 1; + + /* Handle an optional leading minus sign. */ + + if (*p == '-') + { + sign = -1; + p++; + } + + /* Read the digits, as far as they go. */ + + while ((c = *p++) >= '0' && c <= '9') + { + n *= 10; + n += c - '0'; + } + if (end) + { + if (c && c != end) + error ("Invalid symbol data: invalid character \\%03o at symbol pos %d.", c, symnum); + } + else + --p; + + *pp = p; + return n * sign; +} + +/* Read in an argument list. This is a list of types. It is terminated with + a ':', FYI. Return the list of types read in. */ +static struct type ** +read_args (pp, end) + char **pp; + int end; +{ + struct type *types[1024], **rval; /* allow for fns of 1023 parameters */ + int n = 0; + + while (**pp != end) + { + if (**pp != ',') + error ("Invalid argument list: no ',', at symtab pos %d", symnum); + *pp += 1; + + /* Check for and handle cretinous dbx symbol name continuation! */ + if (**pp == '\\') + *pp = next_symbol_text (); + + types[n++] = read_type (pp); + } + *pp += 1; /* get past `end' (the ':' character) */ + + if (n == 1) + { + rval = (struct type **) xmalloc (2 * sizeof (struct type *)); + } + else if (TYPE_CODE (types[n-1]) != TYPE_CODE_VOID) + { + rval = (struct type **) xmalloc ((n + 1) * sizeof (struct type *)); + bzero (rval + n, sizeof (struct type *)); + } + else + { + rval = (struct type **) xmalloc (n * sizeof (struct type *)); + } + bcopy (types, rval, n * sizeof (struct type *)); + return rval; +} + +/* This function is really horrible, but to avoid it, there would need + to be more filling in of forward references. THIS SHOULD BE MOVED OUT + OF COFFREAD.C AND DBXREAD.C TO SOME PLACE WHERE IT CAN BE SHARED */ +int +fill_in_vptr_fieldno (type) + struct type *type; +{ + if (TYPE_VPTR_FIELDNO (type) < 0) + TYPE_VPTR_FIELDNO (type) = + fill_in_vptr_fieldno (TYPE_BASECLASS (type, 1)); + return TYPE_VPTR_FIELDNO (type); +} + +void +_initialize_dbxread () +{ + symfile = 0; + header_files = (struct header_file *) 0; + this_object_header_files = (int *) 0; + + add_com ("symbol-file", class_files, symbol_file_command, + "Load symbol table (in dbx format) from executable file FILE."); + + add_com ("add-file", class_files, add_file_command, + "Load the symbols from FILE, assuming its code is at TEXT_START.") ; +} + +#endif /* READ_DBX_FORMAT */ +@ + + +1.2 +log +@If discarding the symbol table, discard its name too, so "info files" +will give the right answer. +@ +text +@d27 1 +a27 1 +#include <sys/fcntl.h> +@ + + +1.1 +log +@Initial revision +@ +text +@d2 1 +a2 1 + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. +d1457 3 +@ diff --git a/gdb/RCS/default-dep.c,v b/gdb/RCS/default-dep.c,v new file mode 100644 index 0000000..81c18f3 --- /dev/null +++ b/gdb/RCS/default-dep.c,v @@ -0,0 +1,731 @@ +head 1.4; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.4 +date 89.03.27.20.08.50; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 89.03.27.20.07.47; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.03.27.18.49.06; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.19.47.11; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.4 +log +@Restore A/UX-specific changes. +@ +text +@/* Low level interface to ptrace, for GDB when running under Unix. + Copyright (C) 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#ifdef USG +#include <sys/types.h> +#endif + +#ifdef UNISOFT_ASSHOLES +#define PMMU +#define NEW_PMMU +#define mc68881 /* Needed to get float in user.h!!! */ +#include <sys/seg.h> /* For user.h */ +#include <sys/mmu.h> +#include <sys/time.h> +/* Things Unisoft defined differently from every other Unix system */ +#define NBPG PAGESIZE +#define UPAGES USIZE +#define KERNEL_U_ADDR UDOT +#endif + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif +#ifndef N_SET_MAGIC +#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val)) +#endif + +#include <sys/user.h> /* After a.out.h */ +#include <sys/file.h> +#include <sys/stat.h> + +extern int errno; + +/* This function simply calls ptrace with the given arguments. + It exists so that all calls to ptrace are isolated in this + machine-dependent file. + + If you are having trouble debugging ptrace calls, turn on DEBUG + and every call to ptrace, in this module or elsewhere, will be + logged to stderr. */ +int +call_ptrace (request, pid, arg3, arg4) + int request, pid, arg3, arg4; +{ +#ifdef DEBUG + int result; + + fprintf(stderr, "ptrace(%x,,%x, %x) = ", request, arg3, arg4); + result=ptrace (request, pid, arg3, arg4); + fprintf(stderr, "%x\n", result); + return result; + +#define ptrace call_ptrace + +#else + return ptrace (request, pid, arg3, arg4); +#endif +} + +kill_inferior () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + inferior_died (); +} + +/* This is used when GDB is exiting. It gives less chance of error.*/ + +kill_inferior_fast () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +resume (step, signal) + int step; + int signal; +{ + errno = 0; + if (remote_debugging) + remote_resume (step, signal); + else + { + ptrace (step ? 9 : 7, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); + } +} + +void +fetch_inferior_registers () +{ + register int regno; + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0); + regaddr += sizeof (int); + } + supply_register (regno, buf); + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +store_inferior_registers (regno) + int regno; +{ + register unsigned int regaddr; + char buf[80]; + + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; + + if (regno >= 0) + { + regaddr = register_addr (regno, offset); + errno = 0; +#ifdef UNISOFT_ASSHOLES + /* You can't write the PC with ptrace 6, only with ptrace 11! */ + if (regno == PC_REGNUM) + ptrace(11, inferior_pid, 16, read_register(regno)); + else +#endif + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } + else for (regno = 0; regno < NUM_REGS; regno++) + { + regaddr = register_addr (regno, offset); + errno = 0; +#ifdef UNISOFT_ASSHOLES + if (regno == PC_REGNUM) + ptrace(11, inferior_pid, 16, read_register(regno)); + else +#endif + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing all regs, number %d", regno); + perror_with_name (buf); + } + } +} + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. + On failure (cannot read from inferior, usually because address is out + of bounds) returns the value of errno. */ + +int +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + buffer[i] = remote_fetch_word (addr); + else + buffer[i] = ptrace (1, inferior_pid, addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (remote_debugging) + buffer[0] = remote_fetch_word (addr); + else + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + { + if (remote_debugging) + buffer[count - 1] + = remote_fetch_word (addr + (count - 1) * sizeof (int)); + else + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + remote_store_word (addr, buffer[i]); + else + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +/* Work with core dump and executable files, for GDB. + This code would be in core.c if it weren't machine-dependent. */ + +/* Recognize COFF format systems because a.out.h defines AOUTHDR. */ +#ifdef AOUTHDR +#define COFF_FORMAT +#endif + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +/* Make COFF and non-COFF names for things a little more compatible + to reduce conditionals later. */ + +#ifdef COFF_FORMAT +#define a_magic magic +#endif + +#ifndef COFF_FORMAT +#define AOUTHDR struct exec +#endif + +extern char *sys_siglist[]; + + +/* Hook for `exec_file_command' command to call. */ + +extern void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +extern FILHDR file_hdr; +extern SCNHDR text_hdr; +extern SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +extern AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +extern AOUTHDR exec_aouthdr; + +extern void validate_files (); + +core_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + extern char registers[]; + + /* Discard all vestiges of any previous core file + and mark data and stack spaces as empty. */ + + if (corefile) + free (corefile); + corefile = 0; + + if (corechan >= 0) + close (corechan); + corechan = -1; + + data_start = 0; + data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + /* Now, if a new core file was specified, open it and digest it. */ + + if (filename) + { + if (have_inferior_p ()) + error ("To look at a core file, you must kill the inferior with \"kill\"."); + corechan = open (filename, O_RDONLY, 0); + if (corechan < 0) + perror_with_name (filename); + /* 4.2-style (and perhaps also sysV-style) core dump file. */ + { + struct user u; + + int reg_offset; + + val = myread (corechan, &u, sizeof u); + if (val < 0) + perror_with_name ("Not a core file: reading upage"); + if (val != sizeof u) + error ("Not a core file: could only read %d bytes", val); + data_start = exec_data_start; + + data_end = data_start + NBPG * u.u_dsize; + stack_start = stack_end - NBPG * u.u_ssize; + data_offset = NBPG * UPAGES; + stack_offset = NBPG * (UPAGES + u.u_dsize); + + /* Some machines put an absolute address in here; Unisoft + seems to put the offset in the upage of the regs. Sigh. */ + reg_offset = (int) u.u_ar0; + if (reg_offset > NBPG * UPAGES) + reg_offset -= KERNEL_U_ADDR; + + /* I don't know where to find this info. + So, for now, mark it as not available. */ + N_SET_MAGIC (core_aouthdr, 0); + + /* Read the register values out of the core file and store + them where `read_register' will find them. */ + + { + register int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + { + char buf[MAX_REGISTER_RAW_SIZE]; + + val = lseek (corechan, register_addr (regno, reg_offset), 0); + if (val < 0) + perror_with_name (reg_names[regno]); + + val = myread (corechan, buf, sizeof buf); + if (val < 0) + perror_with_name (reg_names[regno]); + supply_register (regno, buf); + } + } + } + if (filename[0] == '/') + corefile = savestring (filename, strlen (filename)); + else + { + corefile = concat (current_directory, "/", filename); + } + + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + select_frame (get_current_frame (), 0); + validate_files (); + } + else if (from_tty) + printf ("No core file now.\n"); +} + +exec_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + + /* Eliminate all traces of old exec file. + Mark text segment as empty. */ + + if (execfile) + free (execfile); + execfile = 0; + data_start = 0; + data_end -= exec_data_start; + text_start = 0; + text_end = 0; + exec_data_start = 0; + exec_data_end = 0; + if (execchan >= 0) + close (execchan); + execchan = -1; + + /* Now open and digest the file the user requested, if any. */ + + if (filename) + { + execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, + &execfile); + if (execchan < 0) + perror_with_name (filename); + +#ifdef COFF_FORMAT + { + int aout_hdrsize; + int num_sections; + + if (read_file_hdr (execchan, &file_hdr) < 0) + error ("\"%s\": not in executable format.", execfile); + + aout_hdrsize = file_hdr.f_opthdr; + num_sections = file_hdr.f_nscns; + + if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0) + error ("\"%s\": can't read optional aouthdr", execfile); + + if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections) < 0) + error ("\"%s\": can't read text section header", execfile); + + if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections) < 0) + error ("\"%s\": can't read data section header", execfile); + + text_start = exec_aouthdr.text_start; + text_end = text_start + exec_aouthdr.tsize; + text_offset = text_hdr.s_scnptr; + exec_data_start = exec_aouthdr.data_start; + exec_data_end = exec_data_start + exec_aouthdr.dsize; + exec_data_offset = data_hdr.s_scnptr; + data_start = exec_data_start; + data_end += exec_data_start; + exec_mtime = file_hdr.f_timdat; + } +#else /* not COFF_FORMAT */ + { + struct stat st_exec; + +#ifdef HEADER_SEEK_FD + HEADER_SEEK_FD (execchan); +#endif + + val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); + + if (val < 0) + perror_with_name (filename); + + text_start = N_TXTADDR (exec_aouthdr); + exec_data_start = N_DATADDR (exec_aouthdr); + + text_offset = N_TXTOFF (exec_aouthdr); + exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; + + text_end = text_start + exec_aouthdr.a_text; + exec_data_end = exec_data_start + exec_aouthdr.a_data; + data_start = exec_data_start; + data_end += exec_data_start; + + fstat (execchan, &st_exec); + exec_mtime = st_exec.st_mtime; + } +#endif /* not COFF_FORMAT */ + + validate_files (); + } + else if (from_tty) + printf ("No exec file now.\n"); + + /* Tell display code (if any) about the changed file name. */ + if (exec_file_display_hook) + (*exec_file_display_hook) (filename); +} +@ + + +1.3 +log +@Remove A/UX-specific changes for shipping to FSF. +@ +text +@d30 13 +d176 7 +a182 1 + ptrace (6, inferior_pid, regaddr, read_register (regno)); +d193 6 +a198 1 + ptrace (6, inferior_pid, regaddr, read_register (regno)); +@ + + +1.2 +log +@A/UX and USG changes, and a little better error reporting. +@ +text +@a29 13 +#ifdef UNISOFT_ASSHOLES +#define PMMU +#define NEW_PMMU +#define mc68881 /* Needed to get float in user.h!!! */ +#include <sys/seg.h> /* For user.h */ +#include <sys/mmu.h> +#include <sys/time.h> +/* Things Unisoft defined differently from every other Unix system */ +#define NBPG PAGESIZE +#define UPAGES USIZE +#define KERNEL_U_ADDR UDOT +#endif + +d163 1 +a163 7 +#ifdef UNISOFT_ASSHOLES + /* You can't write the PC with ptrace 6, only with ptrace 11! */ + if (regno == PC_REGNUM) + ptrace(11, inferior_pid, 16, read_register(regno)); + else +#endif + ptrace (6, inferior_pid, regaddr, read_register (regno)); +d174 1 +a174 6 +#ifdef UNISOFT_ASSHOLES + if (regno == PC_REGNUM) + ptrace(11, inferior_pid, 16, read_register(regno)); + else +#endif + ptrace (6, inferior_pid, regaddr, read_register (regno)); +@ + + +1.1 +log +@Initial revision +@ +text +@d30 13 +a46 1 +#include <sys/user.h> +d58 2 +d67 5 +a71 1 + machine-dependent file. */ +d76 11 +d88 1 +d176 7 +a182 1 + ptrace (6, inferior_pid, regaddr, read_register (regno)); +d193 6 +a198 1 + ptrace (6, inferior_pid, regaddr, read_register (regno)); +d201 1 +a201 1 + sprintf (buf, "writing register number %d", regno); +d446 3 +a448 1 + perror_with_name (filename); +d455 6 +a460 1 + reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR; +d478 1 +a478 1 + perror_with_name (filename); +d482 1 +a482 1 + perror_with_name (filename); +@ diff --git a/gdb/RCS/findvar.c,v b/gdb/RCS/findvar.c,v new file mode 100644 index 0000000..9095f2b --- /dev/null +++ b/gdb/RCS/findvar.c,v @@ -0,0 +1,584 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.04.26.01.06.46; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.04.25.15.38.48; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@use XXX_BIG_ENDIAN macros rather than runtime tests. +@ +text +@/* Find a variable's value in memory, for GDB, the GNU debugger. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "value.h" + +CORE_ADDR read_register (); + +/* Return the address in which frame FRAME's value of register REGNUM + has been saved in memory. Or return zero if it has not been saved. + If REGNUM specifies the SP, the value we return is actually + the SP value, not an address where it was saved. */ + +CORE_ADDR +find_saved_register (frame, regnum) + FRAME frame; + int regnum; +{ + struct frame_info *fi; + struct frame_saved_regs saved_regs; + + register FRAME frame1 = 0; + register CORE_ADDR addr = 0; + +#ifdef HAVE_REGISTER_WINDOWS + /* We assume that a register in a register window will only be saved + in one place (since the name changes and dissapears as you go + towards inner frames), so we only call get_frame_saved_regs on + the current frame. This is directly in contradiction to the + usage below, which assumes that registers used in a frame must be + saved in a lower (more interior) frame. This change is a result + of working on a register window machine; get_frame_saved_regs + always returns the registers saved within a frame, within the + context (register namespace) of that frame. */ + + if (REGISTER_IN_WINDOW_P(regnum)) + { + fi = get_frame_info (frame); + get_frame_saved_regs (fi, &saved_regs); + return (saved_regs.regs[regnum] ? + saved_regs.regs[regnum] : 0); + } +#endif /* HAVE_REGISTER_WINDOWS */ + + /* Note that this next routine assumes that registers used in + frame x will be saved only in the frame that x calls and + frames interior to it. This is not true on the sparc, but the + above macro takes care of it, so we should be all right. */ + while (1) + { + QUIT; + frame1 = get_prev_frame (frame1); + if (frame1 == 0 || frame1 == frame) + break; + fi = get_frame_info (frame1); + get_frame_saved_regs (fi, &saved_regs); + if (saved_regs.regs[regnum]) + addr = saved_regs.regs[regnum]; + } + + return addr; +} + +/* Copy the bytes of register REGNUM, relative to the current stack frame, + into our memory at MYADDR. + The number of bytes copied is REGISTER_RAW_SIZE (REGNUM). */ + +void +read_relative_register_raw_bytes (regnum, myaddr) + int regnum; + char *myaddr; +{ + register CORE_ADDR addr; + + if (regnum == FP_REGNUM) + { + bcopy (&FRAME_FP(selected_frame), myaddr, sizeof (CORE_ADDR)); + return; + } + + addr = find_saved_register (selected_frame, regnum); + + if (addr) + { + if (regnum == SP_REGNUM) + { + CORE_ADDR buffer = addr; + bcopy (&buffer, myaddr, sizeof (CORE_ADDR)); + } + else + read_memory (addr, myaddr, REGISTER_RAW_SIZE (regnum)); + return; + } + read_register_bytes (REGISTER_BYTE (regnum), + myaddr, REGISTER_RAW_SIZE (regnum)); +} + +/* Return a `value' with the contents of register REGNUM + in its virtual format, with the type specified by + REGISTER_VIRTUAL_TYPE. */ + +value +value_of_register (regnum) + int regnum; +{ + register CORE_ADDR addr; + register value val; + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + + if (! (have_inferior_p () || have_core_file_p ())) + error ("Can't get value of register without inferior or core file"); + + addr = find_saved_register (selected_frame, regnum); + if (addr) + { + if (regnum == SP_REGNUM) + return value_from_long (builtin_type_int, (LONGEST) addr); + read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum)); + } + else + read_register_bytes (REGISTER_BYTE (regnum), raw_buffer, + REGISTER_RAW_SIZE (regnum)); + + REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer); + val = allocate_value (REGISTER_VIRTUAL_TYPE (regnum)); + bcopy (virtual_buffer, VALUE_CONTENTS (val), REGISTER_VIRTUAL_SIZE (regnum)); + VALUE_LVAL (val) = addr ? lval_memory : lval_register; + VALUE_ADDRESS (val) = addr ? addr : REGISTER_BYTE (regnum); + VALUE_REGNO (val) = regnum; + return val; +} + +/* Low level examining and depositing of registers. + + Note that you must call `fetch_registers' once + before examining or depositing any registers. */ + +char registers[REGISTER_BYTES]; + +/* Copy LEN bytes of consecutive data from registers + starting with the REGBYTE'th byte of register data + into memory at MYADDR. */ + +void +read_register_bytes (regbyte, myaddr, len) + int regbyte; + char *myaddr; + int len; +{ + bcopy (®isters[regbyte], myaddr, len); +} + +/* Copy LEN bytes of consecutive data from memory at MYADDR + into registers starting with the REGBYTE'th byte of register data. */ + +void +write_register_bytes (regbyte, myaddr, len) + int regbyte; + char *myaddr; + int len; +{ + bcopy (myaddr, ®isters[regbyte], len); + if (have_inferior_p ()) + store_inferior_registers (-1); +} + +/* Return the contents of register REGNO, + regarding it as an integer. */ + +CORE_ADDR +read_register (regno) + int regno; +{ + /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */ + return *(int *) ®isters[REGISTER_BYTE (regno)]; +} + +/* Store VALUE in the register number REGNO, regarded as an integer. */ + +void +write_register (regno, val) + int regno, val; +{ + /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */ +#if defined(sun4) + /* This is a no-op on a Sun 4. */ + if (regno == 0) + return; +#endif + + *(int *) ®isters[REGISTER_BYTE (regno)] = val; + + if (have_inferior_p ()) + store_inferior_registers (regno); +} + +/* Record that register REGNO contains VAL. + This is used when the value is obtained from the inferior or core dump, + so there is no need to store the value there. */ + +void +supply_register (regno, val) + int regno; + char *val; +{ + bcopy (val, ®isters[REGISTER_BYTE (regno)], REGISTER_RAW_SIZE (regno)); +} + +/* Given a struct symbol for a variable, + and a stack frame address, read the value of the variable + and return a (pointer to a) struct value containing the value. */ + +value +read_var_value (var, frame) + register struct symbol *var; + FRAME frame; +{ + register value v; + + struct frame_info *fi; + + struct type *type = SYMBOL_TYPE (var); + register CORE_ADDR addr = 0; + int val = SYMBOL_VALUE (var); + register int len; + + v = allocate_value (type); + VALUE_LVAL (v) = lval_memory; /* The most likely possibility. */ + len = TYPE_LENGTH (type); + + if (frame == 0) frame = selected_frame; + + switch (SYMBOL_CLASS (var)) + { + case LOC_CONST: + case LOC_LABEL: + bcopy (&val, VALUE_CONTENTS (v), len); + VALUE_LVAL (v) = not_lval; + return v; + + case LOC_CONST_BYTES: + bcopy (val, VALUE_CONTENTS (v), len); + VALUE_LVAL (v) = not_lval; + return v; + + case LOC_STATIC: + addr = val; + break; + + case LOC_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + break; + + case LOC_LOCAL: + fi = get_frame_info (frame); + addr = val + FRAME_LOCALS_ADDRESS (fi); + break; + + case LOC_TYPEDEF: + error ("Cannot look up value of a typedef"); + + case LOC_BLOCK: + VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (var)); + return v; + + case LOC_REGISTER: + case LOC_REGPARM: + v = value_from_register (type, val, frame); + return v; + } + + read_memory (addr, VALUE_CONTENTS (v), len); + VALUE_ADDRESS (v) = addr; + return v; +} + +/* Return a value of type TYPE, stored in register REGNUM, in frame + FRAME. */ + +value +value_from_register (type, regnum, frame) + struct type *type; + int regnum; + FRAME frame; +{ + char raw_buffer [MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + CORE_ADDR addr; + value v = allocate_value (type); + int len = TYPE_LENGTH (type); + char *value_bytes = 0; + int value_bytes_copied = 0; + int num_storage_locs; + + VALUE_REGNO (v) = regnum; + + num_storage_locs = (len > REGISTER_VIRTUAL_SIZE (regnum) ? + ((len - 1) / REGISTER_RAW_SIZE (regnum)) + 1 : + 1); + + if (num_storage_locs > 1) + { + /* Value spread across multiple storage locations. */ + + int local_regnum; + int mem_stor = 0, reg_stor = 0; + int mem_tracking = 1; + CORE_ADDR last_addr = 0; + + value_bytes = (char *) alloca (len + MAX_REGISTER_RAW_SIZE); + + /* Copy all of the data out, whereever it may be. */ + + for (local_regnum = regnum; + value_bytes_copied < len; + (value_bytes_copied += REGISTER_RAW_SIZE (local_regnum), + ++local_regnum)) + { + int register_index = local_regnum - regnum; + addr = find_saved_register (frame, local_regnum); + if (addr == 0) + { + read_register_bytes (REGISTER_BYTE (local_regnum), + value_bytes + value_bytes_copied, + REGISTER_RAW_SIZE (local_regnum)); + reg_stor++; + } + else + { + read_memory (addr, value_bytes + value_bytes_copied, + REGISTER_RAW_SIZE (local_regnum)); + mem_stor++; + mem_tracking = + (mem_tracking + && (regnum == local_regnum + || addr == last_addr)); + } + last_addr = addr; + } + + if ((reg_stor && mem_stor) + || (mem_stor && !mem_tracking)) + /* Mixed storage; all of the hassle we just went through was + for some good purpose. */ + { + VALUE_LVAL (v) = lval_reg_frame_relative; + VALUE_FRAME (v) = FRAME_FP (frame); + VALUE_FRAME_REGNUM (v) = regnum; + } + else if (mem_stor) + { + VALUE_LVAL (v) = lval_memory; + VALUE_ADDRESS (v) = find_saved_register (frame, regnum); + } + else if (reg_stor) + { + VALUE_LVAL (v) = lval_register; + VALUE_ADDRESS (v) = REGISTER_BYTE (regnum); + } + else + fatal ("value_from_register: Value not stored anywhere!"); + + /* Any structure stored in more than one register will always be + an inegral number of registers. Otherwise, you'd need to do + some fiddling with the last register copied here for little + endian machines. */ + + /* Copy into the contents section of the value. */ + bcopy (value_bytes, VALUE_CONTENTS (v), len); + + return v; + } + + /* Data is completely contained within a single register. Locate the + register's contents in a real register or in core; + read the data in raw format. */ + + addr = find_saved_register (frame, regnum); + if (addr == 0) + { + /* Value is really in a register. */ + + VALUE_LVAL (v) = lval_register; + VALUE_ADDRESS (v) = REGISTER_BYTE (regnum); + + read_register_bytes (REGISTER_BYTE (regnum), + raw_buffer, REGISTER_RAW_SIZE (regnum)); + } + else + { + /* Value was in a register that has been saved in memory. */ + + read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum)); + VALUE_LVAL (v) = lval_memory; + VALUE_ADDRESS (v) = addr; + } + + /* Convert the raw contents to virtual contents. + (Just copy them if the formats are the same.) */ + + REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer); + + if (REGISTER_CONVERTIBLE (regnum)) + { + /* When the raw and virtual formats differ, the virtual format + corresponds to a specific data type. If we want that type, + copy the data into the value. + Otherwise, do a type-conversion. */ + + if (type != REGISTER_VIRTUAL_TYPE (regnum)) + { + /* eg a variable of type `float' in a 68881 register + with raw type `extended' and virtual type `double'. + Fetch it as a `double' and then convert to `float'. */ + v = allocate_value (REGISTER_VIRTUAL_TYPE (regnum)); + bcopy (virtual_buffer, VALUE_CONTENTS (v), len); + v = value_cast (type, v); + } + else + bcopy (virtual_buffer, VALUE_CONTENTS (v), len); + } + else + { + /* Raw and virtual formats are the same for this register. */ + +#ifdef BYTES_BIG_ENDIAN + if (len < REGISTER_RAW_SIZE (regnum)) + { + /* Big-endian, and we want less than full size. */ + VALUE_OFFSET (v) = REGISTER_RAW_SIZE (regnum) - len; + } +#endif + + bcopy (virtual_buffer + VALUE_OFFSET (v), + VALUE_CONTENTS (v), len); + } + + return v; +} + +/* Given a struct symbol for a variable, + and a stack frame address, + return a (pointer to a) struct value containing the variable's address. */ + +value +locate_var_value (var, frame) + register struct symbol *var; + FRAME frame; +{ + register CORE_ADDR addr = 0; + int val = SYMBOL_VALUE (var); + struct frame_info *fi; + struct type *type = SYMBOL_TYPE (var); + + if (frame == 0) frame = selected_frame; + + switch (SYMBOL_CLASS (var)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + error ("Address requested for identifier \"%s\" which is a constant.", + SYMBOL_NAME (var)); + + case LOC_REGISTER: + case LOC_REGPARM: + addr = find_saved_register (frame, val); + if (addr != 0) + { + int len = TYPE_LENGTH (type); +#ifdef BYTES_BIG_ENDIAN + if (len < REGISTER_RAW_SIZE (val)) + /* Big-endian, and we want less than full size. */ + addr += REGISTER_RAW_SIZE (val) - len; +#endif + break; + } + error ("Address requested for identifier \"%s\" which is in a register.", + SYMBOL_NAME (var)); + + case LOC_STATIC: + case LOC_LABEL: + addr = val; + break; + + case LOC_ARG: + fi = get_frame_info (frame); + addr = val + FRAME_ARGS_ADDRESS (fi); + break; + + case LOC_LOCAL: + fi = get_frame_info (frame); + addr = val + FRAME_LOCALS_ADDRESS (fi); + break; + + case LOC_TYPEDEF: + error ("Address requested for identifier \"%s\" which is a typedef.", + SYMBOL_NAME (var)); + + case LOC_BLOCK: + addr = BLOCK_START (SYMBOL_BLOCK_VALUE (var)); + break; + } + + return value_cast (lookup_pointer_type (type), + value_from_long (builtin_type_long, (LONGEST) addr)); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@d448 2 +a449 5 + union { int i; char c; } test; + /* If we want less than the full size, we need to + test for a big-endian or little-endian machine. */ + test.i = 1; + if (test.c != 1 && len < REGISTER_RAW_SIZE (regnum)) +d454 2 +a455 1 + +a490 1 + union { int i; char c; } test; +d492 2 +a493 4 + /* If var is less than the full size of register, we need to + test for a big-endian or little-endian machine. */ + test.i = 1; + if (test.c != 1 && len < REGISTER_RAW_SIZE (val)) +d496 1 +@ diff --git a/gdb/RCS/gdb.texinfo,v b/gdb/RCS/gdb.texinfo,v new file mode 100644 index 0000000..ff38f18 --- /dev/null +++ b/gdb/RCS/gdb.texinfo,v @@ -0,0 +1,3009 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @@; + + +1.2 +date 89.02.10.01.41.38; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.10.00.33.03; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Improve doc in various ways, mostly xref{Expressions} so you can +find out what an expression is (I had trouble finding it, since +it's in a nested menu somewhere.) +@ +text +@\input texinfo +@@setfilename ../info/gdb +@@settitle GDB, The GNU Debugger +@@ifinfo +This file documents the GNU debugger GDB. + +Copyright (C) 1988 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +sections entitled ``Distribution'' and ``GDB General Public License'' are +included exactly as in the original, and provided that the entire resulting +derived work is distributed under the terms of a permission notice +identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the sections entitled ``Distribution'' and ``GDB General Public +License'' may be included in a translation approved by the author instead +of in the original English. +@@end ifinfo + +@@setchapternewpage odd +@@settitle GDB Manual +@@titlepage +@@sp 6 +@@center @@titlefont{GDB Manual} +@@sp 1 +@@center The GNU Source-Level Debugger +@@sp 4 +@@center Third Edition, GDB version 3.1 +@@sp 1 +@@center January 1989 +@@sp 5 +@@center Richard M. Stallman +@@page +@@vskip 0pt plus 1filll +Copyright @@copyright{} 1988, 1989 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +sections entitled ``Distribution'' and ``GDB General Public License'' are +included exactly as in the original, and provided that the entire resulting +derived work is distributed under the terms of a permission notice +identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the sections entitled ``Distribution'' and ``GDB General Public +License'' may be included in a translation approved by the author instead +of in the original English. +@@end titlepage +@@page + +@@node Top, Commands,, (DIR) +@@unnumbered Summary of GDB + +The purpose of a debugger such as GDB is to allow you to execute another +program while examining what is going on inside it. We call the other +program ``your program'' or ``the program being debugged''. + +GDB can do four kinds of things (plus other things in support of these): + +@@enumerate +@@item +Start the program, specifying anything that might affect its behavior. + +@@item +Make the program stop on specified conditions. + +@@item +Examine what has happened, when the program has stopped, so that you +can see bugs happen. + +@@item +Change things in the program, so you can correct the effects of one bug +and go on to learn about another without having to recompile first. +@@end enumerate + +GDB can be used to debug programs written in C and C++. Pascal support +is being implemented, and Fortran support will be added when a GNU +Fortran compiler is written. + +@@menu +* License:: The GDB General Public License gives you permission + to redistribute GDB on certain terms; and also + explains that there is no warranty. +* Input:: GDB command syntax and input conventions. +* Files:: Specifying files for GDB to operate on. +* Options:: GDB arguments and options. +* Compilation::Compiling your program so you can debug it. +* Running:: Running your program under GDB. +* Stopping:: Making your program stop. Why it may stop. What to do then. +* Stack:: Examining your program's stack. +* Source:: Examining your program's source files. +* Data:: Examining data in your program. +* Symbols:: Examining the debugger's symbol table. +* Altering:: Altering things in your program. +* Sequences:: Canned command sequences for repeated use. +* Emacs:: Using GDB through GNU Emacs. +* Remote:: Remote kernel debugging across a serial line. +* Commands:: Index of GDB commands. +* Concepts:: Index of GDB concepts. +@@end menu + +@@node License, Input, Top, Top +@@unnumbered GDB General Public License +@@center (Clarified 11 Feb 1988) + + The license agreements of most software companies keep you at the mercy +of those companies. By contrast, our general public license is intended to +give everyone the right to share GDB. To make sure that you get the rights +we want you to have, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. Hence this +license agreement. + + Specifically, we want to make sure that you have the right to give away +copies of GDB, that you receive source code or else can get it if you want +it, that you can change GDB or use pieces of it in new free programs, and +that you know you can do these things. + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute copies +of GDB, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must tell them their rights. + + Also, for our own protection, we must make certain that everyone finds +out that there is no warranty for GDB. If GDB is modified by someone else +and passed on, we want its recipients to know that what they have is not +what we distributed, so that any problems introduced by others will not +reflect on our reputation. + + Therefore we (Richard Stallman and the Free Software Foundation, +Inc.) make the following terms which say what you must do to be +allowed to distribute or change GDB. + +@@unnumberedsec Copying Policies + +@@enumerate +@@item +You may copy and distribute verbatim copies of GDB source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each file a valid copyright notice ``Copyright +@@copyright{} 1988 Free Software Foundation, Inc.'' (or with whatever year +is appropriate); keep intact the notices on all files that +refer to this License Agreement and to the absence of any warranty; and +give any other recipients of the GDB program a copy of this License +Agreement along with the program. You may charge a distribution fee +for the physical act of transferring a copy. + +@@item +You may modify your copy or copies of GDB source code or any portion +of it, and copy and distribute such modifications under the terms of +Paragraph 1 above, provided that you also do the following: + +@@itemize @@bullet +@@item +cause the modified files to carry prominent notices stating +that you changed the files and the date of any change; and + +@@item +cause the whole of any work that you distribute or publish, that +in whole or in part contains or is a derivative of GDB or any +part thereof, to be licensed at no charge to all third parties on +terms identical to those contained in this License Agreement +(except that you may choose to grant more extensive warranty +protection to some or all third parties, at your option). + +@@item +if the modified program serves as a debugger, cause it, when +started running in the simplest and usual way, to print an +announcement including a valid copyright notice ``Copyright +@@copyright{} 1988 Free Software Foundation, Inc.'' (or with the +year that is appropriate), saying that there is no warranty (or +else, saying that you provide a warranty) and that users may +redistribute the program under these conditions, and telling the +user how to view a copy of this License Agreement. + +@@item +You may charge a distribution fee for the physical act of +transferring a copy, and you may at your option offer warranty +protection in exchange for a fee. +@@end itemize + +Mere aggregation of another unrelated program with this program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other program under the scope of these terms. + +@@item +You may copy and distribute GDB (or a portion or derivative of it, +under Paragraph 2) in object code or executable form under the terms +of Paragraphs 1 and 2 above provided that you also do one of the +following: + +@@itemize @@bullet +@@item +accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of +Paragraphs 1 and 2 above; or, + +@@item +accompany it with a written offer, valid for at least three +years, to give any third party free (except for a nominal +shipping charge) a complete machine-readable copy of the +corresponding source code, to be distributed under the terms of +Paragraphs 1 and 2 above; or, + +@@item +accompany it with the information you received as to where the +corresponding source code may be obtained. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form alone.) +@@end itemize + +For an executable file, complete source code means all the source code +for all modules it contains; but, as a special exception, it need not +include source code for modules which are standard libraries that +accompany the operating system on which the executable file runs. + +@@item +You may not copy, sublicense, distribute or transfer GDB except as +expressly provided under this License Agreement. Any attempt +otherwise to copy, sublicense, distribute or transfer GDB is void and +your rights to use GDB under this License agreement shall be +automatically terminated. However, parties who have received computer +software programs from you with this License Agreement will not have +their licenses terminated so long as such parties remain in full +compliance. + +@@item +If you wish to incorporate parts of GDB into other free programs whose +distribution conditions are different, write to the Free Software +Foundation. We have not yet worked out a simple rule that can be +stated here, but we will often permit this. We will be guided by the +two goals of preserving the free status of all derivatives our free +software and of promoting the sharing and reuse of software. +@@end enumerate + +@@iftex +@@vfil +@@eject +@@end iftex +@@unnumberedsec NO WARRANTY + + BECAUSE GDB IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY +NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT +WHEN OTHERWISE STATED IN WRITING, THE FREE SOFTWARE FOUNDATION, INC, +RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE GDB ``AS IS'' +WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY +AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE GDB +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY +SERVICING, REPAIR OR CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL FREE SOFTWARE +FOUNDATION, INC., RICHARD M. STALLMAN, AND/OR ANY OTHER PARTY WHO MAY +MODIFY AND REDISTRIBUTE GDB AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY +FREE SOFTWARE FOUNDATION, INC.) THE PROGRAM, EVEN IF YOU HAVE BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY +OTHER PARTY. + +@@node Input, Files, License, Top +@@chapter GDB Input Conventions + +GDB is invoked with the shell command @@samp{gdb}. Once started, it reads +commands from the terminal until you tell it to exit. + +A GDB command is a single line of input. There is no limit on how long +it can be. It starts with a command name, which is followed by arguments +whose meaning depends on the command name. Some command names do not +allow arguments. + +GDB command names may always be abbreviated if the abbreviation is +unambiguous. Sometimes even ambiguous abbreviations are allowed; for +example, @@samp{s} is specially defined as equivalent to @@samp{step} +even though there are other commands whose names start with @@samp{s}. +Possible command abbreviations are often stated in the documentation +of the individual commands. + +A blank line as input to GDB means to repeat the previous command verbatim. +Certain commands do not allow themselves to be repeated this way; these are +commands for which unintentional repetition might cause trouble and which +you are unlikely to want to repeat. Certain others (@@samp{list} and +@@samp{x}) act differently when repeated because that is more useful. + +A line of input starting with @@samp{#} is a comment; it does nothing. +This is useful mainly in command files (@@xref{Command Files}). + +Occasionally it is useful to execute a shell command from within gdb. +This can be done with the @@samp{shell} command, or the shell escape +character @@samp{!}. + +@@table @@code +@@item shell @@var{shell command string} +@@kindex shell +@@item !@@var{shell command string} +@@kindex ! +@@cindex shell escape +Directs GDB to invoke an inferior shell to execute @@samp{shell command string}. +The environmental variable @@samp{SHELL} is used if it exists, otherwise gdb +uses @@samp{/bin/sh}. +@@end table + +GDB @@dfn{prompts} for commands with a string that is normally @@samp{(gdb)}. +When debugging GDB with GDB, it is useful to change the prompt in one of +the GDBs so that you can distinguish them. This can be done with the +@@samp{set prompt} command. + +@@table @@code +@@item set prompt @@var{newprompt} +@@kindex set prompt +Directs GDB to use @@var{newprompt} as its prompt string henceforth. +@@end table + +@@cindex exiting GDB +@@kindex quit +To exit GDB, use the @@samp{quit} command (abbreviated @@samp{q}). +@@kbd{Ctrl-c} will not exit from GDB, but rather will terminate the action +of any GDB command that is in progress and return to GDB command level. +It is safe to type @@kbd{Ctrl-c} at any time because GDB does not allow +it to take effect until a time when it is safe. + +@@node Files, Options, Input, Top +@@chapter Specifying GDB's Files + +@@cindex core dump file +@@cindex executable file +@@cindex symbol table +GDB needs to know the filename of the program to be debugged. To debug a +core dump of a previous run, GDB must be told the filename of the core +dump. + +@@menu +* Arguments: File Arguments. Specifying files with arguments + (when you start GDB). +* Commands: File Commands. Specifying files with GDB commands. +@@end menu + +@@node File Arguments, File Commands, Files, Files +@@section Specifying Files with Arguments + +The usual way to specify the executable and core dump file names is with +two command arguments given when you start GDB. The first argument is used +as the file for execution and symbols, and the second argument (if any) is +used as the core dump file name. Thus, + +@@example +gdb progm core +@@end example + +@@noindent +specifies @@file{progm} as the executable program and @@file{core} as a core +dump file to examine. (You do not need to have a core dump file if what +you plan to do is debug the program interactively.) + +@@xref{Options}, for full information on command options and arguments for +GDB. + +@@node File Commands,, File Arguments, Files +@@section Specifying Files with Commands + +Usually you specify the files for GDB to work with by giving arguments when +you invoke GDB. But occasionally it is necessary to change to a different +file during a GDB session. Or you may run GDB and forget to specify the +files you want to use. In these situations the GDB commands to specify new +files are useful. + +@@table @@code +@@item exec-file @@var{filename} +@@kindex exec-file +Specify that the program to be run is found in @@var{filename}. If you +do not specify a directory and the file is not found in GDB's working +directory, GDB will use the environment variable @@samp{PATH} as a list +of directories to search, just as the shell does when looking for a +program to run. + +@@item symbol-file @@var{filename} +@@kindex symbol-file +Read symbol table information from file @@var{filename}. @@samp{PATH} +is searched when necessary. Most of the time you will use both the +@@samp{exec-file} and @@samp{symbol-file} commands on the same file. + +@@samp{symbol-file} with no argument clears out GDB's symbol table. + +@@item core-file @@var{filename} +@@kindex core-file +Specify the whereabouts of a core dump file to be used as the +``contents of memory''. Note that the core dump contains only the +writable parts of memory; the read-only parts must come from the +executable file. + +@@samp{core-file} with no argument specifies that no core file is +to be used. + +@@item add-file @@var{filename} @@var{address} +@@kindex add-file +The @@samp{add-file} command takes two arguments, a file name, and the +address at which that file has been (or should be) dynamically loaded. +GDB will then treat that file as though it had always been dynamically +linked, and provide the user with all the normal GDB features, including +symbolic debugging. + +With the @@samp{add-file} command, it is possible to debug code which was +not present in the initial load image of the program under test. +Suppose you have a program which can, while running, dynamically link a +program fragment into its address space. One program which does this is +KCL, a free common lisp implementation. The fragment will be loaded +into the main program's address space at some address, and the main +program can then call functions within the fragment by calculating (or +otherwise obtaining) their addresses. + +@@item kill +@@kindex kill +Cancel running the program under GDB. This could be used if you wish +to debug a core dump instead. GDB ignores any core dump file if it is +actually running the program, so the @@samp{kill} command is the only +sure way to go back to using the core dump file. + +@@item info files +@@kindex info files +Print the names of the executable and core dump files currently in +use by GDB, and the file from which symbols were loaded. +@@end table + +While all three file-specifying commands allow both absolute and relative +file names as arguments, GDB always converts the file name to an absolute +one and remembers it that way. + +The @@samp{symbol-file} command causes GDB to forget the contents of its +convenience variables, the value history, and all breakpoints and +auto-display expressions. This is because they may contain pointers to the +internal data recording symbols and data types, which are part of the old +symbol table data being discarded inside GDB. + +@@node Options, Compilation, Files, Top +@@chapter Options and Arguments for GDB + +When you invoke GDB, you can pass commands telling it what files to +operate on and what other things to do. + +@@menu +* Mode Options:: Options controlling modes of operation. +* File Options:: Options to specify files (executable, coredump, commands) +* Other Arguments:: Any other arguments without options + also specify files. +@@end menu + +@@node Mode Options, File Options, Options, Options +@@section Mode Options + +@@table @@samp +@@item -nx +Do not execute commands from the init files @@file{.gdbinit}. +Normally, the commands in these files are executed after all the +command options and arguments have been processed. @@xref{Command +Files}. + +@@item -q +``Quiet''. Do not print the usual introductory messages. + +@@item -batch +Run in batch mode. Exit with code 1 after processing all the command +files specified with @@samp{-x} (and @@file{.gdbinit}, if not +inhibited). Exit also if, due to an error, GDB would otherwise +attempt to read a command from the terminal. + +@@item -fullname +This option is used when Emacs runs GDB as a subprocess. It tells GDB +to output the full file name and line number in a standard, +recognizable fashion each time a stack frame is displayed (which +includes each time the program stops). This recognizable format looks +like two @@samp{\032} characters, followed by the filename, line number +and character position separated by colons, and a newline. The +Emacs-to-GDB interface program uses the two @@samp{\032} characters as +a signal to display the source code for the frame. +@@end table + +@@node File Options, Other Arguments, Mode Options, Options +@@section File-specifying Options + +All the options and command line arguments given are processed +in sequential order. The order makes a difference when the +@@samp{-x} command is used. + +@@table @@samp +@@item -s @@var{file} +Read symbol table from file @@var{file}. + +@@item -e @@var{file} +Use file @@var{file} as the executable file to execute when +appropriate, and for examining pure data in conjunction with a core +dump. + +@@item -se @@var{file} +Read symbol table from file @@var{file} and use it as the executable +file. + +@@item -c @@var{file} +Use file @@var{file} as a core dump to examine. + +@@item -x @@var{file} +Execute GDB commands from file @@var{file}. + +@@item -d @@var{directory} +Add @@var{directory} to the path to search for source files. +@@end table + +@@node Other Arguments,, File Options, Options +@@section Other Arguments + +If there are arguments to GDB that are not options or associated with +options, the first one specifies the symbol table and executable file name +(as if it were preceded by @@samp{-se}) and the second one specifies a core +dump file name (as if it were preceded by @@samp{-c}). + +@@node Compilation, Running, Options, Top +@@chapter Compiling Your Program for Debugging + +In order to debug a program effectively, you need to ask for debugging +information when you compile it. This information in the object file +describes the data type of each variable or function and the correspondence +between source line numbers and addresses in the executable code. + +To request debugging information, specify the @@samp{-g} option when you run +the compiler. + +The Unix C compiler is unable to handle the @@samp{-g} and @@samp{-O} options +together. This means that you cannot ask for optimization if you ask for +debugger information. + +The GNU C compiler supports @@samp{-g} with or without @@samp{-O}, making it +possible to debug optimized code. We recommend that you @@emph{always} use +@@samp{-g} whenever you compile a program. You may think the program is +correct, but there's no sense in pushing your luck. + +If you are using the GNU C compiler, the GNU assembler and the GNU linker, +you can choose between two formats of debugging information: the standard +Unix format, which is what you get with @@samp{-g}, and GDB's own format, +which you request by using @@samp{-gg} instead of @@samp{-g}. This stores +debugging information in the executable file in a format much like that +which is used inside GDB. This has these advantages and disadvantages: + +@@itemize @@bullet +@@item +GDB can read @@samp{-gg} format more than twice as fast as Unix +@@samp{-g} format. + +@@item +The @@samp{-gg} format uses much more disk space than Unix format. + +@@item +The Unix debuggers can understand only Unix format, so you cannot use +Unix source-level debuggers if you compile with @@samp{-gg}. (The +@@code{adb} debugger works with either format; it does not use this +information in any case.) +@@end itemize + +@@node Running, Stopping, Compilation, Top +@@chapter Running Your Program Under GDB + +@@cindex running +@@kindex run +To start your program under GDB, use the @@samp{run} command. The program +must already have been specified using the @@samp{exec-file} command or with +an argument to GDB (@@pxref{Files}); what @@samp{run} does is create an +inferior process, load the program into it, and set it in motion. + +The execution of a program is affected by certain information it receives +from its superior. GDB provides ways to specify them, which you must do +@@i{before} starting the program. (You can change them after starting the +program, but such changes do not affect the program unless you start it +over again.) + +@@table @@asis +@@item The @@i{arguments.} +You specify the arguments to give the program as the arguments of the +@@samp{run} command. + +@@item The @@i{environment.} +The program normally inherits its environment from GDB, but you can +use the GDB commands @@samp{set environment} and +@@samp{unset environment} to change parts of the environment that will +be given to the program.@@refill + +@@item The @@i{working directory.} +The program inherits its working directory from GDB. You can set GDB's +working directory with the @@samp{cd} command in GDB. +@@end table + +After the @@samp{run} command, the debugger does nothing but wait for your +program to stop. @@xref{Stopping}. + +Note that once your program has been started by the @@samp{run} command, +you may evaluate expressions that involve calls to functions in the +inferior. @@xref{Expressions}. If you wish to evaluate a function +simply for it's side affects, you may use the @@samp{set} command. +@@xref{Assignment}. + +@@menu +* Arguments:: Specifying the arguments for your program. +* Environment:: Specifying the environment for your program. +* Working Directory:: Specifying the working directory for giving + to your program when it is run. +* Input/Output:: Specifying the program's standard input and output. +* Attach:: Debugging a process started outside GDB. +@@end menu + +@@node Arguments, Environment, Running, Running +@@section Your Program's Arguments + +@@cindex arguments (to your program) +You specify the arguments to give the program as the arguments of the +@@samp{run} command. They are passed to a shell, which expands wildcard +characters and performs redirection of I/O, and thence to the program. + +@@samp{run} with no arguments uses the same arguments used by the previous +@@samp{run}. + +@@kindex set args +The command @@samp{set args} can be used to specify the arguments to be used +the next time the program is run. If @@samp{set args} has no arguments, it +means to use no arguments the next time the program is run. If you have +run your program with arguments and want to run it again with no arguments, +this is the only way to do so. + +@@node Environment, Working Directory, Arguments, Running +@@section Your Program's Environment + +@@cindex environment (of your program) +The @@dfn{environment} consists of a set of @@dfn{environment variables} and +their values. Environment variables conventionally record such things as +your user name, your home directory, your terminal type, and your search +path for programs to run. Usually you set up environment variables with +the shell and they are inherited by all the other programs you run. When +debugging, it can be useful to try running the program with different +environments without having to start the debugger over again. + +@@table @@code +@@item info environment @@var{varname} +@@kindex info environment +Print the value of environment variable @@var{varname} to be given to +your program when it is started. This command can be abbreviated +@@samp{i env @@var{varname}}. + +@@item info environment +Print the names and values of all environment variables to be given to +your program when it is started. This command can be abbreviated +@@samp{i env}. + +@@item set environment @@var{varname} @@var{value} +@@item set environment @@var{varname} = @@var{value} +@@kindex set environment +Sets environment variable @@var{varname} to @@var{value}, for your program +only, not for GDB itself. @@var{value} may be any string; the values of +environment variables are just strings, and any interpretation is +supplied by your program itself. The @@var{value} parameter is optional; +if it is eliminated, the variable is set to a null value. This command +can be abbreviated as short as @@samp{set e}. + +@@item delete environment @@var{varname} +@@kindex delete environment +@@item unset environment @@var{varname} +@@kindex unset environment +Remove variable @@var{varname} from the environment to be passed to +your program. This is different from @@samp{set env @@var{varname} =} +because @@samp{delete environment} makes a variable not be defined at +all, which is distinguishable from an empty value. This command can +be abbreviated @@samp{d e}. +@@end table + +@@node Working Directory, Input/Output, Environment, Running +@@section Your Program's Working Directory + +@@cindex working directory (of your program) +Each time you start your program with @@samp{run}, it inherits its working +directory from the current working directory of GDB. GDB's working +directory is initially whatever it inherited from its superior, but you can +specify the working directory for GDB with the @@samp{cd} command. + +The GDB working directory also serves as a default for the commands +that specify files for GDB to operate on. @@xref{Files}. + +@@table @@code +@@item cd @@var{directory} +@@kindex cd +Set GDB's working directory to @@var{directory}. + +@@item pwd +@@kindex pwd +Print GDB's working directory. +@@end table + +@@node Input/Output, Attach, Working Directory, Running +@@section Your Program's Input and Output + +@@cindex redirection +@@cindex controlling terminal +By default, the program you run under GDB does input and output to the same +terminal that GDB uses. + +You can redirect the program's input and/or output using @@samp{sh}-style +redirection commands in the @@samp{run} command. For example, + +@@example +run > outfile +@@end example + +@@noindent +starts the program, diverting its output to the file @@file{outfile}. + +@@kindex tty +Another way to specify where the program should do input and output is with +the @@samp{tty} command. This command accepts a file name as argument, and +causes this file to be the default for future @@samp{run} commands. It also +resets the controlling terminal for future @@samp{run} commands. For +example, + +@@example +tty /dev/ttyb +@@end example + +@@noindent +directs that processes started with subsequent @@samp{run} commands default +to do input and output on the terminal @@file{/dev/ttyb} and sets the +controlling terminal to @@file{/dev/ttyb}. An explicit redirection in +@@samp{run} overrides the @@samp{tty} command's effect on input/output +redirection. + +When you use the @@samp{tty} command or redirect input in the @@samp{run} +command, the @@emph{input for your program} comes from the specified file, +but the input for GDB still comes from your terminal. + +@@node Attach,, Input/Output, Running +@@section Debugging an Already-Running Process +@@kindex detach +@@kindex attach +@@cindex attach + +Some operating systems (in particular, Sun) allow GDB to begin debugging an +already-running process that was started outside of GDB. To do this you +must use the @@samp{attach} command instead of the @@samp{run} command. + +The @@samp{attach} command requires one argument, which is the process-id of +the process you want to debug. (The usual way to find out the process-id +of the process is with the @@samp{ps} utility.) + +The first thing GDB does after arranging to debug the process is to stop +it. You can examine and modify an attached process with all the GDB +commands that ordinarily available when you start processes with +@@samp{run}. You can insert breakpoints; you can step and continue; you +can modify storage. If you would rather the process continue running, +use the @@samp{continue} command after attaching. + +When you are finished debugging the attached process, you can use the +@@samp{detach} command to release it from GDB's control. Detaching +the process continues its execution. After the @@samp{detach} command, +that process and GDB become completely independent once more, and you +are ready to @@samp{attach} another process or start one with @@samp{run}. + +If you exit GDB or use the @@samp{run} command while you have an attached +process, you kill that process. You will be asked for confirmation if you +try to do either of these things. + +@@node Stopping, Stack, Running, Top +@@chapter Stopping and Continuing + +When you run a program normally, it runs until exiting. The purpose +of using a debugger is so that you can stop it before that point; +or so that if the program runs into trouble you can find out why. + +@@menu +* Signals:: Fatal signals in your program just stop it; + then you can use GDB to see what is going on. +* Breakpoints:: Breakpoints let you stop your program when it + reaches a specified point in the code. +* Continuing:: Resuming execution until the next signal or breakpoint. +* Stepping:: Stepping runs the program a short distance and + then stops it wherever it has come to. +@@end menu + +@@node Signals, Breakpoints, Stopping, Stopping +@@section Signals + +A signal is an asynchronous event that can happen in a program. The +operating system defines the possible kinds of signals, and gives each kind +a name and a number. For example, @@code{SIGINT} is the signal a program +gets when you type @@kbd{Ctrl-c}; @@code{SIGSEGV} is the signal a program +gets from referencing a place in memory far away from all the areas in use; +@@code{SIGALRM} occurs when the alarm clock timer goes off (which happens +only if the program has requested an alarm). + +Some signals, including @@code{SIGALRM}, are a normal part of the +functioning of the program. Others, such as @@code{SIGSEGV}, indicate +errors; these signals are @@dfn{fatal} (kill the program immediately) if the +program has not specified in advance some other way to handle the signal. +@@code{SIGINT} does not indicate an error in the program, but it is normally +fatal so it can carry out the purpose of @@kbd{Ctrl-c}: to kill the program. + +GDB has the ability to detect any occurrence of a signal in the program +running under GDB's control. You can tell GDB in advance what to do for +each kind of signal. + +Normally, GDB is set up to ignore non-erroneous signals like @@code{SIGALRM} +(so as not to interfere with their role in the functioning of the program) +but to stop the program immediately whenever an error signal happens. +You can change these settings with the @@samp{handle} command. You must +specify which signal you are talking about with its number. + +@@table @@code +@@item info signal +@@kindex info signal +Print a table of all the kinds of signals and how GDB has been told to +handle each one. You can use this to see the signal numbers of all +the defined types of signals. + +@@item handle @@var{signalnum} @@var{keywords}@@dots{} +@@kindex handle +Change the way GDB handles signal @@var{signalnum}. The @@var{keywords} +say what change to make. +@@end table + +To use the @@samp{handle} command you must know the code number of the +signal you are concerned with. To find the code number, type @@samp{info +signal} which prints a table of signal names and numbers. + +The keywords allowed by the handle command can be abbreviated. Their full +names are + +@@table @@code +@@item stop +GDB should stop the program when this signal happens. This implies +the @@samp{print} keyword as well. + +@@item print +GDB should print a message when this signal happens. + +@@item nostop +GDB should not stop the program when this signal happens. It may +still print a message telling you that the signal has come in. + +@@item noprint +GDB should not mention the occurrence of the signal at all. This +implies the @@samp{nostop} keyword as well. + +@@item pass +GDB should allow the program to see this signal; the program will be +able to handle the signal, or may be terminated if the signal is fatal +and not handled. + +@@item nopass +GDB should not allow the program to see this signal. +@@end table + +When a signal has been set to stop the program, the program cannot see the +signal until you continue. It will see the signal then, if @@samp{pass} is +in effect for the signal in question @@i{at that time}. In other words, +after GDB reports a signal, you can use the @@samp{handle} command with +@@samp{pass} or @@samp{nopass} to control whether that signal will be seen by +the program when you later continue it. + +You can also use the @@samp{signal} command to prevent the program from +seeing a signal, or cause it to see a signal it normally would not see, +or to give it any signal at any time. @@xref{Signaling}. + +@@node Breakpoints, Continuing, Signals, Stopping +@@section Breakpoints + +@@cindex breakpoints +A @@dfn{breakpoint} makes your program stop whenever a certain point in the +program is reached. You set breakpoints explicitly with GDB commands, +specifying the place where the program should stop by line number, function +name or exact address in the program. You can add various other conditions +to control whether the program will stop. + +Each breakpoint is assigned a number when it is created; these numbers are +successive integers starting with 1. In many of the commands for controlling +various features of breakpoints you use the breakpoint number to say which +breakpoint you want to change. Each breakpoint may be @@dfn{enabled} or +@@dfn{disabled}; if disabled, it has no effect on the program until you +enable it again. + +@@kindex info break +@@kindex $_ +The command @@samp{info break} prints a list of all breakpoints set and not +cleared, showing their numbers, where in the program they are, and any +special features in use for them. Disabled breakpoints are included in the +list, but marked as disabled. @@samp{info break} with a breakpoint number +as argument lists only that breakpoint. The convenience variable @@samp{$_} +and the default examining-address for the @@samp{x} command are set to the +address of the last breakpoint listed (@@pxref{Memory}). + +@@menu +* Set Breaks:: How to establish breakpoints. +* Clear Breaks:: How to remove breakpoints no longer needed. +* Disabling:: How to disable breakpoints (turn them off temporarily). +* Conditions:: Making extra conditions on whether to stop. +* Break Commands:: Commands to be executed at a breakpoint. +* Error in Breakpoints:: "Cannot insert breakpoints" error--why, what to do. +@@end menu + +@@node Set Breaks, Clear Breaks, Breakpoints, Breakpoints +@@subsection Setting Breakpoints + +@@kindex break +Breakpoints are set with the @@samp{break} command (abbreviated @@samp{b}). +You have several ways to say where the breakpoint should go. + +@@table @@code +@@item break @@var{function} +Set a breakpoint at entry to function @@var{function}. + +@@item break @@var{linenum} +Set a breakpoint at line @@var{linenum} in the current source file. +That file is the last file whose source text was printed. This +breakpoint will stop the program just before it executes any of the +code on that line. + +@@item break @@var{filename}:@@var{linenum} +Set a breakpoint at line @@var{linenum} in source file @@var{filename}. + +@@item break @@var{filename}:@@var{function} +Set a breakpoint at entry to function @@var{function} found in file +@@var{filename}. Specifying a filename as well as a function name is +superfluous except when multiple files contain similarly named +functions. + +@@item break *@@var{address} +Set a breakpoint at address @@var{address}. You can use this to set +breakpoints in parts of the program which do not have debugging +information or source files. + +@@item break +Set a breakpoint at the next instruction to be executed in the selected +stack frame (@@pxref{Stack}). In any selected frame but the innermost, +this will cause the program to stop as soon as control returns to that +frame. This is equivalent to a @@samp{finish} command in the frame +inside the selected frame. If this is done in the innermost frame gdb +will stop the next time it reaches the current location; this may be +useful inside of loops. It does not stop at this breakpoint immediately +upon continuation of the program since no code would be executed if it +did. + +@@item break @@dots{} if @@var{cond} +Set a breakpoint with condition @@var{cond}; evaluate the expression +@@var{cond} each time the breakpoint is reached, and stop only if the +value is nonzero. @@samp{@@dots{}} stands for one of the possible +arguments described above (or no argument) specifying where to break. +@@xref{Conditions}, for more information on breakpoint conditions. + +@@item tbreak @@var{args} +@@kindex tbreak +Set a breakpoint enabled only for one stop. @@var{args} are the +same as in the @@samp{break} command, and the breakpoint is set in the same +way, but the breakpoint is automatically @@dfn{disabled} the first time it +is hit. +@@end table + +GDB allows you to set any number of breakpoints at the same place in the +program. There is nothing silly or meaningless about this. When the +breakpoints are conditional, this is even useful (@@pxref{Conditions}). + +@@node Clear Breaks, Disabling, Set Breaks, Breakpoints +@@subsection Clearing Breakpoints + +@@cindex clear breakpoint +@@cindex delete breakpoints +It is often necessary to eliminate a breakpoint once it has done its job +and you no longer want the program to stop there. This is called +@@dfn{clearing} or @@samp{deleting} the breakpoint. A breakpoint that +has been cleared no longer exists in any sense. + +With the @@samp{clear} command you can clear breakpoints according to where +they are in the program. With the @@samp{delete} command you can clear +individual breakpoints by specifying their breakpoint numbers. + +@@b{It is not necessary to clear a breakpoint to proceed past it.} GDB +automatically ignores breakpoints in the first instruction to be executed +when you continue execution at the same address where the program stopped. + +@@table @@code +@@item clear +@@kindex clear +Clear any breakpoints at the next instruction to be executed in the +selected stack frame (@@pxref{Selection}). When the innermost frame +is selected, this is a good way to clear a breakpoint that the program +just stopped at. + +@@item clear @@var{function} +@@itemx clear @@var{filename}:@@var{function} +Clear any breakpoints set at entry to the function @@var{function}. + +@@item clear @@var{linenum} +@@item clear @@var{filename}:@@var{linenum} +Clear any breakpoints set at or within the code of the specified line. + +@@item delete @@var{bnums}@@dots{} +@@kindex delete +Delete the breakpoints of the numbers specified as arguments. +A breakpoint deleted is forgotten completely. +@@end table + +@@node Disabling, Conditions, Clear Breaks, Breakpoints +@@subsection Disabling Breakpoints + +@@cindex disabled breakpoints +@@cindex enabled breakpoints +Rather than clearing a breakpoint, you might prefer to @@dfn{disable} it. +This makes the breakpoint inoperative as if it had been cleared, but +remembers the information on the breakpoint so that you can @@dfn{enable} +it again later. + +You disable and enable breakpoints with the @@samp{enable} and +@@samp{disable} commands, specifying one or more breakpoint numbers as +arguments. Use @@samp{info break} to print a list of breakpoints if you +don't know which breakpoint numbers to use. + +A breakpoint can have any of four different states of enablement: + +@@itemize @@bullet +@@item +Enabled. The breakpoint will stop the program. A breakpoint made +with the @@samp{break} command starts out in this state. +@@item +Disabled. The breakpoint has no effect on the program. +@@item +Enabled once. The breakpoint will stop the program, but +when it does so it will become disabled. A breakpoint made +with the @@samp{tbreak} command starts out in this state. +@@item +Enabled for deletion. The breakpoint will stop the program, but +immediately after it does so it will be deleted permanently. +@@end itemize + +You change the state of enablement of a breakpoint with the following +commands: + +@@table @@code +@@item disable breakpoints @@var{bnums}@@dots{} +@@kindex disable breakpoints +@@item disable @@var{bnums}@@dots{} +@@kindex disable +Disable the specified breakpoints. A disabled breakpoint has no +effect but is not forgotten. All options such as ignore-counts, +conditions and commands are remembered in case the breakpoint is +enabled again later. + +@@item enable breakpoints @@var{bnums}@@dots{} +@@kindex enable breakpoints +@@item enable @@var{bnums}@@dots{} +@@kindex enable +Enable the specified breakpoints. They become effective once again in +stopping the program, until you specify otherwise. + +@@item enable breakpoints once @@var{bnums}@@dots{} +@@item enable once @@var{bnums}@@dots{} +Enable the specified breakpoints temporarily. Each will be disabled +again the next time it stops the program (unless you have used one of +these commands to specify a different state before that time comes). + +@@item enable breakpoints delete @@var{bnums}@@dots{} +@@item enable delete @@var{bnums}@@dots{} +Enable the specified breakpoints to work once and then die. Each of +the breakpoints will be deleted the next time it stops the program +(unless you have used one of these commands to specify a different +state before that time comes). +@@end table + +Aside from the automatic disablement or deletion of a breakpoint when it +stops the program, which happens only in certain states, the state of +enablement of a breakpoint changes only when one of the commands above +is used. + +@@node Conditions, Break Commands, Disabling, Breakpoints +@@subsection Break Conditions + +@@cindex conditions +The simplest sort of breakpoint breaks every time the program reaches a +specified place. You can also specify a @@dfn{condition} for a breakpoint. +A condition is just a boolean expression in your programming language +(@@xref{Expressions}). A breakpoint with a condition evaluates the +expression each time the program reaches it, and the program stops +only if the condition is true. + +Break conditions may have side effects, and may even call functions in your +program. These may sound like strange things to do, but their effects are +completely predictable unless there is another enabled breakpoint at the +same address. (In that case, GDB might see the other breakpoint first and +stop the program without checking the condition of this one.) Note that +breakpoint commands are usually more convenient and flexible for the +purpose of performing side effects when a breakpoint is reached +(@@pxref{Break Commands}). + +Break conditions can be specified when a breakpoint is set, by using +@@samp{if} in the arguments to the @@samp{break} command. @@xref{Set Breaks}. +They can also be changed at any time with the @@samp{condition} command: + +@@table @@code +@@item condition @@var{bnum} @@var{expression} +@@kindex condition +Specify @@var{expression} as the break condition for breakpoint number +@@var{bnum}. From now on, this breakpoint will stop the program only if +the value of @@var{expression} is true (nonzero, in C). @@var{expression} +is not evaluated at the time the @@samp{condition} command is given. +@@xref{Expressions}. + +@@item condition @@var{bnum} +Remove the condition from breakpoint number @@var{bnum}. It becomes +an ordinary unconditional breakpoint. +@@end table + +@@cindex ignore count (of breakpoint) +A special feature is provided for one kind of condition: to prevent the +breakpoint from doing anything until it has been reached a certain number +of times. This is done with the @@dfn{ignore count} of the breakpoint. +When the program reaches a breakpoint whose ignore count is positive, then +instead of stopping, it just decrements the ignore count by one and +continues. + +@@table @@code +@@item ignore @@var{bnum} @@var{count} +@@kindex ignore +Set the ignore count of breakpoint number @@var{bnum} to @@var{count}. +The next @@var{count} times the breakpoint is reached, it will not stop. + +To make the breakpoint stop the next time it is reached, specify +a count of zero. + +@@item cont @@var{count} +Continue execution of the program, setting the ignore count of the +breakpoint that the program stopped at to @@var{count} minus one. +Continuing through the breakpoint does not itself count as one of +@@var{count}. Thus, the program will not stop at this breakpoint until the +@@var{count}'th time it is hit. + +This command is allowed only when the program stopped due to a +breakpoint. At other times, the argument to @@samp{cont} is ignored. +@@end table + +If a breakpoint has a positive ignore count and a condition, the condition +is not checked. Once the ignore count reaches zero, the condition will +start to be checked. + +Note that you could achieve the effect of the ignore count with a condition +such as @@samp{$foo-- <= 0} using a debugger convenience variable that is +decremented each time. That is why the ignore count is considered a +special case of a condition. @@xref{Convenience Vars}. + +@@node Break Commands, Error in Breakpoints, Conditions, Breakpoints +@@subsection Commands Executed on Breaking + +@@cindex breakpoint commands +You can give any breakpoint a series of commands to execute when the +program stops due to that breakpoint. For example, you might want to +print the values of certain expressions, or enable other breakpoints. + +@@table @@code +@@item commands @@var{bnum} +Specify commands for breakpoint number @@var{bnum}. The commands +themselves appear on the following lines. Type a line containing just +@@samp{end} to terminate the commands. + +To remove all commands from a breakpoint, use the command +@@samp{commands} and follow it immediately by @@samp{end}; that is, give +no commands. + +With no arguments, @@samp{commands} refers to the last breakpoint set. +@@end table + +It is possible for breakpoint commands to start the program up again. +Simply use the @@samp{cont} command, or @@samp{step}, or any other command +to resume execution. However, any remaining breakpoint commands are +ignored. When the program stops again, GDB will act according to why +that stop took place. + +@@kindex silent +If the first command specified is @@samp{silent}, the usual message about +stopping at a breakpoint is not printed. This may be desirable for +breakpoints that are to print a specific message and then continue. +If the remaining commands too print nothing, you will see no sign that +the breakpoint was reached at all. @@samp{silent} is not really a command; +it is meaningful only at the beginning of the commands for a breakpoint. + +The commands @@samp{echo} and @@samp{output} that allow you to print precisely +controlled output are often useful in silent breakpoints. @@xref{Output}. + +For example, here is how you could use breakpoint commands to print the +value of @@code{x} at entry to @@code{foo} whenever it is positive. We +assume that the newly created breakpoint is number 4; @@samp{break} will +print the number that is assigned. + +@@example +break foo if x>0 +commands 4 +silent +echo x is\040 +output x +echo \n +cont +end +@@end example + +One application for breakpoint commands is to correct one bug so you can +test another. Put a breakpoint just after the erroneous line of code, give +it a condition to detect the case in which something erroneous has been +done, and give it commands to assign correct values to any variables that +need them. End with the @@samp{cont} command so that the program does not +stop, and start with the @@samp{silent} command so that no output is +produced. Here is an example: + +@@example +break 403 +commands 5 +silent +set x = y + 4 +cont +end +@@end example + +One deficiency in the operation of automatically continuing breakpoints +under Unix appears when your program uses raw mode for the terminal. +GDB switches back to its own terminal modes (not raw) before executing +commands, and then must switch back to raw mode when your program is +continued. This causes any pending terminal input to be lost. + +In the GNU system, this will be fixed by changing the behavior of +terminal modes. + +Under Unix, when you have this problem, you might be able to get around +it by putting your actions into the breakpoint condition instead of +commands. For example + +@@example +condition 5 (x = y + 4), 0 +@@end example + +@@noindent +is a condition expression (@@xref{Expressions}) that will change @@code{x} +as needed, then always have the value 0 so the program will not stop. +Loss of input is avoided here because break conditions are evaluated +without changing the terminal modes. When you want to have nontrivial +conditions for performing the side effects, the operators @@samp{&&}, +@@samp{||} and @@samp{?@@: @@dots{} :@@:} may be useful. + +@@node Error in Breakpoints,, Break Commands, Breakpoints +@@subsection ``Cannot Insert Breakpoints'' Error + +Under some Unix systems, breakpoints cannot be used in a program if any +other process is running that program. Attempting to run or continue +the program with a breakpoint in this case will cause GDB to stop it. + +When this happens, you have three ways to proceed: + +@@enumerate +@@item +Remove or disable the breakpoints, then continue. + +@@item +Suspend GDB, and copy the file containing the program to a new name. +Resume GDB and use the @@samp{exec-file} command to specify that GDB +should run the program under that name. Then start the program again. + +@@item +Recompile the program so that the text is non-sharable (a.out format +OMAGIC). +@@end enumerate + +@@node Continuing, Stepping, Breakpoints, Stopping +@@section Continuing + +After your program stops, most likely you will want it to run some more if +the bug you are looking for has not happened yet. + +@@table @@code +@@item cont +@@kindex cont +Continue running the program at the place where it stopped. +@@end table + +If the program stopped at a breakpoint, the place to continue running +is the address of the breakpoint. You might expect that continuing would +just stop at the same breakpoint immediately. In fact, @@samp{cont} +takes special care to prevent that from happening. You do not need +to clear the breakpoint to proceed through it after stopping at it. + +You can, however, specify an ignore-count for the breakpoint that the +program stopped at, by means of an argument to the @@samp{cont} command. +@@xref{Conditions}. + +If the program stopped because of a signal other than @@code{SIGINT} or +@@code{SIGTRAP}, continuing will cause the program to see that signal. +You may not want this to happen. For example, if the program stopped +due to some sort of memory reference error, you might store correct +values into the erroneous variables and continue, hoping to see more +execution; but the program would probably terminate immediately as +a result of the fatal signal once it sees the signal. To prevent this, +you can continue with @@samp{signal 0}. @@xref{Signaling}. You can +also act in advance to prevent the program from seeing certain kinds +of signals, using the @@samp{handle} command (@@pxref{Signals}). + +@@node Stepping,, Continuing, Stopping +@@section Stepping + +@@cindex stepping +@@dfn{Stepping} means setting your program in motion for a limited time, so +that control will return automatically to the debugger after one line of +code or one machine instruction. Breakpoints are active during stepping +and the program will stop for them even if it has not gone as far as the +stepping command specifies. + +@@table @@code +@@item step +@@kindex step +Proceed the program until control reaches a different line, then stop +it and return to the debugger. This command is abbreviated @@samp{s}. + +@@item step @@var{count} +Proceed as in @@samp{step}, but do so @@var{count} times. If a breakpoint +or a signal not related to stepping is reached before @@var{count} steps, +stepping stops right away. + +This command may be given when control is within a routine for which +there is no debugging information. In that case, execution will proceed +until control reaches a different routine, or is about to return from +this routine. An argument repeats this action. + +@@item next +@@kindex next +Similar to @@samp{step}, but any function calls appearing within the line of +code are executed without stopping. Execution stops when control reaches a +different line of code at the stack level which was executing when the +@@samp{next} command was given. This command is abbreviated @@samp{n}. + +An argument is a repeat count, as in @@samp{step}. + +@@samp{next} within a routine without debugging information acts as does +@@samp{step}, but any function calls appearing within the code of the +routine are executed without stopping. + +@@item finish +@@kindex finish +Continue running until just after the selected stack frame returns +(or until there is some other reason to stop, such as a fatal signal +or a breakpoint). Print value returned by the selected stack frame (if +any). + +Contrast this with the @@samp{return} command (@@pxref{Returning}). + +@@item until +@@kindex until +Proceed the program until control reaches a line greater than the current +line, then stop is and return to the debugger. Control is also returned to +the debugger if the program exits the current stack frame. Note that this +form of the command uses single stepping, and hence is slower than +@@samp{until} with an argument. This command is abbreviated @@samp{u}. + +@@item until @@var{location} +Proceed the program until either the specified location is reached, or the +current (innermost) stack frame returns. This form of the command uses +breakpoints, and hence is quicker than @@samp{until} without an argument. + +@@item stepi +@@itemx si +@@kindex stepi +@@kindex si +Proceed one machine instruction, then stop and return to the debugger. + +It is often useful to do @@samp{display/i $pc} when stepping by machine +instructions. This will cause the next instruction to be executed to +be displayed automatically at each stop. @@xref{Auto Display}. + +An argument is a repeat count, as in @@samp{step}. + +@@item nexti +@@itemx ni +@@kindex nexti +@@kindex ni +Proceed one machine instruction, but if it is a subroutine call, +proceed until the subroutine returns. + +An argument is a repeat count, as in @@samp{next}. +@@end table + +A typical technique for using stepping is to put a breakpoint +(@@pxref{Breakpoints}) at the beginning of the function or the section of +the program in which a problem is believed to lie, and then step through +the suspect area, examining the variables that are interesting, until the +problem happens. + +The @@samp{cont} command can be used after stepping to resume execution +until the next breakpoint or signal. + +@@node Stack, Source, Stopping, Top +@@chapter Examining the Stack + +When your program has stopped, the first thing you need to know is where it +stopped and how it got there. + +@@cindex call stack +Each time your program performs a function call, the information about +where in the program the call was made from is saved in a block of data +called a @@dfn{stack frame}. The frame also contains the arguments of the +call and the local variables of the function that was called. All the +stack frames are allocated in a region of memory called the @@dfn{call +stack}. + +When your program stops, the GDB commands for examining the stack allow you +to see all of this information. + +One of the stack frames is @@dfn{selected} by GDB and many GDB commands +refer implicitly to the selected frame. In particular, whenever you ask +GDB for the value of a variable in the program, the value is found in the +selected frame. There are special GDB commands to select whichever frame +you are interested in. + +When the program stops, GDB automatically selects the currently executing +frame and describes it briefly as the @@samp{frame} command does +(@@pxref{Frame Info, Info}). + +@@menu +* Frames:: Explanation of stack frames and terminology. +* Backtrace:: Summarizing many frames at once. +* Selection:: How to select a stack frame. +* Info: Frame Info, Commands to print information on stack frames. +@@end menu + +@@node Frames, Backtrace, Stack, Stack +@@section Stack Frames + +@@cindex frame +The call stack is divided up into contiguous pieces called @@dfn{frames}; +each frame is the data associated with one call to one function. The frame +contains the arguments given to the function, the function's local +variables, and the address at which the function is executing. + +@@cindex initial frame +@@cindex outermost frame +@@cindex innermost frame +When your program is started, the stack has only one frame, that of the +function @@code{main}. This is called the @@dfn{initial} frame or the +@@dfn{outermost} frame. Each time a function is called, a new frame is +made. Each time a function returns, the frame for that function invocation +is eliminated. If a function is recursive, there can be many frames for +the same function. The frame for the function in which execution is +actually occurring is called the @@dfn{innermost} frame. This is the most +recently created of all the stack frames that still exist. + +@@cindex frame pointer +Inside your program, stack frames are identified by their addresses. A +stack frame consists of many bytes, each of which has its own address; each +kind of computer has a convention for choosing one of those bytes whose +address serves as the address of the frame. Usually this address is kept +in a register called the @@dfn{frame pointer register} while execution is +going on in that frame. + +@@cindex frame number +GDB assigns numbers to all existing stack frames, starting with zero for +the innermost frame, one for the frame that called it, and so on upward. +These numbers do not really exist in your program; they are to give you a +way of talking about stack frames in GDB commands. + +@@cindex selected frame +Many GDB commands refer implicitly to one stack frame. GDB records a stack +frame that is called the @@dfn{selected} stack frame; you can select any +frame using one set of GDB commands, and then other commands will operate +on that frame. When your program stops, GDB automatically selects the +innermost frame. + +@@node Backtrace, Selection, Frames, Stack +@@section Backtraces + +A backtrace is a summary of how the program got where it is. It shows one +line per frame, for many frames, starting with the currently executing +frame (frame zero), followed by its caller (frame one), and on up the +stack. + +@@table @@code +@@item backtrace +@@itemx bt +Print a backtrace of the entire stack: one line per frame for all +frames in the stack. + +You can stop the backtrace at any time by typing the system interrupt +character, normally @@kbd{Control-C}. + +@@item backtrace @@var{n} +@@itemx bt @@var{n} +Similar, but stop after @@var{n} frames. + +@@item backtrace @@var{-n} +@@itemx bt @@var{-n} +Similar, but print the outermost @@var{n} frames instead of the +innermost. +@@end table + +Each line in a backtrace shows the frame number, the program counter, the +function and its arguments, and the source file name and line number (if +known). The program counter is omitted if is the beginning of the code for +the source line. This is the same as the first of the two lines printed +when you select a frame. + +@@node Selection, Frame Info, Backtrace, Stack +@@section Selecting a Frame + +Most commands for examining the stack and other data in the program work on +whichever stack frame is selected at the moment. Here are the commands for +selecting a stack frame; all of them finish by printing a brief description +of the stack frame just selected. + +@@table @@code +@@item frame @@var{n} +@@kindex frame +Select frame number @@var{n}. Recall that frame zero is the innermost +(currently executing) frame, frame one is the frame that called the +innermost one, and so on. The highest-numbered frame is @@code{main}'s +frame. + +@@item frame @@var{addr} +Select the frame at address @@var{addr}. This is useful mainly if the +chaining of stack frames has been damaged by a bug, making it +impossible for GDB to assign numbers properly to all frames. In +addition, this can be useful when the program has multiple stacks and +switches between them. + +@@item up @@var{n} +@@kindex up +Select the frame @@var{n} frames up from the frame previously selected. +For positive numbers @@var{n}, this advances toward the outermost +frame, to higher frame numbers, to frames that have existed longer. +@@var{n} defaults to one. + +@@item down @@var{n} +@@kindex down +Select the frame @@var{n} frames down from the frame previously +selected. For positive numbers @@var{n}, this advances toward the +innermost frame, to lower frame numbers, to frames that were created +more recently. @@var{n} defaults to one. +@@end table + +All of these commands end by printing some information on the frame that +has been selected: the frame number, the function name, the arguments, the +source file and line number of execution in that frame, and the text of +that source line. For example: + +@@example +#3 main (argc=3, argv=??, env=??) at main.c, line 67 +67 read_input_file (argv[i]); +@@end example + +After such a printout, the @@samp{list} command with no arguments will print +ten lines centered on the point of execution in the frame. @@xref{List}. + +@@node Frame Info,, Selection, Stack +@@section Information on a Frame + +There are several other commands to print information about the selected +stack frame. + +@@table @@code +@@item frame +This command prints a brief description of the selected stack frame. +It can be abbreviated @@samp{f}. With an argument, this command is +used to select a stack frame; with no argument, it does not change +which frame is selected, but still prints the same information. + +@@item info frame +@@kindex info frame +This command prints a verbose description of the selected stack frame, +including the address of the frame, the addresses of the next frame in +(called by this frame) and the next frame out (caller of this frame), +the address of the frame's arguments, the program counter saved in it +(the address of execution in the caller frame), and which registers +were saved in the frame. The verbose description is useful when +something has gone wrong that has made the stack format fail to fit +the usual conventions. + +@@item info frame @@var{addr} +Print a verbose description of the frame at address @@var{addr}, +without selecting that frame. The selected frame remains unchanged by +this command. + +@@item info args +@@kindex info args +Print the arguments of the selected frame, each on a separate line. + +@@item info locals +@@kindex info locals +Print the local variables of the selected frame, each on a separate +line. These are all variables declared static or automatic within all +program blocks that execution in this frame is currently inside of. +@@end table + +@@node Source, Data, Stack, Top +@@chapter Examining Source Files + +GDB knows which source files your program was compiled from, and +can print parts of their text. When your program stops, GDB +spontaneously prints the line it stopped in. Likewise, when you +select a stack frame (@@pxref{Selection}), GDB prints the line +which execution in that frame has stopped in. You can also +print parts of source files by explicit command. + +@@menu +* List:: Using the @@samp{list} command to print source files. +* Search:: Commands for searching source files. +* Source Path:: Specifying the directories to search for source files. +@@end menu + +@@node List, Search, Source, Source +@@section Printing Source Lines + +@@kindex list +To print lines from a source file, use the @@samp{list} command +(abbreviated @@samp{l}). There are several ways to specify what part +of the file you want to print. + +Here are the forms of the @@samp{list} command most commonly used: + +@@table @@code +@@item list @@var{linenum} +Print ten lines centered around line number @@var{linenum} in the +current source file. + +@@item list @@var{function} +Print ten lines centered around the beginning of function +@@var{function}. + +@@item list +Print ten more lines. If the last lines printed were printed with a +@@samp{list} command, this prints ten lines following the last lines +printed; however, if the last line printed was a solitary line printed +as part of displaying a stack frame (@@pxref{Stack}), this prints ten +lines centered around that line. + +@@item list @@minus{} +Print ten lines just before the lines last printed. +@@end table + +Repeating a @@samp{list} command with @@key{RET} discards the argument, +so it is equivalent to typing just @@samp{list}. This is more useful +than listing the same lines again. An exception is made for an +argument of @@samp{-}; that argument is preserved in repetition so that +each repetition moves up in the file. + +In general, the @@samp{list} command expects you to supply zero, one or two +@@dfn{linespecs}. Linespecs specify source lines; there are several ways +of writing them but the effect is always to specify some source line. +Here is a complete description of the possible arguments for @@samp{list}: + +@@table @@code +@@item list @@var{linespec} +Print ten lines centered around the line specified by @@var{linespec}. + +@@item list @@var{first},@@var{last} +Print lines from @@var{first} to @@var{last}. Both arguments are +linespecs. + +@@item list ,@@var{last} +Print ten lines ending with @@var{last}. + +@@item list @@var{first}, +Print ten lines starting with @@var{first}. + +@@item list + +Print ten lines just after the lines last printed. + +@@item list @@minus{} +Print ten lines just before the lines last printed. + +@@item list +As described in the preceding table. +@@end table + +Here are the ways of specifying a single source line---all the +kinds of linespec. + +@@table @@asis +@@item @@var{linenum} +Specifies line @@var{linenum} of the current source file. +When a @@samp{list} command has two linespecs, this refers to +the same source file as the first linespec. + +@@item +@@var{offset} +Specifies the line @@var{offset} lines after the last line printed. +When used as the second linespec in a @@samp{list} command that has +two, this specifies the line @@var{offset} lines down from the +first linespec. + +@@item @@minus{}@@var{offset} +Specifies the line @@var{offset} lines before the last line printed. + +@@item @@var{filename}:@@var{linenum} +Specifies line @@var{linenum} in the source file @@var{filename}. + +@@item @@var{function} +Specifies the line of the open-brace that begins the body of the +function @@var{function}. + +@@item @@var{filename}:@@var{function} +Specifies the line of the open-brace that begins the body of the +function @@var{function} in the file @@var{filename}. The file name is +needed with a function name only for disambiguation of identically +named functions in different source files. + +@@item *@@var{address} +Specifies the line containing the program address @@var{address}. +@@var{address} may be any expression. +@@end table + +One other command is used to map source lines to program addresses. + +@@table @@code +@@item info line @@var{linenum} +@@kindex info line +Print the starting and ending addresses of the compiled code for +source line @@var{linenum}. + +@@kindex $_ +The default examine address for the @@samp{x} command is changed to the +starting address of the line, so that @@samp{x/i} is sufficient to +begin examining the machine code (@@pxref{Memory}). Also, this address +is saved as the value of the convenience variable @@samp{$_} +(@@pxref{Convenience Vars}). +@@end table + +@@node Search, Source Path, List, Source +@@section Searching Source Files +@@cindex searching +@@kindex forward-search +@@kindex reverse-search + +There are two commands for searching through the current source file for a +regular expression. + +The command @@samp{forward-search @@var{regexp}} checks each line, starting +with the one following the last line listed, for a match for @@var{regexp}. +It lists the line that is found. You can abbreviate the command name +as @@samp{fo}. + +The command @@samp{reverse-search @@var{regexp}} checks each line, starting +with the one before the last line listed and going backward, for a match +for @@var{regexp}. It lists the line that is found. You can abbreviate +this command with as little as @@samp{rev}. + +@@node Source Path,, Search, Source +@@section Specifying Source Directories + +@@cindex source path +@@cindex directories for source files +Executable programs do not record the directories of the source files they +were compiled from, just the names. GDB remembers a list of directories to +search for source files; this is called the @@dfn{source path}. Each time +GDB wants a source file, it tries all the directories in the list, in the +order they are present in the list, until it finds a file with the desired +name. + +@@kindex directory +When you start GDB, its source path contains just the current working +directory. To add other directories, use the @@samp{directory} command. +@@b{Note that the search path for executable files and the working directory +are @@i{not} used for finding source files.} + +@@table @@code +@@item directory @@var{dirname} +Add directory @@var{dirname} to the end of the source path. + +@@item directory +Reset the source path to just the current working directory of GDB. +This requires confirmation. + +@@samp{directory} with no argument can cause source files previously +found by GDB to be found in a different directory. To make this work +correctly, this command also clears out the tables GDB maintains +about the source files it has already found. + +@@item info directories +@@kindex info directories +Print the source path: show which directories it contains. +@@end table + +Because the @@samp{directory} command adds to the end of the source path, +it does not affect any file that GDB has already found. If the source +path contains directories that you do not want, and these directories +contain misleading files with names matching your source files, the +way to correct the situation is as follows: + +@@enumerate +@@item +Choose the directory you want at the beginning of the source path. +Use the @@samp{cd} command to make that the current working directory. + +@@item +Use @@samp{directory} with no argument to reset the source path to just +that directory. + +@@item +Use @@samp{directory} with suitable arguments to add any other +directories you want in the source path. +@@end enumerate + +@@node Data, Symbols, Source, Top +@@chapter Examining Data + +@@cindex printing data +@@cindex examining data +@@kindex print +The usual way of examining data in your program is with the @@samp{print} +command (abbreviated @@samp{p}). It evaluates and prints the value of any +valid expression of the language the program is written in (for now, C). +You type + +@@example +print @@var{exp} +@@end example + +@@noindent +where @@var{exp} is any valid expression, and the value of @@var{exp} +is printed in a format appropriate to its data type. + +A more low-level way of examining data is with the @@samp{x} command. +It examines data in memory at a specified address and prints it in a +specified format. + +GDB supports one command to modify the default format of displayed data: + +@@table @@samp +@@item set array-max +@@kindex set array-max +@@samp{set array-max} sets the maximum number of elements of an array which +will be printed. This limit also applies to the display of strings. +@@end table + +@@menu +* Expressions:: Expressions that can be computed and printed. +* Variables:: Using your program's variables in expressions. +* Assignment:: Setting your program's variables. +* Arrays:: Examining part of memory as an array. +* Formats:: Specifying formats for printing values. +* Memory:: Examining memory explicitly. +* Auto Display:: Printing certain expressions whenever program stops. +* Value History:: Referring to values previously printed. +* Convenience Vars:: Giving names to values for future reference. +* Registers:: Referring to and storing in machine registers. +@@end menu + +@@node Expressions, Variables, Data, Data +@@section Expressions + +@@cindex expressions +Many different GDB commands accept an expression and compute its value. +Any kind of constant, variable or operator defined by the programming +language you are using is legal in an expression in GDB. This includes +conditional expressions, function calls, casts and string constants. +It unfortunately does not include symbols defined by preprocessor +#define commands. + +Casts are supported in all languages, not just in C, because it is so +useful to cast a number into a pointer so as to examine a structure +at that address in memory. + +GDB supports three kinds of operator in addition to those of programming +languages: + +@@table @@code +@@item @@@@ +@@samp{@@@@} is a binary operator for treating parts of memory as arrays. +@@xref{Arrays}, for more information. + +@@item :: +@@samp{::} allows you to specify a variable in terms of the file or +function it is defined in. @@xref{Variables}. + +@@item @@{@@var{type}@@} @@var{addr} +Refers to an object of type @@var{type} stored at address @@var{addr} in +memory. @@var{addr} may be any expression whose value is an integer or +pointer (but parentheses are required around nonunary operators, just as in +a cast). This construct is allowed regardless of what kind of data is +officially supposed to reside at @@var{addr}.@@refill +@@end table + +@@node Variables, Arrays, Expressions, Data +@@section Program Variables + +The most common kind of expression to use is the name of a variable +in your program. + +Variables in expressions are understood in the selected stack frame +(@@pxref{Selection}); they must either be global (or static) or be visible +according to the scope rules of the programming language from the point of +execution in that frame. This means that in the function + +@@example +foo (a) + int a; +@@{ + bar (a); + @@{ + int b = test (); + bar (b); + @@} +@@} +@@end example + +@@noindent +the variable @@code{a} is usable whenever the program is executing +within the function @@code{foo}, but the variable @@code{b} is visible +only while the program is executing inside the block in which @@code{b} +is declared. + +As a special exception, you can refer to a variable or function whose +scope is a single source file even if the current execution point is not +in this file. But it is possible to have more than one such variable +or function with the same name (if they are in different source files). +In such a case, it is not defined which one you will get. If you wish, +you can specify any one of them using the colon-colon construct: + +@@example +@@var{block}::@@var{variable} +@@end example + +@@noindent +Here @@var{block} is the name of the source file whose variable you want. + +@@node Arrays, Formats, Variables, Data +@@section Artificial Arrays + +@@cindex artificial array +It is often useful to print out several successive objects of the +same type in memory; a section of an array, or an array of +dynamically determined size for which only a pointer exists in the +program. + +This can be done by constructing an @@dfn{artificial array} with the +binary operator @@samp{@@@@}. The left operand of @@samp{@@@@} should be +the first element of the desired array, as an individual object. +The right operand should be the length of the array. The result is +an array value whose elements are all of the type of the left argument. +The first element is actually the left argument; the second element +comes from bytes of memory immediately following those that hold the +first element, and so on. Here is an example. If a program says + +@@example +int *array = (int *) malloc (len * sizeof (int)); +@@end example + +@@noindent +you can print the contents of @@code{array} with + +@@example +p *array@@@@len +@@end example + +The left operand of @@samp{@@@@} must reside in memory. Array values made +with @@samp{@@@@} in this way behave just like other arrays in terms of +subscripting, and are coerced to pointers when used in expressions. +(It would probably appear in an expression via the value history, +after you had printed it out.) + +@@node Formats, Memory, Arrays, Data +@@section Formats + +@@cindex formatted output +@@cindex output formats +GDB normally prints all values according to their data types. Sometimes +this is not what you want. For example, you might want to print a number +in hex, or a pointer in decimal. Or you might want to view data in memory +at a certain address as a character string or an instruction. These things +can be done with @@dfn{output formats}. + +The simplest use of output formats is to say how to print a value +already computed. This is done by starting the arguments of the +@@samp{print} command with a slash and a format letter. The format +letters supported are: + +@@table @@samp +@@item x +Regard the bits of the value as an integer, and print the integer in +hexadecimal. + +@@item d +Print as integer in signed decimal. + +@@item u +Print as integer in unsigned decimal. + +@@item o +Print as integer in octal. + +@@item a +Print as an address, both absolute in hex and then relative +to a symbol defined as an address below it. + +@@item c +Regard as an integer and print it as a character constant. + +@@item f +Regard the bits of the value as a floating point number and print +using typical floating point syntax. +@@end table + +For example, to print the program counter in hex (@@pxref{Registers}), type + +@@example +p/x $pc +@@end example + +@@noindent +Note that no space is required before the slash; this is because command +names in GDB cannot contain a slash. + +To reprint the last value in the value history with a different format, +you can use the @@samp{print} command with just a format and no +expression. For example, @@samp{p/x} reprints the last value in hex. + +@@node Memory, Auto Display, Formats, Data +@@subsection Examining Memory + +@@cindex examining memory +@@kindex x +The command @@samp{x} (for `examine') can be used to examine memory under +explicit control of formats, without reference to the program's data types. + +@@samp{x} is followed by a slash and an output format specification, +followed by an expression for an address. The expression need not have +a pointer value (though it may); it is used as an integer, as the +address of a byte of memory. @@xref{Expressions} for more information +on expressions. + +The output format in this case specifies both how big a unit of memory +to examine and how to print the contents of that unit. It is done +with one or two of the following letters: + +These letters specify just the size of unit to examine: + +@@table @@samp +@@item b +Examine individual bytes. + +@@item h +Examine halfwords (two bytes each). + +@@item w +Examine words (four bytes each). + +@@cindex word +Many assemblers and cpu designers still use `word' for a 16-bit quantity, +as a holdover from specific predecessor machines of the 1970's that really +did use two-byte words. But more generally the term `word' has always +referred to the size of quantity that a machine normally operates on and +stores in its registers. This is 32 bits for all the machines that GNU +runs on. + +@@item g +Examine giant words (8 bytes). +@@end table + +These letters specify just the way to print the contents: + +@@table @@samp +@@item x +Print as integers in unsigned hexadecimal. + +@@item d +Print as integers in signed decimal. + +@@item u +Print as integers in unsigned decimal. + +@@item o +Print as integers in unsigned octal. + +@@item a +Print as an address, both absolute in hex and then relative +to a symbol defined as an address below it. + +@@item c +Print as character constants. + +@@item f +Print as floating point. This works only with sizes @@samp{w} and +@@samp{g}. + +@@item s +Print a null-terminated string of characters. The specified unit size +is ignored; instead, the unit is however many bytes it takes to reach +a null character (including the null character). + +@@item i +Print a machine instruction in assembler syntax (or nearly). The +specified unit size is ignored; the number of bytes in an instruction +varies depending on the type of machine, the opcode and the addressing +modes used. +@@end table + +If either the manner of printing or the size of unit fails to be specified, +the default is to use the same one that was used last. If you don't want +to use any letters after the slash, you can omit the slash as well. + +You can also omit the address to examine. Then the address used is +just after the last unit examined. This is why string and instruction +formats actually compute a unit-size based on the data: so that the +next string or instruction examined will start in the right place. +The @@samp{print} command sometimes sets the default address for +the @@samp{x} command; when the value printed resides in memory, the +default is set to examine the same location. @@samp{info line} also +sets the default for @@samp{x}, to the address of the start of the +machine code for the specified line and @@samp{info breakpoints} sets +it to the address of the last breakpoint listed. + +When you use @@key{RET} to repeat an @@samp{x} command, it does not repeat +exactly the same: the address specified previously (if any) is ignored, so +that the repeated command examines the successive locations in memory +rather than the same ones. + +You can examine several consecutive units of memory with one command by +writing a repeat-count after the slash (before the format letters, if any). +The repeat count must be a decimal integer. It has the same effect as +repeating the @@samp{x} command that many times except that the output may +be more compact with several units per line. + +@@example +x/10i $pc +@@end example + +@@noindent +Prints ten instructions starting with the one to be executed next in the +selected frame. After doing this, you could print another ten following +instructions with + +@@example +x/10 +@@end example + +@@noindent +in which the format and address are allowed to default. + +@@kindex $_ +@@kindex $__ +The addresses and contents printed by the @@samp{x} command are not put in +the value history because there is often too much of them and they would +get in the way. Instead, GDB makes these values available for subsequent +use in expressions as values of the convenience variables @@samp{$_} and +@@samp{$__}. + +After an @@samp{x} command, the last address examined is available for use +in expressions in the convenience variable @@samp{$_}. The contents of that +address, as examined, are available in the convenience variable @@samp{$__}. + +If the @@samp{x} command has a repeat count, the address and contents saved +are from the last memory unit printed; this is not the same as the last +address printed if several units were printed on the last line of output. + +@@node Auto Display, Value History, Memory, Data +@@section Automatic Display + +If you find that you want to print the value of an expression frequently +(to see how it changes), you might want to add it to the @@dfn{automatic +display list} so that GDB will print its value each time the program stops. +Each expression added to the list is given a number to identify it; +to remove an expression from the list, you specify that number. +The automatic display looks like this: + +@@example +2: foo = 38 +3: bar[5] = (struct hack *) 0x3804 +@@end example + +@@noindent +showing item numbers, expressions and their current values. + +@@table @@code +@@item display @@var{exp} +@@kindex display +Add the expression @@var{exp} to the list of expressions to display +each time the program stops. @@xref{Expressions}. + +@@item display/@@var{fmt} @@var{exp} +For @@var{fmt} specifying only a display format and not a size or +count, add the expression @@var{exp} to the auto-display list but +arranges to display it each time in the specified format @@var{fmt}. + +@@item display/@@var{fmt} @@var{addr} +For @@var{fmt} @@samp{i} or @@samp{s}, or including a unit-size or a +number of units, add the expression @@var{addr} as a memory address to +be examined each time the program stops. Examining means in effect +doing @@samp{x/@@var{fmt} @@var{addr}}. @@xref{Memory}. + +@@item undisplay @@var{dnums}@@dots{} +@@kindex undisplay +@@item delete display @@var{dnums}@@dots{} +@@kindex delete display +Remove item numbers @@var{dnums} from the list of expressions to display. + +@@item disable display @@var{dnums}@@dots{} +@@kindex disable display +Disable the display of item numbers @@var{dnums}. A disabled display item +has no effect but is not forgotten. It may be later enabled. + +@@item enable display @@var{dnums}@@dots{} +@@kindex enable display +Enable display of item numbers @@var{dnums}. It becomes effective once +again in auto display of its expression, until you specify otherwise. + +@@item display +Display the current values of the expressions on the list, just as is +done when the program stops. + +@@item info display +@@kindex info display +Print the list of expressions to display automatically, each one +with its item number, but without showing the values. +@@end table + +@@node Value History, Convenience Vars, Auto Display, Data +@@section Value History + +@@cindex value history +Every value printed by the @@samp{print} command is saved for the entire +session in GDB's @@dfn{value history} so that you can refer to it in +other expressions. + +@@cindex $ +@@cindex $$ +The values printed are given @@dfn{history numbers} for you to refer to them +by. These are successive integers starting with 1. @@samp{print} shows you +the history number assigned to a value by printing @@samp{$@@var{n} = } +before the value; here @@var{n} is the history number. + +To refer to any previous value, use @@samp{$} followed by the value's +history number. The output printed by @@samp{print} is designed to remind +you of this. Just @@samp{$} refers to the most recent value in the history, +and @@samp{$$} refers to the value before that. + +For example, suppose you have just printed a pointer to a structure and +want to see the contents of the structure. It suffices to type + +@@example +p *$ +@@end example + +If you have a chain of structures where the component @@samp{next} points +to the next one, you can print the contents of the next one with + +@@example +p *$.next +@@end example + +It might be useful to repeat this command many times by typing @@key{RET}. + +Note that the history records values, not expressions. If the value of +@@code{x} is 4 and you type + +@@example +print x +set x=5 +@@end example + +@@noindent +then the value recorded in the value history by the @@samp{print} command +remains 4 even though @@code{x}'s value has changed. + +@@table @@code +@@item info history +@@kindex info history +Print the last ten values in the value history, with their item +numbers. This is like @@samp{p $$9} repeated ten times, except that +@@samp{info history} does not change the history. + +@@item info history @@var{n} +Print ten history values centered on history item number @@var{n}. +@@end table + +@@node Convenience Vars, Registers, Value History, Data +@@section Convenience Variables + +@@cindex convenience variables +GDB provides @@dfn{convenience variables} that you can use within GDB to +hold on to a value and refer to it later. These variables exist entirely +within GDB; they are not part of your program, and setting a convenience +variable has no effect on further execution of your program. That's why +you can use them freely. + +Convenience variables have names starting with @@samp{$}. Any name starting +with @@samp{$} can be used for a convenience variable, unless it is one of +the predefined set of register names (@@pxref{Registers}). + +You can save a value in a convenience variable with an assignment +expression, just as you would set a variable in your program. Example: + +@@example +set $foo = *object_ptr +@@end example + +@@noindent +would save in @@samp{$foo} the value contained in the object pointed to by +@@code{object_ptr}. + +Using a convenience variable for the first time creates it; but its value +is @@code{void} until you assign a new value. You can alter the value with +another assignment at any time. + +Convenience variables have no fixed types. You can assign a convenience +variable any type of value, even if it already has a value of a different +type. The convenience variable as an expression has whatever type its +current value has. + +@@table @@code +@@item info convenience +@@kindex info convenience +Print a list of convenience variables used so far, and their values. +Abbreviated @@samp{i con}. +@@end table + +One of the ways to use a convenience variable is as a counter to be +incremented or a pointer to be advanced. For example: + +@@example +set $i = 0 +print bar[$i++]->contents +@@i{@@dots{}repeat that command by typing @@key{RET}.} +@@end example + +Some convenience variables are created automatically by GDB and given +values likely to be useful. + +@@table @@samp +@@item $_ +The variable @@samp{$_} is automatically set by the @@samp{x} command to +the last address examined (@@pxref{Memory}). Other commands which +provide a default address for @@samp{x} to examine also set @@samp{$_} +to that address; these commands include @@samp{info line} and @@samp{info +breakpoint}. + +@@item $__ +The variable @@samp{$__} is automatically set by the @@samp{x} command +to the value found in the last address examined. +@@end table + +@@node Registers,, Convenience Vars, Data +@@section Registers + +@@cindex registers +Machine register contents can be referred to in expressions as variables +with names starting with @@samp{$}. The names of registers are different +for each machine; use @@samp{info registers} to see the names used on your +machine. The names @@samp{$pc} and @@samp{$sp} are used on all machines for +the program counter register and the stack pointer. Often @@samp{$fp} is +used for a register that contains a pointer to the current stack frame. + +GDB always considers the contents of an ordinary register as an integer +when the register is examined in this way. Some machines have special +registers which can hold nothing but floating point; these registers are +considered floating point. There is no way to refer to the contents of an +ordinary register as floating point value (although you can @@emph{print} +it as a floating point value with @@samp{print/f $@@var{regname}}). + +Some registers have distinct ``raw'' and ``virtual'' data formats. This +means that the data format in which the register contents are saved by the +operating system is not the same one that your program normally sees. For +example, the registers of the 68881 floating point coprocessor are always +saved in ``extended'' format, but virtually all C programs expect to work with +``double'' format. In such cases, GDB normally works with the virtual +format only (the format that makes sense for your program), but the +@@samp{info registers} command prints the data in both formats. + +Register values are relative to the selected stack frame +(@@pxref{Selection}). This means that you get the value that the register +would contain if all stack frames farther in were exited and their saved +registers restored. In order to see the real contents of all registers, +you must select the innermost frame (with @@samp{frame 0}). + +Some registers are never saved (typically those numbered zero or one) +because they are used for returning function values; for these registers, +relativization makes no difference. + +@@table @@code +@@item info registers +@@kindex info registers +Print the names and relativized values of all registers. + +@@item info registers @@var{regname} +Print the relativized value of register @@var{regname}. @@var{regname} +may be any register name valid on the machine you are using, with +or without the initial @@samp{$}. +@@end table + +@@subsection Examples + +You could print the program counter in hex with + +@@example +p/x $pc +@@end example + +@@noindent +or print the instruction to be executed next with + +@@example +x/i $pc +@@end example + +@@noindent +or add four to the stack pointer with + +@@example +set $sp += 4 +@@end example + +@@noindent +The last is a way of removing one word from the stack, on machines where +stacks grow downward in memory (most machines, nowadays). This assumes +that the innermost stack frame is selected. Setting @@samp{$sp} is +not allowed when other stack frames are selected. + +@@node Symbols, Altering, Data, Top +@@chapter Examining the Symbol Table + +The commands described in this section allow you to make inquiries for +information about the symbols (names of variables, functions and types) +defined in your program. This information is found by GDB in the symbol +table loaded by the @@samp{symbol-file} command; it is inherent in the text +of your program and does not change as the program executes. + +@@table @@code +@@item whatis @@var{exp} +@@kindex whatis +Print the data type of expression @@var{exp}. @@var{exp} is not +actually evaluated, and any side-effecting operations (such as +assignments or function calls) inside it do not take place. +@@xref{Expressions}. + +@@item whatis +Print the data type of @@samp{$}, the last value in the value history. + +@@item info address @@var{symbol} +@@kindex info address +Describe where the data for @@var{symbol} is stored. For register +variables, this says which register. For other automatic variables, +this prints the stack-frame offset at which the variable is always +stored. Note the contrast with @@samp{print &@@var{symbol}}, which does +not work at all for register variables and for automatic variables +prints the exact address of the current instantiation of the variable. + +@@item ptype @@var{typename} +@@kindex ptype +Print a description of data type @@var{typename}. @@var{typename} may be +the name of a type, or for C code it may have the form +@@samp{struct @@var{struct-tag}}, @@samp{union @@var{union-tag}} or +@@samp{enum @@var{enum-tag}}.@@refill + +@@item info sources +@@kindex info sources +Print the names of all source files in the program for which there +is debugging information. + +@@item info functions +@@kindex info functions +Print the names and data types of all defined functions. + +@@item info functions @@var{regexp} +Print the names and data types of all defined functions +whose names contain a match for regular expression @@var{regexp}. +Thus, @@samp{info fun step} finds all functions whose names +include @@samp{step}; @@samp{info fun ^step} finds those whose names +start with @@samp{step}. + +@@item info variables +@@kindex info variables +Print the names and data types of all variables that are declared +outside of functions. + +@@item info variables @@var{regexp} +Print the names and data types of all variables, declared outside of +functions, whose names contain a match for regular expression +@@var{regexp}. + +@@item info types +@@kindex info types +Print all data types that are defined in the program. + +@@item info types @@var{regexp} +Print all data types that are defined in the program whose names +contain a match for regular expression @@var{regexp}. + +@@item info methods +@@item info methods @@var{regexp} +@@kindex info methods +The @@samp{info-methods} command permits the user to examine all defined +methods within C@@code{++} program, or (with the @@var{regexp} argument) a +specific set of methods found in the various C@@code{++} classes. Many +C@@code{++} classes which implement a large number of differently typed +methods implement a large number of methods as well. Thus, the +@@samp{ptype} command can give the user a tremendous overdose of +information about what methods are associated with a given class. The +@@samp{info-methods} command filters these methods do to only those +methods which match the regular-expression search key. + +@@item printsyms @@var{filename} +@@kindex printsyms +Write a complete dump of the debugger's symbol data into the +file @@var{filename}. +@@end table + +@@node Altering, Sequences, Symbols, Top +@@chapter Altering Execution + +There are several ways to alter the execution of your program with GDB +commands. + +@@menu +* Assignment:: Altering variable values or memory contents. +* Jumping:: Altering control flow. +* Signaling:: Making signals happen in the program. +* Returning:: Making a function return prematurely. +@@end menu + +@@node Assignment, Jumping, Altering, Altering +@@section Assignment to Variables + +@@cindex assignment +@@cindex setting variables +To alter the value of a variable, evaluate an assignment expression. +@@xref{Expressions}. For example, + +@@example +print x=4 +@@end example + +@@noindent +would store the value 4 into the variable @@code{x}, and then print +the value of the assignment expression (which is 4). + +@@kindex set +@@kindex set variable +If you are not interested in seeing the value of the assignment, use the +@@samp{set} command instead of the @@samp{print} command. @@samp{set} is +really the same as @@samp{print} except that the expression's value is not +printed and is not put in the value history (@@pxref{Value History}). The +expression is evaluated only for side effects. + +Note that if the beginning of the argument string of the @@samp{set} command +appears identical to a @@samp{set} subcommand, it may be necessary to use +the @@samp{set variable} command. This command is identical to @@samp{set} +except for its lack of subcommands. + +GDB allows more implicit conversions in assignments than C does; you can +freely store an integer value into a pointer variable or vice versa, and +any structure can be converted to any other structure that is the same +length or shorter. + +In C, all the other assignment operators such as @@samp{+=} and @@samp{++} +are supported as well. + +To store into arbitrary places in memory, use the @@samp{@@{@@dots{}@@}} +construct to generate a value of specified type at a specified address +(@@pxref{Expressions}). For example, + +@@example +set @@{int@@}0x83040 = 4 +@@end example + +@@node Jumping, Signaling, Assignment, Altering +@@section Continuing at a Different Address + +@@table @@code +@@item jump @@var{linenum} +@@kindex jump +Resume execution at line number @@var{linenum}. Execution may stop +immediately if there is a breakpoint there. + +The @@samp{jump} command does not change the current stack frame, or +the stack pointer, or the contents of any memory location or any +register other than the program counter. If line @@var{linenum} is in +a different function from the one currently executing, the results may +be wild if the two functions expect different patterns of arguments or +of local variables. For this reason, the @@samp{jump} command requests +confirmation if the specified line is not in the function currently +executing. However, even wild results are predictable based on +changing the program counter. + +@@item jump *@@var{address} +Resume execution at the instruction at address @@var{address}. +@@end table + +A similar effect can be obtained by storing a new value into the register +@@samp{$pc}, but not exactly the same. + +@@example +set $pc = 0x485 +@@end example + +@@noindent +specifies the address at which execution will resume, but does not resume +execution. That does not happen until you use the @@samp{cont} command or a +stepping command (@@pxref{Stepping}). + +@@node Signaling, Returning, Jumping, Altering +@@section Giving the Program a Signal + +@@table @@code +@@item signal @@var{signalnum} +@@kindex signal +Resume execution where the program stopped, but give it immediately +the signal number @@var{signalnum}. + +Alternatively, if @@var{signalnum} is zero, continue execution and give +no signal. This is useful when the program has received a signal +but you don't want the program to see that signal; the @@samp{cont} command +would signal the program. +@@end table + +@@node Returning,, Signaling, Altering +@@section Returning from a Function + +@@cindex returning from a function +@@kindex return +You can make any function call return immediately, using the @@samp{return} +command. + +First select the stack frame that you wish to return from +(@@pxref{Selection}). Then type the @@samp{return} command. If you wish to +specify the value to be returned, give that as an argument. + +This pops the selected stack frame (and any other frames inside of it), +leaving its caller as the innermost remaining frame. That frame becomes +selected. The specified value is stored in the registers used for +returning values of functions. + +The @@samp{return} command does not resume execution; it leaves the program +stopped in the state that would exist if the function had just returned. +Contrast this with the @@samp{finish} command (@@pxref{Stepping}), which +resumes execution @@i{until} the selected stack frame returns naturally. + +@@node Sequences, Emacs, Altering, Top +@@chapter Canned Sequences of Commands + +GDB provides two ways to store sequences of commands for execution as a +unit: user-defined commands and command files. + +@@menu +* Define:: User-defined commands. +* Command Files:: Command files. +* Output:: Controlled output commands useful in + user-defined commands and command files. +@@end menu + +@@node Define, Command Files, Sequences, Sequences +@@section User-Defined Commands + +@@cindex user-defined commands +A @@dfn{user-defined command} is a sequence of GDB commands to which you +assign a new name as a command. This is done with the @@samp{define} +command. + +@@table @@code +@@item define @@var{commandname} +@@kindex define +Define a command named @@var{commandname}. If there is already a command +by that name, you are asked to confirm that you want to redefine it. + +The definition of the command is made up of other GDB command lines, +which are given following the @@samp{define} command. The end of these +commands is marked by a line containing @@samp{end}. + +@@item document @@var{commandname} +@@kindex document +Give documentation to the user-defined command @@var{commandname}. The +command @@var{commandname} must already be defined. This command reads +lines of documentation just as @@samp{define} reads the lines of the +command definition, ending with @@samp{end}. After the @@samp{document} command is finished, +@@samp{help} on command @@var{commandname} will print the documentation +you have specified. + +You may use the @@samp{document} command again to change the +documentation of a command. Redefining the command with @@samp{define} +does not change the documentation. +@@end table + +User-defined commands do not take arguments. When they are executed, the +commands of the definition are not printed. An error in any command +stops execution of the user-defined command. + +Commands that would ask for confirmation if used interactively proceed +without asking when used inside a user-defined command. Many GDB commands +that normally print messages to say what they are doing omit the messages +when used in user-defined command. + +@@node Command Files, Output, Define, Sequences +@@section Command Files + +@@cindex command files +A command file for GDB is a file of lines that are GDB commands. Comments +(lines starting with @@samp{#}) may also be included. An empty line in a +command file does nothing; it does not mean to repeat the last command, as +it would from the terminal. + +@@cindex init file +@@cindex .gdbinit +When GDB starts, it automatically executes its @@dfn{init files}, command +files named @@file{.gdbinit}. GDB reads the init file (if any) in your home +directory and then the init file (if any) in the current working +directory. (The init files are not executed if the @@samp{-nx} option +is given.) You can also request the execution of a command file with the +@@samp{source} command: + +@@table @@code +@@item source @@var{filename} +@@kindex source +Execute the command file @@var{filename}. +@@end table + +The lines in a command file are executed sequentially. They are not +printed as they are executed. An error in any command terminates execution +of the command file. + +Commands that would ask for confirmation if used interactively proceed +without asking when used in a command file. Many GDB commands that +normally print messages to say what they are doing omit the messages +when used in a command file. + +@@node Output,, Command Files, Sequences +@@section Commands for Controlled Output + +During the execution of a command file or a user-defined command, the only +output that appears is what is explicitly printed by the commands of the +definition. This section describes three commands useful for generating +exactly the output you want. + +@@table @@code +@@item echo @@var{text} +@@kindex echo +Print @@var{text}. Nonprinting characters can be included in +@@var{text} using C escape sequences, such as @@samp{\n} to print a +newline. @@b{No newline will be printed unless you specify one.} + +A backslash at the end of @@var{text} is ignored. It is useful for +outputting a string ending in spaces, since trailing spaces are +trimmed from all arguments. A backslash at the beginning preserves +leading spaces in the same way, because @@samp{\ } as an escape +sequence stands for a space. Thus, to print @@samp{ and foo = }, do + +@@example +echo \ and foo = \ +@@end example + +@@item output @@var{expression} +@@kindex output +Print the value of @@var{expression} and nothing but that value: no +newlines, no @@samp{$@@var{nn} = }. The value is not entered in the +value history either. @@xref{Expressions} for more information +on expressions. + +@@item output/@@var{fmt} @@var{expression} +Print the value of @@var{expression} in format @@var{fmt}. +@@xref{Formats}, for more information. + +@@item printf @@var{string}, @@var{expressions}@@dots{} +@@kindex printf +Print the values of the @@var{expressions} under the control of +@@var{string}. The @@var{expressions} are separated by commas and may +be either numbers or pointers. Their values are printed as specified +by @@var{string}, exactly as if the program were to execute + +@@example +printf (@@var{string}, @@var{expressions}@@dots{}); +@@end example + +For example, you can print two values in hex like this: + +@@example +printf "foo, bar-foo = 0x%x, 0x%x\n", foo, bar-foo +@@end example + +The only backslash-escape sequences that you can use in the string are +the simple ones that consist of backslash followed by a letter. +@@end table + +@@node Emacs, Remote, Sequences, Top +@@chapter Using GDB under GNU Emacs + +A special interface allows you to use GNU Emacs to view (and +edit) the source files for the program you are debugging with +GDB. + +To use this interface, use the command @@kbd{M-x gdb} in Emacs. +Give the executable file you want to debug as an argument. This +command starts a GDB process as a subprocess of Emacs, with input +and output through a newly created Emacs buffer. + +Using this GDB process is just like using GDB normally except for two things: + +@@itemize @@bullet +@@item +All ``terminal'' input and output goes through the Emacs buffer. This +applies both to GDB commands and their output, and to the input and +output done by the program you are debugging. + +This is useful because it means that you can copy the text of previous +commands and input them again; you can even use parts of the output +in this way. + +All the facilities of Emacs's Shell mode are available for this purpose. + +@@item +GDB displays source code through Emacs. Each time GDB displays a +stack frame, Emacs automatically finds the source file for that frame +and puts an arrow (@@samp{=>}) at the left margin of the current line. + +Explicit GDB @@samp{list} or search commands still produce output as +usual, but you probably will have no reason to use them. +@@end itemize + +In the GDB I/O buffer, you can use these special Emacs commands: + +@@table @@kbd +@@item M-s +Execute to another source line, like the GDB @@samp{step} command. + +@@item M-n +Execute to next source line in this function, skipping all function +calls, like the GDB @@samp{next} command. + +@@item M-i +Execute one instruction, like the GDB @@samp{stepi} command. + +@@item M-u +Move up one stack frame (and display that frame's source file in +Emacs), like the GDB @@samp{up} command. + +@@item M-d +Move down one stack frame (and display that frame's source file in +Emacs), like the GDB @@samp{down} command. (This means that you cannot +delete words in the usual fashion in the GDB buffer; I am guessing you +won't often want to do that.) + +@@item C-c C-f +Execute until exit from the selected stack frame, like the GDB +@@samp{finish} command. +@@end table + +In any source file, the Emacs command @@kbd{C-x SPC} (@@code{gdb-break}) +tells GDB to set a breakpoint on the source line point is on. + +The source files displayed in Emacs are in ordinary Emacs buffers +which are visiting the source files in the usual way. You can edit +the files with these buffers if you wish; but keep in mind that GDB +communicates with Emacs in terms of line numbers. If you add or +delete lines from the text, the line numbers that GDB knows will cease +to correspond properly to the code. + +@@node Remote, Commands, Emacs, Top +@@chapter Remote Kernel Debugging + +GDB has a special facility for debugging a remote machine via a serial +connection. This can be used for kernel debugging. + +The program to be debugged on the remote machine needs to contain a +debugging device driver which talks to GDB over the serial line using the +protocol described below. The same version of GDB that is used ordinarily +can be used for this. + +@@menu +* Remote Commands:: Commands used to start and finish remote debugging. +@@end menu + +For details of the communication protocol, see the comments in the GDB +source file @@file{remote.c}. + +@@node Remote Commands,, Remote, Remote +@@section Commands for Remote Debugging + +To start remote debugging, first run GDB and specify as an executable file +the program that is running in the remote machine. This tells GDB how +to find the program's symbols and the contents of its pure text. Then +establish communication using the @@samp{attach} command with a device +name rather than a pid as an argument. For example: + +@@example +attach /dev/ttyd +@@end example + +@@noindent +if the serial line is connected to the device named @@file{/dev/ttyd}. This +will stop the remote machine if it is not already stopped. + +Now you can use all the usual commands to examine and change data and to +step and continue the remote program. + +To resume the remote program and stop debugging it, use the @@samp{detach} +command. + +@@node Commands, Concepts, Remote, Top +@@unnumbered Command Index + +@@printindex ky + +@@node Concepts,, Commands, Top +@@unnumbered Concept Index + +@@printindex cp + +@@contents +@@bye +@ + + +1.1 +log +@Initial revision +@ +text +@d617 3 +a619 2 +inferior. If you wish to evaluate a function simply for it's side +affects, you may use the @@samp{set} command. @@xref{Assignment}. +d1101 4 +a1104 3 +A condition is just a boolean expression in your programming language. +A breakpoint with a condition evaluates the expression each time the +program reaches it, and the program stops only if the condition is true. +d1126 1 +d1259 6 +a1264 5 +is a condition expression that will change @@code{x} as needed, then always +have the value 0 so the program will not stop. Loss of input is avoided +here because break conditions are evaluated without changing the terminal +modes. When you want to have nontrivial conditions for performing the side +effects, the operators @@samp{&&}, @@samp{||} and @@samp{?@@: @@dots{} :@@:} may be useful. +d1269 3 +a1271 3 +Under Unix, breakpoints cannot be used in a program if any other process +is running that program. Attempting to run or continue the program with +a breakpoint in this case will cause GDB to stop it. +d1875 2 +d2047 2 +a2048 1 +address of a byte of memory. +d2196 1 +a2196 1 +each time the program stops. +d2382 1 +a2382 1 +saved in ``extended'' format, but all C programs expect to work with +d2451 1 +d2544 1 +a2544 1 +For example, +d2628 3 +a2630 3 +no signal. This may be useful when the program has received a signal +and the @@samp{cont} command would allow the program to see that +signal. +d2691 1 +a2691 1 +command definition. After the @@samp{document} command is finished, +d2771 2 +a2772 1 +value history either. +@ diff --git a/gdb/RCS/gdbcore.h,v b/gdb/RCS/gdbcore.h,v new file mode 100644 index 0000000..872e5af9 --- /dev/null +++ b/gdb/RCS/gdbcore.h,v @@ -0,0 +1,105 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.02.09.23.23.12; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.22.43.14; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Create gdbcore.h with external variables that relate to core files. +@ +text +@/* Machine independent variables that describe the core file under GDB. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; +@ + + +1.1 +log +@Initial revision +@ +text +@d1 68 +@ diff --git a/gdb/RCS/inflow.c,v b/gdb/RCS/inflow.c,v new file mode 100644 index 0000000..972b615 --- /dev/null +++ b/gdb/RCS/inflow.c,v @@ -0,0 +1,636 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.03.27.20.12.35; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.09.23.23.40; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.22.28.04; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@General portability changes. Make various local terminal control +parameter processing #ifdef the particular IOCTL used to get them. +This handles various Sys V/Berkeley merges. Also avoid vfork +and <sys/fcntl.h>. +@ +text +@/* Low level interface to ptrace, for GDB when running under Unix. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#endif + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> + +#ifdef HAVE_TERMIO +#include <termio.h> +#undef TIOCGETP +#define TIOCGETP TCGETA +#undef TIOCSETN +#define TIOCSETN TCSETA +#undef TIOCSETP +#define TIOCSETP TCSETAF +#define TERMINAL struct termio +#else +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sgtty.h> +#define TERMINAL struct sgttyb +#endif + +#ifdef SET_STACK_LIMIT_HUGE +#include <sys/time.h> +#include <sys/resource.h> +extern int original_stack_limit; +#endif /* SET_STACK_LIMIT_HUGE */ + +extern int errno; + +/* Nonzero if we are debugging an attached outside process + rather than an inferior. */ + +int attach_flag; + + +/* Record terminal status separately for debugger and inferior. */ + +static TERMINAL sg_inferior; +static TERMINAL sg_ours; + +static int tflags_inferior; +static int tflags_ours; + +#ifdef TIOCGETC +static struct tchars tc_inferior; +static struct tchars tc_ours; +#endif + +#ifdef TIOCGLTC +static struct ltchars ltc_inferior; +static struct ltchars ltc_ours; +#endif /* TIOCGLTC */ + +#ifdef TIOCLGET +static int lmode_inferior; +static int lmode_ours; +#endif + +#ifdef TIOCGPGRP +static int pgrp_inferior; +static int pgrp_ours; +#else +static int (*sigint_ours) (); +static int (*sigquit_ours) (); +#endif /* TIOCGPGRP */ + +/* Copy of inferior_io_terminal when inferior was last started. */ +static char *inferior_thisrun_terminal; + +static void terminal_ours_1 (); + +/* Nonzero if our terminal settings are in effect. + Zero if the inferior's settings are in effect. */ +static int terminal_is_ours; + +/* Initialize the terminal settings we record for the inferior, + before we actually run the inferior. */ + +void +terminal_init_inferior () +{ + if (remote_debugging) + return; + + sg_inferior = sg_ours; + tflags_inferior = tflags_ours; + +#ifdef TIOCGETC + tc_inferior = tc_ours; +#endif + +#ifdef TIOCGLTC + ltc_inferior = ltc_ours; +#endif + +#ifdef TIOCLGET + lmode_inferior = lmode_ours; +#endif + +#ifdef TIOCGPGRP + pgrp_inferior = inferior_pid; +#endif /* TIOCGPGRP */ + + terminal_is_ours = 1; +} + +/* Put the inferior's terminal settings into effect. + This is preparation for starting or resuming the inferior. */ + +void +terminal_inferior () +{ + if (remote_debugging) + return; + + if (terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + fcntl (0, F_SETFL, tflags_inferior); + fcntl (0, F_SETFL, tflags_inferior); + ioctl (0, TIOCSETN, &sg_inferior); +#ifdef TIOCGETC + ioctl (0, TIOCSETC, &tc_inferior); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCSLTC, <c_inferior); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLSET, &lmode_inferior); +#endif + +#ifdef TIOCGPGRP + ioctl (0, TIOCSPGRP, &pgrp_inferior); +#else + sigint_ours = (int (*) ()) signal (SIGINT, SIG_IGN); + sigquit_ours = (int (*) ()) signal (SIGQUIT, SIG_IGN); +#endif /* TIOCGPGRP */ + } + terminal_is_ours = 0; +} + +/* Put some of our terminal settings into effect, + enough to get proper results from our output, + but do not change into or out of RAW mode + so that no input is discarded. + + After doing this, either terminal_ours or terminal_inferior + should be called to get back to a normal state of affairs. */ + +void +terminal_ours_for_output () +{ + if (remote_debugging) + return; + + terminal_ours_1 (1); +} + +/* Put our terminal settings into effect. + First record the inferior's terminal settings + so they can be restored properly later. */ + +void +terminal_ours () +{ + if (remote_debugging) + return; + + terminal_ours_1 (0); +} + +static void +terminal_ours_1 (output_only) + int output_only; +{ +#ifdef TIOCGPGRP + /* Ignore this signal since it will happen when we try to set the pgrp. */ + int (*osigttou) (); +#endif /* TIOCGPGRP */ + + if (!terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + terminal_is_ours = 1; + +#ifdef TIOCGPGRP + osigttou = signal (SIGTTOU, SIG_IGN); + + ioctl (0, TIOCGPGRP, &pgrp_inferior); + ioctl (0, TIOCSPGRP, &pgrp_ours); + + signal (SIGTTOU, osigttou); +#else + signal (SIGINT, sigint_ours); + signal (SIGQUIT, sigquit_ours); +#endif /* TIOCGPGRP */ + + tflags_inferior = fcntl (0, F_GETFL, 0); + ioctl (0, TIOCGETP, &sg_inferior); + +#ifdef TIOCGETC + ioctl (0, TIOCGETC, &tc_inferior); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCGLTC, <c_inferior); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLGET, &lmode_inferior); +#endif + } + +#ifdef HAVE_TERMIO + sg_ours.c_lflag |= ICANON; + if (output_only && !(sg_inferior.c_lflag & ICANON)) + sg_ours.c_lflag &= ~ICANON; +#else /* not HAVE_TERMIO */ + sg_ours.sg_flags &= ~RAW & ~CBREAK; + if (output_only) + sg_ours.sg_flags |= (RAW | CBREAK) & sg_inferior.sg_flags; +#endif /* not HAVE_TERMIO */ + + fcntl (0, F_SETFL, tflags_ours); + fcntl (0, F_SETFL, tflags_ours); + ioctl (0, TIOCSETN, &sg_ours); + +#ifdef TIOCGETC + ioctl (0, TIOCSETC, &tc_ours); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCSLTC, <c_ours); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLSET, &lmode_ours); +#endif + + +#ifdef HAVE_TERMIO + sg_ours.c_lflag |= ICANON; +#else /* not HAVE_TERMIO */ + sg_ours.sg_flags &= ~RAW & ~CBREAK; +#endif /* not HAVE_TERMIO */ +} + +static void +term_status_command () +{ + register int i; + + if (remote_debugging) + { + printf ("No terminal status when remote debugging.\n"); + return; + } + + printf ("Inferior's terminal status (currently saved by GDB):\n"); + +#ifdef HAVE_TERMIO + + printf ("fcntl flags = 0x%x, c_iflag = 0x%x, c_oflag = 0x%x,\n", + tflags_inferior, sg_inferior.c_iflag, sg_inferior.c_oflag); + printf ("c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n", + sg_inferior.c_cflag, sg_inferior.c_lflag, sg_inferior.c_line); + printf ("c_cc: "); + for (i = 0; (i < NCC); i += 1) + printf ("0x%x ", sg_inferior.c_cc[i]); + printf ("\n"); + +#else /* not HAVE_TERMIO */ + + printf ("fcntl flags = 0x%x, sgttyb.sg_flags = 0x%x, owner pid = %d.\n", + tflags_inferior, sg_inferior.sg_flags, pgrp_inferior); + +#endif /* not HAVE_TERMIO */ + +#ifdef TIOCGETC + printf ("tchars: "); + for (i = 0; i < sizeof (struct tchars); i++) + printf ("0x%x ", ((char *)&tc_inferior)[i]); + printf ("\n"); +#endif + +#ifdef TIOCGLTC + printf ("ltchars: "); + for (i = 0; i < sizeof (struct ltchars); i++) + printf ("0x%x ", ((char *)<c_inferior)[i]); + printf ("\n"); + ioctl (0, TIOCSLTC, <c_ours); +#endif + +#ifdef TIOCLGET + printf ("lmode: %x\n", lmode_inferior); +#endif +} + +static void +new_tty (ttyname) + char *ttyname; +{ + register int tty; + register int fd; + +#ifdef TIOCNOTTY + /* Disconnect the child process from our controlling terminal. */ + tty = open("/dev/tty", O_RDWR); + if (tty > 0) + { + ioctl(tty, TIOCNOTTY, 0); + close(tty); + } +#endif + + /* Now open the specified new terminal. */ + + tty = open(ttyname, O_RDWR); + if (tty == -1) + _exit(1); + + dup2(tty, 0); + dup2(tty, 1); + dup2(tty, 2); + close(tty); +} + +/* Start an inferior process and returns its pid. + ALLARGS is a string containing shell command to run the program. + ENV is the environment vector to pass. */ + +#ifndef SHELL_FILE +#define SHELL_FILE "/bin/sh" +#endif + +int +create_inferior (allargs, env) + char *allargs; + char **env; +{ + int pid; + char *shell_command; + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + + /* If desired, concat something onto the front of ALLARGS. + SHELL_COMMAND is the result. */ +#ifdef SHELL_COMMAND_CONCAT + shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + strlen (allargs) + 1); + strcpy (shell_command, SHELL_COMMAND_CONCAT); + strcat (shell_command, allargs); +#else + shell_command = allargs; +#endif + + /* exec is said to fail if the executable is open. */ + close_exec_file (); + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { +#ifdef TIOCGPGRP + /* Run inferior in a separate process group. */ + setpgrp (getpid (), getpid ()); +#endif /* TIOCGPGRP */ + +#ifdef SET_STACK_LIMIT_HUGE + /* Reset the stack limit back to what it was. */ + { + struct rlimit rlim; + + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = original_stack_limit; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* SET_STACK_LIMIT_HUGE */ + + + inferior_thisrun_terminal = inferior_io_terminal; + if (inferior_io_terminal != 0) + new_tty (inferior_io_terminal); + +/* Not needed on Sun, at least, and loses there + because it clobbers the superior. */ +/*??? signal (SIGQUIT, SIG_DFL); + signal (SIGINT, SIG_DFL); */ + + call_ptrace (0); + execle (SHELL_FILE, "sh", "-c", shell_command, 0, env); + + fprintf (stderr, "Cannot exec %s: %s.\n", SHELL_FILE, + errno < sys_nerr ? sys_errlist[errno] : "unknown error"); + fflush (stderr); + _exit (0177); + } + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +static void +kill_command () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + error ("The program is not being run."); + if (!query ("Kill the inferior process? ")) + error ("Not confirmed."); + kill_inferior (); +} + +void +inferior_died () +{ + inferior_pid = 0; + attach_flag = 0; + mark_breakpoints_out (); + select_frame ( (FRAME) 0, -1); + reopen_exec_file (); + if (have_core_file_p ()) + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); +} + +static void +try_writing_regs_command () +{ + register int i; + register int value; + extern int errno; + + if (inferior_pid == 0) + error ("There is no inferior process now."); + + for (i = 0; ; i += 2) + { + QUIT; + errno = 0; + value = call_ptrace (3, inferior_pid, i, 0); + call_ptrace (6, inferior_pid, i, value); + if (errno == 0) + { + printf (" Succeeded with address 0x%x; value 0x%x (%d).\n", + i, value, value); + } + else if ((i & 0377) == 0) + printf (" Failed at 0x%x.\n", i); + } +} + +void +_initialize_inflow () +{ + add_com ("term-status", class_obscure, term_status_command, + "Print info on inferior's saved terminal status."); + + add_com ("try-writing-regs", class_obscure, try_writing_regs_command, + "Try writing all locations in inferior's system block.\n\ +Report which ones can be written."); + + add_com ("kill", class_run, kill_command, + "Kill execution of program being debugged."); + + inferior_pid = 0; + + ioctl (0, TIOCGETP, &sg_ours); + fcntl (0, F_GETFL, tflags_ours); + +#ifdef TIOCGETC + ioctl (0, TIOCGETC, &tc_ours); +#endif +#ifdef TIOCGLTC + ioctl (0, TIOCGLTC, <c_ours); +#endif +#ifdef TIOCLGET + ioctl (0, TIOCLGET, &lmode_ours); +#endif + +#ifdef TIOCGPGRP + ioctl (0, TIOCGPGRP, &pgrp_ours); +#endif /* TIOCGPGRP */ + + terminal_is_ours = 1; +} + +@ + + +1.2 +log +@When the inferior process dies, deselect the current frame so that +the "where" ("backtrace") command will not think there's a stack. +@ +text +@d27 1 +a27 1 +#include <sys/fcntl.h> +a34 6 +/* May be unnecessary since many parts of inflow.c + have migrated to *-infdep.c */ +#ifdef USG +#include <sys/user.h> +#endif + +d73 1 +a73 1 +#ifdef TIOCGLTC +d76 3 +d81 3 +d86 1 +a86 1 +#endif /* TIOCGLTC */ +d117 4 +a121 1 + tc_inferior = tc_ours; +d123 3 +d127 1 +a127 1 +#endif /* TIOCGLTC */ +d150 3 +a153 1 + ioctl (0, TIOCSETC, &tc_inferior); +d155 2 +d158 1 +a158 1 +#endif /* TIOCGLTC */ +d228 3 +a231 1 + ioctl (0, TIOCGETC, &tc_inferior); +d233 2 +d236 1 +a236 1 +#endif /* TIOCGLTC */ +d253 3 +a256 1 + ioctl (0, TIOCSETC, &tc_ours); +d258 2 +d261 1 +a261 1 +#endif /* TIOCGLTC */ +d297 6 +a302 3 + printf ("fcntl flags = 0x%x, lmode = 0x%x,\nsgttyb.sg_flags = 0x%x, owner pid = %d.\n", + tflags_inferior, lmode_inferior, + sg_inferior.sg_flags, pgrp_inferior); +d307 3 +d314 2 +d317 3 +a319 1 +#endif /* not HAVE_TERMIO */ +d383 1 +a383 1 + pid = vfork (); +d385 1 +a385 1 + perror_with_name ("vfork"); +d497 3 +a500 1 + ioctl (0, TIOCGETC, &tc_ours); +d502 2 +d505 1 +a505 1 +#endif /* TIOCGLTC */ +@ + + +1.1 +log +@Initial revision +@ +text +@d418 1 +@ diff --git a/gdb/RCS/infrun.c,v b/gdb/RCS/infrun.c,v new file mode 100644 index 0000000..983922f --- /dev/null +++ b/gdb/RCS/infrun.c,v @@ -0,0 +1,1855 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.03.27.20.15.05; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.09.23.25.40; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.17.11.52; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@A/UX-specific change: define X_OK since Apple fucked it up. +@ +text +@/* Start and stop the inferior process, for GDB. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +/* Notes on the algorithm used in wait_for_inferior to determine if we + just did a subroutine call when stepping. We have the following + information at that point: + + Current and previous (just before this step) pc. + Current and previous sp. + Current and previous start of current function. + + If the start's of the functions don't match, then + + a) We did a subroutine call. + + In this case, the pc will be at the beginning of a function. + + b) We did a subroutine return. + + Otherwise. + + c) We did a longjmp. + + If we did a longjump, we were doing "nexti", since a next would + have attempted to skip over the assembly language routine in which + the longjmp is coded and would have simply been the equivalent of a + continue. I consider this ok behaivior. We'd like one of two + things to happen if we are doing a nexti through the longjmp() + routine: 1) It behaves as a stepi, or 2) It acts like a continue as + above. Given that this is a special case, and that anybody who + thinks that the concept of sub calls is meaningful in the context + of a longjmp, I'll take either one. Let's see what happens. + + Acts like a subroutine return. I can handle that with no problem + at all. + + -->So: If the current and previous beginnings of the current + function don't match, *and* the pc is at the start of a function, + we've done a subroutine call. If the pc is not at the start of a + function, we *didn't* do a subroutine call. + + -->If the beginnings of the current and previous function do match, + either: + + a) We just did a recursive call. + + In this case, we would be at the very beginning of a + function and 1) it will have a prologue (don't jump to + before prologue, or 2) (we assume here that it doesn't have + a prologue) there will have been a change in the stack + pointer over the last instruction. (Ie. it's got to put + the saved pc somewhere. The stack is the usual place. In + a recursive call a register is only an option if there's a + prologue to do something with it. This is even true on + register window machines; the prologue sets up the new + window. It might not be true on a register window machine + where the call instruction moved the register window + itself. Hmmm. One would hope that the stack pointer would + also change. If it doesn't, somebody send me a note, and + I'll work out a more general theory. + randy@@wheaties.ai.mit.edu). This is true (albeit slipperly + so) on all machines I'm aware of: + + m68k: Call changes stack pointer. Regular jumps don't. + + sparc: Recursive calls must have frames and therefor, + prologues. + + vax: All calls have frames and hence change the + stack pointer. + + b) We did a return from a recursive call. I don't see that we + have either the ability or the need to distinguish this + from an ordinary jump. The stack frame will be printed + when and if the frame pointer changes; if we are in a + function without a frame pointer, it's the users own + lookout. + + c) We did a jump within a function. We assume that this is + true if we didn't do a recursive call. + + d) We are in no-man's land ("I see no symbols here"). We + don't worry about this; it will make calls look like simple + jumps (and the stack frames will be printed when the frame + pointer moves), which is a reasonably non-violent response. + +#if 0 + We skip this; it causes more problems than it's worth. +#ifdef SUN4_COMPILER_FEATURE + We do a special ifdef for the sun 4, forcing it to single step + into calls which don't have prologues. This means that we can't + nexti over leaf nodes, we can probably next over them (since they + won't have debugging symbols, usually), and we can next out of + functions returning structures (with a "call .stret4" at the end). +#endif +#endif +*/ + + + + + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "inferior.h" +#include "wait.h" + +#include <stdio.h> +#include <signal.h> + +/* unistd.h is needed to #define X_OK */ +#ifdef USG +#include <unistd.h> +#else +#include <sys/file.h> +#endif + +/* The idiots at Apple only define X_OK if POSIX is defined. Fuck 'em. */ +#ifndef X_OK +#define X_OK 1 /* Execute permission for access() */ +#endif + +#ifdef UMAX_PTRACE +#include <sys/param.h> +#include <sys/ptrace.h> +#endif /* UMAX_PTRACE */ + +extern char *sys_siglist[]; +extern int errno; + +/* Tables of how to react to signals; the user sets them. */ + +static char signal_stop[NSIG]; +static char signal_print[NSIG]; +static char signal_program[NSIG]; + +/* Nonzero if breakpoints are now inserted in the inferior. */ + +static int breakpoints_inserted; + +/* Function inferior was in as of last step command. */ + +static struct symbol *step_start_function; + +/* This is the sequence of bytes we insert for a breakpoint. */ + +static char break_insn[] = BREAKPOINT; + +/* Nonzero => address for special breakpoint for resuming stepping. */ + +static CORE_ADDR step_resume_break_address; + +/* Original contents of the byte where the special breakpoint is. */ + +static char step_resume_break_shadow[sizeof break_insn]; + +/* Nonzero means the special breakpoint is a duplicate + so it has not itself been inserted. */ + +static int step_resume_break_duplicate; + +/* Nonzero if we are expecting a trace trap and should proceed from it. + 2 means expecting 2 trace traps and should continue both times. + That occurs when we tell sh to exec the program: we will get + a trap after the exec of sh and a second when the program is exec'd. */ + +static int trap_expected; + +/* Nonzero if the next time we try to continue the inferior, it will + step one instruction and generate a spurious trace trap. + This is used to compensate for a bug in HP-UX. */ + +static int trap_expected_after_continue; + +/* Nonzero means expecting a trace trap + and should stop the inferior and return silently when it happens. */ + +int stop_after_trap; + +/* Nonzero means expecting a trace trap due to attaching to a process. */ + +int stop_after_attach; + +/* Nonzero if pc has been changed by the debugger + since the inferior stopped. */ + +int pc_changed; + +/* Nonzero if debugging a remote machine via a serial link or ethernet. */ + +int remote_debugging; + +/* Save register contents here when about to pop a stack dummy frame. */ + +char stop_registers[REGISTER_BYTES]; + +/* Nonzero if program stopped due to error trying to insert breakpoints. */ + +static int breakpoints_failed; + +/* Nonzero if inferior is in sh before our program got exec'd. */ + +static int running_in_shell; + +/* Nonzero after stop if current stack frame should be printed. */ + +static int stop_print_frame; + +#ifdef NO_SINGLE_STEP +extern int one_stepped; /* From machine dependent code */ +extern void single_step (); /* Same. */ +#endif /* NO_SINGLE_STEP */ + +static void insert_step_breakpoint (); +static void remove_step_breakpoint (); +static void wait_for_inferior (); +static void normal_stop (); + + +/* Clear out all variables saying what to do when inferior is continued. + First do this, then set the ones you want, then call `proceed'. */ + +void +clear_proceed_status () +{ + trap_expected = 0; + step_range_start = 0; + step_range_end = 0; + step_frame_address = 0; + step_over_calls = -1; + step_resume_break_address = 0; + stop_after_trap = 0; + stop_after_attach = 0; + + /* Discard any remaining commands left by breakpoint we had stopped at. */ + clear_breakpoint_commands (); +} + +/* Basic routine for continuing the program in various fashions. + + ADDR is the address to resume at, or -1 for resume where stopped. + SIGNAL is the signal to give it, or 0 for none, + or -1 for act according to how it stopped. + STEP is nonzero if should trap after one instruction. + -1 means return after that and print nothing. + You should probably set various step_... variables + before calling here, if you are stepping. + + You should call clear_proceed_status before calling proceed. */ + +void +proceed (addr, signal, step) + CORE_ADDR addr; + int signal; + int step; +{ + int oneproc = 0; + + if (step > 0) + step_start_function = find_pc_function (read_pc ()); + if (step < 0) + stop_after_trap = 1; + + if (addr == -1) + { + /* If there is a breakpoint at the address we will resume at, + step one instruction before inserting breakpoints + so that we do not stop right away. */ + + if (!pc_changed && breakpoint_here_p (read_pc ())) + oneproc = 1; + } + else + { + write_register (PC_REGNUM, addr); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, addr + 4); +#endif + } + + if (trap_expected_after_continue) + { + /* If (step == 0), a trap will be automatically generated after + the first instruction is executed. Force step one + instruction to clear this condition. This should not occur + if step is nonzero, but it is harmless in that case. */ + oneproc = 1; + trap_expected_after_continue = 0; + } + + if (oneproc) + /* We will get a trace trap after one instruction. + Continue it automatically and insert breakpoints then. */ + trap_expected = 1; + else + { + int temp = insert_breakpoints (); + if (temp) + { + print_sys_errmsg ("ptrace", temp); + error ("Cannot insert breakpoints.\n\ +The same program may be running in another process."); + } + breakpoints_inserted = 1; + } + + /* Install inferior's terminal modes. */ + terminal_inferior (); + + if (signal >= 0) + stop_signal = signal; + /* If this signal should not be seen by program, + give it zero. Used for debugging signals. */ + else if (stop_signal < NSIG && !signal_program[stop_signal]) + stop_signal= 0; + + /* Resume inferior. */ + resume (oneproc || step, stop_signal); + + /* Wait for it to stop (if not standalone) + and in any case decode why it stopped, and act accordingly. */ + + wait_for_inferior (); + normal_stop (); +} + +/* Writing the inferior pc as a register calls this function + to inform infrun that the pc has been set in the debugger. */ + +void +writing_pc (val) + CORE_ADDR val; +{ + stop_pc = val; + pc_changed = 1; +} + +/* Start an inferior process for the first time. + Actually it was started by the fork that created it, + but it will have stopped one instruction after execing sh. + Here we must get it up to actual execution of the real program. */ + +void +start_inferior () +{ + /* We will get a trace trap after one instruction. + Continue it automatically. Eventually (after shell does an exec) + it will get another trace trap. Then insert breakpoints and continue. */ + +#ifdef START_INFERIOR_TRAPS_EXPECTED + trap_expected = START_INFERIOR_TRAPS_EXPECTED; +#else + trap_expected = 2; +#endif + + running_in_shell = 0; /* Set to 1 at first SIGTRAP, 0 at second. */ + trap_expected_after_continue = 0; + breakpoints_inserted = 0; + mark_breakpoints_out (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + terminal_init_inferior (); + + /* Install inferior's terminal modes. */ + terminal_inferior (); + + if (remote_debugging) + { + trap_expected = 0; + fetch_inferior_registers(); + set_current_frame (create_new_frame (read_register (FP_REGNUM), + read_pc ())); + stop_frame_address = FRAME_FP (get_current_frame()); + inferior_pid = 3; + if (insert_breakpoints()) + fatal("Can't insert breakpoints"); + breakpoints_inserted = 1; + proceed(-1, -1, 0); + } + else + { + wait_for_inferior (); + normal_stop (); + } +} + +/* Start remote-debugging of a machine over a serial link. */ + +void +start_remote () +{ + clear_proceed_status (); + running_in_shell = 0; + trap_expected = 0; + inferior_pid = 3; + breakpoints_inserted = 0; + mark_breakpoints_out (); + wait_for_inferior (); + normal_stop(); +} + +#ifdef ATTACH_DETACH + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ + +void +attach_program (pid) + int pid; +{ + attach (pid); + inferior_pid = pid; + + mark_breakpoints_out (); + terminal_init_inferior (); + clear_proceed_status (); + stop_after_attach = 1; + /*proceed (-1, 0, -2);*/ + wait_for_inferior (); + normal_stop (); +} +#endif /* ATTACH_DETACH */ + +/* Wait for control to return from inferior to debugger. + If inferior gets a signal, we may decide to start it up again + instead of returning. That is why there is a loop in this function. + When this function actually returns it means the inferior + should be left stopped and GDB should read more commands. */ + +static void +wait_for_inferior () +{ + register int pid; + WAITTYPE w; + CORE_ADDR pc; + int tem; + int another_trap; + int random_signal; + CORE_ADDR stop_sp, prev_sp; + CORE_ADDR prev_func_start, stop_func_start; + CORE_ADDR prologue_pc; + int stop_step_resume_break; + CORE_ADDR step_resume_break_sp; + int newmisc; + int newfun_pc; + struct symbol *newfun; + struct symtab_and_line sal; + int prev_pc; + extern CORE_ADDR text_end; + + prev_pc = read_pc (); + prev_func_start = get_pc_function_start (prev_pc) + FUNCTION_START_OFFSET; + prev_sp = read_register (SP_REGNUM); + + while (1) + { + /* Clean up saved state that will become invalid */ + pc_changed = 0; + flush_cached_frames (); + + if (remote_debugging) + remote_wait (&w); + else + { + pid = wait (&w); + if (pid != inferior_pid) + continue; + } + + /* See if the process still exists; clean up if it doesn't. */ + if (WIFEXITED (w)) + { + terminal_ours_for_output (); + if (WRETCODE (w)) + printf ("\nProgram exited with code 0%o.\n", WRETCODE (w)); + else + printf ("\nProgram exited normally.\n"); + fflush (stdout); + inferior_died (); +#ifdef NO_SINGLE_STEP + one_stepped = 0; /* Clear single_step state since proc gone */ +#endif /* NO_SINGLE_STEP */ + stop_print_frame = 0; + break; + } + else if (!WIFSTOPPED (w)) + { + kill_inferior (); + stop_print_frame = 0; + stop_signal = WTERMSIG (w); + terminal_ours_for_output (); + printf ("\nProgram terminated with signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + printf ("The inferior process no longer exists.\n"); + fflush (stdout); +#ifdef NO_SINGLE_STEP + one_stepped = 0; /* Clear single_step state since proc gone */ +#endif /* NO_SINGLE_STEP */ + break; + } + +#ifdef NO_SINGLE_STEP + if (one_stepped) + single_step (0); /* This actually cleans up the ss */ +#endif /* NO_SINGLE_STEP */ + + fetch_inferior_registers (); + stop_pc = read_pc (); + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); +#ifdef CONVEX_PTRACE + /* pop frame stored by user-mode trap, if present */ + if (stop_pc == BREAK_TRAP_ADDR) + { + POP_FRAME; + stop_pc = read_pc () - 2; + write_register (PC_REGNUM, stop_pc); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, stop_pc + 4); +#endif + pc_changed = 0; + } + else if (stop_pc > STACK_END_ADDR) + { + POP_FRAME; + stop_pc = read_pc (); + } +#endif /* CONVEX_PTRACE */ + stop_frame_address = FRAME_FP (get_current_frame ()); + stop_sp = read_register (SP_REGNUM); + stop_func_start = + get_pc_function_start (stop_pc) + FUNCTION_START_OFFSET; + another_trap = 0; + stop_breakpoint = 0; + stop_step = 0; + stop_stack_dummy = 0; + stop_print_frame = 1; + stop_step_resume_break = 0; + random_signal = 0; + stopped_by_random_signal = 0; + breakpoints_failed = 0; + + /* Look at the cause of the stop, and decide what to do. + The alternatives are: + 1) break; to really stop and return to the debugger, + 2) drop through to start up again + (set another_trap to 1 to single step once) + 3) set random_signal to 1, and the decision between 1 and 2 + will be made according to the signal handling tables. */ + + stop_signal = WSTOPSIG (w); + + /* First, distinguish signals caused by the debugger from signals + that have to do with the program's own actions. + Note that breakpoint insns may cause SIGTRAP or SIGILL + or SIGEMT, depending on the operating system version. + Here we detect when a SIGILL or SIGEMT is really a breakpoint + and change it to SIGTRAP. */ + + if (stop_signal == SIGTRAP +#ifndef CONVEX_PTRACE + || (breakpoints_inserted && + (stop_signal == SIGILL + || stop_signal == SIGEMT)) +#endif /* not CONVEX_PTRACE */ + || stop_after_attach) + { + if (stop_signal == SIGTRAP && stop_after_trap) + { + stop_print_frame = 0; + break; + } + if (stop_after_attach) + break; + /* Don't even think about breakpoints + if still running the shell that will exec the program + or if just proceeded over a breakpoint. */ + if (stop_signal == SIGTRAP && trap_expected) + stop_breakpoint = 0; + else + { + /* See if there is a breakpoint at the current PC. */ +#if DECR_PC_AFTER_BREAK + /* Notice the case of stepping through a jump + that leads just after a breakpoint. + Don't confuse that with hitting the breakpoint. + What we check for is that 1) stepping is going on + and 2) the pc before the last insn does not match + the address of the breakpoint before the current pc. */ + if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && step_range_end && !step_resume_break_address)) +#endif /* DECR_PC_AFTER_BREAK not zero */ + { + /* For condition exprs. */ + select_frame (get_current_frame (), 0); + stop_breakpoint = + breakpoint_stop_status (stop_pc, stop_frame_address); + /* Following in case break condition called a + function. */ + stop_print_frame = 1; + if (stop_breakpoint && DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); +#ifdef NPC_REGNUM + write_register (NPC_REGNUM, stop_pc + 4); +#endif + pc_changed = 0; + } + } + /* See if we stopped at the special breakpoint for + stepping over a subroutine call. */ + if (stop_pc - DECR_PC_AFTER_BREAK + == step_resume_break_address) + { + stop_step_resume_break = 1; + if (DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); + pc_changed = 0; + } + } + } + + if (stop_signal == SIGTRAP) + random_signal + = !(stop_breakpoint || trap_expected + || stop_step_resume_break +#ifndef CONVEX_PTRACE + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#else + || stop_pc == text_end - 2 +#endif + || (step_range_end && !step_resume_break_address)); + else + { + random_signal + = !(stop_breakpoint + || stop_step_resume_break +#ifdef news800 + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#endif + + ); + if (!random_signal) + stop_signal = SIGTRAP; + } + } + else + random_signal = 1; + + /* For the program's own signals, act according to + the signal handling tables. */ + + if (random_signal + && !(running_in_shell && stop_signal == SIGSEGV)) + { + /* Signal not for debugging purposes. */ + int printed = 0; + + stopped_by_random_signal = 1; + + if (stop_signal >= NSIG + || signal_print[stop_signal]) + { + printed = 1; + terminal_ours_for_output (); + printf ("\nProgram received signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + fflush (stdout); + } + if (stop_signal >= NSIG + || signal_stop[stop_signal]) + break; + /* If not going to stop, give terminal back + if we took it away. */ + else if (printed) + terminal_inferior (); + } + + /* Handle cases caused by hitting a breakpoint. */ + + if (!random_signal + && (stop_breakpoint || stop_step_resume_break)) + { + /* Does a breakpoint want us to stop? */ + if (stop_breakpoint && stop_breakpoint != -1 + && stop_breakpoint != -0x1000001) + { + /* 0x1000000 is set in stop_breakpoint as returned by + breakpoint_stop_status to indicate a silent + breakpoint. */ + if ((stop_breakpoint > 0 ? stop_breakpoint : + -stop_breakpoint) + & 0x1000000) + { + stop_print_frame = 0; + if (stop_breakpoint > 0) + stop_breakpoint -= 0x1000000; + else + stop_breakpoint += 0x1000000; + } + break; + } + /* But if we have hit the step-resumption breakpoint, + remove it. It has done its job getting us here. + The sp test is to make sure that we don't get hung + up in recursive calls in functions without frame + pointers. If the stack pointer isn't outside of + where the breakpoint was set (within a routine to be + stepped over), we're in the middle of a recursive + call. Not true for reg window machines (sparc) + because the must change frames to call things and + the stack pointer doesn't have to change if it + the bp was set in a routine without a frame (pc can + be stored in some other window). + + The removal of the sp test is to allow calls to + alloca. Nasty things were happening. Oh, well, + gdb can only handle one level deep of lack of + frame pointer. */ + if (stop_step_resume_break + && (step_frame_address == 0 + || (stop_frame_address == step_frame_address +#if 0 +#ifndef HAVE_REGISTER_WINDOWS + && step_resume_break_sp INNER_THAN stop_sp +#endif +#endif + ))) + { + remove_step_breakpoint (); + step_resume_break_address = 0; + } + /* Otherwise, must remove breakpoints and single-step + to get us past the one we hit. */ + else + { + remove_breakpoints (); + remove_step_breakpoint (); + breakpoints_inserted = 0; + another_trap = 1; + } + + /* We come here if we hit a breakpoint but should not + stop for it. Possibly we also were stepping + and should stop for that. So fall through and + test for stepping. But, if not stepping, + do not stop. */ + } + + /* If this is the breakpoint at the end of a stack dummy, + just stop silently. */ +#ifndef CONVEX_PTRACE + if (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +#else + /* "stack" dummy must be in text segment for Convex Unix */ + if (stop_pc == text_end - 2) +#endif + { + stop_print_frame = 0; + stop_stack_dummy = 1; +#ifdef HP9K320 + trap_expected_after_continue = 1; +#endif + break; + } + + if (step_resume_break_address) + /* Having a step-resume breakpoint overrides anything + else having to do with stepping commands until + that breakpoint is reached. */ + ; + /* If stepping through a line, keep going if still within it. */ + else if (!random_signal + && step_range_end + && stop_pc >= step_range_start + && stop_pc < step_range_end + /* The step range might include the start of the + function, so if we are at the start of the + step range and either the stack or frame pointers + just changed, we've stepped outside */ + && !(stop_pc == step_range_start + && stop_frame_address + && (stop_sp != prev_sp + || stop_frame_address != step_frame_address))) + { + /* Don't step through the return from a function + unless that is the first instruction stepped through. */ + if (ABOUT_TO_RETURN (stop_pc)) + { + stop_step = 1; + break; + } + } + + /* We stepped out of the stepping range. See if that was due + to a subroutine call that we should proceed to the end of. */ + else if (!random_signal && step_range_end) + { + if (stop_func_start) + { + prologue_pc = stop_func_start; + SKIP_PROLOGUE (prologue_pc); + } + + /* ==> See comments at top of file on this algorithm. <==*/ + + if (stop_pc == stop_func_start + && (stop_func_start != prev_func_start + || prologue_pc != stop_func_start + || stop_sp != prev_sp)) + { + newfun = find_pc_function (stop_pc); + /* It's a subroutine call */ + if (step_over_calls > 0 || (step_over_calls && newfun == 0)) + { + /* A subroutine call has happened. */ + /* Set a special breakpoint after the return */ + step_resume_break_address = + SAVED_PC_AFTER_CALL (get_current_frame ()); + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + step_resume_break_sp = stop_sp; + if (breakpoints_inserted) + insert_step_breakpoint (); + } + /* Subroutine call with source code we should not step over. + Do step to the first line of code in it. */ + else if (step_over_calls) + { + SKIP_PROLOGUE (stop_func_start); + sal = find_pc_line (stop_func_start, 0); + /* Use the step_resume_break to step until + the end of the prologue, even if that involves jumps + (as it seems to on the vax under 4.2). */ + /* If the prologue ends in the middle of a source line, + continue to the end of that source line. + Otherwise, just go to end of prologue. */ +#ifdef convex + /* no, don't either. It skips any code that's + legitimately on the first line. */ +#else + if (sal.end && sal.pc != stop_func_start) + stop_func_start = sal.end; +#endif + + if (stop_func_start == stop_pc) + { + /* We are already there: stop now. */ + stop_step = 1; + break; + } + else + /* Put the step-breakpoint there and go until there. */ + { + step_resume_break_address = stop_func_start; + step_resume_break_sp = stop_sp; + + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + if (breakpoints_inserted) + insert_step_breakpoint (); + /* Do not specify what the fp should be when we stop + since on some machines the prologue + is where the new fp value is established. */ + step_frame_address = 0; + /* And make sure stepping stops right away then. */ + step_range_end = step_range_start; + } + } + else + { + /* We get here only if step_over_calls is 0 and we + just stepped into a subroutine. I presume + that step_over_calls is only 0 when we're + supposed to be stepping at the assembly + language level.*/ + stop_step = 1; + break; + } + } + /* No subroutince call; stop now. */ + else + { + stop_step = 1; + break; + } + } + + /* Save the pc before execution, to compare with pc after stop. */ + prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */ + prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER + BREAK is defined, the + original pc would not have + been at the start of a + function. */ + prev_sp = stop_sp; + + /* If we did not do break;, it means we should keep + running the inferior and not return to debugger. */ + + /* If trap_expected is 2, it means continue once more + and insert breakpoints at the next trap. + If trap_expected is 1 and the signal was SIGSEGV, it means + the shell is doing some memory allocation--just resume it + with SIGSEGV. + Otherwise insert breakpoints now, and possibly single step. */ + + if (trap_expected > 1) + { + trap_expected--; + running_in_shell = 1; + resume (0, 0); + } + else if (running_in_shell && stop_signal == SIGSEGV) + { + resume (0, SIGSEGV); + } + else + { + /* Here, we are not awaiting another exec to get + the program we really want to debug. + Insert breakpoints now, unless we are trying + to one-proceed past a breakpoint. */ + running_in_shell = 0; + if (!breakpoints_inserted && !another_trap) + { + insert_step_breakpoint (); + breakpoints_failed = insert_breakpoints (); + if (breakpoints_failed) + break; + breakpoints_inserted = 1; + } + + trap_expected = another_trap; + + if (stop_signal == SIGTRAP) + stop_signal = 0; + + resume ((step_range_end && !step_resume_break_address) + || trap_expected, + stop_signal); + } + } +} + +/* Here to return control to GDB when the inferior stops for real. + Print appropriate messages, remove breakpoints, give terminal our modes. + + RUNNING_IN_SHELL nonzero means the shell got a signal before + exec'ing the program we wanted to run. + STOP_PRINT_FRAME nonzero means print the executing frame + (pc, function, args, file, line number and line text). + BREAKPOINTS_FAILED nonzero means stop was due to error + attempting to insert breakpoints. */ + +/* FIXME, normal_stop is ALWAYS called immediately after wait_for_inferior. + They should probably be merged into a single function, since that + would avoid numerous tests (e.g. of inferior_pid). */ + +static void +normal_stop () +{ + /* Make sure that the current_frame's pc is correct. This + is a correction for setting up the frame info before doing + DECR_PC_AFTER_BREAK */ + if (inferior_pid) + (get_current_frame ())->pc = read_pc (); + + if (breakpoints_failed) + { + terminal_ours_for_output (); + print_sys_errmsg ("ptrace", breakpoints_failed); + printf ("Stopped; cannot insert breakpoints.\n\ +The same program may be running in another process.\n"); + } + + if (inferior_pid) + remove_step_breakpoint (); + + if (inferior_pid && breakpoints_inserted) + if (remove_breakpoints ()) + { + terminal_ours_for_output (); + printf ("Cannot remove breakpoints because program is no longer writable.\n\ +It must be running in another process.\n\ +Further execution is probably impossible.\n"); + } + + breakpoints_inserted = 0; + + /* Delete the breakpoint we stopped at, if it wants to be deleted. + Delete any breakpoint that is to be deleted at the next stop. */ + + breakpoint_auto_delete (stop_breakpoint); + + /* If an auto-display called a function and that got a signal, + delete that auto-display to avoid an infinite recursion. */ + + if (stopped_by_random_signal) + delete_current_display (); + + if (step_multi && stop_step) + return; + + terminal_ours (); + + if (running_in_shell) + { + if (stop_signal == SIGSEGV) + { + char *exec_file = (char *) get_exec_file (1); + + if (access (exec_file, X_OK) != 0) + printf ("The file \"%s\" is not executable.\n", exec_file); + else + printf ("\ +You have just encountered a bug in \"sh\". GDB starts your program\n\ +by running \"sh\" with a command to exec your program.\n\ +This is so that \"sh\" will process wildcards and I/O redirection.\n\ +This time, \"sh\" crashed.\n\ +\n\ +One known bug in \"sh\" bites when the environment takes up a lot of space.\n\ +Try \"info env\" to see the environment; then use \"unset-env\" to kill\n\ +some variables whose values are large; then do \"run\" again.\n\ +\n\ +If that works, you might want to put those \"unset-env\" commands\n\ +into a \".gdbinit\" file in this directory so they will happen every time.\n"); + } + /* Don't confuse user with his program's symbols on sh's data. */ + stop_print_frame = 0; + } + + if (inferior_pid == 0) + return; + + /* Select innermost stack frame except on return from a stack dummy routine, + or if the program has exited. */ + if (!stop_stack_dummy) + { + select_frame (get_current_frame (), 0); + + if (stop_print_frame) + { + if (stop_breakpoint > 0) + printf ("\nBpt %d, ", stop_breakpoint); + print_sel_frame (stop_step + && step_frame_address == stop_frame_address + && step_start_function == find_pc_function (stop_pc)); + /* Display the auto-display expressions. */ + do_displays (); + } + } + + /* Save the function value return registers + We might be about to restore their previous contents. */ + read_register_bytes (0, stop_registers, REGISTER_BYTES); + + if (stop_stack_dummy) + { + /* Pop the empty frame that contains the stack dummy. + POP_FRAME ends with a setting of the current frame, so we + can use that next. */ + POP_FRAME; + select_frame (get_current_frame (), 0); + } +} + +static void +insert_step_breakpoint () +{ + if (step_resume_break_address && !step_resume_break_duplicate) + { + read_memory (step_resume_break_address, + step_resume_break_shadow, sizeof break_insn); + write_memory (step_resume_break_address, + break_insn, sizeof break_insn); + } +} + +static void +remove_step_breakpoint () +{ + if (step_resume_break_address && !step_resume_break_duplicate) + write_memory (step_resume_break_address, step_resume_break_shadow, + sizeof break_insn); +} + +/* Specify how various signals in the inferior should be handled. */ + +static void +handle_command (args, from_tty) + char *args; + int from_tty; +{ + register char *p = args; + int signum = 0; + register int digits, wordlen; + + if (!args) + error_no_arg ("signal to handle"); + + while (*p) + { + /* Find the end of the next word in the args. */ + for (wordlen = 0; p[wordlen] && p[wordlen] != ' ' && p[wordlen] != '\t'; + wordlen++); + for (digits = 0; p[digits] >= '0' && p[digits] <= '9'; digits++); + + /* If it is all digits, it is signal number to operate on. */ + if (digits == wordlen) + { + signum = atoi (p); + if (signum <= 0 || signum >= NSIG) + { + p[wordlen] = '\0'; + error ("Invalid signal %s given as argument to \"handle\".", p); + } + if (signum == SIGTRAP || signum == SIGINT) + { + if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum)) + error ("Not confirmed."); + } + } + else if (signum == 0) + error ("First argument is not a signal number."); + + /* Else, if already got a signal number, look for flag words + saying what to do for it. */ + else if (!strncmp (p, "stop", wordlen)) + { + signal_stop[signum] = 1; + signal_print[signum] = 1; + } + else if (wordlen >= 2 && !strncmp (p, "print", wordlen)) + signal_print[signum] = 1; + else if (wordlen >= 2 && !strncmp (p, "pass", wordlen)) + signal_program[signum] = 1; + else if (!strncmp (p, "ignore", wordlen)) + signal_program[signum] = 0; + else if (wordlen >= 3 && !strncmp (p, "nostop", wordlen)) + signal_stop[signum] = 0; + else if (wordlen >= 4 && !strncmp (p, "noprint", wordlen)) + { + signal_print[signum] = 0; + signal_stop[signum] = 0; + } + else if (wordlen >= 4 && !strncmp (p, "nopass", wordlen)) + signal_program[signum] = 0; + else if (wordlen >= 3 && !strncmp (p, "noignore", wordlen)) + signal_program[signum] = 1; + /* Not a number and not a recognized flag word => complain. */ + else + { + p[wordlen] = 0; + error ("Unrecognized flag word: \"%s\".", p); + } + + /* Find start of next word. */ + p += wordlen; + while (*p == ' ' || *p == '\t') p++; + } + + if (from_tty) + { + /* Show the results. */ + printf ("Number\tStop\tPrint\tPass to program\tDescription\n"); + printf ("%d\t", signum); + printf ("%s\t", signal_stop[signum] ? "Yes" : "No"); + printf ("%s\t", signal_print[signum] ? "Yes" : "No"); + printf ("%s\t\t", signal_program[signum] ? "Yes" : "No"); + printf ("%s\n", sys_siglist[signum]); + } +} + +/* Print current contents of the tables set by the handle command. */ + +static void +signals_info (signum_exp) + char *signum_exp; +{ + register int i; + printf ("Number\tStop\tPrint\tPass to program\tDescription\n"); + + if (signum_exp) + { + i = parse_and_eval_address (signum_exp); + printf ("%d\t", i); + printf ("%s\t", signal_stop[i] ? "Yes" : "No"); + printf ("%s\t", signal_print[i] ? "Yes" : "No"); + printf ("%s\t\t", signal_program[i] ? "Yes" : "No"); + printf ("%s\n", sys_siglist[i]); + return; + } + + printf ("\n"); + for (i = 0; i < NSIG; i++) + { + QUIT; + if (i > 0 && i % 16 == 0) + { + printf ("[Type Return to see more]"); + fflush (stdout); + gdb_read_line (0, 0); + } + printf ("%d\t", i); + printf ("%s\t", signal_stop[i] ? "Yes" : "No"); + printf ("%s\t", signal_print[i] ? "Yes" : "No"); + printf ("%s\t\t", signal_program[i] ? "Yes" : "No"); + printf ("%s\n", sys_siglist[i]); + } + + printf ("\nUse the \"handle\" command to change these tables.\n"); +} + +/* Save all of the information associated with the inferior<==>gdb + connection. INF_STATUS is a pointer to a "struct inferior_status" + (defined in inferior.h). */ + +struct command_line *get_breakpoint_commands (); + +void +save_inferior_status (inf_status, restore_stack_info) + struct inferior_status *inf_status; + int restore_stack_info; +{ + inf_status->pc_changed = pc_changed; + inf_status->stop_signal = stop_signal; + inf_status->stop_pc = stop_pc; + inf_status->stop_frame_address = stop_frame_address; + inf_status->stop_breakpoint = stop_breakpoint; + inf_status->stop_step = stop_step; + inf_status->stop_stack_dummy = stop_stack_dummy; + inf_status->stopped_by_random_signal = stopped_by_random_signal; + inf_status->trap_expected = trap_expected; + inf_status->step_range_start = step_range_start; + inf_status->step_range_end = step_range_end; + inf_status->step_frame_address = step_frame_address; + inf_status->step_over_calls = step_over_calls; + inf_status->step_resume_break_address = step_resume_break_address; + inf_status->stop_after_trap = stop_after_trap; + inf_status->stop_after_attach = stop_after_attach; + inf_status->breakpoint_commands = get_breakpoint_commands (); + inf_status->restore_stack_info = restore_stack_info; + + bcopy (stop_registers, inf_status->stop_registers, REGISTER_BYTES); + + record_selected_frame (&(inf_status->selected_frame_address), + &(inf_status->selected_level)); + return; +} + +void +restore_inferior_status (inf_status) + struct inferior_status *inf_status; +{ + FRAME fid; + int level = inf_status->selected_level; + + pc_changed = inf_status->pc_changed; + stop_signal = inf_status->stop_signal; + stop_pc = inf_status->stop_pc; + stop_frame_address = inf_status->stop_frame_address; + stop_breakpoint = inf_status->stop_breakpoint; + stop_step = inf_status->stop_step; + stop_stack_dummy = inf_status->stop_stack_dummy; + stopped_by_random_signal = inf_status->stopped_by_random_signal; + trap_expected = inf_status->trap_expected; + step_range_start = inf_status->step_range_start; + step_range_end = inf_status->step_range_end; + step_frame_address = inf_status->step_frame_address; + step_over_calls = inf_status->step_over_calls; + step_resume_break_address = inf_status->step_resume_break_address; + stop_after_trap = inf_status->stop_after_trap; + stop_after_attach = inf_status->stop_after_attach; + set_breakpoint_commands (inf_status->breakpoint_commands); + + bcopy (inf_status->stop_registers, stop_registers, REGISTER_BYTES); + + if (inf_status->restore_stack_info) + { + fid = find_relative_frame (get_current_frame (), + &level); + + if (FRAME_FP (fid) != inf_status->selected_frame_address || + level != 0) + { + fprintf (stderr, "Unable to restore previously selected frame.\n"); + select_frame (get_current_frame (), 0); + return; + } + + select_frame (fid, inf_status->selected_level); + } + return; +} + + +void +_initialize_infrun () +{ + register int i; + + add_info ("signals", signals_info, + "What debugger does when program gets various signals.\n\ +Specify a signal number as argument to print info on that signal only."); + + add_com ("handle", class_run, handle_command, + "Specify how to handle a signal.\n\ +Args are signal number followed by flags.\n\ +Flags allowed are \"stop\", \"print\", \"pass\",\n\ + \"nostop\", \"noprint\" or \"nopass\".\n\ +Print means print a message if this signal happens.\n\ +Stop means reenter debugger if this signal happens (implies print).\n\ +Pass means let program see this signal; otherwise program doesn't know.\n\ +Pass and Stop may be combined."); + + for (i = 0; i < NSIG; i++) + { + signal_stop[i] = 1; + signal_print[i] = 1; + signal_program[i] = 1; + } + + /* Signals caused by debugger's own actions + should not be given to the program afterwards. */ + signal_program[SIGTRAP] = 0; + signal_program[SIGINT] = 0; + + /* Signals that are not errors should not normally enter the debugger. */ +#ifdef SIGALRM + signal_stop[SIGALRM] = 0; + signal_print[SIGALRM] = 0; +#endif /* SIGALRM */ +#ifdef SIGVTALRM + signal_stop[SIGVTALRM] = 0; + signal_print[SIGVTALRM] = 0; +#endif /* SIGVTALRM */ +#ifdef SIGPROF + signal_stop[SIGPROF] = 0; + signal_print[SIGPROF] = 0; +#endif /* SIGPROF */ +#ifdef SIGCHLD + signal_stop[SIGCHLD] = 0; + signal_print[SIGCHLD] = 0; +#endif /* SIGCHLD */ +#ifdef SIGCLD + signal_stop[SIGCLD] = 0; + signal_print[SIGCLD] = 0; +#endif /* SIGCLD */ +#ifdef SIGIO + signal_stop[SIGIO] = 0; + signal_print[SIGIO] = 0; +#endif /* SIGIO */ +#ifdef SIGURG + signal_stop[SIGURG] = 0; + signal_print[SIGURG] = 0; +#endif /* SIGURG */ +} + +@ + + +1.2 +log +@Avoid accessing inferior process if it just exited or terminated with +a signal. Clean up stack frame stuff in that case too, so that the +various stack commands ("i frame", "up", "frame", "where") don't get +confused. +@ +text +@d137 5 +@ + + +1.1 +log +@Initial revision +@ +text +@d472 4 +d485 35 +a524 2 + pc_changed = 0; + flush_cached_frames (); +d569 1 +a569 30 + if (WIFEXITED (w)) + { + terminal_ours_for_output (); + if (WRETCODE (w)) + printf ("\nProgram exited with code 0%o.\n", WRETCODE (w)); + else + printf ("\nProgram exited normally.\n"); + fflush (stdout); + inferior_died (); + stop_print_frame = 0; + break; + } + else if (!WIFSTOPPED (w)) + { + kill_inferior (); + stop_print_frame = 0; + stop_signal = WTERMSIG (w); + terminal_ours_for_output (); + printf ("\nProgram terminated with signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + printf ("The inferior process no longer exists.\n"); + fflush (stdout); + break; + } + else + { + stop_signal = WSTOPSIG (w); +d571 6 +a576 6 + /* First, distinguish signals caused by the debugger from signals + that have to do with the program's own actions. + Note that breakpoint insns may cause SIGTRAP or SIGILL + or SIGEMT, depending on the operating system version. + Here we detect when a SIGILL or SIGEMT is really a breakpoint + and change it to SIGTRAP. */ +d578 1 +a578 1 + if (stop_signal == SIGTRAP +d580 3 +a582 3 + || (breakpoints_inserted && + (stop_signal == SIGILL + || stop_signal == SIGEMT)) +d584 15 +a598 1 + || stop_after_attach) +d600 1 +a600 15 + if (stop_signal == SIGTRAP && stop_after_trap) + { + stop_print_frame = 0; + break; + } + if (stop_after_attach) + break; + /* Don't even think about breakpoints + if still running the shell that will exec the program + or if just proceeded over a breakpoint. */ + if (stop_signal == SIGTRAP && trap_expected) + stop_breakpoint = 0; + else + { + /* See if there is a breakpoint at the current PC. */ +d602 8 +a609 8 + /* Notice the case of stepping through a jump + that leads just after a breakpoint. + Don't confuse that with hitting the breakpoint. + What we check for is that 1) stepping is going on + and 2) the pc before the last insn does not match + the address of the breakpoint before the current pc. */ + if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && step_range_end && !step_resume_break_address)) +d611 9 +d621 2 +a622 11 + /* For condition exprs. */ + select_frame (get_current_frame (), 0); + stop_breakpoint = + breakpoint_stop_status (stop_pc, stop_frame_address); + /* Following in case break condition called a + function. */ + stop_print_frame = 1; + if (stop_breakpoint && DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); +d624 1 +a624 1 + write_register (NPC_REGNUM, stop_pc + 4); +d626 1 +a626 2 + pc_changed = 0; + } +d628 12 +a639 12 + /* See if we stopped at the special breakpoint for + stepping over a subroutine call. */ + if (stop_pc - DECR_PC_AFTER_BREAK + == step_resume_break_address) + { + stop_step_resume_break = 1; + if (DECR_PC_AFTER_BREAK) + { + stop_pc -= DECR_PC_AFTER_BREAK; + write_register (PC_REGNUM, stop_pc); + pc_changed = 0; + } +d642 1 +d644 4 +a647 4 + if (stop_signal == SIGTRAP) + random_signal + = !(stop_breakpoint || trap_expected + || stop_step_resume_break +d649 2 +a650 2 + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +d652 1 +a652 1 + || stop_pc == text_end - 2 +d654 6 +a659 6 + || (step_range_end && !step_resume_break_address)); + else + { + random_signal + = !(stop_breakpoint + || stop_step_resume_break +d661 2 +a662 2 + || (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +d665 3 +a667 4 + ); + if (!random_signal) + stop_signal = SIGTRAP; + } +d669 3 +a671 2 + else + random_signal = 1; +d673 2 +a674 2 + /* For the program's own signals, act according to + the signal handling tables. */ +d676 50 +a725 19 + if (random_signal + && !(running_in_shell && stop_signal == SIGSEGV)) + { + /* Signal not for debugging purposes. */ + int printed = 0; + + stopped_by_random_signal = 1; + + if (stop_signal >= NSIG + || signal_print[stop_signal]) + { + printed = 1; + terminal_ours_for_output (); + printf ("\nProgram received signal %d, %s\n", + stop_signal, + stop_signal < NSIG + ? sys_siglist[stop_signal] + : "(undocumented)"); + fflush (stdout); +d727 1 +a727 7 + if (stop_signal >= NSIG + || signal_stop[stop_signal]) + break; + /* If not going to stop, give terminal back + if we took it away. */ + else if (printed) + terminal_inferior (); +d729 20 +a748 45 + + /* Handle cases caused by hitting a breakpoint. */ + + if (!random_signal + && (stop_breakpoint || stop_step_resume_break)) + { + /* Does a breakpoint want us to stop? */ + if (stop_breakpoint && stop_breakpoint != -1 + && stop_breakpoint != -0x1000001) + { + /* 0x1000000 is set in stop_breakpoint as returned by + breakpoint_stop_status to indicate a silent + breakpoint. */ + if ((stop_breakpoint > 0 ? stop_breakpoint : + -stop_breakpoint) + & 0x1000000) + { + stop_print_frame = 0; + if (stop_breakpoint > 0) + stop_breakpoint -= 0x1000000; + else + stop_breakpoint += 0x1000000; + } + break; + } + /* But if we have hit the step-resumption breakpoint, + remove it. It has done its job getting us here. + The sp test is to make sure that we don't get hung + up in recursive calls in functions without frame + pointers. If the stack pointer isn't outside of + where the breakpoint was set (within a routine to be + stepped over), we're in the middle of a recursive + call. Not true for reg window machines (sparc) + because the must change frames to call things and + the stack pointer doesn't have to change if it + the bp was set in a routine without a frame (pc can + be stored in some other window). + + The removal of the sp test is to allow calls to + alloca. Nasty things were happening. Oh, well, + gdb can only handle one level deep of lack of + frame pointer. */ + if (stop_step_resume_break + && (step_frame_address == 0 + || (stop_frame_address == step_frame_address +d751 1 +a751 1 + && step_resume_break_sp INNER_THAN stop_sp +d754 13 +a766 20 + ))) + { + remove_step_breakpoint (); + step_resume_break_address = 0; + } + /* Otherwise, must remove breakpoints and single-step + to get us past the one we hit. */ + else + { + remove_breakpoints (); + remove_step_breakpoint (); + breakpoints_inserted = 0; + another_trap = 1; + } + + /* We come here if we hit a breakpoint but should not + stop for it. Possibly we also were stepping + and should stop for that. So fall through and + test for stepping. But, if not stepping, + do not stop. */ +d769 9 +a777 2 + /* If this is the breakpoint at the end of a stack dummy, + just stop silently. */ +d779 2 +a780 2 + if (stop_sp INNER_THAN stop_pc + && stop_pc INNER_THAN stop_frame_address) +d782 2 +a783 2 + /* "stack" dummy must be in text segment for Convex Unix */ + if (stop_pc == text_end - 2) +d785 3 +a787 3 + { + stop_print_frame = 0; + stop_stack_dummy = 1; +d789 1 +a789 1 + trap_expected_after_continue = 1; +d791 2 +a792 2 + break; + } +d794 22 +a815 18 + if (step_resume_break_address) + /* Having a step-resume breakpoint overrides anything + else having to do with stepping commands until + that breakpoint is reached. */ + ; + /* If stepping through a line, keep going if still within it. */ + else if (!random_signal + && step_range_end + && stop_pc >= step_range_start + && stop_pc < step_range_end + /* The step range might include the start of the + function, so if we are at the start of the + step range and either the stack or frame pointers + just changed, we've stepped outside */ + && !(stop_pc == step_range_start + && stop_frame_address + && (stop_sp != prev_sp + || stop_frame_address != step_frame_address))) +d817 2 +a818 7 + /* Don't step through the return from a function + unless that is the first instruction stepped through. */ + if (ABOUT_TO_RETURN (stop_pc)) + { + stop_step = 1; + break; + } +d820 1 +d822 43 +a864 43 + /* We stepped out of the stepping range. See if that was due + to a subroutine call that we should proceed to the end of. */ + else if (!random_signal && step_range_end) + { + if (stop_func_start) + { + prologue_pc = stop_func_start; + SKIP_PROLOGUE (prologue_pc); + } + + /* ==> See comments at top of file on this algorithm. <==*/ + + if (stop_pc == stop_func_start + && (stop_func_start != prev_func_start + || prologue_pc != stop_func_start + || stop_sp != prev_sp)) + { + newfun = find_pc_function (stop_pc); + /* It's a subroutine call */ + if (step_over_calls > 0 || (step_over_calls && newfun == 0)) + { + /* A subroutine call has happened. */ + /* Set a special breakpoint after the return */ + step_resume_break_address = + SAVED_PC_AFTER_CALL (get_current_frame ()); + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + step_resume_break_sp = stop_sp; + if (breakpoints_inserted) + insert_step_breakpoint (); + } + /* Subroutine call with source code we should not step over. + Do step to the first line of code in it. */ + else if (step_over_calls) + { + SKIP_PROLOGUE (stop_func_start); + sal = find_pc_line (stop_func_start, 0); + /* Use the step_resume_break to step until + the end of the prologue, even if that involves jumps + (as it seems to on the vax under 4.2). */ + /* If the prologue ends in the middle of a source line, + continue to the end of that source line. + Otherwise, just go to end of prologue. */ +d866 2 +a867 2 + /* no, don't either. It skips any code that's + legitimately on the first line. */ +d869 2 +a870 2 + if (sal.end && sal.pc != stop_func_start) + stop_func_start = sal.end; +d872 6 +a877 24 + + if (stop_func_start == stop_pc) + { + /* We are already there: stop now. */ + stop_step = 1; + break; + } + else + /* Put the step-breakpoint there and go until there. */ + { + step_resume_break_address = stop_func_start; + step_resume_break_sp = stop_sp; + + step_resume_break_duplicate + = breakpoint_here_p (step_resume_break_address); + if (breakpoints_inserted) + insert_step_breakpoint (); + /* Do not specify what the fp should be when we stop + since on some machines the prologue + is where the new fp value is established. */ + step_frame_address = 0; + /* And make sure stepping stops right away then. */ + step_range_end = step_range_start; + } +d880 1 +d882 13 +a894 7 + /* We get here only if step_over_calls is 0 and we + just stepped into a subroutine. I presume + that step_over_calls is only 0 when we're + supposed to be stepping at the assembly + language level.*/ + stop_step = 1; + break; +a896 1 + /* No subroutince call; stop now. */ +d899 5 +d908 6 +d983 4 +d993 2 +a994 1 + (get_current_frame ())->pc = read_pc (); +@ diff --git a/gdb/RCS/m-aux.h,v b/gdb/RCS/m-aux.h,v new file mode 100644 index 0000000..2bf0702 --- /dev/null +++ b/gdb/RCS/m-aux.h,v @@ -0,0 +1,591 @@ +head 1.4; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.4 +date 89.04.26.00.51.42; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 89.03.27.20.16.05; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.03.26.20.13.28; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.13.19.16.52; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.4 +log +@(1) Defined the big-endianness of the target machine. +(2) Define float to be IEEE compatible. +(3) Define invalid floats to be NaNs. +@ +text +@/* Parameters for execution on A/UX, for GDB, the GNU debugger. + Copyright (C) 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#ifndef aux +#define aux +#endif + +/* It's a USG system */ +#define USG + +/* Assembler instructions in USG "SGS" (sw generation system) format */ +#define USG_SGS_ASM + +/* Debugger information will be in COFF format. */ +#define COFF_FORMAT +#define COFF_NO_LONG_FILE_NAMES + +/* Terminal interface via termio */ +#define HAVE_TERMIO + +/* Unisoft fucked up the include files */ +#define UNISOFT_ASSHOLES + +/* Big or Little-Endian target machine + BITS: defined if bit #0 is the high-order bit of a byte. + BYTES:defined if byte#0 is the high-order byte of an int. + WORDS:defined if word#0 is the high-order word of a double. */ +#define BITS_BIG_ENDIAN +#define BYTES_BIG_ENDIAN +#define WORDS_BIG_ENDIAN + +/* Floating point is IEEE compatible */ +#define IEEE_FLOAT + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(pc) \ +{ register int op = read_memory_integer (pc, 2); \ + if (op == 0047126) \ + pc += 4; /* Skip link #word */ \ + else if (op == 0044016) \ + pc += 6; /* Skip link #long */ \ +} + +/* Immediately after a function call, return the saved pc. + Can't go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +#define SAVED_PC_AFTER_CALL(frame) \ +read_memory_integer (read_register (SP_REGNUM), 4) + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0x20000000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Sequence of bytes for breakpoint instruction. */ +/* A/UX uses "trap &1" */ + +#define BREAKPOINT {0x4e, 0x41} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 0 + +/* Nonzero if instruction at PC is a return instruction. */ + +#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 2) == 0x4e75) + +/* Return 1 if P points to an invalid floating point value. */ +/* FIXME, it's not clear what "invalid" means here. I take it to mean + "something that coredumps gdb if gdb tries to manipulate it" */ + +#define INVALID_FLOAT(p, len) is_nan(p, len) + +/* Largest integer type */ +#define LONGEST long + +/* Name of the builtin type for the LONGEST type above. */ +#define BUILTIN_TYPE_LONGEST builtin_type_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 29 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +#define REGISTER_NAMES \ + {"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", \ + "a0", "a1", "a2", "a3", "a4", "a5", "fp", "sp", \ + "ps", "pc", \ + "fp0", "fp1", "fp2", "fp3", "fp4", "fp5", "fp6", "fp7", \ + "fpcontrol", "fpstatus", "fpiaddr" } + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 14 /* Contains address of executing stack frame */ +#define SP_REGNUM 15 /* Contains address of top of stack */ +#define PS_REGNUM 16 /* Contains processor status */ +#define PC_REGNUM 17 /* Contains program counter */ +#define FP0_REGNUM 18 /* Floating point register 0 */ +#define FPC_REGNUM 26 /* 68881 control register */ + +/* This is a piece of magic that is given a register number REGNO + and as BLOCKEND the address in the system of the end of the user structure + and stores in ADDR the address in the kernel or core dump + of that register. */ + +#define REGISTER_U_ADDR(addr, blockend, regno) { \ + struct user u; \ + if (regno <= SP_REGNUM) \ + addr = blockend + regno * 4; \ + else if (regno == PS_REGNUM) \ + addr = blockend + RPS * 4; /* From reg.h */ \ + else if (regno == PC_REGNUM) \ + addr = blockend + PC * 4; /* From reg.h */ \ + else if (regno < FPC_REGNUM) \ + addr = (char *) u.u_fpdreg [regno-FP0_REGNUM] - (char *)&u; \ + else \ + addr = (char *)&u.u_fpsysreg[regno-FPC_REGNUM] - (char *)&u; \ +} + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (16*4+8*12+8+20) + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) \ + ((N) >= FPC_REGNUM ? (((N) - FPC_REGNUM) * 4) + 168 \ + : (N) >= FP0_REGNUM ? (((N) - FP0_REGNUM) * 12) + 72 \ + : (N) * 4) + +/* Number of bytes of storage in the actual machine representation + for register N. On the 68000, all regs are 4 bytes + except the floating point regs which are 12 bytes. */ +/* Note that the unsigned cast here forces the result of the + subtractiion to very high positive values if N < FP0_REGNUM */ + +#define REGISTER_RAW_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 8 ? 12 : 4) + +/* Number of bytes of storage in the program's representation + for register N. On the 68000, all regs are 4 bytes + except the floating point regs which are 8-byte doubles. */ + +#define REGISTER_VIRTUAL_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 8 ? 8 : 4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 12 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 8 + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) (((unsigned)(N) - FP0_REGNUM) < 8) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) \ +{ if ((REGNUM) >= FP0_REGNUM && (REGNUM) < FPC_REGNUM) \ + convert_from_68881 ((FROM), (TO)); \ + else \ + bcopy ((FROM), (TO), 4); } + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) \ +{ if ((REGNUM) >= FP0_REGNUM && (REGNUM) < FPC_REGNUM) \ + convert_to_68881 ((FROM), (TO)); \ + else \ + bcopy ((FROM), (TO), 4); } + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) \ + (((unsigned)(N) - FP0_REGNUM) < 8 ? builtin_type_double : builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { write_register (9, (ADDR)); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE)) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE)) + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF)) + + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +/* In the case of the 68k, the frame's nominal address + is the address of a 4-byte word containing the calling frame's address. */ + +#define FRAME_CHAIN(thisframe) (read_memory_integer ((thisframe)->frame, 4)) + +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (FRAME_SAVED_PC (thisframe) >= first_object_file_end)) + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4)) + +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Set VAL to the number of args passed to frame described by FI. + Can set VAL to -1, meaning no way to tell. */ + +/* We can't tell how many args there are + now that the C compiler delays popping them. */ +#define FRAME_NUM_ARGS(val,fi) (val = -1) + +#if 0 +#define FRAME_NUM_ARGS(val, fi) \ +{ register CORE_ADDR pc = FRAME_SAVED_PC (fi); \ + register int insn = 0177777 & read_memory_integer (pc, 2); \ + val = 0; \ + if (insn == 0047757 || insn == 0157374) /* lea W(sp),sp or addaw #W,sp */ \ + val = read_memory_integer (pc + 2, 2); \ + else if ((insn & 0170777) == 0050217 /* addql #N, sp */ \ + || (insn & 0170777) == 0050117) /* addqw */ \ + { val = (insn >> 9) & 7; if (val == 0) val = 8; } \ + else if (insn == 0157774) /* addal #WW, sp */ \ + val = read_memory_integer (pc + 2, 4); \ + val >>= 2; } +#endif + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 8 + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. */ + +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ +{ register int regnum; \ + register int regmask; \ + register CORE_ADDR next_addr; \ + register CORE_ADDR pc; \ + int nextinsn; \ + bzero (&frame_saved_regs, sizeof frame_saved_regs); \ + if ((frame_info)->pc >= (frame_info)->frame - CALL_DUMMY_LENGTH - FP_REGNUM*4 - 8*12 - 4 \ + && (frame_info)->pc <= (frame_info)->frame) \ + { next_addr = (frame_info)->frame; \ + pc = (frame_info)->frame - CALL_DUMMY_LENGTH - FP_REGNUM * 4 - 8*12 - 4; }\ + else \ + { pc = get_pc_function_start ((frame_info)->pc); \ + /* Verify we have a link a6 instruction next; \ + if not we lose. If we win, find the address above the saved \ + regs using the amount of storage from the link instruction. */\ + if (044016 == read_memory_integer (pc, 2)) \ + next_addr = (frame_info)->frame + read_memory_integer (pc += 2, 4), pc+=4; \ + else if (047126 == read_memory_integer (pc, 2)) \ + next_addr = (frame_info)->frame + read_memory_integer (pc += 2, 2), pc+=2; \ + else goto lose; \ + /* If have an addal #-n, sp next, adjust next_addr. */ \ + if ((0177777 & read_memory_integer (pc, 2)) == 0157774) \ + next_addr += read_memory_integer (pc += 2, 4), pc += 4; \ + } \ + /* next should be a moveml to (sp) or -(sp) or a movl r,-(sp) */ \ + regmask = read_memory_integer (pc + 2, 2); \ + /* But before that can come an fmovem. Check for it. */ \ + nextinsn = 0xffff & read_memory_integer (pc, 2); \ + if (0xf227 == nextinsn \ + && (regmask & 0xff00) == 0xe000) \ + { pc += 4; /* Regmask's low bit is for register fp7, the first pushed */ \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr -= 12); \ + regmask = read_memory_integer (pc + 2, 2); } \ + if (0044327 == read_memory_integer (pc, 2)) \ + { pc += 4; /* Regmask's low bit is for register 0, the first written */ \ + for (regnum = 0; regnum < 16; regnum++, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr += 4) - 4; } \ + else if (0044347 == read_memory_integer (pc, 2)) \ + { pc += 4; /* Regmask's low bit is for register 15, the first pushed */ \ + for (regnum = 15; regnum >= 0; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr -= 4); } \ + else if (0x2f00 == (0xfff0 & read_memory_integer (pc, 2))) \ + { regnum = 0xf & read_memory_integer (pc, 2); pc += 2; \ + (frame_saved_regs).regs[regnum] = (next_addr -= 4); } \ + /* fmovemx to index of sp may follow. */ \ + regmask = read_memory_integer (pc + 2, 2); \ + nextinsn = 0xffff & read_memory_integer (pc, 2); \ + if (0xf236 == nextinsn \ + && (regmask & 0xff00) == 0xf000) \ + { pc += 10; /* Regmask's low bit is for register fp0, the first written */ \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr += 12) - 12; \ + regmask = read_memory_integer (pc + 2, 2); } \ + /* clrw -(sp); movw ccr,-(sp) may follow. */ \ + if (0x426742e7 == read_memory_integer (pc, 4)) \ + (frame_saved_regs).regs[PS_REGNUM] = (next_addr -= 4); \ + lose: ; \ + (frame_saved_regs).regs[SP_REGNUM] = (frame_info)->frame + 8; \ + (frame_saved_regs).regs[FP_REGNUM] = (frame_info)->frame; \ + (frame_saved_regs).regs[PC_REGNUM] = (frame_info)->frame + 4; \ +} + +/* Things needed for making the inferior call functions. */ + +/* Push an empty stack frame, to record the current PC, etc. */ + +#define PUSH_DUMMY_FRAME \ +{ register CORE_ADDR sp = read_register (SP_REGNUM); \ + register int regnum; \ + char raw_buffer[12]; \ + sp = push_word (sp, read_register (PC_REGNUM)); \ + sp = push_word (sp, read_register (FP_REGNUM)); \ + write_register (FP_REGNUM, sp); \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--) \ + { read_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 12); \ + sp = push_bytes (sp, raw_buffer, 12); } \ + for (regnum = FP_REGNUM - 1; regnum >= 0; regnum--) \ + sp = push_word (sp, read_register (regnum)); \ + sp = push_word (sp, read_register (PS_REGNUM)); \ + write_register (SP_REGNUM, sp); } + +/* Discard from the stack the innermost frame, + restoring all saved registers. */ + +#define POP_FRAME \ +{ register FRAME frame = get_current_frame (); \ + register CORE_ADDR fp; \ + register int regnum; \ + struct frame_saved_regs fsr; \ + struct frame_info *fi; \ + char raw_buffer[12]; \ + fi = get_frame_info (frame); \ + fp = fi->frame; \ + get_frame_saved_regs (fi, &fsr); \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--) \ + if (fsr.regs[regnum]) \ + { read_memory (fsr.regs[regnum], raw_buffer, 12); \ + write_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 12); }\ + for (regnum = FP_REGNUM - 1; regnum >= 0; regnum--) \ + if (fsr.regs[regnum]) \ + write_register (regnum, read_memory_integer (fsr.regs[regnum], 4)); \ + if (fsr.regs[PS_REGNUM]) \ + write_register (PS_REGNUM, read_memory_integer (fsr.regs[PS_REGNUM], 4)); \ + write_register (FP_REGNUM, read_memory_integer (fp, 4)); \ + write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); \ + write_register (SP_REGNUM, fp + 8); \ + flush_cached_frames (); \ + set_current_frame (create_new_frame (read_register (FP_REGNUM),\ + read_pc ())); } + +/* This sequence of words is the instructions + fmovem 0xff,-(sp) + moveml 0xfffc,-(sp) + clrw -(sp) + movew ccr,-(sp) + /..* The arguments are pushed at this point by GDB; + no code is needed in the dummy for this. + The CALL_DUMMY_START_OFFSET gives the position of + the following jsr instruction. *../ + jsr @@#32323232 + addl #69696969,sp + trap #15 + nop +Note this is 28 bytes. +We actually start executing at the jsr, since the pushing of the +registers is done by PUSH_DUMMY_FRAME. If this were real code, +the arguments for the function called by the jsr would be pushed +between the moveml and the jsr, and we could allow it to execute through. +But the arguments have to be pushed by GDB after the PUSH_DUMMY_FRAME is done, +and we cannot allow the moveml to push the registers again lest they be +taken for the arguments. */ + +#define CALL_DUMMY {0xf227e0ff, 0x48e7fffc, 0x426742e7, 0x4eb93232, 0x3232dffc, 0x69696969, 0x4e4f4e71} + +#define CALL_DUMMY_LENGTH 28 + +#define CALL_DUMMY_START_OFFSET 12 + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. */ + +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, type) \ +{ *(int *)((char *) dummyname + 20) = nargs * 4; \ + *(int *)((char *) dummyname + 14) = fun; } + +/* Interface definitions for kernel debugger KDB. */ + +/* Map machine fault codes into signal numbers. + First subtract 0, divide by 4, then index in a table. + Faults for which the entry in this table is 0 + are not handled by KDB; the program's own trap handler + gets to handle then. */ + +#define FAULT_CODE_ORIGIN 0 +#define FAULT_CODE_UNITS 4 +#define FAULT_TABLE \ +{ 0, 0, 0, 0, SIGTRAP, 0, 0, 0, \ + 0, SIGTRAP, 0, 0, 0, 0, 0, SIGKILL, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + SIGILL } + +/* Start running with a stack stretching from BEG to END. + BEG and END should be symbols meaningful to the assembler. + This is used only for kdb. */ + +#define INIT_STACK(beg, end) \ +{ asm (".globl end"); \ + asm ("movel #end, sp"); \ + asm ("movel #0,a6"); } + +/* Push the frame pointer register on the stack. */ +#define PUSH_FRAME_PTR \ + asm ("movel a6,sp@@-"); + +/* Copy the top-of-stack to the frame pointer register. */ +#define POP_FRAME_PTR \ + asm ("movl sp@@,a6"); + +/* After KDB is entered by a fault, push all registers + that GDB thinks about (all NUM_REGS of them), + so that they appear in order of ascending GDB register number. + The fault code will be on the stack beyond the last register. */ + +#define PUSH_REGISTERS \ +{ asm ("clrw -(sp)"); \ + asm ("pea sp@@(10)"); \ + asm ("movem #0xfffe,sp@@-"); } + +/* Assuming the registers (including processor status) have been + pushed on the stack in order of ascending GDB register number, + restore them and return to the address in the saved PC register. */ + +#define POP_REGISTERS \ +{ asm ("subil #8,sp@@(28)"); \ + asm ("movem sp@@,#0xffff"); \ + asm ("rte"); } +@ + + +1.3 +log +@Fix DECR_PC_AFTER_BREAK; A/UX reports breaks at the breakpoint addr, +not there+2. +@ +text +@d41 11 +d100 2 +d103 1 +a103 1 +#define INVALID_FLOAT(p, len) 1 /* FIXME! Just a first guess; not checked */ +@ + + +1.2 +log +@Mostly works! +@ +text +@d82 1 +a82 1 +#define DECR_PC_AFTER_BREAK 2 +@ + + +1.1 +log +@Initial revision +@ +text +@d1 504 +@ diff --git a/gdb/RCS/m-hp9k320.h,v b/gdb/RCS/m-hp9k320.h,v new file mode 100644 index 0000000..8df1709 --- /dev/null +++ b/gdb/RCS/m-hp9k320.h,v @@ -0,0 +1,607 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.20.17.11; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.19.29.58; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Change "HPUX_ASM" define to "USG_SGS_ASM", since it's really the USG +Software Generation System assembler that we're fighting here. +., +@ +text +@/* Parameters for execution on an HP 9000 model 320, for GDB, the GNU debugger. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#ifndef HP9K320 +#define HP9K320 +#endif + +/* Set flag to indicate whether HP's assembler is in use. */ +#ifdef __GNU__ +#ifdef __HPUX_ASM__ +#define USG_SGS_ASM +#endif +#else +#define USG_SGS_ASM +#endif + +/* Define this for versions of hp-ux older than 6.0 */ +/* #define HPUX_VERSION_5 */ + +/* define USG if you are using sys5 /usr/include's */ +#define USG + +#define HAVE_TERMIO + +/* Get rid of any system-imposed stack limit if possible. */ + +/* #define SET_STACK_LIMIT_HUGE */ + +/* Define this if the C compiler puts an underscore at the front + of external names before giving them to the linker. */ + +#define NAMES_HAVE_UNDERSCORE + +/* Debugger information will be in DBX format. */ + +#define READ_DBX_FORMAT + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(pc) \ +{ register int op = read_memory_integer (pc, 2); \ + if (op == 0047126) \ + pc += 4; /* Skip link #word */ \ + else if (op == 0044016) \ + pc += 6; /* Skip link #long */ \ +} + +/* Immediately after a function call, return the saved pc. + Can't go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +#define SAVED_PC_AFTER_CALL(frame) \ +read_memory_integer (read_register (SP_REGNUM), 4) + +/* This is the amount to subtract from u.u_ar0 + to get the offset in the core file of the register values. */ + +#ifdef HPUX_VERSION_5 +#define KERNEL_U_ADDR 0x00979000 +#else +#define KERNEL_U_ADDR 0x00C01000 +#endif + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0xFFF00000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0x4e, 0x41} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 2 + +/* Nonzero if instruction at PC is a return instruction. */ + +#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 2) == 0x4e75) + +/* Return 1 if P points to an invalid floating point value. */ + +#define INVALID_FLOAT(p, len) 0 /* Just a first guess; not checked */ + +/* Largest integer type */ +#define LONGEST long + +/* Name of the builtin type for the LONGEST type above. */ +#define BUILTIN_TYPE_LONGEST builtin_type_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 29 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +#define REGISTER_NAMES \ + {"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", \ + "a0", "a1", "a2", "a3", "a4", "a5", "fp", "sp", \ + "ps", "pc", \ + "fp0", "fp1", "fp2", "fp3", "fp4", "fp5", "fp6", "fp7", \ + "fpcontrol", "fpstatus", "fpiaddr" } + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 14 /* Contains address of executing stack frame */ +#define SP_REGNUM 15 /* Contains address of top of stack */ +#define PS_REGNUM 16 /* Contains processor status */ +#define PC_REGNUM 17 /* Contains program counter */ +#define FP0_REGNUM 18 /* Floating point register 0 */ +#define FPC_REGNUM 26 /* 68881 control register */ + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (16*4+8*12+8+12) + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) \ + ((N) >= FPC_REGNUM ? (((N) - FPC_REGNUM) * 4) + 168 \ + : (N) >= FP0_REGNUM ? (((N) - FP0_REGNUM) * 12) + 72 \ + : (N) * 4) + +/* Number of bytes of storage in the actual machine representation + for register N. On the 68000, all regs are 4 bytes + except the floating point regs which are 12 bytes. */ + +#define REGISTER_RAW_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 8 ? 12 : 4) + +/* Number of bytes of storage in the program's representation + for register N. On the 68000, all regs are 4 bytes + except the floating point regs which are 8-byte doubles. */ + +#define REGISTER_VIRTUAL_SIZE(N) (((unsigned)(N) - FP0_REGNUM) < 8 ? 8 : 4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 12 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 8 + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) (((unsigned)(N) - FP0_REGNUM) < 8) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) \ +{ if ((REGNUM) >= FP0_REGNUM && (REGNUM) < FPC_REGNUM) \ + convert_from_68881 ((FROM), (TO)); \ + else \ + bcopy ((FROM), (TO), 4); } + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) \ +{ if ((REGNUM) >= FP0_REGNUM && (REGNUM) < FPC_REGNUM) \ + convert_to_68881 ((FROM), (TO)); \ + else \ + bcopy ((FROM), (TO), 4); } + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) \ + (((unsigned)(N) - FP0_REGNUM) < 8 ? builtin_type_double : builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { write_register (9, (ADDR)); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE)) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ + +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE)) + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF)) + +#define REGISTER_ADDR(u_ar0, regno) \ + (((regno) < PS_REGNUM) \ + ? (&((struct exception_stack *) (u_ar0))->e_regs[(regno + R0)]) \ + : (((regno) == PS_REGNUM) \ + ? ((int *) (&((struct exception_stack *) (u_ar0))->e_PS)) \ + : (&((struct exception_stack *) (u_ar0))->e_PC))) + +#define FP_REGISTER_ADDR(u, regno) \ + (((char *) \ + (((regno) < FPC_REGNUM) \ + ? (&u.u_pcb.pcb_mc68881[FMC68881_R0 + (((regno) - FP0_REGNUM) * 3)]) \ + : (&u.u_pcb.pcb_mc68881[FMC68881_C + ((regno) - FPC_REGNUM)]))) \ + - ((char *) (& u))) + +/* It is safe to look for symsegs on a Sun, because Sun's ld + does not screw up with random garbage at end of file. */ + +#define READ_GDB_SYMSEGS + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +/* In the case of the Sun, the frame's nominal address + is the address of a 4-byte word containing the calling frame's address. */ + +#define FRAME_CHAIN(thisframe) (read_memory_integer ((thisframe)->frame, 4)) + +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (FRAME_SAVED_PC (thisframe) >= first_object_file_end)) + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4)) + +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Set VAL to the number of args passed to frame described by FI. + Can set VAL to -1, meaning no way to tell. */ + +/* We can't tell how many args there are + now that the C compiler delays popping them. */ +#define FRAME_NUM_ARGS(val,fi) (val = -1) + +#if 0 +#define FRAME_NUM_ARGS(val, fi) \ +{ register CORE_ADDR pc = FRAME_SAVED_PC (fi); \ + register int insn = 0177777 & read_memory_integer (pc, 2); \ + val = 0; \ + if (insn == 0047757 || insn == 0157374) /* lea W(sp),sp or addaw #W,sp */ \ + val = read_memory_integer (pc + 2, 2); \ + else if ((insn & 0170777) == 0050217 /* addql #N, sp */ \ + || (insn & 0170777) == 0050117) /* addqw */ \ + { val = (insn >> 9) & 7; if (val == 0) val = 8; } \ + else if (insn == 0157774) /* addal #WW, sp */ \ + val = read_memory_integer (pc + 2, 4); \ + val >>= 2; } +#endif + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 8 + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. */ + +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ +{ register int regnum; \ + register int regmask; \ + register CORE_ADDR next_addr; \ + register CORE_ADDR pc; \ + int nextinsn; \ + bzero (&frame_saved_regs, sizeof frame_saved_regs); \ + if ((frame_info)->pc >= (frame_info)->frame - CALL_DUMMY_LENGTH - FP_REGNUM*4 - 8*12 - 4 \ + && (frame_info)->pc <= (frame_info)->frame) \ + { next_addr = (frame_info)->frame; \ + pc = (frame_info)->frame - CALL_DUMMY_LENGTH - FP_REGNUM * 4 - 8*12 - 4; }\ + else \ + { pc = get_pc_function_start ((frame_info)->pc); \ + /* Verify we have a link a6 instruction next; \ + if not we lose. If we win, find the address above the saved \ + regs using the amount of storage from the link instruction. */\ + if (044016 == read_memory_integer (pc, 2)) \ + next_addr = (frame_info)->frame + read_memory_integer (pc += 2, 4), pc+=4; \ + else if (047126 == read_memory_integer (pc, 2)) \ + next_addr = (frame_info)->frame + read_memory_integer (pc += 2, 2), pc+=2; \ + else goto lose; \ + /* If have an addal #-n, sp next, adjust next_addr. */ \ + if ((0177777 & read_memory_integer (pc, 2)) == 0157774) \ + next_addr += read_memory_integer (pc += 2, 4), pc += 4; \ + } \ + /* next should be a moveml to (sp) or -(sp) or a movl r,-(sp) */ \ + regmask = read_memory_integer (pc + 2, 2); \ + /* But before that can come an fmovem. Check for it. */ \ + nextinsn = 0xffff & read_memory_integer (pc, 2); \ + if (0xf227 == nextinsn \ + && (regmask & 0xff00) == 0xe000) \ + { pc += 4; /* Regmask's low bit is for register fp7, the first pushed */ \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr -= 12); \ + regmask = read_memory_integer (pc + 2, 2); } \ + if (0044327 == read_memory_integer (pc, 2)) \ + { pc += 4; /* Regmask's low bit is for register 0, the first written */ \ + for (regnum = 0; regnum < 16; regnum++, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr += 4) - 4; } \ + else if (0044347 == read_memory_integer (pc, 2)) \ + { pc += 4; /* Regmask's low bit is for register 15, the first pushed */ \ + for (regnum = 15; regnum >= 0; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr -= 4); } \ + else if (0x2f00 == 0xfff0 & read_memory_integer (pc, 2)) \ + { regnum = 0xf & read_memory_integer (pc, 2); pc += 2; \ + (frame_saved_regs).regs[regnum] = (next_addr -= 4); } \ + /* fmovemx to index of sp may follow. */ \ + regmask = read_memory_integer (pc + 2, 2); \ + nextinsn = 0xffff & read_memory_integer (pc, 2); \ + if (0xf236 == nextinsn \ + && (regmask & 0xff00) == 0xf000) \ + { pc += 10; /* Regmask's low bit is for register fp0, the first written */ \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--, regmask >>= 1) \ + if (regmask & 1) \ + (frame_saved_regs).regs[regnum] = (next_addr += 12) - 12; \ + regmask = read_memory_integer (pc + 2, 2); } \ + /* clrw -(sp); movw ccr,-(sp) may follow. */ \ + if (0x426742e7 == read_memory_integer (pc, 4)) \ + (frame_saved_regs).regs[PS_REGNUM] = (next_addr -= 4); \ + lose: ; \ + (frame_saved_regs).regs[SP_REGNUM] = (frame_info)->frame + 8; \ + (frame_saved_regs).regs[FP_REGNUM] = (frame_info)->frame; \ + (frame_saved_regs).regs[PC_REGNUM] = (frame_info)->frame + 4; \ +} + +/* Things needed for making the inferior call functions. */ + +/* Push an empty stack frame, to record the current PC, etc. */ + +#define PUSH_DUMMY_FRAME \ +{ register CORE_ADDR sp = read_register (SP_REGNUM); \ + register int regnum; \ + char raw_buffer[12]; \ + sp = push_word (sp, read_register (PC_REGNUM)); \ + sp = push_word (sp, read_register (FP_REGNUM)); \ + write_register (FP_REGNUM, sp); \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--) \ + { read_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 12); \ + sp = push_bytes (sp, raw_buffer, 12); } \ + for (regnum = FP_REGNUM - 1; regnum >= 0; regnum--) \ + sp = push_word (sp, read_register (regnum)); \ + sp = push_word (sp, read_register (PS_REGNUM)); \ + write_register (SP_REGNUM, sp); } + +/* Discard from the stack the innermost frame, + restoring all saved registers. */ + +#define POP_FRAME \ +{ register FRAME frame = get_current_frame (); \ + register CORE_ADDR fp; \ + register int regnum; \ + struct frame_saved_regs fsr; \ + struct frame_info *fi; \ + char raw_buffer[12]; \ + fi = get_frame_info (frame); \ + fp = fi->frame; \ + get_frame_saved_regs (fi, &fsr); \ + for (regnum = FP0_REGNUM + 7; regnum >= FP0_REGNUM; regnum--) \ + if (fsr.regs[regnum]) \ + { read_memory (fsr.regs[regnum], raw_buffer, 12); \ + write_register_bytes (REGISTER_BYTE (regnum), raw_buffer, 12); }\ + for (regnum = FP_REGNUM - 1; regnum >= 0; regnum--) \ + if (fsr.regs[regnum]) \ + write_register (regnum, read_memory_integer (fsr.regs[regnum], 4)); \ + if (fsr.regs[PS_REGNUM]) \ + write_register (PS_REGNUM, read_memory_integer (fsr.regs[PS_REGNUM], 4)); \ + write_register (FP_REGNUM, read_memory_integer (fp, 4)); \ + write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); \ + write_register (SP_REGNUM, fp + 8); \ + flush_cached_frames (); \ + set_current_frame (create_new_frame (read_register (FP_REGNUM),\ + read_pc ()));} + +/* This sequence of words is the instructions + fmovem 0xff,-(sp) + moveml 0xfffc,-(sp) + clrw -(sp) + movew ccr,-(sp) + /..* The arguments are pushed at this point by GDB; + no code is needed in the dummy for this. + The CALL_DUMMY_START_OFFSET gives the position of + the following jsr instruction. *../ + jsr @@#32323232 + addl #69696969,sp + bpt + nop +Note this is 28 bytes. +We actually start executing at the jsr, since the pushing of the +registers is done by PUSH_DUMMY_FRAME. If this were real code, +the arguments for the function called by the jsr would be pushed +between the moveml and the jsr, and we could allow it to execute through. +But the arguments have to be pushed by GDB after the PUSH_DUMMY_FRAME is done, +and we cannot allow the moveml to push the registers again lest they be +taken for the arguments. */ + +#define CALL_DUMMY {0xf227e0ff, 0x48e7fffc, 0x426742e7, 0x4eb93232, 0x3232dffc, 0x69696969, 0x4e414e71} + +#define CALL_DUMMY_LENGTH 28 + +#define CALL_DUMMY_START_OFFSET 12 + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. */ + +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, type) \ +{ *(int *)((char *) dummyname + 20) = nargs * 4; \ + *(int *)((char *) dummyname + 14) = fun; } + +/* Interface definitions for kernel debugger KDB. */ + +/* Map machine fault codes into signal numbers. + First subtract 0, divide by 4, then index in a table. + Faults for which the entry in this table is 0 + are not handled by KDB; the program's own trap handler + gets to handle then. */ + +#define FAULT_CODE_ORIGIN 0 +#define FAULT_CODE_UNITS 4 +#define FAULT_TABLE \ +{ 0, 0, 0, 0, SIGTRAP, 0, 0, 0, \ + 0, SIGTRAP, 0, 0, 0, 0, 0, SIGKILL, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + SIGILL } + +#ifndef HPUX_ASM + +/* Start running with a stack stretching from BEG to END. + BEG and END should be symbols meaningful to the assembler. + This is used only for kdb. */ + +#define INIT_STACK(beg, end) \ +{ asm (".globl end"); \ + asm ("movel $ end, sp"); \ + asm ("clrl fp"); } + +/* Push the frame pointer register on the stack. */ +#define PUSH_FRAME_PTR \ + asm ("movel fp, -(sp)"); + +/* Copy the top-of-stack to the frame pointer register. */ +#define POP_FRAME_PTR \ + asm ("movl (sp), fp"); + +/* After KDB is entered by a fault, push all registers + that GDB thinks about (all NUM_REGS of them), + so that they appear in order of ascending GDB register number. + The fault code will be on the stack beyond the last register. */ + +#define PUSH_REGISTERS \ +{ asm ("clrw -(sp)"); \ + asm ("pea 10(sp)"); \ + asm ("movem $ 0xfffe,-(sp)"); } + +/* Assuming the registers (including processor status) have been + pushed on the stack in order of ascending GDB register number, + restore them and return to the address in the saved PC register. */ + +#define POP_REGISTERS \ +{ asm ("subil $8,28(sp)"); \ + asm ("movem (sp),$ 0xffff"); \ + asm ("rte"); } + +#else /* HPUX_ASM */ + +/* Start running with a stack stretching from BEG to END. + BEG and END should be symbols meaningful to the assembler. + This is used only for kdb. */ + +#define INIT_STACK(beg, end) \ +{ asm ("global end"); \ + asm ("mov.l &end,%sp"); \ + asm ("clr.l %a6"); } + +/* Push the frame pointer register on the stack. */ +#define PUSH_FRAME_PTR \ + asm ("mov.l %fp,-(%sp)"); + +/* Copy the top-of-stack to the frame pointer register. */ +#define POP_FRAME_PTR \ + asm ("mov.l (%sp),%fp"); + +/* After KDB is entered by a fault, push all registers + that GDB thinks about (all NUM_REGS of them), + so that they appear in order of ascending GDB register number. + The fault code will be on the stack beyond the last register. */ + +#define PUSH_REGISTERS \ +{ asm ("clr.w -(%sp)"); \ + asm ("pea 10(%sp)"); \ + asm ("movm.l &0xfffe,-(%sp)"); } + +/* Assuming the registers (including processor status) have been + pushed on the stack in order of ascending GDB register number, + restore them and return to the address in the saved PC register. */ + +#define POP_REGISTERS \ +{ asm ("subi.l &8,28(%sp)"); \ + asm ("mov.m (%sp),&0xffff"); \ + asm ("rte"); } + +#endif /* HPUX_ASM */ +@ + + +1.1 +log +@Initial revision +@ +text +@d28 1 +a28 1 +#define HPUX_ASM +d31 1 +a31 1 +#define HPUX_ASM +@ diff --git a/gdb/RCS/m-sparc.h,v b/gdb/RCS/m-sparc.h,v new file mode 100644 index 0000000..1720dfe --- /dev/null +++ b/gdb/RCS/m-sparc.h,v @@ -0,0 +1,747 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.04.26.00.52.29; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.03.16.21.10.29; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.14.15.28.32; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@(1) Define big-endianness of SPARC. +(2) Define IEEE compatible float. +@ +text +@/* Parameters for execution on a Sun 4, for GDB, the GNU debugger. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@@mcc.com) + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#ifndef sun4 +#define sun4 +#endif + +/* Get rid of any system-imposed stack limit if possible. */ + +#define SET_STACK_LIMIT_HUGE + +/* Define this if the C compiler puts an underscore at the front + of external names before giving them to the linker. */ + +#define NAMES_HAVE_UNDERSCORE + +/* Debugger information will be in DBX format. */ + +#define READ_DBX_FORMAT + +/* Big or Little-Endian target machine + BITS: defined if bit #0 is the high-order bit of a byte. + BYTES:defined if byte#0 is the high-order byte of an int. + WORDS:defined if word#0 is the high-order word of a double. */ +#define BITS_BIG_ENDIAN +#define BYTES_BIG_ENDIAN +#define WORDS_BIG_ENDIAN + +/* Floating point is IEEE compatible. */ +#define IEEE_FLOAT + +/* Offset from address of function to start of its code. + Zero on most machines. */ + +#define FUNCTION_START_OFFSET 0 + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +#define SKIP_PROLOGUE(pc) \ + { pc = skip_prologue (pc); } + +/* Immediately after a function call, return the saved pc. + Can't go through the frames for this because on some machines + the new frame is not set up until the new function executes + some instructions. */ + +/* On the Sun 4 under SunOS, the compile will leave a fake insn which + encodes the structure size being returned. If we detect such + a fake insn, step past it. */ + +#define PC_ADJUST(pc) ((read_memory_integer (pc + 8, 4) & 0xfffffe00) == 0 ? \ + pc+12 : pc+8) + +#define SAVED_PC_AFTER_CALL(frame) PC_ADJUST (read_register (RP_REGNUM)) + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0xf8000000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Stack has strict alignment. */ + +#define STACK_ALIGN(ADDR) (((ADDR)+7)&-8) + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0x91, 0xd0, 0x20, 0x01} + +/* Amount PC must be decremented by after a breakpoint. + This is often the number of bytes in BREAKPOINT + but not always. */ + +#define DECR_PC_AFTER_BREAK 0 + +/* Nonzero if instruction at PC is a return instruction. */ +/* For SPARC, this is either a "jmpl %o7+8,%g0" or "jmpl %i7+8,%g0". + + Note: this does not work for functions returning structures under SunOS. */ +#define ABOUT_TO_RETURN(pc) \ + ((read_memory_integer (pc, 4)|0x00040000) == 0x81c7e008) + +/* Return 1 if P points to an invalid floating point value. */ + +#define INVALID_FLOAT(p, len) 0 /* Just a first guess; not checked */ + +/* Largest integer type */ +#define LONGEST long + +/* Name of the builtin type for the LONGEST type above. */ +#define BUILTIN_TYPE_LONGEST builtin_type_long + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 72 + +/* Initializer for an array of names of registers. + There should be NUM_REGS strings in this initializer. */ + +#define REGISTER_NAMES \ +{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", \ + "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", \ + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", \ + "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7", \ + \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", \ + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", \ + \ + "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr" }; + +/* Register numbers of various important registers. + Note that some of these values are "real" register numbers, + and correspond to the general registers of the machine, + and some are "phony" register numbers which are too large + to be actual register numbers as far as the user is concerned + but do serve to get the desired values when passed to read_register. */ + +#define FP_REGNUM 30 /* Contains address of executing stack frame */ +#define RP_REGNUM 15 /* Contains return address value, *before* \ + any windows get switched. */ +#define SP_REGNUM 14 /* Contains address of top of stack, \ + which is also the bottom of the frame. */ +#define Y_REGNUM 64 /* Temp register for multiplication, etc. */ +#define PS_REGNUM 65 /* Contains processor status */ +#define PC_REGNUM 68 /* Contains program counter */ +#define NPC_REGNUM 69 /* Contains next PC */ +#define FP0_REGNUM 32 /* Floating point register 0 */ +#define FPS_REGNUM 70 /* Floating point status register */ +#define CPS_REGNUM 71 /* Coprocessor status register */ + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (32*4+32*4+8*4) + +/* Index within `registers' of the first byte of the space for + register N. */ +/* ?? */ +#define REGISTER_BYTE(N) ((N)*4) + +/* The SPARC processor has register windows. */ + +#define HAVE_REGISTER_WINDOWS + +/* Is this register part of the register window system? A yes answer + implies that 1) The name of this register will not be the same in + other frames, and 2) This register is automatically "saved" (out + registers shifting into ins counts) upon subroutine calls and thus + there is no need to search more than one stack frame for it. */ + +#define REGISTER_IN_WINDOW_P(regnum) \ + ((regnum) >= 8 && (regnum) < 32) + +/* Number of bytes of storage in the actual machine representation + for register N. */ + +/* On the SPARC, all regs are 4 bytes. */ + +#define REGISTER_RAW_SIZE(N) (4) + +/* Number of bytes of storage in the program's representation + for register N. */ + +/* On the SPARC, all regs are 4 bytes. */ + +#define REGISTER_VIRTUAL_SIZE(N) (4) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 8 + +/* Largest value REGISTER_VIRTUAL_SIZE can have. */ + +#define MAX_REGISTER_VIRTUAL_SIZE 8 + +/* Nonzero if register N requires conversion + from raw format to virtual format. */ + +#define REGISTER_CONVERTIBLE(N) (0) + +/* Convert data from raw format for register REGNUM + to virtual format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) \ +{ bcopy ((FROM), (TO), 4); } + +/* Convert data from virtual format for register REGNUM + to raw format for register REGNUM. */ + +#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) \ +{ bcopy ((FROM), (TO), 4); } + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +#define REGISTER_VIRTUAL_TYPE(N) \ + ((N) < 32 ? builtin_type_int : (N) < 64 ? builtin_type_float : \ + builtin_type_int) + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +#define STORE_STRUCT_RETURN(ADDR, SP) \ + { write_memory ((SP)+(16*4), &(ADDR), 4); } + +/* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual format, + into VALBUF. */ + +#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \ + bcopy (((int *)(REGBUF))+8, (VALBUF), TYPE_LENGTH (TYPE)) + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. */ +/* On sparc, values are returned in register %o0. */ +#define STORE_RETURN_VALUE(TYPE,VALBUF) \ + write_register_bytes (REGISTER_BYTE (8), VALBUF, TYPE_LENGTH (TYPE)) + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) \ + (read_memory_integer (((int *)(REGBUF))[SP_REGNUM]+(16*4), 4)) + +/* Enable use of alternate code to read and write registers. */ + +#define NEW_SUN_PTRACE + +/* Enable use of alternate code for Sun's format of core dump file. */ + +#define NEW_SUN_CORE + +/* Do implement the attach and detach commands. */ + +#define ATTACH_DETACH + +/* It is safe to look for symsegs on a Sun, because Sun's ld + does not screw up with random garbage at end of file. */ + +#define READ_GDB_SYMSEGS + + +/* Describe the pointer in each stack frame to the previous stack frame + (its caller). */ +#include <machine/reg.h> + +#define GET_RWINDOW_REG(FRAME, REG) \ + (read_memory_integer (&((struct rwindow *)FRAME)->REG, 4)) + +/* FRAME_CHAIN takes a frame's nominal address + and produces the frame's chain-pointer. + + FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address + and produces the nominal address of the caller frame. + + However, if FRAME_CHAIN_VALID returns zero, + it means the given frame is the outermost one and has no caller. + In that case, FRAME_CHAIN_COMBINE is not used. */ + +/* In the case of the Sun 4, the frame-chain's nominal address + is held in the frame pointer register. + + On the Sun4, the frame (in %fp) is %sp for the previous frame. + From the previous frame's %sp, we can find the previous frame's + %fp: it is in the save area just above the previous frame's %sp. + + If we are setting up an arbitrary frame, we'll need to know where + it ends. Hence the following. This part of the frame cache + structure should be checked before it is assumed that this frame's + bottom is in the stack pointer. + + If there isn't a frame below this one, the bottom of this frame is + in the stack pointer. + + If there is a frame below this one, and the frame pointers are + identical, it's a leaf frame and the bottoms are the same also. + + Otherwise the bottom of this frame is the top of the next frame. */ + +#define EXTRA_FRAME_INFO FRAME_ADDR bottom; +#define INIT_EXTRA_FRAME_INFO(fci) \ + (fci)->bottom = \ + ((fci)->next ? \ + ((fci)->frame == (fci)->next_frame ? \ + (fci)->next->bottom : (fci)->next->frame) : \ + read_register (SP_REGNUM)); + +#define FRAME_CHAIN(thisframe) \ + GET_RWINDOW_REG ((thisframe)->frame, rw_in[6]) + +/* Avoid checking FRAME_SAVED_PC since that screws us due to + improperly set up saved PC on a signal trampoline call */ +#if 0 +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 && (FRAME_SAVED_PC (thisframe) >= first_object_file_end)) +#else +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0) +#endif + +#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain) + +/* Define other aspects of the stack frame. */ + +/* Where is the PC for a specific frame */ + +#define FRAME_SAVED_PC(FRAME) frame_saved_pc (FRAME) + +/* If the argument is on the stack, it will be here. */ +#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_STRUCT_ARGS_ADDRESS(fi) ((fi)->frame) + +#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame) + +/* Set VAL to the number of args passed to frame described by FI. + Can set VAL to -1, meaning no way to tell. */ + +/* We can't tell how many args there are + now that the C compiler delays popping them. */ +#define FRAME_NUM_ARGS(val,fi) (val = -1) + +/* Return number of bytes at start of arglist that are not really args. */ + +#define FRAME_ARGS_SKIP 68 + +/* Put here the code to store, into a struct frame_saved_regs, + the addresses of the saved registers of frame described by FRAME_INFO. + This includes special registers such as pc and fp saved in special + ways in the stack frame. sp is even more special: + the address we return for it IS the sp for the next frame. + + Note that on register window machines, we are currently making the + assumption that window registers are being saved somewhere in the + frame in which they are being used. If they are stored in an + inferior frame, find_saved_register will break. + + On the Sun 4, the only time all registers are saved is when + a dummy frame is involved. Otherwise, the only saved registers + are the LOCAL and IN registers which are saved as a result + of the "save/restore" opcodes. This condition is determined + by address rather than by value. */ + +#define FRAME_FIND_SAVED_REGS(fi, frame_saved_regs) \ +{ register int regnum; \ + register CORE_ADDR pc; \ + FRAME_ADDR frame = read_register (FP_REGNUM); \ + FRAME fid = FRAME_INFO_ID (fi); \ + if (!fid) fatal ("Bad frame info struct in FRAME_FIND_SAVED_REGS"); \ + bzero (&(frame_saved_regs), sizeof (frame_saved_regs)); \ + if ((fi)->pc >= frame - CALL_DUMMY_LENGTH - 0x140 \ + && (fi)->pc <= frame) \ + { \ + for (regnum = 1; regnum < 8; regnum++) \ + (frame_saved_regs).regs[regnum] = \ + frame + regnum * 4 - 0xa0; \ + for (regnum = 24; regnum < 32; regnum++) \ + (frame_saved_regs).regs[regnum] = \ + frame + (regnum - 24) * 4 - 0xc0; \ + for (regnum = FP0_REGNUM; regnum < FP0_REGNUM + 32; regnum++) \ + (frame_saved_regs).regs[regnum] = \ + frame + (regnum - FP0_REGNUM) * 4 - 0x80; \ + for (regnum = 64; regnum < NUM_REGS; regnum++) \ + (frame_saved_regs).regs[regnum] = \ + frame + (regnum - 64) * 4 - 0xe0; \ + frame = (fi)->bottom ? \ + (fi)->bottom : read_register (SP_REGNUM); \ + } \ + else \ + { \ + frame = (fi)->bottom ? \ + (fi)->bottom : read_register (SP_REGNUM); \ + for (regnum = 16; regnum < 32; regnum++) \ + (frame_saved_regs).regs[regnum] = frame + (regnum-16) * 4; \ + } \ + if ((fi)->next) \ + { \ + /* Pull off either the next frame pointer or \ + the stack pointer */ \ + FRAME_ADDR next_next_frame = \ + ((fi)->next->bottom ? \ + (fi)->next->bottom : \ + read_register (SP_REGNUM)); \ + for (regnum = 8; regnum < 16; regnum++) \ + (frame_saved_regs).regs[regnum] = next_next_frame + regnum * 4; \ + } \ + /* Otherwise, whatever we would get from ptrace(GETREGS) */ \ + /* is accurate */ \ + for (regnum = 30; regnum < 32; regnum++) \ + (frame_saved_regs).regs[regnum] = frame + (regnum-16) * 4; \ + (frame_saved_regs).regs[SP_REGNUM] = frame; \ + (frame_saved_regs).regs[PC_REGNUM] = frame + 15*4; \ +} + +/* Things needed for making the inferior call functions. */ +/* + * First of all, let me give my opinion of what the DUMMY_FRAME + * actually looks like. + * + * | | + * | | + * + - - - - - - - - - - - - - - - - +<-- fp (level 0) + * | | + * | | + * | | + * | | + * | Frame of innermost program | + * | function | + * | | + * | | + * | | + * | | + * | | + * |---------------------------------|<-- sp (level 0), fp (c) + * | | + * DUMMY | fp0-31 | + * | | + * | ------ |<-- fp - 0x80 + * FRAME | g0-7 |<-- fp - 0xa0 + * | i0-7 |<-- fp - 0xc0 + * | other |<-- fp - 0xe0 + * | ? | + * | ? | + * |---------------------------------|<-- sp' = fp - 0x140 + * | | + * xcution start | | + * sp' + 0x94 -->| CALL_DUMMY (x code) | + * | | + * | | + * |---------------------------------|<-- sp'' = fp - 0x200 + * | align sp to 8 byte boundary | + * | ==> args to fn <== | + * Room for | | + * i & l's + agg | CALL_DUMMY_STACK_ADJUST = 0x0x44| + * |---------------------------------|<-- final sp (variable) + * | | + * | Where function called will | + * | build frame. | + * | | + * | | + * + * I understand everything in this picture except what the space + * between fp - 0xe0 and fp - 0x140 is used for. Oh, and I don't + * understand why there's a large chunk of CALL_DUMMY that never gets + * executed (its function is superceeded by PUSH_DUMMY_FRAME; they + * are designed to do the same thing). + * + * PUSH_DUMMY_FRAME saves the registers above sp' and pushes the + * register file stack down one. + * + * call_function then writes CALL_DUMMY, pushes the args onto the + * stack, and adjusts the stack pointer. + * + * run_stack_dummy then starts execution (in the middle of + * CALL_DUMMY, as directed by call_function). + */ + +/* Push an empty stack frame, to record the current PC, etc. */ + +/* Note: to be perfectly correct, we have to restore the + IN registers (which were the OUT registers of the calling frame). */ +/* Note that the write's are of registers in the context of the newly + pushed frame. Thus the the fp*'s, the g*'s, the i*'s, and + the others, of the new frame, are being saved. + The locals are new; they don't need to be saved. The i's and l's of + the last frame were saved by the do_save_insn in the register + file (ie. on the stack, since a context switch happended imm after) */ +/* We note that the return pointer register does not *need* to have + the pc saved into it (return from this frame will be accomplished + by a POP_FRAME), however, just in case it might be needed, we will + leave it. However, we will write the original value of RP into the + location on the stack for saving i7 (what rp turns into upon call); + this way we don't loose the value with our function call. */ +/* Note that the pc saved must be 8 less than the actual pc, since + both POP_FRAME and the normal return sequence on the sparc return + to 8 more than the value of RP_REGNUM */ + +#define PUSH_DUMMY_FRAME \ +{ extern char registers[]; \ + register int regnum; \ + CORE_ADDR fp = read_register (FP_REGNUM); \ + CORE_ADDR pc = read_register (PC_REGNUM) - 8; \ + CORE_ADDR rp = read_register (RP_REGNUM); \ + void do_save_insn (); \ + supply_register (RP_REGNUM, &pc); \ + do_save_insn (0x140); \ + fp = read_register (FP_REGNUM); \ + write_memory (fp - 0x80, ®isters[REGISTER_BYTE (FP0_REGNUM)], 32 * 4);\ + write_memory (fp - 0xa0, ®isters[REGISTER_BYTE (0)], 8 * 4); \ + write_memory (fp - 0xc0, ®isters[REGISTER_BYTE (24)], 7 * 4); \ + write_memory (fp - 0xa4, &rp, 4); \ + write_memory (fp - 0xe0, ®isters[REGISTER_BYTE (64)], 8 * 4); \ +} + +/* Discard from the stack the innermost frame, + restoring all saved registers. + Note that the values stored in fsr by get_frame_saved_regs are *in + the context of the inferior frame*. What this means is that the i + regs of fsr must be restored into the o regs of the frame popped + into. We don't care about the output regs of the inferior frame. + + This is true for dummy frames. Is it true for normal frames? It + really does appear so. */ + +#define POP_FRAME \ +{ register FRAME frame = get_current_frame (); \ + register CORE_ADDR fp; \ + register CORE_ADDR pc; \ + register int regnum; \ + struct frame_saved_regs fsr; \ + struct frame_info *fi; \ + char raw_buffer[REGISTER_BYTES]; \ + void do_restore_insn (); \ + fi = get_frame_info (frame); \ + fp = fi->frame; \ + get_frame_saved_regs (fi, &fsr); \ + pc = read_memory_integer (fsr.regs[PC_REGNUM], 4); \ + do_restore_insn (PC_ADJUST (pc)); \ + if (fsr.regs[FP0_REGNUM]) \ + { \ + read_memory (fsr.regs[FP0_REGNUM], raw_buffer, 32 * 4); \ + write_register_bytes (REGISTER_BYTE (FP0_REGNUM), raw_buffer, 32 * 4); \ + } \ + if (fsr.regs[1]) \ + { \ + read_memory (fsr.regs[1], raw_buffer, 7 * 4); \ + write_register_bytes (REGISTER_BYTE (1), raw_buffer, 7 * 4); \ + } \ + if (fsr.regs[24]) \ + { \ + read_memory (fsr.regs[24], raw_buffer, 8 * 4); \ + write_register_bytes (REGISTER_BYTE (8), raw_buffer, 8 * 4); \ + } \ + if (fsr.regs[PS_REGNUM]) \ + write_register (PS_REGNUM, read_memory_integer (fsr.regs[PS_REGNUM], 4)); \ + if (fsr.regs[Y_REGNUM]) \ + write_register (Y_REGNUM, read_memory_integer (fsr.regs[Y_REGNUM], 4)); \ + if (fsr.regs[NPC_REGNUM]) \ + write_register (NPC_REGNUM, read_memory_integer (fsr.regs[NPC_REGNUM], 4)); \ + flush_cached_frames (); \ + set_current_frame ( create_new_frame (read_register (FP_REGNUM), \ + read_pc ())); } + +/* This sequence of words is the instructions + + save %sp,-0x140,%sp + std %f30,[%fp-0x08] + std %f28,[%fp-0x10] + std %f26,[%fp-0x18] + std %f24,[%fp-0x20] + std %f22,[%fp-0x28] + std %f20,[%fp-0x30] + std %f18,[%fp-0x38] + std %f16,[%fp-0x40] + std %f14,[%fp-0x48] + std %f12,[%fp-0x50] + std %f10,[%fp-0x58] + std %f8,[%fp-0x60] + std %f6,[%fp-0x68] + std %f4,[%fp-0x70] + std %f2,[%fp-0x78] + std %f0,[%fp-0x80] + std %g6,[%fp-0x88] + std %g4,[%fp-0x90] + std %g2,[%fp-0x98] + std %g0,[%fp-0xa0] + std %i6,[%fp-0xa8] + std %i4,[%fp-0xb0] + std %i2,[%fp-0xb8] + std %i0,[%fp-0xc0] + nop ! stcsr [%fp-0xc4] + nop ! stfsr [%fp-0xc8] + nop ! wr %npc,[%fp-0xcc] + nop ! wr %pc,[%fp-0xd0] + rd %tbr,%o0 + st %o0,[%fp-0xd4] + rd %wim,%o1 + st %o0,[%fp-0xd8] + rd %psr,%o0 + st %o0,[%fp-0xdc] + rd %y,%o0 + st %o0,[%fp-0xe0] + + /..* The arguments are pushed at this point by GDB; + no code is needed in the dummy for this. + The CALL_DUMMY_START_OFFSET gives the position of + the following ld instruction. *../ + + ld [%sp+0x58],%o5 + ld [%sp+0x54],%o4 + ld [%sp+0x50],%o3 + ld [%sp+0x4c],%o2 + ld [%sp+0x48],%o1 + call 0x00000000 + ld [%sp+0x44],%o0 + nop + ta 1 + nop + + note that this is 192 bytes, which is a multiple of 8 (not only 4) bytes. + note that the `call' insn is a relative, not an absolute call. + note that the `nop' at the end is needed to keep the trap from + clobbering things (if NPC pointed to garbage instead). + +We actually start executing at the `sethi', since the pushing of the +registers (as arguments) is done by PUSH_DUMMY_FRAME. If this were +real code, the arguments for the function called by the CALL would be +pushed between the list of ST insns and the CALL, and we could allow +it to execute through. But the arguments have to be pushed by GDB +after the PUSH_DUMMY_FRAME is done, and we cannot allow these ST +insns to be performed again, lest the registers saved be taken for +arguments. */ + +#define CALL_DUMMY { 0x9de3bee0, 0xfd3fbff8, 0xf93fbff0, 0xf53fbfe8, \ + 0xf13fbfe0, 0xed3fbfd8, 0xe93fbfd0, 0xe53fbfc8, \ + 0xe13fbfc0, 0xdd3fbfb8, 0xd93fbfb0, 0xd53fbfa8, \ + 0xd13fbfa0, 0xcd3fbf98, 0xc93fbf90, 0xc53fbf88, \ + 0xc13fbf80, 0xcc3fbf78, 0xc83fbf70, 0xc43fbf68, \ + 0xc03fbf60, 0xfc3fbf58, 0xf83fbf50, 0xf43fbf48, \ + 0xf03fbf40, 0x01000000, 0x01000000, 0x01000000, \ + 0x01000000, 0x91580000, 0xd027bf50, 0x93500000, \ + 0xd027bf4c, 0x91480000, 0xd027bf48, 0x91400000, \ + 0xd027bf44, 0xda03a058, 0xd803a054, 0xd603a050, \ + 0xd403a04c, 0xd203a048, 0x40000000, 0xd003a044, \ + 0x01000000, 0x91d02001, 0x01000000, 0x01000000} + +#define CALL_DUMMY_LENGTH 192 + +#define CALL_DUMMY_START_OFFSET 148 + +#define CALL_DUMMY_STACK_ADJUST 68 + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. */ + +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, type) \ +{ \ + *(int *)((char *) dummyname+168) = (0x40000000|((fun-(pc+168))>>2)); \ + if (TYPE_CODE (type) == TYPE_CODE_STRUCT \ + || TYPE_CODE (type) == TYPE_CODE_UNION) \ + *(int *)((char *) dummyname+176) = (TYPE_LENGTH (type) & 0x1fff); \ +} + + +/* Sparc has no reliable single step ptrace call */ + +#define NO_SINGLE_STEP 1 + +/* It does have a wait structure, and it might help things out . . . */ + +#define HAVE_WAIT_STRUCT + +/* Handle a feature in the sun4 compiler ("call .stret4" at the end of + functions returning structures). */ + +#define SUN4_COMPILER_FEATURE + +/* We need two arguments (in general) to the "info frame" command. + Note that the definition of this macro implies that there exists a + function "setup_arbitrary_frame" in mach-dep.c */ + +#define FRAME_SPECIFICATION_DYADIC + +/* KDB stuff flushed for now. */ +@ + + +1.2 +log +@Don't stop the stack trace until the "next frame pointer" is zero. +@ +text +@d39 11 +@ + + +1.1 +log +@Initial revision +@ +text +@d66 1 +a66 1 +#define STACK_END_ADDR 0xff000000 +d307 3 +d312 4 +@ diff --git a/gdb/RCS/m68k-pinsn.c,v b/gdb/RCS/m68k-pinsn.c,v new file mode 100644 index 0000000..d8c7f84 --- /dev/null +++ b/gdb/RCS/m68k-pinsn.c,v @@ -0,0 +1,824 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.20.19.34; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.19.29.33; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Change HPUX_ASM to USG_SGS_ASM since it's really the Sys V +"Software Generation System" that we are fighting here. +@ +text +@/* Print m68k instructions for GDB, the GNU debugger. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "opcode.h" + +/* 68k instructions are never longer than this many bytes. */ +#define MAXLEN 22 + +/* Number of elements in the opcode table. */ +#define NOPCODES (sizeof m68k_opcodes / sizeof m68k_opcodes[0]) + +extern char *reg_names[]; +char *fpcr_names[] = { "", "fpiar", "fpsr", "fpiar/fpsr", "fpcr", + "fpiar/fpcr", "fpsr/fpcr", "fpiar-fpcr"}; + +static unsigned char *print_insn_arg (); +static unsigned char *print_indexed (); +static void print_base (); +static int fetch_arg (); + +#define NEXTBYTE(p) (p += 2, ((char *)p)[-1]) + +#define NEXTWORD(p) \ + (p += 2, ((((char *)p)[-2]) << 8) + p[-1]) + +#define NEXTLONG(p) \ + (p += 4, (((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]) + +#define NEXTSINGLE(p) \ + (p += 4, *((float *)(p - 4))) + +#define NEXTDOUBLE(p) \ + (p += 8, *((double *)(p - 8))) + +#define NEXTEXTEND(p) \ + (p += 12, 0.0) /* Need a function to convert from extended to double + precision... */ + +#define NEXTPACKED(p) \ + (p += 12, 0.0) /* Need a function to convert from packed to double + precision. Actually, it's easier to print a + packed number than a double anyway, so maybe + there should be a special case to handle this... */ + +/* Print the m68k instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +int +print_insn (memaddr, stream) + CORE_ADDR memaddr; + FILE *stream; +{ + unsigned char buffer[MAXLEN]; + register int i; + register unsigned char *p; + register char *d; + register int bestmask; + int best; + + read_memory (memaddr, buffer, MAXLEN); + + bestmask = 0; + best = -1; + for (i = 0; i < NOPCODES; i++) + { + register unsigned int opcode = m68k_opcodes[i].opcode; + register unsigned int match = m68k_opcodes[i].match; + if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24))) + && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16))) + && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8))) + && ((0xff & buffer[3] & match) == (0xff & opcode))) + { + /* Don't use for printout the variants of divul and divsl + that have the same register number in two places. + The more general variants will match instead. */ + for (d = m68k_opcodes[i].args; *d; d += 2) + if (d[1] == 'D') + break; + + /* Don't use for printout the variants of most floating + point coprocessor instructions which use the same + register number in two places, as above. */ + if (*d == 0) + for (d = m68k_opcodes[i].args; *d; d += 2) + if (d[1] == 't') + break; + + if (*d == 0 && match > bestmask) + { + best = i; + bestmask = match; + } + } + } + + /* Handle undefined instructions. */ + if (best < 0) + { + fprintf (stream, "0%o", (buffer[0] << 8) + buffer[1]); + return 2; + } + + fprintf (stream, "%s", m68k_opcodes[best].name); + + /* Point at first word of argument data, + and at descriptor for first argument. */ + p = buffer + 2; + + /* Why do this this way? -MelloN */ + for (d = m68k_opcodes[best].args; *d; d += 2) + { + if (d[0] == '#') + { + if (d[1] == 'l' && p - buffer < 6) + p = buffer + 6; + else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8' ) + p = buffer + 4; + } + if (d[1] >= '1' && d[1] <= '3' && p - buffer < 4) + p = buffer + 4; + if (d[1] >= '4' && d[1] <= '6' && p - buffer < 6) + p = buffer + 6; + } + + d = m68k_opcodes[best].args; + + if (*d) + fputc (' ', stream); + + while (*d) + { + p = print_insn_arg (d, buffer, p, memaddr + p - buffer, stream); + d += 2; + if (*d && *(d - 2) != 'I' && *d != 'k') + fprintf (stream, ","); + } + return p - buffer; +} + +static unsigned char * +print_insn_arg (d, buffer, p, addr, stream) + char *d; + unsigned char *buffer; + register unsigned char *p; + CORE_ADDR addr; /* PC for this arg to be relative to */ + FILE *stream; +{ + register int val; + register int place = d[1]; + int regno; + register char *regname; + register unsigned char *p1; + register double flval; + int flt_p; + + switch (*d) + { + case 'C': + fprintf (stream, "ccr"); + break; + + case 'S': + fprintf (stream, "sr"); + break; + + case 'U': + fprintf (stream, "usp"); + break; + + case 'J': + { + static struct { char *name; int value; } names[] + = {{"sfc", 0x000}, {"dfc", 0x001}, {"cacr", 0x002}, + {"usp", 0x800}, {"vbr", 0x801}, {"caar", 0x802}, + {"msp", 0x803}, {"isp", 0x804}}; + + val = fetch_arg (buffer, place, 12); + for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--) + if (names[regno].value == val) + { + fprintf (stream, names[regno].name); + break; + } + if (regno < 0) + fprintf (stream, "%d", val); + } + break; + + case 'Q': + val = fetch_arg (buffer, place, 3); + if (val == 0) val = 8; + fprintf (stream, "#%d", val); + break; + + case 'M': + val = fetch_arg (buffer, place, 8); + if (val & 0x80) + val = val - 0x100; + fprintf (stream, "#%d", val); + break; + + case 'T': + val = fetch_arg (buffer, place, 4); + fprintf (stream, "#%d", val); + break; + + case 'D': + fprintf (stream, "%s", reg_names[fetch_arg (buffer, place, 3)]); + break; + + case 'A': + fprintf (stream, "%s", reg_names[fetch_arg (buffer, place, 3) + 010]); + break; + + case 'R': + fprintf (stream, "%s", reg_names[fetch_arg (buffer, place, 4)]); + break; + + case 'F': + fprintf (stream, "fp%d", fetch_arg (buffer, place, 3)); + break; + + case 'O': + val = fetch_arg (buffer, place, 6); + if (val & 0x20) + fprintf (stream, "%s", reg_names [val & 7]); + else + fprintf (stream, "%d", val); + break; + + case '+': + fprintf (stream, "(%s)+", reg_names[fetch_arg (buffer, place, 3) + 8]); + break; + + case '-': + fprintf (stream, "-(%s)", reg_names[fetch_arg (buffer, place, 3) + 8]); + break; + + case 'k': + if (place == 'k') + fprintf (stream, "{%s}", reg_names[fetch_arg (buffer, place, 3)]); + else if (place == 'C') + { + val = fetch_arg (buffer, place, 7); + if ( val > 63 ) /* This is a signed constant. */ + val -= 128; + fprintf (stream, "{#%d}", val); + } + else + error ("Invalid arg format in opcode table: \"%c%c\".", + *d, place); + break; + + case '#': + p1 = buffer + 2; + if (place == 's') + val = fetch_arg (buffer, place, 4); + else if (place == 'C') + val = fetch_arg (buffer, place, 7); + else if (place == '8') + val = fetch_arg (buffer, place, 3); + else if (place == '3') + val = fetch_arg (buffer, place, 8); + else if (place == 'b') + val = NEXTBYTE (p1); + else if (place == 'w') + val = NEXTWORD (p1); + else if (place == 'l') + val = NEXTLONG (p1); + else + error ("Invalid arg format in opcode table: \"%c%c\".", + *d, place); + fprintf (stream, "#%d", val); + break; + + case '^': + if (place == 's') + val = fetch_arg (buffer, place, 4); + else if (place == 'C') + val = fetch_arg (buffer, place, 7); + else if (place == '8') + val = fetch_arg (buffer, place, 3); + else if (place == 'b') + val = NEXTBYTE (p); + else if (place == 'w') + val = NEXTWORD (p); + else if (place == 'l') + val = NEXTLONG (p); + else + error ("Invalid arg format in opcode table: \"%c%c\".", + *d, place); + fprintf (stream, "#%d", val); + break; + + case 'B': + if (place == 'b') + val = NEXTBYTE (p); + else if (place == 'w') + val = NEXTWORD (p); + else if (place == 'l') + val = NEXTLONG (p); + else if (place == 'g') + { + val = ((char *)buffer)[1]; + if (val == 0) + val = NEXTWORD (p); + else if (val == -1) + val = NEXTLONG (p); + } + else if (place == 'c') + { + if (buffer[1] & 0x40) /* If bit six is one, long offset */ + val = NEXTLONG (p); + else + val = NEXTWORD (p); + } + else + error ("Invalid arg format in opcode table: \"%c%c\".", + *d, place); + + print_address (addr + val, stream); + break; + + case 'd': + val = NEXTWORD (p); + fprintf (stream, "%d(%s)", val, reg_names[fetch_arg (buffer, place, 3)]); + break; + + case 's': + fprintf (stream, "%s", fpcr_names[fetch_arg (buffer, place, 3)]); + break; + + case 'I': + val = fetch_arg (buffer, 'd', 3); /* Get coprocessor ID... */ + if (val != 1) /* Unusual coprocessor ID? */ + fprintf (stream, "(cpid=%d) ", val); + if (place == 'i') + p += 2; /* Skip coprocessor extended operands */ + break; + + case '*': + case '~': + case '%': + case ';': + case '@@': + case '!': + case '$': + case '?': + case '/': + case '&': + + if (place == 'd') + { + val = fetch_arg (buffer, 'x', 6); + val = ((val & 7) << 3) + ((val >> 3) & 7); + } + else + val = fetch_arg (buffer, 's', 6); + + /* Get register number assuming address register. */ + regno = (val & 7) + 8; + regname = reg_names[regno]; + switch (val >> 3) + { + case 0: + fprintf (stream, "%s", reg_names[val]); + break; + + case 1: + fprintf (stream, "%s", regname); + break; + + case 2: + fprintf (stream, "(%s)", regname); + break; + + case 3: + fprintf (stream, "(%s)+", regname); + break; + + case 4: + fprintf (stream, "-(%s)", regname); + break; + + case 5: + val = NEXTWORD (p); + fprintf (stream, "%d(%s)", val, regname); + break; + + case 6: + p = print_indexed (regno, p, addr, stream); + break; + + case 7: + switch (val & 7) + { + case 0: + val = NEXTWORD (p); + fprintf (stream, "@@#"); + print_address (val, stream); + break; + + case 1: + val = NEXTLONG (p); + fprintf (stream, "@@#"); + print_address (val, stream); + break; + + case 2: + val = NEXTWORD (p); + print_address (addr + val, stream); + break; + + case 3: + p = print_indexed (-1, p, addr, stream); + break; + + case 4: + flt_p = 1; /* Assume it's a float... */ + switch( place ) + { + case 'b': + val = NEXTBYTE (p); + flt_p = 0; + break; + + case 'w': + val = NEXTWORD (p); + flt_p = 0; + break; + + case 'l': + val = NEXTLONG (p); + flt_p = 0; + break; + + case 'f': + flval = NEXTSINGLE(p); + break; + + case 'F': + flval = NEXTDOUBLE(p); + break; + + case 'x': + flval = NEXTEXTEND(p); + break; + + case 'p': + flval = NEXTPACKED(p); + break; + + default: + error ("Invalid arg format in opcode table: \"%c%c\".", + *d, place); + } + if ( flt_p ) /* Print a float? */ + fprintf (stream, "#%g", flval); + else + fprintf (stream, "#%d", val); + break; + + default: + fprintf (stream, "<invalid address mode 0%o>", val); + } + } + break; + + default: + error ("Invalid arg format in opcode table: \"%c\".", *d); + } + + return (unsigned char *) p; +} + +/* Fetch BITS bits from a position in the instruction specified by CODE. + CODE is a "place to put an argument", or 'x' for a destination + that is a general address (mode and register). + BUFFER contains the instruction. */ + +static int +fetch_arg (buffer, code, bits) + unsigned char *buffer; + char code; + int bits; +{ + register int val; + switch (code) + { + case 's': + val = buffer[1]; + break; + + case 'd': /* Destination, for register or quick. */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 9; + break; + + case 'x': /* Destination, for general arg */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 6; + break; + + case 'k': + val = (buffer[3] >> 4); + break; + + case 'C': + val = buffer[3]; + break; + + case '1': + val = (buffer[2] << 8) + buffer[3]; + val >>= 12; + break; + + case '2': + val = (buffer[2] << 8) + buffer[3]; + val >>= 6; + break; + + case '3': + case 'j': + val = (buffer[2] << 8) + buffer[3]; + break; + + case '4': + val = (buffer[4] << 8) + buffer[5]; + val >>= 12; + break; + + case '5': + val = (buffer[4] << 8) + buffer[5]; + val >>= 6; + break; + + case '6': + val = (buffer[4] << 8) + buffer[5]; + break; + + case '7': + val = (buffer[2] << 8) + buffer[3]; + val >>= 7; + break; + + case '8': + val = (buffer[2] << 8) + buffer[3]; + val >>= 10; + break; + + default: + abort (); + } + + switch (bits) + { + case 3: + return val & 7; + case 4: + return val & 017; + case 5: + return val & 037; + case 6: + return val & 077; + case 7: + return val & 0177; + case 8: + return val & 0377; + case 12: + return val & 07777; + default: + abort (); + } +} + +/* Print an indexed argument. The base register is BASEREG (-1 for pc). + P points to extension word, in buffer. + ADDR is the nominal core address of that extension word. */ + +static unsigned char * +print_indexed (basereg, p, addr, stream) + int basereg; + unsigned char *p; + FILE *stream; + CORE_ADDR addr; +{ + register int word; + static char *scales[] = {"", "*2", "*4", "*8"}; + register int base_disp; + register int outer_disp; + char buf[40]; + + word = NEXTWORD (p); + + /* Generate the text for the index register. + Where this will be output is not yet determined. */ + sprintf (buf, "[%s.%c%s]", + reg_names[(word >> 12) & 0xf], + (word & 0x800) ? 'l' : 'w', + scales[(word >> 9) & 3]); + + /* Handle the 68000 style of indexing. */ + + if ((word & 0x100) == 0) + { + print_base (basereg, + ((word & 0x80) ? word | 0xff00 : word & 0xff) + + ((basereg == -1) ? addr : 0), + stream); + fprintf (stream, "%s", buf); + return p; + } + + /* Handle the generalized kind. */ + /* First, compute the displacement to add to the base register. */ + + if (word & 0200) + basereg = -2; + if (word & 0100) + buf[0] = 0; + base_disp = 0; + switch ((word >> 4) & 3) + { + case 2: + base_disp = NEXTWORD (p); + break; + case 3: + base_disp = NEXTLONG (p); + } + if (basereg == -1) + base_disp += addr; + + /* Handle single-level case (not indirect) */ + + if ((word & 7) == 0) + { + print_base (basereg, base_disp, stream); + fprintf (stream, "%s", buf); + return p; + } + + /* Two level. Compute displacement to add after indirection. */ + + outer_disp = 0; + switch (word & 3) + { + case 2: + outer_disp = NEXTWORD (p); + break; + case 3: + outer_disp = NEXTLONG (p); + } + + fprintf (stream, "%d(", outer_disp); + print_base (basereg, base_disp, stream); + + /* If postindexed, print the closeparen before the index. */ + if (word & 4) + fprintf (stream, ")%s", buf); + /* If preindexed, print the closeparen after the index. */ + else + fprintf (stream, "%s)", buf); + + return p; +} + +/* Print a base register REGNO and displacement DISP, on STREAM. + REGNO = -1 for pc, -2 for none (suppressed). */ + +static void +print_base (regno, disp, stream) + int regno; + int disp; + FILE *stream; +{ + if (regno == -2) + fprintf (stream, "%d", disp); + else if (regno == -1) + fprintf (stream, "0x%x", disp); + else + fprintf (stream, "%d(%s)", disp, reg_names[regno]); +} + +/* This is not part of insn printing, but it is machine-specific, + so this is a convenient place to put it. + + Convert a 68881 extended float to a double. + FROM is the address of the extended float. + Store the double in *TO. */ + +convert_from_68881 (from, to) + char *from; + double *to; +{ +#ifdef USG_SGS_ASM + asm ("mov.l 8(%a6),%a0"); + asm ("mov.l 12(%a6),%a1"); + asm ("fmove.x (%a0),%fp0"); + asm ("fmove.d %fp0,(%a1)"); +#else /* not USG_SGS_ASM */ +#if 0 + asm ("movl a6@@(8),a0"); + asm ("movl a6@@(12),a1"); + asm ("fmovex a0@@,fp0"); + asm ("fmoved fp0,a1@@"); +#else + /* Hand-assemble those insns since some assemblers lose + and some have different syntax. */ + asm (".word 020156"); + asm (".word 8"); + asm (".word 021156"); + asm (".word 12"); + asm (".long 0xf2104800"); + asm (".long 0xf2117400"); +#endif +#endif /* not USG_SGS_ASM */ +} + +/* The converse: convert the double *FROM to an extended float + and store where TO points. */ + +convert_to_68881 (from, to) + double *from; + char *to; +{ +#ifdef USG_SGS_ASM + asm ("mov.l 8(%a6),%a0"); + asm ("mov.l 12(%a6),%a1"); + asm ("fmove.d (%a0),%fp0"); + asm ("fmove.x %fp0,(%a1)"); +#else /* not USG_SGS_ASM */ +#if 0 + asm ("movl a6@@(8),a0"); + asm ("movl a6@@(12),a1"); + asm ("fmoved a0@@,fp0"); + asm ("fmovex fp0,a1@@"); +#else + /* Hand-assemble those insns since some assemblers lose. */ + asm (".word 020156"); + asm (".word 8"); + asm (".word 021156"); + asm (".word 12"); + asm (".long 0xf2105400"); + asm (".long 0xf2116800"); +#endif +#endif /* not USG_SGS_ASM */ +} +@ + + +1.1 +log +@Initial revision +@ +text +@d717 1 +a717 1 +#ifdef HPUX_ASM +d722 1 +a722 1 +#else /* not HPUX_ASM */ +d738 1 +a738 1 +#endif /* not HPUX_ASM */ +d748 1 +a748 1 +#ifdef HPUX_ASM +d753 1 +a753 1 +#else /* not HPUX_ASM */ +d768 1 +a768 1 +#endif /* not HPUX_ASM */ +@ diff --git a/gdb/RCS/main.c,v b/gdb/RCS/main.c,v new file mode 100644 index 0000000..1e6fafa --- /dev/null +++ b/gdb/RCS/main.c,v @@ -0,0 +1,1348 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.21.15.02; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.27.21.11.51; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Fix up "munch" so it generates a name that doesn't match its own +"grep" conventions. Change main so that it calls the new name, +and also doesn't use the conventions for functions that should NOT +be called by init.c. +@ +text +@/* Top level for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ +#include "defs.h" +#include "command.h" +#include "param.h" + +#ifdef USG +#include <sys/types.h> +#include <unistd.h> +#endif + +#include <sys/file.h> +#include <stdio.h> +#include <setjmp.h> +#include <signal.h> +#include <sys/param.h> + +#ifdef SET_STACK_LIMIT_HUGE +#include <sys/time.h> +#include <sys/resource.h> + +int original_stack_limit; +#endif + +/* Version number of GDB, as a string. */ + +extern char *version; + +/* + * Declare all cmd_list_element's + */ + +/* Chain containing all defined commands. */ + +struct cmd_list_element *cmdlist; + +/* Chain containing all defined info subcommands. */ + +struct cmd_list_element *infolist; + +/* Chain containing all defined enable subcommands. */ + +struct cmd_list_element *enablelist; + +/* Chain containing all defined disable subcommands. */ + +struct cmd_list_element *disablelist; + +/* Chain containing all defined delete subcommands. */ + +struct cmd_list_element *deletelist; + +/* Chain containing all defined "enable breakpoint" subcommands. */ + +struct cmd_list_element *enablebreaklist; + +/* Chain containing all defined set subcommands */ + +struct cmd_list_element *setlist; + +/* stdio stream that command input is being read from. */ + +FILE *instream; + +/* Current working directory. */ + +char *current_directory; + +/* The directory name is actually stored here (usually). */ +static char dirbuf[MAXPATHLEN]; + +/* Nonzero if we should refrain from using an X window. */ + +int inhibit_windows = 0; + +/* Function to call before reading a command, if nonzero. + The function receives two args: an input stream, + and a prompt string. */ + +void (*window_hook) (); + +extern int frame_file_full_name; + +void free_command_lines (); +char *gdb_read_line (); +static void init_main (); +static void init_cmd_lists (); +void command_loop (); +static void source_command (); +static void print_gdb_version (); + +/* gdb prints this when reading a command interactively */ +static char *prompt; + +/* Buffer used for reading command lines, and the size + allocated for it so far. */ + +char *line; +int linesize; + +/* This is how `error' returns to command level. */ + +jmp_buf to_top_level; + +void +return_to_top_level () +{ + quit_flag = 0; + immediate_quit = 0; + clear_breakpoint_commands (); + clear_momentary_breakpoints (); + delete_current_display (); + do_cleanups (0); + longjmp (to_top_level, 1); +} + +/* Call FUNC with arg ARG, catching any errors. + If there is no error, return the value returned by FUNC. + If there is an error, return zero after printing ERRSTRING + (which is in addition to the specific error message already printed). */ + +int +catch_errors (func, arg, errstring) + int (*func) (); + int arg; + char *errstring; +{ + jmp_buf saved; + int val; + struct cleanup *saved_cleanup_chain; + + saved_cleanup_chain = save_cleanups (); + + bcopy (to_top_level, saved, sizeof (jmp_buf)); + + if (setjmp (to_top_level) == 0) + val = (*func) (arg); + else + { + fprintf (stderr, "%s\n", errstring); + val = 0; + } + + restore_cleanups (saved_cleanup_chain); + + bcopy (saved, to_top_level, sizeof (jmp_buf)); + return val; +} + +/* Handler for SIGHUP. */ + +static void +disconnect () +{ + kill_inferior_fast (); + signal (SIGHUP, SIG_DFL); + kill (getpid (), SIGHUP); +} + +/* Clean up on error during a "source" command (or execution of a + user-defined command). + Close the file opened by the command + and restore the previous input stream. */ + +static void +source_cleanup (stream) + FILE *stream; +{ + /* Instream may be 0; set to it when executing user-defined command. */ + if (instream) + fclose (instream); + instream = stream; +} + + +int +main (argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + extern void request_quit (); + int count; + int inhibit_gdbinit = 0; + int quiet = 0; + int batch = 0; + register int i; + + quit_flag = 0; + linesize = 100; + line = (char *) xmalloc (linesize); + instream = stdin; + + getwd (dirbuf); + current_directory = dirbuf; + +#ifdef SET_STACK_LIMIT_HUGE + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca (particularly stringtab + * in dbxread.c) does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + original_stack_limit = rlim.rlim_cur; + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* SET_STACK_LIMIT_HUGE */ + + /* Look for flag arguments. */ + + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet")) + quiet = 1; + else if (!strcmp (argv[i], "-nx")) + inhibit_gdbinit = 1; + else if (!strcmp (argv[i], "-nw")) + inhibit_windows = 1; + else if (!strcmp (argv[i], "-batch")) + batch = 1, quiet = 1; + else if (!strcmp (argv[i], "-fullname")) + frame_file_full_name = 1; + else if (argv[i][0] == '-') + i++; + } + + /* Run the init function of each source file */ + + init_cmd_lists (); /* This needs to be done first */ + init_all_files (); + init_main (); /* But that omits this file! Do it now */ + + signal (SIGINT, request_quit); + signal (SIGQUIT, SIG_IGN); + if (signal (SIGHUP, SIG_IGN) != SIG_IGN) + signal (SIGHUP, disconnect); + + if (!quiet) + print_gdb_version (); + + /* Process the command line arguments. */ + + count = 0; + for (i = 1; i < argc; i++) + { + register char *arg = argv[i]; + /* Args starting with - say what to do with the following arg + as a filename. */ + if (arg[0] == '-') + { + extern void exec_file_command (), symbol_file_command (); + extern void core_file_command (), directory_command (); + extern void tty_command (); + + if (!strcmp (arg, "-q") || !strcmp (arg, "-nx") + || !strcmp (arg, "-quiet") || !strcmp (arg, "-batch") + || !strcmp (arg, "-fullname")) + /* Already processed above */ + continue; + + if (++i == argc) + fprintf (stderr, "No argument follows \"%s\".\n", arg); + if (!setjmp (to_top_level)) + { + /* -s foo: get syms from foo. -e foo: execute foo. + -se foo: do both with foo. -c foo: use foo as core dump. */ + if (!strcmp (arg, "-se")) + { + exec_file_command (argv[i], !batch); + symbol_file_command (argv[i], !batch); + } + else if (!strcmp (arg, "-s") || !strcmp (arg, "-symbols")) + symbol_file_command (argv[i], !batch); + else if (!strcmp (arg, "-e") || !strcmp (arg, "-exec")) + exec_file_command (argv[i], !batch); + else if (!strcmp (arg, "-c") || !strcmp (arg, "-core")) + core_file_command (argv[i], !batch); + /* -x foo: execute commands from foo. */ + else if (!strcmp (arg, "-x") || !strcmp (arg, "-command") + || !strcmp (arg, "-commands")) + source_command (argv[i]); + /* -d foo: add directory `foo' to source-file directory + search-list */ + else if (!strcmp (arg, "-d") || !strcmp (arg, "-dir") + || !strcmp (arg, "-directory")) + directory_command (argv[i], 0); + /* -cd FOO: specify current directory as FOO. + GDB remembers the precise string FOO as the dirname. */ + else if (!strcmp (arg, "-cd")) + { + int len = strlen (argv[i]); + current_directory = argv[i]; + if (len > 1 && current_directory[len - 1] == '/') + current_directory = savestring (current_directory, len-1); + chdir (current_directory); + init_source_path (); + } + /* -t /def/ttyp1: use /dev/ttyp1 for inferior I/O. */ + else if (!strcmp (arg, "-t") || !strcmp (arg, "-tty")) + tty_command (argv[i], 0); + else + error ("Unknown command-line switch: \"%s\"\n", arg); + } + } + else + { + /* Args not thus accounted for + are treated as, first, the symbol/executable file + and, second, the core dump file. */ + count++; + if (!setjmp (to_top_level)) + switch (count) + { + case 1: + exec_file_command (arg, !batch); + symbol_file_command (arg, !batch); + break; + + case 2: + core_file_command (arg, !batch); + break; + + case 3: + fprintf (stderr, "Excess command line args ignored. (%s%s)\n", + arg, (i == argc - 1) ? "" : " ..."); + } + } + } + + /* Read init file, if it exists in home directory */ + if (getenv ("HOME")) + { + char *s; + s = (char *) xmalloc (strlen (getenv ("HOME")) + 10); + strcpy (s, getenv ("HOME")); + strcat (s, "/.gdbinit"); + if (!inhibit_gdbinit && access (s, R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (s); + } + + /* Read init file, if it exists in current directory. */ + if (!inhibit_gdbinit && access (".gdbinit", R_OK) == 0) + if (!setjmp (to_top_level)) + source_command (".gdbinit"); + + if (batch) + fatal ("Attempt to read commands from stdin in batch mode."); + + if (!quiet) + printf ("Type \"help\" for a list of commands.\n"); + + /* The command loop. */ + + while (1) + { + if (!setjmp (to_top_level)) + command_loop (); + clearerr (stdin); /* Don't get hung if C-d is typed. */ + } +} + +/* Execute the line P as a command. + Pass FROM_TTY as second argument to the defining function. */ + +void +execute_command (p, from_tty) + char *p; + int from_tty; +{ + register struct cmd_list_element *c; + register struct command_line *cmdlines; + + free_all_values (); + while (*p == ' ' || *p == '\t') p++; + if (*p) + { + c = lookup_cmd (&p, cmdlist, "", 0); + if (c->function == 0) + error ("That is not a command, just a help topic."); + else if (c->class == (int) class_user) + { + struct cleanup *old_chain; + + if (*p) + error ("User-defined commands cannot take arguments."); + cmdlines = (struct command_line *) c->function; + if (cmdlines == (struct command_line *) 0) + /* Null command */ + return; + + /* Set the instream to 0, indicating execution of a + user-defined function. */ + old_chain = make_cleanup (source_cleanup, instream); + instream = (FILE *) 0; + while (cmdlines) + { + execute_command (cmdlines->line, 0); + cmdlines = cmdlines->next; + } + do_cleanups (old_chain); + } + else + /* Pass null arg rather than an empty one. */ + (*c->function) (*p ? p : 0, from_tty); + } +} + +static void +do_nothing () +{ +} + +/* Read commands from `instream' and execute them + until end of file. */ +void +command_loop () +{ + struct cleanup *old_chain; + while (!feof (instream)) + { + if (window_hook && instream == stdin) + (*window_hook) (instream, prompt); + + quit_flag = 0; + old_chain = make_cleanup (do_nothing, 0); + execute_command (gdb_read_line (instream == stdin ? prompt : 0, + instream == stdin), + instream == stdin); + /* Do any commands attached to breakpoint we stopped at. */ + do_breakpoint_commands (); + do_cleanups (old_chain); + } +} + +#ifdef SIGTSTP +static void +stop_sig () +{ + signal (SIGTSTP, SIG_DFL); + sigsetmask (0); + kill (getpid (), SIGTSTP); + signal (SIGTSTP, stop_sig); + printf ("%s", prompt); + fflush (stdout); + + /* Forget about any previous command -- null line now will do nothing. */ + *line = 0; +} +#endif /* SIGTSTP */ + +/* Commands call this if they do not want to be repeated by null lines. */ + +void +dont_repeat () +{ + *line = 0; +} + +/* Read one line from the command input stream `instream' + into the buffer `line' (whose current length is `linesize'). + The buffer is made bigger as necessary. + Returns the address of the start of the line. */ + +char * +gdb_read_line (prompt, repeat) + char *prompt; + int repeat; +{ + register char *p = line; + register char *p1; + register int c; + char *nline; + + /* Control-C quits instantly if typed while in this loop + since it should not wait until the user types a newline. */ + immediate_quit++; +#ifdef SIGTSTP + signal (SIGTSTP, stop_sig); +#endif + + if (prompt) + { + printf (prompt); + fflush (stdout); + } + + while (1) + { + c = fgetc (instream); + if (c == -1 || c == '\n') + break; + /* Ignore backslash-newline; keep adding to the same line. */ + else if (c == '\\') + { + int c1 = fgetc (instream); + if (c1 == '\n') + continue; + else + ungetc (c1, instream); + } + + if (p - line == linesize - 1) + { + linesize *= 2; + nline = (char *) xrealloc (line, linesize); + p += nline - line; + line = nline; + } + *p++ = c; + } + +#ifdef SIGTSTP + signal (SIGTSTP, SIG_DFL); +#endif + immediate_quit--; + + /* If we just got an empty line, and that is supposed + to repeat the previous command, leave the last input unchanged. */ + if (p == line && repeat) + return line; + + /* If line is a comment, clear it out. */ + p1 = line; + while ((c = *p1) == ' ' || c == '\t') p1++; + if (c == '#') + p = line; + + *p = 0; + + return line; +} + +/* Read lines from the input stream + and accumulate them in a chain of struct command_line's + which is then returned. */ + +struct command_line * +read_command_lines () +{ + struct command_line *first = 0; + register struct command_line *next, *tail = 0; + register char *p, *p1; + struct cleanup *old_chain = 0; + + while (1) + { + dont_repeat (); + p = gdb_read_line (0, 1); + /* Remove leading and trailing blanks. */ + while (*p == ' ' || *p == '\t') p++; + p1 = p + strlen (p); + while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t')) p1--; + + /* Is this "end"? */ + if (p1 - p == 3 && !strncmp (p, "end", 3)) + break; + + /* No => add this line to the chain of command lines. */ + next = (struct command_line *) xmalloc (sizeof (struct command_line)); + next->line = savestring (p, p1 - p); + next->next = 0; + if (tail) + { + tail->next = next; + } + else + { + /* We just read the first line. + From now on, arrange to throw away the lines we have + if we quit or get an error while inside this function. */ + first = next; + old_chain = make_cleanup (free_command_lines, &first); + } + tail = next; + } + + dont_repeat (); + + /* Now we are about to return the chain to our caller, + so freeing it becomes his responsibility. */ + if (first) + discard_cleanups (old_chain); + return first; +} + +/* Free a chain of struct command_line's. */ + +void +free_command_lines (lptr) + struct command_line **lptr; +{ + register struct command_line *l = *lptr; + register struct command_line *next; + + while (l) + { + next = l->next; + free (l->line); + free (l); + l = next; + } +} + +/* Add an element to the list of info subcommands. */ + +void +add_info (name, fun, doc) + char *name; + void (*fun) (); + char *doc; +{ + add_cmd (name, no_class, fun, doc, &infolist); +} + +/* Add an alias to the list of info subcommands. */ + +void +add_info_alias (name, oldname, abbrev_flag) + char *name; + char *oldname; + int abbrev_flag; +{ + add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist); +} + +/* The "info" command is defined as a prefix, with allow_unknown = 0. + Therefore, its own definition is called only for "info" with no args. */ + +static void +info_command () +{ + printf ("\"info\" must be followed by the name of an info command.\n"); + help_list (infolist, "info ", -1, stdout); +} + +/* Add an element to the list of commands. */ + +void +add_com (name, class, fun, doc) + char *name; + int class; + void (*fun) (); + char *doc; +{ + add_cmd (name, class, fun, doc, &cmdlist); +} + +/* Add an alias or abbreviation command to the list of commands. */ + +void +add_com_alias (name, oldname, class, abbrev_flag) + char *name; + char *oldname; + int class; + int abbrev_flag; +{ + add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist); +} + +void +error_no_arg (why) + char *why; +{ + error ("Argument required (%s).", why); +} + +static void +help_command (command, from_tty) + char *command; + int from_tty; /* Ignored */ +{ + help_cmd (command, stdout); +} + +static void +validate_comname (comname) + char *comname; +{ + register char *p; + + if (comname == 0) + error_no_arg ("name of command to define"); + + p = comname; + while (*p) + { + if (!(*p >= 'A' && *p <= 'Z') + && !(*p >= 'a' && *p <= 'z') + && !(*p >= '0' && *p <= '9') + && *p != '-') + error ("Junk in argument list: \"%s\"", p); + p++; + } +} + +static void +define_command (comname, from_tty) + char *comname; + int from_tty; +{ + register struct command_line *cmds; + register struct cmd_list_element *c; + char *tem = comname; + + validate_comname (comname); + + c = lookup_cmd (&tem, cmdlist, "", -1); + if (c) + { + if (c->class == (int) class_user || c->class == (int) class_alias) + tem = "Redefine command \"%s\"? "; + else + tem = "Really redefine built-in command \"%s\"? "; + if (!query (tem, comname)) + error ("Command \"%s\" not redefined.", comname); + } + + if (from_tty) + { + printf ("Type commands for definition of \"%s\".\n\ +End with a line saying just \"end\".\n", comname); + fflush (stdout); + } + comname = savestring (comname, strlen (comname)); + + cmds = read_command_lines (); + + if (c && c->class == (int) class_user) + free_command_lines (&c->function); + + add_com (comname, class_user, cmds, + (c && c->class == (int) class_user) + ? c->doc : savestring ("User-defined.", 13)); +} + +static void +document_command (comname, from_tty) + char *comname; + int from_tty; +{ + struct command_line *doclines; + register struct cmd_list_element *c; + char *tem = comname; + + validate_comname (comname); + + c = lookup_cmd (&tem, cmdlist, "", 0); + + if (c->class != (int) class_user) + error ("Command \"%s\" is built-in.", comname); + + if (from_tty) + printf ("Type documentation for \"%s\".\n\ +End with a line saying just \"end\".\n", comname); + + doclines = read_command_lines (); + + if (c->doc) free (c->doc); + + { + register struct command_line *cl1; + register int len = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + len += strlen (cl1->line) + 1; + + c->doc = (char *) xmalloc (len + 1); + *c->doc = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + { + strcat (c->doc, cl1->line); + if (cl1->next) + strcat (c->doc, "\n"); + } + } + + free_command_lines (&doclines); +} + +static void +copying_info () +{ + immediate_quit++; + printf (" GDB GENERAL PUBLIC LICENSE\n\ + (Clarified 11 Feb 1988)\n\ +\n\ + Copyright (C) 1988 Richard M. Stallman\n\ + Everyone is permitted to copy and distribute verbatim copies\n\ + of this license, but changing it is not allowed.\n\ + You can also use this wording to make the terms for other programs.\n\ +\n\ + The license agreements of most software companies keep you at the\n\ +mercy of those companies. By contrast, our general public license is\n\ +intended to give everyone the right to share GDB. To make sure that\n\ +you get the rights we want you to have, we need to make restrictions\n\ +that forbid anyone to deny you these rights or to ask you to surrender\n\ +the rights. Hence this license agreement.\n\ +\n\ + Specifically, we want to make sure that you have the right to give\n\ +away copies of GDB, that you receive source code or else can get it\n\ +if you want it, that you can change GDB or use pieces of it in new\n\ +free programs, and that you know you can do these things.\n\ +--Type Return to print more--"); + fflush (stdout); + gdb_read_line (0, 0); + + printf ("\ + To make sure that everyone has such rights, we have to forbid you to\n\ +deprive anyone else of these rights. For example, if you distribute\n\ +copies of GDB, you must give the recipients all the rights that you\n\ +have. You must make sure that they, too, receive or can get the\n\ +source code. And you must tell them their rights.\n\ +\n\ + Also, for our own protection, we must make certain that everyone\n\ +finds out that there is no warranty for GDB. If GDB is modified by\n\ +someone else and passed on, we want its recipients to know that what\n\ +they have is not what we distributed, so that any problems introduced\n\ +by others will not reflect on our reputation.\n\ +\n\ + Therefore we (Richard Stallman and the Free Software Foundation,\n\ +Inc.) make the following terms which say what you must do to be\n\ +allowed to distribute or change GDB.\n\ +--Type Return to print more--"); + fflush (stdout); + gdb_read_line (0, 0); + + printf ("\ + COPYING POLICIES\n\ +\n\ + 1. You may copy and distribute verbatim copies of GDB source code as\n\ +you receive it, in any medium, provided that you conspicuously and\n\ +appropriately publish on each copy a valid copyright notice \"Copyright\n\ +\(C) 1988 Free Software Foundation, Inc.\" (or with whatever year is\n\ +appropriate); keep intact the notices on all files that refer\n\ +to this License Agreement and to the absence of any warranty; and give\n\ +any other recipients of the GDB program a copy of this License\n\ +Agreement along with the program. You may charge a distribution fee\n\ +for the physical act of transferring a copy.\n\ +\n\ + 2. You may modify your copy or copies of GDB or any portion of it,\n\ +and copy and distribute such modifications under the terms of\n\ +Paragraph 1 above, provided that you also do the following:\n\ +\n\ + a) cause the modified files to carry prominent notices stating\n\ + that you changed the files and the date of any change; and\n\ +--Type Return to print more--"); + fflush (stdout); + gdb_read_line (0, 0); + + printf ("\ + b) cause the whole of any work that you distribute or publish,\n\ + that in whole or in part contains or is a derivative of GDB\n\ + or any part thereof, to be licensed to all third parties on terms\n\ + identical to those contained in this License Agreement (except that\n\ + you may choose to grant more extensive warranty protection to some\n\ + or all third parties, at your option).\n\ +\n"); + printf ("\ + c) if the modified program serves as a debugger, cause it\n\ + when started running in the simplest and usual way, to print\n\ + an announcement including a valid copyright notice\n\ + \"Copyright (C) 1988 Free Software Foundation, Inc.\" (or with\n\ + the year that is appropriate), saying that there is no warranty\n\ + (or else, saying that you provide a warranty) and that users may\n\ + redistribute the program under these conditions, and telling the user\n\ + how to view a copy of this License Agreement.\n\ +\n\ + d) You may charge a distribution fee for the physical act of\n\ + transferring a copy, and you may at your option offer warranty\n\ + protection in exchange for a fee.\n\ +\n\ +Mere aggregation of another unrelated program with this program (or its\n\ +derivative) on a volume of a storage or distribution medium does not bring\n\ +the other program under the scope of these terms.\n\ +--Type Return to print more--"); + fflush (stdout); + gdb_read_line (0, 0); + + printf ("\ + 3. You may copy and distribute GDB (or a portion or derivative of it,\n\ +under Paragraph 2) in object code or executable form under the terms of\n\ +Paragraphs 1 and 2 above provided that you also do one of the following:\n\ +\n\ + a) accompany it with the complete corresponding machine-readable\n\ + source code, which must be distributed under the terms of\n\ + Paragraphs 1 and 2 above; or,\n\ +\n\ + b) accompany it with a written offer, valid for at least three\n\ + years, to give any third party free (except for a nominal\n\ + shipping charge) a complete machine-readable copy of the\n\ + corresponding source code, to be distributed under the terms of\n\ + Paragraphs 1 and 2 above; or,\n\n"); + + printf ("\ + c) accompany it with the information you received as to where the\n\ + corresponding source code may be obtained. (This alternative is\n\ + allowed only for noncommercial distribution and only if you\n\ + received the program in object code or executable form alone.)\n\ +\n\ +For an executable file, complete source code means all the source code for\n\ +all modules it contains; but, as a special exception, it need not include\n\ +source code for modules which are standard libraries that accompany the\n\ +operating system on which the executable file runs.\n\ +--Type Return to print more--"); + fflush (stdout); + gdb_read_line (0, 0); + + printf ("\ + 4. You may not copy, sublicense, distribute or transfer GDB\n\ +except as expressly provided under this License Agreement. Any attempt\n\ +otherwise to copy, sublicense, distribute or transfer GDB is void and\n\ +your rights to use the program under this License agreement shall be\n\ +automatically terminated. However, parties who have received computer\n\ +software programs from you with this License Agreement will not have\n\ +their licenses terminated so long as such parties remain in full compliance.\n\ +\n\ +"); + printf ("\ + 5. If you wish to incorporate parts of GDB into other free\n\ +programs whose distribution conditions are different, write to the Free\n\ +Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet\n\ +worked out a simple rule that can be stated here, but we will often permit\n\ +this. We will be guided by the two goals of preserving the free status of\n\ +all derivatives of our free software and of promoting the sharing and reuse\n\ +of software.\n\ +\n\ +In other words, go ahead and share GDB, but don't try to stop\n\ +anyone else from sharing it farther. Help stamp out software hoarding!\n\ +"); + immediate_quit--; +} + +static void +warranty_info () +{ + immediate_quit++; + printf (" NO WARRANTY\n\ +\n\ + BECAUSE GDB IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO\n\ +WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT\n\ +WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,\n\ +RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE GDB \"AS IS\" WITHOUT\n\ +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT\n\ +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\ +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND\n\ +PERFORMANCE OF GDB IS WITH YOU. SHOULD GDB PROVE DEFECTIVE, YOU\n\ +ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n"); + + printf ("\ + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.\n\ +STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY\n\ +WHO MAY MODIFY AND REDISTRIBUTE GDB, BE LIABLE TO\n\ +YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER\n\ +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR\n\ +INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA\n\ +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A\n\ +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GDB, EVEN\n\ +IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR\n\ +ANY CLAIM BY ANY OTHER PARTY.\n"); + immediate_quit--; +} + +static void +print_gdb_version () +{ + printf ("GDB %s, Copyright (C) 1988 Free Software Foundation, Inc.\n\ +There is ABSOLUTELY NO WARRANTY for GDB; type \"info warranty\" for details.\n\ +GDB is free software and you are welcome to distribute copies of it\n\ + under certain conditions; type \"info copying\" to see the conditions.\n", + version); +} + +static void +version_info () +{ + immediate_quit++; + print_gdb_version (); + immediate_quit--; +} + +/* xgdb calls this to reprint the usual GDB prompt. */ + +void +print_prompt () +{ + printf ("%s", prompt); + fflush (stdout); +} + +/* Command to specify a prompt string instead of "(gdb) ". */ + +static void +set_prompt_command (text) + char *text; +{ + char *p, *q; + register int c; + char *new; + + if (text == 0) + error_no_arg ("string to which to set prompt"); + + new = (char *) xmalloc (strlen (text) + 2); + p = text; q = new; + while (c = *p++) + { + if (c == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + if (*p == 0) + break; + c = parse_escape (&p); + if (c == 0) + break; /* C loses */ + else if (c > 0) + *q++ = c; + } + else + *q++ = c; + } + if (*(p - 1) != '\\') + *q++ = ' '; + *q++ = '\0'; + new = (char *) xrealloc (new, q - new); + free (prompt); + prompt = new; +} + +static void +quit_command () +{ + if (have_inferior_p ()) + { + if (query ("The program is running. Quit anyway? ")) + { + /* Prevent any warning message from reopen_exec_file, in case + we have a core file that's inconsistent with the exec file. */ + exec_file_command (0, 0); + kill_inferior (); + } + else + error ("Not confirmed."); + } + exit (0); +} + +int +input_from_terminal_p () +{ + return instream == stdin; +} + +static void +pwd_command (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) error ("The \"pwd\" command does not take an argument: %s", arg); + getwd (dirbuf); + + if (strcmp (dirbuf, current_directory)) + printf ("Working directory %s\n (canonically %s).\n", + current_directory, dirbuf); + else + printf ("Working directory %s.\n", current_directory); +} + +static void +cd_command (dir, from_tty) + char *dir; + int from_tty; +{ + int len; + int change; + + if (dir == 0) + error_no_arg ("new working directory"); + + len = strlen (dir); + dir = savestring (dir, len - (len > 1 && dir[len-1] == '/')); + if (dir[0] == '/') + current_directory = dir; + else + { + current_directory = concat (current_directory, "/", dir); + free (dir); + } + + /* Now simplify any occurrences of `.' and `..' in the pathname. */ + + change = 1; + while (change) + { + char *p; + change = 0; + + for (p = current_directory; *p;) + { + if (!strncmp (p, "/./", 2) + && (p[2] == 0 || p[2] == '/')) + strcpy (p, p + 2); + else if (!strncmp (p, "/..", 3) + && (p[3] == 0 || p[3] == '/') + && p != current_directory) + { + char *q = p; + while (q != current_directory && q[-1] != '/') q--; + if (q != current_directory) + { + strcpy (q-1, p+3); + p = q-1; + } + } + else p++; + } + } + + if (chdir (dir) < 0) + perror_with_name (dir); + + if (from_tty) + pwd_command ((char *) 0, 1); +} + +static void +source_command (file) + char *file; +{ + FILE *stream; + struct cleanup *cleanups; + + if (file == 0) + error_no_arg ("file to read commands from"); + + stream = fopen (file, "r"); + if (stream == 0) + perror_with_name (file); + + cleanups = make_cleanup (source_cleanup, instream); + + instream = stream; + + command_loop (); + + do_cleanups (cleanups); +} + +static void +echo_command (text) + char *text; +{ + char *p = text; + register int c; + + if (text) + while (c = *p++) + { + if (c == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + if (*p == 0) + return; + + c = parse_escape (&p); + if (c >= 0) + fputc (c, stdout); + } + else + fputc (c, stdout); + } +} + +static void +dump_me_command () +{ + if (query ("Should GDB dump core? ")) + { + signal (SIGQUIT, SIG_DFL); + kill (getpid (), SIGQUIT); + } +} + +static void +init_cmd_lists () +{ + cmdlist = (struct cmd_list_element *) 0; + infolist = (struct cmd_list_element *) 0; + enablelist = (struct cmd_list_element *) 0; + disablelist = (struct cmd_list_element *) 0; + deletelist = (struct cmd_list_element *) 0; + enablebreaklist = (struct cmd_list_element *) 0; + setlist = (struct cmd_list_element *) 0; +} + +static void +init_main () +{ + prompt = savestring ("(gdb) ", 6); + + /* Define the classes of commands. + They will appear in the help list in the reverse of this order. */ + + add_cmd ("obscure", class_obscure, 0, "Obscure features.", &cmdlist); + add_cmd ("alias", class_alias, 0, "Aliases of other commands.", &cmdlist); + add_cmd ("user", class_user, 0, "User-defined commands.\n\ +The commands in this class are those defined by the user.\n\ +Use the \"define\" command to define a command.", &cmdlist); + add_cmd ("support", class_support, 0, "Support facilities.", &cmdlist); + add_cmd ("status", class_info, 0, "Status inquiries.", &cmdlist); + add_cmd ("files", class_files, 0, "Specifying and examining files.", &cmdlist); + add_cmd ("breakpoints", class_breakpoint, 0, "Making program stop at certain points.", &cmdlist); + add_cmd ("data", class_vars, 0, "Examining data.", &cmdlist); + add_cmd ("stack", class_stack, 0, "Examining the stack.\n\ +The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\ +counting from zero for the innermost (currently executing) frame.\n\n\ +At any time gdb identifies one frame as the \"selected\" frame.\n\ +Variable lookups are done with respect to the selected frame.\n\ +When the program being debugged stops, gdb selects the innermost frame.\n\ +The commands below can be used to select other frames by number or address.", + &cmdlist); + add_cmd ("running", class_run, 0, "Running the program.", &cmdlist); + + add_com ("pwd", class_files, pwd_command, + "Print working directory. This is used for your program as well."); + add_com ("cd", class_files, cd_command, + "Set working directory to DIR for debugger and program being debugged.\n\ +The change does not take effect for the program being debugged\n\ +until the next time it is started."); + + add_cmd ("prompt", class_support, set_prompt_command, + "Change gdb's prompt from the default of \"(gdb)\"", + &setlist); + add_com ("echo", class_support, echo_command, + "Print a constant string. Give string as argument.\n\ +C escape sequences may be used in the argument.\n\ +No newline is added at the end of the argument;\n\ +use \"\\n\" if you want a newline to be printed.\n\ +Since leading and trailing whitespace are ignored in command arguments,\n\ +if you want to print some you must use \"\\\" before leading whitespace\n\ +to be printed or after trailing whitespace."); + add_com ("document", class_support, document_command, + "Document a user-defined command.\n\ +Give command name as argument. Give documentation on following lines.\n\ +End with a line of just \"end\"."); + add_com ("define", class_support, define_command, + "Define a new command name. Command name is argument.\n\ +Definition appears on following lines, one command per line.\n\ +End with a line of just \"end\".\n\ +Use the \"document\" command to give documentation for the new command.\n\ +Commands defined in this way do not take arguments."); + + add_com ("source", class_support, source_command, + "Read commands from a file named FILE.\n\ +Note that the file \".gdbinit\" is read automatically in this way\n\ +when gdb is started."); + add_com ("quit", class_support, quit_command, "Exit gdb."); + add_com ("help", class_support, help_command, "Print list of commands."); + add_com_alias ("q", "quit", class_support, 1); + add_com_alias ("h", "help", class_support, 1); + + add_com ("dump-me", class_obscure, dump_me_command, + "Get fatal error; make debugger dump its core."); + + add_prefix_cmd ("info", class_info, info_command, + "Generic command for printing status.", + &infolist, "info ", 0, &cmdlist); + add_com_alias ("i", "info", class_info, 1); + + add_info ("copying", copying_info, "Conditions for redistributing copies of GDB."); + add_info ("warranty", warranty_info, "Various kinds of warranty you do not have."); + add_info ("version", version_info, "Report what version of GDB this is."); +} +@ + + +1.1 +log +@Initial revision +@ +text +@d103 2 +a104 2 +static void initialize_main (); +static void initialize_cmd_lists (); +d247 3 +a249 3 + initialize_cmd_lists (); /* This needs to be done first */ + initialize_all_files (); + initialize_main (); /* But that omits this file! Do it now */ +d1206 1 +a1206 1 +initialize_cmd_lists () +d1218 1 +a1218 1 +initialize_main () +@ diff --git a/gdb/RCS/munch,v b/gdb/RCS/munch,v new file mode 100755 index 0000000..bac6946 --- /dev/null +++ b/gdb/RCS/munch,v @@ -0,0 +1,75 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @# @; + + +1.3 +date 89.03.27.21.15.45; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.03.27.20.18.28; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.18.58.17; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@Fix up "munch" so it generates a name that doesn't match its own +"grep" conventions. Change main so that it calls the new name, +and also doesn't use the conventions for functions that should NOT +be called by init.c. +@ +text +@#! /bin/sh + +# create an initialization procedure from a list of .o files +# Look in object files, find symbols including the string _initialize_, +# and call each one as a function. + +echo '/* Do not modify this file. It is created automatically by "munch". */' +echo 'void init_all_files () {' + +nm $* | egrep '_initialize_' | \ + sed -e 's/^.*\(initialize_[a-zA-Z_0-9]*\).*$/ _\1 ();/' | \ + sort -u + +echo '}' +@ + + +1.2 +log +@Generic change: make it not care much about the output format of "nm". +Now as long as _initialize_foo is not touching any other +symbol or alphanumeric, we'll find it and use it. +@ +text +@d8 1 +a8 1 +echo 'void initialize_all_files () {' +@ + + +1.1 +log +@Initial revision +@ +text +@d4 2 +d10 3 +a12 1 +nm -p $* | egrep 'T *__?initialize_' | sed -e 's/^.*T *_*\(.*\)/ _\1 ();/' +@ diff --git a/gdb/RCS/printcmd.c,v b/gdb/RCS/printcmd.c,v new file mode 100644 index 0000000..2ab3acc --- /dev/null +++ b/gdb/RCS/printcmd.c,v @@ -0,0 +1,1707 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.04.26.00.54.22; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.04.25.15.38.49; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@(1) Depend on XXX_BIG_ENDIAN rather than testing gdb's object code. +(2) Print to the STREAM parameter, not to stdout. +(3) Use is_nan with new arguments. +(4) Use unpack_double with new args, and deal with invalid results. +(5) Don't print history numbers in the "print" command for things +that weren't recorded in history (because they were invalid values). +@ +text +@/* Print values for GNU debugger GDB. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" + +struct format_data +{ + int count; + char format; + char size; +}; + +/* Last specified output format. */ + +static char last_format = 'x'; + +/* Last specified examination size. 'b', 'h', 'w' or `q'. */ + +static char last_size = 'w'; + +/* Default address to examine next. */ + +static CORE_ADDR next_address; + +/* Last address examined. */ + +static CORE_ADDR last_examine_address; + +/* Contents of last address examined. + This is not valid past the end of the `x' command! */ + +static value last_examine_value; + +/* Number of auto-display expression currently being displayed. + So that we can deleted it if we get an error or a signal within it. + -1 when not doing one. */ + +int current_display_number; + +static void do_one_display (); + +void do_displays (); +void print_address (); +void print_scalar_formatted (); + + +/* Decode a format specification. *STRING_PTR should point to it. + OFORMAT and OSIZE are used as defaults for the format and size + if none are given in the format specification. + The structure returned describes all the data + found in the specification. In addition, *STRING_PTR is advanced + past the specification and past all whitespace following it. */ + +struct format_data +decode_format (string_ptr, oformat, osize) + char **string_ptr; + char oformat; + char osize; +{ + struct format_data val; + register char *p = *string_ptr; + + val.format = oformat; + val.size = osize; + val.count = 1; + + if (*p >= '0' && *p <= '9') + val.count = atoi (p); + while (*p >= '0' && *p <= '9') p++; + + /* Now process size or format letters that follow. */ + + while (1) + { + if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g') + val.size = *p++; +#ifdef LONG_LONG + else if (*p == 'l') + { + val.size = 'g'; + p++; + } +#endif + else if (*p >= 'a' && *p <= 'z') + val.format = *p++; + else + break; + } + +#ifndef LONG_LONG + /* Make sure 'g' size is not used on integer types. + Well, actually, we can handle hex. */ + if (val.size == 'g' && val.format != 'f' && val.format != 'x') + val.size = 'w'; +#endif + + while (*p == ' ' || *p == '\t') p++; + *string_ptr = p; + + return val; +} + +/* Print value VAL on stdout according to FORMAT, a letter or 0. + Do not end with a newline. + 0 means print VAL according to its own type. + SIZE is the letter for the size of datum being printed. + This is used to pad hex numbers so they line up. */ + +static void +print_formatted (val, format, size) + register value val; + register char format; + char size; +{ + int len = TYPE_LENGTH (VALUE_TYPE (val)); + + if (VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val) + len; + + switch (format) + { + case 's': + next_address = VALUE_ADDRESS (val) + + value_print (value_addr (val), stdout, 0); + break; + + case 'i': + next_address = VALUE_ADDRESS (val) + + print_insn (VALUE_ADDRESS (val), stdout); + break; + + default: + if (format == 0 + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION + || VALUE_REPEATED (val)) + value_print (val, stdout, format); + else + print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val), + format, size, stdout); + } +} + +/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR, + according to letters FORMAT and SIZE on STREAM. + FORMAT may not be zero. Formats s and i are not supported at this level. + + This is how the elements of an array or structure are printed + with a format. */ + +void +print_scalar_formatted (valaddr, type, format, size, stream) + char *valaddr; + struct type *type; + char format; + int size; + FILE *stream; +{ + LONGEST val_long; + int len = TYPE_LENGTH (type); + + if (size == 'g' && sizeof (LONGEST) < 8 + && format == 'x') + { + /* ok, we're going to have to get fancy here. Assumption: a + long is four bytes. */ + unsigned long v1, v2, tmp; + + v1 = unpack_long (builtin_type_long, valaddr); + v2 = unpack_long (builtin_type_long, valaddr + 4); + +#ifdef BYTES_BIG_ENDIAN +#else + /* Little endian -- swap the two for printing */ + tmp = v1; + v1 = v2; + v2 = tmp; +#endif + + switch (format) + { + case 'x': + fprintf (stream, "0x%08x%08x", v1, v2); + break; + default: + error ("Output size \"g\" unimplemented for format \"%c\".", + format); + } + return; + } + + val_long = unpack_long (type, valaddr); + + /* If value is unsigned, truncate it in case negative. */ + if (format != 'd') + { + if (len == sizeof (char)) + val_long &= (1 << 8 * sizeof(char)) - 1; + else if (len == sizeof (short)) + val_long &= (1 << 8 * sizeof(short)) - 1; + else if (len == sizeof (long)) + val_long &= (unsigned long) - 1; + } + + switch (format) + { + case 'x': +#ifdef LONG_LONG + if (!size) + size = (len < sizeof (long long) ? 'w' : 'g'); + switch (size) + { + case 'b': + fprintf (stream, "0x%02llx", val_long); + break; + case 'h': + fprintf (stream, "0x%04llx", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf (stream, "0x%08llx", val_long); + break; + case 'g': + fprintf (stream, "0x%16llx", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#else + switch (size) + { + case 'b': + fprintf (stream, "0x%02x", val_long); + break; + case 'h': + fprintf (stream, "0x%04x", val_long); + break; + case 0: /* no size specified, like in print */ + case 'w': + fprintf (stream, "0x%08x", val_long); + break; + case 'g': + fprintf (stream, "0x%16x", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#endif /* not LONG_LONG */ + break; + + case 'd': +#ifdef LONG_LONG + fprintf (stream, "%lld", val_long); +#else + fprintf (stream, "%d", val_long); +#endif + break; + + case 'u': +#ifdef LONG_LONG + fprintf (stream, "%llu", val_long); +#else + fprintf (stream, "%u", val_long); +#endif + break; + + case 'o': + if (val_long) +#ifdef LONG_LONG + fprintf (stream, "0%llo", val_long); +#else + fprintf (stream, "0%o", val_long); +#endif + else + fprintf (stream, "0"); + break; + + case 'a': + print_address ((CORE_ADDR) val_long, stream); + break; + + case 'c': + value_print (value_from_long (builtin_type_char, val_long), stream, 0); + break; + + case 'f': + if (len == sizeof (float)) + type = builtin_type_float; + else if (len == sizeof (double)) + type = builtin_type_double; + else abort(); + +#ifdef IEEE_FLOAT + if (is_nan (valaddr, len)) + { + fprintf (stream, "NaN"); + break; + } +#endif + { + double doub; + int inv; + + doub = unpack_double (type, valaddr, &inv); + if (inv) + fprintf (stream, "Invalid float value"); + else + fprintf (stream, len > 4? "%.16g": "%.6g", doub); + } + break; + + case 0: + abort (); + + default: + error ("Undefined output format \"%c\".", format); + } +} + +/* Specify default address for `x' command. + `info lines' uses this. */ + +void +set_next_address (addr) + CORE_ADDR addr; +{ + next_address = addr; + + /* Make address available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, (LONGEST) addr)); +} + +/* Print address ADDR symbolically on STREAM. + First print it as a number. Then perhaps print + <SYMBOL + OFFSET> after the number. */ + +void +print_address (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + register int i; + struct symbol *fs; + char *name; + int name_location; + + fprintf (stream, "0x%x", addr); + + fs = find_pc_function (addr); + + if (!fs) + { + i = find_pc_misc_function (addr); + + if (i < 0) return; /* If nothing comes through, don't + print anything symbolic */ + + name = misc_function_vector[i].name; + name_location = misc_function_vector[i].address; + } + else + { + name = fs->name; + name_location = BLOCK_START (SYMBOL_BLOCK_VALUE (fs)); + } + + if (addr - name_location) + fprintf (stream, " <%s+%d>", + name, + addr - name_location); + else + fprintf (stream, " <%s>", name); +} + +/* Examine data at address ADDR in format FMT. + Fetch it from memory and print on stdout. */ + +static void +do_examine (fmt, addr) + struct format_data fmt; + CORE_ADDR addr; +{ + register char format = 0; + register char size; + register int count = 1; + struct type *val_type; + register int i; + register int maxelts; + + format = fmt.format; + size = fmt.size; + count = fmt.count; + next_address = addr; + + /* String or instruction format implies fetch single bytes + regardless of the specified size. */ + if (format == 's' || format == 'i') + size = 'b'; + + if (size == 'b') + val_type = builtin_type_char; + else if (size == 'h') + val_type = builtin_type_short; + else if (size == 'w') + val_type = builtin_type_long; + else if (size == 'g') +#ifndef LONG_LONG + val_type = builtin_type_double; +#else + val_type = builtin_type_long_long; +#endif + + maxelts = 8; + if (size == 'w') + maxelts = 4; + if (size == 'g') + maxelts = 2; + if (format == 's' || format == 'i') + maxelts = 1; + + /* Print as many objects as specified in COUNT, at most maxelts per line, + with the address of the next one at the start of each line. */ + + while (count > 0) + { + print_address (next_address, stdout); + fputc (':', stdout); + for (i = maxelts; + i > 0 && count > 0; + i--, count--) + { + fputc ('\t', stdout); + /* Note that print_formatted sets next_address for the next + object. */ + last_examine_address = next_address; + last_examine_value = value_at (val_type, next_address); + print_formatted (last_examine_value, format, size); + } + fputc ('\n', stdout); + fflush (stdout); + } +} + +static void +validate_format (fmt, cmdname) + struct format_data fmt; + char *cmdname; +{ + if (fmt.size != 0) + error ("Size letters are meaningless in \"%s\" command.", cmdname); + if (fmt.count != 1) + error ("Item count other than 1 is meaningless in \"%s\" command.", + cmdname); + if (fmt.format == 'i' || fmt.format == 's') + error ("Format letter \"%c\" is meaningless in \"%s\" command.", + fmt.format, cmdname); +} + +static void +print_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain = 0; + register char format = 0; + register value val; + struct format_data fmt; + int histindex; + int cleanup = 0; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, 0); + validate_format (fmt, "print"); + last_format = format = fmt.format; + } + + if (exp && *exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + cleanup = 1; + val = evaluate_expression (expr); + } + else + val = access_value_history (0); + + histindex = record_latest_value (val); + if (histindex >= 0) printf ("$%d = ", histindex); + + print_formatted (val, format, fmt.size); + printf ("\n"); + + if (cleanup) + do_cleanups (old_chain); +} + +static void +output_command (exp) + char *exp; +{ + struct expression *expr; + register struct cleanup *old_chain; + register char format = 0; + register value val; + struct format_data fmt; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + validate_format (fmt, "print"); + format = fmt.format; + } + + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + + print_formatted (val, format, fmt.size); + + do_cleanups (old_chain); +} + +static void +set_command (exp) + char *exp; +{ + struct expression *expr = parse_c_expression (exp); + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); +} + +static void +address_info (exp) + char *exp; +{ + register struct symbol *sym; + register CORE_ADDR val; + int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero + if exp is a field of `this'. */ + + if (exp == 0) + error ("Argument required."); + + sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE, + &is_a_field_of_this); + if (sym == 0) + { + register int i; + + if (is_a_field_of_this) + { + printf("Symbol \"%s\" is a field of the local class variable `this'\n", exp); + return; + } + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, exp)) + break; + + if (i < misc_function_count) + printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n", + exp, misc_function_vector[i].address); + else + error ("No symbol \"%s\" in current context.", exp); + return; + } + + printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym)); + val = SYMBOL_VALUE (sym); + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + printf ("constant"); + break; + + case LOC_LABEL: + printf ("a label at address 0x%x", val); + break; + + case LOC_REGISTER: + printf ("a variable in register %s", reg_names[val]); + break; + + case LOC_STATIC: + printf ("static at address 0x%x", val); + break; + + case LOC_REGPARM: + printf ("an argument in register %s", reg_names[val]); + break; + + case LOC_ARG: + printf ("an argument at offset %d", val); + break; + + case LOC_LOCAL: + printf ("a local variable at frame offset %d", val); + break; + + case LOC_TYPEDEF: + printf ("a typedef"); + break; + + case LOC_BLOCK: + printf ("a function at address 0x%x", + BLOCK_START (SYMBOL_BLOCK_VALUE (sym))); + break; + } + printf (".\n"); +} + +static void +x_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr; + struct format_data fmt; + struct cleanup *old_chain; + + fmt.format = last_format; + fmt.size = last_size; + fmt.count = 1; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, last_size); + last_size = fmt.size; + last_format = fmt.format; + } + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + { + expr = parse_c_expression (exp); + /* Cause expression not to be there any more + if this command is repeated with Newline. + But don't clobber a user-defined command's definition. */ + if (from_tty) + *exp = 0; + old_chain = make_cleanup (free_current_contents, &expr); + next_address = (CORE_ADDR) value_as_long (evaluate_expression (expr)); + do_cleanups (old_chain); + } + + do_examine (fmt, next_address); + + /* Set a couple of internal variables if appropriate. */ + if (last_examine_value) + { + /* Make last address examined available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, + (LONGEST) last_examine_address)); + + /* Make contents of last address examined available to the user as $__.*/ + set_internalvar (lookup_internalvar ("__"), last_examine_value); + } +} + +/* Commands for printing types of things. */ + +static void +whatis_command (exp) + char *exp; +{ + struct expression *expr; + register value val; + register struct cleanup *old_chain; + + if (exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_type (expr); + } + else + val = access_value_history (0); + + printf ("type = "); + type_print (VALUE_TYPE (val), "", stdout, 1); + printf ("\n"); + + if (exp) + do_cleanups (old_chain); +} + +static void +ptype_command (typename) + char *typename; +{ + register char *p = typename; + register int len; + extern struct block *get_current_block (); + register struct block *b + = (have_inferior_p () || have_core_file_p ()) ? get_current_block () : 0; + register struct type *type; + + if (typename == 0) + error_no_arg ("type name"); + + while (*p && *p != ' ' && *p != '\t') p++; + len = p - typename; + while (*p == ' ' || *p == '\t') p++; + + if (len == 6 && !strncmp (typename, "struct", 6)) + type = lookup_struct (p, b); + else if (len == 5 && !strncmp (typename, "union", 5)) + type = lookup_union (p, b); + else if (len == 4 && !strncmp (typename, "enum", 4)) + type = lookup_enum (p, b); + else + { + type = lookup_typename (typename, b, 1); + if (type == 0) + { + register struct symbol *sym + = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0); + if (sym == 0) + error ("No type named %s.", typename); + printf ("No type named %s, but there is a ", + typename); + switch (TYPE_CODE (SYMBOL_TYPE (sym))) + { + case TYPE_CODE_STRUCT: + printf ("struct"); + break; + + case TYPE_CODE_UNION: + printf ("union"); + break; + + case TYPE_CODE_ENUM: + printf ("enum"); + } + printf (" %s. Type \"help ptype\".\n", typename); + type = SYMBOL_TYPE (sym); + } + } + + type_print (type, "", stdout, 1); + printf ("\n"); +} + +enum display_status {disabled, enabled}; + +struct display +{ + /* Chain link to next auto-display item. */ + struct display *next; + /* Expression to be evaluated and displayed. */ + struct expression *exp; + /* Item number of this auto-display item. */ + int number; + /* Display format specified. */ + struct format_data format; + /* Innermost block required by this expression when evaluated */ + struct block *block; + /* Status of this display (enabled or disabled) */ + enum display_status status; +}; + +/* Chain of expressions whose values should be displayed + automatically each time the program stops. */ + +static struct display *display_chain; + +static int display_number; + +/* Add an expression to the auto-display chain. + Specify the expression. */ + +static void +display_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct format_data fmt; + register struct expression *expr; + register struct display *new; + extern struct block *innermost_block; + + if (exp == 0) + { + do_displays (); + return; + } + + if (*exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + if (fmt.size && fmt.format == 0) + fmt.format = 'x'; + if (fmt.format == 'i' || fmt.format == 's') + fmt.size = 'b'; + } + else + { + fmt.format = 0; + fmt.size = 0; + fmt.count = 0; + } + + innermost_block = 0; + expr = parse_c_expression (exp); + + new = (struct display *) xmalloc (sizeof (struct display)); + + new->exp = expr; + new->block = innermost_block; + new->next = display_chain; + new->number = ++display_number; + new->format = fmt; + new->status = enabled; + display_chain = new; + + if (from_tty && have_inferior_p ()) + do_one_display (new); + + dont_repeat (); +} + +static void +free_display (d) + struct display *d; +{ + free (d->exp); + free (d); +} + +/* Clear out the display_chain. + Done when new symtabs are loaded, since this invalidates + the types stored in many expressions. */ + +void +clear_displays () +{ + register struct display *d; + + while (d = display_chain) + { + free (d->exp); + display_chain = d->next; + free (d); + } +} + +/* Delete the auto-display number NUM. */ + +void +delete_display (num) + int num; +{ + register struct display *d1, *d; + + if (!display_chain) + error ("No display number %d.", num); + + if (display_chain->number == num) + { + d1 = display_chain; + display_chain = d1->next; + free_display (d1); + } + else + for (d = display_chain; ; d = d->next) + { + if (d->next == 0) + error ("No display number %d.", num); + if (d->next->number == num) + { + d1 = d->next; + d->next = d1->next; + free_display (d1); + break; + } + } +} + +/* Delete some values from the auto-display chain. + Specify the element numbers. */ + +static void +undisplay_command (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d, *d1; + + if (args == 0) + { + if (query ("Delete all auto-display expressions? ")) + clear_displays (); + dont_repeat (); + return; + } + + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + delete_display (num); + + p = p1; + while (*p == ' ' || *p == '\t') p++; + } + dont_repeat (); +} + +/* Display a single auto-display. + Do nothing if the display cannot be printed in the current context, + or if the display is disabled. */ + +static void +do_one_display (d) + struct display *d; +{ + int within_current_scope; + + if (d->status == disabled) + return; + + if (d->block) + within_current_scope = contained_in (get_selected_block (), d->block); + else + within_current_scope = 1; + if (!within_current_scope) + return; + + current_display_number = d->number; + + printf ("%d: ", d->number); + if (d->format.size) + { + printf ("x/"); + if (d->format.count != 1) + printf ("%d", d->format.count); + printf ("%c", d->format.format); + if (d->format.format != 'i' && d->format.format != 's') + printf ("%c", d->format.size); + printf (" "); + print_expression (d->exp, stdout); + if (d->format.count != 1) + printf ("\n"); + else + printf (" "); + do_examine (d->format, + (CORE_ADDR) value_as_long (evaluate_expression (d->exp))); + + } + else + { + if (d->format.format) + printf ("/%c ", d->format.format); + print_expression (d->exp, stdout); + printf (" = "); + print_formatted (evaluate_expression (d->exp), + d->format.format, d->format.size); + printf ("\n"); + } + + fflush (stdout); + current_display_number = -1; +} + +/* Display all of the values on the auto-display chain which can be + evaluated in the current scope. */ + +void +do_displays () +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + do_one_display (d); +} + +/* Delete the auto-display which we were in the process of displaying. + This is done when there is an error or a signal. */ + +void +delete_current_display () +{ + if (current_display_number >= 0) + { + delete_display (current_display_number); + fprintf (stderr, "Deleting display %d to avoid infinite recursion.\n", + current_display_number); + } + current_display_number = -1; +} + +static void +display_info () +{ + register struct display *d; + + if (!display_chain) + printf ("There are no auto-display expressions now.\n"); + else + printf ("Auto-display expressions now in effect:\n\ +Num Enb Expression\n"); + + for (d = display_chain; d; d = d->next) + { + printf ("%d: %c ", d->number, "ny"[(int)d->status]); + if (d->format.size) + printf ("/%d%c%c ", d->format.count, d->format.size, + d->format.format); + else if (d->format.format) + printf ("/%c ", d->format.format); + print_expression (d->exp, stdout); + if (d->block && !contained_in (get_selected_block (), d->block)) + printf (" (cannot be evaluated in the current context)"); + printf ("\n"); + fflush (stdout); + } +} + +void +enable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d->next) + d->status = enabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = enabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + +void +disable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d->next) + d->status = disabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = disabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + + +/* Print the value in stack frame FRAME of a variable + specified by a struct symbol. */ + +void +print_variable_value (var, frame, stream) + struct symbol *var; + CORE_ADDR frame; + FILE *stream; +{ + value val = read_var_value (var, frame); + value_print (val, stream, 0); +} + +/* Print the arguments of a stack frame, given the function FUNC + running in that frame (as a symbol), the info on the frame, + and the number of args according to the stack frame (or -1 if unknown). */ + +static void print_frame_nameless_args (); + +void +print_frame_args (func, fi, num, stream) + struct symbol *func; + struct frame_info *fi; + int num; + FILE *stream; +{ + struct block *b; + int nsyms = 0; + int first = 1; + register int i; + register int last_offset = FRAME_ARGS_SKIP; + register int last_regparm = 0; + register struct symbol *lastsym, *sym, *nextsym; + register value val; + register CORE_ADDR addr = FRAME_ARGS_ADDRESS (fi); + + if (func) + { + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + } + + lastsym = 0; + while (1) + { + /* Find first arg that is not before LAST_OFFSET. */ + nextsym = 0; + for (i = 0; i < nsyms; i++) + { + QUIT; + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_ARG) + { + if (SYMBOL_VALUE (sym) >= last_offset + && (nextsym == 0 + || SYMBOL_VALUE (sym) < SYMBOL_VALUE (nextsym))) + nextsym = sym; + } + else if (SYMBOL_CLASS (sym) == LOC_REGPARM) + { + /* This shouldn't be sorted by number. Since we can't + find nameless args with register parameters, print + this out in order by .stabs. */ + if (sym > lastsym && nextsym == 0) + nextsym = sym; + } + } + if (nextsym == 0) + break; + sym = nextsym; + /* Print any nameless args between the last arg printed + and the next arg. */ + if (SYMBOL_CLASS (sym) == LOC_ARG + && last_offset != (SYMBOL_VALUE (sym) / sizeof (int)) * sizeof (int)) + { + print_frame_nameless_args (addr, last_offset, SYMBOL_VALUE (sym), + stream); + first = 0; + } + /* Print the next arg. */ + if (SYMBOL_CLASS (sym) == LOC_REGPARM) + val = value_from_register (SYMBOL_TYPE (sym), + SYMBOL_VALUE (sym), + FRAME_INFO_ID (fi)); + else + val = value_at (SYMBOL_TYPE (sym), addr + SYMBOL_VALUE (sym)); + + if (! first) + fprintf (stream, ", "); + fprintf (stream, "%s=", SYMBOL_NAME (sym)); + value_print (val, stream, 0); + first = 0; + if (SYMBOL_CLASS (sym) == LOC_ARG) + last_offset = SYMBOL_VALUE (sym) + TYPE_LENGTH (SYMBOL_TYPE (sym)); + else + { + last_regparm = SYMBOL_VALUE (sym) + 1; + last_offset += TYPE_LENGTH (SYMBOL_TYPE (sym)); + } + + /* Round up address of next arg to multiple of size of int. */ + last_offset + = ((last_offset + sizeof (int) - 1) / sizeof (int)) * sizeof (int); + lastsym = sym; + } + if (num >= 0 && num * sizeof (int) + FRAME_ARGS_SKIP > last_offset) + print_frame_nameless_args (addr, last_offset, + num * sizeof (int) + FRAME_ARGS_SKIP, stream); +} + +static void +print_frame_nameless_args (argsaddr, start, end, stream) + CORE_ADDR argsaddr; + int start; + int end; + FILE *stream; +{ + while (start < end) + { + QUIT; + if (start != FRAME_ARGS_SKIP) + fprintf (stream, ", "); + fprintf (stream, "%d", + read_memory_integer (argsaddr + start, sizeof (int))); + start += sizeof (int); + } +} + +static void +printf_command (arg) + char *arg; +{ + register char *f; + register char *s = arg; + char *string; + value *val_args; + int nargs = 0; + int allocated_args = 20; + char *arg_bytes; + char *argclass; + int i; + int argindex; + int nargs_wanted; + + val_args = (value *) xmalloc (allocated_args * sizeof (value)); + + if (s == 0) + error_no_arg ("format-control string and values to print"); + + /* Skip white space before format string */ + while (*s == ' ' || *s == '\t') s++; + + /* A format string should follow, enveloped in double quotes */ + if (*s++ != '"') + error ("Bad format string, missing '\"'."); + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + while (*s != '"') + { + int c = *s++; + switch (c) + { + case '\0': + error ("Bad format string, non-terminated '\"'."); + /* doesn't return */ + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'n': + *f++ = '\n'; + break; + case 't': + *f++ = '\t'; + break; + case 'r': + *f++ = '\r'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences */ + error ("Unrecognized \\ escape character in format string."); + } + break; + + default: + *f++ = c; + } + } + + /* Skip over " and following space and comma. */ + s++; + *f++ = '\0'; + while (*s == ' ' || *s == '\t') s++; + + if (*s != ',' && *s != 0) + error ("Invalid argument syntax"); + + if (*s == ',') s++; + while (*s == ' ' || *s == '\t') s++; + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass[I] is set to 1 if the Ith arg should be a string. + It's set to 2 if the Ith arg should be floating point. */ + + argclass = (char *) alloca (strlen (s)); + nargs_wanted = 0; + f = string; + while (*f) + if (*f++ == '%') + { + while (index ("0123456789.hlL-+ #", *f)) f++; + if (*f == 's') + argclass[nargs_wanted++] = 1; + else if (*f == 'e' || *f == 'f' || *f == 'g') + argclass[nargs_wanted++] = 2; + else if (*f != '%') + argclass[nargs_wanted++] = 0; + f++; + } + + /* Now, parse all arguments and evaluate them. + Store the VALUEs in VAL_ARGS. */ + + while (*s != '\0') + { + char *s1; + if (nargs == allocated_args) + val_args = (value *) xrealloc (val_args, + (allocated_args *= 2) + * sizeof (value)); + s1 = s; + val_args[nargs] = parse_to_comma_and_eval (&s1); + + /* If format string wants a float, unchecked-convert the value to + floating point of the same size */ + + if (argclass[nargs] == 2) + { + argclass[nargs] = 0; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float)) + VALUE_TYPE (val_args[nargs]) = builtin_type_float; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double)) + VALUE_TYPE (val_args[nargs]) = builtin_type_double; + } + nargs++; + s = s1; + if (*s == ',') + s++; + } + + if (nargs != nargs_wanted) + error ("Wrong number of arguments for specified format-string"); + + /* Now lay out an argument-list containing the arguments + as doubles, integers and C pointers. */ + + arg_bytes = (char *) alloca (sizeof (double) * nargs); + argindex = 0; + for (i = 0; i < nargs; i++) + { + if (argclass[i]) + { + char *str; + int tem, j; + tem = value_as_long (val_args[i]); + + /* This is a %s argument. Find the length of the string. */ + for (j = 0; ; j++) + { + char c; + QUIT; + read_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (char *) alloca (j + 1); + read_memory (tem, str, j); + str[j] = 0; + + /* Pass address of internal copy as the arg to vprintf. */ + *((int *) &arg_bytes[argindex]) = (int) str; + argindex += sizeof (int); + } + else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT) + { + *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]); + argindex += sizeof (double); + } + else +#ifdef LONG_LONG + if (TYPE_LENGTH (VALUE_TYPE (val_args[i])) == sizeof (long long)) + { + *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]); + argindex += sizeof (long long); + } + else +#endif + { + *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]); + argindex += sizeof (int); + } + } + + vprintf (string, arg_bytes); +} + +extern struct cmd_list_element *enablelist, *disablelist, *deletelist; +extern struct cmd_list_element *cmdlist, *setlist; + +void +_initialize_printcmd () +{ + current_display_number = -1; + + add_info ("address", address_info, + "Describe where variable VAR is stored."); + + add_com ("x", class_vars, x_command, + "Examine memory: x/FMT ADDRESS.\n\ +ADDRESS is an expression for the memory address to examine.\n\ +FMT is a repeat count followed by a format letter and a size letter.\n\ +Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\ + f(float), a(address), i(instruction), c(char) and s(string).\n\ +Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\ + g is meaningful only with f, for type double.\n\ +The specified number of objects of the specified size are printed\n\ +according to the format.\n\n\ +Defaults for format and size letters are those previously used.\n\ +Default count is 1. Default address is following last thing printed\n\ +with this command or \"print\"."); + + add_com ("ptype", class_vars, ptype_command, + "Print definition of type TYPE.\n\ +Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\ +or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\ +The selected stack frame's lexical context is used to look up the name."); + + add_com ("whatis", class_vars, whatis_command, + "Print data type of expression EXP."); + + add_info ("display", display_info, + "Expressions to display when program stops, with code numbers."); + + add_abbrev_cmd ("undisplay", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +\"delete display\" has the same effect as this command.\n\ +Do \"info display\" to see current list of code numbers.", + &cmdlist); + + add_com ("display", class_vars, display_command, + "Print value of expression EXP each time the program stops.\n\ +/FMT may be used before EXP as in the \"print\" command.\n\ +/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\ +as in the \"x\" command, and then EXP is used to get the address to examine\n\ +and examining is done as in the \"x\" command.\n\n\ +With no argument, display all currently requested auto-display expressions.\n\ +Use \"undisplay\" to cancel display requests previously made."); + + add_cmd ("display", class_vars, enable_display, + "Enable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to resume displaying.\n\ +No argument means enable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &enablelist); + + add_cmd ("display", class_vars, disable_display, + "Disable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means disable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &disablelist); + + add_cmd ("display", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &deletelist); + + add_com ("printf", class_vars, printf_command, + "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +This is useful for formatted output in user-defined commands."); + add_com ("output", class_vars, output_command, + "Like \"print\" but don't put in value history and don't print newline.\n\ +This is useful in user-defined commands."); + + add_prefix_cmd ("set", class_vars, set_command, +"Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +Use \"set variable\" for variables with names identical to set subcommands.\n\ +\nWith a subcommand, this command modifies parts of the gdb environment", + &setlist, "set ", 1, &cmdlist); + + add_cmd ("variable", class_vars, set_command, + "Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +This may usually be abbreviated to simply \"set\".", + &setlist); + + add_com ("print", class_vars, print_command, + concat ("Print value of expression EXP.\n\ +Variables accessible are those of the lexical environment of the selected\n\ +stack frame, plus all those whose scope is global or an entire file.\n\ +\n\ +$NUM gets previous value number NUM. $ and $$ are the last two values.\n\ +$$NUM refers to NUM'th value back from the last one.\n\ +Names starting with $ refer to registers (with the values they would have\n\ +if the program were to return to the stack frame now selected, restoring\n\ +all registers saved by frames farther in) or else to debugger\n\ +\"convenience\" variables (any such name not a known register).\n\ +Use assignment expressions to give values to convenience variables.\n", + "\n\ +\{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\ +@@ is a binary operator for treating consecutive data objects\n\ +anywhere in memory as an array. FOO@@NUM gives an array whose first\n\ +element is FOO, whose second element is stored in the space following\n\ +where FOO is stored, etc. FOO must be an expression whose value\n\ +resides in memory.\n", + "\n\ +EXP may be preceded with /FMT, where FMT is a format letter\n\ +but no count or size letter (see \"x\" command).")); + add_com_alias ("p", "print", class_vars, 1); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@a191 1 + int bigendian = 0; +d196 7 +a202 5 + { + union { + char a, b, c, d; + long i; + } x; +a203 10 + x.i = 1; + if (x.a) + { + /* Little endian */ + tmp = v1; + v1 = v2; + v2 = tmp; + } + } + +d207 1 +a207 1 + printf ("0x%08x%08x", v1, v2); +d238 1 +a238 1 + printf ("0x%02llx", val_long); +d241 1 +a241 1 + printf ("0x%04llx", val_long); +d245 1 +a245 1 + printf ("0x%08llx", val_long); +d248 1 +a248 1 + printf ("0x%16llx", val_long); +d257 1 +a257 1 + printf ("0x%02x", val_long); +d260 1 +a260 1 + printf ("0x%04x", val_long); +d264 1 +a264 1 + printf ("0x%08x", val_long); +d267 1 +a267 1 + printf ("0x%16x", val_long); +d277 1 +a277 1 + printf ("%lld", val_long); +d279 1 +a279 1 + printf ("%d", val_long); +d285 1 +a285 1 + printf ("%llu", val_long); +d287 1 +a287 1 + printf ("%u", val_long); +d294 1 +a294 1 + printf ("0%llo", val_long); +d296 1 +a296 1 + printf ("0%o", val_long); +d299 1 +a299 1 + printf ("0"); +d313 1 +a313 1 + if (len == sizeof (double)) +d315 2 +d318 1 +a318 1 + if (is_nan (unpack_double (type, valaddr))) +d320 1 +a320 1 + printf ("Nan"); +d324 10 +a333 4 + if (len > 4) + printf ("%.16g", unpack_double (type, valaddr)); + else + printf ("%.6g", unpack_double (type, valaddr)); +d515 1 +a515 1 + printf ("$%d = ", histindex); +@ diff --git a/gdb/RCS/remote.c,v b/gdb/RCS/remote.c,v new file mode 100644 index 0000000..213113d --- /dev/null +++ b/gdb/RCS/remote.c,v @@ -0,0 +1,662 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.20.21.22; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.18.45.46; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Avoid <sys/fcntl.h>. +@ +text +@/* Memory-access and commands for inferior process, for GDB. + Copyright (C) 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +/* Remote communication protocol. + All values are encoded in ascii hex digits. + + Request Packet + + read registers g + reply XX....X Each byte of register data + is described by two hex digits. + Registers are in the internal order + for GDB, and the bytes in a register + are in the same order the machine uses. + or ENN for an error. + + write regs GXX..XX Each byte of register data + is described by two hex digits. + reply OK for success + ENN for an error + + read mem mAA..AA,LLLL AA..AA is address, LLLL is length. + reply XX..XX XX..XX is mem contents + or ENN NN is errno + + write mem MAA..AA,LLLL:XX..XX + AA..AA is address, + LLLL is number of bytes, + XX..XX is data + reply OK for success + ENN for an error + + cont cAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + step sAA..AA AA..AA is address to resume + If AA..AA is omitted, + resume at same address. + + There is no immediate reply to step or cont. + The reply comes when the machine stops. + It is SAA AA is the "signal number" + + kill req k +*/ + +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#include "wait.h" + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#endif + +#include <stdio.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/file.h> + +#ifdef HAVE_TERMIO +#include <termio.h> +#undef TIOCGETP +#define TIOCGETP TCGETA +#undef TIOCSETN +#define TIOCSETN TCSETA +#undef TIOCSETP +#define TIOCSETP TCSETAF +#define TERMINAL struct termio +#else +#include <sgtty.h> +#define TERMINAL struct sgttyb +#endif + +int kiodebug; + +int icache; + +/* Descriptor for I/O to remote machine. */ +int remote_desc; + +#define PBUFSIZ 400 + +static void remote_send (); +static void putpkt (); +static void getpkt (); +static void dcache_flush (); + + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +void +remote_open (name, from_tty) + char *name; + int from_tty; +{ + TERMINAL sg; + + remote_debugging = 0; + dcache_init (); + + remote_desc = open (name, O_RDWR); + if (remote_desc < 0) + perror_with_name (name); + + ioctl (remote_desc, TIOCGETP, &sg); +#ifdef HAVE_TERMIO + sg.c_lflag &= ~ICANON; +#else + sg.sg_flags = RAW; +#endif + ioctl (remote_desc, TIOCSETP, &sg); + + if (from_tty) + printf ("Remote debugging using %s\n", name); + remote_debugging = 1; +} + +/* Convert hex digit A to a number. */ + +static int +fromhex (a) + int a; +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + error ("Reply contains invalid hex digit"); +} + +/* Convert number NIB to a hex digit. */ + +static int +tohex (nib) + int nib; +{ + if (nib < 10) + return '0'+nib; + else + return 'a'+nib-10; +} + +/* Tell the remote machine to resume. */ + +int +remote_resume (step, signal) + int step, signal; +{ + char buf[PBUFSIZ]; + + dcache_flush (); + + strcpy (buf, step ? "s": "c"); + + putpkt (buf); +} + +/* Wait until the remote machine stops, then return, + storing status in STATUS just as `wait' would. */ + +int +remote_wait (status) + WAITTYPE *status; +{ + char buf[PBUFSIZ]; + + WSETEXIT ((*status), 0); + getpkt (buf); + if (buf[0] == 'E') + error ("Remote failure reply: %s", buf); + if (buf[0] != 'S') + error ("Invalid remote reply: %s", buf); + WSETSTOP ((*status), (((fromhex (buf[1])) << 4) + (fromhex (buf[2])))); +} + +/* Read the remote registers into the block REGS. */ + +void +remote_fetch_registers (regs) + char *regs; +{ + char buf[PBUFSIZ]; + int i; + char *p; + + sprintf (buf, "g"); + remote_send (buf); + + /* Reply describes registers byte by byte, + each byte encoded as two hex characters. */ + + p = buf; + for (i = 0; i < REGISTER_BYTES; i++) + { + if (p[0] == 0 || p[1] == 0) + error ("Remote reply is too short: %s", buf); + regs[i] = fromhex (p[0]) * 16 + fromhex (p[1]); + p += 2; + } +} + +/* Store the remote registers from the contents of the block REGS. */ + +void +remote_store_registers (regs) + char *regs; +{ + char buf[PBUFSIZ]; + int i; + char *p; + + buf[0] = 'G'; + + /* Command describes registers byte by byte, + each byte encoded as two hex characters. */ + + p = buf + 1; + for (i = 0; i < REGISTER_BYTES; i++) + { + *p++ = tohex ((regs[i] >> 4) & 0xf); + *p++ = tohex (regs[i] & 0xf); + } + + remote_send (buf); +} + +/* Read a word from remote address ADDR and return it. + This goes through the data cache. */ + +int +remote_fetch_word (addr) + CORE_ADDR addr; +{ + if (icache) + { + extern CORE_ADDR text_start, text_end; + + if (addr >= text_start && addr < text_end) + { + int buffer; + xfer_core_file (addr, &buffer, sizeof (int)); + return buffer; + } + } + return dcache_fetch (addr); +} + +/* Write a word WORD into remote address ADDR. + This goes through the data cache. */ + +void +remote_store_word (addr, word) + CORE_ADDR addr; + int word; +{ + dcache_poke (addr, word); +} + +/* Write memory data directly to the remote machine. + This does not inform the data cache; the data cache uses this. + MEMADDR is the address in the remote memory space. + MYADDR is the address of the buffer in our space. + LEN is the number of bytes. */ + +void +remote_write_bytes (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + char buf[PBUFSIZ]; + int i; + char *p; + + if (len > PBUFSIZ / 2 - 20) + abort (); + + sprintf (buf, "M%x,%x:", memaddr, len); + + /* Command describes registers byte by byte, + each byte encoded as two hex characters. */ + + p = buf + strlen (buf); + for (i = 0; i < len; i++) + { + *p++ = tohex ((myaddr[i] >> 4) & 0xf); + *p++ = tohex (myaddr[i] & 0xf); + } + + remote_send (buf); +} + +/* Read memory data directly from the remote machine. + This does not use the data cache; the data cache uses this. + MEMADDR is the address in the remote memory space. + MYADDR is the address of the buffer in our space. + LEN is the number of bytes. */ + +void +remote_read_bytes (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + char buf[PBUFSIZ]; + int i; + char *p; + + if (len > PBUFSIZ / 2 - 1) + abort (); + + sprintf (buf, "m%x,%x", memaddr, len); + remote_send (buf); + + /* Reply describes registers byte by byte, + each byte encoded as two hex characters. */ + + p = buf; + for (i = 0; i < len; i++) + { + if (p[0] == 0 || p[1] == 0) + error ("Remote reply is too short: %s", buf); + myaddr[i] = fromhex (p[0]) * 16 + fromhex (p[1]); + p += 2; + } +} + +/* + +A debug packet whose contents are <data> +is encapsulated for transmission in the form: + + $ <data> # CSUM1 CSUM2 + + <data> must be ASCII alphanumeric and cannot include characters + '$' or '#' + + CSUM1 and CSUM2 are ascii hex representation of an 8-bit + checksum of <data>, the most significant nibble is sent first. + the hex digits 0-9,a-f are used. + +Receiver responds with: + + + - if CSUM is correct and ready for next packet + - - if CSUM is incorrect + +*/ + +/* Send the command in BUF to the remote machine, + and read the reply into BUF. + Report an error if we get an error reply. */ + +static void +remote_send (buf) + char *buf; +{ + int i; + putpkt (buf); + getpkt (buf); + + if (buf[0] == 'E') + error ("Remote failure reply: %s", buf); +} + +/* Send a packet to the remote machine, with error checking. + The data of the packet is in BUF. */ + +static void +putpkt (buf) + char *buf; +{ + int i; + char csum = 0; + char buf2[500]; + char buf3[1]; + int cnt = strlen (buf); + char *p; + + if (kiodebug) + fprintf (stderr, "Sending packet: %s\n", buf); + + /* Copy the packet into buffer BUF2, encapsulating it + and giving it a checksum. */ + + p = buf2; + *p++ = '$'; + + for (i = 0; i < cnt; i++) + { + csum += buf[i]; + *p++ = buf[i]; + } + *p++ = '#'; + *p++ = tohex ((csum >> 4) & 0xf); + *p++ = tohex (csum & 0xf); + + /* Send it over and over until we get a positive ack. */ + + do { + write (remote_desc, buf2, p - buf2); + read (remote_desc, buf3, 1); + } while (buf3[0] != '+'); +} + +static int +readchar () +{ + char buf[1]; + while (read (remote_desc, buf, 1) != 1) ; + return buf[0] & 0x7f; +} + +/* Read a packet from the remote machine, with error checking, + and store it in BUF. */ + +static void +getpkt (buf) + char *buf; +{ + char *bp; + unsigned char csum; + unsigned int c, c1, c2; + extern kiodebug; + + while (1) + { + /* Force csum to be zero here because of possible error retry. */ + csum = 0; + + while ((c = readchar()) != '$'); + + bp = buf; + while (1) + { + c = readchar (); + if (c == '#') + break; + *bp++ = c; + csum += c; + } + *bp = 0; + + c1 = fromhex (readchar ()); + c2 = fromhex (readchar ()); + if (csum == (c1 << 4) + c2) + break; + printf ("Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n", + (c1 << 4) + c2, csum, buf); + write (remote_desc, "-", 1); + } + + write (remote_desc, "+", 1); + + if (kiodebug) + fprintf (stderr,"Packet received :%s\n", buf); +} + +/* The data cache records all the data read from the remote machine + since the last time it stopped. + + Each cache block holds 16 bytes of data + starting at a multiple-of-16 address. */ + +#define DCACHE_SIZE 64 /* Number of cache blocks */ + +struct dcache_block { + struct dcache_block *next, *last; + unsigned int addr; /* Address for which data is recorded. */ + int data[4]; +}; + +struct dcache_block dcache_free, dcache_valid; + +/* Free all the data cache blocks, thus discarding all cached data. */ + +static void +dcache_flush () +{ + register struct dcache_block *db; + + while ((db = dcache_valid.next) != &dcache_valid) + { + remque (db); + insque (db, &dcache_free); + } +} + +/* + * If addr is present in the dcache, return the address of the block + * containing it. + */ + +struct dcache_block * +dcache_hit (addr) +{ + register struct dcache_block *db; + + if (addr & 3) + abort (); + + /* Search all cache blocks for one that is at this address. */ + db = dcache_valid.next; + while (db != &dcache_valid) + { + if ((addr & 0xfffffff0) == db->addr) + return db; + db = db->next; + } + return NULL; +} + +/* Return the int data at address ADDR in dcache block DC. */ + +int +dcache_value (db, addr) + struct dcache_block *db; + unsigned int addr; +{ + if (addr & 3) + abort (); + return (db->data[(addr>>2)&3]); +} + +/* Get a free cache block, put it on the valid list, + and return its address. The caller should store into the block + the address and data that it describes. */ + +struct dcache_block * +dcache_alloc () +{ + register struct dcache_block *db; + + if ((db = dcache_free.next) == &dcache_free) + /* If we can't get one from the free list, take last valid */ + db = dcache_valid.last; + + remque (db); + insque (db, &dcache_valid); + return (db); +} + +/* Return the contents of the word at address ADDR in the remote machine, + using the data cache. */ + +int +dcache_fetch (addr) + CORE_ADDR addr; +{ + register struct dcache_block *db; + + db = dcache_hit (addr); + if (db == 0) + { + db = dcache_alloc (); + remote_read_bytes (addr & ~0xf, db->data, 16); + db->addr = addr & ~0xf; + } + return (dcache_value (db, addr)); +} + +/* Write the word at ADDR both in the data cache and in the remote machine. */ + +dcache_poke (addr, data) + CORE_ADDR addr; + int data; +{ + register struct dcache_block *db; + + /* First make sure the word is IN the cache. DB is its cache block. */ + db = dcache_hit (addr); + if (db == 0) + { + db = dcache_alloc (); + remote_read_bytes (addr & ~0xf, db->data, 16); + db->addr = addr & ~0xf; + } + + /* Modify the word in the cache. */ + db->data[(addr>>2)&3] = data; + + /* Send the changed word. */ + remote_write_bytes (addr, &data, 4); +} + +/* Initialize the data cache. */ + +dcache_init () +{ + register i; + register struct dcache_block *db; + + db = (struct dcache_block *) xmalloc (sizeof (struct dcache_block) * + DCACHE_SIZE); + dcache_free.next = dcache_free.last = &dcache_free; + dcache_valid.next = dcache_valid.last = &dcache_valid; + for (i=0;i<DCACHE_SIZE;i++,db++) + insque (db, &dcache_free); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@d74 1 +a74 1 +#include <sys/fcntl.h> +@ diff --git a/gdb/RCS/source.c,v b/gdb/RCS/source.c,v new file mode 100644 index 0000000..f19ff1d --- /dev/null +++ b/gdb/RCS/source.c,v @@ -0,0 +1,990 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.20.21.45; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.20.18.45.48; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Avoid <sys/fcntl.h>. +@ +text +@/* List lines of source files for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "symtab.h" +#include "param.h" + +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> +#endif + +#include <stdio.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/file.h> + +/* Path of directories to search for source files. + Same format as the PATH environment variable's value. */ + +static char *source_path; + +/* Symtab of default file for listing lines of. */ + +struct symtab *current_source_symtab; + +/* Default next line to list. */ + +int current_source_line; + +/* Line number of last line printed. Default for various commands. + current_source_line is usually, but not always, the same as this. */ + +static int last_line_listed; + +/* First line number listed by last listing command. */ + +static int first_line_listed; + + +struct symtab *psymtab_to_symtab (); + +/* Set the source file default for the "list" command, + specifying a symtab. */ + +void +select_source_symtab (s) + register struct symtab *s; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct partial_symtab *ps, *cs_pst; + + /* Make the default place to list be the function `main' + if one exists. */ + if (lookup_symbol ("main", 0, VAR_NAMESPACE, 0)) + { + sals = decode_line_spec ("main", 1); + sal = sals.sals[0]; + free (sals.sals); + current_source_symtab = sal.symtab; + current_source_line = sal.line - 9; + return; + } + + /* If there is no `main', use the last symtab in the list, + which is actually the first found in the file's symbol table. + But ignore .h files. */ + if (s) + { + do + { + char *name = s->filename; + int len = strlen (name); + if (! (len > 2 && !strcmp (&name[len - 2], ".h"))) + current_source_symtab = s; + s = s->next; + } + while (s); + current_source_line = 1; + } + else + { + ps = partial_symtab_list; + while (ps) + { + char *name = ps->filename; + int len = strlen (name); + if (! (len > 2 && !strcmp (&name[len - 2], ".h"))) + cs_pst = ps; + ps = ps->next; + } + if (cs_pst) + current_source_symtab = psymtab_to_symtab (cs_pst); + else + current_source_symtab = 0; + current_source_line = 1; + } +} + +static void +directories_info () +{ + printf ("Source directories searched: %s\n", source_path); +} + +void +init_source_path () +{ + register struct symtab *s; + + source_path = savestring (current_directory, strlen (current_directory)); + + /* Forget what we learned about line positions in source files; + must check again now since files may be found in + a different directory now. */ + for (s = symtab_list; s; s = s->next) + if (s->line_charpos != 0) + { + free (s->line_charpos); + s->line_charpos = 0; + } +} + +void +directory_command (dirname, from_tty) + char *dirname; + int from_tty; +{ + char *old = source_path; + + if (dirname == 0) + { + if (query ("Reinitialize source path to %s? ", current_directory)) + { + init_source_path (); + free (old); + } + } + else + { + struct stat st; + register int len = strlen (dirname); + register char *tem; + extern char *index (); + + if (index (dirname, ':')) + error ("Please add one directory at a time to the source path."); + if (dirname[len - 1] == '/') + /* Sigh. "foo/" => "foo" */ + dirname[--len] == '\0'; + + while (dirname[len - 1] == '.') + { + if (len == 1) + { + /* "." => getwd () */ + dirname = current_directory; + goto append; + } + else if (dirname[len - 2] == '/') + { + if (len == 2) + { + /* "/." => "/" */ + dirname[--len] = '\0'; + goto append; + } + else + { + /* "...foo/." => "...foo" */ + dirname[len -= 2] = '\0'; + continue; + } + } + break; + } + + if (dirname[0] != '/') + dirname = concat (current_directory, "/", dirname); + else + dirname = savestring (dirname, len); + make_cleanup (free, dirname); + + if (stat (dirname, &st) < 0) + perror_with_name (dirname); + if ((st.st_mode & S_IFMT) != S_IFDIR) + error ("%s is not a directory.", dirname); + + append: + len = strlen (dirname); + tem = source_path; + while (1) + { + if (!strncmp (tem, dirname, len) + && (tem[len] == '\0' || tem[len] == ':')) + { + printf ("\"%s\" is already in the source path.\n", + dirname); + break; + } + tem = index (tem, ':'); + if (tem) + tem++; + else + { + source_path = concat (old, ":", dirname); + free (old); + break; + } + } + if (from_tty) + directories_info (); + } +} + +/* Open a file named STRING, searching path PATH (dir names sep by colons) + using mode MODE and protection bits PROT in the calls to open. + If TRY_CWD_FIRST, try to open ./STRING before searching PATH. + (ie pretend the first element of PATH is ".") + If FILENAMED_OPENED is non-null, set it to a newly allocated string naming + the actual file opened (this string will always start with a "/" + + If a file is found, return the descriptor. + Otherwise, return -1, with errno set for the last name we tried to open. */ + +/* >>>> This should only allow files of certain types, + >>>> eg executable, non-directory */ +int +openp (path, try_cwd_first, string, mode, prot, filename_opened) + char *path; + int try_cwd_first; + char *string; + int mode; + int prot; + char **filename_opened; +{ + register int fd; + register char *filename; + register char *p, *p1; + register int len; + + /* ./foo => foo */ + while (string[0] == '.' && string[1] == '/') + string += 2; + + if (try_cwd_first || string[0] == '/') + { + filename = string; + fd = open (filename, mode, prot); + if (fd >= 0 || string[0] == '/') + goto done; + } + + filename = (char *) alloca (strlen (path) + strlen (string) + 2); + fd = -1; + for (p = path; p; p = p1 ? p1 + 1 : 0) + { + p1 = (char *) index (p, ':'); + if (p1) + len = p1 - p; + else + len = strlen (p); + + strncpy (filename, p, len); + filename[len] = 0; + strcat (filename, "/"); + strcat (filename, string); + + fd = open (filename, mode, prot); + if (fd >= 0) break; + } + + done: + if (filename_opened) + if (fd < 0) + *filename_opened = (char *) 0; + else if (filename[0] == '/') + *filename_opened = savestring (filename, strlen (filename)); + else + { + *filename_opened = concat (current_directory, "/", filename); + } + + return fd; +} + +/* Create and initialize the table S->line_charpos that records + the positions of the lines in the source file, which is assumed + to be open on descriptor DESC. + All set S->nlines to the number of such lines. */ + +static void +find_source_lines (s, desc) + struct symtab *s; + int desc; +{ + struct stat st; + register char *data, *p, *end; + int nlines = 0; + int lines_allocated = 1000; + int *line_charpos = (int *) xmalloc (lines_allocated * sizeof (int)); + extern int exec_mtime; + + fstat (desc, &st); + if (get_exec_file (0) != 0 && exec_mtime < st.st_mtime) + printf ("Source file is more recent than executable.\n"); + + data = (char *) alloca (st.st_size); + myread (desc, data, st.st_size); + end = data + st.st_size; + p = data; + line_charpos[0] = 0; + nlines = 1; + while (p != end) + { + if (*p++ == '\n' + /* A newline at the end does not start a new line. */ + && p != end) + { + if (nlines == lines_allocated) + { + lines_allocated *= 2; + line_charpos = (int *) xrealloc (line_charpos, + sizeof (int) * lines_allocated); + } + line_charpos[nlines++] = p - data; + } + } + s->nlines = nlines; + s->line_charpos = (int *) xrealloc (line_charpos, nlines * sizeof (int)); +} + +/* Return the character position of a line LINE in symtab S. + Return 0 if anything is invalid. */ + +int +source_line_charpos (s, line) + struct symtab *s; + int line; +{ + if (!s) return 0; + if (!s->line_charpos || line <= 0) return 0; + if (line > s->nlines) + line = s->nlines; + return s->line_charpos[line - 1]; +} + +/* Return the line number of character position POS in symtab S. */ + +int +source_charpos_line (s, chr) + register struct symtab *s; + register int chr; +{ + register int line = 0; + register int *lnp; + + if (s == 0 || s->line_charpos == 0) return 0; + lnp = s->line_charpos; + /* Files are usually short, so sequential search is Ok */ + while (line < s->nlines && *lnp <= chr) + { + line++; + lnp++; + } + if (line >= s->nlines) + line = s->nlines; + return line; +} + +/* Get full pathname and line number positions for a symtab. + Return nonzero if line numbers may have changed. + Set *FULLNAME to actual name of the file as found by `openp', + or to 0 if the file is not found. */ + +int +get_filename_and_charpos (s, line, fullname) + struct symtab *s; + int line; + char **fullname; +{ + register int desc, linenums_changed = 0; + + desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname); + if (desc < 0) + { + if (fullname) + *fullname = NULL; + return 0; + } + if (fullname) + *fullname = s->fullname; + if (s->line_charpos == 0) linenums_changed = 1; + if (linenums_changed) find_source_lines (s, desc); + close (desc); + return linenums_changed; +} + +/* Print text describing the full name of the source file S + and the line number LINE and its corresponding character position. + The text starts with two Ctrl-z so that the Emacs-GDB interface + can easily find it. + + MID_STATEMENT is nonzero if the PC is not at the beginning of that line. + + Return 1 if successful, 0 if could not find the file. */ + +int +identify_source_line (s, line, mid_statement) + struct symtab *s; + int line; + int mid_statement; +{ + if (s->line_charpos == 0) + get_filename_and_charpos (s, line, 0); + if (s->fullname == 0) + return 0; + printf ("\032\032%s:%d:%d:%s\n", s->fullname, + line, s->line_charpos[line - 1], + mid_statement ? "middle" : "beg"); + current_source_line = line; + first_line_listed = line; + last_line_listed = line; + current_source_symtab = s; + return 1; +} + +/* Print source lines from the file of symtab S, + starting with line number LINE and stopping before line number STOPLINE. */ + +void +print_source_lines (s, line, stopline, noerror) + struct symtab *s; + int line, stopline; + int noerror; +{ + register int c; + register int desc; + register FILE *stream; + int nlines = stopline - line; + + desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname); + if (desc < 0) + { + extern int errno; + if (! noerror) + perror_with_name (s->filename); + print_sys_errmsg (s->filename, errno); + return; + } + + if (s->line_charpos == 0) + find_source_lines (s, desc); + + if (line < 1 || line > s->nlines) + { + close (desc); + error ("Line number out of range; %s has %d lines.", + s->filename, s->nlines); + } + + if (lseek (desc, s->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (s->filename); + } + + current_source_symtab = s; + current_source_line = line; + first_line_listed = line; + + stream = fdopen (desc, "r"); + clearerr (stream); + + while (nlines-- > 0) + { + c = fgetc (stream); + if (c == EOF) break; + last_line_listed = current_source_line; + printf ("%d\t", current_source_line++); + do + { + if (c < 040 && c != '\t' && c != '\n') + { + fputc ('^', stdout); + fputc (c + 0100, stdout); + } + else if (c == 0177) + printf ("^?"); + else + fputc (c, stdout); + } while (c != '\n' && (c = fgetc (stream)) >= 0); + } + + fclose (stream); +} + + + +/* + C++ + Print a list of files and line numbers which a user may choose from + in order to list a function which was specified ambiguously + (as with `list classname::overloadedfuncname', for example). + The vector in SALS provides the filenames and line numbers. + */ +static void +ambiguous_line_spec (sals) + struct symtabs_and_lines *sals; +{ + int i; + + for (i = 0; i < sals->nelts; ++i) + printf("file: \"%s\", line number: %d\n", + sals->sals[i].symtab->filename, sals->sals[i].line); +} + + +static void +list_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals, sals_end; + struct symtab_and_line sal, sal_end; + struct symbol *sym; + char *arg1; + int no_end = 1; + int dummy_end = 0; + int dummy_beg = 0; + int linenum_beg = 0; + char *p; + + if (symtab_list == 0 && partial_symtab_list == 0) + error ("Listing source lines requires symbols."); + + /* Pull in a current source symtab if necessary */ + if (current_source_symtab == 0 && + (arg == 0 || arg[0] == '+' || arg[0] == '-')) + select_source_symtab (symtab_list); + + /* "l" or "l +" lists next ten lines. */ + + if (arg == 0 || !strcmp (arg, "+")) + { + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + print_source_lines (current_source_symtab, current_source_line, + current_source_line + 10, 0); + return; + } + + /* "l -" lists previous ten lines, the ones before the ten just listed. */ + if (!strcmp (arg, "-")) + { + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + print_source_lines (current_source_symtab, + max (first_line_listed - 10, 1), + first_line_listed, 0); + return; + } + + /* Now if there is only one argument, decode it in SAL + and set NO_END. + If there are two arguments, decode them in SAL and SAL_END + and clear NO_END; however, if one of the arguments is blank, + set DUMMY_BEG or DUMMY_END to record that fact. */ + + arg1 = arg; + if (*arg1 == ',') + dummy_beg = 1; + else + { + sals = decode_line_1 (&arg1, 0, 0, 0); + + if (! sals.nelts) return; /* C++ */ + if (sals.nelts > 1) + { + ambiguous_line_spec (&sals); + free (sals.sals); + return; + } + + sal = sals.sals[0]; + free (sals.sals); + } + + /* Record whether the BEG arg is all digits. */ + + for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++); + linenum_beg = (p == arg1); + + while (*arg1 == ' ' || *arg1 == '\t') + arg1++; + if (*arg1 == ',') + { + no_end = 0; + arg1++; + while (*arg1 == ' ' || *arg1 == '\t') + arg1++; + if (*arg1 == 0) + dummy_end = 1; + else + { + if (dummy_beg) + sals_end = decode_line_1 (&arg1, 0, 0, 0); + else + sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line); + if (sals_end.nelts == 0) + return; + if (sals_end.nelts > 1) + { + ambiguous_line_spec (&sals_end); + free (sals_end.sals); + return; + } + sal_end = sals_end.sals[0]; + free (sals_end.sals); + } + } + + if (*arg1) + error ("Junk at end of line specification."); + + if (!no_end && !dummy_beg && !dummy_end + && sal.symtab != sal_end.symtab) + error ("Specified start and end are in different files."); + if (dummy_beg && dummy_end) + error ("Two empty args do not say what lines to list."); + + /* if line was specified by address, + first print exactly which line, and which file. + In this case, sal.symtab == 0 means address is outside + of all known source files, not that user failed to give a filename. */ + if (*arg == '*') + { + if (sal.symtab == 0) + error ("No source file for address 0x%x.", sal.pc); + sym = find_pc_function (sal.pc); + if (sym) + printf ("0x%x is in %s (%s, line %d).\n", + sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line); + else + printf ("0x%x is in %s, line %d.\n", + sal.pc, sal.symtab->filename, sal.line); + } + + /* If line was not specified by just a line number, + and it does not imply a symtab, it must be an undebuggable symbol + which means no source code. */ + + if (! linenum_beg && sal.symtab == 0) + error ("No line number known for %s.", arg); + + /* If this command is repeated with RET, + turn it into the no-arg variant. */ + + if (from_tty) + *arg = 0; + + if (dummy_beg && sal_end.symtab == 0) + error ("No default source file yet. Do \"help list\"."); + if (dummy_beg) + print_source_lines (sal_end.symtab, max (sal_end.line - 9, 1), + sal_end.line + 1, 0); + else if (sal.symtab == 0) + error ("No default source file yet. Do \"help list\"."); + else if (no_end) + print_source_lines (sal.symtab, max (sal.line - 5, 1), sal.line + 5, 0); + else + print_source_lines (sal.symtab, sal.line, + dummy_end ? sal.line + 10 : sal_end.line + 1, + 0); +} + +/* Print info on range of pc's in a specified line. */ + +static void +line_info (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtabs_and_lines sals; + struct symtab_and_line sal; + int start_pc, end_pc; + int i; + + if (arg == 0) + { + sal.symtab = current_source_symtab; + sal.line = last_line_listed; + sals.nelts = 1; + sals.sals = (struct symtab_and_line *) + xmalloc (sizeof (struct symtab_and_line)); + sals.sals[0] = sal; + } + else + { + sals = decode_line_spec_1 (arg, 0); + + /* If this command is repeated with RET, + turn it into the no-arg variant. */ + if (from_tty) + *arg = 0; + } + + /* C++ More than one line may have been specified, as when the user + specifies an overloaded function name. Print info on them all. */ + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + if (sal.symtab == 0) + error ("No source file specified."); + + if (sal.line > 0 + && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc)) + { + if (start_pc == end_pc) + printf ("Line %d of \"%s\" is at pc 0x%x but contains no code.\n", + sal.line, sal.symtab->filename, start_pc); + else + printf ("Line %d of \"%s\" starts at pc 0x%x and ends at 0x%x.\n", + sal.line, sal.symtab->filename, start_pc, end_pc); + /* x/i should display this line's code. */ + set_next_address (start_pc); + /* Repeating "info line" should do the following line. */ + last_line_listed = sal.line + 1; + } + else + printf ("Line number %d is out of range for \"%s\".\n", + sal.line, sal.symtab->filename); + } +} + +/* Commands to search the source file for a regexp. */ + +static void +forward_search_command (regex, from_tty) + char *regex; +{ + register int c; + register int desc; + register FILE *stream; + int line = last_line_listed + 1; + char *msg; + + msg = (char *) re_comp (regex); + if (msg) + error (msg); + + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + + /* Search from last_line_listed+1 in current_source_symtab */ + + desc = openp (source_path, 0, current_source_symtab->filename, + O_RDONLY, 0, ¤t_source_symtab->fullname); + if (desc < 0) + perror_with_name (current_source_symtab->filename); + + if (current_source_symtab->line_charpos == 0) + find_source_lines (current_source_symtab, desc); + + if (line < 1 || line > current_source_symtab->nlines) + { + close (desc); + error ("Expression not found"); + } + + if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (current_source_symtab->filename); + } + + stream = fdopen (desc, "r"); + clearerr (stream); + while (1) { + char buf[4096]; /* Should be reasonable??? */ + register char *p = buf; + + c = fgetc (stream); + if (c == EOF) + break; + do { + *p++ = c; + } while (c != '\n' && (c = fgetc (stream)) >= 0); + + /* we now have a source line in buf, null terminate and match */ + *p = 0; + if (re_exec (buf) > 0) + { + /* Match! */ + fclose (stream); + print_source_lines (current_source_symtab, + line, line+1, 0); + current_source_line = max (line - 5, 1); + return; + } + line++; + } + + printf ("Expression not found\n"); + fclose (stream); +} + +static void +reverse_search_command (regex, from_tty) + char *regex; +{ + register int c; + register int desc; + register FILE *stream; + int line = last_line_listed - 1; + char *msg; + + msg = (char *) re_comp (regex); + if (msg) + error (msg); + + if (current_source_symtab == 0) + error ("No default source file yet. Do \"help list\"."); + + /* Search from last_line_listed-1 in current_source_symtab */ + + desc = openp (source_path, 0, current_source_symtab->filename, + O_RDONLY, 0, ¤t_source_symtab->fullname); + if (desc < 0) + perror_with_name (current_source_symtab->filename); + + if (current_source_symtab->line_charpos == 0) + find_source_lines (current_source_symtab, desc); + + if (line < 1 || line > current_source_symtab->nlines) + { + close (desc); + error ("Expression not found"); + } + + if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + close (desc); + perror_with_name (current_source_symtab->filename); + } + + stream = fdopen (desc, "r"); + clearerr (stream); + while (1) + { + char buf[4096]; /* Should be reasonable??? */ + register char *p = buf; + + c = fgetc (stream); + if (c == EOF) + break; + do { + *p++ = c; + } while (c != '\n' && (c = fgetc (stream)) >= 0); + + /* We now have a source line in buf; null terminate and match. */ + *p = 0; + if (re_exec (buf) > 0) + { + /* Match! */ + fclose (stream); + print_source_lines (current_source_symtab, + line, line+1, 0); + current_source_line = max (line - 5, 1); + return; + } + line--; + if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0) + { + fclose (stream); + perror_with_name (current_source_symtab->filename); + } + } + + printf ("Expression not found\n"); + fclose (stream); + return; +} + +void +_initialize_source () +{ + current_source_symtab = 0; + init_source_path (); + + add_com ("directory", class_files, directory_command, + "Add directory DIR to end of search path for source files.\n\ +With no argument, reset the search path to just the working directory\n\ +and forget cached info on line positions in source files."); + + add_info ("directories", directories_info, + "Current search path for finding source files."); + + add_info ("line", line_info, + "Core addresses of the code for a source line.\n\ +Line can be specified as\n\ + LINENUM, to list around that line in current file,\n\ + FILE:LINENUM, to list around that line in that file,\n\ + FUNCTION, to list around beginning of that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ +Default is to describe the last source line that was listed.\n\n\ +This sets the default address for \"x\" to the line's first instruction\n\ +so that \"x/i\" suffices to start examining the machine code.\n\ +The address is also stored as the value of \"$_\"."); + + add_com ("forward-search", class_files, forward_search_command, + "Search for regular expression (see regex(3)) from last line listed."); + add_com_alias ("search", "forward-search", class_files, 0); + + add_com ("reverse-search", class_files, reverse_search_command, + "Search backward for regular expression (see regex(3)) from last line listed."); + + add_com ("list", class_files, list_command, + "List specified function or line.\n\ +With no argument, lists ten more lines after or around previous listing.\n\ +\"list -\" lists the ten lines before a previous ten-line listing.\n\ +One argument specifies a line, and ten lines are listed around that line.\n\ +Two arguments with comma between specify starting and ending lines to list.\n\ +Lines can be specified in these ways:\n\ + LINENUM, to list around that line in current file,\n\ + FILE:LINENUM, to list around that line in that file,\n\ + FUNCTION, to list around beginning of that function,\n\ + FILE:FUNCTION, to distinguish among like-named static functions.\n\ + *ADDRESS, to list around the line containing that address.\n\ +With two args if one is empty it stands for ten lines away from the other arg."); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@d27 1 +a27 1 +#include <sys/fcntl.h> +@ diff --git a/gdb/RCS/sparc-dep.c,v b/gdb/RCS/sparc-dep.c,v new file mode 100644 index 0000000..8178e00 --- /dev/null +++ b/gdb/RCS/sparc-dep.c,v @@ -0,0 +1,1091 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.04.04.21.31.02; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.02.10.01.47.27; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.10.01.46.36; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@Fix handling of annulled branches in single step. "b foo; bcc,a bar" +annuls the instruction at foo, not just after the bcc,a. Also, +handle CBcc (coprocessor) annulled branch, and improve doc. +@ +text +@/* Machine-dependent code which would otherwise be in inflow.c and core.c, + for GDB, the GNU debugger. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + This code is for the sparc cpu. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "obstack.h" +#include "sparc-opcode.h" +#include "gdbcore.h" + +#include <stdio.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include <sys/ptrace.h> +#include <machine/reg.h> + +#include <a.out.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/core.h> + +extern int errno; +extern int attach_flag; + +/* This function simply calls ptrace with the given arguments. + It exists so that all calls to ptrace are isolated in this + machine-dependent file. */ +int +call_ptrace (request, pid, arg3, arg4) + int request, pid, arg3, arg4; +{ + return ptrace (request, pid, arg3, arg4); +} + +void +kill_inferior () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + inferior_died (); +} + +/* This is used when GDB is exiting. It gives less chance of error.*/ + +void +kill_inferior_fast () +{ + if (remote_debugging) + return; + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); +} + +/* Simulate single-step ptrace call for sun4. Code written by Gary + Beihl (beihl@@mcc.com). */ + +/* + * Duplicated from breakpoint.c because (at least for now) this is a + * machine dependent routine. + */ +static char break_insn[] = BREAKPOINT; + +/* From infrun.c */ +extern int stop_after_trap, stop_after_attach; + +static CORE_ADDR next_pc, npc4, target; +static int brknpc4, brktrg; +typedef char binsn_quantum[sizeof break_insn]; +static binsn_quantum break_mem[3]; + +/* Non-zero if we just simulated a single-step ptrace call. This is + needed because we cannot remove the breakpoints in the inferior + process until after the `wait' in `wait_for_inferior'. Used for + sun4. */ + +int one_stepped; + +void +single_step (signal) + int signal; +{ + branch_type br, isannulled(); + CORE_ADDR pc; + + next_pc = read_register (NPC_REGNUM); + npc4 = next_pc + 4; /* branch not taken */ + + if (!one_stepped) + { + /* Always set breakpoint for NPC. */ + read_memory (next_pc, break_mem[0], sizeof break_insn); + write_memory (next_pc, break_insn, sizeof break_insn); + /* printf ("set break at %x\n",next_pc); */ + + pc = read_register (PC_REGNUM); + br = isannulled (pc, &target); + brknpc4 = brktrg = 0; + + if (br == bicca) + { + /* Conditional annulled branch will either end up at + npc (if taken) or at npc+4 (if not taken). Trap npc+4. */ + brknpc4 = 1; + read_memory (npc4, break_mem[1], sizeof break_insn); + write_memory (npc4, break_insn, sizeof break_insn); + } + else if (br == baa && target != next_pc) + { + /* Unconditional annulled branch will always end up at + the target. */ + brktrg = 1; + read_memory (target, break_mem[2], sizeof break_insn); + write_memory (target, break_insn, sizeof break_insn); + } + + /* Let it go */ + ptrace (7, inferior_pid, 1, signal); + one_stepped = 1; + return; + } + else + { + /* Remove breakpoints */ + write_memory (next_pc, break_mem[0], sizeof break_insn); + + if (brknpc4) + { + write_memory (npc4, break_mem[1], sizeof break_insn); + } + if (brktrg) + { + write_memory (target, break_mem[2], sizeof break_insn); + } + one_stepped = 0; + } +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +resume (step, signal) + int step; + int signal; +{ + errno = 0; + if (remote_debugging) + remote_resume (step, signal); + else + { + /* Sparc doesn't have single step on ptrace */ + if (step) + single_step (signal); + else + ptrace (7, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); + } +} + +#ifdef ATTACH_DETACH + +/* Start debugging the process whose number is PID. */ + +int +attach (pid) + int pid; +{ + errno = 0; + ptrace (PTRACE_ATTACH, pid, 0, 0); + if (errno) + perror_with_name ("ptrace"); + attach_flag = 1; + return pid; +} + +/* Stop debugging the process whose number is PID + and continue it with signal number SIGNAL. + SIGNAL = 0 means just continue it. */ + +void +detach (signal) + int signal; +{ + errno = 0; + ptrace (PTRACE_DETACH, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); + attach_flag = 0; +} +#endif /* ATTACH_DETACH */ + +void +fetch_inferior_registers () +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + extern char registers[]; + int cwp; + struct rwindow local_and_ins; + + if (remote_debugging) + remote_fetch_registers (registers); + else + { + ptrace (PTRACE_GETREGS, inferior_pid, &inferior_registers); + ptrace (PTRACE_GETFPREGS, inferior_pid, &inferior_fp_registers); + + registers[REGISTER_BYTE (0)] = 0; + bcopy (&inferior_registers.r_g1, ®isters[REGISTER_BYTE (1)], 15 * 4); + bcopy (&inferior_fp_registers, ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof inferior_fp_registers.fpu_fr); + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = inferior_registers.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = inferior_registers.r_pc; + *(int *)®isters[REGISTER_BYTE (NPC_REGNUM)] = inferior_registers.r_npc; + *(int *)®isters[REGISTER_BYTE (Y_REGNUM)] = inferior_registers.r_y; +/* *(int *)®isters[REGISTER_BYTE (RP_REGNUM)] = + inferior_registers.r_o7 + 8; + bcopy (&inferior_fp_registers.Fpu_fsr, + ®isters[REGISTER_BYTE (FPS_REGNUM)], + sizeof (FPU_FSR_TYPE)); */ + + read_inferior_memory (inferior_registers.r_sp, + ®isters[REGISTER_BYTE (16)], + 16*4); + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (regno) + int regno; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + extern char registers[]; + + if (remote_debugging) + remote_store_registers (registers); + else + { + int in_regs = 1, in_fpregs = 1, in_fparegs, in_cpregs = 1; + + if (regno >= 0) + if (FP0_REGNUM <= regno && regno <= FP0_REGNUM + 32) + in_regs = 0; + else + in_fpregs = 0; + + if (in_regs) + { + bcopy (®isters[REGISTER_BYTE (1)], + &inferior_registers.r_g1, 15 * 4); + + inferior_registers.r_ps = + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)]; + inferior_registers.r_pc = + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)]; + inferior_registers.r_npc = + *(int *)®isters[REGISTER_BYTE (NPC_REGNUM)]; + inferior_registers.r_y = + *(int *)®isters[REGISTER_BYTE (Y_REGNUM)]; + + write_inferior_memory (*(int *)®isters[REGISTER_BYTE (SP_REGNUM)], + ®isters[REGISTER_BYTE (16)], + 16*4); + } + if (in_fpregs) + { + bcopy (®isters[REGISTER_BYTE (FP0_REGNUM)], + &inferior_fp_registers, + sizeof inferior_fp_registers.fpu_fr); + + /* bcopy (®isters[REGISTER_BYTE (FPS_REGNUM)], + &inferior_fp_registers.Fpu_fsr, + sizeof (FPU_FSR_TYPE)); + ****/ + } + + if (in_regs) + ptrace (PTRACE_SETREGS, inferior_pid, &inferior_registers); + if (in_fpregs) + ptrace (PTRACE_SETFPREGS, inferior_pid, &inferior_fp_registers); + } +} + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. + On failure (cannot read from inferior, usually because address is out + of bounds) returns the value of errno. */ + +int +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + buffer[i] = remote_fetch_word (addr); + else + buffer[i] = ptrace (1, inferior_pid, addr, 0); + if (errno) + return errno; + } + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + return 0; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (remote_debugging) + buffer[0] = remote_fetch_word (addr); + else + buffer[0] = ptrace (1, inferior_pid, addr, 0); + + if (count > 1) + { + if (remote_debugging) + buffer[count - 1] + = remote_fetch_word (addr + (count - 1) * sizeof (int)); + else + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + if (remote_debugging) + remote_store_word (addr, buffer[i]); + else + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + + +/* Machine-dependent code which would otherwise be in core.c */ +/* Work with core dump and executable files, for GDB. */ + +/* Recognize COFF format systems because a.out.h defines AOUTHDR. */ +#ifdef AOUTHDR +#define COFF_FORMAT +#endif + +#ifndef N_TXTADDR +#define N_TXTADDR(hdr) 0 +#endif /* no N_TXTADDR */ + +#ifndef N_DATADDR +#define N_DATADDR(hdr) hdr.a_text +#endif /* no N_DATADDR */ + +/* Make COFF and non-COFF names for things a little more compatible + to reduce conditionals later. */ + +#ifdef COFF_FORMAT +#define a_magic magic +#endif + +#ifndef COFF_FORMAT +#define AOUTHDR struct exec +#endif + +extern char *sys_siglist[]; + +/* Hook for `exec_file_command' command to call. */ + +extern void (*exec_file_display_hook) (); + +#ifdef COFF_FORMAT +/* various coff data structures */ + +extern FILHDR file_hdr; +extern SCNHDR text_hdr; +extern SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +extern AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +extern AOUTHDR exec_aouthdr; + +extern void validate_files (); + +void +core_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + extern char registers[]; + + /* Discard all vestiges of any previous core file + and mark data and stack spaces as empty. */ + + if (corefile) + free (corefile); + corefile = 0; + + if (corechan >= 0) + close (corechan); + corechan = -1; + + data_start = 0; + data_end = 0; + stack_start = STACK_END_ADDR; + stack_end = STACK_END_ADDR; + + /* Now, if a new core file was specified, open it and digest it. */ + + if (filename) + { + if (have_inferior_p ()) + error ("To look at a core file, you must kill the inferior with \"kill\"."); + corechan = open (filename, O_RDONLY, 0); + if (corechan < 0) + perror_with_name (filename); + + { + struct core corestr; + + val = myread (corechan, &corestr, sizeof corestr); + if (val < 0) + perror_with_name (filename); + if (corestr.c_magic != CORE_MAGIC) + error ("\"%s\" does not appear to be a core dump file (magic 0x%x, expected 0x%x)", + filename, corestr.c_magic, (int) CORE_MAGIC); + else if (sizeof (struct core) != corestr.c_len) + error ("\"%s\" has an invalid struct core length (%d, expected %d)", + filename, corestr.c_len, (int) sizeof (struct core)); + + /* Note that data_start and data_end don't depend on the exec file */ + data_start = N_DATADDR (corestr.c_aouthdr); + data_end = data_start + corestr.c_dsize; + stack_start = stack_end - corestr.c_ssize; + data_offset = sizeof corestr; + stack_offset = sizeof corestr + corestr.c_dsize; + + /* G0 *always* holds 0. */ + *(int *)®isters[REGISTER_BYTE (0)] = 0; + /* The globals and output registers. */ + + bcopy (&corestr.c_regs.r_g1, ((int *) registers) + 1, 15 * 4); + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = corestr.c_regs.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = corestr.c_regs.r_pc; + *(int *)®isters[REGISTER_BYTE (NPC_REGNUM)] = corestr.c_regs.r_npc; + *(int *)®isters[REGISTER_BYTE (Y_REGNUM)] = corestr.c_regs.r_y; + + /* My best guess at where to get the locals and input + registers is exactly where they usually are, right above + the stack pointer. If the core dump was caused by a bus + writing off the stack pointer (as is possible) then this + won't work, but it's worth the try. */ + { + int sp; + + sp = *(int *)®isters[REGISTER_BYTE (SP_REGNUM)]; + lseek (corechan, sp - stack_start + stack_offset, L_SET); + if (16 * 4 != myread (corechan, + ®isters[REGISTER_BYTE (16)], + 16 * 4)) + /* fprintf so user can still use gdb */ + fprintf (stderr, "Couldn't read input and local registers from core file\n"); + } + + bcopy (corestr.c_fpu.fpu_regs, + ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof corestr.c_fpu.fpu_regs); +#ifdef FPU + bcopy (&corestr.c_fpu.fpu_fsr, + ®isters[REGISTER_BYTE (FPS_REGNUM)], + sizeof (FPU_FSR_TYPE)); +#endif + + bcopy (&corestr.c_aouthdr, &core_aouthdr, sizeof (struct exec)); + + printf ("Core file is from \"%s\".\n", corestr.c_cmdname); + if (corestr.c_signo > 0) + printf ("Program terminated with signal %d, %s.\n", + corestr.c_signo, + corestr.c_signo < NSIG + ? sys_siglist[corestr.c_signo] + : "(undocumented)"); + } + if (filename[0] == '/') + corefile = savestring (filename, strlen (filename)); + else + { + corefile = concat (current_directory, "/", filename); + } + + set_current_frame ( create_new_frame (read_register (FP_REGNUM), + read_pc ())); + select_frame (get_current_frame (), 0); + validate_files (); + } + else if (from_tty) + printf ("No core file now.\n"); +} + +void +exec_file_command (filename, from_tty) + char *filename; + int from_tty; +{ + int val; + + /* Eliminate all traces of old exec file. + Mark text segment as empty. */ + + if (execfile) + free (execfile); + execfile = 0; + text_start = 0; + text_end = 0; + exec_data_start = 0; + exec_data_end = 0; + if (execchan >= 0) + close (execchan); + execchan = -1; + + /* Now open and digest the file the user requested, if any. */ + + if (filename) + { + execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, + &execfile); + if (execchan < 0) + perror_with_name (filename); + +#ifdef COFF_FORMAT + { + int aout_hdrsize; + int num_sections; + + if (read_file_hdr (execchan, &file_hdr) < 0) + error ("\"%s\": not in executable format.", execfile); + + aout_hdrsize = file_hdr.f_opthdr; + num_sections = file_hdr.f_nscns; + + if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0) + error ("\"%s\": can't read optional aouthdr", execfile); + + if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections) < 0) + error ("\"%s\": can't read text section header", execfile); + + if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections) < 0) + error ("\"%s\": can't read data section header", execfile); + + text_start = exec_aouthdr.text_start; + text_end = text_start + exec_aouthdr.tsize; + text_offset = text_hdr.s_scnptr; + exec_data_start = exec_aouthdr.data_start; + exec_data_end = exec_data_start + exec_aouthdr.dsize; + exec_data_offset = data_hdr.s_scnptr; + exec_mtime = file_hdr.f_timdat; + } +#else /* not COFF_FORMAT */ + { + struct stat st_exec; + val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); + + if (val < 0) + perror_with_name (filename); + + text_start = N_TXTADDR (exec_aouthdr); + exec_data_start = N_DATADDR (exec_aouthdr); + text_offset = N_TXTOFF (exec_aouthdr); + exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; + + text_end = text_start + exec_aouthdr.a_text; + exec_data_end = exec_data_start + exec_aouthdr.a_data; + + fstat (execchan, &st_exec); + exec_mtime = st_exec.st_mtime; + } +#endif /* not COFF_FORMAT */ + + validate_files (); + } + else if (from_tty) + printf ("No exec file now.\n"); + + /* Tell display code (if any) about the changed file name. */ + if (exec_file_display_hook) + (*exec_file_display_hook) (filename); +} + +/* + * Find the pc saved in frame FRAME. + */ +CORE_ADDR +frame_saved_pc (frame) + FRAME frame; +{ + CORE_ADDR prev_pc; + + /* If it's at the bottom, the return value's stored in i7/rp */ + if (get_current_frame () == frame) + prev_pc = GET_RWINDOW_REG (read_register (SP_REGNUM), rw_in[7]); + else + /* Wouldn't this always work? This would allow this routine to + be completely a macro. */ + prev_pc = GET_RWINDOW_REG (frame->bottom, rw_in[7]); + + return PC_ADJUST (prev_pc); +} + +/* + * Since an individual frame in the frame cache is defined by two + * arguments (a frame pointer and a stack pointer), we need two + * arguments to get info for an arbitrary stack frame. This routine + * takes two arguments and makes the cached frames look as if these + * two arguments defined a frame on the cache. This allows the rest + * of info frame to extract the important arguments without + * difficulty. + */ +FRAME +setup_arbitrary_frame (frame, stack) + FRAME_ADDR frame, stack; +{ + struct frame_info *fci; + FRAME fid = create_new_frame (frame, 0); + + if (!fid) + fatal ("internal: create_new_frame returned invalid frame id"); + + fid->bottom = stack; + + return fid; +} + +/* This code was written by Gary Beihl (beihl@@mcc.com). + It was modified by Michael Tiemann (tiemann@@corto.inria.fr). */ + +struct command_line *get_breakpoint_commands (); + +/* + * This routine appears to be passed a size by which to increase the + * stack. It then executes a save instruction in the inferior to + * increase the stack by this amount. Only the register window system + * should be affected by this; the program counter & etc. will not be. + * + * This instructions used for this purpose are: + * + * sethi %hi(0x0),g1 * + * add g1,0x1ee0,g1 * + * save sp,g1,sp + * sethi %hi(0x0),g1 * + * add g1,0x1ee0,g1 * + * t g0,0x1,o0 + * sethi %hi(0x0),g0 (nop) + * + * I presume that these set g1 to be the negative of the size, do a + * save (putting the stack pointer at sp - size) and restore the + * original contents of g1. A * indicates that the actual value of + * the instruction is modified below. + */ +static int save_insn_opcodes[] = { + 0x03000000, 0x82007ee0, 0x9de38001, 0x03000000, + 0x82007ee0, 0x91d02001, 0x01000000 }; + +/* Neither do_save_insn or do_restore_insn save stack configuration + (since the stack is in an indeterminate state through the call to + each of them); that responsibility of the routine which calls them. */ + +void +do_save_insn (size) + int size; +{ + int g1 = read_register (1); + CORE_ADDR sp = read_register (SP_REGNUM); + CORE_ADDR pc = read_register (PC_REGNUM); + CORE_ADDR npc = read_register (NPC_REGNUM); + CORE_ADDR fake_pc = sp - sizeof (save_insn_opcodes); + struct inferior_status inf_status; + + save_inferior_status (&inf_status, 0); /* Don't restore stack info */ + /* + * See above. + */ + save_insn_opcodes[0] = 0x03000000 | ((-size >> 10) & 0x3fffff); + save_insn_opcodes[1] = 0x82006000 | (-size & 0x3ff); + save_insn_opcodes[3] = 0x03000000 | ((g1 >> 10) & 0x3fffff); + save_insn_opcodes[4] = 0x82006000 | (g1 & 0x3ff); + write_memory (fake_pc, save_insn_opcodes, sizeof (save_insn_opcodes)); + + clear_proceed_status (); + stop_after_trap = 1; + proceed (fake_pc, 0, 0); + + write_register (PC_REGNUM, pc); + write_register (NPC_REGNUM, npc); + restore_inferior_status (&inf_status); +} + +/* + * This routine takes a program counter value. It restores the + * register window system to the frame above the current one, and sets + * the pc and npc to the correct values. + */ + +/* The following insns translate to: + + restore + t g0,0x1,o0 + sethi %hi(0x0), g0 */ + +static int restore_insn_opcodes[] = { 0x81e80000, 0x91d02001, 0x01000000 }; + +void +do_restore_insn (pc) + CORE_ADDR pc; +{ + CORE_ADDR sp = read_register (SP_REGNUM); + CORE_ADDR npc = pc + 4; + CORE_ADDR fake_pc = sp - sizeof (restore_insn_opcodes); + struct inferior_status inf_status; + + save_inferior_status (&inf_status, 0); /* Don't restore stack info */ + + if (!pc) + abort(); + + write_memory (fake_pc, restore_insn_opcodes, sizeof (restore_insn_opcodes)); + + clear_proceed_status (); + stop_after_trap = 1; + proceed (fake_pc, 0, 0); + + write_register (PC_REGNUM, pc); + write_register (NPC_REGNUM, npc); + restore_inferior_status (&inf_status); +} + +/* + * This routine should be more specific in it's actions; making sure + * that it uses the same register in the initial prologue section. + */ +CORE_ADDR +skip_prologue (pc) + CORE_ADDR pc; +{ + union + { + union insn_fmt insn; + int i; + } x; + int dest = -1; + + x.i = read_memory_integer (pc, 4); + + /* Recognize sethi insn. Record destination. */ + if (x.insn.sethi.op == 0 + && x.insn.sethi.op2 == 4) + { + dest = x.insn.sethi.rd; + pc += 4; + x.i = read_memory_integer (pc, 4); + } + + /* Recognizes an add immediate value to register to either %g1 or + the destination register recorded above. Actually, this might + well recognize several different arithmetic operations.*/ + if (x.insn.arith_imm.op == 2 + && x.insn.arith_imm.i == 1 + && (x.insn.arith_imm.rd == 1 + || x.insn.arith_imm.rd == dest)) + { + pc += 4; + x.i = read_memory_integer (pc, 4); + } + + /* This recognizes any SAVE insn. But why do the XOR and then + the compare? That's identical to comparing against 60 (as long + as there isn't any sign extension). */ + if (x.insn.arith.op == 2 + && (x.insn.arith.op3 ^ 32) == 28) + { + pc += 4; + x.i = read_memory_integer (pc, 4); + } + + /* Now we need to recognize stores into the frame from the input + registers. This recognizes all non alternate stores of input + register, into a location offset from the frame pointer. */ + while (x.insn.arith_imm.op == 3 + && (x.insn.arith_imm.op3 & 0x3c) == 4 /* Store, non-alt */ + && (x.insn.arith_imm.rd & 0x18) == 0x18 /* Input register */ + && x.insn.arith_imm.i == 1 /* Immediate mode */ + && x.insn.arith_imm.rs1 == 30 /* Off of frame pointer */ + && x.insn.arith_imm.simm >= 0x44 /* Into reserved */ + && x.insn.arith_imm.simm < 0x5b) /* stack space. */ + { + pc += 4; + x.i = read_memory_integer (pc, 4); + } + return pc; +} + +/* + * Check instruction at "addr" to see if it is an annulled branch. + * All other instructions will go to NPC or will trap. + * + * Set *target if we find a candidate branch; set to zero if not. + */ + +branch_type +isannulled (addr, target) + CORE_ADDR addr, *target; +{ + union insn_fmt instr; + branch_type val = not_branch; + long offset; /* Must be signed for sign-extend */ + + *target = 0; + instr.intval = read_memory_integer (addr, 4); + /* printf("intval = %x\n",instr.intval); */ + switch (instr.op1.op1) + { + case 0: /* Format 2 */ + switch(instr.op2.op2) + { + case 2: case 6: case 7: /* Bcc, FBcc, CBcc */ + if (instr.branch.cond == 8) + val = instr.branch.a ? baa : ba; + else + val = instr.branch.a ? bicca : bicc; + /* 22 bits, sign extended */ + offset = 4 * ((int) (instr.branch.disp << 10) >> 10); + *target = addr + offset; + break; + } + break; + } + /*printf("isannulled ret: %d\n",val); */ + return val; +} +@ + + +1.2 +log +@ * Use gdbcore.h rather than a bunch of externs. + * Avoid dependency on "exec file" when figuring out data_start and data_end +of core file. +@ +text +@d97 2 +a98 2 +static CORE_ADDR next_pc, pc8, target; +static int brkpc8, brktrg; +d113 2 +a114 1 + branch_type br, isabranch(); +d117 1 +a117 1 + pc8 = read_register (PC_REGNUM) + 8; /* branch not taken */ +d124 1 +d126 3 +a128 3 + /* printf ("set break at %x\n",next_pc); */ + br = isabranch (pc8 - 8, &target); + brkpc8 = brktrg = 0; +d130 7 +a136 6 + if (br == bicca && pc8 != next_pc) + { + /* Handle branches with care */ + brkpc8 = 1; + read_memory (pc8, break_mem[1], sizeof break_insn); + write_memory (pc8, break_insn, sizeof break_insn); +d140 2 +d157 1 +a157 1 + if (brkpc8) +d159 1 +a159 1 + write_memory (pc8, break_mem[1], sizeof break_insn); +d895 6 +a900 1 +/* Set *target if we find a branch. */ +d903 1 +a903 1 +isabranch (addr, target) +d918 1 +a918 1 + case 2: case 6: /* BICC & FBCC */ +d930 1 +a930 1 + /*printf("isabranch ret: %d\n",val); */ +@ + + +1.1 +log +@Initial revision +@ +text +@d29 1 +a453 49 +/* File names of core file and executable file. */ + +extern char *corefile; +extern char *execfile; + +/* Descriptors on which core file and executable file are open. + Note that the execchan is closed when an inferior is created + and reopened if the inferior dies or is killed. */ + +extern int corechan; +extern int execchan; + +/* Last modification time of executable file. + Also used in source.c to compare against mtime of a source file. */ + +extern int exec_mtime; + +/* Virtual addresses of bounds of the two areas of memory in the core file. */ + +extern CORE_ADDR data_start; +extern CORE_ADDR data_end; +extern CORE_ADDR stack_start; +extern CORE_ADDR stack_end; + +/* Virtual addresses of bounds of two areas of memory in the exec file. + Note that the data area in the exec file is used only when there is no core file. */ + +extern CORE_ADDR text_start; +extern CORE_ADDR text_end; + +extern CORE_ADDR exec_data_start; +extern CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +extern int text_offset; + +/* Address in executable file of start of data area data. */ + +extern int exec_data_offset; + +/* Address in core file of start of data area data. */ + +extern int data_offset; + +/* Address in core file of start of stack area data. */ + +extern int stack_offset; + +d520 2 +a521 1 + data_start = exec_data_start; +a601 2 + data_start = 0; + data_end -= exec_data_start; +a644 2 + data_start = exec_data_start; + data_end += exec_data_start; +a661 2 + data_start = exec_data_start; + data_end += exec_data_start; +@ diff --git a/gdb/RCS/stack.c,v b/gdb/RCS/stack.c,v new file mode 100644 index 0000000..fc755c2 --- /dev/null +++ b/gdb/RCS/stack.c,v @@ -0,0 +1,882 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.02.09.23.53.05; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.02.09.15.03.51; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Avoid coredumps if stack commands are used when there is no stack. +@ +text +@/* Print and select stack frames for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> + +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "inferior.h" +#include "gdbcore.h" + + +/* Thie "selected" stack frame is used by default for local and arg access. + May be zero, for no selected frame. */ + +FRAME selected_frame; + +/* Level of the selected frame: + 0 for innermost, 1 for its caller, ... + or -1 for frame specified by address with no defined level. */ + +int selected_frame_level; + +/* Error message when selected_frame is zero when it's needed */ +char no_sel_frame[] = "There is no current stack frame."; + +/* Nonzero means print the full filename and linenumber + when a frame is printed, and do so in a format programs can parse. */ + +int frame_file_full_name = 0; + +static void select_calling_frame (); + +void print_frame_info (); + +/* Print a stack frame briefly. FRAME should be the frame address + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +/* FIXME, the argument "frame" is always "selected_frame". This is why + we can say "No selected frame" if it == 0. Probably shouldn't be an + argument anymore... */ + +static void +print_stack_frame (frame, level, source) + FRAME frame; + int level; + int source; +{ + struct frame_info *fi; + + if (frame == 0) + error (no_sel_frame); + fi = get_frame_info (frame); + + print_frame_info (fi, level, source, 1); +} + +void +print_frame_info (fi, level, source, args) + struct frame_info *fi; + register int level; + int source; + int args; +{ + struct symtab_and_line sal; + struct symbol *func; + register char *funname = 0; + int numargs; + + sal = find_pc_line (fi->pc, fi->next_frame); + func = find_pc_function (fi->pc); + if (func) + funname = SYMBOL_NAME (func); + else + { + register int misc_index = find_pc_misc_function (fi->pc); + if (misc_index >= 0) + funname = misc_function_vector[misc_index].name; + } + + if (source >= 0 || !sal.symtab) + { + if (level >= 0) + printf ("#%-2d ", level); + if (fi->pc != sal.pc || !sal.symtab) + printf ("0x%x in ", fi->pc); + printf ("%s (", funname ? funname : "??"); + if (args) + { + FRAME_NUM_ARGS (numargs, fi); + print_frame_args (func, fi, numargs, stdout); + } + printf (")"); + if (sal.symtab) + printf (" (%s line %d)", sal.symtab->filename, sal.line); + printf ("\n"); + } + + if (source != 0 && sal.symtab) + { + int done = 0; + int mid_statement = source < 0 && fi->pc != sal.pc; + if (frame_file_full_name) + done = identify_source_line (sal.symtab, sal.line, mid_statement); + if (!done) + { + if (mid_statement) + printf ("0x%x\t", fi->pc); + print_source_lines (sal.symtab, sal.line, sal.line + 1, 1); + } + current_source_line = max (sal.line - 5, 1); + } + if (source != 0) + set_default_breakpoint (1, fi->pc, sal.symtab, sal.line); + + fflush (stdout); +} + +/* Call here to print info on selected frame, after a trap. */ + +void +print_sel_frame (just_source) + int just_source; +{ + print_stack_frame (selected_frame, -1, just_source ? -1 : 1); +} + +/* Print info on the selected frame, including level number + but not source. */ + +void +print_selected_frame () +{ + print_stack_frame (selected_frame, selected_frame_level, 0); +} + +void flush_cached_frames (); /* FIXME, never called! */ + +#ifdef FRAME_SPECIFICATION_DYADIC +extern FRAME setup_arbitrary_frame (); +#endif + +/* + * Read a frame specification in whatever the appropriate format is. + */ +static FRAME +parse_frame_specification (frame_exp) + char *frame_exp; +{ + int numargs = 0; + int arg1, arg2; + + if (frame_exp) + { + char *addr_string, *p; + struct cleanup *tmp_cleanup; + struct frame_info *fci; + + while (*frame_exp == ' ') frame_exp++; + for (p = frame_exp; *p && *p != ' '; p++) + ; + + if (*frame_exp) + { + numargs = 1; + addr_string = savestring(frame_exp, p - frame_exp); + + { + tmp_cleanup = make_cleanup (free, addr_string); + arg1 = parse_and_eval_address (addr_string); + do_cleanups (tmp_cleanup); + } + + while (*p == ' ') p++; + + if (*p) + { + numargs = 2; + arg2 = parse_and_eval_address (p); + } + } + } + + switch (numargs) + { + case 0: + if (selected_frame == 0) + error (no_sel_frame); + return selected_frame; + /* NOTREACHED */ + case 1: + { + int level = arg1; + FRAME fid = find_relative_frame (get_current_frame (), &level); + FRAME tfid; + + if (level == 0) + /* find_relative_frame was successful */ + return fid; + + /* If (s)he specifies the frame with an address, he deserves what + (s)he gets. Still, give the highest one that matches. */ + + for (fid = get_current_frame (); + fid && FRAME_FP (fid) != arg1; + fid = get_prev_frame (fid)) + ; + + if (fid) + while ((tfid = get_prev_frame (fid)) && + (FRAME_FP (tfid) == arg1)) + fid = tfid; + +#ifdef FRAME_SPECIFICATION_DYADIC + if (!fid) + error ("Incorrect number of args in frame specification"); + + return fid; +#else + return create_new_frame (arg1, 0); +#endif + } + /* NOTREACHED */ + case 2: + /* Must be addresses */ +#ifndef FRAME_SPECIFICATION_DYADIC + error ("Incorrect number of args in frame specification"); +#else + return setup_arbitrary_frame (arg1, arg2); +#endif + /* NOTREACHED */ + } + fatal ("Internal: Error in parsing in parse_frame_specification"); + /* NOTREACHED */ +} + +/* Print verbosely the selected frame or the frame at address ADDR. + This means absolutely all information in the frame is printed. */ + +static void +frame_info (addr_exp) + char *addr_exp; +{ + FRAME frame; + struct frame_info *fi; + struct frame_saved_regs fsr; + struct symtab_and_line sal; + struct symbol *func; + FRAME calling_frame; + int i, count; + char *funname = 0; + int numargs; + + frame = parse_frame_specification (addr_exp); + + fi = get_frame_info (frame); + get_frame_saved_regs (fi, &fsr); + sal = find_pc_line (fi->pc, fi->next_frame); + func = get_frame_function (frame); + if (func) + funname = SYMBOL_NAME (func); + else + { + register int misc_index = find_pc_misc_function (fi->pc); + if (misc_index >= 0) + funname = misc_function_vector[misc_index].name; + } + calling_frame = get_prev_frame (frame); + + if (!addr_exp && selected_frame_level >= 0) + printf ("Stack level %d, frame at 0x%x:\n pc = 0x%x", + selected_frame_level, FRAME_FP(frame), fi->pc); + else + printf ("Stack frame at 0x%x:\n pc = 0x%x", + FRAME_FP(frame), fi->pc); + + if (funname) + printf (" in %s", funname); + if (sal.symtab) + printf (" (%s line %d)", sal.symtab->filename, sal.line); + printf ("; saved pc 0x%x\n", FRAME_SAVED_PC (frame)); + if (calling_frame) + printf (" called by frame at 0x%x", FRAME_FP (calling_frame)); + if (fi->next_frame && calling_frame) + printf (","); + if (fi->next_frame) + printf (" caller of frame at 0x%x", fi->next_frame); + if (fi->next_frame || calling_frame) + printf ("\n"); + printf (" Arglist at 0x%x,", FRAME_ARGS_ADDRESS (fi)); + FRAME_NUM_ARGS (i, fi); + if (i < 0) + printf (" args: "); + else if (i == 0) + printf (" no args."); + else if (i == 1) + printf (" 1 arg: "); + else + printf (" %d args: ", i); + + FRAME_NUM_ARGS (numargs, fi); + print_frame_args (func, fi, numargs, stdout); + printf ("\n"); + count = 0; + for (i = 0; i < NUM_REGS; i++) + if (fsr.regs[i]) + { + if (count % 4 != 0) + printf (", "); + else + { + if (count == 0) + printf (" Saved registers:"); + printf ("\n "); + } + printf ("%s at 0x%x", reg_names[i], fsr.regs[i]); + count++; + } + if (count) + printf ("\n"); +} + +#if 0 +/* Set a limit on the number of frames printed by default in a + backtrace. */ + +static int backtrace_limit; + +static void +set_backtrace_limit_command (count_exp, from_tty) + char *count_exp; + int from_tty; +{ + int count = parse_and_eval_address (count_exp); + + if (count < 0) + error ("Negative argument not meaningful as backtrace limit."); + + backtrace_limit = count; +} + +static void +backtrace_limit_info (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg) + error ("\"Info backtrace-limit\" takes no arguments."); + + printf ("Backtrace limit: %d.\n", backtrace_limit); +} +#endif + +/* Print briefly all stack frames or just the innermost COUNT frames. */ + +static void +backtrace_command (count_exp) + char *count_exp; +{ + struct frame_info *fi; + register int count; + register FRAME frame; + register int i; + register FRAME trailing; + register int trailing_level; + + if (have_inferior_p () == 0 && corefile == 0) + error ("There is no running program or core file."); + + /* The following code must do two things. First, it must + set the variable TRAILING to the frame from which we should start + printing. Second, it must set the variable count to the number + of frames which we should print, or -1 if all of them. */ + trailing = get_current_frame (); + trailing_level = 0; + if (count_exp) + { + count = parse_and_eval_address (count_exp); + if (count < 0) + { + FRAME current; + + count = -count; + + current = trailing; + while (current && count--) + current = get_prev_frame (current); + + /* Will stop when CURRENT reaches the top of the stack. TRAILING + will be COUNT below it. */ + while (current) + { + trailing = get_prev_frame (trailing); + current = get_prev_frame (current); + trailing_level++; + } + + count = -1; + } + } + else +#if 0 + count = backtrace_limit; +#else + count = -1; +#endif + + for (i = 0, frame = trailing; + frame && count--; + i++, frame = get_prev_frame (frame)) + { + QUIT; + fi = get_frame_info (frame); + print_frame_info (fi, trailing_level + i, 0, 1); + } + + /* If we've stopped before the end, mention that. */ + if (frame) + printf ("(More stack frames follow...)\n"); +} + +/* Print the local variables of a block B active in FRAME. */ + +static void +print_block_frame_locals (b, frame, stream) + struct block *b; + register FRAME frame; + register FILE *stream; +{ + int nsyms; + register int i; + register struct symbol *sym; + + nsyms = BLOCK_NSYMS (b); + + for (i = 0; i < nsyms; i++) + { + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_LOCAL + || SYMBOL_CLASS (sym) == LOC_REGISTER + || SYMBOL_CLASS (sym) == LOC_STATIC) + { + fprintf (stream, "%s = ", SYMBOL_NAME (sym)); + print_variable_value (sym, frame, stream); + fprintf (stream, "\n"); + fflush (stream); + } + } +} + +/* Print on STREAM all the local variables in frame FRAME, + including all the blocks active in that frame + at its current pc. + + Returns 1 if the job was done, + or 0 if nothing was printed because we have no info + on the function running in FRAME. */ + +static int +print_frame_local_vars (frame, stream) + register FRAME frame; + register FILE *stream; +{ + register struct block *block; + + block = get_frame_block (frame); + if (block == 0) + return 0; + while (block != 0) + { + print_block_frame_locals (block, frame, stream); + /* After handling the function's top-level block, stop. + Don't continue to its superblock, the block of + per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + return 1; +} + +static void +locals_info () +{ + if (selected_frame == 0) + error(no_sel_frame); + print_frame_local_vars (selected_frame, stdout); +} + +static int +print_frame_arg_vars (frame, stream) + register FRAME frame; + register FILE *stream; +{ + struct symbol *func; + register struct block *b; + int nsyms; + register int i; + register struct symbol *sym; + + func = get_frame_function (frame); + if (func == 0) + return 0; + + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + + for (i = 0; i < nsyms; i++) + { + sym = BLOCK_SYM (b, i); + if (SYMBOL_CLASS (sym) == LOC_ARG || SYMBOL_CLASS (sym) == LOC_REGPARM) + { + fprintf (stream, "%s = ", SYMBOL_NAME (sym)); + print_variable_value (sym, frame, stream); + fprintf (stream, "\n"); + fflush (stream); + } + } + + return 1; +} + +static void +args_info () +{ + if (selected_frame == 0) + error(no_sel_frame); + print_frame_arg_vars (selected_frame, stdout); +} + +/* Select frame FRAME, and note that its stack level is LEVEL. + LEVEL may be -1 if an actual level number is not known. */ + +void +select_frame (frame, level) + FRAME frame; + int level; +{ + selected_frame = frame; + selected_frame_level = level; +} + +/* Store the selected frame and its level into *FRAMEP and *LEVELP. */ + +void +record_selected_frame (frameaddrp, levelp) + FRAME_ADDR *frameaddrp; + int *levelp; +{ + *frameaddrp = FRAME_FP (selected_frame); + *levelp = selected_frame_level; +} + +/* Return the symbol-block in which the selected frame is executing. + Can return zero under various legitimate circumstances. */ + +struct block * +get_selected_block () +{ + if (!have_inferior_p () && !have_core_file_p ()) + return 0; + + if (!selected_frame) + return get_current_block (); + return get_frame_block (selected_frame); +} + +/* Find a frame a certain number of levels away from FRAME. + LEVEL_OFFSET_PTR points to an int containing the number of levels. + Positive means go to earlier frames (up); negative, the reverse. + The int that contains the number of levels is counted toward + zero as the frames for those levels are found. + If the top or bottom frame is reached, that frame is returned, + but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates + how much farther the original request asked to go. */ + +FRAME +find_relative_frame (frame, level_offset_ptr) + register FRAME frame; + register int* level_offset_ptr; +{ + register FRAME prev; + register FRAME frame1, frame2; + + if (frame == 0) + error (no_sel_frame); + /* Going up is simple: just do get_prev_frame enough times + or until initial frame is reached. */ + while (*level_offset_ptr > 0) + { + prev = get_prev_frame (frame); + if (prev == 0) + break; + (*level_offset_ptr)--; + frame = prev; + } + /* Going down could be done by iterating get_frame_info to + find the next frame, but that would be quadratic + since get_frame_info must scan all the way from the current frame. + The following algorithm is linear. */ + if (*level_offset_ptr < 0) + { + /* First put frame1 at innermost frame + and frame2 N levels up from there. */ + frame1 = get_current_frame (); + frame2 = frame1; + while (*level_offset_ptr < 0 && frame2 != frame) + { + frame2 = get_prev_frame (frame2); + (*level_offset_ptr) ++; + } + /* Then slide frame1 and frame2 up in synchrony + and when frame2 reaches our starting point + frame1 must be N levels down from there. */ + while (frame2 != frame) + { + frame1 = get_prev_frame (frame1); + frame2 = get_prev_frame (frame2); + } + return frame1; + } + return frame; +} + +/* The "frame" command. With no arg, print selected frame briefly. + With arg LEVEL_EXP, select the frame at level LEVEL if it is a + valid level. Otherwise, treat level_exp as an address expression + and print it. See parse_frame_specification for more info on proper + frame expressions. */ + +static void +frame_command (level_exp, from_tty) + char *level_exp; + int from_tty; +{ + register FRAME frame, frame1; + unsigned int level = 0; + + frame = parse_frame_specification (level_exp); + + for (frame1 = get_prev_frame (0); + frame1 && frame1 != frame; + frame1 = get_prev_frame (frame1)) + level++; + + if (!frame1) + level = 0; + + select_frame (frame, level); + + if (!from_tty) + return; + + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +/* Select the frame up one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +static void +up_command (count_exp) + char *count_exp; +{ + register FRAME frame; + int count = 1, count1; + if (count_exp) + count = parse_and_eval_address (count_exp); + count1 = count; + + frame = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + error ("Initial frame selected; you cannot go up."); + select_frame (frame, selected_frame_level + count - count1); + + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +/* Select the frame down one or COUNT stack levels + from the previously selected frame, and print it briefly. */ + +static void +down_command (count_exp) + char *count_exp; +{ + register FRAME frame; + int count = -1, count1; + if (count_exp) + count = - parse_and_eval_address (count_exp); + count1 = count; + + frame = find_relative_frame (selected_frame, &count1); + if (count1 != 0 && count_exp == 0) + error ("Bottom (i.e., innermost) frame selected; you cannot go down."); + select_frame (frame, selected_frame_level + count - count1); + + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +static void +return_command (retval_exp, from_tty) + char *retval_exp; + int from_tty; +{ + struct symbol *thisfun = get_frame_function (selected_frame); + + /* If interactive, require confirmation. */ + + if (from_tty) + { + if (thisfun != 0) + { + if (!query ("Make %s return now? ", SYMBOL_NAME (thisfun))) + error ("Not confirmed."); + } + else + if (!query ("Make selected stack frame return now? ")) + error ("Not confirmed."); + } + + /* Do the real work. Pop until the specified frame is current. */ + + while (selected_frame != get_current_frame ()) + POP_FRAME; + + /* Then pop that frame. */ + + POP_FRAME; + + /* Compute the return value (if any) and store in the place + for return values. */ + + if (retval_exp) + set_return_value (parse_and_eval (retval_exp)); + + /* If interactive, print the frame that is now current. */ + + if (from_tty) + frame_command ("0", 1); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_stack () +{ +#if 0 + backtrace_limit = 30; +#endif + + add_com ("return", class_stack, return_command, + "Make selected stack frame return to its caller.\n\ +Control remains in the debugger, but when you continue\n\ +execution will resume in the frame above the one now selected.\n\ +If an argument is given, it is an expression for the value to return."); + + add_com ("up", class_stack, up_command, + "Select and print stack frame that called this one.\n\ +An argument says how many frames up to go."); + + add_com ("down", class_stack, down_command, + "Select and print stack frame called by this one.\n\ +An argument says how many frames down to go."); + add_com_alias ("do", "down", class_stack, 1); + + add_com ("frame", class_stack, frame_command, + "Select and print a stack frame.\n\ +With no argument, print the selected stack frame. (See also \"info frame\").\n\ +An argument specifies the frame to select.\n\ +It can be a stack frame number or the address of the frame.\n\ +With argument, nothing is printed if input is coming from\n\ +a command file or a user-defined command."); + + add_com_alias ("f", "frame", class_stack, 1); + + add_com ("backtrace", class_stack, backtrace_command, + "Print backtrace of all stack frames, or innermost COUNT frames.\n\ +With a negative argument, print outermost -COUNT frames."); + add_com_alias ("bt", "backtrace", class_stack, 0); + add_com_alias ("where", "backtrace", class_alias, 0); + add_info ("stack", backtrace_command, + "Backtrace of the stack, or innermost COUNT frames."); + add_info_alias ("s", "stack", 1); + add_info ("frame", frame_info, + "All about selected stack frame, or frame at ADDR."); + add_info_alias ("f", "frame", 1); + add_info ("locals", locals_info, + "Local variables of current stack frame."); + add_info ("args", args_info, + "Argument variables of current stack frame."); + +#if 0 + add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command, + "Specify maximum number of frames for \"backtrace\" to print by default.", + &setlist); + add_info ("backtrace-limit", backtrace_limit_info, + "The maximum number of frames for \"backtrace\" to print by default."); +#endif +} + +@ + + +1.1 +log +@Initial revision +@ +text +@d2 1 +a2 1 + Copyright (C) 1986, 1987 Free Software Foundation, Inc. +d27 2 +d42 3 +d64 4 +d76 2 +d162 1 +a162 1 +void flush_cached_frames (); +d212 2 +d392 3 +d489 3 +a491 1 + register struct block *block = get_frame_block (frame); +d510 2 +d520 1 +a520 1 + struct symbol *func = get_frame_function (frame); +d526 1 +d551 2 +d610 2 +@ diff --git a/gdb/RCS/utils.c,v b/gdb/RCS/utils.c,v new file mode 100644 index 0000000..1d8000d --- /dev/null +++ b/gdb/RCS/utils.c,v @@ -0,0 +1,726 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.03.27.20.22.34; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.03.23.14.27.48; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@Portability changes. If USG, we need to re-enable the SIGINT +signal handler when it is called. Also, build the sys_siglist +table at runtime, based on the actual values of the signal +#define's. Too many USG systems added Berkeley signal names +as various numbers. +@ +text +@/* General utility routines for GDB, the GNU debugger. + Copyright (C) 1986 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include "defs.h" +#include "param.h" +#ifdef HAVE_TERMIO +#include <termio.h> +#endif + +void error (); +void fatal (); + +/* Chain of cleanup actions established with make_cleanup, + to be executed if an error happens. */ + +static struct cleanup *cleanup_chain; + +/* Nonzero means a quit has been requested. */ + +int quit_flag; + +/* Nonzero means quit immediately if Control-C is typed now, + rather than waiting until QUIT is executed. */ + +int immediate_quit; + +/* Add a new cleanup to the cleanup_chain, + and return the previous chain pointer + to be passed later to do_cleanups or discard_cleanups. + Args are FUNCTION to clean up with, and ARG to pass to it. */ + +struct cleanup * +make_cleanup (function, arg) + void (*function) (); + int arg; +{ + register struct cleanup *new + = (struct cleanup *) xmalloc (sizeof (struct cleanup)); + register struct cleanup *old_chain = cleanup_chain; + + new->next = cleanup_chain; + new->function = function; + new->arg = arg; + cleanup_chain = new; + + return old_chain; +} + +/* Discard cleanups and do the actions they describe + until we get back to the point OLD_CHAIN in the cleanup_chain. */ + +void +do_cleanups (old_chain) + register struct cleanup *old_chain; +{ + register struct cleanup *ptr; + while ((ptr = cleanup_chain) != old_chain) + { + (*ptr->function) (ptr->arg); + cleanup_chain = ptr->next; + free (ptr); + } +} + +/* Discard cleanups, not doing the actions they describe, + until we get back to the point OLD_CHAIN in the cleanup_chain. */ + +void +discard_cleanups (old_chain) + register struct cleanup *old_chain; +{ + register struct cleanup *ptr; + while ((ptr = cleanup_chain) != old_chain) + { + cleanup_chain = ptr->next; + free (ptr); + } +} + +/* Set the cleanup_chain to 0, and return the old cleanup chain. */ +struct cleanup * +save_cleanups () +{ + struct cleanup *old_chain = cleanup_chain; + + cleanup_chain = 0; + return old_chain; +} + +/* Restore the cleanup chain from a previously saved chain. */ +void +restore_cleanups (chain) + struct cleanup *chain; +{ + cleanup_chain = chain; +} + +/* This function is useful for cleanups. + Do + + foo = xmalloc (...); + old_chain = make_cleanup (free_current_contents, &foo); + + to arrange to free the object thus allocated. */ + +void +free_current_contents (location) + char **location; +{ + free (*location); +} + +/* Generally useful subroutines used throughout the program. */ + +/* Like malloc but get error if no storage available. */ + +char * +xmalloc (size) + long size; +{ + register char *val = (char *) malloc (size); + if (!val) + fatal ("virtual memory exhausted.", 0); + return val; +} + +/* Like realloc but get error if no storage available. */ + +char * +xrealloc (ptr, size) + char *ptr; + long size; +{ + register char *val = (char *) realloc (ptr, size); + if (!val) + fatal ("virtual memory exhausted.", 0); + return val; +} + +/* Print the system error message for errno, and also mention STRING + as the file name for which the error was encountered. + Then return to command level. */ + +void +perror_with_name (string) + char *string; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + char *err; + char *combined; + + if (errno < sys_nerr) + err = sys_errlist[errno]; + else + err = "unknown error"; + + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + + error ("%s.", combined); +} + +/* Print the system error message for ERRCODE, and also mention STRING + as the file name for which the error was encountered. */ + +void +print_sys_errmsg (string, errcode) + char *string; + int errcode; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + char *err; + char *combined; + + if (errcode < sys_nerr) + err = sys_errlist[errcode]; + else + err = "unknown error"; + + combined = (char *) alloca (strlen (err) + strlen (string) + 3); + strcpy (combined, string); + strcat (combined, ": "); + strcat (combined, err); + + printf ("%s.\n", combined); +} + +void +quit () +{ + fflush (stdout); +#ifdef HAVE_TERMIO + ioctl (fileno (stdout), TCFLSH, 1); +#else /* not HAVE_TERMIO */ + ioctl (fileno (stdout), TIOCFLUSH, 0); +#endif /* not HAVE_TERMIO */ + +#ifdef TIOCGPGRP + error ("Quit"); +#else + error ("Quit (expect signal %d when inferior is resumed)", SIGINT); +#endif /* TIOCGPGRP */ +} + +/* Control C comes here */ + +void +request_quit () +{ + quit_flag = 1; + +#ifdef USG + /* Restore the signal handler */ + signal(SIGINT, request_quit); +#endif + + if (immediate_quit) + quit (); +} + +/* Print an error message and return to command level. + STRING is the error message, used as a fprintf string, + and ARG is passed as an argument to it. */ + +void +error (string, arg1, arg2, arg3) + char *string; + int arg1, arg2, arg3; +{ + fflush (stdout); + fprintf (stderr, string, arg1, arg2, arg3); + fprintf (stderr, "\n"); + return_to_top_level (); +} + +/* Print an error message and exit reporting failure. + This is for a error that we cannot continue from. + STRING and ARG are passed to fprintf. */ + +void +fatal (string, arg) + char *string; + int arg; +{ + fprintf (stderr, "gdb: "); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Make a copy of the string at PTR with SIZE characters + (and add a null character at the end in the copy). + Uses malloc to get the space. Returns the address of the copy. */ + +char * +savestring (ptr, size) + char *ptr; + int size; +{ + register char *p = (char *) xmalloc (size + 1); + bcopy (ptr, p, size); + p[size] = 0; + return p; +} + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1; + register char *val = (char *) xmalloc (len); + strcpy (val, s1); + strcat (val, s2); + strcat (val, s3); + return val; +} + +void +print_spaces (n, file) + register int n; + register FILE *file; +{ + while (n-- > 0) + fputc (' ', file); +} + +/* Ask user a y-or-n question and return 1 iff answer is yes. + Takes three args which are given to printf to print the question. + The first, a control string, should end in "? ". + It should not say how to answer, because we do that. */ + +int +query (ctlstr, arg1, arg2) + char *ctlstr; +{ + register int answer; + + /* Automatically answer "yes" if input is not from a terminal. */ + if (!input_from_terminal_p ()) + return 1; + + while (1) + { + printf (ctlstr, arg1, arg2); + printf ("(y or n) "); + fflush (stdout); + answer = fgetc (stdin); + clearerr (stdin); /* in case of C-d */ + if (answer != '\n') + while (fgetc (stdin) != '\n') clearerr (stdin); + if (answer >= 'a') + answer -= 040; + if (answer == 'Y') + return 1; + if (answer == 'N') + return 0; + printf ("Please answer y or n.\n"); + } +} + +/* Parse a C escape sequence. STRING_PTR points to a variable + containing a pointer to the string to parse. That pointer + is updated past the characters we use. The value of the + escape sequence is returned. + + A negative value means the sequence \ newline was seen, + which is supposed to be equivalent to nothing at all. + + If \ is followed by a null character, we return a negative + value and leave the string pointer pointing at the null character. + + If \ is followed by 000, we return 0 and leave the string pointer + after the zeros. A value of 0 does not mean end of string. */ + +int +parse_escape (string_ptr) + char **string_ptr; +{ + register int c = *(*string_ptr)++; + switch (c) + { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'e': + return 033; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + case '\n': + return -2; + case 0: + (*string_ptr)--; + return 0; + case '^': + c = *(*string_ptr)++; + if (c == '\\') + c = parse_escape (string_ptr); + if (c == '?') + return 0177; + return (c & 0200) | (c & 037); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + register int i = c - '0'; + register int count = 0; + while (++count < 3) + { + if ((c = *(*string_ptr)++) >= '0' && c <= '7') + { + i *= 8; + i += c - '0'; + } + else + { + (*string_ptr)--; + break; + } + } + return i; + } + default: + return c; + } +} + +/* Print the character CH on STREAM as part of the contents + of a literal string whose delimiter is QUOTER. */ + +void +printchar (ch, stream, quoter) + unsigned char ch; + FILE *stream; + int quoter; +{ + register int c = ch; + if (c < 040 || c >= 0177) + { + if (c == '\n') + fprintf (stream, "\\n"); + else if (c == '\b') + fprintf (stream, "\\b"); + else if (c == '\t') + fprintf (stream, "\\t"); + else if (c == '\f') + fprintf (stream, "\\f"); + else if (c == '\r') + fprintf (stream, "\\r"); + else if (c == 033) + fprintf (stream, "\\e"); + else if (c == '\a') + fprintf (stream, "\\a"); + else + fprintf (stream, "\\%03o", c); + } + else + { + if (c == '\\' || c == quoter) + fputc ('\\', stream); + fputc (c, stream); + } +} + + +#ifdef USG +bcopy (from, to, count) +char *from, *to; +{ + memcpy (to, from, count); +} + +bcmp (from, to, count) +{ + return (memcmp (to, from, count)); +} + +bzero (to, count) +char *to; +{ + while (count--) + *to++ = 0; +} + +getwd (buf) +char *buf; +{ + getcwd (buf, MAXPATHLEN); +} + +char * +index (s, c) + char *s; +{ + char *strchr (); + return strchr (s, c); +} + +char * +rindex (s, c) + char *s; +{ + char *strrchr (); + return strrchr (s, c); +} + +/* Queue routines */ + +struct queue { + struct queue *forw; + struct queue *back; +}; + +insque (item, after) +struct queue *item; +struct queue *after; +{ + item->forw = after->forw; + after->forw->back = item; + + item->back = after; + after->forw = item; +} + +remque (item) +struct queue *item; +{ + item->forw->back = item->back; + item->back->forw = item->forw; +} + + +/* + * There is too much variation in Sys V signal numbers and names, so + * we must initialize them at runtime. If C provided a way to initialize + * an array based on subscript and value, this would not be necessary. + */ +static char undoc[] = "(undocumented)"; + +char *sys_siglist[NSIG]; + +_initialize_utils() +{ + int i; + + for (i = 0; i < NSIG; i++) + sys_siglist[i] = undoc; + +#ifdef SIGHUP + sys_siglist[SIGHUP ] = "SIGHUP"; +#endif +#ifdef SIGINT + sys_siglist[SIGINT ] = "SIGINT"; +#endif +#ifdef SIGQUIT + sys_siglist[SIGQUIT ] = "SIGQUIT"; +#endif +#ifdef SIGILL + sys_siglist[SIGILL ] = "SIGILL"; +#endif +#ifdef SIGTRAP + sys_siglist[SIGTRAP ] = "SIGTRAP"; +#endif +#ifdef SIGIOT + sys_siglist[SIGIOT ] = "SIGIOT"; +#endif +#ifdef SIGEMT + sys_siglist[SIGEMT ] = "SIGEMT"; +#endif +#ifdef SIGFPE + sys_siglist[SIGFPE ] = "SIGFPE"; +#endif +#ifdef SIGKILL + sys_siglist[SIGKILL ] = "SIGKILL"; +#endif +#ifdef SIGBUS + sys_siglist[SIGBUS ] = "SIGBUS"; +#endif +#ifdef SIGSEGV + sys_siglist[SIGSEGV ] = "SIGSEGV"; +#endif +#ifdef SIGSYS + sys_siglist[SIGSYS ] = "SIGSYS"; +#endif +#ifdef SIGPIPE + sys_siglist[SIGPIPE ] = "SIGPIPE"; +#endif +#ifdef SIGALRM + sys_siglist[SIGALRM ] = "SIGALRM"; +#endif +#ifdef SIGTERM + sys_siglist[SIGTERM ] = "SIGTERM"; +#endif +#ifdef SIGUSR1 + sys_siglist[SIGUSR1 ] = "SIGUSR1"; +#endif +#ifdef SIGUSR2 + sys_siglist[SIGUSR2 ] = "SIGUSR2"; +#endif +#ifdef SIGCLD + sys_siglist[SIGCLD ] = "SIGCLD"; +#endif +#ifdef SIGCHLD + sys_siglist[SIGCHLD ] = "SIGCHLD"; +#endif +#ifdef SIGPWR + sys_siglist[SIGPWR ] = "SIGPWR"; +#endif +#ifdef SIGTSTP + sys_siglist[SIGTSTP ] = "SIGTSTP"; +#endif +#ifdef SIGTTIN + sys_siglist[SIGTTIN ] = "SIGTTIN"; +#endif +#ifdef SIGTTOU + sys_siglist[SIGTTOU ] = "SIGTTOU"; +#endif +#ifdef SIGSTOP + sys_siglist[SIGSTOP ] = "SIGSTOP"; +#endif +#ifdef SIGXCPU + sys_siglist[SIGXCPU ] = "SIGXCPU"; +#endif +#ifdef SIGXFSZ + sys_siglist[SIGXFSZ ] = "SIGXFSZ"; +#endif +#ifdef SIGVTALRM + sys_siglist[SIGVTALRM ] = "SIGVTALRM"; +#endif +#ifdef SIGPROF + sys_siglist[SIGPROF ] = "SIGPROF"; +#endif +#ifdef SIGWINCH + sys_siglist[SIGWINCH ] = "SIGWINCH"; +#endif +#ifdef SIGCONT + sys_siglist[SIGCONT ] = "SIGCONT"; +#endif +#ifdef SIGURG + sys_siglist[SIGURG ] = "SIGURG"; +#endif +#ifdef SIGIO + sys_siglist[SIGIO ] = "SIGIO"; +#endif +#ifdef SIGWIND + sys_siglist[SIGWIND ] = "SIGWIND"; +#endif +#ifdef SIGPHONE + sys_siglist[SIGPHONE ] = "SIGPHONE"; +#endif +#ifdef SIGPOLL + sys_siglist[SIGPOLL ] = "SIGPOLL"; +#endif +} +#endif /* USG */ + +@ + + +1.1 +log +@Initial revision +@ +text +@d223 1 +d237 6 +a506 26 +char *sys_siglist[32] = { + "SIG0", + "SIGHUP", + "SIGINT", + "SIGQUIT", + "SIGILL", + "SIGTRAP", + "SIGIOT", + "SIGEMT", + "SIGFPE", + "SIGKILL", + "SIGBUS", + "SIGSEGV", + "SIGSYS", + "SIGPIPE", + "SIGALRM", + "SIGTERM", + "SIGUSR1", + "SIGUSR2", + "SIGCLD", + "SIGPWR", + "SIGWIND", + "SIGPHONE", + "SIGPOLL", +}; + +d530 124 +@ diff --git a/gdb/RCS/valprint.c,v b/gdb/RCS/valprint.c,v new file mode 100644 index 0000000..52d89b0 --- /dev/null +++ b/gdb/RCS/valprint.c,v @@ -0,0 +1,1117 @@ +head 1.3; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.3 +date 89.04.26.01.49.11; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 89.04.26.00.57.15; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.04.25.15.16.40; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.3 +log +@Fix spelling Nan => NaN +@ +text +@/* Print values for GNU debugger gdb. + Copyright (C) 1986, 1988 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" + +/* Maximum number of chars to print for a string pointer value + or vector contents. */ + +static int print_max; + +static void type_print_varspec_suffix (); +static void type_print_varspec_prefix (); +static void type_print_base (); +static void type_print_method_args (); + + +char **unsigned_type_table; +char **signed_type_table; +char **float_type_table; + +/* Print the value VAL in C-ish syntax on stream STREAM. + FORMAT is a format-letter, or 0 for print in natural format of data type. + If the object printed is a string pointer, returns + the number of string bytes printed. */ + +int +value_print (val, stream, format) + value val; + FILE *stream; + char format; +{ + register int i, n, typelen; + + /* A "repeated" value really contains several values in a row. + They are made by the @@ operator. + Print such values as if they were arrays. */ + + if (VALUE_REPEATED (val)) + { + n = VALUE_REPETITIONS (val); + typelen = TYPE_LENGTH (VALUE_TYPE (val)); + fputc ('{', stream); + /* Print arrays of characters using string syntax. */ + if (typelen == 1 && TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT + && format == 0) + { + fputc ('"', stream); + for (i = 0; i < n && i < print_max; i++) + { + QUIT; + printchar (VALUE_CONTENTS (val)[i], stream, '"'); + } + if (i < n) + fprintf (stream, "..."); + fputc ('"', stream); + } + else + { + for (i = 0; i < n && i < print_max; i++) + { + if (i) + fprintf (stream, ", "); + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val) + typelen * i, + VALUE_ADDRESS (val) + typelen * i, + stream, format, 1); + } + if (i < n) + fprintf (stream, "..."); + } + fputc ('}', stream); + return n * typelen; + } + else + { + /* If it is a pointer, indicate what it points to. + + Print type also if it is a reference. + + C++: if it is a member pointer, we will take care + of that when we print it. */ + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_PTR + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF) + { + fprintf (stream, "("); + type_print (VALUE_TYPE (val), "", stream, -1); + fprintf (stream, ") "); + } + return val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), + VALUE_ADDRESS (val), stream, format, 1); + } +} + +/* Print data of type TYPE located at VALADDR (within GDB), + which came from the inferior at address ADDRESS, + onto stdio stream STREAM according to FORMAT + (a letter or 0 for natural format). + + If the data are a string pointer, returns the number of + sting characters printed. + + if DEREF_REF is nonzero, then dereference references, + otherwise just print them like pointers. */ + +int +val_print (type, valaddr, address, stream, format, deref_ref) + struct type *type; + char *valaddr; + CORE_ADDR address; + FILE *stream; + char format; + int deref_ref; +{ + register int i; + int len, n_baseclasses; + struct type *elttype; + int eltlen; + LONGEST val; + unsigned char c; + + QUIT; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + if (TYPE_LENGTH (type) >= 0) + { + elttype = TYPE_TARGET_TYPE (type); + eltlen = TYPE_LENGTH (elttype); + len = TYPE_LENGTH (type) / eltlen; + fprintf (stream, "{"); + /* For an array of chars, print with string syntax. */ + if (eltlen == 1 && TYPE_CODE (elttype) == TYPE_CODE_INT + && format == 0) + { + fputc ('"', stream); + for (i = 0; i < len && i < print_max; i++) + { + QUIT; + printchar (valaddr[i], stream, '"'); + } + if (i < len) + fprintf (stream, "..."); + fputc ('"', stream); + } + else + { + for (i = 0; i < len && i < print_max; i++) + { + if (i) fprintf (stream, ", "); + val_print (elttype, valaddr + i * eltlen, + 0, stream, format, deref_ref); + } + if (i < len) + fprintf (stream, "..."); + } + fprintf (stream, "}"); + break; + } + /* Array of unspecified length: treat like pointer. */ + + case TYPE_CODE_PTR: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER) + { + struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type)); + struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)); + struct fn_field *f; + int j, len2; + char *kind = ""; + + val = unpack_long (builtin_type_int, valaddr); + if (TYPE_CODE (target) == TYPE_CODE_FUNC) + { + if (val < 128) + { + len = TYPE_NFN_FIELDS (domain); + for (i = 0; i < len; i++) + { + f = TYPE_FN_FIELDLIST1 (domain, i); + len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + + for (j = 0; j < len2; j++) + { + QUIT; + if (TYPE_FN_FIELD_VOFFSET (f, j) == val) + { + kind = "virtual"; + goto common; + } + } + } + } + else + { + struct symbol *sym = find_pc_function ((CORE_ADDR) val); + if (sym == 0) + error ("invalid pointer to member function"); + len = TYPE_NFN_FIELDS (domain); + for (i = 0; i < len; i++) + { + f = TYPE_FN_FIELDLIST1 (domain, i); + len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i); + + for (j = 0; j < len2; j++) + { + QUIT; + if (!strcmp (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j))) + goto common; + } + } + } + common: + if (i < len) + { + fputc ('&', stream); + type_print_varspec_prefix (TYPE_FN_FIELD_TYPE (f, j), stream, 0, 0); + fprintf (stream, kind); + if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_' + && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$') + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j) + 1, "~", + TYPE_FN_FIELDLIST_NAME (domain, i), stream); + else + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j), "", + TYPE_FN_FIELDLIST_NAME (domain, i), stream); + break; + } + } + else + { + /* VAL is a byte offset into the structure type DOMAIN. + Find the name of the field for that offset and + print it. */ + int extra = 0; + int bits = 0; + len = TYPE_NFIELDS (domain); + val <<= 3; /* @@@@ Make VAL into bit offset */ + for (i = 0; i < len; i++) + { + int bitpos = TYPE_FIELD_BITPOS (domain, i); + QUIT; + if (val == bitpos) + break; + if (val < bitpos && i > 0) + { + int ptrsize = (TYPE_LENGTH (builtin_type_char) * TYPE_LENGTH (target)); + /* Somehow pointing into a field. */ + i -= 1; + extra = (val - TYPE_FIELD_BITPOS (domain, i)); + if (extra & 0x3) + bits = 1; + else + extra >>= 3; + break; + } + } + if (i < len) + { + fputc ('&', stream); + type_print_base (domain, stream, 0, 0); + fprintf (stream, "::"); + fprintf (stream, "%s", TYPE_FIELD_NAME (domain, i)); + if (extra) + fprintf (stream, " + %d bytes", extra); + if (bits) + fprintf (stream, " (offset in bits)"); + break; + } + } + fputc ('(', stream); + type_print (type, "", stream, -1); + fprintf (stream, ") %d", val >> 3); + } + else + { + fprintf (stream, "0x%x", * (int *) valaddr); + /* For a pointer to char or unsigned char, + also print the string pointed to, unless pointer is null. */ + + /* For an array of chars, print with string syntax. */ + elttype = TYPE_TARGET_TYPE (type); + i = 0; /* Number of characters printed. */ + if (TYPE_LENGTH (elttype) == 1 + && TYPE_CODE (elttype) == TYPE_CODE_INT + && format == 0 + /* Convex needs this typecast to a long */ + && (long) unpack_long (type, valaddr) != 0 + && print_max) + { + fputc (' ', stream); + + /* Get first character. */ + if (read_memory ( (CORE_ADDR) unpack_long (type, valaddr), + &c, 1)) + { + /* First address out of bounds. */ + fprintf (stream, "<Address 0x%x out of bounds>", + (* (int *) valaddr)); + break; + } + else + { + /* A real string. */ + int out_of_bounds = 0; + + fputc ('"', stream); + while (c) + { + QUIT; + printchar (c, stream, '"'); + if (++i >= print_max) + break; + if (read_memory ((CORE_ADDR) unpack_long (type, valaddr) + + i, &c, 1)) + { + /* This address was out of bounds. */ + fprintf (stream, + "\"*** <Address 0x%x out of bounds>", + (* (int *) valaddr) + i); + out_of_bounds = 1; + break; + } + } + if (!out_of_bounds) + { + fputc ('"', stream); + if (i == print_max) + fprintf (stream, "..."); + } + } + fflush (stream); + } + /* Return number of characters printed, plus one for the + terminating null if we have "reached the end". */ + return i + (print_max && i != print_max); + } + break; + + case TYPE_CODE_MEMBER: + error ("not implemented: member type in val_print"); + break; + + case TYPE_CODE_REF: + fprintf (stream, "(0x%x &) = ", * (int *) valaddr); + /* De-reference the reference. */ + if (deref_ref) + { + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF) + { + value val = value_at (TYPE_TARGET_TYPE (type), * (int *) valaddr); + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), + VALUE_ADDRESS (val), stream, format, deref_ref); + } + else + fprintf (stream, "???"); + } + break; + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + fprintf (stream, "{"); + len = TYPE_NFIELDS (type); + n_baseclasses = TYPE_N_BASECLASSES (type); + for (i = 1; i <= n_baseclasses; i++) + { + fprintf (stream, "\n<%s> = ", TYPE_NAME (TYPE_BASECLASS (type, i))); + val_print (TYPE_FIELD_TYPE (type, 0), + valaddr + TYPE_FIELD_BITPOS (type, i-1) / 8, + 0, stream, 0, 0); + } + if (i > 1) fprintf (stream, "\nmembers of %s: ", TYPE_NAME (type)); + for (i -= 1; i < len; i++) + { + if (i > n_baseclasses) fprintf (stream, ", "); + fprintf (stream, "%s = ", TYPE_FIELD_NAME (type, i)); + /* check if static field */ + if (TYPE_FIELD_STATIC (type, i)) + { + value v; + + v = value_static_field (type, TYPE_FIELD_NAME (type, i), i); + val_print (TYPE_FIELD_TYPE (type, i), + VALUE_CONTENTS (v), 0, stream, format, deref_ref); + } + else if (TYPE_FIELD_PACKED (type, i)) + { + val = unpack_field_as_long (type, valaddr, i); + val_print (TYPE_FIELD_TYPE (type, i), &val, 0, + stream, format, deref_ref); + } + else + { + val_print (TYPE_FIELD_TYPE (type, i), + valaddr + TYPE_FIELD_BITPOS (type, i) / 8, + 0, stream, format, deref_ref); + } + } + fprintf (stream, "}"); + break; + + case TYPE_CODE_ENUM: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + len = TYPE_NFIELDS (type); + val = (long) unpack_long (builtin_type_int, valaddr); + for (i = 0; i < len; i++) + { + QUIT; + if (val == TYPE_FIELD_BITPOS (type, i)) + break; + } + if (i < len) + fprintf (stream, "%s", TYPE_FIELD_NAME (type, i)); + else + fprintf (stream, "%d", val); + break; + + case TYPE_CODE_FUNC: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + fprintf (stream, "{"); + type_print (type, "", stream, -1); + fprintf (stream, "} "); + fprintf (stream, "0x%x", address); + break; + + case TYPE_CODE_INT: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + fprintf (stream, + TYPE_UNSIGNED (type) ? "%u" : "%d", + unpack_long (type, valaddr)); + if (TYPE_LENGTH (type) == 1) + { + fprintf (stream, " '"); + printchar ((unsigned char) unpack_long (type, valaddr), + stream, '\''); + fputc ('\'', stream); + } + break; + + case TYPE_CODE_FLT: + if (format) + { + print_scalar_formatted (valaddr, type, format, 0, stream); + break; + } + /* FIXME: When printing NaNs or invalid floats, print them + in raw hex in addition to the message. */ +#ifdef IEEE_FLOAT + if (is_nan ((void *)valaddr, TYPE_LENGTH(type))) + { + fprintf (stream, "NaN"); + break; + } +#endif + { + double doub; + int inv; + + doub = unpack_double (type, valaddr, &inv); + if (inv) + fprintf (stream, "Invalid float value"); + else + fprintf (stream, TYPE_LENGTH (type) <= 4? "%.6g": "%.16g", doub); + } + break; + + case TYPE_CODE_VOID: + fprintf (stream, "void"); + break; + + default: + error ("Invalid type code in symbol table."); + } + fflush (stream); +} + +#ifdef IEEE_FLOAT + +/* Nonzero if ARG (a double) is a NAN. */ + +int +is_nan (fp, len) + void *fp; + int len; +{ + int lowhalf, highhalf; + union ieee { + long i[2]; /* ASSUMED 32 BITS */ + float f; /* ASSUMED 32 BITS */ + double d; /* ASSUMED 64 BITS */ + } *arg; + + arg = (union ieee *)fp; + + /* + * Single precision float. + */ + if (len == sizeof(long)) { + highhalf = arg->i[0]; + return ((((highhalf >> 23) & 0xFF) == 0xFF) + && 0 != (highhalf & 0x7FFFFF)); + } + + /* Separate the high and low words of the double. + Distinguish big and little-endian machines. */ +#ifdef WORDS_BIG_ENDIAN + lowhalf = arg->i[1], highhalf = arg->i[0]; +#else + lowhalf = arg->i[0], highhalf = arg->i[1]; +#endif + + /* Nan: exponent is the maximum possible, and fraction is nonzero. */ + return (((highhalf>>20) & 0x7ff) == 0x7ff + && + ! ((highhalf & 0xfffff == 0) && (lowhalf == 0))); +} +#endif + +/* Print a description of a type TYPE + in the form of a declaration of a variable named VARSTRING. + Output goes to STREAM (via stdio). + If SHOW is positive, we show the contents of the outermost level + of structure even if there is a type name that could be used instead. + If SHOW is negative, we never show the details of elements' types. */ + +void +type_print (type, varstring, stream, show) + struct type *type; + char *varstring; + FILE *stream; + int show; +{ + type_print_1 (type, varstring, stream, show, 0); +} + +/* LEVEL is the depth to indent lines by. */ + +void +type_print_1 (type, varstring, stream, show, level) + struct type *type; + char *varstring; + FILE *stream; + int show; + int level; +{ + register enum type_code code; + type_print_base (type, stream, show, level); + code = TYPE_CODE (type); + if ((varstring && *varstring) + || + /* Need a space if going to print stars or brackets; + but not if we will print just a type name. */ + ((show > 0 || TYPE_NAME (type) == 0) + && + (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC + || code == TYPE_CODE_ARRAY + || code == TYPE_CODE_MEMBER + || code == TYPE_CODE_REF))) + fprintf (stream, " "); + type_print_varspec_prefix (type, stream, show, 0); + fprintf (stream, "%s", varstring); + type_print_varspec_suffix (type, stream, show, 0); +} + +/* Print the method arguments ARGS to the file STREAM. */ +static void +type_print_method_args (args, prefix, varstring, stream) + struct type **args; + char *prefix, *varstring; + FILE *stream; +{ + int i; + + fprintf (stream, " %s%s (", prefix, varstring); + if (args[1] && args[1]->code != TYPE_CODE_VOID) + { + i = 1; /* skip the class variable */ + while (1) + { + type_print (args[i++], "", stream, 0); + if (!args[i]) + { + fprintf (stream, " ..."); + break; + } + else if (args[i]->code != TYPE_CODE_VOID) + { + fprintf (stream, ", "); + } + else break; + } + } + fprintf (stream, ")"); +} + +/* If TYPE is a derived type, then print out derivation + information. Print out all layers of the type heirarchy + until we encounter one with multiple inheritance. + At that point, print out that ply, and return. */ +static void +type_print_derivation_info (stream, type) + FILE *stream; + struct type *type; +{ + char *name; + int i, n_baseclasses = TYPE_N_BASECLASSES (type); + struct type *basetype = 0; + + while (type && n_baseclasses == 1) + { + basetype = TYPE_BASECLASS (type, 1); + if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype))) + { + while (*name != ' ') name++; + fprintf (stream, ": %s%s %s ", + TYPE_VIA_PUBLIC (basetype) ? "public" : "private", + TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "", + name + 1); + } + n_baseclasses = TYPE_N_BASECLASSES (basetype); + type = basetype; + } + + if (type) + { + if (n_baseclasses != 0) + fprintf (stream, ": "); + for (i = 1; i <= n_baseclasses; i++) + { + basetype = TYPE_BASECLASS (type, i); + if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype))) + { + while (*name != ' ') name++; + fprintf (stream, "%s%s %s", + TYPE_VIA_PUBLIC (basetype) ? "public" : "private", + TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "", + name + 1); + } + if (i < n_baseclasses) + fprintf (stream, ", "); + } + putc (' ', stream); + } +} + +/* Print any asterisks or open-parentheses needed before the + variable name (to describe its type). + + On outermost call, pass 0 for PASSED_A_PTR. + On outermost call, SHOW > 0 means should ignore + any typename for TYPE and show its details. + SHOW is always zero on recursive calls. */ + +static void +type_print_varspec_prefix (type, stream, show, passed_a_ptr) + struct type *type; + FILE *stream; + int show; + int passed_a_ptr; +{ + if (type == 0) + return; + + if (TYPE_NAME (type) && show <= 0) + return; + + QUIT; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_PTR: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); + fputc ('*', stream); + break; + + case TYPE_CODE_MEMBER: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + fputc (' ', stream); + type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0, + passed_a_ptr); + fprintf (stream, "::"); + break; + + case TYPE_CODE_REF: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); + fputc ('&', stream); + break; + + case TYPE_CODE_FUNC: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + if (passed_a_ptr) + fputc ('(', stream); + break; + + case TYPE_CODE_ARRAY: + type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + } +} + +/* Print any array sizes, function arguments or close parentheses + needed after the variable name (to describe its type). + Args work like type_print_varspec_prefix. */ + +static void +type_print_varspec_suffix (type, stream, show, passed_a_ptr) + struct type *type; + FILE *stream; + int show; + int passed_a_ptr; +{ + if (type == 0) + return; + + if (TYPE_NAME (type) && show <= 0) + return; + + QUIT; + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + fprintf (stream, "["); + if (TYPE_LENGTH (type) >= 0 + && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0) + fprintf (stream, "%d", + TYPE_LENGTH (type) / TYPE_LENGTH (TYPE_TARGET_TYPE (type))); + fprintf (stream, "]"); + break; + + case TYPE_CODE_MEMBER: + if (passed_a_ptr) + fputc (')', stream); + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0); + break; + + case TYPE_CODE_PTR: + case TYPE_CODE_REF: + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 1); + break; + + case TYPE_CODE_FUNC: + type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, + passed_a_ptr); + if (passed_a_ptr) + fprintf (stream, ")"); + fprintf (stream, "()"); + break; + } +} + +/* Print the name of the type (or the ultimate pointer target, + function value or array element), or the description of a + structure or union. + + SHOW nonzero means don't print this type as just its name; + show its real definition even if it has a name. + SHOW zero means print just typename or struct tag if there is one + SHOW negative means abbreviate structure elements. + SHOW is decremented for printing of structure elements. + + LEVEL is the depth to indent by. + We increase it for some recursive calls. */ + +static void +type_print_base (type, stream, show, level) + struct type *type; + FILE *stream; + int show; + int level; +{ + char *name; + register int i; + register int len; + register int lastval; + + QUIT; + + if (type == 0) + { + fprintf (stream, "type unknown"); + return; + } + + if (TYPE_NAME (type) && show <= 0) + { + fprintf (stream, TYPE_NAME (type)); + return; + } + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_ARRAY: + case TYPE_CODE_PTR: + case TYPE_CODE_MEMBER: + case TYPE_CODE_REF: + case TYPE_CODE_FUNC: + type_print_base (TYPE_TARGET_TYPE (type), stream, show, level); + break; + + case TYPE_CODE_STRUCT: + fprintf (stream, "struct "); + goto struct_union; + + case TYPE_CODE_UNION: + fprintf (stream, "union "); + struct_union: + if (TYPE_NAME (type) && (name = TYPE_NAME (type))) + { + while (*name != ' ') name++; + fprintf (stream, "%s ", name + 1); + } + if (show < 0) + fprintf (stream, "{...}"); + else + { + int i; + + type_print_derivation_info (stream, type); + + fprintf (stream, "{"); + len = TYPE_NFIELDS (type); + if (len) fprintf (stream, "\n"); + else fprintf (stream, "<no data fields>\n"); + + /* If there is a base class for this type, + do not print the field that it occupies. */ + for (i = TYPE_N_BASECLASSES (type); i < len; i++) + { + QUIT; + /* Don't print out virtual function table. */ + if (! strncmp (TYPE_FIELD_NAME (type, i), + "_vptr$", 6)) + continue; + + print_spaces (level + 4, stream); + if (TYPE_FIELD_STATIC (type, i)) + { + fprintf (stream, "static "); + } + type_print_1 (TYPE_FIELD_TYPE (type, i), + TYPE_FIELD_NAME (type, i), + stream, show - 1, level + 4); + if (!TYPE_FIELD_STATIC (type, i) + && TYPE_FIELD_PACKED (type, i)) + { + /* ??? don't know what to put here ??? */; + } + fprintf (stream, ";\n"); + } + + /* C++: print out the methods */ + len = TYPE_NFN_FIELDS (type); + if (len) fprintf (stream, "\n"); + for (i = 0; i < len; i++) + { + struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i); + + for (j = 0; j < len2; j++) + { + QUIT; + print_spaces (level + 4, stream); + if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) + fprintf (stream, "virtual "); + type_print (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j))), "", stream, 0); + if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_' + && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$') + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j) + 1, "~", + TYPE_FN_FIELDLIST_NAME (type, i), stream); + else + type_print_method_args + (TYPE_FN_FIELD_ARGS (f, j), "", + TYPE_FN_FIELDLIST_NAME (type, i), stream); + + fprintf (stream, ";\n"); + } + if (len2) fprintf (stream, "\n"); + } + + print_spaces (level, stream); + fputc ('}', stream); + } + break; + + case TYPE_CODE_ENUM: + fprintf (stream, "enum "); + if (TYPE_NAME (type)) + { + name = TYPE_NAME (type); + while (*name != ' ') name++; + fprintf (stream, "%s ", name + 1); + } + if (show < 0) + fprintf (stream, "{...}"); + else + { + fprintf (stream, "{"); + len = TYPE_NFIELDS (type); + lastval = 0; + for (i = 0; i < len; i++) + { + QUIT; + if (i) fprintf (stream, ", "); + fprintf (stream, "%s", TYPE_FIELD_NAME (type, i)); + if (lastval != TYPE_FIELD_BITPOS (type, i)) + { + fprintf (stream, " : %d", TYPE_FIELD_BITPOS (type, i)); + lastval = TYPE_FIELD_BITPOS (type, i); + } + lastval++; + } + fprintf (stream, "}"); + } + break; + + case TYPE_CODE_INT: + if (TYPE_UNSIGNED (type)) + name = unsigned_type_table[TYPE_LENGTH (type)]; + else + name = signed_type_table[TYPE_LENGTH (type)]; + fprintf (stream, "%s", name); + break; + + case TYPE_CODE_FLT: + name = float_type_table[TYPE_LENGTH (type)]; + fprintf (stream, "%s", name); + break; + + case TYPE_CODE_VOID: + fprintf (stream, "void"); + break; + + case 0: + fprintf (stream, "struct unknown"); + break; + + default: + error ("Invalid type code in symbol table."); + } +} + +static void +set_maximum_command (arg) + char *arg; +{ + if (!arg) error_no_arg ("value for maximum elements to print"); + print_max = atoi (arg); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_valprint () +{ + add_cmd ("array-max", class_vars, set_maximum_command, + "Set NUMBER as limit on string chars or array elements to print.", + &setlist); + + print_max = 200; + + unsigned_type_table + = (char **) xmalloc ((1 + sizeof (unsigned LONGEST)) * sizeof (char *)); + bzero (unsigned_type_table, (1 + sizeof (unsigned LONGEST))); + unsigned_type_table[sizeof (unsigned char)] = "unsigned char"; + unsigned_type_table[sizeof (unsigned short)] = "unsigned short"; + unsigned_type_table[sizeof (unsigned long)] = "unsigned long"; + unsigned_type_table[sizeof (unsigned int)] = "unsigned int"; +#ifdef LONG_LONG + unsigned_type_table[sizeof (unsigned long long)] = "unsigned long long"; +#endif + + signed_type_table + = (char **) xmalloc ((1 + sizeof (LONGEST)) * sizeof (char *)); + bzero (signed_type_table, (1 + sizeof (LONGEST))); + signed_type_table[sizeof (char)] = "char"; + signed_type_table[sizeof (short)] = "short"; + signed_type_table[sizeof (long)] = "long"; + signed_type_table[sizeof (int)] = "int"; +#ifdef LONG_LONG + signed_type_table[sizeof (long long)] = "long long"; +#endif + + float_type_table + = (char **) xmalloc ((1 + sizeof (double)) * sizeof (char *)); + bzero (float_type_table, (1 + sizeof (double))); + float_type_table[sizeof (float)] = "float"; + float_type_table[sizeof (double)] = "double"; +} + +@ + + +1.2 +log +@(1) Use XXX_BIG_ENDIAN macros rather than testing at runtime. +(2) Change args to is_nan, support floats, change one call to it. +(3) Change args to unpack_double. +@ +text +@d488 1 +a488 1 + fprintf (stream, "Nan"); +@ + + +1.1 +log +@Initial revision +@ +text +@d483 2 +d486 1 +a486 1 + if (is_nan (unpack_double (type, valaddr))) +d492 10 +a501 4 + if (TYPE_LENGTH (type) <= 4) + fprintf (stream, "%.6g", unpack_double (type, valaddr)); + else + fprintf (stream, "%.16g", unpack_double (type, valaddr)); +a515 5 +union ieee { + int i[2]; + double d; +}; + +d519 3 +a521 2 +is_nan (arg) + union ieee arg; +d524 16 +a539 1 + union { int i; char c; } test; +d543 5 +a547 6 + test.i = 1; + if (test.c != 1) + /* Big-endian machine */ + lowhalf = arg.i[1], highhalf = arg.i[0]; + else + lowhalf = arg.i[0], highhalf = arg.i[1]; +@ diff --git a/gdb/RCS/values.c,v b/gdb/RCS/values.c,v new file mode 100644 index 0000000..bc32f31 --- /dev/null +++ b/gdb/RCS/values.c,v @@ -0,0 +1,1047 @@ +head 1.2; +access ; +symbols ; +locks ; strict; +comment @ * @; + + +1.2 +date 89.04.26.01.05.45; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 89.04.25.15.38.44; author gnu; state Exp; +branches ; +next ; + + +desc +@@ + + +1.2 +log +@(1) use XXX_BIG_ENDIAN macros rather than runtime tests. +(2) Invalid values aren't stored in the value history, but they do +not cause an error; a -1 is returned as their value index. +(3) unpack_double takes a new arg, and callers check it. +@ +text +@ +/* Low level packing and unpacking of values for GDB. + Copyright (C) 1986, 1987 Free Software Foundation, Inc. + +GDB is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone +for the consequences of using it or for whether it serves any +particular purpose or works at all, unless he says so in writing. +Refer to the GDB General Public License for full details. + +Everyone is granted permission to copy, modify and redistribute GDB, +but only under the conditions described in the GDB General Public +License. A copy of this license is supposed to have been given to you +along with GDB so you can know your rights and responsibilities. It +should be in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. + +In other words, go ahead and share GDB, but don't try to stop +anyone else from sharing it farther. Help stamp out software hoarding! +*/ + +#include <stdio.h> +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "value.h" + +/* The value-history records all the values printed + by print commands during this session. Each chunk + records 60 consecutive values. The first chunk on + the chain records the most recent values. + The total number of values is in value_history_count. */ + +#define VALUE_HISTORY_CHUNK 60 + +struct value_history_chunk +{ + struct value_history_chunk *next; + value values[VALUE_HISTORY_CHUNK]; +}; + +/* Chain of chunks now in use. */ + +static struct value_history_chunk *value_history_chain; + +static int value_history_count; /* Abs number of last entry stored */ + + +/* List of all value objects currently allocated + (except for those released by calls to release_value) + This is so they can be freed after each command. */ + +static value all_values; + +/* Allocate a value that has the correct length for type TYPE. */ + +value +allocate_value (type) + struct type *type; +{ + register value val; + + val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type)); + VALUE_NEXT (val) = all_values; + all_values = val; + VALUE_TYPE (val) = type; + VALUE_LVAL (val) = not_lval; + VALUE_ADDRESS (val) = 0; + VALUE_FRAME (val) = 0; + VALUE_OFFSET (val) = 0; + VALUE_BITPOS (val) = 0; + VALUE_BITSIZE (val) = 0; + VALUE_REPEATED (val) = 0; + VALUE_REPETITIONS (val) = 0; + VALUE_REGNO (val) = -1; + return val; +} + +/* Allocate a value that has the correct length + for COUNT repetitions type TYPE. */ + +value +allocate_repeat_value (type, count) + struct type *type; + int count; +{ + register value val; + + val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count); + VALUE_NEXT (val) = all_values; + all_values = val; + VALUE_TYPE (val) = type; + VALUE_LVAL (val) = not_lval; + VALUE_ADDRESS (val) = 0; + VALUE_FRAME (val) = 0; + VALUE_OFFSET (val) = 0; + VALUE_BITPOS (val) = 0; + VALUE_BITSIZE (val) = 0; + VALUE_REPEATED (val) = 1; + VALUE_REPETITIONS (val) = count; + VALUE_REGNO (val) = -1; + return val; +} + +/* Free all the values that have been allocated (except for those released). + Called after each command, successful or not. */ + +void +free_all_values () +{ + register value val, next; + + for (val = all_values; val; val = next) + { + next = VALUE_NEXT (val); + free (val); + } + + all_values = 0; +} + +/* Remove VAL from the chain all_values + so it will not be freed automatically. */ + +void +release_value (val) + register value val; +{ + register value v; + + if (all_values == val) + { + all_values = val->next; + return; + } + + for (v = all_values; v; v = v->next) + { + if (v->next == val) + { + v->next = val->next; + break; + } + } +} + +/* Return a copy of the value ARG. + It contains the same contents, for same memory address, + but it's a different block of storage. */ + +static value +value_copy (arg) + value arg; +{ + register value val; + register struct type *type = VALUE_TYPE (arg); + if (VALUE_REPEATED (arg)) + val = allocate_repeat_value (type, VALUE_REPETITIONS (arg)); + else + val = allocate_value (type); + VALUE_LVAL (val) = VALUE_LVAL (arg); + VALUE_ADDRESS (val) = VALUE_ADDRESS (arg); + VALUE_OFFSET (val) = VALUE_OFFSET (arg); + VALUE_BITPOS (val) = VALUE_BITPOS (arg); + VALUE_BITSIZE (val) = VALUE_BITSIZE (arg); + VALUE_REGNO (val) = VALUE_REGNO (arg); + bcopy (VALUE_CONTENTS (arg), VALUE_CONTENTS (val), + TYPE_LENGTH (VALUE_TYPE (arg)) + * (VALUE_REPEATED (arg) ? VALUE_REPETITIONS (arg) : 1)); + return val; +} + +/* Access to the value history. */ + +/* Record a new value in the value history. + Returns the absolute history index of the entry. */ + +int +record_latest_value (val) + value val; +{ + int i; + double foo; + + /* Check error now if about to store an invalid float. We return -1 + to the caller, but allow them to continue, e.g. to print it as "Nan". */ + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT) { + foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &i); + if (i) return -1; /* Indicate value not saved in history */ + } + + /* Here we treat value_history_count as origin-zero + and applying to the value being stored now. */ + + i = value_history_count % VALUE_HISTORY_CHUNK; + if (i == 0) + { + register struct value_history_chunk *new + = (struct value_history_chunk *) xmalloc (sizeof (struct value_history_chunk)); + bzero (new->values, sizeof new->values); + new->next = value_history_chain; + value_history_chain = new; + } + + value_history_chain->values[i] = val; + release_value (val); + + /* Now we regard value_history_count as origin-one + and applying to the value just stored. */ + + return ++value_history_count; +} + +/* Return a copy of the value in the history with sequence number NUM. */ + +value +access_value_history (num) + int num; +{ + register struct value_history_chunk *chunk; + register int i; + register int absnum = num; + + if (absnum <= 0) + absnum += value_history_count; + + if (absnum <= 0) + { + if (num == 0) + error ("The history is empty."); + else if (num == 1) + error ("There is only one value in the history."); + else + error ("History does not go back to $$%d.", -num); + } + if (absnum > value_history_count) + error ("History has not yet reached $%d.", absnum); + + absnum--; + + /* Now absnum is always absolute and origin zero. */ + + chunk = value_history_chain; + for (i = (value_history_count - 1) / VALUE_HISTORY_CHUNK - absnum / VALUE_HISTORY_CHUNK; + i > 0; i--) + chunk = chunk->next; + + return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]); +} + +/* Clear the value history entirely. + Must be done when new symbol tables are loaded, + because the type pointers become invalid. */ + +void +clear_value_history () +{ + register struct value_history_chunk *next; + register int i; + register value val; + + while (value_history_chain) + { + for (i = 0; i < VALUE_HISTORY_CHUNK; i++) + if (val = value_history_chain->values[i]) + free (val); + next = value_history_chain->next; + free (value_history_chain); + value_history_chain = next; + } + value_history_count = 0; +} + +static void +history_info (num_exp) + char *num_exp; +{ + register int i; + register value val; + register int num; + + if (num_exp) + num = parse_and_eval_address (num_exp) - 5; + else + num = value_history_count - 9; + + if (num <= 0) + num = 1; + + for (i = num; i < num + 10 && i <= value_history_count; i++) + { + val = access_value_history (i); + printf ("$%d = ", i); + value_print (val, stdout, 0); + printf ("\n"); + } +} + +/* Internal variables. These are variables within the debugger + that hold values assigned by debugger commands. + The user refers to them with a '$' prefix + that does not appear in the variable names stored internally. */ + +static struct internalvar *internalvars; + +/* Look up an internal variable with name NAME. NAME should not + normally include a dollar sign. + + If the specified internal variable does not exist, + one is created, with a void value. */ + +struct internalvar * +lookup_internalvar (name) + char *name; +{ + register struct internalvar *var; + + for (var = internalvars; var; var = var->next) + if (!strcmp (var->name, name)) + return var; + + var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); + var->name = concat (name, "", ""); + var->value = allocate_value (builtin_type_void); + release_value (var->value); + var->next = internalvars; + internalvars = var; + return var; +} + +value +value_of_internalvar (var) + struct internalvar *var; +{ + register value val = value_copy (var->value); + VALUE_LVAL (val) = lval_internalvar; + VALUE_INTERNALVAR (val) = var; + return val; +} + +void +set_internalvar_component (var, offset, bitpos, bitsize, newval) + struct internalvar *var; + int offset, bitpos, bitsize; + value newval; +{ + register char *addr = VALUE_CONTENTS (var->value) + offset; + if (bitsize) + modify_field (addr, (int) value_as_long (newval), + bitpos, bitsize); + else + bcopy (VALUE_CONTENTS (newval), addr, + TYPE_LENGTH (VALUE_TYPE (newval))); +} + +void +set_internalvar (var, val) + struct internalvar *var; + value val; +{ + free (var->value); + var->value = value_copy (val); + release_value (var->value); +} + +char * +internalvar_name (var) + struct internalvar *var; +{ + return var->name; +} + +/* Free all internalvars. Done when new symtabs are loaded, + because that makes the values invalid. */ + +void +clear_internalvars () +{ + register struct internalvar *var; + + while (internalvars) + { + var = internalvars; + internalvars = var->next; + free (var->name); + free (var->value); + free (var); + } +} + +static void +convenience_info () +{ + register struct internalvar *var; + + if (internalvars) + printf ("Debugger convenience variables:\n\n"); + else + printf ("No debugger convenience variables now defined.\n\ +Convenience variables have names starting with \"$\";\n\ +use \"set\" as in \"set $foo = 5\" to define them.\n"); + + for (var = internalvars; var; var = var->next) + { + printf ("$%s: ", var->name); + value_print (var->value, stdout, 0); + printf ("\n"); + } +} + +/* Extract a value as a C number (either long or double). + Knows how to convert fixed values to double, or + floating values to long. + Does not deallocate the value. */ + +LONGEST +value_as_long (val) + register value val; +{ + return unpack_long (VALUE_TYPE (val), VALUE_CONTENTS (val)); +} + +double +value_as_double (val) + register value val; +{ + double foo; + int inv; + + foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &inv); + if (inv) + error ("Invalid floating value found in program."); + return foo; +} + +/* Unpack raw data (copied from debugee) at VALADDR + as a long, or as a double, assuming the raw data is described + by type TYPE. Knows how to convert different sizes of values + and can convert between fixed and floating point. + + C++: It is assumed that the front-end has taken care of + all matters concerning pointers to members. A pointer + to member which reaches here is considered to be equivalent + to an INT (or some size). After all, it is only an offset. */ + +LONGEST +unpack_long (type, valaddr) + struct type *type; + char *valaddr; +{ + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + register int nosign = TYPE_UNSIGNED (type); + + if (code == TYPE_CODE_ENUM) + code = TYPE_CODE_INT; + if (code == TYPE_CODE_FLT) + { + if (len == sizeof (float)) + return * (float *) valaddr; + + if (len == sizeof (double)) + return * (double *) valaddr; + } + else if (code == TYPE_CODE_INT && nosign) + { + if (len == sizeof (char)) + return * (unsigned char *) valaddr; + + if (len == sizeof (short)) + return * (unsigned short *) valaddr; + + if (len == sizeof (int)) + return * (unsigned int *) valaddr; + + if (len == sizeof (long)) + return * (unsigned long *) valaddr; + } + else if (code == TYPE_CODE_INT) + { + if (len == sizeof (char)) + return * (char *) valaddr; + + if (len == sizeof (short)) + return * (short *) valaddr; + + if (len == sizeof (int)) + return * (int *) valaddr; + + if (len == sizeof (long)) + return * (long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (long long *) valaddr; +#endif + } + else if (code == TYPE_CODE_PTR + || code == TYPE_CODE_REF) + { + if (len == sizeof (char *)) + return (CORE_ADDR) * (char **) valaddr; + } + else if (code == TYPE_CODE_MEMBER) + error ("not implemented: member types in unpack_long"); + + error ("Value not integer or pointer."); +} + +/* Return a double value from the specified type and address. + * INVP points to an int which is set to 0 for valid value, + * 1 for invalid value (bad float format). In either case, + * the returned double is OK to use. */ + +double +unpack_double (type, valaddr, invp) + struct type *type; + char *valaddr; + int *invp; +{ + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + register int nosign = TYPE_UNSIGNED (type); + + *invp = 0; /* Assume valid */ + if (code == TYPE_CODE_FLT) + { + if (INVALID_FLOAT (valaddr, len)) { + *invp = 1; + return 1.234567891011121314; + } + + if (len == sizeof (float)) + return * (float *) valaddr; + + if (len == sizeof (double)) + { + /* Some machines require doubleword alignment for doubles. + This code works on them, and on other machines. */ + double temp; + bcopy ((char *) valaddr, (char *) &temp, sizeof (double)); + return temp; + } + } + else if (code == TYPE_CODE_INT && nosign) + { + if (len == sizeof (char)) + return * (unsigned char *) valaddr; + + if (len == sizeof (short)) + return * (unsigned short *) valaddr; + + if (len == sizeof (int)) + return * (unsigned int *) valaddr; + + if (len == sizeof (long)) + return * (unsigned long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (unsigned long long *) valaddr; +#endif + } + else if (code == TYPE_CODE_INT) + { + if (len == sizeof (char)) + return * (char *) valaddr; + + if (len == sizeof (short)) + return * (short *) valaddr; + + if (len == sizeof (int)) + return * (int *) valaddr; + + if (len == sizeof (long)) + return * (long *) valaddr; + +#ifdef LONG_LONG + if (len == sizeof (long long)) + return * (long long *) valaddr; +#endif + } + + error ("Value not floating number."); +} + +/* Given a value ARG1 of a struct or union type, + extract and return the value of one of its fields. + FIELDNO says which field. + + For C++, must also be able to return values from static fields */ + +value +value_field (arg1, fieldno) + register value arg1; + register int fieldno; +{ + register value v; + register struct type *type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno); + register int offset; + + /* Handle packed fields */ + + offset = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) / 8; + if (TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno)) + { + v = value_from_long (type, + (LONGEST) unpack_field_as_long (VALUE_TYPE (arg1), + VALUE_CONTENTS (arg1), + fieldno)); + VALUE_BITPOS (v) = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) % 8; + VALUE_BITSIZE (v) = TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno); + } + else + { + v = allocate_value (type); + bcopy (VALUE_CONTENTS (arg1) + offset, + VALUE_CONTENTS (v), + TYPE_LENGTH (type)); + } + VALUE_LVAL (v) = VALUE_LVAL (arg1); + if (VALUE_LVAL (arg1) == lval_internalvar) + VALUE_LVAL (v) = lval_internalvar_component; + VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1); + VALUE_OFFSET (v) = offset + VALUE_OFFSET (arg1); + return v; +} + +value +value_fn_field (arg1, fieldno, subfieldno) + register value arg1; + register int fieldno; +{ + register value v; + struct fn_field *f = TYPE_FN_FIELDLIST1 (VALUE_TYPE (arg1), fieldno); + register struct type *type = TYPE_FN_FIELD_TYPE (f, subfieldno); + struct symbol *sym; + + sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, subfieldno), + 0, VAR_NAMESPACE, 0); + if (! sym) error ("Internal error: could not find physical method named %s", + TYPE_FN_FIELD_PHYSNAME (f, subfieldno)); + + v = allocate_value (type); + VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + VALUE_TYPE (v) = type; + return v; +} + +/* Return a virtual function as a value. + ARG1 is the object which provides the virtual function + table pointer. + F is the list of member functions which contains the desired virtual + function. + J is an index into F which provides the desired virtual function. + TYPE is the basetype which first provides the virtual function table. */ +value +value_virtual_fn_field (arg1, f, j, type) + value arg1; + struct fn_field *f; + int j; + struct type *type; +{ + /* First, get the virtual function table pointer. That comes + with a strange type, so cast it to type `pointer to long' (which + should serve just fine as a function type). Then, index into + the table, and convert final value to appropriate function type. */ + value vfn, vtbl; + value vi = value_from_long (builtin_type_int, + (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j)); + VALUE_TYPE (arg1) = TYPE_VPTR_BASETYPE (type); + + /* This type may have been defined before its virtual function table + was. If so, fill in the virtual function table entry for the + type now. */ + if (TYPE_VPTR_FIELDNO (type) < 0) + TYPE_VPTR_FIELDNO (type) + = fill_in_vptr_fieldno (type); + + /* Pretend that this array is just an array of pointers to integers. + This will have to change for multiple inheritance. */ + vtbl = value_copy (value_field (arg1, TYPE_VPTR_FIELDNO (type))); + VALUE_TYPE (vtbl) = lookup_pointer_type (builtin_type_int); + + /* Index into the virtual function table. */ + vfn = value_subscript (vtbl, vi); + + /* Reinstantiate the function pointer with the correct type. */ + VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j)); + return vfn; +} + +/* The value of a static class member does not depend + on its instance, only on its type. If FIELDNO >= 0, + then fieldno is a valid field number and is used directly. + Otherwise, FIELDNAME is the name of the field we are + searching for. If it is not a static field name, an + error is signaled. TYPE is the type in which we look for the + static field member. */ +value +value_static_field (type, fieldname, fieldno) + register struct type *type; + char *fieldname; + register int fieldno; +{ + register value v; + struct symbol *sym; + + if (fieldno < 0) + { + register struct type *t = type; + /* Look for static field. */ + while (t) + { + int i; + for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--) + if (! strcmp (TYPE_FIELD_NAME (t, i), fieldname)) + { + if (TYPE_FIELD_STATIC (t, i)) + { + fieldno = i; + goto found; + } + else + error ("field `%s' is not static"); + } + t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0; + } + + t = type; + + if (destructor_name_p (fieldname, t)) + error ("use `info method' command to print out value of destructor"); + + while (t) + { + int i, j; + + for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--) + { + if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), fieldname)) + { + error ("use `info method' command to print value of method \"%s\"", fieldname); + } + } + t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0; + } + error("there is no field named %s", fieldname); + } + + found: + + sym = lookup_symbol (TYPE_FIELD_STATIC_PHYSNAME (type, fieldno), + 0, VAR_NAMESPACE, 0); + if (! sym) error ("Internal error: could not find physical static variable named %s", TYPE_FIELD_BITSIZE (type, fieldno)); + + type = TYPE_FIELD_TYPE (type, fieldno); + v = value_at (type, (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym)); + return v; +} + +long +unpack_field_as_long (type, valaddr, fieldno) + struct type *type; + char *valaddr; + int fieldno; +{ + long val; + int bitpos = TYPE_FIELD_BITPOS (type, fieldno); + int bitsize = TYPE_FIELD_BITSIZE (type, fieldno); + + bcopy (valaddr + bitpos / 8, &val, sizeof val); + + /* Extracting bits depends on endianness of the target machine. */ +#ifdef BITS_BIG_ENDIAN + val = val >> (sizeof val * 8 - bitpos % 8 - bitsize); +#else + val = val >> (bitpos % 8); +#endif + + val &= (1 << bitsize) - 1; + return val; +} + +void +modify_field (addr, fieldval, bitpos, bitsize) + char *addr; + int fieldval; + int bitpos, bitsize; +{ + long oword; + + bcopy (addr, &oword, sizeof oword); + + /* Shifting for bit field depends on endianness of the target machine. */ +#ifdef BITS_BIG_ENDIAN + bitpos = sizeof oword * 8 - bitpos - bitsize; +#endif + + oword &= ~(((1 << bitsize) - 1) << bitpos); + oword |= fieldval << bitpos; + bcopy (&oword, addr, sizeof oword); +} + +/* Convert C numbers into newly allocated values */ + +value +value_from_long (type, num) + struct type *type; + register LONGEST num; +{ + register value val = allocate_value (type); + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + + if (code == TYPE_CODE_INT || code == TYPE_CODE_ENUM) + { + if (len == sizeof (char)) + * (char *) VALUE_CONTENTS (val) = num; + else if (len == sizeof (short)) + * (short *) VALUE_CONTENTS (val) = num; + else if (len == sizeof (int)) + * (int *) VALUE_CONTENTS (val) = num; + else if (len == sizeof (long)) + * (long *) VALUE_CONTENTS (val) = num; +#ifdef LONG_LONG + else if (len == sizeof (long long)) + * (long long *) VALUE_CONTENTS (val) = num; +#endif + else + error ("Integer type encountered with unexpected data length."); + } + else + error ("Unexpected type encountered for integer constant."); + + return val; +} + +value +value_from_double (type, num) + struct type *type; + double num; +{ + register value val = allocate_value (type); + register enum type_code code = TYPE_CODE (type); + register int len = TYPE_LENGTH (type); + + if (code == TYPE_CODE_FLT) + { + if (len == sizeof (float)) + * (float *) VALUE_CONTENTS (val) = num; + else if (len == sizeof (double)) + * (double *) VALUE_CONTENTS (val) = num; + else + error ("Floating type encountered with unexpected data length."); + } + else + error ("Unexpected type encountered for floating constant."); + + return val; +} + +/* Deal with the value that is "about to be returned". */ + +/* Return the value that a function returning now + would be returning to its caller, assuming its type is VALTYPE. + RETBUF is where we look for what ought to be the contents + of the registers (in raw form). This is because it is often + desirable to restore old values to those registers + after saving the contents of interest, and then call + this function using the saved values. + struct_return is non-zero when the function in question is + using the structure return conventions on the machine in question; + 0 when it is using the value returning conventions (this often + means returning pointer to where structure is vs. returning value). */ + +value +value_being_returned (valtype, retbuf, struct_return) + register struct type *valtype; + char retbuf[REGISTER_BYTES]; + int struct_return; +{ + register value val; + + if (struct_return) + return value_at (valtype, EXTRACT_STRUCT_VALUE_ADDRESS (retbuf)); + + val = allocate_value (valtype); + EXTRACT_RETURN_VALUE (valtype, retbuf, VALUE_CONTENTS (val)); + + return val; +} + +/* Return true if the function specified is using the structure returning + convention on this machine to return arguments, or 0 if it is using + the value returning convention. FUNCTION is the value representing + the function, FUNCADDR is the address of the function, and VALUE_TYPE + is the type returned by the function */ + +struct block *block_for_pc (); + +int +using_struct_return (function, funcaddr, value_type) + value function; + CORE_ADDR funcaddr; + struct type *value_type; +{ + register enum type_code code = TYPE_CODE (value_type); + + if (code == TYPE_CODE_STRUCT || + code == TYPE_CODE_ENUM || + code == TYPE_CODE_ARRAY) + { + struct block *b = block_for_pc (funcaddr); + + if (!(BLOCK_GCC_COMPILED (b) && TYPE_LENGTH (value_type) < 8)) + return 1; + } + + return 0; +} + +/* Store VAL so it will be returned if a function returns now. + Does not verify that VAL's type matches what the current + function wants to return. */ + +void +set_return_value (val) + value val; +{ + register enum type_code code = TYPE_CODE (VALUE_TYPE (val)); + char regbuf[REGISTER_BYTES]; + double dbuf; + LONGEST lbuf; + + if (code == TYPE_CODE_STRUCT + || code == TYPE_CODE_UNION) + error ("Specifying a struct or union return value is not supported."); + + if (code == TYPE_CODE_FLT) + { + dbuf = value_as_double (val); + + STORE_RETURN_VALUE (VALUE_TYPE (val), &dbuf); + } + else + { + lbuf = value_as_long (val); + STORE_RETURN_VALUE (VALUE_TYPE (val), &lbuf); + } +} + +void +_initialize_values () +{ + add_info ("convenience", convenience_info, + "Debugger convenience (\"$foo\") variables.\n\ +These variables are created when you assign them values;\n\ +thus, \"print $foo=1\" gives \"$foo\" the value 1. Values may be any type.\n\n\ +A few convenience variables are given values automatically GDB:\n\ +\"$_\"holds the last address examined with \"x\" or \"info lines\",\n\ +\"$__\" holds the contents of the last address examined with \"x\"."); + + add_info ("history", history_info, + "Elements of value history (around item number IDX, or last ten)."); +} + +@ + + +1.1 +log +@Initial revision +@ +text +@d182 2 +a183 1 + register int i; +d185 6 +a190 3 + /* Get error now if about to store an invalid float. */ + if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT) + value_as_double (val); +d427 7 +a433 1 + return unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val)); +d510 5 +d516 1 +a516 1 +unpack_double (type, valaddr) +d519 1 +d525 1 +d528 4 +a531 2 + if (INVALID_FLOAT (valaddr, len)) + error ("Invalid floating value found in program."); +a770 1 + union { int i; char c; } test; +d774 6 +a779 7 + /* Extracting bits depends on endianness of the machine. */ + test.i = 1; + if (test.c == 1) + /* Little-endian. */ + val = val >> (bitpos % 8); + else + val = val >> (sizeof val * 8 - bitpos % 8 - bitsize); +a791 1 + union { int i; char c; } test; +d795 4 +a798 5 + /* Shifting for bit field depends on endianness of the machine. */ + test.i = 1; + if (test.c != 1) + /* not little-endian: assume big-endian. */ + bitpos = sizeof oword * 8 - bitpos - bitsize; +@ |