diff options
Diffstat (limited to 'gdb/RCS')
-rw-r--r-- | gdb/RCS/Makefile,v | 160 | ||||
-rw-r--r-- | gdb/RCS/coffread.c,v | 304 | ||||
-rw-r--r-- | gdb/RCS/core.c,v | 763 | ||||
-rw-r--r-- | gdb/RCS/infcmd.c,v | 966 | ||||
-rw-r--r-- | gdb/RCS/inflow.c,v | 731 | ||||
-rw-r--r-- | gdb/RCS/m-mac-aux.h,v | 523 | ||||
-rw-r--r-- | gdb/RCS/m-mac-auxinit.h,v | 43 | ||||
-rw-r--r-- | gdb/RCS/m68k-pinsn.c,v | 828 | ||||
-rw-r--r-- | gdb/RCS/main.c,v | 1110 | ||||
-rw-r--r-- | gdb/RCS/source.c,v | 705 | ||||
-rw-r--r-- | gdb/RCS/symmisc.c,v | 575 | ||||
-rw-r--r-- | gdb/RCS/symtab.c,v | 1153 | ||||
-rw-r--r-- | gdb/RCS/utils.c,v | 461 |
13 files changed, 8322 insertions, 0 deletions
diff --git a/gdb/RCS/Makefile,v b/gdb/RCS/Makefile,v new file mode 100644 index 0000000..d942be0 --- /dev/null +++ b/gdb/RCS/Makefile,v @@ -0,0 +1,160 @@ +head 1.4; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @# @; + + +1.4 +date 88.06.08.23.14.28; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 88.02.28.03.38.17; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 88.01.26.05.14.43; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.05.14.07; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS's wheaties devl dirs +@ + + +1.4 +log +@Add -DEBUG +@ +text +@# -I. for "#include <obstack.h>" +CFLAGS = -g -I. -Dvfork=fork -DDEBUG +# NOTE!!! -O may FAIL TO WORK! See initialize.h for some weird hacks. + +# 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. +OBSTACK = obstack.o alloca.o -lPW +OBSTACK1 = obstack.o alloca.o + +STARTOBS = main.o firstfile.o + +OBS = 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 + +TSOBS = core.o inflow.o + +NTSOBS = standalone.o + +ENDOBS = lastfile.o command.o utils.o expread.o expprint.o pinsn.o \ + environ.o version.o + +TSSTART = /lib/crt0.o + +NTSSTART = kdb-start.o + +gdb : $(STARTOBS) $(OBS) $(TSOBS) $(ENDOBS) $(OBSTACK1) + $(CC) -o gdb $(STARTOBS) $(OBS) $(TSOBS) $(ENDOBS) -lg $(OBSTACK) + +xgdb : $(STARTOBS) $(OBS) xgdb.o $(TSOBS) $(ENDOBS) $(OBSTACK1) + $(CC) -o xgdb $(STARTOBS) $(OBS) xgdb.o $(TSOBS) $(ENDOBS) \ + -lXtk11 -lXrm -lX11 -lg $(OBSTACK) + +kdb : $(NTSSTART) $(STARTOBS) $(OBS) $(NTSOBS) $(ENDOBS) $(OBSTACK1) + ld -o kdb $(NTSSTART) $(STARTOBS) $(OBS) $(NTSOBS) $(ENDOBS) -lc -lg $(OBSTACK) + +clean: + rm -f $(STARTOBS) $(OBS) $(TSOBS) $(OBSTACK1) $(NTSSTART) $(NTSOBS) + rm -f xgdb.o gdb xgdb kdb tags errs expread.tab.c + +blockframe.o : blockframe.c defs.h initialize.h param.h symtab.h frame.h +breakpoint.o : breakpoint.c defs.h initialize.h param.h symtab.h frame.h +command.o : command.c command.h +coffread.o : coffread.c defs.h initialize.h param.h symtab.h +core.o : core.c defs.h initialize.h param.h +dbxread.o : dbxread.c defs.h initialize.h param.h symtab.h +environ.o : environ.c environ.h +expprint.o : expprint.c defs.h symtab.h expression.h +expread.tab.c : expread.y + @@echo 'Expect 96 shift/reduce conflicts.' + 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 +eval.o : eval.c defs.h initialize.h symtab.h value.h expression.h +findvar.o : findvar.c defs.h initialize.h param.h symtab.h frame.h value.h +firstfile.o : firstfile.c initialize.h +infcmd.o : infcmd.c defs.h initialize.h param.h symtab.h frame.h inferior.h environ.h value.h +inflow.o : inflow.c defs.h initialize.h param.h frame.h inferior.h +infrun.o : infrun.c defs.h initialize.h param.h symtab.h frame.h inferior.h wait.h +kdb-start.o : kdb-start.c defs.h param.h +lastfile.o : lastfile.c +main.o : main.c defs.h command.h +# pinsn.o depends on ALL the opcode printers +# since we don't know which one is really being used. +pinsn.o : pinsn.c defs.h param.h symtab.h \ + vax-opcode.h vax-pinsn.c m68k-opcode.h m68k-pinsn.c +printcmd.o : printcmd.c defs.h initialize.h param.h symtab.h value.h expression.h +source.o : source.c defs.h initialize.h symtab.h +stack.o : stack.c defs.h initialize.h param.h symtab.h frame.h +standalone.o : standalone.c defs.h initialize.h param.h symtab.h frame.h inferior.h wait.h +symmisc.o : symmisc.c defs.h initialize.h symtab.h +symtab.o : symtab.c defs.h initialize.h param.h symtab.h +utils.o : utils.c defs.h +valarith.o : valarith.c defs.h initialize.h param.h symtab.h value.h expression.h +valops.o : valops.c defs.h initialize.h param.h symtab.h value.h +valprint.o : valprint.c defs.h initialize.h symtab.h value.h +values.o : values.c defs.h initialize.h param.h symtab.h value.h +version.o : version.c +xgdb.o : xgdb.c defs.h initialize.h param.h symtab.h frame.h + $(CC) -c $(CFLAGS) xgdb.c -o $@@ + +obstack.o : obstack.c +@ + + +1.3 +log +@Make clean +@ +text +@d2 1 +a2 1 +CFLAGS = -g -I. -Dvfork=fork +@ + + +1.2 +log +@We don't have vfork or alloca, and regexp routines are in libPW.a for +no good reason. +@ +text +@d38 4 +@ + + +1.1 +log +@Initial revision +@ +text +@d2 1 +a2 1 +CFLAGS = -g -I. +d8 2 +a9 2 +OBSTACK = obstack.o +OBSTACK1 = obstack.o +@ diff --git a/gdb/RCS/coffread.c,v b/gdb/RCS/coffread.c,v new file mode 100644 index 0000000..7542030 --- /dev/null +++ b/gdb/RCS/coffread.c,v @@ -0,0 +1,304 @@ +head 1.4; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.4 +date 88.06.08.23.13.40; author gnu; state Exp; +branches ; +next 1.3; + +1.3 +date 88.02.28.03.37.53; author gnu; state Exp; +branches ; +next 1.2; + +1.2 +date 88.01.26.05.02.32; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.00.38.04; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS's work dirs on Wheaties +@ + + +1.4 +log +@Half reasonable reading of coff files. Problem was that it assumed +that a .text would show up sometime, and it never did. We have to close +out each source file's symtab as we hit the next one. +@ +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 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 "initialize.h" +#include "symtab.h" + +#include <a.out.h> +#include <stdio.h> +#include <obstack.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> + +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 (); + +START_FILE + +/* 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; + +int debug = 1; + + +/* 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) + { + 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]; +} + +/* 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 *) xmalloc (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
\ No newline at end of file diff --git a/gdb/RCS/core.c,v b/gdb/RCS/core.c,v new file mode 100644 index 0000000..99547f8 --- /dev/null +++ b/gdb/RCS/core.c,v @@ -0,0 +1,763 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.04.52; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.05.04.03; author gnu; state Exp; +branches ; +next ; + + +desc +@From RMS's development version on wheaties, 20Jan88 +@ + + +1.2 +log +@Hacks to get it to compile on a/ux. Needs work at finding the registers +in a core file. +@ +text +@/* Work with core dump and executable files, 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 "initialize.h" +#include "defs.h" +#include "param.h" + +#include <a.out.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <sys/file.h> +#include <sys/stat.h> + +/* Recognize COFF format systems because a.out.h defines AOUTHDR. */ +#ifdef AOUTHDR +#define COFF_FORMAT +#endif + +#ifdef NEW_SUN_CORE +#include <sys/core.h> +#else /* not NEW_SUN_CORE */ +#ifdef UMAX_CORE +#include <sys/ptrace.h> +#else /* not UMAX_CORE */ +#ifdef mac_aux +#include <sys/seg.h> +#include <sys/mmu.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/user.h> +#else +#include <sys/user.h> +#endif /* mac_aux */ +#endif /* UMAX_CORE */ +#endif /* NEW_SUN_CORE */ + +#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 + +START_FILE + +/* Hook for `exec_file_command' command to call. */ + +void (*exec_file_display_hook) (); + +/* File names of core file and executable file. */ + +static char *corefile; +static 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. */ + +static int corechan; +static 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. */ + +static CORE_ADDR data_start; +static CORE_ADDR data_end; +static CORE_ADDR stack_start; +static 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. */ + +static CORE_ADDR text_start; +static CORE_ADDR text_end; +static CORE_ADDR exec_data_start; +static CORE_ADDR exec_data_end; + +/* Address in executable file of start of text area data. */ + +static int text_offset; + +/* Address in executable file of start of data area data. */ + +static int exec_data_offset; + +/* Address in core file of start of data area data. */ + +static int data_offset; + +/* Address in core file of start of stack area data. */ + +static int stack_offset; + +#ifdef COFF_FORMAT +/* various coff data structures */ + +static FILHDR file_hdr; +static SCNHDR text_hdr; +static SCNHDR data_hdr; + +#endif /* not COFF_FORMAT */ + +/* a.out header saved in core file. */ + +static AOUTHDR core_aouthdr; + +/* a.out header of exec file. */ + +static AOUTHDR exec_aouthdr; + +static void validate_files (); +unsigned int register_addr (); + +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); +#ifdef NEW_SUN_CORE + { + 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)); + + data_start = exec_data_start; + 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; + + bcopy (&corestr.c_regs, registers, 16 * 4); + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = corestr.c_regs.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = corestr.c_regs.r_pc; + bcopy (corestr.c_fpstatus.fps_regs, + ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof corestr.c_fpstatus.fps_regs); + bcopy (&corestr.c_fpstatus.fps_control, + ®isters[REGISTER_BYTE (FPC_REGNUM)], + sizeof corestr.c_fpstatus - sizeof corestr.c_fpstatus.fps_regs); + + bcopy (&corestr.c_aouthdr, &core_aouthdr, sizeof (struct exec)); + + printf ("Core file is from \"%s\".\n", corestr.c_cmdname); + } +#else /* not NEW_SUN_CORE */ + /* 4.2-style (and perhaps also sysV-style) core dump file. */ + { +#ifdef UMAX_CORE + struct ptrace_user u; +#else + struct user u; +#endif + int reg_offset; + + val = myread (corechan, &u, sizeof u); + if (val < 0) + perror_with_name (filename); + data_start = exec_data_start; + +#ifdef UMAX_CORE + data_end = data_start + u.pt_dsize; + stack_start = stack_end - u.pt_ssize; + data_offset = sizeof u; + stack_offset = data_offset + u.pt_dsize; + reg_offset = 0; + + bcopy (&u.pt_aouthdr, &core_aouthdr, sizeof (AOUTHDR)); + +#else /* not UMAX_CORE */ +#ifdef mac_aux + /* This may well not work for 0407 (nonshared text) a.out's */ + data_end = data_start + u.u_dsize << PAGESHIFT; + stack_start = stack_end - u.u_ssize << PAGESHIFT; + data_offset = USIZE; + stack_offset = USIZE + u.u_dsize << PAGESHIFT; + reg_offset = (int) &u.u_ar0[0] - (int) &u; + + core_aouthdr.a_magic = u.u_exdata.ux_mag; +#else + 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); + reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR; + + /* I don't know where to find this info. + So, for now, mark it as not available. */ + core_aouthdr.a_magic = 0; +#endif /* not mac_aux */ +#endif /* not UMAX_CORE */ + + /* 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 (filename); + + val = myread (corechan, buf, sizeof buf); + if (val < 0) + perror_with_name (filename); + supply_register (regno, buf); + } + } + } +#endif /* not NEW_SUN_CORE */ + if (filename[0] == '/') + corefile = savestring (filename, strlen (filename)); + else + { + char dirname[MAXPATHLEN]; + + getwd (dirname); + corefile = concat (dirname, "/", filename); + } + + set_current_frame (read_register (FP_REGNUM)); + 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; + + val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); + + if (val < 0) + perror_with_name (filename); + + text_start = N_TXTADDR (exec_aouthdr); + text_end = text_start + exec_aouthdr.a_text; + text_offset = N_TXTOFF (exec_aouthdr); + exec_data_start = N_DATADDR (exec_aouthdr); + exec_data_end = exec_data_start + exec_aouthdr.a_data; + exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; + 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 ? filename : "No executable specified.\n"); +} + +/* Call this to specify the hook for exec_file_command to call back. + This is called from the x-window display code. */ + +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. */ + +close_exec_file () +{ + if (execchan >= 0) + close (execchan); + execchan = -1; +} + +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. */ + +static void +validate_files () +{ + if (execfile != 0 && corefile != 0) + { + struct stat st_core; + + fstat (corechan, &st_core); + + if (core_aouthdr.a_magic != 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"); + } +} + +char * +get_exec_file () +{ + if (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 == 0) + printf ("No core dump file\n"); + else + printf ("Core dump file \"%s\".\n", corefile); + + 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 loaded from \"%s\".\n", symfile); + + if (! have_inferior_p ()) + { + if (execfile) + { + printf ("Text segment from 0x%x to 0x%x.\n", + text_start, text_end); + } + if (corefile) + { + printf ("Data segment from 0x%x to 0x%x.\nStack segment from 0x%x to 0x%x.\n", + data_start, data_end, stack_start, stack_end); + } + else + { + printf ("Data segment in executable from 0x%x to 0x%x.\n", + exec_data_start, exec_data_end); + } + } +} + +/* Read "memory data" from core file and/or executable file */ + +read_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + if (have_inferior_p ()) + read_inferior_memory (memaddr, myaddr, len); + else + xfer_core_file (memaddr, myaddr, len, 0); +} + +/* 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."); +} + +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; + + 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. */ + + 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); + } + /* Note that if there is no core file + data_start and data_end are equal. */ + else 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; + } + + /* 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. */ + else + bzero (myaddr, i); + + memaddr += i; + myaddr += i; + len -= i; + } +} + +/* My replacement for the read system call. + Used like `read' but keeps going if `read' returns too soon. */ + +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; + } +} + +#ifndef NEW_SUN_CORE + +/* 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); + +#ifdef mac_aux +/* FIXME, we don't know where the regs are. Maybe the test command + * that tests what parts of the upage are writeable will find 'em for us. + */ +#define REGISTER_U_ADDR(addr, foo, bar) addr = 0; +#endif + REGISTER_U_ADDR (addr, blockend, regno); + + return addr; +} + +#endif /* not NEW_SUN_CORE */ + +static +initialize () +{ + 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."); +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d27 1 +d44 5 +d50 4 +a53 1 +#endif +d240 10 +d259 1 +d675 6 +@ diff --git a/gdb/RCS/infcmd.c,v b/gdb/RCS/infcmd.c,v new file mode 100644 index 0000000..cc30fe4 --- /dev/null +++ b/gdb/RCS/infcmd.c,v @@ -0,0 +1,966 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.06.19; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.01.19.05; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS's wheaties devl sources +@ + + +1.2 +log +@Add local sys_siglist for a/ux because they don't provide one, sigh. +@ +text +@/* Memory-access and commands for inferior process, 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 "defs.h" +#include "initialize.h" +#include "symtab.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "environ.h" +#include "value.h" + +#include <stdio.h> +#include <signal.h> +#include <sys/param.h> + +#ifdef mac_aux +/* Warning! This table is positional and highly dependent on the local + system. Check it closely against <sys/signal.h> when porting. */ +char *sys_siglist[] = { + "Signal 0", + "Hangup", + "Interrupt", + "Quit", + "Invalid instruction", + "Trace/breakpoint trap", + "IOT trap", + "EMT trap", + "Floating point exception", + "Killed", + "Bus error", + "Segmentation fault", + "Bad system call", + "Broken pipe", + "Alarm clock", + "Terminated", + "User signal 1", + "User signal 2", + "Child exited", + "Power-fail restart", + "Stopped", + "Stopped (tty input)", + "Stopped (tty output)", + "Stopped (signal)", + "Cputime limit exceeded", + "File size limit exceeded", + "Virtual timer expired", + "Profiling timer expired", + "Window changed", + "Continued", + "Urgent I/O condition", + "I/O possible", +}; +#else +/* More portable systems do it for you */ +extern char *sys_siglist[]; +#endif + +#define ERROR_NO_INFERIOR \ + if (inferior_pid == 0) error ("The program is not being run."); + +/* String containing arguments to give to the program, + with a space added at the front. Just a space means no args. */ + +static char *inferior_args; + +/* File name for default use for standard in/out in the inferior. */ + +char *inferior_io_terminal; + +/* Pid of our debugged inferior, or 0 if no inferior now. */ + +int inferior_pid; + +/* Last signal that the inferior received (why it stopped). */ + +int stop_signal; + +/* Address at which inferior stopped. */ + +CORE_ADDR stop_pc; + +/* Stack frame when program stopped. */ + +FRAME stop_frame; + +/* Number of breakpoint it stopped at, or 0 if none. */ + +int stop_breakpoint; + +/* Nonzero if stopped due to a step command. */ + +int stop_step; + +/* Nonzero if stopped due to completion of a stack dummy routine. */ + +int stop_stack_dummy; + +/* Range to single step within. + If this is nonzero, respond to a single-step signal + by continuing to step if the pc is in this range. */ + +CORE_ADDR step_range_start; /* Inclusive */ +CORE_ADDR step_range_end; /* Exclusive */ + +/* Stack frame address as of when stepping command was issued. + This is how we know when we step into a subroutine call, + and how to set the frame for the breakpoint used to step out. */ + +CORE_ADDR step_frame; + +/* 1 means step over all subroutine calls. + -1 means step over calls to undebuggable functions. */ + +int step_over_calls; + +/* If stepping, nonzero means step count is > 1 + so don't print frame next time inferior stops + if it stops due to stepping. */ + +int step_multi; + +/* Environment to use for running inferior, + in format described in environ.h. */ + +struct environ *inferior_environ; + +CORE_ADDR read_pc (); +struct command_line *get_breakpoint_commands (); + +START_FILE + +int +have_inferior_p () +{ + return inferior_pid != 0; +} + +static void +set_args_command (args) + char *args; +{ + free (inferior_args); + if (!args) args = ""; + inferior_args = concat (" ", args, ""); +} + +void +tty_command (file) + char *file; +{ + if (file == 0) + error_no_arg ("terminal name for running target process"); + + inferior_io_terminal = savestring (file, strlen (file)); +} + +static void +run_command (args, from_tty) + char *args; + int from_tty; +{ + extern char **environ; + register int i; + char *exec_file; + char *allargs; + + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + + dont_repeat (); + + if (inferior_pid) + { + if (query ("The program being debugged has been started already.\n\ +Start it from the beginning? ")) + kill_inferior (); + else + error ("Program already started."); + } + + if (args) + set_args_command (args); + + exec_file = (char *) get_exec_file (); + if (from_tty) + { + printf ("Starting program: %s%s\n", + exec_file, inferior_args); + fflush (stdout); + } + + allargs = concat ("exec ", exec_file, inferior_args); + inferior_pid = create_inferior (allargs, environ_vector (inferior_environ)); + + clear_proceed_status (); + + start_inferior (); +} + +void +cont_command (proc_count_exp, from_tty) + char *proc_count_exp; + int from_tty; +{ + ERROR_NO_INFERIOR; + + clear_proceed_status (); + + /* If have argument, set proceed count of breakpoint we stopped at. */ + + if (stop_breakpoint && proc_count_exp) + { + set_ignore_count (stop_breakpoint, + parse_and_eval_address (proc_count_exp) - 1, + from_tty); + if (from_tty) + printf (" "); + } + + if (from_tty) + printf ("Continuing.\n"); + + proceed (-1, -1, 0); +} + +/* Step until outside of current statement. */ +static void step_1 (); + +static void +step_command (count_string) +{ + step_1 (0, 0, count_string); +} + +/* Likewise, but skip over subroutine calls as if single instructions. */ + +static void +next_command (count_string) +{ + step_1 (1, 0, count_string); +} + +/* Likewise, but step only one instruction. */ + +static void +stepi_command (count_string) +{ + step_1 (0, 1, count_string); +} + +static void +nexti_command (count_string) +{ + step_1 (1, 1, count_string); +} + +static void +step_1 (skip_subroutines, single_inst, count_string) + int skip_subroutines; + int single_inst; + char *count_string; +{ + register int count = 1; + + ERROR_NO_INFERIOR; + count = count_string ? parse_and_eval_address (count_string) : 1; + + for (; count > 0; count--) + { + clear_proceed_status (); + + step_frame = get_current_frame (); + + if (! single_inst) + { + find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end); + if (step_range_end == 0) + { + terminal_ours (); + error ("Current function has no line number information."); + } + } + else + { + /* Say we are stepping, but stop after one insn whatever it does. + Don't step through subroutine calls even to undebuggable functions. */ + step_range_start = step_range_end = 1; + if (!skip_subroutines) + step_over_calls = 0; + } + + if (skip_subroutines) + step_over_calls = 1; + + step_multi = (count > 1); + proceed (-1, -1, 1); + if (! stop_step) + break; + } +} + +/* Continue program at specified address. */ + +static void +jump_command (arg, from_tty) + char *arg; + int from_tty; +{ + register CORE_ADDR addr; + struct symtab_and_line sal; + + ERROR_NO_INFERIOR; + + if (!arg) + error_no_arg ("starting address"); + + sal = decode_line_spec (arg, 1); + + if (sal.symtab == 0 && sal.pc == 0) + error ("No source file has been specified."); + + if (sal.pc == 0) + sal.pc = find_line_pc (sal.symtab, sal.line); + + { + struct symbol *fn = get_frame_function (get_current_frame ()); + struct symbol *sfn = find_pc_function (sal.pc); + if (fn != 0 && sfn != fn + && ! query ("That is not in function %s. Continue there? ", + sal.line, SYMBOL_NAME (fn))) + error ("Not confirmed."); + } + + if (sal.pc == 0) + error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename); + + addr = sal.pc; + + clear_proceed_status (); + + if (from_tty) + printf ("Continuing at 0x%x.\n", addr); + + proceed (addr, 0, 0); +} + +/* Continue program giving it specified signal. */ + +static void +signal_command (signum_exp, from_tty) + char *signum_exp; + int from_tty; +{ + register int signum; + + dont_repeat (); /* Too dangerous. */ + ERROR_NO_INFERIOR; + + if (!signum_exp) + error_no_arg ("signal number"); + + signum = parse_and_eval_address (signum_exp); + + clear_proceed_status (); + + if (from_tty) + printf ("Continuing with signal %d.\n", signum); + + proceed (stop_pc, signum, 0); +} + +/* Execute a "stack dummy", a piece of code stored in the stack + by the debugger to be executed in the inferior. + + To call: first, do PUSH_DUMMY_FRAME. + Then push the contents of the dummy. It should end with a breakpoint insn. + Then call here, passing address at which to start the dummy. + + The contents of all registers are saved before the dummy frame is popped + and copied into the buffer BUFFER. + + The dummy's frame is automatically popped whenever that break is hit. + If that is the first time the program stops, run_stack_dummy + returns to its caller with that frame already gone. + Otherwise, the caller never gets returned to. */ + +/* 4 => return instead of letting the stack dummy run. */ + +static int stack_dummy_testing = 0; + +void +run_stack_dummy (addr, buffer) + CORE_ADDR addr; + REGISTER_TYPE *buffer; +{ + int saved_pc_changed = pc_changed; + int saved_stop_signal = stop_signal; + int saved_stop_pc = stop_pc; + int saved_stop_frame = stop_frame; + int saved_stop_breakpoint = stop_breakpoint; + int saved_stop_step = stop_step; + int saved_stop_stack_dummy = stop_stack_dummy; + FRAME saved_selected_frame; + int saved_selected_level; + struct command_line *saved_breakpoint_commands + = get_breakpoint_commands (); + + record_selected_frame (&saved_selected_frame, &saved_selected_level); + + /* Now proceed, having reached the desired place. */ + clear_proceed_status (); + if (stack_dummy_testing & 4) + { + POP_FRAME; + return; + } + proceed (addr, 0, 0); + + if (!stop_stack_dummy) + error ("Cannot continue previously requested operation."); + + set_breakpoint_commands (saved_breakpoint_commands); + select_frame (saved_selected_frame, saved_selected_level); + stop_signal = saved_stop_signal; + stop_pc = saved_stop_pc; + stop_frame = saved_stop_frame; + stop_breakpoint = saved_stop_breakpoint; + stop_step = saved_stop_step; + stop_stack_dummy = saved_stop_stack_dummy; + pc_changed = saved_pc_changed; + + /* On return, the stack dummy has been popped already. */ + + bcopy (stop_registers, buffer, sizeof stop_registers); +} + +/* "finish": Set a temporary breakpoint at the place + the selected frame will return to, then continue. */ + +static void +finish_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtab_and_line sal; + register FRAME frame; + struct frame_info fi; + + register struct symbol *function; + + if (!have_inferior_p ()) + error ("The program is not being run."); + if (arg) + error ("The \"finish\" command does not take any arguments."); + + frame = get_prev_frame (selected_frame); + if (frame == 0) + error ("\"finish\" not meaningful in the outermost frame."); + + clear_proceed_status (); + + fi = get_frame_info (frame); + sal = find_pc_line (fi.pc, 0); + sal.pc = fi.pc; + set_momentary_breakpoint (sal, frame); + + /* Find the function we will return from. */ + + fi = get_frame_info (fi.next_frame); + function = find_pc_function (fi.pc); + + if (from_tty) + { + printf ("Run till exit from "); + print_selected_frame (); + } + + proceed (-1, -1, 0); + + if (stop_breakpoint == -3 && function != 0) + { + struct type *value_type; + register value val; + + if (TYPE_CODE (SYMBOL_TYPE (function)) != TYPE_CODE_VOID) + value_type = SYMBOL_TYPE (function); + else + return; + + val = value_being_returned (value_type, stop_registers); + printf ("Value returned is $%d = ", record_latest_value (val)); + value_print (val, stdout); + putchar ('\n'); + } +} + +static void +program_info () +{ + if (inferior_pid == 0) + { + printf ("The program being debugged is not being run.\n"); + return; + } + + printf ("Program being debugged is in process %d, stopped at 0x%x.\n", + inferior_pid, stop_pc); + if (stop_step) + printf ("It stopped after being stepped.\n"); + else if (stop_breakpoint) + printf ("It stopped at breakpoint %d.\n", stop_breakpoint); + else if (stop_signal) + printf ("It stopped with signal %d (%s).\n", + stop_signal, sys_siglist[stop_signal]); + + printf ("\nType \"info stack\" or \"info reg\" for more information.\n"); +} + +static void +environment_info (var) + char *var; +{ + if (var) + { + register char *val = get_in_environ (inferior_environ, var); + if (val) + printf ("%s = %s\n", var, val); + else + printf ("Environment variable \"%s\" not defined.\n", var); + } + else + { + register char **vector = environ_vector (inferior_environ); + while (*vector) + printf ("%s\n", *vector++); + } +} + +static void +set_environment_command (arg) + char *arg; +{ + register char *p, *val, *var; + + if (arg == 0) + error_no_arg ("environment variable and value"); + + p = (char *) index (arg, '='); + val = (char *) index (arg, ' '); + if (p != 0 && val != 0) + p = arg + min (p - arg, val - arg); + else if (val != 0 && p == 0) + p = val; + + if (p == 0) + error ("Space or \"=\" must separate variable name and its value"); + if (p[1] == 0) + error_no_arg ("value for the variable"); + if (p == arg) + error_no_arg ("environment variable to set"); + + val = p + 1; + while (*val == ' ' || *val == '\t') val++; + while (p != arg && (p[-1] == ' ' || p[-1] == '\t')) p--; + + var = savestring (arg, p - arg); + set_in_environ (inferior_environ, var, val); + free (var); +} + +static void +unset_environment_command (var) + char *var; +{ + if (var == 0) + error_no_arg ("environment variable"); + + unset_in_environ (inferior_environ, var); +} + +/* Read an integer from debugged memory, given address and number of bytes. */ + +read_memory_integer (memaddr, len) + CORE_ADDR memaddr; + int len; +{ + char cbuf; + short sbuf; + int ibuf; + long lbuf; + + if (len == sizeof (char)) + { + read_memory (memaddr, &cbuf, len); + return cbuf; + } + if (len == sizeof (short)) + { + read_memory (memaddr, &sbuf, len); + return sbuf; + } + if (len == sizeof (int)) + { + read_memory (memaddr, &ibuf, len); + return ibuf; + } + if (len == sizeof (lbuf)) + { + read_memory (memaddr, &lbuf, len); + return lbuf; + } + error ("Cannot handle integers of %d bytes.", len); +} + +CORE_ADDR +read_pc () +{ + return (CORE_ADDR) read_register (PC_REGNUM); +} + +write_pc (val) + CORE_ADDR val; +{ + write_register (PC_REGNUM, (long) val); +} + +char *reg_names[] = REGISTER_NAMES; + +static void +registers_info (addr_exp) + char *addr_exp; +{ + register int i; + int regnum; + + if (addr_exp) + { + if (*addr_exp >= '0' && *addr_exp <= '9') + regnum = atoi (addr_exp); + else + { + register char *p = addr_exp; + if (p[0] == '$') + p++; + for (regnum = 0; regnum < NUM_REGS; regnum++) + if (!strcmp (p, reg_names[regnum])) + break; + if (regnum == NUM_REGS) + error ("%s: invalid register name.", addr_exp); + } + } + else + printf ("Reg\tContents\n\n"); + + for (i = 0; i < NUM_REGS; i++) + { + unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE]; + unsigned char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + REGISTER_TYPE val; + + if (addr_exp != 0 && i != regnum) + continue; + + /* On machines with lots of registers, pause every 16 lines + so user can read the output. */ + if (addr_exp == 0 && i > 0 && i % 16 == 0) + { + printf ("--Type Return to print more--"); + fflush (stdout); + read_line (); + } + + /* Get the data in raw format, then convert also to virtual format. */ + read_relative_register_raw_bytes (i, raw_buffer); + REGISTER_CONVERT_TO_VIRTUAL (i, raw_buffer, virtual_buffer); + + printf ("%s\t", reg_names[i]); + + /* If virtual format is floating, print it that way. */ + if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT + && ! INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i))) + val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, stdout); + /* Else if virtual format is too long for printf, + print in hex a byte at a time. */ + else if (REGISTER_VIRTUAL_SIZE (i) > sizeof (long)) + { + register int j; + printf ("0x"); + for (j = 0; j < REGISTER_VIRTUAL_SIZE (i); j++) + printf ("%02x", virtual_buffer[j]); + } + /* Else print as integer in hex and in decimal. */ + else + { + long val; + + bcopy (virtual_buffer, &val, sizeof (long)); + if (val == 0) + printf ("0"); + else + printf ("0x%08x %d", val, val); + } + + /* If register has different raw and virtual formats, + print the raw format in hex now. */ + + if (REGISTER_CONVERTIBLE (i)) + { + register int j; + + printf (" (raw 0x"); + for (j = 0; j < REGISTER_RAW_SIZE (i); j++) + printf ("%02x", raw_buffer[j]); + printf (")"); + } + printf ("\n"); + } + + printf ("Contents are relative to selected stack frame.\n"); +} + +#ifdef ATTACH_DETACH +/* + * TODO: + * Should save/restore the tty state since it might be that the + * program to be debugged was started on this tty and it wants + * the tty in some state other than what we want. If it's running + * on another terminal or without a terminal, then saving and + * restoring the tty state is a harmless no-op. + */ + +/* + * attach_command -- + * takes a program started up outside of gdb and ``attaches'' to it. + * This stops it cold in it's tracks and allows us to start tracing + * it. For this to work, we must be able to send the process a + * signal and we must have the same effective uid as the program. + */ +static void +attach_command (args, from_tty) + char *args; + int from_tty; +{ + char *exec_file; + int pid; + + dont_repeat(); + + if (!args) + error_no_arg ("process-id to attach"); + else + pid = atoi (args); + + if (inferior_pid) + { + if (query ("A program is being debugged already. Kill it? ")) + kill_inferior (); + else + error ("Inferior not killed."); + } + + exec_file = (char *) get_exec_file (); + + if (from_tty) + { + printf ("Attaching program: %s pid %d\n", + exec_file, pid); + fflush (stdout); + } + + attach_program (pid); +} + +/* + * detach_command -- + * takes a program previously attached to and detaches it. + * The program resumes execution and will no longer stop + * on signals, etc. We better not have left any breakpoints + * in the program or it'll die when it hits one. For this + * to work, it may be necessary for the process to have been + * previously attached. It *might* work if the program was + * started via the normal ptrace (PTRACE_TRACEME). + */ + +static void +detach_command (args, from_tty) + char *args; + int from_tty; +{ + char *exec_file = (char *)get_exec_file (); + int signal = 0; + + if (!inferior_pid) + error ("Not currently tracing a program\n"); + if (from_tty) + { + printf ("Detaching program: %s pid %d\n", + exec_file, inferior_pid); + fflush (stdout); + } + if (args) + signal = atoi (args); + + detach (signal); + inferior_pid = 0; +} +#endif /* ATTACH_DETACH */ + +static +initialize () +{ + add_com ("tty", class_run, tty_command, + "Set terminal for future runs of program being debugged."); + + add_com ("set-args", class_run, set_args_command, + "Specify arguments to give program being debugged when it is started.\n\ +Follow this command with any number of args, to be passed to the program."); + + add_info ("environment", environment_info, + "The environment to give the program, or one variable's value.\n\ +With an argument VAR, prints the value of environment variable VAR to\n\ +give the program being debugged. With no arguments, prints the entire\n\ +environment to be given to the program."); + + add_com ("unset-environment", class_run, unset_environment_command, + "Cancel environment variable VAR for the program.\n\ +This does not affect the program until the next \"run\" command."); + add_com ("set-environment", class_run, set_environment_command, + "Set environment variable value to give the program.\n\ +Arguments are VAR VALUE where VAR is variable name and VALUE is value.\n\ +VALUES of environment variables are uninterpreted strings.\n\ +This does not affect the program until the next \"run\" command."); + +#ifdef ATTACH_DETACH + add_com ("attach", class_run, attach_command, + "Attach to a process that was started up outside of GDB.\n\ +To do this, you must have permission to send the process a signal.\n\ +And it must have the same effective uid as the debugger.\n\n\ +Before using \"attach\", you must use the \"exec-file\" command\n\ +to specify the program running in the process,\n\ +and the \"symbol-file\" command to load its symbol table."); + add_com ("detach", class_run, detach_command, + "Detach the process previously attached.\n\ +The process is no longer traced and continues its execution."); +#endif /* ATTACH_DETACH */ + + add_com ("signal", class_run, signal_command, + "Continue program giving it signal number SIGNUMBER."); + + add_com ("stepi", class_run, stepi_command, + "Step one instruction exactly.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("si", "stepi", class_alias, 0); + + add_com ("nexti", class_run, nexti_command, + "Step one instruction, but proceed through subroutine calls.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("ni", "nexti", class_alias, 0); + + add_com ("finish", class_run, finish_command, + "Execute until selected stack frame returns.\n\ +Upon return, the value returned is printed and put in the value history."); + + add_com ("next", class_run, next_command, + "Step program, proceeding through subroutine calls.\n\ +Like the \"step\" command as long as subroutine calls do not happen;\n\ +when they do, the call is treated as one instruction.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("n", "next", class_run, 1); + + add_com ("step", class_run, step_command, + "Step program until it reaches a different source line.\n\ +Argument N means do this N times (or till program stops for another reason)."); + add_com_alias ("s", "step", class_run, 1); + + add_com ("jump", class_run, jump_command, + "Continue program being debugged at specified line or address.\n\ +Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\ +for an address to start at."); + + add_com ("cont", class_run, cont_command, + "Continue program being debugged, after signal or breakpoint.\n\ +If proceeding from breakpoint, a number N may be used as an argument:\n\ +then the same breakpoint won't break until the Nth time it is reached."); + add_com_alias ("c", "cont", class_run, 1); + + add_com ("run", class_run, run_command, + "Start debugged program. You may specify arguments to give it.\n\ +Args may include \"*\", or \"[...]\"; they are expanded using \"sh\".\n\ +Input and output redirection with \">\", \"<\", or \">>\" are also allowed.\n\n\ +With no arguments, uses arguments last specified (with \"run\" or \"set-args\".\n\ +To cancel previous arguments and run with no arguments,\n\ +use \"set-args\" without arguments."); + add_com_alias ("r", "run", class_run, 1); + + add_info ("registers", registers_info, + "List of registers and their contents, for selected stack frame.\n\ +Register name as argument means describe only that register."); + + add_info ("program", program_info, + "Execution status of the program."); + + inferior_args = savestring (" ", 1); /* By default, no args. */ + inferior_environ = make_environ (); + init_environ (inferior_environ); +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d34 39 +d74 1 +@ diff --git a/gdb/RCS/inflow.c,v b/gdb/RCS/inflow.c,v new file mode 100644 index 0000000..83f44d9 --- /dev/null +++ b/gdb/RCS/inflow.c,v @@ -0,0 +1,731 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.07.38; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.05.04.57; author gnu; state Exp; +branches ; +next ; + + +desc +@From RMS's development sources on wheaties, 20Jan88 +@ + + +1.2 +log +@Major Sys V tty changes, and a few changes to try to find the registers +in the upage (untested yet). +@ +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 "initialize.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> + +#ifdef mac_aux +#include <sys/seg.h> +#include <sys/mmu.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/user.h> +#else +#include <sys/user.h> +#endif /* mac_aux */ + + +#ifdef UMAX_PTRACE +#include <a.out.h> +#endif + +#ifdef NEW_SUN_PTRACE +#include <sys/ptrace.h> +#include <machine/reg.h> +#endif + +#ifdef SYSV_TTYS +#include <termio.h> +#endif + +extern int errno; + +/* Nonzero if we are debugging an attached outside process + rather than an inferior. */ + +static int attach_flag; + +#define UPAGE_MASK 0x00003FFF + +START_FILE + +/* Record terminal status separately for debugger and inferior. */ + +#ifdef SYSV_TTYS +static struct termio ti_inferior; +#else +static struct sgttyb sg_inferior; +static struct tchars tc_inferior; +static struct ltchars ltc_inferior; +static int lmode_inferior; +#endif +static int tflags_inferior; +static int pgrp_inferior; + +#ifdef SYSV_TTYS +static struct termio ti_ours; +#else +static struct sgttyb sg_ours; +static struct tchars tc_ours; +static struct ltchars ltc_ours; +static int lmode_ours; +#endif +static int tflags_ours; +static int pgrp_ours; + +/* 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 () +{ + +#ifdef SYSV_TTYS + ti_inferior = ti_ours; +#else + sg_inferior = sg_ours; + tc_inferior = tc_ours; + ltc_inferior = ltc_ours; + lmode_inferior = lmode_ours; +#endif + tflags_inferior = tflags_ours; + pgrp_inferior = inferior_pid; + + 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 (terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + fcntl (0, F_SETFL, tflags_inferior); + fcntl (0, F_SETFL, tflags_inferior); +#ifdef SYSV_TTYS + ioctl (0, TCSETA, &ti_inferior); +#else + ioctl (0, TIOCSETN, &sg_inferior); + ioctl (0, TIOCSETC, &tc_inferior); + ioctl (0, TIOCSLTC, <c_inferior); + ioctl (0, TIOCLSET, &lmode_inferior); +#endif + ioctl (0, TIOCSPGRP, &pgrp_inferior); + } + 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 () +{ + 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 () +{ + terminal_ours_1 (0); +} + +static void +terminal_ours_1 (output_only) + int output_only; +{ + /* Ignore this signal since it will happen when we try to set the pgrp. */ + int (*osigttou) (); + + if (!terminal_is_ours) /* && inferior_thisrun_terminal == 0) */ + { + terminal_is_ours = 1; + + osigttou = signal (SIGTTOU, SIG_IGN); + + ioctl (0, TIOCGPGRP, &pgrp_inferior); + ioctl (0, TIOCSPGRP, &pgrp_ours); + + signal (SIGTTOU, osigttou); + + tflags_inferior = fcntl (0, F_GETFL, 0); +#ifdef SYSV_TTYS + ioctl (0, TCGETA, &ti_inferior); +#else + ioctl (0, TIOCGETP, &sg_inferior); + ioctl (0, TIOCGETC, &tc_inferior); + ioctl (0, TIOCGLTC, <c_inferior); + ioctl (0, TIOCLGET, &lmode_inferior); +#endif + } + + fcntl (0, F_SETFL, tflags_ours); + fcntl (0, F_SETFL, tflags_ours); + + +#ifdef SYSV_TTYS + ti_ours.c_lflag |= ICANON | ISIG; + if (output_only) + ti_ours.c_lflag &= ~((ICANON|ISIG)&ti_inferior.c_lflag); + ioctl (0, TCSETA, &ti_ours); + ti_ours.c_lflag |= ICANON | ISIG; +#else + sg_ours.sg_flags &= ~RAW & ~CBREAK; + if (output_only) + sg_ours.sg_flags |= (RAW | CBREAK) & sg_inferior.sg_flags; + ioctl (0, TIOCSETN, &sg_ours); + ioctl (0, TIOCSETC, &tc_ours); + ioctl (0, TIOCSLTC, <c_ours); + ioctl (0, TIOCLSET, &lmode_ours); + sg_ours.sg_flags &= ~RAW & ~CBREAK; +#endif +} + +static void +term_status_command () +{ + register int i; + printf ("Inferior's terminal status (currently saved by GDB):\n"); +#ifdef SYSV_TTYS + printf ("fcntl flags = 0x%x, owner pid = %d.\n", + tflags_inferior, pgrp_inferior); + printf ("iflag = 0x%04x, oflag = 0x%04x, cflag = 0x%04x, lflag = 0x%04x\n", + ti_inferior.c_iflag, ti_inferior.c_oflag, + ti_inferior.c_cflag, ti_inferior.c_lflag); + printf ("line discipline = %d\n", ti_inferior.c_line); + printf ("control chars: "); + for (i = 0; i < NCC; i++) + printf ("0x%x ", ti_inferior.c_cc[i]); + printf ("\n"); +#else + 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); + printf ("tchars: "); + for (i = 0; i < sizeof (struct tchars); i++) + printf ("0x%x ", ((char *)&tc_inferior)[i]); + printf ("\n"); + printf ("ltchars: "); + for (i = 0; i < sizeof (struct ltchars); i++) + printf ("0x%x ", ((char *)<c_inferior)[i]); + printf ("\n"); +#endif +} + +static void +new_tty (ttyname) + char *ttyname; +{ + register int tty; + register int fd; + +#if 0 + /* I think it is better not to do this. Then C-z on the GDB terminal + will still stop the program, while C-z on the data terminal + will be input. */ + + /* 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 vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (allargs, env) + char **allargs; + char **env; +{ + int pid; + extern int sys_nerr; + extern char *sys_errlist[]; + extern int errno; + + /* exec is said to fail if the executable is open. */ + close_exec_file (); + + pid = vfork (); + if (pid < 0) + perror_with_name ("vfork"); + + if (pid == 0) + { + /* Run inferior in a separate process group. */ + setpgrp (getpid (), getpid ()); + + 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); */ + + ptrace (0); + execle ("/bin/sh", "sh", "-c", allargs, 0, env); + + fprintf (stderr, "Cannot exec /bin/sh: %s.\n", + 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 (inferior_pid == 0) + error ("The program is not being run."); + if (!query ("Kill the inferior process? ")) + error ("Not confirmed."); + kill_inferior (); +} + +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (8, inferior_pid, 0, 0); + wait (0); + inferior_died (); +} + +inferior_died () +{ + inferior_pid = 0; + attach_flag = 0; + mark_breakpoints_out (); + reopen_exec_file (); + if (have_core_file_p ()) + set_current_frame (read_register (FP_REGNUM)); +} + +/* 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; + ptrace (step ? 9 : 7, inferior_pid, 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + +#ifdef NEW_SUN_PTRACE + +/* Start debugging the process whose number is PID. */ + +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 + +#ifdef NEW_SUN_PTRACE + +void +fetch_inferior_registers () +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + extern char registers[]; + + ptrace (PTRACE_GETREGS, inferior_pid, &inferior_registers); + ptrace (PTRACE_GETFPREGS, inferior_pid, &inferior_fp_registers); + + bcopy (&inferior_registers, registers, 16 * 4); + bcopy (&inferior_fp_registers, ®isters[REGISTER_BYTE (FP0_REGNUM)], + sizeof inferior_fp_registers.fps_regs); + *(int *)®isters[REGISTER_BYTE (PS_REGNUM)] = inferior_registers.r_ps; + *(int *)®isters[REGISTER_BYTE (PC_REGNUM)] = inferior_registers.r_pc; + bcopy (&inferior_fp_registers.fps_control, + ®isters[REGISTER_BYTE (FPC_REGNUM)], + sizeof inferior_fp_registers - sizeof inferior_fp_registers.fps_regs); +} + +/* 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; +{ + struct regs inferior_registers; + struct fp_status inferior_fp_registers; + extern char registers[]; + + bcopy (registers, &inferior_registers, 16 * 4); + bcopy (®isters[REGISTER_BYTE (FP0_REGNUM)], &inferior_fp_registers, + sizeof inferior_fp_registers.fps_regs); + inferior_registers.r_ps = *(int *)®isters[REGISTER_BYTE (PS_REGNUM)]; + inferior_registers.r_pc = *(int *)®isters[REGISTER_BYTE (PC_REGNUM)]; + bcopy (®isters[REGISTER_BYTE (FPC_REGNUM)], + &inferior_fp_registers.fps_control, + sizeof inferior_fp_registers - sizeof inferior_fp_registers.fps_regs); + + ptrace (PTRACE_SETREGS, inferior_pid, &inferior_registers); + ptrace (PTRACE_SETFPREGS, inferior_pid, &inferior_fp_registers); +} + +#else + +void +fetch_inferior_registers () +{ + register int regno; + register unsigned int regaddr; + char buf[MAX_REGISTER_RAW_SIZE]; + register int i; + +#ifdef UMAX_PTRACE + unsigned int offset = 0; +#else + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) & UPAGE_MASK; +#endif + + 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]; + +#ifdef UMAX_PTRACE + unsigned int offset = 0; +#else + struct user u; + unsigned int offset = (char *) &u.u_ar0 - (char *) &u; + offset = ptrace (3, inferior_pid, offset, 0) & UPAGE_MASK; +#endif + + if (regno >= 0) + { + regaddr = register_addr (regno, offset); + errno = 0; + 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; + ptrace (6, inferior_pid, regaddr, read_register (regno)); + if (errno != 0) + { + sprintf (buf, "writing register number %d", regno); + perror_with_name (buf); + } + } +} + +#endif /* not NEW_SUN_PTRACE */ + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + buffer[i] = ptrace (1, inferior_pid, addr, 0); + + /* Copy appropriate bytes out of the buffer. */ + bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); +} + +/* Copy LEN bytes of data from debugger memnory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (1, inferior_pid, addr, 0); + if (count > 1) + buffer[count - 1] + = ptrace (1, inferior_pid, + addr + (count - 1) * sizeof (int), 0); + + /* Copy data to be written over corresponding part of buffer */ + + bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (4, inferior_pid, addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +static void +try_writing_regs_command () +{ + register int i; + register int value; + extern int errno; + + if (inferior_pid == 0) + error ("The program is not being run."); + + for (i = 0; ; i += 2) + { + QUIT; + errno = 0; + value = ptrace (3, inferior_pid, i, 0); + 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); + } +} + +static +initialize () +{ + 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; + +#ifdef SYSV_TTYS + ioctl (0, TCGETA, &ti_ours); +#else + ioctl (0, TIOCGETP, &sg_ours); + ioctl (0, TIOCGETC, &tc_ours); + ioctl (0, TIOCGLTC, <c_ours); + ioctl (0, TIOCLGET, &lmode_ours); +#endif + fcntl (0, F_GETFL, tflags_ours); + ioctl (0, TIOCGPGRP, &pgrp_ours); + + terminal_is_ours = 1; +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d28 1 +a30 1 +#include <sys/user.h> +d36 11 +d56 4 +d67 2 +d73 3 +d80 1 +d84 3 +d91 1 +d110 4 +d118 1 +d135 3 +d142 1 +d191 3 +d198 1 +d201 11 +a214 3 + + fcntl (0, F_SETFL, tflags_ours); + fcntl (0, F_SETFL, tflags_ours); +d220 1 +d228 12 +d251 1 +d470 1 +a470 1 + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; +d500 1 +a500 1 + offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; +d647 3 +d654 1 +@ diff --git a/gdb/RCS/m-mac-aux.h,v b/gdb/RCS/m-mac-aux.h,v new file mode 100644 index 0000000..eaac3dd --- /dev/null +++ b/gdb/RCS/m-mac-aux.h,v @@ -0,0 +1,523 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.16.06; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.05.15.44; author gnu; state Exp; +branches ; +next ; + + +desc +@Originally nonexistent, I create it. +@ + + +1.2 +log +@Original new config file for Mac-II running A/UX. +@ +text +@/* Parameters for execution on Macintosh under A/UX, 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 mac_aux +#define mac_aux +#endif + +/* Get rid of any system-imposed stack limit if possible. */ + +#undef 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. */ + +#undef NAMES_HAVE_UNDERSCORE + +/* COFF format object files */ + +#define COFF_FORMAT + +/* System eVil ttys */ + +#define SYSV_TTYS + +/* Debugger information will not be in DBX format. */ + +#undef 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) + +/* Address of end of stack space. */ + +#define STACK_END_ADDR 0x20000000 + +/* Stack grows downward. */ + +#define INNER_THAN < + +/* Sequence of bytes for breakpoint instruction. */ + +#define BREAKPOINT {0x4e, 0x4f} + +/* 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) == 0x4e76) + +/* Return 1 if P points to an invalid floating point value. */ + +#define INVALID_FLOAT(p, len) 0 /* Just a first guess; not checked */ + +/* Say how long (ordinary) registers are. */ + +#define REGISTER_TYPE long + +/* Number of machine registers */ + +#define NUM_REGS 31 + +/* 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", "fpcode", "fpflags" } + +/* 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+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. */ + +#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) + +/* 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)) + +/* Enable use of alternate code to read and write registers. */ + +#undef NEW_SUN_PTRACE + +/* Enable use of alternate code for Sun's format of core dump file. */ + +#undef NEW_SUN_CORE + +/* Do implement the attach and detach commands. */ + +#undef 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). */ + +/* 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, 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 + 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.frame); \ + 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 CORE_ADDR fp = read_register (FP_REGNUM); \ + register int regnum; \ + struct frame_saved_regs fsr; \ + struct frame_info fi; \ + char raw_buffer[12]; \ + fi = get_frame_info (fp); \ + 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); \ + set_current_frame (read_register (FP_REGNUM)); } + +/* 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, 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, fun, nargs) \ +{ *(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.1 +log +@Initial revision +@ +text +@d1 485 +@ diff --git a/gdb/RCS/m-mac-auxinit.h,v b/gdb/RCS/m-mac-auxinit.h,v new file mode 100644 index 0000000..796bc8d --- /dev/null +++ b/gdb/RCS/m-mac-auxinit.h,v @@ -0,0 +1,43 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.19.09; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.05.18.45; author gnu; state Exp; +branches ; +next ; + + +desc +@Originally nonexistent. +@ + + +1.2 +log +@Created by John Gilmore for Mac A/UX +@ +text +@ +/* This is how the size of an individual .o file's text segment + is rounded on a mac under a/ux. */ + +#define FILEADDR_ROUND(addr) (addr) +@ + + +1.1 +log +@Initial revision +@ +text +@d1 5 +@ diff --git a/gdb/RCS/m68k-pinsn.c,v b/gdb/RCS/m68k-pinsn.c,v new file mode 100644 index 0000000..dedc0e7 --- /dev/null +++ b/gdb/RCS/m68k-pinsn.c,v @@ -0,0 +1,828 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.08.29; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.22.04.55; author gnu; state Exp; +branches ; +next ; + + +desc +@From RMS's development sources on wheaties, 20Jan88 +@ + + +1.2 +log +@Avoid the so-called "portable" preassembled instructions; call a macro +to generate them, since a/ux assembler uses a different syntax (grumble) +@ +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 "m68k-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, 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. */ + +#ifdef mac_aux +#ifdef __STDC__ +#define asm16(str) asm ("short " str#) +#else +#define asm16(str) asm ("short str") +#endif +#else +#ifdef __STDC__ +#define asm16(str) asm (".word " str#) +#else +#define asm16(str) asm (".word str") +#endif +#endif + +convert_from_68881 (from, to) + char *from; + double *to; +{ +#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. */ + asm16 (020156); + asm16 (8); + asm16 (021156); + asm16 (12); + asm16 (0xf210); + asm16 (0x4800); + asm16 (0xf211); + asm16 (0x7400); +#endif +} + +/* The converse: convert the double *FROM to an extended float + and store where TO points. */ + +convert_to_68881 (from, to) + double *from; + char *to; +{ +#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. */ + asm16 (020156); + asm16 (8); + asm16 (021156); + asm16 (12); + asm16 (0xf210); + asm16 (0x5400); + asm16 (0xf211); + asm16 (0x6800); +#endif +} +@ + + +1.1 +log +@Initial revision +@ +text +@d713 14 +d739 8 +a746 6 + asm (".word 020156"); + asm (".word 8"); + asm (".word 021156"); + asm (".word 12"); + asm (".long 0xf2104800"); + asm (".long 0xf2117400"); +d764 8 +a771 6 + asm (".word 020156"); + asm (".word 8"); + asm (".word 021156"); + asm (".word 12"); + asm (".long 0xf2105400"); + asm (".long 0xf2116800"); +@ diff --git a/gdb/RCS/main.c,v b/gdb/RCS/main.c,v new file mode 100644 index 0000000..ae6615f --- /dev/null +++ b/gdb/RCS/main.c,v @@ -0,0 +1,1110 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.09.12; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.04.23.16; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS development on wheaties, 20Jan88 +@ + + +1.2 +log +@Add <sys/types.h> +@ +text +@/* Top level 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 <sys/types.h> +#include <sys/file.h> +#include <stdio.h> +#include <setjmp.h> +#include <signal.h> +#include <sys/param.h> +#include "defs.h" +#include "command.h" +#include "param.h" + +#ifdef SET_STACK_LIMIT_HUGE +#include <sys/time.h> +#include <sys/resource.h> +#endif + +/* Version number of GDB, as a string. */ + +extern char *version; + +/* Chain containing all defined commands. */ + +struct cmd_list_element *cmdlist; + +/* Chain containing all defined info subcommands. */ + +struct cmd_list_element *infolist; + +/* stdio stream that command input is being read from. */ + +FILE *instream; + +/* 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) (); + +void free_command_lines (); +char *read_line (); +static void initialize_main (); +void command_loop (); +static void source_command (); +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; + +return_to_top_level () +{ + quit_flag = 0; + immediate_quit = 0; + clear_breakpoint_commands (); + clear_momentary_breakpoints (); + do_cleanups (0); + longjmp (to_top_level, 1); +} + +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; + +#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); + 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 (argv[i][0] == '-') + i++; + } + + /* Run the init function of each source file */ + + initialize_all_files (); + initialize_main (); /* But that omits this file! Do it now */ + + signal (SIGINT, request_quit); + signal (SIGQUIT, SIG_IGN); + + 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")) + /* 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); + /* -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) + { + if (*p) + error ("User-defined commands cannot take command-line arguments: \"%s\"", + p); + cmdlines = (struct command_line *) c->function; + if (cmdlines == (struct command_line *) 0) + /* Null command */ + return; + while (cmdlines) + { + execute_command (cmdlines->line, 0); + cmdlines = cmdlines->next; + } + } + 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 (instream == stdin) + printf ("%s", prompt); + fflush (stdout); + + if (window_hook && instream == stdin) + (*window_hook) (instream, prompt); + + quit_flag = 0; + old_chain = make_cleanup (do_nothing, 0); + execute_command (read_line (instream == stdin), instream == stdin); + /* Do any commands attached to breakpoint we stopped at. */ + do_breakpoint_commands (); + do_cleanups (old_chain); + } +} + +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; +} + +/* 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 * +read_line (repeat) + 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++; + signal (SIGTSTP, stop_sig); + + while (1) + { + c = fgetc (instream); + if (c == -1 || c == '\n') + break; + if (p - line == linesize - 1) + { + linesize *= 2; + nline = (char *) xrealloc (line, linesize); + p += nline - line; + line = nline; + } + *p++ = c; + } + + signal (SIGTSTP, SIG_DFL); + 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 = read_line (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, 0, 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_cmd (0, 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, cmdlist, "", -2, 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 >= '1' && *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); + + 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\ +\n\ + Copyright (C) 1986 Richard M. Stallman\n\ + Everyone is permitted to copy and distribute verbatim copies\n\ + of this license, but changing it is not allowed.\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); + read_line (); + + 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); + read_line (); + + 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) 1987 Free Software Foundation, Inc.\" (or with the year updated if\n\ +that is 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); + read_line (); + + 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 third\n\ + 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) 1987 Free Software Foundation, Inc.\" (or with\n\ + the year updated if appropriate), saying that there\n\ + is no warranty (or else, saying that you provide\n\ + a warranty) and that users may redistribute the program under\n\ + these conditions, and telling the user how to view a copy of\n\ + 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\ +--Type Return to print more--"); + fflush (stdout); + read_line (); + + printf ("\ + 3. You may copy and distribute GDB or any portion of it in\n\ +compiled, executable or object code form under the terms of Paragraphs\n\ +1 and 2 above provided that you do the following:\n\ +\n\ + a) cause each such copy to be accompanied by the\n\ + corresponding machine-readable source code, which must\n\ + be distributed under the terms of Paragraphs 1 and 2 above; or,\n\ +\n\ + b) cause each such copy to be accompanied by a\n\ + written offer, with no time limit, to give any third party\n\ + free (except for a nominal shipping charge) a machine readable\n\ + copy of the corresponding source code, to be distributed\n\ + under the terms of Paragraphs 1 and 2 above; or,\n\n"); + + printf ("\ + c) in the case of a recipient of GDB in compiled, executable\n\ + or object code form (without the corresponding source code) you\n\ + shall cause copies you distribute to be accompanied by a copy\n\ + of the written offer of source code which you received along\n\ + with the copy you received.\n\ +--Type Return to print more--"); + fflush (stdout); + read_line (); + + 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\ + 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) 1987 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--; +} + +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; +{ + char buf[MAXPATHLEN]; + if (arg) error ("The \"pwd\" command does not take an argument: %s", arg); + printf ("Working directory %s.\n", getwd (buf)); +} + +static void +cd_command (dir, from_tty) + char *dir; + int from_tty; +{ + if (dir == 0) + error_no_arg ("new working directory"); + + if (chdir (dir) < 0) + perror_with_name (dir); + if (from_tty) + pwd_command ((char *) 0, 1); +} + + +/* Clean up on error during a "source" command. + Close the file opened by the command + and restore the previous input stream. */ + +static void +source_cleanup (stream) + FILE *stream; +{ + fclose (instream); + instream = stream; +} + +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 +initialize_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_com ("set-prompt", class_support, set_prompt_command, + "Change gdb's prompt from the default of \"(gdb)\""); + 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 +@d21 1 +@ diff --git a/gdb/RCS/source.c,v b/gdb/RCS/source.c,v new file mode 100644 index 0000000..bd3218b --- /dev/null +++ b/gdb/RCS/source.c,v @@ -0,0 +1,705 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.09.34; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.04.30.11; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS development sources on wheaties, 20Jan88 +@ + + +1.2 +log +@Add <sys/types.h> +@ +text +@/* List lines of source files 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 <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/file.h> +#include "defs.h" +#include "initialize.h" +#include "symtab.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 for "info line" to work on if no line specified. */ + +static int line_info_default_line; + +/* First line number listed by last listing command. */ + +static int first_line_listed; + +START_FILE + +/* Set the source file default for the "list" command, + specifying a symtab. */ + +void +select_source_symtab (s) + register struct symtab *s; +{ + if (s) + { + struct symtab_and_line sal; + + /* Make the default place to list be the function `main' + if one exists. */ + if (lookup_symbol ("main", 0, VAR_NAMESPACE)) + { + sal = decode_line_spec ("main", 1); + 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. */ + 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; + } +} + +static void +directories_info () +{ + printf ("Source directories searched: %s\n", source_path); +} + +static void +init_source_path () +{ + register struct symtab *s; + char wd[MAXPATHLEN]; + if (getwd (wd) == NULL) + perror_with_name ("getwd"); + + source_path = savestring (wd, strlen (wd)); + + /* 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; + + char wd[MAXPATHLEN]; + if (getwd (wd) == NULL) + perror_with_name ("getwd"); + + if (dirname == 0) + { + if (query ("Reinitialize source path to %s? ", wd)) + { + 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 = wd; + 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 (wd, "/", 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 + { + char dirname[MAXPATHLEN]; + if (getwd (dirname) == NULL) + perror_with_name ("getwd"); + *filename_opened = concat (dirname, "/", 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 && 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') + { + if (nlines == lines_allocated) + line_charpos = (int *) xrealloc (line_charpos, + sizeof (int) * (lines_allocated *= 2)); + 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, fullname); + if (desc < 0) + { + *fullname = NULL; + return 0; + } + if (s->line_charpos == 0) linenums_changed = 1; + if (linenums_changed) find_source_lines (s, desc); + close (desc); + return linenums_changed; +} + +/* 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) + struct symtab *s; + int line, stopline; +{ + register int c; + register int desc; + register FILE *stream; + int nlines = stopline - line; + + desc = openp (source_path, 0, s->filename, O_RDONLY, 0, (char **) 0); + if (desc < 0) + perror_with_name (s->filename); + + 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; + line_info_default_line = 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); +} + +static void +list_command (arg, from_tty) + char *arg; + int from_tty; +{ + 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) + error ("Listing source lines requires symbols."); + + /* "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); + 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); + 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 + sal = decode_line_1 (&arg1, 0, 0, 0); + + /* 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) + sal_end = decode_line_1 (&arg1, 0, 0, 0); + else + sal_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line); + } + + 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); + 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); + else + print_source_lines (sal.symtab, sal.line, + dummy_end ? sal.line + 10 : sal_end.line + 1); +} + +/* Print info on range of pc's in a specified line. */ + +static void +line_info (arg, from_tty) + char *arg; + int from_tty; +{ + struct symtab_and_line sal; + int start_pc, end_pc; + + if (arg == 0) + { + sal.symtab = current_source_symtab; + sal.line = line_info_default_line; + } + else + { + sal = decode_line_spec (arg); + + /* If this command is repeated with RET, + turn it into the no-arg variant. */ + + if (from_tty) + *arg = 0; + } + + 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. */ + line_info_default_line = sal.line + 1; + } + else + printf ("Line number %d is out of range for \"%s\".\n", + sal.line, sal.symtab->filename); +} + +static +initialize () +{ + 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 ("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."); +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d22 1 +@ diff --git a/gdb/RCS/symmisc.c,v b/gdb/RCS/symmisc.c,v new file mode 100644 index 0000000..42653dfb --- /dev/null +++ b/gdb/RCS/symmisc.c,v @@ -0,0 +1,575 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.09.53; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.04.25.22; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS's devl sources on wheaties +@ + + +1.2 +log +@Check for null pointer passed to free()... +@ +text +@/* Do various things to symbol tables (other than lookup)), 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 "defs.h" +#include "initialize.h" +#include "symtab.h" + +#include <stdio.h> +#include <obstack.h> + +static void free_symtab (); + +START_FILE + +/* Free all the symtabs that are currently installed, + and all storage associated with them. + Leaves us in a consistent state with no symtabs installed. */ + +void +free_all_symtabs () +{ + register struct symtab *s, *snext; + + /* All values will be invalid because their types will be! */ + + clear_value_history (); + clear_displays (); + clear_internalvars (); + clear_breakpoints (); + set_default_breakpoint (0, 0, 0, 0); + + current_source_symtab = 0; + + for (s = symtab_list; s; s = snext) + { + snext = s->next; + free_symtab (s); + } + symtab_list = 0; + obstack_free (symbol_obstack, 0); + obstack_init (symbol_obstack); + + if (misc_function_vector) + free (misc_function_vector); + misc_function_count = 0; + misc_function_vector = 0; +} + +/* Free a struct block <- B and all the symbols defined in that block. */ + +static void +free_symtab_block (b) + struct block *b; +{ + register int i, n; + n = BLOCK_NSYMS (b); + for (i = 0; i < n; i++) + { + free (SYMBOL_NAME (BLOCK_SYM (b, i))); + free (BLOCK_SYM (b, i)); + } + free (b); +} + +/* Free all the storage associated with the struct symtab <- S. + Note that some symtabs have contents malloc'ed structure by structure, + while some have contents that all live inside one big block of memory, + and some share the contents of another symbol table and so you should + not free the contents on their behalf (except sometimes the linetable, + which maybe per symtab even when the rest is not). + It is s->free_code that says which alternative to use. */ + +static void +free_symtab (s) + register struct symtab *s; +{ + register int i, n; + register struct blockvector *bv; + register struct type *type; + register struct typevector *tv; + + switch (s->free_code) + { + case free_nothing: + /* All the contents are part of a big block of memory + and some other symtab is in charge of freeing that block. + Therefore, do nothing. */ + break; + + case free_explicit: + /* All the contents are part of a big block of memory + and that is our `free_ptr' and will be freed below. */ + break; + + case free_contents: + /* Here all the contents were malloc'ed structure by structure + and must be freed that way. */ + /* First free the blocks (and their symbols. */ + bv = BLOCKVECTOR (s); + n = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < n; i++) + free_symtab_block (BLOCKVECTOR_BLOCK (bv, i)); + /* Free the blockvector itself. */ + free (bv); + /* Free the type vector. */ + tv = TYPEVECTOR (s); + if (tv) /* FIXME, should this happen? It does... */ + free (tv); + /* Also free the linetable. */ + + case free_linetable: + /* Everything will be freed either by our `free_ptr' + or by some other symbatb, except for our linetable. + Free that now. */ + free (LINETABLE (s)); + break; + } + + /* If there is a single block of memory to free, free it. */ + if (s->free_ptr) + free (s->free_ptr); + + if (s->line_charpos) + free (s->line_charpos); + free (s->filename); + free (s); +} + +/* Convert a raw symbol-segment to a struct symtab, + and relocate its internal pointers so that it is valid. */ + +/* This is how to relocate one pointer, given a name for it. + Works independent of the type of object pointed to. */ +#define RELOCATE(slot) (slot ? (* (char **) &slot += relocation) : 0) + +/* This is the inverse of RELOCATE. We use it when storing + a core address into a slot that has yet to be relocated. */ +#define UNRELOCATE(slot) (slot ? (* (char **) &slot -= relocation) : 0) + +/* During the process of relocation, this holds the amount to relocate by + (the address of the file's symtab data, in core in the debugger). */ +static int relocation; + +#define CORE_RELOCATE(slot) \ + ((slot) += (((slot) < data_start) ? text_relocation \ + : ((slot) < bss_start) ? data_relocation : bss_relocation)) + +#define TEXT_RELOCATE(slot) ((slot) += text_relocation) + +/* Relocation amounts for addresses in the program's core image. */ +static int text_relocation, data_relocation, bss_relocation; + +/* Boundaries that divide program core addresses into text, data and bss; + used to determine which relocation amount to use. */ +static int data_start, bss_start; + +static void relocate_typevector (); +static void relocate_blockvector (); +static void relocate_type (); +static void relocate_block (); +static void relocate_symbol (); + +/* Relocate a file symbol table so that all the pointers + are valid C pointers. Pass the struct symtab for the file + and the amount to relocate by. */ + +static struct symtab * +relocate_symtab (root) + struct symbol_root *root; +{ + struct symtab *sp = (struct symtab *) xmalloc (sizeof (struct symtab)); + bzero (sp, sizeof (struct symtab)); + + relocation = (int) root; + text_relocation = root->textrel; + data_relocation = root->datarel; + bss_relocation = root->bssrel; + data_start = root->databeg; + bss_start = root->bssbeg; + + sp->filename = root->filename; + sp->ldsymoff = root->ldsymoff; + sp->language = root->language; + sp->compilation = root->compilation; + sp->version = root->version; + sp->blockvector = root->blockvector; + sp->typevector = root->typevector; + sp->free_code = free_explicit; + sp->free_ptr = (char *) root; + + RELOCATE (TYPEVECTOR (sp)); + RELOCATE (BLOCKVECTOR (sp)); + RELOCATE (sp->version); + RELOCATE (sp->compilation); + RELOCATE (sp->filename); + + relocate_typevector (TYPEVECTOR (sp)); + relocate_blockvector (BLOCKVECTOR (sp)); + + return sp; +} + +static void +relocate_typevector (tv) + struct typevector *tv; +{ + register int ntypes = TYPEVECTOR_NTYPES (tv); + register int i; + + for (i = 0; i < ntypes; i++) + RELOCATE (TYPEVECTOR_TYPE (tv, i)); + for (i = 0; i < ntypes; i++) + relocate_type (TYPEVECTOR_TYPE (tv, i)); +} + +static void +relocate_blockvector (blp) + register struct blockvector *blp; +{ + register int nblocks = BLOCKVECTOR_NBLOCKS (blp); + register int i; + for (i = 0; i < nblocks; i++) + RELOCATE (BLOCKVECTOR_BLOCK (blp, i)); + for (i = 0; i < nblocks; i++) + relocate_block (BLOCKVECTOR_BLOCK (blp, i)); +} + +static void +relocate_block (bp) + register struct block *bp; +{ + register int nsyms = BLOCK_NSYMS (bp); + register int i; + + TEXT_RELOCATE (BLOCK_START (bp)); + TEXT_RELOCATE (BLOCK_END (bp)); + + /* These two should not be recursively processed. + The superblock need not be because all blocks are + processed from relocate_blockvector. + The function need not be because it will be processed + under the block which is its scope. */ + RELOCATE (BLOCK_SUPERBLOCK (bp)); + RELOCATE (BLOCK_FUNCTION (bp)); + + for (i = 0; i < nsyms; i++) + RELOCATE (BLOCK_SYM (bp, i)); + + for (i = 0; i < nsyms; i++) + relocate_symbol (BLOCK_SYM (bp, i)); +} + +static void +relocate_symbol (sp) + register struct symbol *sp; +{ + RELOCATE (SYMBOL_NAME (sp)); + if (SYMBOL_CLASS (sp) == LOC_BLOCK) + { + RELOCATE (SYMBOL_BLOCK_VALUE (sp)); + /* We can assume the block that belongs to this symbol + is not relocated yet, since it comes after + the block that contains this symbol. */ + BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp)) = sp; + UNRELOCATE (BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp))); + } + else if (SYMBOL_CLASS (sp) == LOC_STATIC) + CORE_RELOCATE (SYMBOL_VALUE (sp)); + else if (SYMBOL_CLASS (sp) == LOC_LABEL) + TEXT_RELOCATE (SYMBOL_VALUE (sp)); + RELOCATE (SYMBOL_TYPE (sp)); +} + +/* We cannot come up with an a priori spanning tree + for the network of types, since types can be used + for many symbols and also as components of other types. + Therefore, we need to be able to mark types that we + already have relocated (or are already in the middle of relocating) + as in a garbage collector. */ + +static void +relocate_type (tp) + register struct type *tp; +{ + register int nfields = TYPE_NFIELDS (tp); + register int i; + + RELOCATE (TYPE_NAME (tp)); + RELOCATE (TYPE_TARGET_TYPE (tp)); + RELOCATE (TYPE_FIELDS (tp)); + RELOCATE (TYPE_POINTER_TYPE (tp)); + + for (i = 0; i < nfields; i++) + { + RELOCATE (TYPE_FIELD_TYPE (tp, i)); + RELOCATE (TYPE_FIELD_NAME (tp, i)); + } +} + +/* Read symsegs from file named NAME open on DESC, + make symtabs from them, and return a chain of them. + Assumes DESC is prepositioned at the end of the string table, + just before the symsegs if there are any. */ + +struct symtab * +read_symsegs (desc, name) + int desc; + char *name; +{ + struct symbol_root root; + register char *data; + register struct symtab *sp, *chain = 0; + register int len; + + while (1) + { + len = myread (desc, &root, sizeof root); + if (len == 0 || root.format == 0) + break; + if (root.format != 1 || + root.length < sizeof root) + error ("Invalid symbol segment format code"); + data = (char *) xmalloc (root.length); + bcopy (&root, data, sizeof root); + len = myread (desc, data + sizeof root, + root.length - sizeof root); + sp = relocate_symtab (data); + sp->next = chain; + chain = sp; + } + + return chain; +} + +static int block_depth (); +static void print_spaces (); +static void print_symbol (); + +print_symtabs (filename) + char *filename; +{ + FILE *outfile; + register struct symtab *s; + register int i, j; + int len, line, blen; + register struct linetable *l; + struct blockvector *bv; + register struct block *b; + int depth; + struct cleanup *cleanups; + extern int fclose(); + + if (filename == 0) + error_no_arg ("file to write symbol data in"); + outfile = fopen (filename, "w"); + + cleanups = make_cleanup (fclose, outfile); + immediate_quit++; + + for (s = symtab_list; s; s = s->next) + { + /* First print the line table. */ + fprintf (outfile, "Symtab for file %s\n\n", s->filename); + fprintf (outfile, "Line table:\n\n"); + l = LINETABLE (s); + len = l->nitems; + for (i = 0; i < len; i++) + { + if (l->item[i] < 0) + line = - l->item[i] - 1; + else + fprintf (outfile, " line %d at %x\n", ++line, l->item[i]); + } + /* Now print the block info. */ + fprintf (outfile, "\nBlockvector:\n\n"); + bv = BLOCKVECTOR (s); + len = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < len; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + depth = block_depth (b) * 2; + print_spaces (depth, outfile); + fprintf (outfile, "block #%03d (object 0x%x) ", i, b); + fprintf (outfile, "[0x%x..0x%x]", BLOCK_START (b), BLOCK_END (b)); + if (BLOCK_SUPERBLOCK (b)) + fprintf (outfile, " (under 0x%x)", BLOCK_SUPERBLOCK (b)); + if (BLOCK_FUNCTION (b)) + fprintf (outfile, " %s", SYMBOL_NAME (BLOCK_FUNCTION (b))); + fputc ('\n', outfile); + blen = BLOCK_NSYMS (b); + for (j = 0; j < blen; j++) + { + print_symbol (BLOCK_SYM (b, j), depth + 1, outfile); + } + } + + fprintf (outfile, "\n\n"); + } + + immediate_quit--; + do_cleanups (cleanups); +} + +static void +print_symbol (symbol, depth, outfile) + struct symbol *symbol; + int depth; + FILE *outfile; +{ + print_spaces (depth, outfile); + if (SYMBOL_NAMESPACE (symbol) == LABEL_NAMESPACE) + { + fprintf (outfile, "label %s at 0x%x", SYMBOL_NAME (symbol), + SYMBOL_VALUE (symbol)); + return; + } + if (SYMBOL_NAMESPACE (symbol) == STRUCT_NAMESPACE) + { + if (TYPE_NAME (SYMBOL_TYPE (symbol))) + { + type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth); + } + else + { + fprintf (outfile, "%s %s = ", + (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_ENUM + ? "enum" + : (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_STRUCT + ? "struct" : "union")), + SYMBOL_NAME (symbol)); + type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth); + } + fprintf (outfile, ";\n"); + } + else + { + if (SYMBOL_CLASS (symbol) == LOC_TYPEDEF) + fprintf (outfile, "typedef "); + if (SYMBOL_TYPE (symbol)) + { + type_print_1 (SYMBOL_TYPE (symbol), SYMBOL_NAME (symbol), + outfile, 1, depth); + fprintf (outfile, "; "); + } + else + fprintf (outfile, "%s ", SYMBOL_NAME (symbol)); + + switch (SYMBOL_CLASS (symbol)) + { + case LOC_CONST: + fprintf (outfile, "const %d (0x%x),", + SYMBOL_VALUE (symbol), SYMBOL_VALUE (symbol)); + break; + + case LOC_CONST_BYTES: + fprintf (outfile, "const %d hex bytes:", + TYPE_LENGTH (SYMBOL_TYPE (symbol))); + { + int i; + for (i = 0; i < TYPE_LENGTH (SYMBOL_TYPE (symbol)); i++) + fprintf (outfile, " %2x", SYMBOL_VALUE_BYTES (symbol) [i]); + fprintf (outfile, ","); + } + break; + + case LOC_STATIC: + fprintf (outfile, "static at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_REGISTER: + fprintf (outfile, "register %d,", SYMBOL_VALUE (symbol)); + break; + + case LOC_ARG: + fprintf (outfile, "arg at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_LOCAL: + fprintf (outfile, "local at 0x%x,", SYMBOL_VALUE (symbol)); + break; + + case LOC_TYPEDEF: + break; + + case LOC_LABEL: + fprintf (outfile, "label at 0x%x", SYMBOL_VALUE (symbol)); + break; + + case LOC_BLOCK: + fprintf (outfile, "block (object 0x%x) starting at 0x%x,", + SYMBOL_VALUE (symbol), + BLOCK_START (SYMBOL_BLOCK_VALUE (symbol))); + break; + } + } + fprintf (outfile, "\n"); +} + +/* Return the nexting depth of a block within other blocks in its symtab. */ + +static int +block_depth (block) + struct block *block; +{ + register int i = 0; + while (block = BLOCK_SUPERBLOCK (block)) i++; + return i; +} + +static +initialize () +{ + add_com ("printsyms", class_obscure, print_symtabs, + "Print dump of current symbol definitions to file OUTFILE."); +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d125 2 +a126 1 + free (tv); +@ diff --git a/gdb/RCS/symtab.c,v b/gdb/RCS/symtab.c,v new file mode 100644 index 0000000..7ccdaac --- /dev/null +++ b/gdb/RCS/symtab.c,v @@ -0,0 +1,1153 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.10.33; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.26.02.18.56; author gnu; state Exp; +branches ; +next ; + + +desc +@Original from RMS's wheaties devel sources +@ + + +1.2 +log +@Permit SYS V regular expression library as well as real Unix one. +@ +text +@/* Symbol table lookup for the GNU debugger, 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 "defs.h" +#include "initialize.h" +#include "symtab.h" +#include "param.h" + +#include <stdio.h> +#include <obstack.h> + +#ifdef mac_aux +#define REGCMP +#endif + +START_FILE + +/* Allocate an obstack to hold objects that should be freed + when we load a new symbol table. + This includes the symbols made by dbxread + and the types that are not permanent. */ + +struct obstack obstack1; + +struct obstack *symbol_obstack = &obstack1; + +/* These variables point to the objects + representing the predefined C data types. */ + +struct type *builtin_type_void; +struct type *builtin_type_char; +struct type *builtin_type_short; +struct type *builtin_type_int; +struct type *builtin_type_long; +struct type *builtin_type_unsigned_char; +struct type *builtin_type_unsigned_short; +struct type *builtin_type_unsigned_int; +struct type *builtin_type_unsigned_long; +struct type *builtin_type_float; +struct type *builtin_type_double; + +/* Lookup the symbol table of a source file named NAME. */ + +struct symtab * +lookup_symtab (name) + char *name; +{ + register struct symtab *s; + register char *copy; + + for (s = symtab_list; s; s = s->next) + if (!strcmp (name, s->filename)) + return s; + + /* If name not found as specified, see if adding ".c" helps. */ + + copy = (char *) alloca (strlen (name) + 3); + strcpy (copy, name); + strcat (copy, ".c"); + for (s = symtab_list; s; s = s->next) + if (!strcmp (copy, s->filename)) + return s; + + return 0; +} + +/* Lookup a typedef or primitive type named NAME, + visible in lexical block BLOCK. + If NOERR is nonzero, return zero if NAME is not suitably defined. */ + +struct type * +lookup_typename (name, block, noerr) + char *name; + struct block *block; + int noerr; +{ + register struct symbol *sym = lookup_symbol (name, block, VAR_NAMESPACE); + if (sym == 0 || SYMBOL_CLASS (sym) != LOC_TYPEDEF) + { + if (!strcmp (name, "int")) + return builtin_type_int; + if (!strcmp (name, "long")) + return builtin_type_long; + if (!strcmp (name, "short")) + return builtin_type_short; + if (!strcmp (name, "char")) + return builtin_type_char; + if (!strcmp (name, "float")) + return builtin_type_float; + if (!strcmp (name, "double")) + return builtin_type_double; + if (!strcmp (name, "void")) + return builtin_type_void; + + if (noerr) + return 0; + error ("No type named %s.", name); + } + return SYMBOL_TYPE (sym); +} + +struct type * +lookup_unsigned_typename (name) + char *name; +{ + if (!strcmp (name, "int")) + return builtin_type_unsigned_int; + if (!strcmp (name, "long")) + return builtin_type_unsigned_long; + if (!strcmp (name, "short")) + return builtin_type_unsigned_short; + if (!strcmp (name, "char")) + return builtin_type_unsigned_char; + error ("No type named unsigned %s.", name); +} + +/* Lookup a structure type named "struct NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_struct (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym = lookup_symbol (name, block, STRUCT_NAMESPACE); + if (sym == 0) + error ("No struct type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT) + error ("This context has union or enum %s, not a struct.", name); + return SYMBOL_TYPE (sym); +} + +/* Lookup a union type named "union NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_union (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym = lookup_symbol (name, block, STRUCT_NAMESPACE); + if (sym == 0) + error ("No union type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_UNION) + error ("This context has struct or enum %s, not a union.", name); + return SYMBOL_TYPE (sym); +} + +/* Lookup an enum type named "enum NAME", + visible in lexical block BLOCK. */ + +struct type * +lookup_enum (name, block) + char *name; + struct block *block; +{ + register struct symbol *sym = lookup_symbol (name, block, STRUCT_NAMESPACE); + if (sym == 0) + error ("No enum type named %s.", name); + if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_ENUM) + error ("This context has struct or union %s, not an enum.", name); + return SYMBOL_TYPE (sym); +} + +/* Given a type TYPE, return a type of pointers to that type. + May need to construct such a type if this is the first use. */ + +struct type * +lookup_pointer_type (type) + struct type *type; +{ + register struct type *ptype = TYPE_POINTER_TYPE (type); + if (ptype) return ptype; + + /* This is the first time anyone wanted a pointer to a TYPE. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + ptype = (struct type *) xmalloc (sizeof (struct type)); + else + ptype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (ptype, sizeof (struct type)); + TYPE_TARGET_TYPE (ptype) = type; + TYPE_POINTER_TYPE (type) = ptype; + /* New type is permanent if type pointed to is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (ptype) = sizeof (char *); + TYPE_CODE (ptype) = TYPE_CODE_PTR; + return ptype; +} + +/* Given a type TYPE, return a type of functions that return that type. + May need to construct such a type if this is the first use. */ + +struct type * +lookup_function_type (type) + struct type *type; +{ + register struct type *ptype = TYPE_FUNCTION_TYPE (type); + if (ptype) return ptype; + + /* This is the first time anyone wanted a function returning a TYPE. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + ptype = (struct type *) xmalloc (sizeof (struct type)); + else + ptype = (struct type *) obstack_alloc (symbol_obstack, + sizeof (struct type)); + + bzero (ptype, sizeof (struct type)); + TYPE_TARGET_TYPE (ptype) = type; + TYPE_FUNCTION_TYPE (type) = ptype; + /* New type is permanent if type returned is permanent. */ + if (TYPE_FLAGS (type) & TYPE_FLAG_PERM) + TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM; + TYPE_LENGTH (ptype) = 1; + TYPE_CODE (ptype) = TYPE_CODE_FUNC; + TYPE_NFIELDS (ptype) = 0; + return ptype; +} + +/* Smash TYPE to be a type of pointers to TO_TYPE. + If TO_TYPE is not permanent and has no pointer-type yet, + record TYPE as its pointer-type. */ + +void +smash_to_pointer_type (type, to_type) + struct type *type, *to_type; +{ + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + /* We assume the machine has only one representation for pointers! */ + TYPE_LENGTH (type) = sizeof (char *); + TYPE_CODE (type) = TYPE_CODE_PTR; + + if (TYPE_POINTER_TYPE (to_type) == 0 + && !(TYPE_FLAGS (type) & TYPE_FLAG_PERM)) + { + TYPE_POINTER_TYPE (to_type) = type; + } +} + +/* Smash TYPE to be a type of functions returning TO_TYPE. + If TO_TYPE is not permanent and has no function-type yet, + record TYPE as its function-type. */ + +void +smash_to_function_type (type, to_type) + struct type *type, *to_type; +{ + bzero (type, sizeof (struct type)); + TYPE_TARGET_TYPE (type) = to_type; + TYPE_LENGTH (type) = 1; + TYPE_CODE (type) = TYPE_CODE_FUNC; + TYPE_NFIELDS (type) = 0; + + if (TYPE_FUNCTION_TYPE (to_type) == 0 + && !(TYPE_FLAGS (type) & TYPE_FLAG_PERM)) + { + TYPE_FUNCTION_TYPE (to_type) = type; + } +} + +static struct symbol *lookup_block_symbol (); + +/* Find the definition for a specified symbol name NAME + in namespace NAMESPACE, visible from lexical block BLOCK. + Returns the struct symbol pointer, or zero if no symbol is found. */ + +struct symbol * +lookup_symbol (name, block, namespace) + char *name; + register struct block *block; + enum namespace namespace; +{ + register int i, n; + register struct symbol *sym; + register struct symtab *s; + struct blockvector *bv; + + /* Search specified block and its superiors. */ + + while (block != 0) + { + sym = lookup_block_symbol (block, name, namespace); + if (sym) return sym; + block = BLOCK_SUPERBLOCK (block); + } + + /* Now search all symtabs' global blocks. */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 0); + sym = lookup_block_symbol (block, name, namespace); + if (sym) return sym; + } + + /* Now search all symtabs' per-file blocks. + Not strictly correct, but more useful than an error. */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + block = BLOCKVECTOR_BLOCK (bv, 1); + sym = lookup_block_symbol (block, name, namespace); + if (sym) return sym; + } + return 0; +} + +/* Look for a symbol in block BLOCK using binary search. */ + +static struct symbol * +lookup_block_symbol (block, name, namespace) + register struct block *block; + char *name; + enum namespace namespace; +{ + register int bot, top, inc; + register struct symbol *sym; + + top = BLOCK_NSYMS (block); + bot = 0; + + /* First, advance BOT to not far before + the first symbol whose name is NAME. */ + + while (1) + { + inc = (top - bot + 1); + /* No need to keep binary searching for the last few bits worth. */ + if (inc < 7) + break; + inc >>= 1; + sym = BLOCK_SYM (block, bot + inc); + if (strcmp (SYMBOL_NAME (sym), name) < 0) + bot += inc; + else + top = bot + inc; + } + + /* Now scan forward until we run out of symbols, + find one whose name is greater than NAME, + or find one we want. + If there is more than one symbol with the right name and namespace, + we return the first one. dbxread.c is careful to make sure + that if one is a register then it comes first. */ + + top = BLOCK_NSYMS (block); + while (bot < top) + { + sym = BLOCK_SYM (block, bot); + inc = strcmp (SYMBOL_NAME (sym), name); + if (inc == 0 && SYMBOL_NAMESPACE (sym) == namespace) + return sym; + if (inc > 0) + return 0; + bot++; + } + return 0; +} + +/* Return the symbol for the function which contains a specified + lexical block, described by a struct block BL. */ + +struct symbol * +block_function (bl) + struct block *bl; +{ + while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0) + bl = BLOCK_SUPERBLOCK (bl); + + return BLOCK_FUNCTION (bl); +} + +/* Subroutine of find_pc_line */ + +static struct symtab * +find_pc_symtab (pc) + register CORE_ADDR pc; +{ + register struct block *b; + struct blockvector *bv; + register struct symtab *s; + + /* Search all symtabs for one whose file contains our pc */ + + for (s = symtab_list; s; s = s->next) + { + bv = BLOCKVECTOR (s); + b = BLOCKVECTOR_BLOCK (bv, 0); + if (BLOCK_START (b) <= pc + && BLOCK_END (b) > pc) + break; + } + + return s; +} + +/* Find the source file and line number for a given PC value. + Return a structure containing a symtab pointer, a line number, + and a pc range for the entire source line. + The value's .pc field is NOT the specified pc. + NOTCURRENT nonzero means, if specified pc is on a line boundary, + use the line that ends there. Otherwise, in that case, the line + that begins there is used. */ + +struct symtab_and_line +find_pc_line (pc, notcurrent) + CORE_ADDR pc; + int notcurrent; +{ + struct symtab *s; + register struct linetable *l; + register int len; + register int i, item; + int line; + struct symtab_and_line value; + struct blockvector *bv; + + /* Info on best line seen so far, and where it starts, and its file. */ + + int best_line = 0; + CORE_ADDR best_pc = 0; + CORE_ADDR best_end = 0; + struct symtab *best_symtab = 0; + + /* Store here the first line number + of a file which contains the line at the smallest pc after PC. + If we don't find a line whose range contains PC, + we will use a line one less than this, + with a range from the start of that file to the first line's pc. */ + int alt_line = 0; + CORE_ADDR alt_pc = 0; + struct symtab *alt_symtab = 0; + + /* Info on best line seen in this file. */ + + int prev_line; + CORE_ADDR prev_pc; + + /* Info on first line of this file. */ + + int first_line; + CORE_ADDR first_pc; + + /* If this pc is not from the current frame, + it is the address of the end of a call instruction. + Quite likely that is the start of the following statement. + But what we want is the statement containing the instruction. + Fudge the pc to make sure we get that. */ + + if (notcurrent) pc -= 1; + + s = find_pc_symtab (pc); + if (s == 0) + { + value.symtab = 0; + value.line = 0; + value.pc = pc; + return value; + } + + bv = BLOCKVECTOR (s); + + /* Look at all the symtabs that share this blockvector. + They all have the same apriori range, that we found was right; + but they have different line tables. */ + + for (; s && BLOCKVECTOR (s) == bv; s = s->next) + { + /* Find the best line in this symtab. */ + l = LINETABLE (s); + len = l->nitems; + prev_line = -1; + first_line = -1; + for (i = 0; i < len; i++) + { + item = l->item[i]; + if (item < 0) + line = - item - 1; + else + { + line++; + if (first_line < 0) + { + first_line = line; + first_pc = item; + } + /* Return the last line that did not start after PC. */ + if (pc >= item) + { + prev_line = line; + prev_pc = item; + } + else + break; + } + } + + /* Is this file's best line closer than the best in the other files? + If so, record this file, and its best line, as best so far. */ + if (prev_line >= 0 && prev_pc > best_pc) + { + best_pc = prev_pc; + best_line = prev_line; + best_symtab = s; + if (i < len) + best_end = item; + else + best_end = 0; + } + /* Is this file's first line closer than the first lines of other files? + If so, record this file, and its first line, as best alternate. */ + if (first_line >= 0 && first_pc > pc + && (alt_pc == 0 || first_pc < alt_pc)) + { + alt_pc = first_pc; + alt_line = first_line; + alt_symtab = s; + } + } + if (best_symtab == 0) + { + value.symtab = alt_symtab; + value.line = alt_line - 1; + value.pc = BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0)); + value.end = alt_pc; + } + else + { + value.symtab = best_symtab; + value.line = best_line; + value.pc = best_pc; + value.end = (best_end ? best_end + : (alt_pc ? alt_pc + : BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0)))); + } + return value; +} + +/* Find the range of pc values in a line. + Store the starting pc of the line into *STARTPTR + and the ending pc (start of next line) into *ENDPTR. + Returns 1 to indicate success. + Returns 0 if could not find the specified line. */ + +int +find_line_pc_range (symtab, thisline, startptr, endptr) + struct symtab *symtab; + int thisline; + CORE_ADDR *startptr, *endptr; +{ + register struct linetable *l; + register int i, line, item; + int len; + register CORE_ADDR prev_pc; + CORE_ADDR last_pc; + + if (symtab == 0) + return 0; + + l = LINETABLE (symtab); + len = l->nitems; + prev_pc = -1; + for (i = 0; i < len; i++) + { + item = l->item[i]; + if (item < 0) + line = - item - 1; + else + { + line++; + /* As soon as we find a line following the specified one + we know the end pc and can return. */ + if (line > thisline) + { + /* If we have not seen an entry for the specified line, + assume that means the specified line has zero bytes. */ + *startptr = prev_pc == -1 ? item : prev_pc; + *endptr = item; + return 1; + } + /* If we see an entry for the specified line, + it gives the beginning. */ + if (line == thisline) + prev_pc = item; + last_pc = item; + } + } + if (prev_pc != -1) + { + /* If we found the specified line but no later line, it's file's last. + Its range is from line's pc to file's end pc. */ + *startptr = last_pc; + *endptr = BLOCK_END (BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), 0)); + return 1; + } + + return 0; +} + +/* Find the PC value for a given source file and line number. + Returns zero for invalid line number. + The source file is specified with a struct symtab. */ + +CORE_ADDR +find_line_pc (symtab, line) + struct symtab *symtab; + int line; +{ + register struct linetable *l; + register int len; + register int i; + register int item; + register int nextline = -1; + + if (line <= 0) + return 0; + + l = LINETABLE (symtab); + len = l->nitems; + for (i = 0; i < len; i++) + { + item = l->item[i]; + if (item < 0) + nextline = - item - 1; + else + { + nextline++; + if (line <= nextline) + return item; + } + } + return 0; +} + +int +find_pc_line_pc_range (pc, startptr, endptr) + CORE_ADDR pc; + CORE_ADDR *startptr, *endptr; +{ + struct symtab_and_line sal; + sal = find_pc_line (pc, 0); + *startptr = sal.pc; + *endptr = sal.end; + return sal.symtab != 0; +} + +/* Parse a string that specifies a line number. + Pass the address of a char * variable; that variable will be + advanced over the characters actually parsed. + + The string can be: + + LINENUM -- that line number in current file. PC returned is 0. + FILE:LINENUM -- that line in that file. PC returned is 0. + FUNCTION -- line number of openbrace of that function. + PC returned is the start of the function. + FILE:FUNCTION -- likewise, but prefer functions in that file. + *EXPR -- line in which address EXPR appears. + + FUNCTION may be an undebuggable function found in misc_function_vector. + + If the argument FUNFIRSTLINE is nonzero, we want the first line + of real code inside a function when a function is specified. + + DEFAULT_SYMTAB specifies the file to use if none is specified. + It defaults to current_source_symtab. + DEFAULT_LINE specifies the line number to use for relative + line numbers (that start with signs). Defaults to current_source_line. + + Note that it is possible to return zero for the symtab + if no file is validly specified. Callers must check that. + Also, the line number returned may be invalid. */ + +struct symtab_and_line +decode_line_1 (argptr, funfirstline, default_symtab, default_line) + char **argptr; + int funfirstline; + struct symtab *default_symtab; + int default_line; +{ + struct symtab_and_line value; + register char *p, *p1; + register struct symtab *s; + register struct symbol *sym; + register CORE_ADDR pc; + register int i; + char *copy; + + /* Defaults have defaults. */ + + if (default_symtab == 0) + { + default_symtab = current_source_symtab; + default_line = current_source_line; + } + + /* See if arg is *PC */ + + if (**argptr == '*') + { + (*argptr)++; + pc = parse_and_eval_address_1 (argptr); + value = find_pc_line (pc, 0); + value.pc = pc; + return value; + } + + /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */ + + s = 0; + + for (p = *argptr; *p; p++) + { + if (p[0] == ':' || p[0] == ' ' || p[0] == '\t') + break; + } + while (p[0] == ' ' || p[0] == '\t') p++; + + if (p[0] == ':') + { + /* Extract the file name. */ + p1 = p; + while (p != *argptr && p[-1] == ' ') --p; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = 0; + + /* Find that file's data. */ + s = lookup_symtab (copy); + if (s == 0) + { + if (symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + error ("No source file named %s.", copy); + } + + /* Discard the file name from the arg. */ + p = p1 + 1; + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + } + + /* S is specified file's symtab, or 0 if no file specified. + arg no longer contains the file name. */ + + /* Check whether arg is all digits (and sign) */ + + p = *argptr; + if (*p == '-' || *p == '+') p++; + while (*p >= '0' && *p <= '9') + p++; + + if (p != *argptr && (*p == 0 || *p == ' ' || *p == '\t' || *p == ',')) + { + /* We found a token consisting of all digits -- at least one digit. */ + enum sign {none, plus, minus} sign = none; + + if (**argptr == '+') + sign = plus, (*argptr)++; + else if (**argptr == '-') + sign = minus, (*argptr)++; + value.line = atoi (*argptr); + switch (sign) + { + case plus: + if (p == *argptr) + value.line = 5; + if (s == 0) + value.line = default_line + value.line; + break; + case minus: + if (p == *argptr) + value.line = 15; + if (s == 0) + value.line = default_line - value.line; + else + value.line = 1; + break; + } + + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + if (s == 0) + s = default_symtab; + value.symtab = s; + value.pc = 0; + return value; + } + + /* Arg token is not digits => try it as a function name + Find the next token (everything up to end or next whitespace). */ + p = *argptr; + while (*p && *p != ' ' && *p != '\t' && *p != ',') p++; + copy = (char *) alloca (p - *argptr + 1); + bcopy (*argptr, copy, p - *argptr); + copy[p - *argptr] = 0; + while (*p == ' ' || *p == '\t') p++; + *argptr = p; + + /* Look up that token as a function. + If file specified, use that file's per-file block to start with. */ + + sym = lookup_symbol (copy, s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1) : 0, + VAR_NAMESPACE); + + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + /* Arg is the name of a function */ + pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (pc); + value = find_pc_line (pc, 0); + value.pc = (value.end && value.pc != pc) ? value.end : pc; + return value; + } + + if (sym) + error ("%s is not a function.", copy); + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, copy)) + { + value.symtab = 0; + value.line = 0; + value.pc = misc_function_vector[i].address + FUNCTION_START_OFFSET; + if (funfirstline) + SKIP_PROLOGUE (value.pc); + return value; + } + + if (symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + error ("Function %s not defined.", copy); +} + +struct symtab_and_line +decode_line_spec (string, funfirstline) + char *string; + int funfirstline; +{ + struct symtab_and_line sal; + if (string == 0) + error ("Empty line specification."); + sal = decode_line_1 (&string, funfirstline, + current_source_symtab, current_source_line); + if (*string) + error ("Junk at end of line specification: %s", string); + return sal; +} + +static void +sources_info () +{ + register struct symtab *s; + register int column = 0; + + if (symtab_list == 0) + { + printf ("No symbol table is loaded.\n"); + return; + } + printf ("Source files for which symbol table is known:\n"); + for (s = symtab_list; s; s = s->next) + { + if (column != 0 && column + strlen (s->filename) >= 70) + { + printf ("\n"); + column = 0; + } + else if (column != 0) + { + printf (" "); + column++; + } + printf ("%s", s->filename); + column += strlen (s->filename); + if (s->next) + { + printf (","); + column++; + } + } + printf ("\n"); +} + +/* List all symbols (if REGEXP is 0) or all symbols matching REGEXP. + If CLASS is zero, list all symbols except functions and type names. + If CLASS is 1, list only functions. + If CLASS is 2, list only type names. */ + +#define MORE \ +{ print_count++; \ + if (print_count >= 21) \ + { printf ("--Type Return to print more--"); \ + print_count = 0; \ + fflush (stdout); \ + read_line (); } } + +static void +list_symbols (regexp, class) + char *regexp; + int class; +{ + register struct symtab *s; + register struct blockvector *bv; + struct blockvector *prev_bv = 0; + register struct block *b; + register int i, j; + register struct symbol *sym; + char *val = 0; + int found_in_file; + static char *classnames[] + = {"variable", "function", "type"}; + int print_count = 0; +#ifdef REGCMP + extern char *regcmp(), *regex(), *loc1; +#endif + + if (regexp) { +#ifdef REGCMP + val = regcmp(regexp, (char *)0); + if (val == 0) + error ("Invalid regexp: %s", regexp); +#else + if (val = (char *) re_comp (regexp)) + error ("Invalid regexp: %s", val); +#endif + } + + printf (regexp + ? "All %ss matching regular expression \"%s\":\n" + : "All defined %ss:\n", + classnames[class], + regexp); + + for (s = symtab_list; s; s = s->next) + { + found_in_file = 0; + bv = BLOCKVECTOR (s); + /* Often many files share a blockvector. + Scan each blockvector only once so that + we don't get every symbol many times. + It happens that the first symtab in the list + for any given blockvector is the main file. */ + if (bv != prev_bv) + for (i = 0; i < 2; i++) + { + b = BLOCKVECTOR_BLOCK (bv, i); + for (j = 0; j < BLOCK_NSYMS (b); j++) + { + QUIT; + sym = BLOCK_SYM (b, j); + if (regexp) { +#ifdef REGCMP + if (!regex(val, SYMBOL_NAME (sym))) + continue; +#else + if (!re_exec (SYMBOL_NAME (sym))) + continue; +#endif + } + if ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF + && SYMBOL_CLASS (sym) != LOC_BLOCK) + || (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK) + || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF)) + { + if (!found_in_file) + { + printf ("\nFile %s:\n", s->filename); + print_count += 2; + } + found_in_file = 1; + MORE; + if (class != 2 && i == 1) + printf ("static "); + if (class == 2 + && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE) + printf ("typedef "); + + type_print (SYMBOL_TYPE (sym), + (SYMBOL_CLASS (sym) == LOC_TYPEDEF + ? "" : SYMBOL_NAME (sym)), + stdout, 0); + if (class == 2 + && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE + && (TYPE_NAME ((SYMBOL_TYPE (sym))) == 0 + || 0 != strcmp (TYPE_NAME ((SYMBOL_TYPE (sym))), + SYMBOL_NAME (sym)))) + printf (" %s", SYMBOL_NAME (sym)); + printf (";\n"); + } + } + } + prev_bv = bv; + } +#ifdef REGCMP + if (val) + (void)free(val); +#endif +} + +static void +variables_info (regexp) + char *regexp; +{ + list_symbols (regexp, 0); +} + +static void +functions_info (regexp) + char *regexp; +{ + list_symbols (regexp, 1); +} + +static void +types_info (regexp) + char *regexp; +{ + list_symbols (regexp, 2); +} + +/* Initialize the standard C scalar types. */ + +static +struct type * +init_type (code, length, uns, name) + enum type_code code; + int length, uns; + char *name; +{ + register struct type *type; + + type = (struct type *) xmalloc (sizeof (struct type)); + bzero (type, sizeof *type); + TYPE_CODE (type) = code; + TYPE_LENGTH (type) = length; + TYPE_FLAGS (type) = uns ? TYPE_FLAG_UNSIGNED : 0; + TYPE_FLAGS (type) |= TYPE_FLAG_PERM; + TYPE_NFIELDS (type) = 0; + TYPE_NAME (type) = name; + + return type; +} + +static +initialize () +{ + add_info ("variables", variables_info, + "All global and static variable names, or those matching REGEXP."); + add_info ("functions", functions_info, + "All function names, or those matching REGEXP."); + add_info ("types", types_info, + "All types names, or those matching REGEXP."); + add_info ("sources", sources_info, + "Source files in the program."); + + obstack_init (symbol_obstack); + + builtin_type_void = init_type (TYPE_CODE_VOID, 0, 0, "void"); + + builtin_type_float = init_type (TYPE_CODE_FLT, sizeof (float), 0, "float"); + builtin_type_double = init_type (TYPE_CODE_FLT, sizeof (double), 0, "double"); + + builtin_type_char = init_type (TYPE_CODE_INT, sizeof (char), 0, "char"); + builtin_type_short = init_type (TYPE_CODE_INT, sizeof (short), 0, "short"); + builtin_type_long = init_type (TYPE_CODE_INT, sizeof (long), 0, "long"); + builtin_type_int = init_type (TYPE_CODE_INT, sizeof (int), 0, "int"); + + builtin_type_unsigned_char = init_type (TYPE_CODE_INT, sizeof (char), 1, "unsigned char"); + builtin_type_unsigned_short = init_type (TYPE_CODE_INT, sizeof (short), 1, "unsigned short"); + builtin_type_unsigned_long = init_type (TYPE_CODE_INT, sizeof (long), 1, "unsigned long"); + builtin_type_unsigned_int = init_type (TYPE_CODE_INT, sizeof (int), 1, "unsigned int"); +} + +END_FILE +@ + + +1.1 +log +@Initial revision +@ +text +@d29 4 +d933 1 +a933 1 + char *val; +d938 3 +d942 6 +a947 1 + if (regexp) +d950 2 +d976 10 +a985 2 + if ((regexp == 0 || re_exec (SYMBOL_NAME (sym))) + && ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF +d988 1 +a988 1 + || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF))) +d1019 4 +@ diff --git a/gdb/RCS/utils.c,v b/gdb/RCS/utils.c,v new file mode 100644 index 0000000..3f7a836 --- /dev/null +++ b/gdb/RCS/utils.c,v @@ -0,0 +1,461 @@ +head 1.2; +access ; +symbols RMS-has:1.2; +locks ; strict; +comment @ * @; + + +1.2 +date 88.01.26.05.11.12; author gnu; state Exp; +branches ; +next 1.1; + +1.1 +date 88.01.21.05.11.11; author gnu; state Exp; +branches ; +next ; + + +desc +@From RMS's development sources on wheaties, 20Jan88 +@ + + +1.2 +log +@Avoid using TIOCFLUSH if it is not defined. +@ +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 <sys/ioctl.h> +#include "defs.h" + +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); + } +} + +/* 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 TIOCFLUSH + ioctl (fileno (stdout), TIOCFLUSH, 0); +#endif + error ("Quit"); +} + +/* Control C comes here */ + +void +request_quit () +{ + quit_flag = 1; + 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; + } +} + +void +printchar (ch, stream) + unsigned char ch; + FILE *stream; +{ + 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 == '"' || c == '\'') + fputc ('\\', stream); + fputc (c, stream); + } +} +@ + + +1.1 +log +@Initial revision +@ +text +@d194 1 +d196 1 +@ |