From 35f5886ebb1a049991b3fee67a00ff10ffb1ad74 Mon Sep 17 00:00:00 2001 From: Fred Fish Date: Thu, 24 Oct 1991 11:28:54 +0000 Subject: [PATCH] New files for DWARF debugging format support, ELF object file support, SVR4 /proc (process file system) support, Amiga UNIX target and host defines, SVR4 target and host defines, and m68k hosts defines. --- gdb/ChangeLog | 13 + gdb/dwarfread.c | 3574 +++++++++++++++++++++++++++++++++++++++++++++++ gdb/elfread.c | 208 +++ gdb/procfs.c | 988 +++++++++++++ gdb/tm-amix.h | 56 + gdb/tm-svr4.h | 21 + gdb/xm-amix.h | 28 + gdb/xm-m68k.h | 22 + gdb/xm-svr4.h | 56 + 9 files changed, 4966 insertions(+) create mode 100644 gdb/dwarfread.c create mode 100644 gdb/elfread.c create mode 100644 gdb/procfs.c create mode 100644 gdb/tm-amix.h create mode 100755 gdb/tm-svr4.h create mode 100644 gdb/xm-amix.h create mode 100644 gdb/xm-m68k.h create mode 100755 gdb/xm-svr4.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 5d6ade0771..ab81f0ca57 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,18 @@ Thu Oct 24 01:32:51 1991 Fred Fish (fnf at cygnus.com) + * dwarfread.c: New file for DWARF debugging format support. + + * elfread.c: New file for ELF object file format support. + + * procfs.c: New file for SVR4 /proc (process file system) support. + + * tm-amix.h, xm-amix.h, tconfig/amix, xconfig/amix: New files for + Amiga UNIX support. + + * xm-svr4.h, tm-svr4.h: New files for SVR4 support. + + * xm-m68k.h: New file for host machines with m68k cpu. + * Makefile.in: Add elfread.c and dwarfread.c to SFILES_MAINDIR. Add elfread.o and dwarfread.o to OBS. diff --git a/gdb/dwarfread.c b/gdb/dwarfread.c new file mode 100644 index 0000000000..6bb6c16fc2 --- /dev/null +++ b/gdb/dwarfread.c @@ -0,0 +1,3574 @@ +/* DWARF debugging format support for GDB. + Copyright (C) 1991 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support, portions based on dbxread.c, + mipsread.c, coffread.c, and dwarfread.c from a Data General SVR4 gdb port. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + +FIXME: Figure out how to get the frame pointer register number in the +execution environment of the target. Remove R_FP kludge + +FIXME: Add generation of dependencies list to partial symtab code. + +FIXME: Currently we ignore host/target byte ordering and integer size +differences. Should remap data from external form to an internal form +before trying to use it. + +FIXME: Resolve minor differences between what information we put in the +partial symbol table and what dbxread puts in. For example, we don't yet +put enum constants there. And dbxread seems to invent a lot of typedefs +we never see. Use the new printpsym command to see the partial symbol table +contents. + +FIXME: Change forward declarations of static functions to allow for compilers +without prototypes. + +FIXME: Figure out a better way to tell gdb (all the debug reading routines) +the names of the gccX_compiled flags. + +FIXME: Figure out a better way to tell gdb about the name of the function +contain the user's entry point (I.E. main()) + +FIXME: The current DWARF specification has a very strong bias towards +machines with 32-bit integers, as it assumes that many attributes of the +program (such as an address) will fit in such an integer. There are many +references in the spec to things that are 2, 4, or 8 bytes long. Given that +we will probably run into problems on machines where some of these assumptions +are invalid (64-bit ints for example), we don't bother at this time to try to +make this code more flexible and just use shorts, ints, and longs (and their +sizes) where it seems appropriate. I.E. we use a short int to hold DWARF +tags, and assume that the tag size in the file is the same as sizeof(short). + +FIXME: Figure out how to get the name of the symbol indicating that a module +has been compiled with gcc (gcc_compiledXX) in a more portable way than +hardcoding it into the object file readers. + +FIXME: See other FIXME's and "ifdef 0" scattered throughout the code for +other things to work on, if you get bored. :-) + +*/ + +#include +#include +#include + +#include "defs.h" +#include "param.h" +#include "bfd.h" +#include "symtab.h" +#include "symfile.h" +#include "dwarf.h" +#include "ansidecl.h" + +#ifdef MAINTENANCE /* Define to 1 to compile in some maintenance stuff */ +#define SQUAWK(stuff) dwarfwarn stuff +#else +#define SQUAWK(stuff) +#endif + +#ifndef R_FP /* FIXME */ +#define R_FP 14 /* Kludge to get frame pointer register number */ +#endif + +typedef unsigned int DIEREF; /* Reference to a DIE */ + +#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled%" /* FIXME */ +#define GCC2_COMPILED_FLAG_SYMBOL "gcc2_compiled%" /* FIXME */ + +#define STREQ(a,b) (strcmp(a,b)==0) + +extern CORE_ADDR entry_point; /* Process entry point */ +extern CORE_ADDR startup_file_start; /* From blockframe.c */ +extern CORE_ADDR startup_file_end; /* From blockframe.c */ +extern CORE_ADDR entry_scope_lowpc; /* From blockframe.c */ +extern CORE_ADDR entry_scope_highpc; /* From blockframc.c */ +extern CORE_ADDR main_scope_lowpc; /* From blockframe.c */ +extern CORE_ADDR main_scope_highpc; /* From blockframc.c */ +extern int info_verbose; /* From main.c; nonzero => verbose */ + + +/* The DWARF debugging information consists of two major pieces, + one is a block of DWARF Information Entries (DIE's) and the other + is a line number table. The "struct dieinfo" structure contains + the information for a single DIE, the one currently being processed. + + In order to make it easier to randomly access the attribute fields + of the current DIE, which are specifically unordered within the DIE + each DIE is scanned and an instance of the "struct dieinfo" + structure is initialized. + + Initialization is done in two levels. The first, done by basicdieinfo(), + just initializes those fields that are vital to deciding whether or not + to use this DIE, how to skip past it, etc. The second, done by the + function completedieinfo(), fills in the rest of the information. + + Attributes which have block forms are not interpreted at the time + the DIE is scanned, instead we just save pointers to the start + of their value fields. + + Some fields have a flag _p that is set when the value of the + field is valid (I.E. we found a matching attribute in the DIE). Since + we may want to test for the presence of some attributes in the DIE, + such as AT_is_external, without restricting the values of the field, + we need someway to note that we found such an attribute. + + */ + +typedef char BLOCK; + +struct dieinfo { + char * die; /* Pointer to the raw DIE data */ + long dielength; /* Length of the raw DIE data */ + DIEREF dieref; /* Offset of this DIE */ + short dietag; /* Tag for this DIE */ + long at_padding; + long at_sibling; + BLOCK * at_location; + char * at_name; + unsigned short at_fund_type; + BLOCK * at_mod_fund_type; + long at_user_def_type; + BLOCK * at_mod_u_d_type; + short at_ordering; + BLOCK * at_subscr_data; + long at_byte_size; + short at_bit_offset; + long at_bit_size; + BLOCK * at_deriv_list; + BLOCK * at_element_list; + long at_stmt_list; + long at_low_pc; + long at_high_pc; + long at_language; + long at_member; + long at_discr; + BLOCK * at_discr_value; + short at_visibility; + long at_import; + BLOCK * at_string_length; + char * at_comp_dir; + char * at_producer; + long at_loclist; + long at_frame_base; + short at_incomplete; + long at_start_scope; + long at_stride_size; + long at_src_info; + short at_prototyped; + BLOCK * at_const_data; + short at_is_external; + unsigned int at_is_external_p:1; + unsigned int at_stmt_list_p:1; +}; + +static int diecount; /* Approximate count of dies for compilation unit */ +static struct dieinfo *curdie; /* For warnings and such */ + +static char *dbbase; /* Base pointer to dwarf info */ +static int dbroff; /* Relative offset from start of .debug section */ +static char *lnbase; /* Base pointer to line section */ +static int isreg; /* Kludge to identify register variables */ + +static CORE_ADDR baseaddr; /* Add to each symbol value */ + +/* Each partial symbol table entry contains a pointer to private data for the + read_symtab() function to use when expanding a partial symbol table entry + to a full symbol table entry. For DWARF debugging info, this data is + contained in the following structure and macros are provided for easy + access to the members given a pointer to a partial symbol table entry. + + dbfoff Always the absolute file offset to the start of the ".debug" + section for the file containing the DIE's being accessed. + + dbroff Relative offset from the start of the ".debug" access to the + first DIE to be accessed. When building the partial symbol + table, this value will be zero since we are accessing the + entire ".debug" section. When expanding a partial symbol + table entry, this value will be the offset to the first + DIE for the compilation unit containing the symbol that + triggers the expansion. + + dblength The size of the chunk of DIE's being examined, in bytes. + + lnfoff The absolute file offset to the line table fragment. Ignored + when building partial symbol tables, but used when expanding + them, and contains the absolute file offset to the fragment + of the ".line" section containing the line numbers for the + current compilation unit. + */ + +struct dwfinfo { + int dbfoff; /* Absolute file offset to start of .debug section */ + int dbroff; /* Relative offset from start of .debug section */ + int dblength; /* Size of the chunk of DIE's being examined */ + int lnfoff; /* Absolute file offset to line table fragment */ +}; + +#define DBFOFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->dbfoff) +#define DBROFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->dbroff) +#define DBLENGTH(p) (((struct dwfinfo *)((p)->read_symtab_private))->dblength) +#define LNFOFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->lnfoff) + +/* Record the symbols defined for each context in a linked list. We don't + create a struct block for the context until we know how long to make it. + Global symbols for each file are maintained in the global_symbols list. */ + +struct pending_symbol { + struct pending_symbol *next; /* Next pending symbol */ + struct symbol *symbol; /* The actual symbol */ +}; + +static struct pending_symbol *global_symbols; /* global funcs and vars */ +static struct block *global_symbol_block; + +/* Line number entries are read into a dynamically expandable vector before + being added to the symbol table section. Once we know how many there are + we can add them. */ + +static struct linetable *line_vector; /* Vector of line numbers. */ +static int line_vector_index; /* Index of next entry. */ +static int line_vector_length; /* Current allocation limit */ + +/* Scope information is kept in a scope tree, one node per scope. Each time + a new scope is started, a child node is created under the current node + and set to the current scope. Each time a scope is closed, the current + scope moves back up the tree to the parent of the current scope. + + Each scope contains a pointer to the list of symbols defined in the scope, + a pointer to the block vector for the scope, a pointer to the symbol + that names the scope (if any), and the range of PC values that mark + the start and end of the scope. */ + +struct scopenode { + struct scopenode *parent; + struct scopenode *child; + struct scopenode *sibling; + struct pending_symbol *symbols; + struct block *block; + struct symbol *namesym; + CORE_ADDR lowpc; + CORE_ADDR highpc; +}; + +static struct scopenode *scopetree; +static struct scopenode *scope; + +/* DIES which have user defined types or modified user defined types refer to + other DIES for the type information. Thus we need to associate the offset + of a DIE for a user defined type with a pointer to the type information. + + Originally this was done using a simple but expensive algorithm, with an + array of unsorted structures, each containing an offset/type-pointer pair. + This array was scanned linearly each time a lookup was done. The result + was that gdb was spending over half it's startup time munging through this + array of pointers looking for a structure that had the right offset member. + + The second attempt used the same array of structures, but the array was + sorted using qsort each time a new offset/type was recorded, and a binary + search was used to find the type pointer for a given DIE offset. This was + even slower, due to the overhead of sorting the array each time a new + offset/type pair was entered. + + The third attempt uses a fixed size array of type pointers, indexed by a + value derived from the DIE offset. Since the minimum DIE size is 4 bytes, + we can divide any DIE offset by 4 to obtain a unique index into this fixed + size array. Since each element is a 4 byte pointer, it takes exactly as + much memory to hold this array as to hold the DWARF info for a given + compilation unit. But it gets freed as soon as we are done with it. */ + +static struct type **utypes; /* Pointer to array of user type pointers */ +static int numutypes; /* Max number of user type pointers */ + +/* Forward declarations of static functions so we don't have to worry + about ordering within this file. The EXFUN macro may be slightly + misleading. Should probably be called DCLFUN instead, or something + more intuitive, since it can be used for both static and external + definitions. */ + +static void +EXFUN (dwarfwarn, (char *fmt DOTS)); + +static void +EXFUN (scan_partial_symbols, (char *thisdie AND char *enddie)); + +static void +EXFUN (scan_compilation_units, + (char *filename AND CORE_ADDR addr AND char *thisdie AND char *enddie + AND unsigned int dbfoff AND unsigned int lnoffset)); + +static struct partial_symtab * +EXFUN(start_psymtab, (char *symfile_name AND CORE_ADDR addr + AND char *filename AND CORE_ADDR textlow + AND CORE_ADDR texthigh AND int dbfoff + AND int curoff AND int culength AND int lnfoff + AND struct partial_symbol *global_syms + AND struct partial_symbol *static_syms)); +static void +EXFUN(add_partial_symbol, (struct dieinfo *dip)); + +static void +EXFUN(add_psymbol_to_list, + (struct psymbol_allocation_list *listp AND char *name + AND enum namespace space AND enum address_class class + AND CORE_ADDR value)); + +static void +EXFUN(init_psymbol_list, (int total_symbols)); + +static void +EXFUN(basicdieinfo, (struct dieinfo *dip AND char *diep)); + +static void +EXFUN(completedieinfo, (struct dieinfo *dip)); + +static void +EXFUN(dwarf_psymtab_to_symtab, (struct partial_symtab *pst)); + +static void +EXFUN(psymtab_to_symtab_1, (struct partial_symtab *pst AND int desc )); + +static struct symtab * +EXFUN(read_ofile_symtab, (struct partial_symtab *pst AND int desc)); + +static void +EXFUN(process_dies, (char *thisdie AND char *enddie)); + +static void +EXFUN(read_lexical_block_scope, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static void +EXFUN(read_structure_scope, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static struct type * +EXFUN(decode_array_element_type, (char *scan AND char *end)); + +static struct type * +EXFUN(decode_subscr_data, (char *scan AND char *end)); + +static void +EXFUN(read_array_type, (struct dieinfo *dip)); + +static void +EXFUN(read_subroutine_type, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static void +EXFUN(read_enumeration, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static struct type * +EXFUN(struct_type, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static struct type * +EXFUN(enum_type, (struct dieinfo *dip)); + +static void +EXFUN(read_func_scope, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static void +EXFUN(read_file_scope, + (struct dieinfo *dip AND char *thisdie AND char *enddie)); + +static void +EXFUN(start_symtab, (void)); + +static void +EXFUN(end_symtab, (char *filename AND long language)); + +static int +EXFUN(scopecount, (struct scopenode *node)); + +static void +EXFUN(openscope, + (struct symbol *namesym AND CORE_ADDR lowpc AND CORE_ADDR highpc)); + +static void +EXFUN(freescope, (struct scopenode *node)); + +static struct block * +EXFUN(buildblock, (struct pending_symbol *syms)); + +static void +EXFUN(closescope, (void)); + +static void +EXFUN(record_line, (int line AND CORE_ADDR pc)); + +static void +EXFUN(decode_line_numbers, (char *linetable)); + +static struct type * +EXFUN(decode_die_type, (struct dieinfo *dip)); + +static struct type * +EXFUN(decode_mod_fund_type, (char *typedata)); + +static struct type * +EXFUN(decode_mod_u_d_type, (char *typedata)); + +static struct type * +EXFUN(decode_modified_type, + (unsigned char *modifiers AND unsigned short modcount AND int mtype)); + +static struct type * +EXFUN(decode_fund_type, (unsigned short fundtype)); + +static char * +EXFUN(create_name, (char *name AND struct obstack *obstackp)); + +static void +EXFUN(add_symbol_to_list, + (struct symbol *symbol AND struct pending_symbol **listhead)); + +static struct block ** +EXFUN(gatherblocks, (struct block **dest AND struct scopenode *node)); + +static struct blockvector * +EXFUN(make_blockvector, (void)); + +static struct type * +EXFUN(lookup_utype, (DIEREF dieref)); + +static struct type * +EXFUN(alloc_utype, (DIEREF dieref AND struct type *usetype)); + +static struct symbol * +EXFUN(new_symbol, (struct dieinfo *dip)); + +static int +EXFUN(locval, (char *loc)); + +static void +EXFUN(record_misc_function, (char *name AND CORE_ADDR address)); + +static int +EXFUN(compare_psymbols, + (struct partial_symbol *s1 AND struct partial_symbol *s2)); + + +/* + +GLOBAL FUNCTION + + dwarf_build_psymtabs -- build partial symtabs from DWARF debug info + +SYNOPSIS + + void dwarf_build_psymtabs (int desc, char *filename, CORE_ADDR addr, + int mainline, unsigned int dbfoff, unsigned int dbsize, + unsigned int lnoffset, unsigned int lnsize) + +DESCRIPTION + + This function is called upon to build partial symtabs from files + containing DIE's (Dwarf Information Entries) and DWARF line numbers. + + It is passed a file descriptor for an open file containing the DIES + and line number information, the corresponding filename for that + file, a base address for relocating the symbols, a flag indicating + whether or not this debugging information is from a "main symbol + table" rather than a shared library or dynamically linked file, + and file offset/size pairs for the DIE information and line number + information. + +RETURNS + + No return value. + + */ + +void +DEFUN(dwarf_build_psymtabs, + (desc, filename, addr, mainline, dbfoff, dbsize, lnoffset, lnsize), + int desc AND + char *filename AND + CORE_ADDR addr AND + int mainline AND + unsigned int dbfoff AND + unsigned int dbsize AND + unsigned int lnoffset AND + unsigned int lnsize) +{ + struct cleanup *back_to; + + dbbase = xmalloc (dbsize); + dbroff = 0; + if ((lseek (desc, dbfoff, 0) != dbfoff) || + (read (desc, dbbase, dbsize) != dbsize)) + { + free (dbbase); + error ("can't read DWARF data from '%s'", filename); + } + back_to = make_cleanup (free, dbbase); + + /* If we are reinitializing, or if we have never loaded syms yet, init. + Since we have no idea how many DIES we are looking at, we just guess + some arbitrary value. */ + + if (mainline || global_psymbols.size == 0 || static_psymbols.size == 0) + { + init_psymbol_list (1024); + } + + init_misc_bunches (); + make_cleanup (discard_misc_bunches, 0); + + /* Follow the compilation unit sibling chain, building a partial symbol + table entry for each one. Save enough information about each compilation + unit to locate the full DWARF information later. */ + + scan_compilation_units (filename, addr, dbbase, dbbase + dbsize, + dbfoff, lnoffset); + + /* Go over the miscellaneous functions and install them in the miscellaneous + function vector. */ + + condense_misc_bunches (!mainline); + do_cleanups (back_to); +} + + +/* + +LOCAL FUNCTION + + record_misc_function -- add entry to miscellaneous function vector + +SYNOPSIS + + static void record_misc_function (char *name, CORE_ADDR address) + +DESCRIPTION + + Given a pointer to the name of a symbol that should be added to the + miscellaneous function vector, and the address associated with that + symbol, records this information for later use in building the + miscellaneous function vector. + +NOTES + + FIXME: For now we just use mf_text as the type. This should be + fixed. + */ + +static void +DEFUN(record_misc_function, (name, address), char *name AND CORE_ADDR address) +{ + prim_record_misc_function (obsavestring (name, strlen (name)), address, + mf_text); +} + +/* + +LOCAL FUNCTION + + dwarfwarn -- issue a DWARF related warning + +DESCRIPTION + + Issue warnings about DWARF related things that aren't serious enough + to warrant aborting with an error, but should not be ignored either. + This includes things like detectable corruption in DIE's, missing + DIE's, unimplemented features, etc. + + In general, running across tags or attributes that we don't recognize + is not considered to be a problem and we should not issue warnings + about such. + +NOTES + + We mostly follow the example of the error() routine, but without + returning to command level. It is arguable about whether warnings + should be issued at all, and if so, where they should go (stdout or + stderr). + + We assume that curdie is valid and contains at least the basic + information for the DIE where the problem was noticed. +*/ + +static void +DEFUN(dwarfwarn, (fmt), char *fmt DOTS) +{ + va_list ap; + + va_start (ap, fmt); + warning_setup (); + fprintf (stderr, "DWARF warning (ref 0x%x): ", curdie -> dieref); + if (curdie -> at_name) + { + fprintf (stderr, "'%s': ", curdie -> at_name); + } + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + fflush (stderr); + va_end (ap); +} + +/* + +LOCAL FUNCTION + + compare_psymbols -- compare two partial symbols by name + +DESCRIPTION + + Given pointer to two partial symbol table entries, compare + them by name and return -N, 0, or +N (ala strcmp). Typically + used by sorting routines like qsort(). + +NOTES + + This is a copy from dbxread.c. It should be moved to a generic + gdb file and made available for all psymtab builders (FIXME). + + Does direct compare of first two characters before punting + and passing to strcmp for longer compares. Note that the + original version had a bug whereby two null strings or two + identically named one character strings would return the + comparison of memory following the null byte. + + */ + +static int +DEFUN(compare_psymbols, (s1, s2), + struct partial_symbol *s1 AND + struct partial_symbol *s2) +{ + register char *st1 = SYMBOL_NAME (s1); + register char *st2 = SYMBOL_NAME (s2); + + if ((st1[0] - st2[0]) || !st1[0]) + { + return (st1[0] - st2[0]); + } + else if ((st1[1] - st2[1]) || !st1[1]) + { + return (st1[1] - st2[1]); + } + else + { + return (strcmp (st1 + 2, st2 + 2)); + } +} + +/* + +LOCAL FUNCTION + + read_lexical_block_scope -- process all dies in a lexical block + +SYNOPSIS + + static void read_lexical_block_scope (struct dieinfo *dip, + char *thisdie, char *enddie) + +DESCRIPTION + + Process all the DIES contained within a lexical block scope. + Start a new scope, process the dies, and then close the scope. + + */ + +static void +DEFUN(read_lexical_block_scope, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + openscope (NULL, dip -> at_low_pc, dip -> at_high_pc); + process_dies (thisdie + dip -> dielength, enddie); + closescope (); +} + +/* + +LOCAL FUNCTION + + lookup_utype -- look up a user defined type from die reference + +SYNOPSIS + + static type *lookup_utype (DIEREF dieref) + +DESCRIPTION + + Given a DIE reference, lookup the user defined type associated with + that DIE, if it has been registered already. If not registered, then + return NULL. Alloc_utype() can be called to register an empty + type for this reference, which will be filled in later when the + actual referenced DIE is processed. + */ + +static struct type * +DEFUN(lookup_utype, (dieref), DIEREF dieref) +{ + struct type *type = NULL; + int utypeidx; + + utypeidx = (dieref - dbroff) / 4; + if ((utypeidx < 0) || (utypeidx >= numutypes)) + { + dwarfwarn ("reference to DIE (0x%x) outside compilation unit", dieref); + } + else + { + type = *(utypes + utypeidx); + } + return (type); +} + + +/* + +LOCAL FUNCTION + + alloc_utype -- add a user defined type for die reference + +SYNOPSIS + + static type *alloc_utype (DIEREF dieref, struct type *utypep) + +DESCRIPTION + + Given a die reference DIEREF, and a possible pointer to a user + defined type UTYPEP, register that this reference has a user + defined type and either use the specified type in UTYPEP or + make a new empty type that will be filled in later. + + We should only be called after calling lookup_utype() to verify that + there is not currently a type registered for DIEREF. + */ + +static struct type * +DEFUN(alloc_utype, (dieref, utypep), + DIEREF dieref AND + struct type *utypep) +{ + struct type **typep; + int utypeidx; + + utypeidx = (dieref - dbroff) / 4; + typep = utypes + utypeidx; + if ((utypeidx < 0) || (utypeidx >= numutypes)) + { + utypep = builtin_type_int; + dwarfwarn ("reference to DIE (0x%x) outside compilation unit", dieref); + } + else if (*typep != NULL) + { + utypep = *typep; + SQUAWK (("internal error: dup user type allocation")); + } + else + { + if (utypep == NULL) + { + utypep = (struct type *) + obstack_alloc (symbol_obstack, sizeof (struct type)); + (void) memset (utypep, 0, sizeof (struct type)); + } + *typep = utypep; + } + return (utypep); +} + +/* + +LOCAL FUNCTION + + decode_die_type -- return a type for a specified die + +SYNOPSIS + + static struct type *decode_die_type (struct dieinfo *dip) + +DESCRIPTION + + Given a pointer to a die information structure DIP, decode the + type of the die and return a pointer to the decoded type. All + dies without specific types default to type int. + */ + +static struct type * +DEFUN(decode_die_type, (dip), struct dieinfo *dip) +{ + struct type *type = NULL; + + if (dip -> at_fund_type != 0) + { + type = decode_fund_type (dip -> at_fund_type); + } + else if (dip -> at_mod_fund_type != NULL) + { + type = decode_mod_fund_type (dip -> at_mod_fund_type); + } + else if (dip -> at_user_def_type) + { + if ((type = lookup_utype (dip -> at_user_def_type)) == NULL) + { + type = alloc_utype (dip -> at_user_def_type, NULL); + } + } + else if (dip -> at_mod_u_d_type) + { + type = decode_mod_u_d_type (dip -> at_mod_u_d_type); + } + else + { + type = builtin_type_int; + } + return (type); +} + +/* + +LOCAL FUNCTION + + struct_type -- compute and return the type for a struct or union + +SYNOPSIS + + static struct type *struct_type (struct dieinfo *dip, char *thisdie, + char *enddie) + +DESCRIPTION + + Given pointer to a die information structure for a die which + defines a union or structure, and pointers to the raw die data + that define the range of dies which define the members, compute + and return the user defined type for the structure or union. + */ + +static struct type * +DEFUN(struct_type, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct type *type; + struct nextfield { + struct nextfield *next; + struct field field; + }; + struct nextfield *list = NULL; + struct nextfield *new; + int nfields = 0; + int n; + char *tpart1; + char *tpart2; + char *tpart3; + struct dieinfo mbr; + + if ((type = lookup_utype (dip -> dieref)) == NULL) + { + type = alloc_utype (dip -> dieref, NULL); + } + switch (dip -> dietag) + { + case TAG_structure_type: + TYPE_CODE (type) = TYPE_CODE_STRUCT; + tpart1 = "struct "; + break; + case TAG_union_type: + TYPE_CODE (type) = TYPE_CODE_UNION; + tpart1 = "union "; + break; + default: + tpart1 = ""; + SQUAWK (("missing structure or union tag")); + TYPE_CODE (type) = TYPE_CODE_UNDEF; + break; + } + if (dip -> at_name == NULL) + { + tpart2 = "{...}"; + } + else + { + tpart2 = dip -> at_name; + } + if (dip -> at_byte_size == 0) + { + tpart3 = " "; + } else { + TYPE_LENGTH (type) = dip -> at_byte_size; + tpart3 = ""; + } + TYPE_NAME (type) = concat (tpart1, tpart2, tpart3); + thisdie += dip -> dielength; + while (thisdie < enddie) + { + basicdieinfo (&mbr, thisdie); + completedieinfo (&mbr); + if (mbr.dielength <= sizeof (long)) + { + break; + } + switch (mbr.dietag) + { + case TAG_member: + /* Get space to record the next field's data. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new -> next = list; + list = new; + /* Save the data. */ + list -> field.name = savestring (mbr.at_name, strlen (mbr.at_name)); + list -> field.type = decode_die_type (&mbr); + list -> field.bitpos = 8 * locval (mbr.at_location); + list -> field.bitsize = 0; + nfields++; + break; + default: + SQUAWK (("bad member of '%s'", TYPE_NAME (type))); + break; + } + thisdie += mbr.dielength; + } + /* Now create the vector of fields, and record how big it is. */ + TYPE_NFIELDS (type) = nfields; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (symbol_obstack, sizeof (struct field) * nfields); + /* Copy the saved-up fields into the field vector. */ + for (n = nfields; list; list = list -> next) + { + TYPE_FIELD (type, --n) = list -> field; + } + return (type); +} + +/* + +LOCAL FUNCTION + + read_structure_scope -- process all dies within struct or union + +SYNOPSIS + + static void read_structure_scope (struct dieinfo *dip, + char *thisdie, char *enddie) + +DESCRIPTION + + Called when we find the DIE that starts a structure or union + scope (definition) to process all dies that define the members + of the structure or union. DIP is a pointer to the die info + struct for the DIE that names the structure or union. + +NOTES + + Note that we need to call struct_type regardless of whether or not + we have a symbol, since we might have a structure or union without + a tag name (thus no symbol for the tagname). + */ + +static void +DEFUN(read_structure_scope, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct type *type; + struct symbol *sym; + + type = struct_type (dip, thisdie, enddie); + if ((sym = new_symbol (dip)) != NULL) + { + SYMBOL_TYPE (sym) = type; + } +} + +/* + +LOCAL FUNCTION + + decode_array_element_type -- decode type of the array elements + +SYNOPSIS + + static struct type *decode_array_element_type (char *scan, char *end) + +DESCRIPTION + + As the last step in decoding the array subscript information for an + array DIE, we need to decode the type of the array elements. We are + passed a pointer to this last part of the subscript information and + must return the appropriate type. If the type attribute is not + recognized, just warn about the problem and return type int. + */ + +static struct type * +DEFUN(decode_array_element_type, (scan, end), char *scan AND char *end) +{ + struct type *typep; + short attribute; + DIEREF dieref; + unsigned short fundtype; + + (void) memcpy (&attribute, scan, sizeof (short)); + scan += sizeof (short); + switch (attribute) + { + case AT_fund_type: + (void) memcpy (&fundtype, scan, sizeof (short)); + typep = decode_fund_type (fundtype); + break; + case AT_mod_fund_type: + typep = decode_mod_fund_type (scan); + break; + case AT_user_def_type: + (void) memcpy (&dieref, scan, sizeof (DIEREF)); + if ((typep = lookup_utype (dieref)) == NULL) + { + typep = alloc_utype (dieref, NULL); + } + break; + case AT_mod_u_d_type: + typep = decode_mod_u_d_type (scan); + break; + default: + SQUAWK (("bad array element type attribute 0x%x", attribute)); + typep = builtin_type_int; + break; + } + return (typep); +} + +/* + +LOCAL FUNCTION + + decode_subscr_data -- decode array subscript and element type data + +SYNOPSIS + + static struct type *decode_subscr_data (char *scan, char *end) + +DESCRIPTION + + The array subscripts and the data type of the elements of an + array are described by a list of data items, stored as a block + of contiguous bytes. There is a data item describing each array + dimension, and a final data item describing the element type. + The data items are ordered the same as their appearance in the + source (I.E. leftmost dimension first, next to leftmost second, + etc). + + We are passed a pointer to the start of the block of bytes + containing the data items, and a pointer to the first byte past + the data. This function decodes the data and returns a type. + +BUGS + FIXME: This code only implements the forms currently used + by the AT&T and GNU C compilers. + + The end pointer is supplied for error checking, maybe we should + use it for that... + */ + +static struct type * +DEFUN(decode_subscr_data, (scan, end), char *scan AND char *end) +{ + struct type *typep = NULL; + struct type *nexttype; + int format; + short fundtype; + long lowbound; + long highbound; + + format = *scan++; + switch (format) + { + case FMT_ET: + typep = decode_array_element_type (scan, end); + break; + case FMT_FT_C_C: + (void) memcpy (&fundtype, scan, sizeof (short)); + scan += sizeof (short); + if (fundtype != FT_integer && fundtype != FT_signed_integer + && fundtype != FT_unsigned_integer) + { + SQUAWK (("array subscripts must be integral types, not type 0x%x", + fundtype)); + } + else + { + (void) memcpy (&lowbound, scan, sizeof (long)); + scan += sizeof (long); + (void) memcpy (&highbound, scan, sizeof (long)); + scan += sizeof (long); + nexttype = decode_subscr_data (scan, end); + if (nexttype != NULL) + { + typep = (struct type *) + obstack_alloc (symbol_obstack, sizeof (struct type)); + (void) memset (typep, 0, sizeof (struct type)); + TYPE_CODE (typep) = TYPE_CODE_ARRAY; + TYPE_LENGTH (typep) = TYPE_LENGTH (nexttype); + TYPE_LENGTH (typep) *= lowbound + highbound + 1; + TYPE_TARGET_TYPE (typep) = nexttype; + } + } + break; + case FMT_FT_C_X: + case FMT_FT_X_C: + case FMT_FT_X_X: + case FMT_UT_C_C: + case FMT_UT_C_X: + case FMT_UT_X_C: + case FMT_UT_X_X: + SQUAWK (("array subscript format 0x%x not handled yet", format)); + break; + default: + SQUAWK (("unknown array subscript format %x", format)); + break; + } + return (typep); +} + +/* + +LOCAL FUNCTION + + read_array_type -- read TAG_array_type DIE + +SYNOPSIS + + static void read_array_type (struct dieinfo *dip) + +DESCRIPTION + + Extract all information from a TAG_array_type DIE and add to + the user defined type vector. + */ + +static void +DEFUN(read_array_type, (dip), struct dieinfo *dip) +{ + struct type *type; + char *sub; + char *subend; + short temp; + + if (dip -> at_ordering != ORD_row_major) + { + /* FIXME: Can gdb even handle column major arrays? */ + SQUAWK (("array not row major; not handled correctly")); + } + if ((sub = dip -> at_subscr_data) != NULL) + { + (void) memcpy (&temp, sub, sizeof (short)); + subend = sub + sizeof (short) + temp; + sub += sizeof (short); + type = decode_subscr_data (sub, subend); + if (type == NULL) + { + type = alloc_utype (dip -> dieref, NULL); + TYPE_CODE (type) = TYPE_CODE_ARRAY; + TYPE_TARGET_TYPE (type) = builtin_type_int; + TYPE_LENGTH (type) = 1 * TYPE_LENGTH (TYPE_TARGET_TYPE (type)); + } + else + { + type = alloc_utype (dip -> dieref, type); + } + } +} + +/* + +LOCAL FUNCTION + + read_subroutine_type -- process TAG_subroutine_type dies + +SYNOPSIS + + static void read_subroutine_type (struct dieinfo *dip, char thisdie, + char *enddie) + +DESCRIPTION + + Handle DIES due to C code like: + + struct foo { + int (*funcp)(int a, long l); (Generates TAG_subroutine_type DIE) + int b; + }; + +NOTES + + The parameter DIES are currently ignored. See if gdb has a way to + include this info in it's type system, and decode them if so. Is + this what the type structure's "arg_types" field is for? (FIXME) + */ + +static void +DEFUN(read_subroutine_type, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct type *type; + + type = decode_die_type (dip); + type = lookup_function_type (type); + type = alloc_utype (dip -> dieref, type); +} + +/* + +LOCAL FUNCTION + + read_enumeration -- process dies which define an enumeration + +SYNOPSIS + + static void read_enumeration (struct dieinfo *dip, char *thisdie, + char *enddie) + +DESCRIPTION + + Given a pointer to a die which begins an enumeration, process all + the dies that define the members of the enumeration. + +NOTES + + Note that we need to call enum_type regardless of whether or not we + have a symbol, since we might have an enum without a tag name (thus + no symbol for the tagname). + */ + +static void +DEFUN(read_enumeration, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct type *type; + struct symbol *sym; + + type = enum_type (dip); + if ((sym = new_symbol (dip)) != NULL) + { + SYMBOL_TYPE (sym) = type; + } +} + +/* + +LOCAL FUNCTION + + enum_type -- decode and return a type for an enumeration + +SYNOPSIS + + static type *enum_type (struct dieinfo *dip) + +DESCRIPTION + + Given a pointer to a die information structure for the die which + starts an enumeration, process all the dies that define the members + of the enumeration and return a type pointer for the enumeration. + */ + +static struct type * +DEFUN(enum_type, (dip), struct dieinfo *dip) +{ + struct type *type; + struct nextfield { + struct nextfield *next; + struct field field; + }; + struct nextfield *list = NULL; + struct nextfield *new; + int nfields = 0; + int n; + char *tpart1; + char *tpart2; + char *tpart3; + char *scan; + char *listend; + long temp; + + if ((type = lookup_utype (dip -> dieref)) == NULL) + { + type = alloc_utype (dip -> dieref, NULL); + } + TYPE_CODE (type) = TYPE_CODE_ENUM; + tpart1 = "enum "; + if (dip -> at_name == NULL) + { + tpart2 = "{...}"; + } else { + tpart2 = dip -> at_name; + } + if (dip -> at_byte_size == 0) + { + tpart3 = " "; + } + else + { + TYPE_LENGTH (type) = dip -> at_byte_size; + tpart3 = ""; + } + TYPE_NAME (type) = concat (tpart1, tpart2, tpart3); + if ((scan = dip -> at_element_list) != NULL) + { + (void) memcpy (&temp, scan, sizeof (temp)); + listend = scan + temp + sizeof (temp); + scan += sizeof (temp); + while (scan < listend) + { + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new -> next = list; + list = new; + list -> field.type = NULL; + list -> field.bitsize = 0; + (void) memcpy (&list -> field.bitpos, scan, sizeof (long)); + scan += sizeof (long); + list -> field.name = savestring (scan, strlen (scan)); + scan += strlen (scan) + 1; + nfields++; + } + } + /* Now create the vector of fields, and record how big it is. */ + TYPE_NFIELDS (type) = nfields; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (symbol_obstack, sizeof (struct field) * nfields); + /* Copy the saved-up fields into the field vector. */ + for (n = nfields; list; list = list -> next) + { + TYPE_FIELD (type, --n) = list -> field; + } + return (type); +} + +/* + +LOCAL FUNCTION + + read_func_scope -- process all dies within a function scope + +SYNOPSIS + + static void read_func_scope (struct dieinfo dip, char *thisdie, + char *enddie) + +DESCRIPTION + + Process all dies within a given function scope. We are passed + a die information structure pointer DIP for the die which + starts the function scope, and pointers into the raw die data + that define the dies within the function scope. + + For now, we ignore lexical block scopes within the function. + The problem is that AT&T cc does not define a DWARF lexical + block scope for the function itself, while gcc defines a + lexical block scope for the function. We need to think about + how to handle this difference, or if it is even a problem. + (FIXME) + */ + +static void +DEFUN(read_func_scope, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct symbol *sym; + + if (entry_point >= dip -> at_low_pc && entry_point < dip -> at_high_pc) + { + entry_scope_lowpc = dip -> at_low_pc; + entry_scope_highpc = dip -> at_high_pc; + } + if (strcmp (dip -> at_name, "main") == 0) /* FIXME: hardwired name */ + { + main_scope_lowpc = dip -> at_low_pc; + main_scope_highpc = dip -> at_high_pc; + } + sym = new_symbol (dip); + openscope (sym, dip -> at_low_pc, dip -> at_high_pc); + process_dies (thisdie + dip -> dielength, enddie); + closescope (); +} + +/* + +LOCAL FUNCTION + + read_file_scope -- process all dies within a file scope + +SYNOPSIS + + static void read_file_scope (struct dieinfo *dip, char *thisdie + char *enddie) + +DESCRIPTION + + Process all dies within a given file scope. We are passed a + pointer to the die information structure for the die which + starts the file scope, and pointers into the raw die data which + mark the range of dies within the file scope. + + When the partial symbol table is built, the file offset for the line + number table for each compilation unit is saved in the partial symbol + table entry for that compilation unit. As the symbols for each + compilation unit are read, the line number table is read into memory + and the variable lnbase is set to point to it. Thus all we have to + do is use lnbase to access the line number table for the current + compilation unit. + */ + +static void +DEFUN(read_file_scope, (dip, thisdie, enddie), + struct dieinfo *dip AND + char *thisdie AND + char *enddie) +{ + struct cleanup *back_to; + + if (entry_point >= dip -> at_low_pc && entry_point < dip -> at_high_pc) + { + startup_file_start = dip -> at_low_pc; + startup_file_end = dip -> at_high_pc; + } + numutypes = (enddie - thisdie) / 4; + utypes = (struct type **) xmalloc (numutypes * sizeof (struct type *)); + back_to = make_cleanup (free, utypes); + (void) memset (utypes, 0, numutypes * sizeof (struct type *)); + start_symtab (); + openscope (NULL, dip -> at_low_pc, dip -> at_high_pc); + decode_line_numbers (lnbase); + process_dies (thisdie + dip -> dielength, enddie); + closescope (); + end_symtab (dip -> at_name, dip -> at_language); + do_cleanups (back_to); + utypes = NULL; + numutypes = 0; +} + +/* + +LOCAL FUNCTION + + start_symtab -- do initialization for starting new symbol table + +SYNOPSIS + + static void start_symtab (void) + +DESCRIPTION + + Called whenever we are starting to process dies for a new + compilation unit, to perform initializations. Right now + the only thing we really have to do is initialize storage + space for the line number vector. + + */ + +static void +DEFUN_VOID (start_symtab) +{ + int nbytes; + + line_vector_index = 0; + line_vector_length = 1000; + nbytes = sizeof (struct linetable); + nbytes += line_vector_length * sizeof (struct linetable_entry); + line_vector = (struct linetable *) xmalloc (nbytes); +} + +/* + +LOCAL FUNCTION + + process_dies -- process a range of DWARF Information Entries + +SYNOPSIS + + static void process_dies (char *thisdie, char *enddie) + +DESCRIPTION + + Process all DIE's in a specified range. May be (and almost + certainly will be) called recursively. + */ + +static void +DEFUN(process_dies, (thisdie, enddie), char *thisdie AND char *enddie) +{ + char *nextdie; + struct dieinfo di; + + while (thisdie < enddie) + { + basicdieinfo (&di, thisdie); + if (di.dielength < sizeof (long)) + { + break; + } + else if (di.dietag == TAG_padding) + { + nextdie = thisdie + di.dielength; + } + else + { + completedieinfo (&di); + if (di.at_sibling != 0) + { + nextdie = dbbase + di.at_sibling - dbroff; + } + else + { + nextdie = thisdie + di.dielength; + } + switch (di.dietag) + { + case TAG_compile_unit: + read_file_scope (&di, thisdie, nextdie); + break; + case TAG_global_subroutine: + case TAG_subroutine: + if (!di.at_is_external_p) + { + read_func_scope (&di, thisdie, nextdie); + } + break; + case TAG_lexical_block: + read_lexical_block_scope (&di, thisdie, nextdie); + break; + case TAG_structure_type: + case TAG_union_type: + read_structure_scope (&di, thisdie, nextdie); + break; + case TAG_enumeration_type: + read_enumeration (&di, thisdie, nextdie); + break; + case TAG_subroutine_type: + read_subroutine_type (&di, thisdie, nextdie); + break; + case TAG_array_type: + read_array_type (&di); + break; + default: + (void) new_symbol (&di); + break; + } + } + thisdie = nextdie; + } +} + +/* + +LOCAL FUNCTION + + end_symtab -- finish processing for a compilation unit + +SYNOPSIS + + static void end_symtab (char *filename, long language) + +DESCRIPTION + + Complete the symbol table entry for the current compilation + unit. Make the struct symtab and put it on the list of all + such symtabs. + + */ + +static void +DEFUN(end_symtab, (filename, language), char *filename AND long language) +{ + struct symtab *symtab; + struct blockvector *blockvector; + int nbytes; + + /* Ignore a file that has no functions with real debugging info. */ + if (global_symbols == NULL && scopetree -> block == NULL) + { + free (line_vector); + line_vector = NULL; + line_vector_length = -1; + freescope (scopetree); + scope = scopetree = NULL; + } + + /* Create the blockvector that points to all the file's blocks. */ + + blockvector = make_blockvector (); + + /* Now create the symtab object for this source file. */ + + symtab = (struct symtab *) xmalloc (sizeof (struct symtab)); + (void) memset (symtab, 0, sizeof (struct symtab)); + + symtab -> free_ptr = 0; + + /* Fill in its components. */ + symtab -> blockvector = blockvector; + symtab -> free_code = free_linetable; + symtab -> filename = savestring (filename, strlen (filename)); + + /* Save the line number information. */ + + line_vector -> nitems = line_vector_index; + nbytes = sizeof (struct linetable); + if (line_vector_index > 1) + { + nbytes += (line_vector_index - 1) * sizeof (struct linetable_entry); + } + symtab -> linetable = (struct linetable *) xrealloc (line_vector, nbytes); + symtab -> nlines = 0; + symtab -> line_charpos = 0; + + /* FIXME: The following may need to be expanded for other languages */ + if (language == LANG_C89 || language == LANG_C) + { + symtab -> language = language_c; + } + + /* Link the new symtab into the list of such. */ + symtab -> next = symtab_list; + symtab_list = symtab; + + /* Recursively free the scope tree */ + freescope (scopetree); + scope = scopetree = NULL; + + /* Reinitialize for beginning of new file. */ + line_vector = 0; + line_vector_length = -1; +} + +/* + +LOCAL FUNCTION + + scopecount -- count the number of enclosed scopes + +SYNOPSIS + + static int scopecount (struct scopenode *node) + +DESCRIPTION + + Given pointer to a node, compute the size of the subtree which is + rooted in this node, which also happens to be the number of scopes + to the subtree. + */ + +static int +DEFUN(scopecount, (node), struct scopenode *node) +{ + int count = 0; + + if (node != NULL) + { + count += scopecount (node -> child); + count += scopecount (node -> sibling); + count++; + } + return (count); +} + +/* + +LOCAL FUNCTION + + openscope -- start a new lexical block scope + +SYNOPSIS + + static void openscope (struct symbol *namesym, CORE_ADDR lowpc, + CORE_ADDR highpc) + +DESCRIPTION + + Start a new scope by allocating a new scopenode, adding it as the + next child of the current scope (if any) or as the root of the + scope tree, and then making the new node the current scope node. + */ + +static void +DEFUN(openscope, (namesym, lowpc, highpc), + struct symbol *namesym AND + CORE_ADDR lowpc AND + CORE_ADDR highpc) +{ + struct scopenode *new; + struct scopenode *child; + + new = (struct scopenode *) xmalloc (sizeof (*new)); + (void) memset (new, 0, sizeof (*new)); + new -> namesym = namesym; + new -> lowpc = lowpc; + new -> highpc = highpc; + if (scope == NULL) + { + scopetree = new; + } + else if ((child = scope -> child) == NULL) + { + scope -> child = new; + new -> parent = scope; + } + else + { + while (child -> sibling != NULL) + { + child = child -> sibling; + } + child -> sibling = new; + new -> parent = scope; + } + scope = new; +} + +/* + +LOCAL FUNCTION + + freescope -- free a scope tree rooted at the given node + +SYNOPSIS + + static void freescope (struct scopenode *node) + +DESCRIPTION + + Given a pointer to a node in the scope tree, free the subtree + rooted at that node. First free all the children and sibling + nodes, and then the node itself. Used primarily for cleaning + up after ourselves and returning memory to the system. + */ + +static void +DEFUN(freescope, (node), struct scopenode *node) +{ + if (node != NULL) + { + freescope (node -> child); + freescope (node -> sibling); + free (node); + } +} + +/* + +LOCAL FUNCTION + + buildblock -- build a new block from pending symbols list + +SYNOPSIS + + static struct block *buildblock (struct pending_symbol *syms) + +DESCRIPTION + + Given a pointer to a list of symbols, build a new block and free + the symbol list structure. Also check each symbol to see if it + is the special symbol that flags that this block was compiled by + gcc, and if so, mark the block appropriately. + */ + +static struct block * +DEFUN(buildblock, (syms), struct pending_symbol *syms) +{ + struct pending_symbol *next, *next1; + int i; + struct block *newblock; + int nbytes; + + for (next = syms, i = 0 ; next ; next = next -> next, i++) {;} + + /* Allocate a new block */ + + nbytes = sizeof (struct block); + if (i > 1) + { + nbytes += (i - 1) * sizeof (struct symbol *); + } + newblock = (struct block *) obstack_alloc (symbol_obstack, nbytes); + (void) memset (newblock, 0, nbytes); + + /* Copy the symbols into the block. */ + + BLOCK_NSYMS (newblock) = i; + for (next = syms ; next ; next = next -> next) + { + BLOCK_SYM (newblock, --i) = next -> symbol; + if (STREQ (GCC_COMPILED_FLAG_SYMBOL, SYMBOL_NAME (next -> symbol)) || + STREQ (GCC2_COMPILED_FLAG_SYMBOL, SYMBOL_NAME (next -> symbol))) + { + BLOCK_GCC_COMPILED (newblock) = 1; + } + } + + /* Now free the links of the list, and empty the list. */ + + for (next = syms ; next ; next = next1) + { + next1 = next -> next; + free (next); + } + + return (newblock); +} + +/* + +LOCAL FUNCTION + + closescope -- close a lexical block scope + +SYNOPSIS + + static void closescope (void) + +DESCRIPTION + + Close the current lexical block scope. Closing the current scope + is as simple as moving the current scope pointer up to the parent + of the current scope pointer. But we also take this opportunity + to build the block for the current scope first, since we now have + all of it's symbols. + */ + +static void +DEFUN_VOID(closescope) +{ + struct scopenode *child; + + if (scope == NULL) + { + error ("DWARF parse error, too many close scopes"); + } + else + { + if (scope -> parent == NULL) + { + global_symbol_block = buildblock (global_symbols); + global_symbols = NULL; + BLOCK_START (global_symbol_block) = scope -> lowpc + baseaddr; + BLOCK_END (global_symbol_block) = scope -> highpc + baseaddr; + } + scope -> block = buildblock (scope -> symbols); + scope -> symbols = NULL; + BLOCK_START (scope -> block) = scope -> lowpc + baseaddr; + BLOCK_END (scope -> block) = scope -> highpc + baseaddr; + + /* Put the local block in as the value of the symbol that names it. */ + + if (scope -> namesym) + { + SYMBOL_BLOCK_VALUE (scope -> namesym) = scope -> block; + BLOCK_FUNCTION (scope -> block) = scope -> namesym; + } + + /* Install this scope's local block as the superblock of all child + scope blocks. */ + + for (child = scope -> child ; child ; child = child -> sibling) + { + BLOCK_SUPERBLOCK (child -> block) = scope -> block; + } + + scope = scope -> parent; + } +} + +/* + +LOCAL FUNCTION + + record_line -- record a line number entry in the line vector + +SYNOPSIS + + static void record_line (int line, CORE_ADDR pc) + +DESCRIPTION + + Given a line number and the corresponding pc value, record + this pair in the line number vector, expanding the vector as + necessary. + */ + +static void +DEFUN(record_line, (line, pc), int line AND CORE_ADDR pc) +{ + struct linetable_entry *e; + int nbytes; + + /* Make sure line vector is big enough. */ + + if (line_vector_index + 2 >= line_vector_length) + { + line_vector_length *= 2; + nbytes = sizeof (struct linetable); + nbytes += (line_vector_length * sizeof (struct linetable_entry)); + line_vector = (struct linetable *) xrealloc (line_vector, nbytes); + } + e = line_vector -> item + line_vector_index++; + e -> line = line; + e -> pc = pc; +} + +/* + +LOCAL FUNCTION + + decode_line_numbers -- decode a line number table fragment + +SYNOPSIS + + static void decode_line_numbers (char *tblscan, char *tblend, + long length, long base, long line, long pc) + +DESCRIPTION + + Translate the DWARF line number information to gdb form. + + The ".line" section contains one or more line number tables, one for + each ".line" section from the objects that were linked. + + The AT_stmt_list attribute for each TAG_source_file entry in the + ".debug" section contains the offset into the ".line" section for the + start of the table for that file. + + The table itself has the following structure: + + + 4 bytes 4 bytes 10 bytes + + The table length is the total size of the table, including the 4 bytes + for the length information. + + The base address is the address of the first instruction generated + for the source file. + + Each source statement entry has the following structure: + +
+ 4 bytes 2 bytes 4 bytes + + The line number is relative to the start of the file, starting with + line 1. + + The statement position either -1 (0xFFFF) or the number of characters + from the beginning of the line to the beginning of the statement. + + The address delta is the difference between the base address and + the address of the first instruction for the statement. + + Note that we must copy the bytes from the packed table to our local + variables before attempting to use them, to avoid alignment problems + on some machines, particularly RISC processors. + +BUGS + + Does gdb expect the line numbers to be sorted? They are now by + chance/luck, but are not required to be. (FIXME) + + The line with number 0 is unused, gdb apparently can discover the + span of the last line some other way. How? (FIXME) + */ + +static void +DEFUN(decode_line_numbers, (linetable), char *linetable) +{ + char *tblscan; + char *tblend; + long length; + long base; + long line; + long pc; + + if (linetable != NULL) + { + tblscan = tblend = linetable; + (void) memcpy (&length, tblscan, sizeof (long)); + tblscan += sizeof (long); + tblend += length; + (void) memcpy (&base, tblscan, sizeof (long)); + base += baseaddr; + tblscan += sizeof (long); + while (tblscan < tblend) + { + (void) memcpy (&line, tblscan, sizeof (long)); + tblscan += sizeof (long) + sizeof (short); + (void) memcpy (&pc, tblscan, sizeof (long)); + tblscan += sizeof (long); + pc += base; + if (line > 0) + { + record_line (line, pc); + } + } + } +} + +/* + +LOCAL FUNCTION + + add_symbol_to_list -- add a symbol to head of current symbol list + +SYNOPSIS + + static void add_symbol_to_list (struct symbol *symbol, struct + pending_symbol **listhead) + +DESCRIPTION + + Given a pointer to a symbol and a pointer to a pointer to a + list of symbols, add this symbol as the current head of the + list. Typically used for example to add a symbol to the + symbol list for the current scope. + + */ + +static void +DEFUN(add_symbol_to_list, (symbol, listhead), + struct symbol *symbol AND struct pending_symbol **listhead) +{ + struct pending_symbol *link; + + if (symbol != NULL) + { + link = (struct pending_symbol *) xmalloc (sizeof (*link)); + link -> next = *listhead; + link -> symbol = symbol; + *listhead = link; + } +} + +/* + +LOCAL FUNCTION + + gatherblocks -- walk a scope tree and build block vectors + +SYNOPSIS + + static struct block **gatherblocks (struct block **dest, + struct scopenode *node) + +DESCRIPTION + + Recursively walk a scope tree rooted in the given node, adding blocks + to the array pointed to by DEST, in preorder. I.E., first we add the + block for the current scope, then all the blocks for child scopes, + and finally all the blocks for sibling scopes. + */ + +static struct block ** +DEFUN(gatherblocks, (dest, node), + struct block **dest AND struct scopenode *node) +{ + if (node != NULL) + { + *dest++ = node -> block; + dest = gatherblocks (dest, node -> child); + dest = gatherblocks (dest, node -> sibling); + } + return (dest); +} + +/* + +LOCAL FUNCTION + + make_blockvector -- make a block vector from current scope tree + +SYNOPSIS + + static struct blockvector *make_blockvector (void) + +DESCRIPTION + + Make a blockvector from all the blocks in the current scope tree. + The first block is always the global symbol block, followed by the + block for the root of the scope tree which is the local symbol block, + followed by all the remaining blocks in the scope tree, which are all + local scope blocks. + +NOTES + + Note that since the root node of the scope tree is created at the time + each file scope is entered, there are always at least two blocks, + neither of which may have any symbols, but always contribute a block + to the block vector. So the test for number of blocks greater than 1 + below is unnecessary given bug free code. + + The resulting block structure varies slightly from that produced + by dbxread.c, in that block 0 and block 1 are sibling blocks while + with dbxread.c, block 1 is a child of block 0. This does not + seem to cause any problems, but probably should be fixed. (FIXME) + */ + +static struct blockvector * +DEFUN_VOID(make_blockvector) +{ + struct blockvector *blockvector = NULL; + int i; + int nbytes; + + /* Recursively walk down the tree, counting the number of blocks. + Then add one to account for the global's symbol block */ + + i = scopecount (scopetree) + 1; + nbytes = sizeof (struct blockvector); + if (i > 1) + { + nbytes += (i - 1) * sizeof (struct block *); + } + blockvector = (struct blockvector *) + obstack_alloc (symbol_obstack, nbytes); + + /* Copy the blocks into the blockvector. */ + + BLOCKVECTOR_NBLOCKS (blockvector) = i; + BLOCKVECTOR_BLOCK (blockvector, 0) = global_symbol_block; + gatherblocks (&BLOCKVECTOR_BLOCK (blockvector, 1), scopetree); + + return (blockvector); +} + +/* + +LOCAL FUNCTION + + locval -- compute the value of a location attribute + +SYNOPSIS + + static int locval (char *loc) + +DESCRIPTION + + Given pointer to a string of bytes that define a location, compute + the location and return the value. + + When computing values involving the current value of the frame pointer, + the value zero is used, which results in a value relative to the frame + pointer, rather than the absolute value. This is what GDB wants + anyway. + + When the result is a register number, the global isreg flag is set, + otherwise it is cleared. This is a kludge until we figure out a better + way to handle the problem. Gdb's design does not mesh well with the + DWARF notion of a location computing interpreter, which is a shame + because the flexibility goes unused. + +NOTES + + Note that stack[0] is unused except as a default error return. + Note that stack overflow is not yet handled. + */ + +static int +DEFUN(locval, (loc), char *loc) +{ + unsigned short nbytes; + auto int stack[64]; + int stacki; + char *end; + long regno; + + (void) memcpy (&nbytes, loc, sizeof (short)); + end = loc + sizeof (short) + nbytes; + stacki = 0; + stack[stacki] = 0; + isreg = 0; + for (loc += sizeof (short); loc < end; loc += sizeof (long)) + { + switch (*loc++) { + case 0: + /* error */ + loc = end; + break; + case OP_REG: + /* push register (number) */ + (void) memcpy (&stack[++stacki], loc, sizeof (long)); + isreg = 1; + break; + case OP_BASEREG: + /* push value of register (number) */ + /* Actually, we compute the value as if register has 0 */ + (void) memcpy (®no, loc, sizeof (long)); + if (regno == R_FP) + { + stack[++stacki] = 0; + } + else + { + stack[++stacki] = 0; + SQUAWK (("BASEREG %d not handled!", regno)); + } + break; + case OP_ADDR: + /* push address (relocated address) */ + (void) memcpy (&stack[++stacki], loc, sizeof (long)); + break; + case OP_CONST: + /* push constant (number) */ + (void) memcpy (&stack[++stacki], loc, sizeof (long)); + break; + case OP_DEREF2: + /* pop, deref and push 2 bytes (as a long) */ + SQUAWK (("OP_DEREF2 address %#x not handled", stack[stacki])); + break; + case OP_DEREF4: /* pop, deref and push 4 bytes (as a long) */ + SQUAWK (("OP_DEREF4 address %#x not handled", stack[stacki])); + break; + case OP_ADD: /* pop top 2 items, add, push result */ + stack[stacki - 1] += stack[stacki]; + stacki--; + break; + } + } + return (stack[stacki]); +} + +/* + +LOCAL FUNCTION + + read_ofile_symtab -- build a full symtab entry from chunk of DIE's + +SYNOPSIS + + static struct symtab *read_ofile_symtab (struct partial_symtab *pst, + int desc) + +DESCRIPTION + + DESC is the file descriptor for the file, positioned at the + beginning of the symtab + SYM_SIZE is the size of the symbol section to read + TEXT_OFFSET is the beginning of the text segment we are reading + symbols for + TEXT_SIZE is the size of the text segment read in. + OFFSET is a relocation offset which gets added to each symbol + + */ + +static struct symtab * +DEFUN(read_ofile_symtab, (pst, desc), + struct partial_symtab *pst AND + int desc) +{ + struct cleanup *back_to; + long lnsize; + int foffset; + + /* Allocate a buffer for the entire chunk of DIE's for this compilation + unit, seek to the location in the file, and read in all the DIE's. */ + + diecount = 0; + dbbase = xmalloc (DBLENGTH(pst)); + dbroff = DBROFF(pst); + foffset = DBFOFF(pst) + dbroff; + if ((lseek (desc, foffset, 0) != foffset) || + (read (desc, dbbase, DBLENGTH(pst)) != DBLENGTH(pst))) + { + free (dbbase); + error ("can't read DWARF data"); + } + back_to = make_cleanup (free, dbbase); + + /* If there is a line number table associated with this compilation unit + then read the first long word from the line number table fragment, which + contains the size of the fragment in bytes (including the long word + itself). Allocate a buffer for the fragment and read it in for future + processing. */ + + lnbase = NULL; + if (LNFOFF (pst)) + { + if ((lseek (desc, LNFOFF (pst), 0) != LNFOFF (pst)) || + (read (desc, &lnsize, sizeof(long)) != sizeof(long))) + { + error ("can't read DWARF line number table size"); + } + lnbase = xmalloc (lnsize); + if ((lseek (desc, LNFOFF (pst), 0) != LNFOFF (pst)) || + (read (desc, lnbase, lnsize) != lnsize)) + { + free (lnbase); + error ("can't read DWARF line numbers"); + } + make_cleanup (free, lnbase); + } + + process_dies (dbbase, dbbase + DBLENGTH(pst)); + do_cleanups (back_to); + return (symtab_list); +} + +/* + +LOCAL FUNCTION + + psymtab_to_symtab_1 -- do grunt work for building a full symtab entry + +SYNOPSIS + + static void psymtab_to_symtab_1 (struct partial_symtab *pst, int desc) + +DESCRIPTION + + Called once for each partial symbol table entry that needs to be + expanded into a full symbol table entry. + +*/ + +static void +DEFUN(psymtab_to_symtab_1, + (pst, desc), + struct partial_symtab *pst AND + int desc) +{ + int i; + + if (!pst) + { + return; + } + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst -> filename); + return; + } + + /* Read in all partial symtabs on which this one is dependent */ + for (i = 0; i < pst -> number_of_dependencies; i++) + if (!pst -> dependencies[i] -> readin) + { + /* Inform about additional files that need to be read in. */ + if (info_verbose) + { + fputs_filtered (" ", stdout); + wrap_here (""); + fputs_filtered ("and ", stdout); + wrap_here (""); + printf_filtered ("%s...", pst -> dependencies[i] -> filename); + wrap_here (""); /* Flush output */ + fflush (stdout); + } + psymtab_to_symtab_1 (pst -> dependencies[i], desc); + } + + if (DBLENGTH(pst)) /* Otherwise it's a dummy */ + { + /* Init stuff necessary for reading in symbols */ + pst -> symtab = read_ofile_symtab (pst, desc); + if (info_verbose) + { + printf_filtered ("%d DIE's, sorting...", diecount); + fflush (stdout); + } + sort_symtab_syms (pst -> symtab); + } + pst -> readin = 1; +} + +/* + +LOCAL FUNCTION + + dwarf_psymtab_to_symtab -- build a full symtab entry from partial one + +SYNOPSIS + + static void dwarf_psymtab_to_symtab (struct partial_symtab *pst) + +DESCRIPTION + + This is the DWARF support entry point for building a full symbol + table entry from a partial symbol table entry. We are passed a + pointer to the partial symbol table entry that needs to be expanded. + +*/ + +static void +DEFUN(dwarf_psymtab_to_symtab, (pst), struct partial_symtab *pst) +{ + int desc; + struct cleanup *old_chain; + bfd *sym_bfd; + + if (!pst) + { + return; + } + if (pst -> readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst -> filename); + return; + } + + if (DBLENGTH(pst) || pst -> number_of_dependencies) + { + /* Print the message now, before starting serious work, to avoid + disconcerting pauses. */ + if (info_verbose) + { + printf_filtered ("Reading in symbols for %s...", pst -> filename); + fflush (stdout); + } + + /* Open symbol file. Symbol_file_command guarantees that the symbol + file name will be absolute, so there is no need for openp. */ + desc = open (pst -> symfile_name, O_RDONLY, 0); + + if (desc < 0) + { + perror_with_name (pst -> symfile_name); + } + + sym_bfd = bfd_fdopenr (pst -> symfile_name, NULL, desc); + if (!sym_bfd) + { + (void) close (desc); + error ("Could not open `%s' to read symbols: %s", + pst -> symfile_name, bfd_errmsg (bfd_error)); + } + old_chain = make_cleanup (bfd_close, sym_bfd); + if (!bfd_check_format (sym_bfd, bfd_object)) + { + error ("\"%s\": can't read symbols: %s.", + pst -> symfile_name, bfd_errmsg (bfd_error)); + } + + psymtab_to_symtab_1 (pst, desc); + +#if 0 /* FIXME: Check to see what dbxread is doing here and see if + we need to do an equivalent or is this something peculiar to + stabs/a.out format. */ + /* Match with global symbols. This only needs to be done once, + after all of the symtabs and dependencies have been read in. */ + scan_file_globals (); +#endif + + do_cleanups (old_chain); + + /* Finish up the debug error message. */ + if (info_verbose) + { + printf_filtered ("done.\n"); + } + } +} + +/* + +LOCAL FUNCTION + + init_psymbol_list -- initialize storage for partial symbols + +SYNOPSIS + + static void init_psymbol_list (int total_symbols) + +DESCRIPTION + + Initializes storage for all of the partial symbols that will be + created by dwarf_build_psymtabs and subsidiaries. + */ + +static void +DEFUN(init_psymbol_list, (total_symbols), int total_symbols) +{ + /* Free any previously allocated psymbol lists. */ + + if (global_psymbols.list) + { + free (global_psymbols.list); + } + if (static_psymbols.list) + { + free (static_psymbols.list); + } + + /* Current best guess is that there are approximately a twentieth + of the total symbols (in a debugging file) are global or static + oriented symbols */ + + global_psymbols.size = total_symbols / 10; + static_psymbols.size = total_symbols / 10; + global_psymbols.next = global_psymbols.list = (struct partial_symbol *) + xmalloc (global_psymbols.size * sizeof (struct partial_symbol)); + static_psymbols.next = static_psymbols.list = (struct partial_symbol *) + xmalloc (static_psymbols.size * sizeof (struct partial_symbol)); +} + +/* + +LOCAL FUNCTION + + start_psymtab -- allocate and partially fill a partial symtab entry + +DESCRIPTION + + Allocate and partially fill a partial symtab. It will be completely + filled at the end of the symbol list. + + SYMFILE_NAME is the name of the symbol-file we are reading from, and + ADDR is the address relative to which its symbols are (incremental) + or 0 (normal). FILENAME is the name of the compilation unit that + these symbols were defined in, and they appear starting a address + TEXTLOW. DBROFF is the absolute file offset in SYMFILE_NAME where + the full symbols can be read for compilation unit FILENAME. + GLOBAL_SYMS and STATIC_SYMS are pointers to the current end of the + psymtab vector. + + */ + +static struct partial_symtab * +DEFUN(start_psymtab, + (symfile_name, addr, filename, textlow, texthigh, dbfoff, curoff, + culength, lnfoff, global_syms, static_syms), + char *symfile_name AND + CORE_ADDR addr AND + char *filename AND + CORE_ADDR textlow AND + CORE_ADDR texthigh AND + int dbfoff AND + int curoff AND + int culength AND + int lnfoff AND + struct partial_symbol *global_syms AND + struct partial_symbol *static_syms) +{ + struct partial_symtab *result; + + result = (struct partial_symtab *) + obstack_alloc (psymbol_obstack, sizeof (struct partial_symtab)); + (void) memset (result, 0, sizeof (struct partial_symtab)); + result -> addr = addr; + result -> symfile_name = create_name (symfile_name, psymbol_obstack); + result -> filename = create_name (filename, psymbol_obstack); + result -> textlow = textlow; + result -> texthigh = texthigh; + result -> read_symtab_private = (char *) obstack_alloc (psymbol_obstack, + sizeof (struct dwfinfo)); + DBFOFF (result) = dbfoff; + DBROFF (result) = curoff; + DBLENGTH (result) = culength; + LNFOFF (result) = lnfoff; + result -> readin = 0; + result -> symtab = NULL; + result -> read_symtab = dwarf_psymtab_to_symtab; + result -> globals_offset = global_syms - global_psymbols.list; + result -> statics_offset = static_syms - static_psymbols.list; + + result->n_global_syms = 0; + result->n_static_syms = 0; + + return result; +} + +/* + +LOCAL FUNCTION + + add_psymbol_to_list -- add a partial symbol to given list + +DESCRIPTION + + Add a partial symbol to one of the partial symbol vectors (pointed to + by listp). The vector is grown as necessary. + + */ + +static void +DEFUN(add_psymbol_to_list, + (listp, name, space, class, value), + struct psymbol_allocation_list *listp AND + char *name AND + enum namespace space AND + enum address_class class AND + CORE_ADDR value) +{ + struct partial_symbol *psym; + int newsize; + + if (listp -> next >= listp -> list + listp -> size) + { + newsize = listp -> size * 2; + listp -> list = (struct partial_symbol *) + xrealloc (listp -> list, (newsize * sizeof (struct partial_symbol))); + /* Next assumes we only went one over. Should be good if program works + correctly */ + listp -> next = listp -> list + listp -> size; + listp -> size = newsize; + } + psym = listp -> next++; + SYMBOL_NAME (psym) = create_name (name, psymbol_obstack); + SYMBOL_NAMESPACE (psym) = space; + SYMBOL_CLASS (psym) = class; + SYMBOL_VALUE (psym) = value; +} + +/* + +LOCAL FUNCTION + + add_partial_symbol -- add symbol to partial symbol table + +DESCRIPTION + + Given a DIE, if it is one of the types that we want to + add to a partial symbol table, finish filling in the die info + and then add a partial symbol table entry for it. + +*/ + +static void +DEFUN(add_partial_symbol, (dip), struct dieinfo *dip) +{ + switch (dip -> dietag) + { + case TAG_global_subroutine: + record_misc_function (dip -> at_name, dip -> at_low_pc); + add_psymbol_to_list (&global_psymbols, dip -> at_name, VAR_NAMESPACE, + LOC_BLOCK, dip -> at_low_pc); + break; + case TAG_global_variable: + add_psymbol_to_list (&global_psymbols, dip -> at_name, VAR_NAMESPACE, + LOC_STATIC, 0); + break; + case TAG_subroutine: + add_psymbol_to_list (&static_psymbols, dip -> at_name, VAR_NAMESPACE, + LOC_BLOCK, dip -> at_low_pc); + break; + case TAG_local_variable: + add_psymbol_to_list (&static_psymbols, dip -> at_name, VAR_NAMESPACE, + LOC_STATIC, 0); + break; + case TAG_typedef: + add_psymbol_to_list (&static_psymbols, dip -> at_name, VAR_NAMESPACE, + LOC_TYPEDEF, 0); + break; + case TAG_structure_type: + case TAG_union_type: + case TAG_enumeration_type: + add_psymbol_to_list (&static_psymbols, dip -> at_name, STRUCT_NAMESPACE, + LOC_TYPEDEF, 0); + break; + } +} + +/* + +LOCAL FUNCTION + + scan_partial_symbols -- scan DIE's within a single compilation unit + +DESCRIPTION + + Process the DIE's within a single compilation unit, looking for + interesting DIE's that contribute to the partial symbol table entry + for this compilation unit. Since we cannot follow any sibling + chains without reading the complete DIE info for every DIE, + it is probably faster to just sequentially check each one to + see if it is one of the types we are interested in, and if + so, then extracting all the attributes info and generating a + partial symbol table entry. + + */ + +static void +DEFUN(scan_partial_symbols, (thisdie, enddie), char *thisdie AND char *enddie) +{ + char *nextdie; + struct dieinfo di; + + while (thisdie < enddie) + { + basicdieinfo (&di, thisdie); + if (di.dielength < sizeof (long)) + { + break; + } + else + { + nextdie = thisdie + di.dielength; + switch (di.dietag) + { + case TAG_global_subroutine: + case TAG_global_variable: + case TAG_subroutine: + case TAG_local_variable: + case TAG_typedef: + case TAG_structure_type: + case TAG_union_type: + case TAG_enumeration_type: + completedieinfo (&di); + /* Don't attempt to add anonymous structures, unions, or + enumerations since they have no name. Also check that + this is the place where the actual definition occurs, + rather than just a reference to an external. */ + if (di.at_name != NULL && !di.at_is_external_p) + { + add_partial_symbol (&di); + } + break; + } + } + thisdie = nextdie; + } +} + +/* + +LOCAL FUNCTION + + scan_compilation_units -- build a psymtab entry for each compilation + +DESCRIPTION + + This is the top level dwarf parsing routine for building partial + symbol tables. + + It scans from the beginning of the DWARF table looking for the first + TAG_compile_unit DIE, and then follows the sibling chain to locate + each additional TAG_compile_unit DIE. + + For each TAG_compile_unit DIE it creates a partial symtab structure, + calls a subordinate routine to collect all the compilation unit's + global DIE's, file scope DIEs, typedef DIEs, etc, and then links the + new partial symtab structure into the partial symbol table. It also + records the appropriate information in the partial symbol table entry + to allow the chunk of DIE's and line number table for this compilation + unit to be located and re-read later, to generate a complete symbol + table entry for the compilation unit. + + Thus it effectively partitions up a chunk of DIE's for multiple + compilation units into smaller DIE chunks and line number tables, + and associates them with a partial symbol table entry. + +NOTES + + If any compilation unit has no line number table associated with + it for some reason (a missing at_stmt_list attribute, rather than + just one with a value of zero, which is valid) then we ensure that + the recorded file offset is zero so that the routine which later + reads line number table fragments knows that there is no fragment + to read. + +RETURNS + + Returns no value. + + */ + +static void +DEFUN(scan_compilation_units, + (filename, addr, thisdie, enddie, dbfoff, lnoffset), + char *filename AND + CORE_ADDR addr AND + char *thisdie AND + char *enddie AND + unsigned int dbfoff AND + unsigned int lnoffset) +{ + char *nextdie; + struct dieinfo di; + struct partial_symtab *pst; + int culength; + int curoff; + int curlnoffset; + + while (thisdie < enddie) + { + basicdieinfo (&di, thisdie); + if (di.dielength < sizeof (long)) + { + break; + } + else if (di.dietag != TAG_compile_unit) + { + nextdie = thisdie + di.dielength; + } + else + { + completedieinfo (&di); + if (di.at_sibling != 0) + { + nextdie = dbbase + di.at_sibling - dbroff; + } + else + { + nextdie = thisdie + di.dielength; + } + curoff = thisdie - dbbase; + culength = nextdie - thisdie; + curlnoffset = di.at_stmt_list_p ? lnoffset + di.at_stmt_list : 0; + pst = start_psymtab (filename, addr, di.at_name, + di.at_low_pc, di.at_high_pc, + dbfoff, curoff, culength, curlnoffset, + global_psymbols.next, + static_psymbols.next); + scan_partial_symbols (thisdie + di.dielength, nextdie); + pst -> n_global_syms = global_psymbols.next - + (global_psymbols.list + pst -> globals_offset); + pst -> n_static_syms = static_psymbols.next - + (static_psymbols.list + pst -> statics_offset); + /* Sort the global list; don't sort the static list */ + qsort (global_psymbols.list + pst -> globals_offset, + pst -> n_global_syms, sizeof (struct partial_symbol), + compare_psymbols); + /* If there is already a psymtab or symtab for a file of this name, + remove it. (If there is a symtab, more drastic things also + happen.) This happens in VxWorks. */ + free_named_symtabs (pst -> filename); + /* Place the partial symtab on the partial symtab list */ + pst -> next = partial_symtab_list; + partial_symtab_list = pst; + } + thisdie = nextdie; + } +} + +/* + +LOCAL FUNCTION + + new_symbol -- make a symbol table entry for a new symbol + +SYNOPSIS + + static struct symbol *new_symbol (struct dieinfo *dip) + +DESCRIPTION + + Given a pointer to a DWARF information entry, figure out if we need + to make a symbol table entry for it, and if so, create a new entry + and return a pointer to it. + */ + +static struct symbol * +DEFUN(new_symbol, (dip), struct dieinfo *dip) +{ + struct symbol *sym = NULL; + + if (dip -> at_name != NULL) + { + sym = (struct symbol *) obstack_alloc (symbol_obstack, + sizeof (struct symbol)); + (void) memset (sym, 0, sizeof (struct symbol)); + SYMBOL_NAME (sym) = create_name (dip -> at_name, symbol_obstack); + /* default assumptions */ + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_TYPE (sym) = decode_die_type (dip); + switch (dip -> dietag) + { + case TAG_label: + SYMBOL_VALUE (sym) = dip -> at_low_pc + baseaddr; + SYMBOL_CLASS (sym) = LOC_LABEL; + break; + case TAG_global_subroutine: + case TAG_subroutine: + SYMBOL_VALUE (sym) = dip -> at_low_pc + baseaddr; + SYMBOL_TYPE (sym) = lookup_function_type (SYMBOL_TYPE (sym)); + SYMBOL_CLASS (sym) = LOC_BLOCK; + if (dip -> dietag == TAG_global_subroutine) + { + add_symbol_to_list (sym, &global_symbols); + } + else + { + add_symbol_to_list (sym, &scope -> symbols); + } + break; + case TAG_global_variable: + case TAG_local_variable: + if (dip -> at_location != NULL) + { + SYMBOL_VALUE (sym) = locval (dip -> at_location); + } + if (dip -> dietag == TAG_global_variable) + { + add_symbol_to_list (sym, &global_symbols); + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) += baseaddr; + } + else + { + add_symbol_to_list (sym, &scope -> symbols); + if (scope -> parent != NULL) + { + if (isreg) + { + SYMBOL_CLASS (sym) = LOC_REGISTER; + } + else + { + SYMBOL_CLASS (sym) = LOC_LOCAL; + } + } + else + { + SYMBOL_CLASS (sym) = LOC_STATIC; + SYMBOL_VALUE (sym) += baseaddr; + } + } + break; + case TAG_formal_parameter: + if (dip -> at_location != NULL) + { + SYMBOL_VALUE (sym) = locval (dip -> at_location); + } + add_symbol_to_list (sym, &scope -> symbols); + if (isreg) + { + SYMBOL_CLASS (sym) = LOC_REGPARM; + } + else + { + SYMBOL_CLASS (sym) = LOC_ARG; + } + break; + case TAG_unspecified_parameters: + /* From varargs functions; gdb doesn't seem to have any interest in + this information, so just ignore it for now. (FIXME?) */ + break; + case TAG_structure_type: + case TAG_union_type: + case TAG_enumeration_type: + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE; + add_symbol_to_list (sym, &scope -> symbols); + break; + case TAG_typedef: + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + add_symbol_to_list (sym, &scope -> symbols); + break; + default: + /* Not a tag we recognize. Hopefully we aren't processing trash + data, but since we must specifically ignore things we don't + recognize, there is nothing else we should do at this point. */ + break; + } + } + return (sym); +} + +/* + +LOCAL FUNCTION + + decode_mod_fund_type -- decode a modified fundamental type + +SYNOPSIS + + static struct type *decode_mod_fund_type (char *typedata) + +DESCRIPTION + + Decode a block of data containing a modified fundamental + type specification. TYPEDATA is a pointer to the block, + which consists of a two byte length, containing the size + of the rest of the block. At the end of the block is a + two byte value that gives the fundamental type. Everything + in between are type modifiers. + + We simply compute the number of modifiers and call the general + function decode_modified_type to do the actual work. +*/ + +static struct type * +DEFUN(decode_mod_fund_type, (typedata), char *typedata) +{ + struct type *typep = NULL; + unsigned short modcount; + unsigned char *modifiers; + + /* Get the total size of the block, exclusive of the size itself */ + (void) memcpy (&modcount, typedata, sizeof (short)); + /* Deduct the size of the fundamental type bytes at the end of the block. */ + modcount -= sizeof (short); + /* Skip over the two size bytes at the beginning of the block. */ + modifiers = typedata + sizeof (short); + /* Now do the actual decoding */ + typep = decode_modified_type (modifiers, modcount, AT_mod_fund_type); + return (typep); +} + +/* + +LOCAL FUNCTION + + decode_mod_u_d_type -- decode a modified user defined type + +SYNOPSIS + + static struct type *decode_mod_u_d_type (char *typedata) + +DESCRIPTION + + Decode a block of data containing a modified user defined + type specification. TYPEDATA is a pointer to the block, + which consists of a two byte length, containing the size + of the rest of the block. At the end of the block is a + four byte value that gives a reference to a user defined type. + Everything in between are type modifiers. + + We simply compute the number of modifiers and call the general + function decode_modified_type to do the actual work. +*/ + +static struct type * +DEFUN(decode_mod_u_d_type, (typedata), char *typedata) +{ + struct type *typep = NULL; + unsigned short modcount; + unsigned char *modifiers; + + /* Get the total size of the block, exclusive of the size itself */ + (void) memcpy (&modcount, typedata, sizeof (short)); + /* Deduct the size of the reference type bytes at the end of the block. */ + modcount -= sizeof (long); + /* Skip over the two size bytes at the beginning of the block. */ + modifiers = typedata + sizeof (short); + /* Now do the actual decoding */ + typep = decode_modified_type (modifiers, modcount, AT_mod_u_d_type); + return (typep); +} + +/* + +LOCAL FUNCTION + + decode_modified_type -- decode modified user or fundamental type + +SYNOPSIS + + static struct type *decode_modified_type (unsigned char *modifiers, + unsigned short modcount, int mtype) + +DESCRIPTION + + Decode a modified type, either a modified fundamental type or + a modified user defined type. MODIFIERS is a pointer to the + block of bytes that define MODCOUNT modifiers. Immediately + following the last modifier is a short containing the fundamental + type or a long containing the reference to the user defined + type. Which one is determined by MTYPE, which is either + AT_mod_fund_type or AT_mod_u_d_type to indicate what modified + type we are generating. + + We call ourself recursively to generate each modified type,` + until MODCOUNT reaches zero, at which point we have consumed + all the modifiers and generate either the fundamental type or + user defined type. When the recursion unwinds, each modifier + is applied in turn to generate the full modified type. + +NOTES + + If we find a modifier that we don't recognize, and it is not one + of those reserved for application specific use, then we issue a + warning and simply ignore the modifier. + +BUGS + + We currently ignore MOD_const and MOD_volatile. (FIXME) + + */ + +static struct type * +DEFUN(decode_modified_type, + (modifiers, modcount, mtype), + unsigned char *modifiers AND unsigned short modcount AND int mtype) +{ + struct type *typep = NULL; + unsigned short fundtype; + DIEREF dieref; + unsigned char modifier; + + if (modcount == 0) + { + switch (mtype) + { + case AT_mod_fund_type: + (void) memcpy (&fundtype, modifiers, sizeof (short)); + typep = decode_fund_type (fundtype); + break; + case AT_mod_u_d_type: + (void) memcpy (&dieref, modifiers, sizeof (DIEREF)); + if ((typep = lookup_utype (dieref)) == NULL) + { + typep = alloc_utype (dieref, NULL); + } + break; + default: + SQUAWK (("botched modified type decoding (mtype 0x%x)", mtype)); + typep = builtin_type_int; + break; + } + } + else + { + modifier = *modifiers++; + typep = decode_modified_type (modifiers, --modcount, mtype); + switch (modifier) + { + case MOD_pointer_to: + typep = lookup_pointer_type (typep); + break; + case MOD_reference_to: + typep = lookup_reference_type (typep); + break; + case MOD_const: + SQUAWK (("type modifier 'const' ignored")); /* FIXME */ + break; + case MOD_volatile: + SQUAWK (("type modifier 'volatile' ignored")); /* FIXME */ + break; + default: + if (!(MOD_lo_user <= modifier && modifier <= MOD_hi_user)) + { + SQUAWK (("unknown type modifier %u", modifier)); + } + break; + } + } + return (typep); +} + +/* + +LOCAL FUNCTION + + decode_fund_type -- translate basic DWARF type to gdb base type + +DESCRIPTION + + Given an integer that is one of the fundamental DWARF types, + translate it to one of the basic internal gdb types and return + a pointer to the appropriate gdb type (a "struct type *"). + +NOTES + + If we encounter a fundamental type that we are unprepared to + deal with, and it is not in the range of those types defined + as application specific types, then we issue a warning and + treat the type as builtin_type_int. +*/ + +static struct type * +DEFUN(decode_fund_type, (fundtype), unsigned short fundtype) +{ + struct type *typep = NULL; + + switch (fundtype) + { + + case FT_void: + typep = builtin_type_void; + break; + + case FT_pointer: /* (void *) */ + typep = lookup_pointer_type (builtin_type_void); + break; + + case FT_char: + case FT_signed_char: + typep = builtin_type_char; + break; + + case FT_short: + case FT_signed_short: + typep = builtin_type_short; + break; + + case FT_integer: + case FT_signed_integer: + case FT_boolean: /* Was FT_set in AT&T version */ + typep = builtin_type_int; + break; + + case FT_long: + case FT_signed_long: + typep = builtin_type_long; + break; + + case FT_float: + typep = builtin_type_float; + break; + + case FT_dbl_prec_float: + typep = builtin_type_double; + break; + + case FT_unsigned_char: + typep = builtin_type_unsigned_char; + break; + + case FT_unsigned_short: + typep = builtin_type_unsigned_short; + break; + + case FT_unsigned_integer: + typep = builtin_type_unsigned_int; + break; + + case FT_unsigned_long: + typep = builtin_type_unsigned_long; + break; + + case FT_ext_prec_float: + typep = builtin_type_long_double; + break; + + case FT_complex: + typep = builtin_type_complex; + break; + + case FT_dbl_prec_complex: + typep = builtin_type_double_complex; + break; + + case FT_long_long: + case FT_signed_long_long: + typep = builtin_type_long_long; + break; + + case FT_unsigned_long_long: + typep = builtin_type_unsigned_long_long; + break; + + } + + if ((typep == NULL) && !(FT_lo_user <= fundtype && fundtype <= FT_hi_user)) + { + SQUAWK (("unexpected fundamental type 0x%x", fundtype)); + typep = builtin_type_void; + } + + return (typep); +} + +/* + +LOCAL FUNCTION + + create_name -- allocate a fresh copy of a string on an obstack + +DESCRIPTION + + Given a pointer to a string and a pointer to an obstack, allocates + a fresh copy of the string on the specified obstack. + +*/ + +static char * +DEFUN(create_name, (name, obstackp), char *name AND struct obstack *obstackp) +{ + int length; + char *newname; + + length = strlen (name) + 1; + newname = (char *) obstack_alloc (obstackp, length); + (void) strcpy (newname, name); + return (newname); +} + +/* + +LOCAL FUNCTION + + basicdieinfo -- extract the minimal die info from raw die data + +SYNOPSIS + + void basicdieinfo (char *diep, struct dieinfo *dip) + +DESCRIPTION + + Given a pointer to raw DIE data, and a pointer to an instance of a + die info structure, this function extracts the basic information + from the DIE data required to continue processing this DIE, along + with some bookkeeping information about the DIE. + + The information we absolutely must have includes the DIE tag, + and the DIE length. If we need the sibling reference, then we + will have to call completedieinfo() to process all the remaining + DIE information. + + Note that since there is no guarantee that the data is properly + aligned in memory for the type of access required (indirection + through anything other than a char pointer), we use memcpy to + shuffle data items larger than a char. Possibly inefficient, but + quite portable. + + We also take care of some other basic things at this point, such + as ensuring that the instance of the die info structure starts + out completely zero'd and that curdie is initialized for use + in error reporting if we have a problem with the current die. + +NOTES + + All DIE's must have at least a valid length, thus the minimum + DIE size is sizeof (long). In order to have a valid tag, the + DIE size must be at least sizeof (short) larger, otherwise they + are forced to be TAG_padding DIES. + + Padding DIES must be at least sizeof(long) in length, implying that + if a padding DIE is used for alignment and the amount needed is less + than sizeof(long) then the padding DIE has to be big enough to align + to the next alignment boundry. + */ + +static void +DEFUN(basicdieinfo, (dip, diep), struct dieinfo *dip AND char *diep) +{ + curdie = dip; + (void) memset (dip, 0, sizeof (struct dieinfo)); + dip -> die = diep; + dip -> dieref = dbroff + (diep - dbbase); + (void) memcpy (&dip -> dielength, diep, sizeof (long)); + if (dip -> dielength < sizeof (long)) + { + dwarfwarn ("malformed DIE, bad length (%d bytes)", dip -> dielength); + } + else if (dip -> dielength < (sizeof (long) + sizeof (short))) + { + dip -> dietag = TAG_padding; + } + else + { + (void) memcpy (&dip -> dietag, diep + sizeof (long), sizeof (short)); + } +} + +/* + +LOCAL FUNCTION + + completedieinfo -- finish reading the information for a given DIE + +SYNOPSIS + + void completedieinfo (struct dieinfo *dip) + +DESCRIPTION + + Given a pointer to an already partially initialized die info structure, + scan the raw DIE data and finish filling in the die info structure + from the various attributes found. + + Note that since there is no guarantee that the data is properly + aligned in memory for the type of access required (indirection + through anything other than a char pointer), we use memcpy to + shuffle data items larger than a char. Possibly inefficient, but + quite portable. + +NOTES + + Each time we are called, we increment the diecount variable, which + keeps an approximate count of the number of dies processed for + each compilation unit. This information is presented to the user + if the info_verbose flag is set. + + */ + +static void +DEFUN(completedieinfo, (dip), struct dieinfo *dip) +{ + char *diep; /* Current pointer into raw DIE data */ + char *end; /* Terminate DIE scan here */ + unsigned short attr; /* Current attribute being scanned */ + unsigned short form; /* Form of the attribute */ + short block2sz; /* Size of a block2 attribute field */ + long block4sz; /* Size of a block4 attribute field */ + + diecount++; + diep = dip -> die; + end = diep + dip -> dielength; + diep += sizeof (long) + sizeof (short); + while (diep < end) + { + (void) memcpy (&attr, diep, sizeof (short)); + diep += sizeof (short); + switch (attr) + { + case AT_fund_type: + (void) memcpy (&dip -> at_fund_type, diep, sizeof (short)); + break; + case AT_ordering: + (void) memcpy (&dip -> at_ordering, diep, sizeof (short)); + break; + case AT_bit_offset: + (void) memcpy (&dip -> at_bit_offset, diep, sizeof (short)); + break; + case AT_visibility: + (void) memcpy (&dip -> at_visibility, diep, sizeof (short)); + break; + case AT_sibling: + (void) memcpy (&dip -> at_sibling, diep, sizeof (long)); + break; + case AT_stmt_list: + (void) memcpy (&dip -> at_stmt_list, diep, sizeof (long)); + dip -> at_stmt_list_p = 1; + break; + case AT_low_pc: + (void) memcpy (&dip -> at_low_pc, diep, sizeof (long)); + break; + case AT_high_pc: + (void) memcpy (&dip -> at_high_pc, diep, sizeof (long)); + break; + case AT_language: + (void) memcpy (&dip -> at_language, diep, sizeof (long)); + break; + case AT_user_def_type: + (void) memcpy (&dip -> at_user_def_type, diep, sizeof (long)); + break; + case AT_byte_size: + (void) memcpy (&dip -> at_byte_size, diep, sizeof (long)); + break; + case AT_bit_size: + (void) memcpy (&dip -> at_bit_size, diep, sizeof (long)); + break; + case AT_member: + (void) memcpy (&dip -> at_member, diep, sizeof (long)); + break; + case AT_discr: + (void) memcpy (&dip -> at_discr, diep, sizeof (long)); + break; + case AT_import: + (void) memcpy (&dip -> at_import, diep, sizeof (long)); + break; + case AT_location: + dip -> at_location = diep; + break; + case AT_mod_fund_type: + dip -> at_mod_fund_type = diep; + break; + case AT_subscr_data: + dip -> at_subscr_data = diep; + break; + case AT_mod_u_d_type: + dip -> at_mod_u_d_type = diep; + break; + case AT_deriv_list: + dip -> at_deriv_list = diep; + break; + case AT_element_list: + dip -> at_element_list = diep; + break; + case AT_discr_value: + dip -> at_discr_value = diep; + break; + case AT_string_length: + dip -> at_string_length = diep; + break; + case AT_name: + dip -> at_name = diep; + break; + case AT_comp_dir: + dip -> at_comp_dir = diep; + break; + case AT_producer: + dip -> at_producer = diep; + break; + case AT_loclist: + (void) memcpy (&dip -> at_loclist, diep, sizeof (long)); + break; + case AT_frame_base: + (void) memcpy (&dip -> at_frame_base, diep, sizeof (long)); + break; + case AT_incomplete: + (void) memcpy (&dip -> at_incomplete, diep, sizeof (short)); + break; + case AT_start_scope: + (void) memcpy (&dip -> at_start_scope, diep, sizeof (long)); + break; + case AT_stride_size: + (void) memcpy (&dip -> at_stride_size, diep, sizeof (long)); + break; + case AT_src_info: + (void) memcpy (&dip -> at_src_info, diep, sizeof (long)); + break; + case AT_prototyped: + (void) memcpy (&dip -> at_prototyped, diep, sizeof (short)); + break; + case AT_const_data: + dip -> at_const_data = diep; + break; + case AT_is_external: + (void) memcpy (&dip -> at_is_external, diep, sizeof (short)); + dip -> at_is_external_p = 1; + break; + default: + /* Found an attribute that we are unprepared to handle. However + it is specifically one of the design goals of DWARF that + consumers should ignore unknown attributes. As long as the + form is one that we recognize (so we know how to skip it), + we can just ignore the unknown attribute. */ + break; + } + form = attr & 0xF; + switch (form) + { + case FORM_DATA2: + diep += sizeof (short); + break; + case FORM_DATA4: + diep += sizeof (long); + break; + case FORM_DATA8: + diep += 8 * sizeof (char); /* sizeof (long long) ? */ + break; + case FORM_ADDR: + case FORM_REF: + diep += sizeof (long); + break; + case FORM_BLOCK2: + (void) memcpy (&block2sz, diep, sizeof (short)); + block2sz += sizeof (short); + diep += block2sz; + break; + case FORM_BLOCK4: + (void) memcpy (&block4sz, diep, sizeof (long)); + block4sz += sizeof (long); + diep += block4sz; + break; + case FORM_STRING: + diep += strlen (diep) + 1; + break; + default: + SQUAWK (("unknown attribute form (0x%x), skipped rest", form)); + diep = end; + break; + } + } +} diff --git a/gdb/elfread.c b/gdb/elfread.c new file mode 100644 index 0000000000..8c8e8c0f36 --- /dev/null +++ b/gdb/elfread.c @@ -0,0 +1,208 @@ +/* Read ELF (Executable and Linking Format) object files for GDB. + Copyright (C) 1991 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/************************************************************************ + * * + * NOTICE * + * * + * This file is still under construction. When it is complete, this * + * notice will be removed. Until then, direct any questions or changes * + * to Fred Fish at Cygnus Support (fnf@cygint) * + * * + * FIXME Still needs support for shared libraries. * + * FIXME Still needs support for core files. * + * FIXME The ".debug" and ".line" section names are hardwired. * + * FIXME Still needs support ELF symbol tables (as distinct * + * from DWARF support). Can use them to build the misc * + * function vector at least. This is fairly trivial once * + * bfd is extended to handle ELF symbol tables. * + * * + ************************************************************************/ + +#include + +#include "defs.h" +#include "param.h" +#include "elf-common.h" +#include "elf-external.h" +#include "elf-internal.h" +#include "bfd.h" +#include "symfile.h" +#include "symtab.h" +#include "ansidecl.h" + +extern int EXFUN(strcmp, (CONST char *a, CONST char *b)); +#define STREQ(a,b) (strcmp((a),(b))==0) + +struct elfinfo { + unsigned int dboffset; /* Offset to dwarf debug section */ + unsigned int dbsize; /* Size of dwarf debug section */ + unsigned int lnoffset; /* Offset to dwarf line number section */ + unsigned int lnsize; /* Size of dwarf line number section */ +}; + +/* FIXME - crude hack to resolve undefined global. This function is + part of support for corefiles, which is not yet implemented. */ + +unsigned int +DEFUN(register_addr, (regno, blockend), + int regno AND + int blockend) +{ + error ("Fetching registers from corefiles unimplemented."); +} + +/* We are called once per section from elf_symfile_read. We + need to examine each section we are passed, check to see + if it is something we are interested in processing, and + if so, stash away some access information for the section. + + For now we recognize the dwarf debug information sections and + line number sections from matching their section names. The + ELF definition is no real help here since it has no direct + knowledge of DWARF (by design, so any debugging format can be + used). + + FIXME: The section names should not be hardwired strings. */ + +static void +DEFUN(elf_locate_sections, (abfd, sectp, ei), + bfd *abfd AND + asection *sectp AND + struct elfinfo *ei) +{ + if (STREQ (sectp -> name, ".debug")) + { + ei -> dboffset = sectp -> filepos; + ei -> dbsize = sectp -> size; + } + else if (STREQ (sectp -> name, ".line")) + { + ei -> lnoffset = sectp -> filepos; + ei -> lnsize = sectp -> size; + } +} + +/* Scan and build partial symbols for a symbol file. + We have been initialized by a call to elf_symfile_init, which + currently does nothing. + + ADDR is the address relative to which the symbols in it are (e.g. + the base address of the text segment). + + MAINLINE is true if we are reading the main symbol + table (as opposed to a shared lib or dynamically loaded file). + + This function only does the minimum work necessary for letting the + user "name" things symbolically; it does not read the entire symtab. + Instead, it reads the external and static symbols and puts them in partial + symbol tables. When more extensive information is requested of a + file, the corresponding partial symbol table is mutated into a full + fledged symbol table by going back and reading the symbols + for real. The function dwarf_psymtab_to_symtab() is the function that + does this for DWARF symbols. */ + +static void +DEFUN(elf_symfile_read, (sf, addr, mainline), + struct sym_fns *sf AND + CORE_ADDR addr AND + int mainline) +{ + bfd *abfd = sf -> sym_bfd; + struct elfinfo ei; + + bfd_map_over_sections (abfd, elf_locate_sections, &ei); + if (ei.dboffset && ei.lnoffset) + { + addr = 0; /* FIXME: force address base to zero for now */ + dwarf_build_psymtabs (fileno ((FILE *)(abfd -> iostream)), + bfd_get_filename (abfd), + addr, mainline, + ei.dboffset, ei.dbsize, + ei.lnoffset, ei.lnsize); + } + if (!partial_symtab_list) + { + wrap_here (""); + printf_filtered ("(no debugging symbols found)..."); + wrap_here (""); + } +} + +/* Initialize anything that needs initializing when a completely new symbol + file is specified (not just adding some symbols from another file, e.g. a + shared library). + + For now at least, we have nothing in particular to do, so this function is + just a stub. */ + +static void +DEFUN_VOID (elf_new_init) +{ +} + +/* ELF specific initialization routine for reading symbols. + + It is passed a pointer to a struct sym_fns which contains, among other + things, the BFD for the file whose symbols are being read, and a slot for + a pointer to "private data" which we can fill with goodies. + + For now at least, we have nothing in particular to do, so this function is + just a stub. */ + +static void +DEFUN(elf_symfile_init, (sf), + struct sym_fns *sf) +{ +} + + +/* Register that we are able to handle ELF object file formats and DWARF + debugging formats. + + Unlike other object file formats, where the debugging information format + is implied by the object file format, the ELF object file format and the + DWARF debugging information format are two distinct, and potentially + separate entities. I.E. it is perfectly possible to have ELF objects + with debugging formats other than DWARF. And it is conceivable that the + DWARF debugging format might be used with another object file format, + like COFF, by simply using COFF's custom section feature. + + GDB, and to a lesser extent BFD, should support the notion of separate + object file formats and debugging information formats. For now, we just + use "elf" in the same sense as "a.out" or "coff", to imply both the ELF + object file format and the DWARF debugging format. */ + +static struct sym_fns elf_sym_fns = { + "elf", /* sym_name: name or name prefix of BFD target type */ + 3, /* sym_namelen: number of significant sym_name chars */ + elf_new_init, /* sym_new_init: init anything gbl to entire symtab */ + elf_symfile_init, /* sym_init: read initial info, setup for sym_read() */ + elf_symfile_read, /* sym_read: read a symbol file into symtab */ + NULL, /* sym_bfd: accessor for symbol file being read */ + NULL, /* sym_private: sym_init & sym_read shared info */ + NULL /* next: pointer to next struct sym_fns */ +}; + +void +DEFUN_VOID (_initialize_elfread) +{ + add_symtab_fns (&elf_sym_fns); +} diff --git a/gdb/procfs.c b/gdb/procfs.c new file mode 100644 index 0000000000..a277369953 --- /dev/null +++ b/gdb/procfs.c @@ -0,0 +1,988 @@ +/* Machine independent support for SVR4 /proc (process file system) for GDB. + Copyright (C) 1991 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* N O T E S + +For information on the details of using /proc consult section proc(4) +in the UNIX System V Release 4 System Administrator's Reference Manual. + +The general register and floating point register sets are manipulated by +separate ioctl's. This file makes the assumption that if FP0_REGNUM is +defined, then support for the floating point register set is desired, +regardless of whether or not the actual target has floating point hardware. + + */ + + + +#include "param.h" + +#ifdef USE_PROC_FS /* Entire file goes away if not using /proc */ + +#include +#include +#include +#include + +#include "defs.h" +#include "ansidecl.h" +#include "inferior.h" +#include "target.h" + +#ifndef PROC_NAME_FMT +#define PROC_NAME_FMT "/proc/%d" +#endif + +extern void EXFUN(supply_gregset, (gregset_t *gregsetp)); +extern void EXFUN(fill_gregset, (gregset_t *gresetp, int regno)); + +#if defined (FP0_REGNUM) +extern void EXFUN(supply_fpregset, (fpregset_t *fpregsetp)); +extern void EXFUN(fill_fpregset, (fpregset_t *fpresetp, int regno)); +#endif + +#if 1 /* FIXME: Gross and ugly hack to resolve coredep.c global */ +CORE_ADDR kernel_u_addr; +#endif + +/* All access to the inferior, either one started by gdb or one that has + been attached to, is controlled by an instance of a procinfo structure, + defined below. Since gdb currently only handles one inferior at a time, + the procinfo structure is statically allocated and only one exists at + any given time. */ + +struct procinfo { + int valid; /* Nonzero if pid, fd, & pathname are valid */ + int pid; /* Process ID of inferior */ + int fd; /* File descriptor for /proc entry */ + char *pathname; /* Pathname to /proc entry */ + int was_stopped; /* Nonzero if was stopped prior to attach */ + prrun_t prrun; /* Control state when it is run */ + prstatus_t prstatus; /* Current process status info */ + gregset_t gregset; /* General register set */ + fpregset_t fpregset; /* Floating point register set */ + fltset_t fltset; /* Current traced hardware fault set */ + sigset_t trace; /* Current traced signal set */ + sysset_t exitset; /* Current traced system call exit set */ + sysset_t entryset; /* Current traced system call entry set */ +} pi; + +/* Forward declarations of static functions so we don't have to worry + about ordering within this file. The EXFUN macro may be slightly + misleading. Should probably be called DCLFUN instead, or something + more intuitive, since it can be used for both static and external + definitions. */ + +static void EXFUN(proc_init_failed, (char *why)); +static int EXFUN(open_proc_file, (int pid)); +static void EXFUN(close_proc_file, (void)); +static void EXFUN(unconditionally_kill_inferior, (void)); + +/* + +GLOBAL FUNCTION + + ptrace -- override library version to force errors for /proc version + +SYNOPSIS + + int ptrace (int request, int pid, int arg3, int arg4) + +DESCRIPTION + + When gdb is configured to use /proc, it should not be calling + or otherwise attempting to use ptrace. In order to catch errors + where use of /proc is configured, but some routine is still calling + ptrace, we provide a local version of a function with that name + that does nothing but issue an error message. +*/ + +int +DEFUN(ptrace, (request, pid, arg3, arg4), + int request AND + int pid AND + int arg3 AND + int arg4) +{ + error ("internal error - there is a call to ptrace() somewhere"); + /*NOTREACHED*/ +} + +/* + +GLOBAL FUNCTION + + kill_inferior_fast -- kill inferior while gdb is exiting + +SYNOPSIS + + void kill_inferior_fast (void) + +DESCRIPTION + + This is used when GDB is exiting. It gives less chance of error. + +NOTES + + Don't attempt to kill attached inferiors since we may be called + when gdb is in the process of aborting, and killing the attached + inferior may be very anti-social. This is particularly true if we + were attached just so we could use the /proc facilities to get + detailed information about it's status. + +*/ + +void +DEFUN_VOID(kill_inferior_fast) +{ + if (inferior_pid != 0 && !attach_flag) + { + unconditionally_kill_inferior (); + } +} + +/* + +GLOBAL FUNCTION + + kill_inferior - kill any currently inferior + +SYNOPSIS + + void kill_inferior (void) + +DESCRIPTION + + Kill any current inferior. + +NOTES + + Kills even attached inferiors. Presumably the user has already + been prompted that the inferior is an attached one rather than + one started by gdb. (FIXME?) + +*/ + +void +DEFUN_VOID(kill_inferior) +{ + if (inferior_pid != 0) + { + unconditionally_kill_inferior (); + target_mourn_inferior (); + } +} + +/* + +LOCAL FUNCTION + + unconditionally_kill_inferior - terminate the inferior + +SYNOPSIS + + static void unconditionally_kill_inferior (void) + +DESCRIPTION + + Kill the current inferior. Should not be called until it + is at least tested that there is an inferior. + +NOTE + + A possibly useful enhancement would be to first try sending + the inferior a terminate signal, politely asking it to commit + suicide, before we murder it. + +*/ + +static void +DEFUN_VOID(unconditionally_kill_inferior) +{ + int signo; + + signo = SIGKILL; + (void) ioctl (pi.fd, PIOCKILL, &signo); + close_proc_file (); + wait ((int *) 0); +} + +/* + +GLOBAL FUNCTION + + child_xfer_memory -- copy data to or from inferior memory space + +SYNOPSIS + + int child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int dowrite, struct target_ops target) + +DESCRIPTION + + Copy LEN bytes to/from inferior's memory starting at MEMADDR + from/to debugger memory starting at MYADDR. Copy from inferior + if DOWRITE is zero or to inferior if DOWRITE is nonzero. + + Returns the length copied, which is either the LEN argument or + zero. This xfer function does not do partial moves, since child_ops + doesn't allow memory operations to cross below us in the target stack + anyway. + +NOTES + + The /proc interface makes this an almost trivial task. + */ + + +int +DEFUN(child_xfer_memory, (memaddr, myaddr, len, dowrite, target), + CORE_ADDR memaddr AND + char *myaddr AND + int len AND + int dowrite AND + struct target_ops target /* ignored */) +{ + int nbytes = 0; + + if (lseek (pi.fd, (off_t) memaddr, 0) == (off_t) memaddr) + { + if (dowrite) + { + nbytes = write (pi.fd, myaddr, len); + } + else + { + nbytes = read (pi.fd, myaddr, len); + } + if (nbytes < 0) + { + nbytes = 0; + } + } + return (nbytes); +} + +/* + +GLOBAL FUNCTION + + store_inferior_registers -- copy register values back to inferior + +SYNOPSIS + + void store_inferior_registers (int regno) + +DESCRIPTION + + Store our current register values back into the inferior. If + REGNO is -1 then store all the register, otherwise store just + the value specified by REGNO. + +NOTES + + If we are storing only a single register, we first have to get all + the current values from the process, overwrite the desired register + in the gregset with the one we want from gdb's registers, and then + send the whole set back to the process. For writing all the + registers, all we have to do is generate the gregset and send it to + the process. + + Also note that the process has to be stopped on an event of interest + for this to work, which basically means that it has to have been + run under the control of one of the other /proc ioctl calls and not + ptrace. Since we don't use ptrace anyway, we don't worry about this + fine point, but it is worth noting for future reference. + + Gdb is confused about what this function is supposed to return. + Some versions return a value, others return nothing. Some are + declared to return a value and actually return nothing. Gdb ignores + anything returned. (FIXME) + + */ + +void +DEFUN(store_inferior_registers, (regno), + int regno) +{ + if (regno != -1) + { + (void) ioctl (pi.fd, PIOCGREG, &pi.gregset); + } + fill_gregset (&pi.gregset, regno); + (void) ioctl (pi.fd, PIOCSREG, &pi.gregset); + +#if defined (FP0_REGNUM) + + /* Now repeat everything using the floating point register set, if the + target has floating point hardware. Since we ignore the returned value, + we'll never know whether it worked or not anyway. */ + + if (regno != -1) + { + (void) ioctl (pi.fd, PIOCGFPREG, &pi.fpregset); + } + fill_fpregset (&pi.fpregset, regno); + (void) ioctl (pi.fd, PIOCSFPREG, &pi.fpregset); + +#endif /* FP0_REGNUM */ + +} + +/* + +GLOBAL FUNCTION + + inferior_proc_init - initialize access to a /proc entry + +SYNOPSIS + + void inferior_proc_init (int pid) + +DESCRIPTION + + When gdb starts an inferior, this function is called in the parent + process immediately after the fork. It waits for the child to stop + on the return from the exec system call (the child itself takes care + of ensuring that this is set up), then sets up the set of signals + and faults that are to be traced. + +NOTES + + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. + */ + +void +DEFUN(inferior_proc_init, (int pid), + int pid) +{ + if (!open_proc_file (pid)) + { + proc_init_failed ("can't open process file"); + } + else + { + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + prfillset (&pi.prrun.pr_trace); + prfillset (&pi.prrun.pr_fault); + prdelset (&pi.prrun.pr_fault, FLTPAGE); + if (ioctl (pi.fd, PIOCWSTOP, &pi.prstatus) < 0) + { + proc_init_failed ("PIOCWSTOP failed"); + } + else if (ioctl (pi.fd, PIOCSTRACE, &pi.prrun.pr_trace) < 0) + { + proc_init_failed ("PIOCSTRACE failed"); + } + else if (ioctl (pi.fd, PIOCSFAULT, &pi.prrun.pr_fault) < 0) + { + proc_init_failed ("PIOCSFAULT failed"); + } + } +} + +/* + +GLOBAL FUNCTION + + proc_set_exec_trap -- arrange for exec'd child to halt at startup + +SYNOPSIS + + void proc_set_exec_trap (void) + +DESCRIPTION + + This function is called in the child process when starting up + an inferior, prior to doing the exec of the actual inferior. + It sets the child process's exitset to make exit from the exec + system call an event of interest to stop on, and then simply + returns. The child does the exec, the system call returns, and + the child stops at the first instruction, ready for the gdb + parent process to take control of it. + +NOTE + + We need to use all local variables since the child may be sharing + it's data space with the parent, if vfork was used rather than + fork. + */ + +void +DEFUN_VOID(proc_set_exec_trap) +{ + sysset_t exitset; + auto char procname[32]; + int fd; + + (void) sprintf (procname, PROC_NAME_FMT, getpid ()); + if ((fd = open (procname, O_RDWR)) < 0) + { + perror (procname); + fflush (stderr); + _exit (127); + } + premptyset (&exitset); + praddset (&exitset, SYS_exec); + praddset (&exitset, SYS_execve); + if (ioctl (fd, PIOCSEXIT, &exitset) < 0) + { + perror (procname); + fflush (stderr); + _exit (127); + } +} + + +#ifdef ATTACH_DETACH + +/* + +GLOBAL FUNCTION + + attach -- attach to an already existing process + +SYNOPSIS + + int attach (int pid) + +DESCRIPTION + + Attach to an already existing process with the specified process + id. If the process is not already stopped, query whether to + stop it or not. + +NOTES + + The option of stopping at attach time is specific to the /proc + versions of gdb. Versions using ptrace force the attachee + to stop. + +*/ + +int +DEFUN(attach, (pid), + int pid) +{ + if (!open_proc_file (pid)) + { + perror_with_name (pi.pathname); + /* NOTREACHED */ + } + + /* Get current status of process and if it is not already stopped, + then stop it. Remember whether or not it was stopped when we first + examined it. */ + + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + close_proc_file (); + error ("PIOCSTATUS failed"); + } + if (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + pi.was_stopped = 1; + } + else + { + pi.was_stopped = 0; + if (query ("Process is currently running, stop it? ")) + { + if (ioctl (pi.fd, PIOCSTOP, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + close_proc_file (); + error ("PIOCSTOP failed"); + } + } + } + + /* Remember some things about the inferior that we will, or might, change + so that we can restore them when we detach. */ + + (void) ioctl (pi.fd, PIOCGTRACE, &pi.trace); + (void) ioctl (pi.fd, PIOCGFAULT, &pi.fltset); + (void) ioctl (pi.fd, PIOCGENTRY, &pi.entryset); + (void) ioctl (pi.fd, PIOCGEXIT, &pi.exitset); + + /* Set up trace and fault sets, as gdb expects them. */ + + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + prfillset (&pi.prrun.pr_trace); + prfillset (&pi.prrun.pr_fault); + prdelset (&pi.prrun.pr_fault, FLTPAGE); + if (ioctl (pi.fd, PIOCSFAULT, &pi.prrun.pr_fault)) + { + print_sys_errmsg ("PIOCSFAULT failed"); + } + if (ioctl (pi.fd, PIOCSTRACE, &pi.prrun.pr_trace)) + { + print_sys_errmsg ("PIOCSTRACE failed"); + } + attach_flag = 1; + return (pid); +} + +/* + +GLOBAL FUNCTION + + detach -- detach from an attached-to process + +SYNOPSIS + + void detach (int signal) + +DESCRIPTION + + Detach from the current attachee. + + If signal is non-zero, the attachee is started running again and sent + the specified signal. + + If signal is zero and the attachee was not already stopped when we + attached to it, then we make it runnable again when we detach. + + Otherwise, we query whether or not to make the attachee runnable + again, since we may simply want to leave it in the state it was in + when we attached. + + We report any problems, but do not consider them errors, since we + MUST detach even if some things don't seem to go right. This may not + be the ideal situation. (FIXME). + */ + +void +DEFUN(detach, (signal), + int signal) +{ + if (signal) + { + struct siginfo siginfo; + siginfo.si_signo = signal; + siginfo.si_code = 0; + siginfo.si_errno = 0; + if (ioctl (pi.fd, PIOCSSIG, &siginfo) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSSIG failed.\n"); + } + } + if (ioctl (pi.fd, PIOCSEXIT, &pi.exitset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSEXIT failed.\n"); + } + if (ioctl (pi.fd, PIOCSENTRY, &pi.entryset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSENTRY failed.\n"); + } + if (ioctl (pi.fd, PIOCSTRACE, &pi.trace) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSTRACE failed.\n"); + } + if (ioctl (pi.fd, PIOCSFAULT, &pi.fltset) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSFAULT failed.\n"); + } + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCSTATUS failed.\n"); + } + else + { + if (signal || (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (signal || !pi.was_stopped || + query ("Was stopped when attached, make it runnable again? ")) + { + (void) memset (&pi.prrun, 0, sizeof (pi.prrun)); + pi.prrun.pr_flags = PRCFAULT; + if (ioctl (pi.fd, PIOCRUN, &pi.prrun)) + { + print_sys_errmsg (pi.pathname, errno); + printf ("PIOCRUN failed.\n"); + } + } + } + } + close_proc_file (); + attach_flag = 0; +} + +/* + +GLOBAL FUNCTION + + proc_wait -- emulate wait() as much as possible + +SYNOPSIS + + int proc_wait (int *statloc) + +DESCRIPTION + + Try to emulate wait() as much as possible. Not sure why we can't + just use wait(), but it seems to have problems when applied to a + process being controlled with the /proc interface. + +NOTES + + We have a race problem here with no obvious solution. We need to let + the inferior run until it stops on an event of interest, which means + that we need to use the PIOCWSTOP ioctl. However, we cannot use this + ioctl if the process is already stopped on something that is not an + event of interest, or the call will hang indefinitely. Thus we first + use PIOCSTATUS to see if the process is not stopped. If not, then we + use PIOCWSTOP. But during the window between the two, if the process + stops for any reason that is not an event of interest (such as a job + control signal) then gdb will hang. One possible workaround is to set + an alarm to wake up every minute of so and check to see if the process + is still running, and if so, then reissue the PIOCWSTOP. But this is + a real kludge, so has not been implemented. FIXME: investigate + alternatives. + + FIXME: Investigate why wait() seems to have problems with programs + being control by /proc routines. + + */ + +int +DEFUN(proc_wait, (statloc), + int *statloc) +{ + short what; + short why; + int statval = 0; + int checkerr = 0; + int rtnval = -1; + + if (ioctl (pi.fd, PIOCSTATUS, &pi.prstatus) < 0) + { + checkerr++; + } + else if (!(pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (ioctl (pi.fd, PIOCWSTOP, &pi.prstatus) < 0) + { + checkerr++; + } + } + if (checkerr) + { + if (errno == ENOENT) + { + rtnval = wait (&statval); + if (rtnval != inferior_pid) + { + error ("PIOCWSTOP, wait failed, returned %d", rtnval); + /* NOTREACHED */ + } + } + else + { + print_sys_errmsg (pi.pathname, errno); + error ("PIOCSTATUS or PIOCWSTOP failed."); + /* NOTREACHED */ + } + } + else if (pi.prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + rtnval = pi.prstatus.pr_pid; + why = pi.prstatus.pr_why; + what = pi.prstatus.pr_what; + if (why == PR_SIGNALLED) + { + statval = (what << 8) | 0177; + } + else if ((why == PR_SYSEXIT) && + (what == SYS_exec || what == SYS_execve)) + { + statval = (SIGTRAP << 8) | 0177; + } + else if (why == PR_REQUESTED) + { + statval = (SIGSTOP << 8) | 0177; + } + else if (why == PR_JOBCONTROL) + { + statval = (what << 8) | 0177; + } + else if (why == PR_FAULTED) + { + switch (what) + { + case FLTPRIV: + case FLTILL: + statval = (SIGILL << 8) | 0177; + break; + case FLTBPT: + case FLTTRACE: + statval = (SIGTRAP << 8) | 0177; + break; + case FLTSTACK: + case FLTACCESS: + case FLTBOUNDS: + statval = (SIGSEGV << 8) | 0177; + break; + case FLTIOVF: + case FLTIZDIV: + case FLTFPE: + statval = (SIGFPE << 8) | 0177; + break; + case FLTPAGE: /* Recoverable page fault */ + default: + rtnval = -1; + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + /* NOTREACHED */ + } + } + else + { + rtnval = -1; + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + /* NOTREACHED */ + } + } + else + { + error ("PIOCWSTOP, stopped for unknown/unhandled reason, flags %#x", + pi.prstatus.pr_flags); + /* NOTREACHED */ + } + if (statloc) + { + *statloc = statval; + } + return (rtnval); +} + +/* + +GLOBAL FUNCTION + + child_resume -- resume execution of the inferior process + +SYNOPSIS + + void child_resume (int step, int signal) + +DESCRIPTION + + Resume execution of the inferior process. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. + +NOTE + + It may not be absolutely necessary to specify the PC value for + restarting, but to be safe we use the value that gdb considers + to be current. One case where this might be necessary is if the + user explicitly changes the PC value that gdb considers to be + current. FIXME: Investigate if this is necessary or not. + */ + +void +DEFUN(child_resume, (step, signal), + int step AND + int signal) +{ + errno = 0; + pi.prrun.pr_flags = PRSVADDR | PRSTRACE | PRSFAULT | PRCFAULT; + pi.prrun.pr_vaddr = (caddr_t) *(int *) ®isters[REGISTER_BYTE (PC_REGNUM)]; + if (signal) + { + if (signal != pi.prstatus.pr_cursig) + { + struct siginfo siginfo; + siginfo.si_signo = signal; + siginfo.si_code = 0; + siginfo.si_errno = 0; + (void) ioctl (pi.fd, PIOCSSIG, &siginfo); + } + } + else + { + pi.prrun.pr_flags |= PRCSIG; + } + if (step) + { + pi.prrun.pr_flags |= PRSTEP; + } + if (ioctl (pi.fd, PIOCRUN, &pi.prrun) != 0) + { + perror_with_name (pi.pathname); + /* NOTREACHED */ + } +} + +/* + +GLOBAL FUNCTION + + fetch_inferior_registers -- fetch current registers from inferior + +SYNOPSIS + + void fetch_inferior_registers (void) + +DESCRIPTION + + Read the current values of the inferior's registers, both the + general register set and floating point registers (if supported) + and update gdb's idea of their current values. + +*/ + +void +DEFUN_VOID(fetch_inferior_registers) +{ + if (ioctl (pi.fd, PIOCGREG, &pi.gregset) != -1) + { + supply_gregset (&pi.gregset); + } +#if defined (FP0_REGNUM) + if (ioctl (pi.fd, PIOCGFPREG, &pi.fpregset) != -1) + { + supply_fpregset (&pi.fpregset); + } +#endif +} + +#endif /* ATTACH_DETACH */ + +/* + +LOCAL FUNCTION + + proc_init_failed - called whenever /proc access initialization fails + +SYNOPSIS + + static void proc_init_failed (char *why) + +DESCRIPTION + + This function is called whenever initialization of access to a /proc + entry fails. It prints a suitable error message, does some cleanup, + and then invokes the standard error processing routine which dumps + us back into the command loop. + */ + +static void +DEFUN(proc_init_failed, (why), + char *why) +{ + print_sys_errmsg (pi.pathname, errno); + (void) kill (pi.pid, SIGKILL); + close_proc_file (); + error (why); + /* NOTREACHED */ +} + +/* + +LOCAL FUNCTION + + close_proc_file - close any currently open /proc entry + +SYNOPSIS + + static void close_proc_file (void) + +DESCRIPTION + + Close any currently open /proc entry and mark the process information + entry as invalid. In order to ensure that we don't try to reuse any + stale information, the pid, fd, and pathnames are explicitly + invalidated, which may be overkill. + + */ + +static void +DEFUN_VOID(close_proc_file) +{ + pi.pid = 0; + if (pi.valid) + { + (void) close (pi.fd); + } + pi.fd = -1; + if (pi.pathname) + { + free (pi.pathname); + pi.pathname = NULL; + } + pi.valid = 0; +} + +/* + +LOCAL FUNCTION + + open_proc_file - open a /proc entry for a given process id + +SYNOPSIS + + static int open_proc_file (pid) + +DESCRIPTION + + Given a process id, close the existing open /proc entry (if any) + and open one for the new process id. Once it is open, then + mark the local process information structure as valid, which + guarantees that the pid, fd, and pathname fields match an open + /proc entry. Returns zero if the open fails, nonzero otherwise. + + Note that the pathname is left intact, even when the open fails, + so that callers can use it to construct meaningful error messages + rather than just "file open failed". + */ + +static int +DEFUN(open_proc_file, (pid), + int pid) +{ + pi.valid = 0; + if (pi.valid) + { + (void) close (pi.fd); + } + if (pi.pathname == NULL) + { + pi.pathname = xmalloc (32); + } + sprintf (pi.pathname, PROC_NAME_FMT, pid); + if ((pi.fd = open (pi.pathname, O_RDWR)) >= 0) + { + pi.valid = 1; + pi.pid = pid; + } + return (pi.valid); +} + +#endif /* USE_PROC_FS */ diff --git a/gdb/tm-amix.h b/gdb/tm-amix.h new file mode 100644 index 0000000000..056bd82ae8 --- /dev/null +++ b/gdb/tm-amix.h @@ -0,0 +1,56 @@ +/* Macro definitions for GDB on a Commodore Amiga running SVR4 (amix). + Copyright (C) 1991, Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygint) + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* All Amiga's (so far) running UNIX have come standard with the floating + point coprocessor. */ + +#define HAVE_68881 /* Amiga has floating point coprocessor */ + +/* Sequence of bytes for breakpoint instruction. + This is a TRAP instruction. The last 4 bits (0x1 below) is the + vector. */ + +#define BREAKPOINT {0x4e, 0x41 } /* Trap using vector 0x1 */ + +/* How much to decrement the PC after a trap. Depends on kernel. */ + +#define DECR_PC_AFTER_BREAK 0 /* No decrement required */ + + +#include "tm-68k.h" +#include "tm-svr4.h" + +/* Address of end of stack space. (actually one byte past it). + This value is typically very OS dependent. + FIXME: Check to see if SVR4 offers some machine independent way + of discovering this value and use it if so, and if we need it. */ + +/* #define STACK_END_ADDR 0xc0800000 */ + +/* Use the alternate method of avoiding running up off the end of + the frame chain or following frames back into the startup code. + See the comments in blockframe.c */ + +#undef FRAME_CHAIN_VALID +#define FRAME_CHAIN_VALID(chain, thisframe) \ + (chain != 0 \ + && !(inside_main_scope ((thisframe)->pc)) \ + && !(inside_entry_scope ((thisframe)->pc))) + diff --git a/gdb/tm-svr4.h b/gdb/tm-svr4.h new file mode 100755 index 0000000000..ed142142b8 --- /dev/null +++ b/gdb/tm-svr4.h @@ -0,0 +1,21 @@ +/* Macro definitions for GDB on all SVR4 target systems. + Copyright (C) 1991, Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygint) + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Currently empty */ diff --git a/gdb/xm-amix.h b/gdb/xm-amix.h new file mode 100644 index 0000000000..cf55ce10e0 --- /dev/null +++ b/gdb/xm-amix.h @@ -0,0 +1,28 @@ +/* Macro definitions for GDB on a Commodore Amiga running SVR4 (amix) + Copyright (C) 1991, Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygint) + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Pick up most of what we need from the generic m68k host include file. */ + +#include "xm-m68k.h" + +/* Pick up more stuff from the generic SVR4 host include file. */ + +#include "xm-svr4.h" + diff --git a/gdb/xm-m68k.h b/gdb/xm-m68k.h new file mode 100644 index 0000000000..f417e97b56 --- /dev/null +++ b/gdb/xm-m68k.h @@ -0,0 +1,22 @@ +/* Macro definitions for running gdb on host machines with m68k cpu's. + Copyright (C) 1991, Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygint) + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define HOST_BYTE_ORDER BIG_ENDIAN + diff --git a/gdb/xm-svr4.h b/gdb/xm-svr4.h new file mode 100755 index 0000000000..f4c4a53690 --- /dev/null +++ b/gdb/xm-svr4.h @@ -0,0 +1,56 @@ +/* Definitions for running gdb on a host machine running any flavor of SVR4. + Copyright (C) 1991, Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygint) + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* SVR4 has /proc support, so use it instead of ptrace. */ + +#define USE_PROC_FS + +/* SVR4 has termio facilities. */ + +#define HAVE_TERMIO + +/* TIOCGETC and TIOCGLTC are picked up somewhere, but struct tchars + and struct ltchars are not. This makes problems for inflow.c. + It is unknown at this time if this is a generic SVR4 problem or + one just limited to the initial SVR4 port host machine. */ + +#define TIOCGETC_BROKEN +#define TIOCGLTC_BROKEN + +/* SVR4 is a derivative of System V Release 3 (USG) */ + +#define USG + +/* Get rid of any system-imposed stack limit if possible. */ + +/* #define SET_STACK_LIMIT_HUGE */ + +/* SVR4 machines can easily do attach and detach via /proc (procfs.c) + support */ + +#define ATTACH_DETACH + +/* If we are using SVR4 /proc instead of ptrace, use CREATE_INFERIOR_HOOK + to do internal /proc initialization. */ + +#ifdef USE_PROC_FS +#define CREATE_INFERIOR_HOOK(pid) inferior_proc_init(pid) +#endif + -- 2.34.1