diff --git a/contrib/toolchain/binutils/Makefile b/contrib/toolchain/binutils/Makefile index 704db71930..3153c17d0e 100644 --- a/contrib/toolchain/binutils/Makefile +++ b/contrib/toolchain/binutils/Makefile @@ -15,7 +15,7 @@ CFLAGS = -c $(CFLAGS_OPT) LDFLAGS = -nostdlib -shared -s --image-base 0 -T ../newlib/dll.lds -e _DllStartup LDFLAGS+= --out-implib -SUBDIRS = libiberty bfd ld +SUBDIRS = libiberty bfd binutils ld # targets diff --git a/contrib/toolchain/binutils/binutils/Makefile b/contrib/toolchain/binutils/binutils/Makefile new file mode 100644 index 0000000000..0007ff0949 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/Makefile @@ -0,0 +1,43 @@ +NAME= objcopy-new + +LIB_DIR:= $(SDK_DIR)/lib + +CFLAGS_OPT = -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 -U_MSC_VER -O2 +CFLAGS_OPT+= -fomit-frame-pointer -fno-ident -mno-ms-bitfields +CFLAGS_OPT+= -W -Wall -Wmissing-prototypes -Wno-format +CFLAGS = -c $(CFLAGS_OPT) + +INCLUDES= -I. -I../bfd -I../include -I$(SDK_DIR)/sources/newlib/libc/include + +DEFINES= -DHAVE_CONFIG_H -DLOCALEDIR='"/home/autobuild/tools/win32/share/locale"' +DEFINES+= -Dbin_dummy_emulation=bin_vanilla_emulation + +LIBS= -lbfd -liberty -lz -lgcc -lc.dll -lapp + +LIBPATH:= -L$(LIB_DIR) -L/home/autobuild/tools/win32/mingw32/lib + +LDFLAGS = -static -nostdlib --stack 12582912 -T$(SDK_DIR)/sources/newlib/app.lds --image-base 0 + + +SRCS = \ + objcopy.c not-strip.c rename.c \ + rddbg.c debug.c stabs.c ieee.c \ + rdcoff.c wrstabs.c bucomm.c \ + version.c filemode.c + + + +OBJS = $(patsubst %.cpp, %.o, $(patsubst %.c, %.o, $(SRCS))) + +# targets + +all: $(NAME) + +$(NAME): $(OBJS) Makefile + $(LD) $(LDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LIBS) + kos32-objcopy $@ kos32-objcopy -O binary + +%.o : %.c Makefile + $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o $@ $< + + \ No newline at end of file diff --git a/contrib/toolchain/binutils/binutils/bucomm.c b/contrib/toolchain/binutils/binutils/bucomm.c new file mode 100644 index 0000000000..34aef5982c --- /dev/null +++ b/contrib/toolchain/binutils/binutils/bucomm.c @@ -0,0 +1,629 @@ +/* bucomm.c -- Bin Utils COMmon code. + Copyright 1991, 1992, 1993, 1994, 1995, 1997, 1998, 2000, 2001, 2002, + 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* We might put this in a library someday so it could be dynamically + loaded, but for now it's not necessary. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libiberty.h" +#include "filenames.h" +#include "libbfd.h" + +#include /* ctime, maybe time_t */ +#include +#include "bucomm.h" + +#ifndef HAVE_TIME_T_IN_TIME_H +#ifndef HAVE_TIME_T_IN_TYPES_H +typedef long time_t; +#endif +#endif + +static const char * endian_string (enum bfd_endian); +static int display_target_list (void); +static int display_info_table (int, int); +static int display_target_tables (void); + +/* Error reporting. */ + +char *program_name; + +void +bfd_nonfatal (const char *string) +{ + const char *errmsg; + + errmsg = bfd_errmsg (bfd_get_error ()); + fflush (stdout); + if (string) + fprintf (stderr, "%s: %s: %s\n", program_name, string, errmsg); + else + fprintf (stderr, "%s: %s\n", program_name, errmsg); +} + +/* Issue a non fatal error message. FILENAME, or if NULL then BFD, + are used to indicate the problematic file. SECTION, if non NULL, + is used to provide a section name. If FORMAT is non-null, then it + is used to print additional information via vfprintf. Finally the + bfd error message is printed. In summary, error messages are of + one of the following forms: + + PROGRAM:file: bfd-error-message + PROGRAM:file[section]: bfd-error-message + PROGRAM:file: printf-message: bfd-error-message + PROGRAM:file[section]: printf-message: bfd-error-message. */ + +void +bfd_nonfatal_message (const char *filename, + const bfd *abfd, + const asection *section, + const char *format, ...) +{ + const char *errmsg; + const char *section_name; + va_list args; + + errmsg = bfd_errmsg (bfd_get_error ()); + fflush (stdout); + section_name = NULL; + va_start (args, format); + fprintf (stderr, "%s", program_name); + + if (abfd) + { + if (!filename) + filename = bfd_get_archive_filename (abfd); + if (section) + section_name = bfd_get_section_name (abfd, section); + } + if (section_name) + fprintf (stderr, ":%s[%s]", filename, section_name); + else + fprintf (stderr, ":%s", filename); + + if (format) + { + fprintf (stderr, ": "); + vfprintf (stderr, format, args); + } + fprintf (stderr, ": %s\n", errmsg); + va_end (args); +} + +void +bfd_fatal (const char *string) +{ + bfd_nonfatal (string); + xexit (1); +} + +void +report (const char * format, va_list args) +{ + fflush (stdout); + fprintf (stderr, "%s: ", program_name); + vfprintf (stderr, format, args); + putc ('\n', stderr); +} + +void +fatal VPARAMS ((const char *format, ...)) +{ + VA_OPEN (args, format); + VA_FIXEDARG (args, const char *, format); + + report (format, args); + VA_CLOSE (args); + xexit (1); +} + +void +non_fatal VPARAMS ((const char *format, ...)) +{ + VA_OPEN (args, format); + VA_FIXEDARG (args, const char *, format); + + report (format, args); + VA_CLOSE (args); +} + +/* Set the default BFD target based on the configured target. Doing + this permits the binutils to be configured for a particular target, + and linked against a shared BFD library which was configured for a + different target. */ + +void +set_default_bfd_target (void) +{ + /* The macro TARGET is defined by Makefile. */ + const char *target = TARGET; + + if (! bfd_set_default_target (target)) + fatal (_("can't set BFD default target to `%s': %s"), + target, bfd_errmsg (bfd_get_error ())); +} + +/* After a FALSE return from bfd_check_format_matches with + bfd_get_error () == bfd_error_file_ambiguously_recognized, print + the possible matching targets. */ + +void +list_matching_formats (char **p) +{ + fflush (stdout); + fprintf (stderr, _("%s: Matching formats:"), program_name); + while (*p) + fprintf (stderr, " %s", *p++); + fputc ('\n', stderr); +} + +/* List the supported targets. */ + +void +list_supported_targets (const char *name, FILE *f) +{ + int t; + const char **targ_names; + + if (name == NULL) + fprintf (f, _("Supported targets:")); + else + fprintf (f, _("%s: supported targets:"), name); + + targ_names = bfd_target_list (); + for (t = 0; targ_names[t] != NULL; t++) + fprintf (f, " %s", targ_names[t]); + fprintf (f, "\n"); + free (targ_names); +} + +/* List the supported architectures. */ + +void +list_supported_architectures (const char *name, FILE *f) +{ + const char ** arch; + const char ** arches; + + if (name == NULL) + fprintf (f, _("Supported architectures:")); + else + fprintf (f, _("%s: supported architectures:"), name); + + for (arch = arches = bfd_arch_list (); *arch; arch++) + fprintf (f, " %s", *arch); + fprintf (f, "\n"); + free (arches); +} + +/* The length of the longest architecture name + 1. */ +#define LONGEST_ARCH sizeof ("powerpc:common") + +static const char * +endian_string (enum bfd_endian endian) +{ + switch (endian) + { + case BFD_ENDIAN_BIG: return _("big endian"); + case BFD_ENDIAN_LITTLE: return _("little endian"); + default: return _("endianness unknown"); + } +} + +/* List the targets that BFD is configured to support, each followed + by its endianness and the architectures it supports. */ + +static int +display_target_list (void) +{ + char *dummy_name; + int t; + int ret = 1; + + dummy_name = make_temp_file (NULL); + for (t = 0; bfd_target_vector[t]; t++) + { + const bfd_target *p = bfd_target_vector[t]; + bfd *abfd = bfd_openw (dummy_name, p->name); + int a; + + printf (_("%s\n (header %s, data %s)\n"), p->name, + endian_string (p->header_byteorder), + endian_string (p->byteorder)); + + if (abfd == NULL) + { + bfd_nonfatal (dummy_name); + ret = 0; + continue; + } + + if (! bfd_set_format (abfd, bfd_object)) + { + if (bfd_get_error () != bfd_error_invalid_operation) + { + bfd_nonfatal (p->name); + ret = 0; + } + bfd_close_all_done (abfd); + continue; + } + + for (a = bfd_arch_obscure + 1; a < bfd_arch_last; a++) + if (bfd_set_arch_mach (abfd, (enum bfd_architecture) a, 0)) + printf (" %s\n", + bfd_printable_arch_mach ((enum bfd_architecture) a, 0)); + bfd_close_all_done (abfd); + } + unlink (dummy_name); + free (dummy_name); + + return ret; +} + +/* Print a table showing which architectures are supported for entries + FIRST through LAST-1 of bfd_target_vector (targets across, + architectures down). */ + +static int +display_info_table (int first, int last) +{ + int t; + int ret = 1; + char *dummy_name; + int a; + + /* Print heading of target names. */ + printf ("\n%*s", (int) LONGEST_ARCH, " "); + for (t = first; t < last && bfd_target_vector[t]; t++) + printf ("%s ", bfd_target_vector[t]->name); + putchar ('\n'); + + dummy_name = make_temp_file (NULL); + for (a = bfd_arch_obscure + 1; a < bfd_arch_last; a++) + if (strcmp (bfd_printable_arch_mach ((enum bfd_architecture) a, 0), + "UNKNOWN!") != 0) + { + printf ("%*s ", (int) LONGEST_ARCH - 1, + bfd_printable_arch_mach ((enum bfd_architecture) a, 0)); + for (t = first; t < last && bfd_target_vector[t]; t++) + { + const bfd_target *p = bfd_target_vector[t]; + bfd_boolean ok = TRUE; + bfd *abfd = bfd_openw (dummy_name, p->name); + + if (abfd == NULL) + { + bfd_nonfatal (p->name); + ret = 0; + ok = FALSE; + } + + if (ok) + { + if (! bfd_set_format (abfd, bfd_object)) + { + if (bfd_get_error () != bfd_error_invalid_operation) + { + bfd_nonfatal (p->name); + ret = 0; + } + ok = FALSE; + } + } + + if (ok) + { + if (! bfd_set_arch_mach (abfd, (enum bfd_architecture) a, 0)) + ok = FALSE; + } + + if (ok) + printf ("%s ", p->name); + else + { + int l = strlen (p->name); + while (l--) + putchar ('-'); + putchar (' '); + } + if (abfd != NULL) + bfd_close_all_done (abfd); + } + putchar ('\n'); + } + unlink (dummy_name); + free (dummy_name); + + return ret; +} + +/* Print tables of all the target-architecture combinations that + BFD has been configured to support. */ + +static int +display_target_tables (void) +{ + int t; + int columns; + int ret = 1; + char *colum; + + columns = 0; + colum = getenv ("COLUMNS"); + if (colum != NULL) + columns = atoi (colum); + if (columns == 0) + columns = 80; + + t = 0; + while (bfd_target_vector[t] != NULL) + { + int oldt = t, wid; + + wid = LONGEST_ARCH + strlen (bfd_target_vector[t]->name) + 1; + ++t; + while (wid < columns && bfd_target_vector[t] != NULL) + { + int newwid; + + newwid = wid + strlen (bfd_target_vector[t]->name) + 1; + if (newwid >= columns) + break; + wid = newwid; + ++t; + } + if (! display_info_table (oldt, t)) + ret = 0; + } + + return ret; +} + +int +display_info (void) +{ + printf (_("BFD header file version %s\n"), BFD_VERSION_STRING); + if (! display_target_list () || ! display_target_tables ()) + return 1; + else + return 0; +} + +/* Display the archive header for an element as if it were an ls -l listing: + + Mode User\tGroup\tSize\tDate Name */ + +void +print_arelt_descr (FILE *file, bfd *abfd, bfd_boolean verbose) +{ + struct stat buf; + + if (verbose) + { + if (bfd_stat_arch_elt (abfd, &buf) == 0) + { + char modebuf[11]; + char timebuf[40]; + time_t when = buf.st_mtime; + const char *ctime_result = (const char *) ctime (&when); + bfd_size_type size; + + /* POSIX format: skip weekday and seconds from ctime output. */ + sprintf (timebuf, "%.12s %.4s", ctime_result + 4, ctime_result + 20); + + mode_string (buf.st_mode, modebuf); + modebuf[10] = '\0'; + size = buf.st_size; + /* POSIX 1003.2/D11 says to skip first character (entry type). */ + fprintf (file, "%s %ld/%ld %6" BFD_VMA_FMT "u %s ", modebuf + 1, + (long) buf.st_uid, (long) buf.st_gid, + size, timebuf); + } + } + + fprintf (file, "%s\n", bfd_get_filename (abfd)); +} + +/* Return a path for a new temporary file in the same directory + as file PATH. */ + +static char * +template_in_dir (const char *path) +{ +#define template "stXXXXXX" + const char *slash = strrchr (path, '/'); + char *tmpname; + size_t len; + +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + { + /* We could have foo/bar\\baz, or foo\\bar, or d:bar. */ + char *bslash = strrchr (path, '\\'); + + if (slash == NULL || (bslash != NULL && bslash > slash)) + slash = bslash; + if (slash == NULL && path[0] != '\0' && path[1] == ':') + slash = path + 1; + } +#endif + + if (slash != (char *) NULL) + { + len = slash - path; + tmpname = (char *) xmalloc (len + sizeof (template) + 2); + memcpy (tmpname, path, len); + +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + /* If tmpname is "X:", appending a slash will make it a root + directory on drive X, which is NOT the same as the current + directory on drive X. */ + if (len == 2 && tmpname[1] == ':') + tmpname[len++] = '.'; +#endif + tmpname[len++] = '/'; + } + else + { + tmpname = (char *) xmalloc (sizeof (template)); + len = 0; + } + + memcpy (tmpname + len, template, sizeof (template)); + return tmpname; +#undef template +} + +/* Return the name of a created temporary file in the same directory + as FILENAME. */ + +char * +make_tempname (char *filename) +{ + char *tmpname = template_in_dir (filename); + int fd; + +#ifdef HAVE_MKSTEMP + fd = mkstemp (tmpname); +#else + tmpname = mktemp (tmpname); + if (tmpname == NULL) + return NULL; + fd = open (tmpname, O_RDWR | O_CREAT | O_EXCL, 0600); +#endif + if (fd == -1) + { + free (tmpname); + return NULL; + } + close (fd); + return tmpname; +} + +/* Return the name of a created temporary directory inside the + directory containing FILENAME. */ + +char * +make_tempdir (char *filename) +{ + char *tmpname = template_in_dir (filename); + +/* +#ifdef HAVE_MKDTEMP + return mkdtemp (tmpname); +#else + tmpname = mktemp (tmpname); + if (tmpname == NULL) + return NULL; +#if defined (_WIN32) && !defined (__CYGWIN32__) + if (mkdir (tmpname) != 0) + return NULL; +#else + if (mkdir (tmpname, 0700) != 0) + return NULL; +#endif + return tmpname; +#endif +*/ + return NULL; +} + +/* Parse a string into a VMA, with a fatal error if it can't be + parsed. */ + +bfd_vma +parse_vma (const char *s, const char *arg) +{ + bfd_vma ret; + const char *end; + + ret = bfd_scan_vma (s, &end, 0); + + if (*end != '\0') + fatal (_("%s: bad number: %s"), arg, s); + + return ret; +} + +/* Returns the size of the named file. If the file does not + exist, or if it is not a real file, then a suitable non-fatal + error message is printed and (off_t) -1 is returned. */ + +off_t +get_file_size (const char * file_name) +{ + struct stat statbuf; + + if (stat (file_name, &statbuf) < 0) + { + if (errno == ENOENT) + non_fatal (_("'%s': No such file"), file_name); + else + non_fatal (_("Warning: could not locate '%s'. reason: %s"), + file_name, strerror (errno)); + } + else if (! S_ISREG (statbuf.st_mode)) + non_fatal (_("Warning: '%s' is not an ordinary file"), file_name); + else if (statbuf.st_size < 0) + non_fatal (_("Warning: '%s' has negative size, probably it is too large"), + file_name); + else + return statbuf.st_size; + + return (off_t) -1; +} + +/* Return the filename in a static buffer. */ + +const char * +bfd_get_archive_filename (const bfd *abfd) +{ + static size_t curr = 0; + static char *buf; + size_t needed; + + assert (abfd != NULL); + + if (!abfd->my_archive) + return bfd_get_filename (abfd); + + needed = (strlen (bfd_get_filename (abfd->my_archive)) + + strlen (bfd_get_filename (abfd)) + 3); + if (needed > curr) + { + if (curr) + free (buf); + curr = needed + (needed >> 1); + buf = (char *) bfd_malloc (curr); + /* If we can't malloc, fail safe by returning just the file name. + This function is only used when building error messages. */ + if (!buf) + { + curr = 0; + return bfd_get_filename (abfd); + } + } + sprintf (buf, "%s(%s)", bfd_get_filename (abfd->my_archive), + bfd_get_filename (abfd)); + return buf; +} diff --git a/contrib/toolchain/binutils/binutils/bucomm.h b/contrib/toolchain/binutils/binutils/bucomm.h new file mode 100644 index 0000000000..fcbc32b738 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/bucomm.h @@ -0,0 +1,79 @@ +/* bucomm.h -- binutils common include file. + Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 + Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef _BUCOMM_H +#define _BUCOMM_H + +/* Return the filename in a static buffer. */ +const char *bfd_get_archive_filename (const bfd *); + +void bfd_nonfatal (const char *); + +void bfd_nonfatal_message (const char *, const bfd *, const asection *, + const char *, ...); + +void bfd_fatal (const char *) ATTRIBUTE_NORETURN; + +void report (const char *, va_list) ATTRIBUTE_PRINTF(1,0); + +void fatal (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN; + +void non_fatal (const char *, ...) ATTRIBUTE_PRINTF_1; + +void set_default_bfd_target (void); + +void list_matching_formats (char **); + +void list_supported_targets (const char *, FILE *); + +void list_supported_architectures (const char *, FILE *); + +int display_info (void); + +void print_arelt_descr (FILE *, bfd *, bfd_boolean); + +char *make_tempname (char *); +char *make_tempdir (char *); + +bfd_vma parse_vma (const char *, const char *); + +off_t get_file_size (const char *); + +extern char *program_name; + +/* filemode.c */ +void mode_string (unsigned long, char *); + +/* version.c */ +extern void print_version (const char *); + +/* rename.c */ +extern void set_times (const char *, const struct stat *); + +extern int smart_rename (const char *, const char *, int); + +/* libiberty. */ +void *xmalloc (size_t); + +void *xrealloc (void *, size_t); + +#endif /* _BUCOMM_H */ diff --git a/contrib/toolchain/binutils/binutils/budbg.h b/contrib/toolchain/binutils/binutils/budbg.h new file mode 100644 index 0000000000..b87defbda4 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/budbg.h @@ -0,0 +1,57 @@ +/* budbg.c -- Interfaces to the generic debugging information routines. + Copyright 1995, 1996, 2002, 2003, 2005, 2007, 2008, 2012 + Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef BUDBG_H +#define BUDBG_H + +/* Routine used to read generic debugging information. */ + +extern void *read_debugging_info (bfd *, asymbol **, long, bfd_boolean); + +/* Routine used to print generic debugging information. */ + +extern bfd_boolean print_debugging_info + (FILE *, void *, bfd *, asymbol **, void *, bfd_boolean); + +/* Routines used to read and write stabs information. */ + +extern void *start_stab (void *, bfd *, bfd_boolean, asymbol **, long); + +extern bfd_boolean finish_stab (void *, void *); + +extern bfd_boolean parse_stab + (void *, void *, int, int, bfd_vma, const char *); + +extern bfd_boolean write_stabs_in_sections_debugging_info + (bfd *, void *, bfd_byte **, bfd_size_type *, bfd_byte **, bfd_size_type *); + +/* Routines used to read and write IEEE debugging information. */ + +extern bfd_boolean parse_ieee (void *, bfd *, const bfd_byte *, bfd_size_type); + +extern bfd_boolean write_ieee_debugging_info (bfd *, void *); + +/* Routine used to read COFF debugging information. */ + +extern bfd_boolean parse_coff (bfd *, asymbol **, long, void *); + +#endif diff --git a/contrib/toolchain/binutils/binutils/config.h b/contrib/toolchain/binutils/binutils/config.h new file mode 100644 index 0000000000..1bac8299ff --- /dev/null +++ b/contrib/toolchain/binutils/binutils/config.h @@ -0,0 +1,273 @@ +/* config.h. Generated from config.in by configure. */ +/* config.in. Generated from configure.in by autoheader. */ + +/* Check that config.h is #included before system headers + (this works only for glibc, but that should be enough). */ +#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__CONFIG_H__) +# error config.h must be #included before system headers +#endif + +#define __CONFIG_H__ 1 + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Should ar and ranlib use -D behavior by default? */ +#define DEFAULT_AR_DETERMINISTIC 0 + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +/* #undef ENABLE_NLS */ + +/* Suffix used for executables, if any. */ +#define EXECUTABLE_SUFFIX ".exe" + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define to 1 if you have the declaration of `environ', and to 0 if you + don't. */ +#define HAVE_DECL_ENVIRON 1 + +/* Define to 1 if you have the declaration of `fprintf', and to 0 if you + don't. */ +#define HAVE_DECL_FPRINTF 1 + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_GETC_UNLOCKED 0 + +/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't. + */ +#define HAVE_DECL_GETENV 1 + +/* Is the prototype for getopt in in the expected format? */ +#define HAVE_DECL_GETOPT 1 + +/* Define to 1 if you have the declaration of `sbrk', and to 0 if you don't. + */ +#define HAVE_DECL_SBRK 0 + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 1 + +/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't. + */ +#define HAVE_DECL_STPCPY 0 + +/* Define to 1 if you have the declaration of `strnlen', and to 0 if you + don't. */ +#define HAVE_DECL_STRNLEN 0 + +/* Define to 1 if you have the declaration of `strstr', and to 0 if you don't. + */ +#define HAVE_DECL_STRSTR 1 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Does the platform use an executable suffix? */ +#define HAVE_EXECUTABLE_SUFFIX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getc_unlocked' function. */ +/* #undef HAVE_GETC_UNLOCKED */ + +/* Does define struct utimbuf? */ +#define HAVE_GOOD_UTIME_H 1 + +/* Define if you have the iconv() function. */ +#define HAVE_ICONV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if your file defines LC_MESSAGES. */ +/* #undef HAVE_LC_MESSAGES */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if mbstate_t exists in wchar.h. */ +#define HAVE_MBSTATE_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mkdtemp' function. */ +/* #undef HAVE_MKDTEMP */ + +/* Define to 1 if you have the `mkstemp' function. */ +#define HAVE_MKSTEMP 1 + +/* Define to 1 if you have the `sbrk' function. */ +/* #undef HAVE_SBRK */ + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setmode' function. */ +#define HAVE_SETMODE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcoll' function. */ +#define HAVE_STRCOLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +/* #undef HAVE_SYS_WAIT_H */ + +/* Is the type time_t defined in ? */ +#define HAVE_TIME_T_IN_TIME_H 1 + +/* Is the type time_t defined in ? */ +#define HAVE_TIME_T_IN_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `utimes' function. */ +/* #undef HAVE_UTIMES */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ZLIB_H 1 + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "binutils" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define STRING_WITH_STRINGS 1 + +/* Configured target name. */ +#define TARGET "i686-pc-mingw32" + +/* Define to 1 if user symbol names have a leading underscore, 0 if not. */ +#define TARGET_PREPENDS_UNDERSCORE 1 + +/* Use b modifier when opening binary files? */ +#define USE_BINARY_FOPEN 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "2.24" + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +/* #undef YYTEXT_POINTER */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ diff --git a/contrib/toolchain/binutils/binutils/debug.c b/contrib/toolchain/binutils/binutils/debug.c new file mode 100644 index 0000000000..ee0d62e8ff --- /dev/null +++ b/contrib/toolchain/binutils/binutils/debug.c @@ -0,0 +1,3371 @@ +/* debug.c -- Handle generic debugging information. + Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2005, 2007, + 2009 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + + +/* This file implements a generic debugging format. We may eventually + have readers which convert different formats into this generic + format, and writers which write it out. The initial impetus for + this was writing a converter from stabs to HP IEEE-695 debugging + format. */ + +#include "sysdep.h" +#include +#include "bfd.h" +#include "libiberty.h" +#include "filenames.h" +#include "debug.h" + +/* Global information we keep for debugging. A pointer to this + structure is the debugging handle passed to all the routines. */ + +struct debug_handle +{ + /* A linked list of compilation units. */ + struct debug_unit *units; + /* The current compilation unit. */ + struct debug_unit *current_unit; + /* The current source file. */ + struct debug_file *current_file; + /* The current function. */ + struct debug_function *current_function; + /* The current block. */ + struct debug_block *current_block; + /* The current line number information for the current unit. */ + struct debug_lineno *current_lineno; + /* Mark. This is used by debug_write. */ + unsigned int mark; + /* A struct/class ID used by debug_write. */ + unsigned int class_id; + /* The base for class_id for this call to debug_write. */ + unsigned int base_id; + /* The current line number in debug_write. */ + struct debug_lineno *current_write_lineno; + unsigned int current_write_lineno_index; + /* A list of classes which have assigned ID's during debug_write. + This is linked through the next_id field of debug_class_type. */ + struct debug_class_id *id_list; + /* A list used to avoid recursion during debug_type_samep. */ + struct debug_type_compare_list *compare_list; +}; + +/* Information we keep for a single compilation unit. */ + +struct debug_unit +{ + /* The next compilation unit. */ + struct debug_unit *next; + /* A list of files included in this compilation unit. The first + file is always the main one, and that is where the main file name + is stored. */ + struct debug_file *files; + /* Line number information for this compilation unit. This is not + stored by function, because assembler code may have line number + information without function information. */ + struct debug_lineno *linenos; +}; + +/* Information kept for a single source file. */ + +struct debug_file +{ + /* The next source file in this compilation unit. */ + struct debug_file *next; + /* The name of the source file. */ + const char *filename; + /* Global functions, variables, types, etc. */ + struct debug_namespace *globals; +}; + +/* A type. */ + +struct debug_type_s +{ + /* Kind of type. */ + enum debug_type_kind kind; + /* Size of type (0 if not known). */ + unsigned int size; + /* Type which is a pointer to this type. */ + debug_type pointer; + /* Tagged union with additional information about the type. */ + union + { + /* DEBUG_KIND_INDIRECT. */ + struct debug_indirect_type *kindirect; + /* DEBUG_KIND_INT. */ + /* Whether the integer is unsigned. */ + bfd_boolean kint; + /* DEBUG_KIND_STRUCT, DEBUG_KIND_UNION, DEBUG_KIND_CLASS, + DEBUG_KIND_UNION_CLASS. */ + struct debug_class_type *kclass; + /* DEBUG_KIND_ENUM. */ + struct debug_enum_type *kenum; + /* DEBUG_KIND_POINTER. */ + struct debug_type_s *kpointer; + /* DEBUG_KIND_FUNCTION. */ + struct debug_function_type *kfunction; + /* DEBUG_KIND_REFERENCE. */ + struct debug_type_s *kreference; + /* DEBUG_KIND_RANGE. */ + struct debug_range_type *krange; + /* DEBUG_KIND_ARRAY. */ + struct debug_array_type *karray; + /* DEBUG_KIND_SET. */ + struct debug_set_type *kset; + /* DEBUG_KIND_OFFSET. */ + struct debug_offset_type *koffset; + /* DEBUG_KIND_METHOD. */ + struct debug_method_type *kmethod; + /* DEBUG_KIND_CONST. */ + struct debug_type_s *kconst; + /* DEBUG_KIND_VOLATILE. */ + struct debug_type_s *kvolatile; + /* DEBUG_KIND_NAMED, DEBUG_KIND_TAGGED. */ + struct debug_named_type *knamed; + } u; +}; + +/* Information kept for an indirect type. */ + +struct debug_indirect_type +{ + /* Slot where the final type will appear. */ + debug_type *slot; + /* Tag. */ + const char *tag; +}; + +/* Information kept for a struct, union, or class. */ + +struct debug_class_type +{ + /* NULL terminated array of fields. */ + debug_field *fields; + /* A mark field which indicates whether the struct has already been + printed. */ + unsigned int mark; + /* This is used to uniquely identify unnamed structs when printing. */ + unsigned int id; + /* The remaining fields are only used for DEBUG_KIND_CLASS and + DEBUG_KIND_UNION_CLASS. */ + /* NULL terminated array of base classes. */ + debug_baseclass *baseclasses; + /* NULL terminated array of methods. */ + debug_method *methods; + /* The type of the class providing the virtual function table for + this class. This may point to the type itself. */ + debug_type vptrbase; +}; + +/* Information kept for an enum. */ + +struct debug_enum_type +{ + /* NULL terminated array of names. */ + const char **names; + /* Array of corresponding values. */ + bfd_signed_vma *values; +}; + +/* Information kept for a function. FIXME: We should be able to + record the parameter types. */ + +struct debug_function_type +{ + /* Return type. */ + debug_type return_type; + /* NULL terminated array of argument types. */ + debug_type *arg_types; + /* Whether the function takes a variable number of arguments. */ + bfd_boolean varargs; +}; + +/* Information kept for a range. */ + +struct debug_range_type +{ + /* Range base type. */ + debug_type type; + /* Lower bound. */ + bfd_signed_vma lower; + /* Upper bound. */ + bfd_signed_vma upper; +}; + +/* Information kept for an array. */ + +struct debug_array_type +{ + /* Element type. */ + debug_type element_type; + /* Range type. */ + debug_type range_type; + /* Lower bound. */ + bfd_signed_vma lower; + /* Upper bound. */ + bfd_signed_vma upper; + /* Whether this array is really a string. */ + bfd_boolean stringp; +}; + +/* Information kept for a set. */ + +struct debug_set_type +{ + /* Base type. */ + debug_type type; + /* Whether this set is really a bitstring. */ + bfd_boolean bitstringp; +}; + +/* Information kept for an offset type (a based pointer). */ + +struct debug_offset_type +{ + /* The type the pointer is an offset from. */ + debug_type base_type; + /* The type the pointer points to. */ + debug_type target_type; +}; + +/* Information kept for a method type. */ + +struct debug_method_type +{ + /* The return type. */ + debug_type return_type; + /* The object type which this method is for. */ + debug_type domain_type; + /* A NULL terminated array of argument types. */ + debug_type *arg_types; + /* Whether the method takes a variable number of arguments. */ + bfd_boolean varargs; +}; + +/* Information kept for a named type. */ + +struct debug_named_type +{ + /* Name. */ + struct debug_name *name; + /* Real type. */ + debug_type type; +}; + +/* A field in a struct or union. */ + +struct debug_field_s +{ + /* Name of the field. */ + const char *name; + /* Type of the field. */ + struct debug_type_s *type; + /* Visibility of the field. */ + enum debug_visibility visibility; + /* Whether this is a static member. */ + bfd_boolean static_member; + union + { + /* If static_member is false. */ + struct + { + /* Bit position of the field in the struct. */ + unsigned int bitpos; + /* Size of the field in bits. */ + unsigned int bitsize; + } f; + /* If static_member is true. */ + struct + { + const char *physname; + } s; + } u; +}; + +/* A base class for an object. */ + +struct debug_baseclass_s +{ + /* Type of the base class. */ + struct debug_type_s *type; + /* Bit position of the base class in the object. */ + unsigned int bitpos; + /* Whether the base class is virtual. */ + bfd_boolean is_virtual; + /* Visibility of the base class. */ + enum debug_visibility visibility; +}; + +/* A method of an object. */ + +struct debug_method_s +{ + /* The name of the method. */ + const char *name; + /* A NULL terminated array of different types of variants. */ + struct debug_method_variant_s **variants; +}; + +/* The variants of a method function of an object. These indicate + which method to run. */ + +struct debug_method_variant_s +{ + /* The physical name of the function. */ + const char *physname; + /* The type of the function. */ + struct debug_type_s *type; + /* The visibility of the function. */ + enum debug_visibility visibility; + /* Whether the function is const. */ + bfd_boolean constp; + /* Whether the function is volatile. */ + bfd_boolean volatilep; + /* The offset to the function in the virtual function table. */ + bfd_vma voffset; + /* If voffset is VOFFSET_STATIC_METHOD, this is a static method. */ +#define VOFFSET_STATIC_METHOD ((bfd_vma) -1) + /* Context of a virtual method function. */ + struct debug_type_s *context; +}; + +/* A variable. This is the information we keep for a variable object. + This has no name; a name is associated with a variable in a + debug_name structure. */ + +struct debug_variable +{ + /* Kind of variable. */ + enum debug_var_kind kind; + /* Type. */ + debug_type type; + /* Value. The interpretation of the value depends upon kind. */ + bfd_vma val; +}; + +/* A function. This has no name; a name is associated with a function + in a debug_name structure. */ + +struct debug_function +{ + /* Return type. */ + debug_type return_type; + /* Parameter information. */ + struct debug_parameter *parameters; + /* Block information. The first structure on the list is the main + block of the function, and describes function local variables. */ + struct debug_block *blocks; +}; + +/* A function parameter. */ + +struct debug_parameter +{ + /* Next parameter. */ + struct debug_parameter *next; + /* Name. */ + const char *name; + /* Type. */ + debug_type type; + /* Kind. */ + enum debug_parm_kind kind; + /* Value (meaning depends upon kind). */ + bfd_vma val; +}; + +/* A typed constant. */ + +struct debug_typed_constant +{ + /* Type. */ + debug_type type; + /* Value. FIXME: We may eventually need to support non-integral + values. */ + bfd_vma val; +}; + +/* Information about a block within a function. */ + +struct debug_block +{ + /* Next block with the same parent. */ + struct debug_block *next; + /* Parent block. */ + struct debug_block *parent; + /* List of child blocks. */ + struct debug_block *children; + /* Start address of the block. */ + bfd_vma start; + /* End address of the block. */ + bfd_vma end; + /* Local variables. */ + struct debug_namespace *locals; +}; + +/* Line number information we keep for a compilation unit. FIXME: + This structure is easy to create, but can be very space + inefficient. */ + +struct debug_lineno +{ + /* More line number information for this block. */ + struct debug_lineno *next; + /* Source file. */ + struct debug_file *file; + /* Line numbers, terminated by a -1 or the end of the array. */ +#define DEBUG_LINENO_COUNT 10 + unsigned long linenos[DEBUG_LINENO_COUNT]; + /* Addresses for the line numbers. */ + bfd_vma addrs[DEBUG_LINENO_COUNT]; +}; + +/* A namespace. This is a mapping from names to objects. FIXME: This + should be implemented as a hash table. */ + +struct debug_namespace +{ + /* List of items in this namespace. */ + struct debug_name *list; + /* Pointer to where the next item in this namespace should go. */ + struct debug_name **tail; +}; + +/* Kinds of objects that appear in a namespace. */ + +enum debug_object_kind +{ + /* A type. */ + DEBUG_OBJECT_TYPE, + /* A tagged type (really a different sort of namespace). */ + DEBUG_OBJECT_TAG, + /* A variable. */ + DEBUG_OBJECT_VARIABLE, + /* A function. */ + DEBUG_OBJECT_FUNCTION, + /* An integer constant. */ + DEBUG_OBJECT_INT_CONSTANT, + /* A floating point constant. */ + DEBUG_OBJECT_FLOAT_CONSTANT, + /* A typed constant. */ + DEBUG_OBJECT_TYPED_CONSTANT +}; + +/* Linkage of an object that appears in a namespace. */ + +enum debug_object_linkage +{ + /* Local variable. */ + DEBUG_LINKAGE_AUTOMATIC, + /* Static--either file static or function static, depending upon the + namespace is. */ + DEBUG_LINKAGE_STATIC, + /* Global. */ + DEBUG_LINKAGE_GLOBAL, + /* No linkage. */ + DEBUG_LINKAGE_NONE +}; + +/* A name in a namespace. */ + +struct debug_name +{ + /* Next name in this namespace. */ + struct debug_name *next; + /* Name. */ + const char *name; + /* Mark. This is used by debug_write. */ + unsigned int mark; + /* Kind of object. */ + enum debug_object_kind kind; + /* Linkage of object. */ + enum debug_object_linkage linkage; + /* Tagged union with additional information about the object. */ + union + { + /* DEBUG_OBJECT_TYPE. */ + struct debug_type_s *type; + /* DEBUG_OBJECT_TAG. */ + struct debug_type_s *tag; + /* DEBUG_OBJECT_VARIABLE. */ + struct debug_variable *variable; + /* DEBUG_OBJECT_FUNCTION. */ + struct debug_function *function; + /* DEBUG_OBJECT_INT_CONSTANT. */ + bfd_vma int_constant; + /* DEBUG_OBJECT_FLOAT_CONSTANT. */ + double float_constant; + /* DEBUG_OBJECT_TYPED_CONSTANT. */ + struct debug_typed_constant *typed_constant; + } u; +}; + +/* During debug_write, a linked list of these structures is used to + keep track of ID numbers that have been assigned to classes. */ + +struct debug_class_id +{ + /* Next ID number. */ + struct debug_class_id *next; + /* The type with the ID. */ + struct debug_type_s *type; + /* The tag; NULL if no tag. */ + const char *tag; +}; + +/* During debug_type_samep, a linked list of these structures is kept + on the stack to avoid infinite recursion. */ + +struct debug_type_compare_list +{ + /* Next type on list. */ + struct debug_type_compare_list *next; + /* The types we are comparing. */ + struct debug_type_s *t1; + struct debug_type_s *t2; +}; + +/* During debug_get_real_type, a linked list of these structures is + kept on the stack to avoid infinite recursion. */ + +struct debug_type_real_list +{ + /* Next type on list. */ + struct debug_type_real_list *next; + /* The type we are checking. */ + struct debug_type_s *t; +}; + +/* Local functions. */ + +static void debug_error (const char *); +static struct debug_name *debug_add_to_namespace + (struct debug_handle *, struct debug_namespace **, const char *, + enum debug_object_kind, enum debug_object_linkage); +static struct debug_name *debug_add_to_current_namespace + (struct debug_handle *, const char *, enum debug_object_kind, + enum debug_object_linkage); +static struct debug_type_s *debug_make_type + (struct debug_handle *, enum debug_type_kind, unsigned int); +static struct debug_type_s *debug_get_real_type + (void *, debug_type, struct debug_type_real_list *); +static bfd_boolean debug_write_name + (struct debug_handle *, const struct debug_write_fns *, void *, + struct debug_name *); +static bfd_boolean debug_write_type + (struct debug_handle *, const struct debug_write_fns *, void *, + struct debug_type_s *, struct debug_name *); +static bfd_boolean debug_write_class_type + (struct debug_handle *, const struct debug_write_fns *, void *, + struct debug_type_s *, const char *); +static bfd_boolean debug_write_function + (struct debug_handle *, const struct debug_write_fns *, void *, + const char *, enum debug_object_linkage, struct debug_function *); +static bfd_boolean debug_write_block + (struct debug_handle *, const struct debug_write_fns *, void *, + struct debug_block *); +static bfd_boolean debug_write_linenos + (struct debug_handle *, const struct debug_write_fns *, void *, bfd_vma); +static bfd_boolean debug_set_class_id + (struct debug_handle *, const char *, struct debug_type_s *); +static bfd_boolean debug_type_samep + (struct debug_handle *, struct debug_type_s *, struct debug_type_s *); +static bfd_boolean debug_class_type_samep + (struct debug_handle *, struct debug_type_s *, struct debug_type_s *); + +/* Issue an error message. */ + +static void +debug_error (const char *message) +{ + fprintf (stderr, "%s\n", message); +} + +/* Add an object to a namespace. */ + +static struct debug_name * +debug_add_to_namespace (struct debug_handle *info ATTRIBUTE_UNUSED, + struct debug_namespace **nsp, const char *name, + enum debug_object_kind kind, + enum debug_object_linkage linkage) +{ + struct debug_name *n; + struct debug_namespace *ns; + + n = (struct debug_name *) xmalloc (sizeof *n); + memset (n, 0, sizeof *n); + + n->name = name; + n->kind = kind; + n->linkage = linkage; + + ns = *nsp; + if (ns == NULL) + { + ns = (struct debug_namespace *) xmalloc (sizeof *ns); + memset (ns, 0, sizeof *ns); + + ns->tail = &ns->list; + + *nsp = ns; + } + + *ns->tail = n; + ns->tail = &n->next; + + return n; +} + +/* Add an object to the current namespace. */ + +static struct debug_name * +debug_add_to_current_namespace (struct debug_handle *info, const char *name, + enum debug_object_kind kind, + enum debug_object_linkage linkage) +{ + struct debug_namespace **nsp; + + if (info->current_unit == NULL + || info->current_file == NULL) + { + debug_error (_("debug_add_to_current_namespace: no current file")); + return NULL; + } + + if (info->current_block != NULL) + nsp = &info->current_block->locals; + else + nsp = &info->current_file->globals; + + return debug_add_to_namespace (info, nsp, name, kind, linkage); +} + +/* Return a handle for debugging information. */ + +void * +debug_init (void) +{ + struct debug_handle *ret; + + ret = (struct debug_handle *) xmalloc (sizeof *ret); + memset (ret, 0, sizeof *ret); + return (void *) ret; +} + +/* Set the source filename. This implicitly starts a new compilation + unit. */ + +bfd_boolean +debug_set_filename (void *handle, const char *name) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_file *nfile; + struct debug_unit *nunit; + + if (name == NULL) + name = ""; + + nfile = (struct debug_file *) xmalloc (sizeof *nfile); + memset (nfile, 0, sizeof *nfile); + + nfile->filename = name; + + nunit = (struct debug_unit *) xmalloc (sizeof *nunit); + memset (nunit, 0, sizeof *nunit); + + nunit->files = nfile; + info->current_file = nfile; + + if (info->current_unit != NULL) + info->current_unit->next = nunit; + else + { + assert (info->units == NULL); + info->units = nunit; + } + + info->current_unit = nunit; + + info->current_function = NULL; + info->current_block = NULL; + info->current_lineno = NULL; + + return TRUE; +} + +/* Change source files to the given file name. This is used for + include files in a single compilation unit. */ + +bfd_boolean +debug_start_source (void *handle, const char *name) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_file *f, **pf; + + if (name == NULL) + name = ""; + + if (info->current_unit == NULL) + { + debug_error (_("debug_start_source: no debug_set_filename call")); + return FALSE; + } + + for (f = info->current_unit->files; f != NULL; f = f->next) + { + if (filename_cmp (f->filename, name) == 0) + { + info->current_file = f; + return TRUE; + } + } + + f = (struct debug_file *) xmalloc (sizeof *f); + memset (f, 0, sizeof *f); + + f->filename = name; + + for (pf = &info->current_file->next; + *pf != NULL; + pf = &(*pf)->next) + ; + *pf = f; + + info->current_file = f; + + return TRUE; +} + +/* Record a function definition. This implicitly starts a function + block. The debug_type argument is the type of the return value. + The boolean indicates whether the function is globally visible. + The bfd_vma is the address of the start of the function. Currently + the parameter types are specified by calls to + debug_record_parameter. FIXME: There is no way to specify nested + functions. */ + +bfd_boolean +debug_record_function (void *handle, const char *name, + debug_type return_type, bfd_boolean global, + bfd_vma addr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_function *f; + struct debug_block *b; + struct debug_name *n; + + if (name == NULL) + name = ""; + if (return_type == NULL) + return FALSE; + + if (info->current_unit == NULL) + { + debug_error (_("debug_record_function: no debug_set_filename call")); + return FALSE; + } + + f = (struct debug_function *) xmalloc (sizeof *f); + memset (f, 0, sizeof *f); + + f->return_type = return_type; + + b = (struct debug_block *) xmalloc (sizeof *b); + memset (b, 0, sizeof *b); + + b->start = addr; + b->end = (bfd_vma) -1; + + f->blocks = b; + + info->current_function = f; + info->current_block = b; + + /* FIXME: If we could handle nested functions, this would be the + place: we would want to use a different namespace. */ + n = debug_add_to_namespace (info, + &info->current_file->globals, + name, + DEBUG_OBJECT_FUNCTION, + (global + ? DEBUG_LINKAGE_GLOBAL + : DEBUG_LINKAGE_STATIC)); + if (n == NULL) + return FALSE; + + n->u.function = f; + + return TRUE; +} + +/* Record a parameter for the current function. */ + +bfd_boolean +debug_record_parameter (void *handle, const char *name, debug_type type, + enum debug_parm_kind kind, bfd_vma val) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_parameter *p, **pp; + + if (name == NULL || type == NULL) + return FALSE; + + if (info->current_unit == NULL + || info->current_function == NULL) + { + debug_error (_("debug_record_parameter: no current function")); + return FALSE; + } + + p = (struct debug_parameter *) xmalloc (sizeof *p); + memset (p, 0, sizeof *p); + + p->name = name; + p->type = type; + p->kind = kind; + p->val = val; + + for (pp = &info->current_function->parameters; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = p; + + return TRUE; +} + +/* End a function. FIXME: This should handle function nesting. */ + +bfd_boolean +debug_end_function (void *handle, bfd_vma addr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + + if (info->current_unit == NULL + || info->current_block == NULL + || info->current_function == NULL) + { + debug_error (_("debug_end_function: no current function")); + return FALSE; + } + + if (info->current_block->parent != NULL) + { + debug_error (_("debug_end_function: some blocks were not closed")); + return FALSE; + } + + info->current_block->end = addr; + + info->current_function = NULL; + info->current_block = NULL; + + return TRUE; +} + +/* Start a block in a function. All local information will be + recorded in this block, until the matching call to debug_end_block. + debug_start_block and debug_end_block may be nested. The bfd_vma + argument is the address at which this block starts. */ + +bfd_boolean +debug_start_block (void *handle, bfd_vma addr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_block *b, **pb; + + /* We must always have a current block: debug_record_function sets + one up. */ + if (info->current_unit == NULL + || info->current_block == NULL) + { + debug_error (_("debug_start_block: no current block")); + return FALSE; + } + + b = (struct debug_block *) xmalloc (sizeof *b); + memset (b, 0, sizeof *b); + + b->parent = info->current_block; + b->start = addr; + b->end = (bfd_vma) -1; + + /* This new block is a child of the current block. */ + for (pb = &info->current_block->children; + *pb != NULL; + pb = &(*pb)->next) + ; + *pb = b; + + info->current_block = b; + + return TRUE; +} + +/* Finish a block in a function. This matches the call to + debug_start_block. The argument is the address at which this block + ends. */ + +bfd_boolean +debug_end_block (void *handle, bfd_vma addr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_block *parent; + + if (info->current_unit == NULL + || info->current_block == NULL) + { + debug_error (_("debug_end_block: no current block")); + return FALSE; + } + + parent = info->current_block->parent; + if (parent == NULL) + { + debug_error (_("debug_end_block: attempt to close top level block")); + return FALSE; + } + + info->current_block->end = addr; + + info->current_block = parent; + + return TRUE; +} + +/* Associate a line number in the current source file and function + with a given address. */ + +bfd_boolean +debug_record_line (void *handle, unsigned long lineno, bfd_vma addr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_lineno *l; + unsigned int i; + + if (info->current_unit == NULL) + { + debug_error (_("debug_record_line: no current unit")); + return FALSE; + } + + l = info->current_lineno; + if (l != NULL && l->file == info->current_file) + { + for (i = 0; i < DEBUG_LINENO_COUNT; i++) + { + if (l->linenos[i] == (unsigned long) -1) + { + l->linenos[i] = lineno; + l->addrs[i] = addr; + return TRUE; + } + } + } + + /* If we get here, then either 1) there is no current_lineno + structure, which means this is the first line number in this + compilation unit, 2) the current_lineno structure is for a + different file, or 3) the current_lineno structure is full. + Regardless, we want to allocate a new debug_lineno structure, put + it in the right place, and make it the new current_lineno + structure. */ + + l = (struct debug_lineno *) xmalloc (sizeof *l); + memset (l, 0, sizeof *l); + + l->file = info->current_file; + l->linenos[0] = lineno; + l->addrs[0] = addr; + for (i = 1; i < DEBUG_LINENO_COUNT; i++) + l->linenos[i] = (unsigned long) -1; + + if (info->current_lineno != NULL) + info->current_lineno->next = l; + else + info->current_unit->linenos = l; + + info->current_lineno = l; + + return TRUE; +} + +/* Start a named common block. This is a block of variables that may + move in memory. */ + +bfd_boolean +debug_start_common_block (void *handle ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ + /* FIXME */ + debug_error (_("debug_start_common_block: not implemented")); + return FALSE; +} + +/* End a named common block. */ + +bfd_boolean +debug_end_common_block (void *handle ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED) +{ + /* FIXME */ + debug_error (_("debug_end_common_block: not implemented")); + return FALSE; +} + +/* Record a named integer constant. */ + +bfd_boolean +debug_record_int_const (void *handle, const char *name, bfd_vma val) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_name *n; + + if (name == NULL) + return FALSE; + + n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_INT_CONSTANT, + DEBUG_LINKAGE_NONE); + if (n == NULL) + return FALSE; + + n->u.int_constant = val; + + return TRUE; +} + +/* Record a named floating point constant. */ + +bfd_boolean +debug_record_float_const (void *handle, const char *name, double val) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_name *n; + + if (name == NULL) + return FALSE; + + n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_FLOAT_CONSTANT, + DEBUG_LINKAGE_NONE); + if (n == NULL) + return FALSE; + + n->u.float_constant = val; + + return TRUE; +} + +/* Record a typed constant with an integral value. */ + +bfd_boolean +debug_record_typed_const (void *handle, const char *name, debug_type type, + bfd_vma val) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_name *n; + struct debug_typed_constant *tc; + + if (name == NULL || type == NULL) + return FALSE; + + n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_TYPED_CONSTANT, + DEBUG_LINKAGE_NONE); + if (n == NULL) + return FALSE; + + tc = (struct debug_typed_constant *) xmalloc (sizeof *tc); + memset (tc, 0, sizeof *tc); + + tc->type = type; + tc->val = val; + + n->u.typed_constant = tc; + + return TRUE; +} + +/* Record a label. */ + +bfd_boolean +debug_record_label (void *handle ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED, + debug_type type ATTRIBUTE_UNUSED, + bfd_vma addr ATTRIBUTE_UNUSED) +{ + /* FIXME. */ + debug_error (_("debug_record_label: not implemented")); + return FALSE; +} + +/* Record a variable. */ + +bfd_boolean +debug_record_variable (void *handle, const char *name, debug_type type, + enum debug_var_kind kind, bfd_vma val) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_namespace **nsp; + enum debug_object_linkage linkage; + struct debug_name *n; + struct debug_variable *v; + + if (name == NULL || type == NULL) + return FALSE; + + if (info->current_unit == NULL + || info->current_file == NULL) + { + debug_error (_("debug_record_variable: no current file")); + return FALSE; + } + + if (kind == DEBUG_GLOBAL || kind == DEBUG_STATIC) + { + nsp = &info->current_file->globals; + if (kind == DEBUG_GLOBAL) + linkage = DEBUG_LINKAGE_GLOBAL; + else + linkage = DEBUG_LINKAGE_STATIC; + } + else + { + if (info->current_block == NULL) + nsp = &info->current_file->globals; + else + nsp = &info->current_block->locals; + linkage = DEBUG_LINKAGE_AUTOMATIC; + } + + n = debug_add_to_namespace (info, nsp, name, DEBUG_OBJECT_VARIABLE, linkage); + if (n == NULL) + return FALSE; + + v = (struct debug_variable *) xmalloc (sizeof *v); + memset (v, 0, sizeof *v); + + v->kind = kind; + v->type = type; + v->val = val; + + n->u.variable = v; + + return TRUE; +} + +/* Make a type with a given kind and size. */ + +static struct debug_type_s * +debug_make_type (struct debug_handle *info ATTRIBUTE_UNUSED, + enum debug_type_kind kind, unsigned int size) +{ + struct debug_type_s *t; + + t = (struct debug_type_s *) xmalloc (sizeof *t); + memset (t, 0, sizeof *t); + + t->kind = kind; + t->size = size; + + return t; +} + +/* Make an indirect type which may be used as a placeholder for a type + which is referenced before it is defined. */ + +debug_type +debug_make_indirect_type (void *handle, debug_type *slot, const char *tag) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_indirect_type *i; + + t = debug_make_type (info, DEBUG_KIND_INDIRECT, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + i = (struct debug_indirect_type *) xmalloc (sizeof *i); + memset (i, 0, sizeof *i); + + i->slot = slot; + i->tag = tag; + + t->u.kindirect = i; + + return t; +} + +/* Make a void type. There is only one of these. */ + +debug_type +debug_make_void_type (void *handle) +{ + struct debug_handle *info = (struct debug_handle *) handle; + + return debug_make_type (info, DEBUG_KIND_VOID, 0); +} + +/* Make an integer type of a given size. The boolean argument is true + if the integer is unsigned. */ + +debug_type +debug_make_int_type (void *handle, unsigned int size, bfd_boolean unsignedp) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + t = debug_make_type (info, DEBUG_KIND_INT, size); + if (t == NULL) + return DEBUG_TYPE_NULL; + + t->u.kint = unsignedp; + + return t; +} + +/* Make a floating point type of a given size. FIXME: On some + platforms, like an Alpha, you probably need to be able to specify + the format. */ + +debug_type +debug_make_float_type (void *handle, unsigned int size) +{ + struct debug_handle *info = (struct debug_handle *) handle; + + return debug_make_type (info, DEBUG_KIND_FLOAT, size); +} + +/* Make a boolean type of a given size. */ + +debug_type +debug_make_bool_type (void *handle, unsigned int size) +{ + struct debug_handle *info = (struct debug_handle *) handle; + + return debug_make_type (info, DEBUG_KIND_BOOL, size); +} + +/* Make a complex type of a given size. */ + +debug_type +debug_make_complex_type (void *handle, unsigned int size) +{ + struct debug_handle *info = (struct debug_handle *) handle; + + return debug_make_type (info, DEBUG_KIND_COMPLEX, size); +} + +/* Make a structure type. The second argument is true for a struct, + false for a union. The third argument is the size of the struct. + The fourth argument is a NULL terminated array of fields. */ + +debug_type +debug_make_struct_type (void *handle, bfd_boolean structp, bfd_vma size, + debug_field *fields) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_class_type *c; + + t = debug_make_type (info, + structp ? DEBUG_KIND_STRUCT : DEBUG_KIND_UNION, + size); + if (t == NULL) + return DEBUG_TYPE_NULL; + + c = (struct debug_class_type *) xmalloc (sizeof *c); + memset (c, 0, sizeof *c); + + c->fields = fields; + + t->u.kclass = c; + + return t; +} + +/* Make an object type. The first three arguments after the handle + are the same as for debug_make_struct_type. The next arguments are + a NULL terminated array of base classes, a NULL terminated array of + methods, the type of the object holding the virtual function table + if it is not this object, and a boolean which is true if this + object has its own virtual function table. */ + +debug_type +debug_make_object_type (void *handle, bfd_boolean structp, bfd_vma size, + debug_field *fields, debug_baseclass *baseclasses, + debug_method *methods, debug_type vptrbase, + bfd_boolean ownvptr) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_class_type *c; + + t = debug_make_type (info, + structp ? DEBUG_KIND_CLASS : DEBUG_KIND_UNION_CLASS, + size); + if (t == NULL) + return DEBUG_TYPE_NULL; + + c = (struct debug_class_type *) xmalloc (sizeof *c); + memset (c, 0, sizeof *c); + + c->fields = fields; + c->baseclasses = baseclasses; + c->methods = methods; + if (ownvptr) + c->vptrbase = t; + else + c->vptrbase = vptrbase; + + t->u.kclass = c; + + return t; +} + +/* Make an enumeration type. The arguments are a null terminated + array of strings, and an array of corresponding values. */ + +debug_type +debug_make_enum_type (void *handle, const char **names, + bfd_signed_vma *values) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_enum_type *e; + + t = debug_make_type (info, DEBUG_KIND_ENUM, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + e = (struct debug_enum_type *) xmalloc (sizeof *e); + memset (e, 0, sizeof *e); + + e->names = names; + e->values = values; + + t->u.kenum = e; + + return t; +} + +/* Make a pointer to a given type. */ + +debug_type +debug_make_pointer_type (void *handle, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + if (type->pointer != DEBUG_TYPE_NULL) + return type->pointer; + + t = debug_make_type (info, DEBUG_KIND_POINTER, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + t->u.kpointer = type; + + type->pointer = t; + + return t; +} + +/* Make a function returning a given type. FIXME: We should be able + to record the parameter types. */ + +debug_type +debug_make_function_type (void *handle, debug_type type, + debug_type *arg_types, bfd_boolean varargs) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_function_type *f; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_FUNCTION, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + f = (struct debug_function_type *) xmalloc (sizeof *f); + memset (f, 0, sizeof *f); + + f->return_type = type; + f->arg_types = arg_types; + f->varargs = varargs; + + t->u.kfunction = f; + + return t; +} + +/* Make a reference to a given type. */ + +debug_type +debug_make_reference_type (void *handle, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_REFERENCE, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + t->u.kreference = type; + + return t; +} + +/* Make a range of a given type from a lower to an upper bound. */ + +debug_type +debug_make_range_type (void *handle, debug_type type, bfd_signed_vma lower, + bfd_signed_vma upper) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_range_type *r; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_RANGE, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + r = (struct debug_range_type *) xmalloc (sizeof *r); + memset (r, 0, sizeof *r); + + r->type = type; + r->lower = lower; + r->upper = upper; + + t->u.krange = r; + + return t; +} + +/* Make an array type. The second argument is the type of an element + of the array. The third argument is the type of a range of the + array. The fourth and fifth argument are the lower and upper + bounds, respectively. The sixth argument is true if this array is + actually a string, as in C. */ + +debug_type +debug_make_array_type (void *handle, debug_type element_type, + debug_type range_type, bfd_signed_vma lower, + bfd_signed_vma upper, bfd_boolean stringp) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_array_type *a; + + if (element_type == NULL || range_type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_ARRAY, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + a = (struct debug_array_type *) xmalloc (sizeof *a); + memset (a, 0, sizeof *a); + + a->element_type = element_type; + a->range_type = range_type; + a->lower = lower; + a->upper = upper; + a->stringp = stringp; + + t->u.karray = a; + + return t; +} + +/* Make a set of a given type. For example, a Pascal set type. The + boolean argument is true if this set is actually a bitstring, as in + CHILL. */ + +debug_type +debug_make_set_type (void *handle, debug_type type, bfd_boolean bitstringp) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_set_type *s; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_SET, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + s = (struct debug_set_type *) xmalloc (sizeof *s); + memset (s, 0, sizeof *s); + + s->type = type; + s->bitstringp = bitstringp; + + t->u.kset = s; + + return t; +} + +/* Make a type for a pointer which is relative to an object. The + second argument is the type of the object to which the pointer is + relative. The third argument is the type that the pointer points + to. */ + +debug_type +debug_make_offset_type (void *handle, debug_type base_type, + debug_type target_type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_offset_type *o; + + if (base_type == NULL || target_type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_OFFSET, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + o = (struct debug_offset_type *) xmalloc (sizeof *o); + memset (o, 0, sizeof *o); + + o->base_type = base_type; + o->target_type = target_type; + + t->u.koffset = o; + + return t; +} + +/* Make a type for a method function. The second argument is the + return type, the third argument is the domain, and the fourth + argument is a NULL terminated array of argument types. */ + +debug_type +debug_make_method_type (void *handle, debug_type return_type, + debug_type domain_type, debug_type *arg_types, + bfd_boolean varargs) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_method_type *m; + + if (return_type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_METHOD, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + m = (struct debug_method_type *) xmalloc (sizeof *m); + memset (m, 0, sizeof *m); + + m->return_type = return_type; + m->domain_type = domain_type; + m->arg_types = arg_types; + m->varargs = varargs; + + t->u.kmethod = m; + + return t; +} + +/* Make a const qualified version of a given type. */ + +debug_type +debug_make_const_type (void *handle, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_CONST, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + t->u.kconst = type; + + return t; +} + +/* Make a volatile qualified version of a given type. */ + +debug_type +debug_make_volatile_type (void *handle, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + if (type == NULL) + return DEBUG_TYPE_NULL; + + t = debug_make_type (info, DEBUG_KIND_VOLATILE, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + t->u.kvolatile = type; + + return t; +} + +/* Make an undefined tagged type. For example, a struct which has + been mentioned, but not defined. */ + +debug_type +debug_make_undefined_tagged_type (void *handle, const char *name, + enum debug_type_kind kind) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + + if (name == NULL) + return DEBUG_TYPE_NULL; + + switch (kind) + { + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_UNION: + case DEBUG_KIND_CLASS: + case DEBUG_KIND_UNION_CLASS: + case DEBUG_KIND_ENUM: + break; + + default: + debug_error (_("debug_make_undefined_type: unsupported kind")); + return DEBUG_TYPE_NULL; + } + + t = debug_make_type (info, kind, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + return debug_tag_type (handle, name, t); +} + +/* Make a base class for an object. The second argument is the base + class type. The third argument is the bit position of this base + class in the object (always 0 unless doing multiple inheritance). + The fourth argument is whether this is a virtual class. The fifth + argument is the visibility of the base class. */ + +debug_baseclass +debug_make_baseclass (void *handle ATTRIBUTE_UNUSED, debug_type type, + bfd_vma bitpos, bfd_boolean is_virtual, + enum debug_visibility visibility) +{ + struct debug_baseclass_s *b; + + b = (struct debug_baseclass_s *) xmalloc (sizeof *b); + memset (b, 0, sizeof *b); + + b->type = type; + b->bitpos = bitpos; + b->is_virtual = is_virtual; + b->visibility = visibility; + + return b; +} + +/* Make a field for a struct. The second argument is the name. The + third argument is the type of the field. The fourth argument is + the bit position of the field. The fifth argument is the size of + the field (it may be zero). The sixth argument is the visibility + of the field. */ + +debug_field +debug_make_field (void *handle ATTRIBUTE_UNUSED, const char *name, + debug_type type, bfd_vma bitpos, bfd_vma bitsize, + enum debug_visibility visibility) +{ + struct debug_field_s *f; + + f = (struct debug_field_s *) xmalloc (sizeof *f); + memset (f, 0, sizeof *f); + + f->name = name; + f->type = type; + f->static_member = FALSE; + f->u.f.bitpos = bitpos; + f->u.f.bitsize = bitsize; + f->visibility = visibility; + + return f; +} + +/* Make a static member of an object. The second argument is the + name. The third argument is the type of the member. The fourth + argument is the physical name of the member (i.e., the name as a + global variable). The fifth argument is the visibility of the + member. */ + +debug_field +debug_make_static_member (void *handle ATTRIBUTE_UNUSED, const char *name, + debug_type type, const char *physname, + enum debug_visibility visibility) +{ + struct debug_field_s *f; + + f = (struct debug_field_s *) xmalloc (sizeof *f); + memset (f, 0, sizeof *f); + + f->name = name; + f->type = type; + f->static_member = TRUE; + f->u.s.physname = physname; + f->visibility = visibility; + + return f; +} + +/* Make a method. The second argument is the name, and the third + argument is a NULL terminated array of method variants. */ + +debug_method +debug_make_method (void *handle ATTRIBUTE_UNUSED, const char *name, + debug_method_variant *variants) +{ + struct debug_method_s *m; + + m = (struct debug_method_s *) xmalloc (sizeof *m); + memset (m, 0, sizeof *m); + + m->name = name; + m->variants = variants; + + return m; +} + +/* Make a method argument. The second argument is the real name of + the function. The third argument is the type of the function. The + fourth argument is the visibility. The fifth argument is whether + this is a const function. The sixth argument is whether this is a + volatile function. The seventh argument is the offset in the + virtual function table, if any. The eighth argument is the virtual + function context. FIXME: Are the const and volatile arguments + necessary? Could we just use debug_make_const_type? */ + +debug_method_variant +debug_make_method_variant (void *handle ATTRIBUTE_UNUSED, + const char *physname, debug_type type, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep, + bfd_vma voffset, debug_type context) +{ + struct debug_method_variant_s *m; + + m = (struct debug_method_variant_s *) xmalloc (sizeof *m); + memset (m, 0, sizeof *m); + + m->physname = physname; + m->type = type; + m->visibility = visibility; + m->constp = constp; + m->volatilep = volatilep; + m->voffset = voffset; + m->context = context; + + return m; +} + +/* Make a static method argument. The arguments are the same as for + debug_make_method_variant, except that the last two are omitted + since a static method can not also be virtual. */ + +debug_method_variant +debug_make_static_method_variant (void *handle ATTRIBUTE_UNUSED, + const char *physname, debug_type type, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep) +{ + struct debug_method_variant_s *m; + + m = (struct debug_method_variant_s *) xmalloc (sizeof *m); + memset (m, 0, sizeof *m); + + m->physname = physname; + m->type = type; + m->visibility = visibility; + m->constp = constp; + m->volatilep = volatilep; + m->voffset = VOFFSET_STATIC_METHOD; + + return m; +} + +/* Name a type. */ + +debug_type +debug_name_type (void *handle, const char *name, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_named_type *n; + struct debug_name *nm; + + if (name == NULL || type == NULL) + return DEBUG_TYPE_NULL; + + if (info->current_unit == NULL + || info->current_file == NULL) + { + debug_error (_("debug_name_type: no current file")); + return DEBUG_TYPE_NULL; + } + + t = debug_make_type (info, DEBUG_KIND_NAMED, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + n = (struct debug_named_type *) xmalloc (sizeof *n); + memset (n, 0, sizeof *n); + + n->type = type; + + t->u.knamed = n; + + /* We always add the name to the global namespace. This is probably + wrong in some cases, but it seems to be right for stabs. FIXME. */ + + nm = debug_add_to_namespace (info, &info->current_file->globals, name, + DEBUG_OBJECT_TYPE, DEBUG_LINKAGE_NONE); + if (nm == NULL) + return DEBUG_TYPE_NULL; + + nm->u.type = t; + + n->name = nm; + + return t; +} + +/* Tag a type. */ + +debug_type +debug_tag_type (void *handle, const char *name, debug_type type) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_type_s *t; + struct debug_named_type *n; + struct debug_name *nm; + + if (name == NULL || type == NULL) + return DEBUG_TYPE_NULL; + + if (info->current_file == NULL) + { + debug_error (_("debug_tag_type: no current file")); + return DEBUG_TYPE_NULL; + } + + if (type->kind == DEBUG_KIND_TAGGED) + { + if (strcmp (type->u.knamed->name->name, name) == 0) + return type; + debug_error (_("debug_tag_type: extra tag attempted")); + return DEBUG_TYPE_NULL; + } + + t = debug_make_type (info, DEBUG_KIND_TAGGED, 0); + if (t == NULL) + return DEBUG_TYPE_NULL; + + n = (struct debug_named_type *) xmalloc (sizeof *n); + memset (n, 0, sizeof *n); + + n->type = type; + + t->u.knamed = n; + + /* We keep a global namespace of tags for each compilation unit. I + don't know if that is the right thing to do. */ + + nm = debug_add_to_namespace (info, &info->current_file->globals, name, + DEBUG_OBJECT_TAG, DEBUG_LINKAGE_NONE); + if (nm == NULL) + return DEBUG_TYPE_NULL; + + nm->u.tag = t; + + n->name = nm; + + return t; +} + +/* Record the size of a given type. */ + +bfd_boolean +debug_record_type_size (void *handle ATTRIBUTE_UNUSED, debug_type type, + unsigned int size) +{ + if (type->size != 0 && type->size != size) + fprintf (stderr, _("Warning: changing type size from %d to %d\n"), + type->size, size); + + type->size = size; + + return TRUE; +} + +/* Find a named type. */ + +debug_type +debug_find_named_type (void *handle, const char *name) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_block *b; + struct debug_file *f; + + /* We only search the current compilation unit. I don't know if + this is right or not. */ + + if (info->current_unit == NULL) + { + debug_error (_("debug_find_named_type: no current compilation unit")); + return DEBUG_TYPE_NULL; + } + + for (b = info->current_block; b != NULL; b = b->parent) + { + if (b->locals != NULL) + { + struct debug_name *n; + + for (n = b->locals->list; n != NULL; n = n->next) + { + if (n->kind == DEBUG_OBJECT_TYPE + && n->name[0] == name[0] + && strcmp (n->name, name) == 0) + return n->u.type; + } + } + } + + for (f = info->current_unit->files; f != NULL; f = f->next) + { + if (f->globals != NULL) + { + struct debug_name *n; + + for (n = f->globals->list; n != NULL; n = n->next) + { + if (n->kind == DEBUG_OBJECT_TYPE + && n->name[0] == name[0] + && strcmp (n->name, name) == 0) + return n->u.type; + } + } + } + + return DEBUG_TYPE_NULL; +} + +/* Find a tagged type. */ + +debug_type +debug_find_tagged_type (void *handle, const char *name, + enum debug_type_kind kind) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_unit *u; + + /* We search the globals of all the compilation units. I don't know + if this is correct or not. It would be easy to change. */ + + for (u = info->units; u != NULL; u = u->next) + { + struct debug_file *f; + + for (f = u->files; f != NULL; f = f->next) + { + struct debug_name *n; + + if (f->globals != NULL) + { + for (n = f->globals->list; n != NULL; n = n->next) + { + if (n->kind == DEBUG_OBJECT_TAG + && (kind == DEBUG_KIND_ILLEGAL + || n->u.tag->kind == kind) + && n->name[0] == name[0] + && strcmp (n->name, name) == 0) + return n->u.tag; + } + } + } + } + + return DEBUG_TYPE_NULL; +} + +/* Get a base type. We build a linked list on the stack to avoid + crashing if the type is defined circularly. */ + +static struct debug_type_s * +debug_get_real_type (void *handle, debug_type type, + struct debug_type_real_list *list) +{ + struct debug_type_real_list *l; + struct debug_type_real_list rl; + + switch (type->kind) + { + default: + return type; + + case DEBUG_KIND_INDIRECT: + case DEBUG_KIND_NAMED: + case DEBUG_KIND_TAGGED: + break; + } + + for (l = list; l != NULL; l = l->next) + { + if (l->t == type || l == l->next) + { + fprintf (stderr, + _("debug_get_real_type: circular debug information for %s\n"), + debug_get_type_name (handle, type)); + return NULL; + } + } + + rl.next = list; + rl.t = type; + + switch (type->kind) + { + /* The default case is just here to avoid warnings. */ + default: + case DEBUG_KIND_INDIRECT: + if (*type->u.kindirect->slot != NULL) + return debug_get_real_type (handle, *type->u.kindirect->slot, &rl); + return type; + case DEBUG_KIND_NAMED: + case DEBUG_KIND_TAGGED: + return debug_get_real_type (handle, type->u.knamed->type, &rl); + } + /*NOTREACHED*/ +} + +/* Get the kind of a type. */ + +enum debug_type_kind +debug_get_type_kind (void *handle, debug_type type) +{ + if (type == NULL) + return DEBUG_KIND_ILLEGAL; + type = debug_get_real_type (handle, type, NULL); + if (type == NULL) + return DEBUG_KIND_ILLEGAL; + return type->kind; +} + +/* Get the name of a type. */ + +const char * +debug_get_type_name (void *handle, debug_type type) +{ + if (type->kind == DEBUG_KIND_INDIRECT) + { + if (*type->u.kindirect->slot != NULL) + return debug_get_type_name (handle, *type->u.kindirect->slot); + return type->u.kindirect->tag; + } + if (type->kind == DEBUG_KIND_NAMED + || type->kind == DEBUG_KIND_TAGGED) + return type->u.knamed->name->name; + return NULL; +} + +/* Get the size of a type. */ + +bfd_vma +debug_get_type_size (void *handle, debug_type type) +{ + if (type == NULL) + return 0; + + /* We don't call debug_get_real_type, because somebody might have + called debug_record_type_size on a named or indirect type. */ + + if (type->size != 0) + return type->size; + + switch (type->kind) + { + default: + return 0; + case DEBUG_KIND_INDIRECT: + if (*type->u.kindirect->slot != NULL) + return debug_get_type_size (handle, *type->u.kindirect->slot); + return 0; + case DEBUG_KIND_NAMED: + case DEBUG_KIND_TAGGED: + return debug_get_type_size (handle, type->u.knamed->type); + } + /*NOTREACHED*/ +} + +/* Get the return type of a function or method type. */ + +debug_type +debug_get_return_type (void *handle, debug_type type) +{ + if (type == NULL) + return DEBUG_TYPE_NULL; + + type = debug_get_real_type (handle, type, NULL); + if (type == NULL) + return DEBUG_TYPE_NULL; + + switch (type->kind) + { + default: + return DEBUG_TYPE_NULL; + case DEBUG_KIND_FUNCTION: + return type->u.kfunction->return_type; + case DEBUG_KIND_METHOD: + return type->u.kmethod->return_type; + } + /*NOTREACHED*/ +} + +/* Get the parameter types of a function or method type (except that + we don't currently store the parameter types of a function). */ + +const debug_type * +debug_get_parameter_types (void *handle, debug_type type, + bfd_boolean *pvarargs) +{ + if (type == NULL) + return NULL; + + type = debug_get_real_type (handle, type, NULL); + if (type == NULL) + return NULL; + + switch (type->kind) + { + default: + return NULL; + case DEBUG_KIND_FUNCTION: + *pvarargs = type->u.kfunction->varargs; + return type->u.kfunction->arg_types; + case DEBUG_KIND_METHOD: + *pvarargs = type->u.kmethod->varargs; + return type->u.kmethod->arg_types; + } + /*NOTREACHED*/ +} + +/* Get the target type of a type. */ + +debug_type +debug_get_target_type (void *handle, debug_type type) +{ + if (type == NULL) + return NULL; + + type = debug_get_real_type (handle, type, NULL); + if (type == NULL) + return NULL; + + switch (type->kind) + { + default: + return NULL; + case DEBUG_KIND_POINTER: + return type->u.kpointer; + case DEBUG_KIND_REFERENCE: + return type->u.kreference; + case DEBUG_KIND_CONST: + return type->u.kconst; + case DEBUG_KIND_VOLATILE: + return type->u.kvolatile; + } + /*NOTREACHED*/ +} + +/* Get the NULL terminated array of fields for a struct, union, or + class. */ + +const debug_field * +debug_get_fields (void *handle, debug_type type) +{ + if (type == NULL) + return NULL; + + type = debug_get_real_type (handle, type, NULL); + if (type == NULL) + return NULL; + + switch (type->kind) + { + default: + return NULL; + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_UNION: + case DEBUG_KIND_CLASS: + case DEBUG_KIND_UNION_CLASS: + return type->u.kclass->fields; + } + /*NOTREACHED*/ +} + +/* Get the type of a field. */ + +debug_type +debug_get_field_type (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL) + return NULL; + return field->type; +} + +/* Get the name of a field. */ + +const char * +debug_get_field_name (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL) + return NULL; + return field->name; +} + +/* Get the bit position of a field. */ + +bfd_vma +debug_get_field_bitpos (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL || field->static_member) + return (bfd_vma) -1; + return field->u.f.bitpos; +} + +/* Get the bit size of a field. */ + +bfd_vma +debug_get_field_bitsize (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL || field->static_member) + return (bfd_vma) -1; + return field->u.f.bitsize; +} + +/* Get the visibility of a field. */ + +enum debug_visibility +debug_get_field_visibility (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL) + return DEBUG_VISIBILITY_IGNORE; + return field->visibility; +} + +/* Get the physical name of a field. */ + +const char * +debug_get_field_physname (void *handle ATTRIBUTE_UNUSED, debug_field field) +{ + if (field == NULL || ! field->static_member) + return NULL; + return field->u.s.physname; +} + +/* Write out the debugging information. This is given a handle to + debugging information, and a set of function pointers to call. */ + +bfd_boolean +debug_write (void *handle, const struct debug_write_fns *fns, void *fhandle) +{ + struct debug_handle *info = (struct debug_handle *) handle; + struct debug_unit *u; + + /* We use a mark to tell whether we have already written out a + particular name. We use an integer, so that we don't have to + clear the mark fields if we happen to write out the same + information more than once. */ + ++info->mark; + + /* The base_id field holds an ID value which will never be used, so + that we can tell whether we have assigned an ID during this call + to debug_write. */ + info->base_id = info->class_id; + + /* We keep a linked list of classes for which was have assigned ID's + during this call to debug_write. */ + info->id_list = NULL; + + for (u = info->units; u != NULL; u = u->next) + { + struct debug_file *f; + bfd_boolean first_file; + + info->current_write_lineno = u->linenos; + info->current_write_lineno_index = 0; + + if (! (*fns->start_compilation_unit) (fhandle, u->files->filename)) + return FALSE; + + first_file = TRUE; + for (f = u->files; f != NULL; f = f->next) + { + struct debug_name *n; + + if (first_file) + first_file = FALSE; + else if (! (*fns->start_source) (fhandle, f->filename)) + return FALSE; + + if (f->globals != NULL) + for (n = f->globals->list; n != NULL; n = n->next) + if (! debug_write_name (info, fns, fhandle, n)) + return FALSE; + } + + /* Output any line number information which hasn't already been + handled. */ + if (! debug_write_linenos (info, fns, fhandle, (bfd_vma) -1)) + return FALSE; + } + + return TRUE; +} + +/* Write out an element in a namespace. */ + +static bfd_boolean +debug_write_name (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + struct debug_name *n) +{ + switch (n->kind) + { + case DEBUG_OBJECT_TYPE: + if (! debug_write_type (info, fns, fhandle, n->u.type, n) + || ! (*fns->typdef) (fhandle, n->name)) + return FALSE; + return TRUE; + case DEBUG_OBJECT_TAG: + if (! debug_write_type (info, fns, fhandle, n->u.tag, n)) + return FALSE; + return (*fns->tag) (fhandle, n->name); + case DEBUG_OBJECT_VARIABLE: + if (! debug_write_type (info, fns, fhandle, n->u.variable->type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->variable) (fhandle, n->name, n->u.variable->kind, + n->u.variable->val); + case DEBUG_OBJECT_FUNCTION: + return debug_write_function (info, fns, fhandle, n->name, + n->linkage, n->u.function); + case DEBUG_OBJECT_INT_CONSTANT: + return (*fns->int_constant) (fhandle, n->name, n->u.int_constant); + case DEBUG_OBJECT_FLOAT_CONSTANT: + return (*fns->float_constant) (fhandle, n->name, n->u.float_constant); + case DEBUG_OBJECT_TYPED_CONSTANT: + if (! debug_write_type (info, fns, fhandle, n->u.typed_constant->type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->typed_constant) (fhandle, n->name, + n->u.typed_constant->val); + default: + abort (); + return FALSE; + } + /*NOTREACHED*/ +} + +/* Write out a type. If the type is DEBUG_KIND_NAMED or + DEBUG_KIND_TAGGED, then the name argument is the name for which we + are about to call typedef or tag. If the type is anything else, + then the name argument is a tag from a DEBUG_KIND_TAGGED type which + points to this one. */ + +static bfd_boolean +debug_write_type (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + struct debug_type_s *type, struct debug_name *name) +{ + unsigned int i; + int is; + const char *tag = NULL; + + /* If we have a name for this type, just output it. We only output + typedef names after they have been defined. We output type tags + whenever we are not actually defining them. */ + if ((type->kind == DEBUG_KIND_NAMED + || type->kind == DEBUG_KIND_TAGGED) + && (type->u.knamed->name->mark == info->mark + || (type->kind == DEBUG_KIND_TAGGED + && type->u.knamed->name != name))) + { + if (type->kind == DEBUG_KIND_NAMED) + return (*fns->typedef_type) (fhandle, type->u.knamed->name->name); + else + { + struct debug_type_s *real; + unsigned int id; + + real = debug_get_real_type ((void *) info, type, NULL); + if (real == NULL) + return (*fns->empty_type) (fhandle); + id = 0; + if ((real->kind == DEBUG_KIND_STRUCT + || real->kind == DEBUG_KIND_UNION + || real->kind == DEBUG_KIND_CLASS + || real->kind == DEBUG_KIND_UNION_CLASS) + && real->u.kclass != NULL) + { + if (real->u.kclass->id <= info->base_id) + { + if (! debug_set_class_id (info, + type->u.knamed->name->name, + real)) + return FALSE; + } + id = real->u.kclass->id; + } + + return (*fns->tag_type) (fhandle, type->u.knamed->name->name, id, + real->kind); + } + } + + /* Mark the name after we have already looked for a known name, so + that we don't just define a type in terms of itself. We need to + mark the name here so that a struct containing a pointer to + itself will work. */ + if (name != NULL) + name->mark = info->mark; + + if (name != NULL + && type->kind != DEBUG_KIND_NAMED + && type->kind != DEBUG_KIND_TAGGED) + { + assert (name->kind == DEBUG_OBJECT_TAG); + tag = name->name; + } + + switch (type->kind) + { + case DEBUG_KIND_ILLEGAL: + debug_error (_("debug_write_type: illegal type encountered")); + return FALSE; + case DEBUG_KIND_INDIRECT: + if (*type->u.kindirect->slot == DEBUG_TYPE_NULL) + return (*fns->empty_type) (fhandle); + return debug_write_type (info, fns, fhandle, *type->u.kindirect->slot, + name); + case DEBUG_KIND_VOID: + return (*fns->void_type) (fhandle); + case DEBUG_KIND_INT: + return (*fns->int_type) (fhandle, type->size, type->u.kint); + case DEBUG_KIND_FLOAT: + return (*fns->float_type) (fhandle, type->size); + case DEBUG_KIND_COMPLEX: + return (*fns->complex_type) (fhandle, type->size); + case DEBUG_KIND_BOOL: + return (*fns->bool_type) (fhandle, type->size); + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_UNION: + if (type->u.kclass != NULL) + { + if (type->u.kclass->id <= info->base_id) + { + if (! debug_set_class_id (info, tag, type)) + return FALSE; + } + + if (info->mark == type->u.kclass->mark) + { + /* We are currently outputting this struct, or we have + already output it. I don't know if this can happen, + but it can happen for a class. */ + assert (type->u.kclass->id > info->base_id); + return (*fns->tag_type) (fhandle, tag, type->u.kclass->id, + type->kind); + } + type->u.kclass->mark = info->mark; + } + + if (! (*fns->start_struct_type) (fhandle, tag, + (type->u.kclass != NULL + ? type->u.kclass->id + : 0), + type->kind == DEBUG_KIND_STRUCT, + type->size)) + return FALSE; + if (type->u.kclass != NULL + && type->u.kclass->fields != NULL) + { + for (i = 0; type->u.kclass->fields[i] != NULL; i++) + { + struct debug_field_s *f; + + f = type->u.kclass->fields[i]; + if (! debug_write_type (info, fns, fhandle, f->type, + (struct debug_name *) NULL) + || ! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos, + f->u.f.bitsize, f->visibility)) + return FALSE; + } + } + return (*fns->end_struct_type) (fhandle); + case DEBUG_KIND_CLASS: + case DEBUG_KIND_UNION_CLASS: + return debug_write_class_type (info, fns, fhandle, type, tag); + case DEBUG_KIND_ENUM: + if (type->u.kenum == NULL) + return (*fns->enum_type) (fhandle, tag, (const char **) NULL, + (bfd_signed_vma *) NULL); + return (*fns->enum_type) (fhandle, tag, type->u.kenum->names, + type->u.kenum->values); + case DEBUG_KIND_POINTER: + if (! debug_write_type (info, fns, fhandle, type->u.kpointer, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->pointer_type) (fhandle); + case DEBUG_KIND_FUNCTION: + if (! debug_write_type (info, fns, fhandle, + type->u.kfunction->return_type, + (struct debug_name *) NULL)) + return FALSE; + if (type->u.kfunction->arg_types == NULL) + is = -1; + else + { + for (is = 0; type->u.kfunction->arg_types[is] != NULL; is++) + if (! debug_write_type (info, fns, fhandle, + type->u.kfunction->arg_types[is], + (struct debug_name *) NULL)) + return FALSE; + } + return (*fns->function_type) (fhandle, is, + type->u.kfunction->varargs); + case DEBUG_KIND_REFERENCE: + if (! debug_write_type (info, fns, fhandle, type->u.kreference, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->reference_type) (fhandle); + case DEBUG_KIND_RANGE: + if (! debug_write_type (info, fns, fhandle, type->u.krange->type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->range_type) (fhandle, type->u.krange->lower, + type->u.krange->upper); + case DEBUG_KIND_ARRAY: + if (! debug_write_type (info, fns, fhandle, type->u.karray->element_type, + (struct debug_name *) NULL) + || ! debug_write_type (info, fns, fhandle, + type->u.karray->range_type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->array_type) (fhandle, type->u.karray->lower, + type->u.karray->upper, + type->u.karray->stringp); + case DEBUG_KIND_SET: + if (! debug_write_type (info, fns, fhandle, type->u.kset->type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->set_type) (fhandle, type->u.kset->bitstringp); + case DEBUG_KIND_OFFSET: + if (! debug_write_type (info, fns, fhandle, type->u.koffset->base_type, + (struct debug_name *) NULL) + || ! debug_write_type (info, fns, fhandle, + type->u.koffset->target_type, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->offset_type) (fhandle); + case DEBUG_KIND_METHOD: + if (! debug_write_type (info, fns, fhandle, + type->u.kmethod->return_type, + (struct debug_name *) NULL)) + return FALSE; + if (type->u.kmethod->arg_types == NULL) + is = -1; + else + { + for (is = 0; type->u.kmethod->arg_types[is] != NULL; is++) + if (! debug_write_type (info, fns, fhandle, + type->u.kmethod->arg_types[is], + (struct debug_name *) NULL)) + return FALSE; + } + if (type->u.kmethod->domain_type != NULL) + { + if (! debug_write_type (info, fns, fhandle, + type->u.kmethod->domain_type, + (struct debug_name *) NULL)) + return FALSE; + } + return (*fns->method_type) (fhandle, + type->u.kmethod->domain_type != NULL, + is, + type->u.kmethod->varargs); + case DEBUG_KIND_CONST: + if (! debug_write_type (info, fns, fhandle, type->u.kconst, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->const_type) (fhandle); + case DEBUG_KIND_VOLATILE: + if (! debug_write_type (info, fns, fhandle, type->u.kvolatile, + (struct debug_name *) NULL)) + return FALSE; + return (*fns->volatile_type) (fhandle); + case DEBUG_KIND_NAMED: + return debug_write_type (info, fns, fhandle, type->u.knamed->type, + (struct debug_name *) NULL); + case DEBUG_KIND_TAGGED: + return debug_write_type (info, fns, fhandle, type->u.knamed->type, + type->u.knamed->name); + default: + abort (); + return FALSE; + } +} + +/* Write out a class type. */ + +static bfd_boolean +debug_write_class_type (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + struct debug_type_s *type, const char *tag) +{ + unsigned int i; + unsigned int id; + struct debug_type_s *vptrbase; + + if (type->u.kclass == NULL) + { + id = 0; + vptrbase = NULL; + } + else + { + if (type->u.kclass->id <= info->base_id) + { + if (! debug_set_class_id (info, tag, type)) + return FALSE; + } + + if (info->mark == type->u.kclass->mark) + { + /* We are currently outputting this class, or we have + already output it. This can happen when there are + methods for an anonymous class. */ + assert (type->u.kclass->id > info->base_id); + return (*fns->tag_type) (fhandle, tag, type->u.kclass->id, + type->kind); + } + type->u.kclass->mark = info->mark; + id = type->u.kclass->id; + + vptrbase = type->u.kclass->vptrbase; + if (vptrbase != NULL && vptrbase != type) + { + if (! debug_write_type (info, fns, fhandle, vptrbase, + (struct debug_name *) NULL)) + return FALSE; + } + } + + if (! (*fns->start_class_type) (fhandle, tag, id, + type->kind == DEBUG_KIND_CLASS, + type->size, + vptrbase != NULL, + vptrbase == type)) + return FALSE; + + if (type->u.kclass != NULL) + { + if (type->u.kclass->fields != NULL) + { + for (i = 0; type->u.kclass->fields[i] != NULL; i++) + { + struct debug_field_s *f; + + f = type->u.kclass->fields[i]; + if (! debug_write_type (info, fns, fhandle, f->type, + (struct debug_name *) NULL)) + return FALSE; + if (f->static_member) + { + if (! (*fns->class_static_member) (fhandle, f->name, + f->u.s.physname, + f->visibility)) + return FALSE; + } + else + { + if (! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos, + f->u.f.bitsize, f->visibility)) + return FALSE; + } + } + } + + if (type->u.kclass->baseclasses != NULL) + { + for (i = 0; type->u.kclass->baseclasses[i] != NULL; i++) + { + struct debug_baseclass_s *b; + + b = type->u.kclass->baseclasses[i]; + if (! debug_write_type (info, fns, fhandle, b->type, + (struct debug_name *) NULL)) + return FALSE; + if (! (*fns->class_baseclass) (fhandle, b->bitpos, b->is_virtual, + b->visibility)) + return FALSE; + } + } + + if (type->u.kclass->methods != NULL) + { + for (i = 0; type->u.kclass->methods[i] != NULL; i++) + { + struct debug_method_s *m; + unsigned int j; + + m = type->u.kclass->methods[i]; + if (! (*fns->class_start_method) (fhandle, m->name)) + return FALSE; + for (j = 0; m->variants[j] != NULL; j++) + { + struct debug_method_variant_s *v; + + v = m->variants[j]; + if (v->context != NULL) + { + if (! debug_write_type (info, fns, fhandle, v->context, + (struct debug_name *) NULL)) + return FALSE; + } + if (! debug_write_type (info, fns, fhandle, v->type, + (struct debug_name *) NULL)) + return FALSE; + if (v->voffset != VOFFSET_STATIC_METHOD) + { + if (! (*fns->class_method_variant) (fhandle, v->physname, + v->visibility, + v->constp, + v->volatilep, + v->voffset, + v->context != NULL)) + return FALSE; + } + else + { + if (! (*fns->class_static_method_variant) (fhandle, + v->physname, + v->visibility, + v->constp, + v->volatilep)) + return FALSE; + } + } + if (! (*fns->class_end_method) (fhandle)) + return FALSE; + } + } + } + + return (*fns->end_class_type) (fhandle); +} + +/* Write out information for a function. */ + +static bfd_boolean +debug_write_function (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + const char *name, enum debug_object_linkage linkage, + struct debug_function *function) +{ + struct debug_parameter *p; + struct debug_block *b; + + if (! debug_write_linenos (info, fns, fhandle, function->blocks->start)) + return FALSE; + + if (! debug_write_type (info, fns, fhandle, function->return_type, + (struct debug_name *) NULL)) + return FALSE; + + if (! (*fns->start_function) (fhandle, name, + linkage == DEBUG_LINKAGE_GLOBAL)) + return FALSE; + + for (p = function->parameters; p != NULL; p = p->next) + { + if (! debug_write_type (info, fns, fhandle, p->type, + (struct debug_name *) NULL) + || ! (*fns->function_parameter) (fhandle, p->name, p->kind, p->val)) + return FALSE; + } + + for (b = function->blocks; b != NULL; b = b->next) + { + if (! debug_write_block (info, fns, fhandle, b)) + return FALSE; + } + + return (*fns->end_function) (fhandle); +} + +/* Write out information for a block. */ + +static bfd_boolean +debug_write_block (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + struct debug_block *block) +{ + struct debug_name *n; + struct debug_block *b; + + if (! debug_write_linenos (info, fns, fhandle, block->start)) + return FALSE; + + /* I can't see any point to writing out a block with no local + variables, so we don't bother, except for the top level block. */ + if (block->locals != NULL || block->parent == NULL) + { + if (! (*fns->start_block) (fhandle, block->start)) + return FALSE; + } + + if (block->locals != NULL) + { + for (n = block->locals->list; n != NULL; n = n->next) + { + if (! debug_write_name (info, fns, fhandle, n)) + return FALSE; + } + } + + for (b = block->children; b != NULL; b = b->next) + { + if (! debug_write_block (info, fns, fhandle, b)) + return FALSE; + } + + if (! debug_write_linenos (info, fns, fhandle, block->end)) + return FALSE; + + if (block->locals != NULL || block->parent == NULL) + { + if (! (*fns->end_block) (fhandle, block->end)) + return FALSE; + } + + return TRUE; +} + +/* Write out line number information up to ADDRESS. */ + +static bfd_boolean +debug_write_linenos (struct debug_handle *info, + const struct debug_write_fns *fns, void *fhandle, + bfd_vma address) +{ + while (info->current_write_lineno != NULL) + { + struct debug_lineno *l; + + l = info->current_write_lineno; + + while (info->current_write_lineno_index < DEBUG_LINENO_COUNT) + { + if (l->linenos[info->current_write_lineno_index] + == (unsigned long) -1) + break; + + if (l->addrs[info->current_write_lineno_index] >= address) + return TRUE; + + if (! (*fns->lineno) (fhandle, l->file->filename, + l->linenos[info->current_write_lineno_index], + l->addrs[info->current_write_lineno_index])) + return FALSE; + + ++info->current_write_lineno_index; + } + + info->current_write_lineno = l->next; + info->current_write_lineno_index = 0; + } + + return TRUE; +} + +/* Get the ID number for a class. If during the same call to + debug_write we find a struct with the same definition with the same + name, we use the same ID. This type of things happens because the + same struct will be defined by multiple compilation units. */ + +static bfd_boolean +debug_set_class_id (struct debug_handle *info, const char *tag, + struct debug_type_s *type) +{ + struct debug_class_type *c; + struct debug_class_id *l; + + assert (type->kind == DEBUG_KIND_STRUCT + || type->kind == DEBUG_KIND_UNION + || type->kind == DEBUG_KIND_CLASS + || type->kind == DEBUG_KIND_UNION_CLASS); + + c = type->u.kclass; + + if (c->id > info->base_id) + return TRUE; + + for (l = info->id_list; l != NULL; l = l->next) + { + if (l->type->kind != type->kind) + continue; + + if (tag == NULL) + { + if (l->tag != NULL) + continue; + } + else + { + if (l->tag == NULL + || l->tag[0] != tag[0] + || strcmp (l->tag, tag) != 0) + continue; + } + + if (debug_type_samep (info, l->type, type)) + { + c->id = l->type->u.kclass->id; + return TRUE; + } + } + + /* There are no identical types. Use a new ID, and add it to the + list. */ + ++info->class_id; + c->id = info->class_id; + + l = (struct debug_class_id *) xmalloc (sizeof *l); + memset (l, 0, sizeof *l); + + l->type = type; + l->tag = tag; + + l->next = info->id_list; + info->id_list = l; + + return TRUE; +} + +/* See if two types are the same. At this point, we don't care about + tags and the like. */ + +static bfd_boolean +debug_type_samep (struct debug_handle *info, struct debug_type_s *t1, + struct debug_type_s *t2) +{ + struct debug_type_compare_list *l; + struct debug_type_compare_list top; + bfd_boolean ret; + + if (t1 == NULL) + return t2 == NULL; + if (t2 == NULL) + return FALSE; + + while (t1->kind == DEBUG_KIND_INDIRECT) + { + t1 = *t1->u.kindirect->slot; + if (t1 == NULL) + return FALSE; + } + while (t2->kind == DEBUG_KIND_INDIRECT) + { + t2 = *t2->u.kindirect->slot; + if (t2 == NULL) + return FALSE; + } + + if (t1 == t2) + return TRUE; + + /* As a special case, permit a typedef to match a tag, since C++ + debugging output will sometimes add a typedef where C debugging + output will not. */ + if (t1->kind == DEBUG_KIND_NAMED + && t2->kind == DEBUG_KIND_TAGGED) + return debug_type_samep (info, t1->u.knamed->type, t2); + else if (t1->kind == DEBUG_KIND_TAGGED + && t2->kind == DEBUG_KIND_NAMED) + return debug_type_samep (info, t1, t2->u.knamed->type); + + if (t1->kind != t2->kind + || t1->size != t2->size) + return FALSE; + + /* Get rid of the trivial cases first. */ + switch (t1->kind) + { + default: + break; + case DEBUG_KIND_VOID: + case DEBUG_KIND_FLOAT: + case DEBUG_KIND_COMPLEX: + case DEBUG_KIND_BOOL: + return TRUE; + case DEBUG_KIND_INT: + return t1->u.kint == t2->u.kint; + } + + /* We have to avoid an infinite recursion. We do this by keeping a + list of types which we are comparing. We just keep the list on + the stack. If we encounter a pair of types we are currently + comparing, we just assume that they are equal. */ + for (l = info->compare_list; l != NULL; l = l->next) + { + if (l->t1 == t1 && l->t2 == t2) + return TRUE; + } + + top.t1 = t1; + top.t2 = t2; + top.next = info->compare_list; + info->compare_list = ⊤ + + switch (t1->kind) + { + default: + abort (); + ret = FALSE; + break; + + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_UNION: + case DEBUG_KIND_CLASS: + case DEBUG_KIND_UNION_CLASS: + if (t1->u.kclass == NULL) + ret = t2->u.kclass == NULL; + else if (t2->u.kclass == NULL) + ret = FALSE; + else if (t1->u.kclass->id > info->base_id + && t1->u.kclass->id == t2->u.kclass->id) + ret = TRUE; + else + ret = debug_class_type_samep (info, t1, t2); + break; + + case DEBUG_KIND_ENUM: + if (t1->u.kenum == NULL) + ret = t2->u.kenum == NULL; + else if (t2->u.kenum == NULL) + ret = FALSE; + else + { + const char **pn1, **pn2; + bfd_signed_vma *pv1, *pv2; + + pn1 = t1->u.kenum->names; + pn2 = t2->u.kenum->names; + pv1 = t1->u.kenum->values; + pv2 = t2->u.kenum->values; + while (*pn1 != NULL && *pn2 != NULL) + { + if (**pn1 != **pn2 + || *pv1 != *pv2 + || strcmp (*pn1, *pn2) != 0) + break; + ++pn1; + ++pn2; + ++pv1; + ++pv2; + } + ret = *pn1 == NULL && *pn2 == NULL; + } + break; + + case DEBUG_KIND_POINTER: + ret = debug_type_samep (info, t1->u.kpointer, t2->u.kpointer); + break; + + case DEBUG_KIND_FUNCTION: + if (t1->u.kfunction->varargs != t2->u.kfunction->varargs + || ! debug_type_samep (info, t1->u.kfunction->return_type, + t2->u.kfunction->return_type) + || ((t1->u.kfunction->arg_types == NULL) + != (t2->u.kfunction->arg_types == NULL))) + ret = FALSE; + else if (t1->u.kfunction->arg_types == NULL) + ret = TRUE; + else + { + struct debug_type_s **a1, **a2; + + a1 = t1->u.kfunction->arg_types; + a2 = t2->u.kfunction->arg_types; + while (*a1 != NULL && *a2 != NULL) + { + if (! debug_type_samep (info, *a1, *a2)) + break; + ++a1; + ++a2; + } + ret = *a1 == NULL && *a2 == NULL; + } + break; + + case DEBUG_KIND_REFERENCE: + ret = debug_type_samep (info, t1->u.kreference, t2->u.kreference); + break; + + case DEBUG_KIND_RANGE: + ret = (t1->u.krange->lower == t2->u.krange->lower + && t1->u.krange->upper == t2->u.krange->upper + && debug_type_samep (info, t1->u.krange->type, + t2->u.krange->type)); + + case DEBUG_KIND_ARRAY: + ret = (t1->u.karray->lower == t2->u.karray->lower + && t1->u.karray->upper == t2->u.karray->upper + && t1->u.karray->stringp == t2->u.karray->stringp + && debug_type_samep (info, t1->u.karray->element_type, + t2->u.karray->element_type)); + break; + + case DEBUG_KIND_SET: + ret = (t1->u.kset->bitstringp == t2->u.kset->bitstringp + && debug_type_samep (info, t1->u.kset->type, t2->u.kset->type)); + break; + + case DEBUG_KIND_OFFSET: + ret = (debug_type_samep (info, t1->u.koffset->base_type, + t2->u.koffset->base_type) + && debug_type_samep (info, t1->u.koffset->target_type, + t2->u.koffset->target_type)); + break; + + case DEBUG_KIND_METHOD: + if (t1->u.kmethod->varargs != t2->u.kmethod->varargs + || ! debug_type_samep (info, t1->u.kmethod->return_type, + t2->u.kmethod->return_type) + || ! debug_type_samep (info, t1->u.kmethod->domain_type, + t2->u.kmethod->domain_type) + || ((t1->u.kmethod->arg_types == NULL) + != (t2->u.kmethod->arg_types == NULL))) + ret = FALSE; + else if (t1->u.kmethod->arg_types == NULL) + ret = TRUE; + else + { + struct debug_type_s **a1, **a2; + + a1 = t1->u.kmethod->arg_types; + a2 = t2->u.kmethod->arg_types; + while (*a1 != NULL && *a2 != NULL) + { + if (! debug_type_samep (info, *a1, *a2)) + break; + ++a1; + ++a2; + } + ret = *a1 == NULL && *a2 == NULL; + } + break; + + case DEBUG_KIND_CONST: + ret = debug_type_samep (info, t1->u.kconst, t2->u.kconst); + break; + + case DEBUG_KIND_VOLATILE: + ret = debug_type_samep (info, t1->u.kvolatile, t2->u.kvolatile); + break; + + case DEBUG_KIND_NAMED: + case DEBUG_KIND_TAGGED: + ret = (strcmp (t1->u.knamed->name->name, t2->u.knamed->name->name) == 0 + && debug_type_samep (info, t1->u.knamed->type, + t2->u.knamed->type)); + break; + } + + info->compare_list = top.next; + + return ret; +} + +/* See if two classes are the same. This is a subroutine of + debug_type_samep. */ + +static bfd_boolean +debug_class_type_samep (struct debug_handle *info, struct debug_type_s *t1, + struct debug_type_s *t2) +{ + struct debug_class_type *c1, *c2; + + c1 = t1->u.kclass; + c2 = t2->u.kclass; + + if ((c1->fields == NULL) != (c2->fields == NULL) + || (c1->baseclasses == NULL) != (c2->baseclasses == NULL) + || (c1->methods == NULL) != (c2->methods == NULL) + || (c1->vptrbase == NULL) != (c2->vptrbase == NULL)) + return FALSE; + + if (c1->fields != NULL) + { + struct debug_field_s **pf1, **pf2; + + for (pf1 = c1->fields, pf2 = c2->fields; + *pf1 != NULL && *pf2 != NULL; + pf1++, pf2++) + { + struct debug_field_s *f1, *f2; + + f1 = *pf1; + f2 = *pf2; + if (f1->name[0] != f2->name[0] + || f1->visibility != f2->visibility + || f1->static_member != f2->static_member) + return FALSE; + if (f1->static_member) + { + if (strcmp (f1->u.s.physname, f2->u.s.physname) != 0) + return FALSE; + } + else + { + if (f1->u.f.bitpos != f2->u.f.bitpos + || f1->u.f.bitsize != f2->u.f.bitsize) + return FALSE; + } + /* We do the checks which require function calls last. We + don't require that the types of fields have the same + names, since that sometimes fails in the presence of + typedefs and we really don't care. */ + if (strcmp (f1->name, f2->name) != 0 + || ! debug_type_samep (info, + debug_get_real_type ((void *) info, + f1->type, NULL), + debug_get_real_type ((void *) info, + f2->type, NULL))) + return FALSE; + } + if (*pf1 != NULL || *pf2 != NULL) + return FALSE; + } + + if (c1->vptrbase != NULL) + { + if (! debug_type_samep (info, c1->vptrbase, c2->vptrbase)) + return FALSE; + } + + if (c1->baseclasses != NULL) + { + struct debug_baseclass_s **pb1, **pb2; + + for (pb1 = c1->baseclasses, pb2 = c2->baseclasses; + *pb1 != NULL && *pb2 != NULL; + ++pb1, ++pb2) + { + struct debug_baseclass_s *b1, *b2; + + b1 = *pb1; + b2 = *pb2; + if (b1->bitpos != b2->bitpos + || b1->is_virtual != b2->is_virtual + || b1->visibility != b2->visibility + || ! debug_type_samep (info, b1->type, b2->type)) + return FALSE; + } + if (*pb1 != NULL || *pb2 != NULL) + return FALSE; + } + + if (c1->methods != NULL) + { + struct debug_method_s **pm1, **pm2; + + for (pm1 = c1->methods, pm2 = c2->methods; + *pm1 != NULL && *pm2 != NULL; + ++pm1, ++pm2) + { + struct debug_method_s *m1, *m2; + + m1 = *pm1; + m2 = *pm2; + if (m1->name[0] != m2->name[0] + || strcmp (m1->name, m2->name) != 0 + || (m1->variants == NULL) != (m2->variants == NULL)) + return FALSE; + if (m1->variants == NULL) + { + struct debug_method_variant_s **pv1, **pv2; + + for (pv1 = m1->variants, pv2 = m2->variants; + *pv1 != NULL && *pv2 != NULL; + ++pv1, ++pv2) + { + struct debug_method_variant_s *v1, *v2; + + v1 = *pv1; + v2 = *pv2; + if (v1->physname[0] != v2->physname[0] + || v1->visibility != v2->visibility + || v1->constp != v2->constp + || v1->volatilep != v2->volatilep + || v1->voffset != v2->voffset + || (v1->context == NULL) != (v2->context == NULL) + || strcmp (v1->physname, v2->physname) != 0 + || ! debug_type_samep (info, v1->type, v2->type)) + return FALSE; + if (v1->context != NULL) + { + if (! debug_type_samep (info, v1->context, + v2->context)) + return FALSE; + } + } + if (*pv1 != NULL || *pv2 != NULL) + return FALSE; + } + } + if (*pm1 != NULL || *pm2 != NULL) + return FALSE; + } + + return TRUE; +} diff --git a/contrib/toolchain/binutils/binutils/debug.h b/contrib/toolchain/binutils/binutils/debug.h new file mode 100644 index 0000000000..1846119b78 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/debug.h @@ -0,0 +1,793 @@ +/* debug.h -- Describe generic debugging information. + Copyright 1995, 1996, 2002, 2003, 2005, 2007, 2009 + Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef DEBUG_H +#define DEBUG_H + +/* This header file describes a generic debugging information format. + We may eventually have readers which convert different formats into + this generic format, and writers which write it out. The initial + impetus for this was writing a converter from stabs to HP IEEE-695 + debugging format. */ + +/* Different kinds of types. */ + +enum debug_type_kind +{ + /* Not used. */ + DEBUG_KIND_ILLEGAL, + /* Indirect via a pointer. */ + DEBUG_KIND_INDIRECT, + /* Void. */ + DEBUG_KIND_VOID, + /* Integer. */ + DEBUG_KIND_INT, + /* Floating point. */ + DEBUG_KIND_FLOAT, + /* Complex. */ + DEBUG_KIND_COMPLEX, + /* Boolean. */ + DEBUG_KIND_BOOL, + /* Struct. */ + DEBUG_KIND_STRUCT, + /* Union. */ + DEBUG_KIND_UNION, + /* Class. */ + DEBUG_KIND_CLASS, + /* Union class (can this really happen?). */ + DEBUG_KIND_UNION_CLASS, + /* Enumeration type. */ + DEBUG_KIND_ENUM, + /* Pointer. */ + DEBUG_KIND_POINTER, + /* Function. */ + DEBUG_KIND_FUNCTION, + /* Reference. */ + DEBUG_KIND_REFERENCE, + /* Range. */ + DEBUG_KIND_RANGE, + /* Array. */ + DEBUG_KIND_ARRAY, + /* Set. */ + DEBUG_KIND_SET, + /* Based pointer. */ + DEBUG_KIND_OFFSET, + /* Method. */ + DEBUG_KIND_METHOD, + /* Const qualified type. */ + DEBUG_KIND_CONST, + /* Volatile qualified type. */ + DEBUG_KIND_VOLATILE, + /* Named type. */ + DEBUG_KIND_NAMED, + /* Tagged type. */ + DEBUG_KIND_TAGGED +}; + +/* Different kinds of variables. */ + +enum debug_var_kind +{ + /* Not used. */ + DEBUG_VAR_ILLEGAL, + /* A global variable. */ + DEBUG_GLOBAL, + /* A static variable. */ + DEBUG_STATIC, + /* A local static variable. */ + DEBUG_LOCAL_STATIC, + /* A local variable. */ + DEBUG_LOCAL, + /* A register variable. */ + DEBUG_REGISTER +}; + +/* Different kinds of function parameters. */ + +enum debug_parm_kind +{ + /* Not used. */ + DEBUG_PARM_ILLEGAL, + /* A stack based parameter. */ + DEBUG_PARM_STACK, + /* A register parameter. */ + DEBUG_PARM_REG, + /* A stack based reference parameter. */ + DEBUG_PARM_REFERENCE, + /* A register reference parameter. */ + DEBUG_PARM_REF_REG +}; + +/* Different kinds of visibility. */ + +enum debug_visibility +{ + /* A public field (e.g., a field in a C struct). */ + DEBUG_VISIBILITY_PUBLIC, + /* A protected field. */ + DEBUG_VISIBILITY_PROTECTED, + /* A private field. */ + DEBUG_VISIBILITY_PRIVATE, + /* A field which should be ignored. */ + DEBUG_VISIBILITY_IGNORE +}; + +/* A type. */ + +typedef struct debug_type_s *debug_type; + +#define DEBUG_TYPE_NULL ((debug_type) NULL) + +/* A field in a struct or union. */ + +typedef struct debug_field_s *debug_field; + +#define DEBUG_FIELD_NULL ((debug_field) NULL) + +/* A base class for an object. */ + +typedef struct debug_baseclass_s *debug_baseclass; + +#define DEBUG_BASECLASS_NULL ((debug_baseclass) NULL) + +/* A method of an object. */ + +typedef struct debug_method_s *debug_method; + +#define DEBUG_METHOD_NULL ((debug_method) NULL) + +/* The arguments to a method function of an object. These indicate + which method to run. */ + +typedef struct debug_method_variant_s *debug_method_variant; + +#define DEBUG_METHOD_VARIANT_NULL ((debug_method_variant) NULL) + +/* This structure is passed to debug_write. It holds function + pointers that debug_write will call based on the accumulated + debugging information. */ + +struct debug_write_fns +{ + /* This is called at the start of each new compilation unit with the + name of the main file in the new unit. */ + bfd_boolean (*start_compilation_unit) (void *, const char *); + + /* This is called at the start of each source file within a + compilation unit, before outputting any global information for + that file. The argument is the name of the file. */ + bfd_boolean (*start_source) (void *, const char *); + + /* Each writer must keep a stack of types. */ + + /* Push an empty type onto the type stack. This type can appear if + there is a reference to a type which is never defined. */ + bfd_boolean (*empty_type) (void *); + + /* Push a void type onto the type stack. */ + bfd_boolean (*void_type) (void *); + + /* Push an integer type onto the type stack, given the size and + whether it is unsigned. */ + bfd_boolean (*int_type) (void *, unsigned int, bfd_boolean); + + /* Push a floating type onto the type stack, given the size. */ + bfd_boolean (*float_type) (void *, unsigned int); + + /* Push a complex type onto the type stack, given the size. */ + bfd_boolean (*complex_type) (void *, unsigned int); + + /* Push a bfd_boolean type onto the type stack, given the size. */ + bfd_boolean (*bool_type) (void *, unsigned int); + + /* Push an enum type onto the type stack, given the tag, a NULL + terminated array of names and the associated values. If there is + no tag, the tag argument will be NULL. If this is an undefined + enum, the names and values arguments will be NULL. */ + bfd_boolean (*enum_type) + (void *, const char *, const char **, bfd_signed_vma *); + + /* Pop the top type on the type stack, and push a pointer to that + type onto the type stack. */ + bfd_boolean (*pointer_type) (void *); + + /* Push a function type onto the type stack. The second argument + indicates the number of argument types that have been pushed onto + the stack. If the number of argument types is passed as -1, then + the argument types of the function are unknown, and no types have + been pushed onto the stack. The third argument is TRUE if the + function takes a variable number of arguments. The return type + of the function is pushed onto the type stack below the argument + types, if any. */ + bfd_boolean (*function_type) (void *, int, bfd_boolean); + + /* Pop the top type on the type stack, and push a reference to that + type onto the type stack. */ + bfd_boolean (*reference_type) (void *); + + /* Pop the top type on the type stack, and push a range of that type + with the given lower and upper bounds onto the type stack. */ + bfd_boolean (*range_type) (void *, bfd_signed_vma, bfd_signed_vma); + + /* Push an array type onto the type stack. The top type on the type + stack is the range, and the next type on the type stack is the + element type. These should be popped before the array type is + pushed. The arguments are the lower bound, the upper bound, and + whether the array is a string. */ + bfd_boolean (*array_type) + (void *, bfd_signed_vma, bfd_signed_vma, bfd_boolean); + + /* Pop the top type on the type stack, and push a set of that type + onto the type stack. The argument indicates whether this set is + a bitstring. */ + bfd_boolean (*set_type) (void *, bfd_boolean); + + /* Push an offset type onto the type stack. The top type on the + type stack is the target type, and the next type on the type + stack is the base type. These should be popped before the offset + type is pushed. */ + bfd_boolean (*offset_type) (void *); + + /* Push a method type onto the type stack. If the second argument + is TRUE, the top type on the stack is the class to which the + method belongs; otherwise, the class must be determined by the + class to which the method is attached. The third argument is the + number of argument types; these are pushed onto the type stack in + reverse order (the first type popped is the last argument to the + method). A value of -1 for the third argument means that no + argument information is available. The fourth argument is TRUE + if the function takes a variable number of arguments. The next + type on the type stack below the domain and the argument types is + the return type of the method. All these types must be popped, + and then the method type must be pushed. */ + bfd_boolean (*method_type) (void *, bfd_boolean, int, bfd_boolean); + + /* Pop the top type off the type stack, and push a const qualified + version of that type onto the type stack. */ + bfd_boolean (*const_type) (void *); + + /* Pop the top type off the type stack, and push a volatile + qualified version of that type onto the type stack. */ + bfd_boolean (*volatile_type) (void *); + + /* Start building a struct. This is followed by calls to the + struct_field function, and finished by a call to the + end_struct_type function. The second argument is the tag; this + will be NULL if there isn't one. If the second argument is NULL, + the third argument is a constant identifying this struct for use + with tag_type. The fourth argument is TRUE for a struct, FALSE + for a union. The fifth argument is the size. If this is an + undefined struct or union, the size will be 0 and struct_field + will not be called before end_struct_type is called. */ + bfd_boolean (*start_struct_type) + (void *, const char *, unsigned int, bfd_boolean, unsigned int); + + /* Add a field to the struct type currently being built. The type + of the field should be popped off the type stack. The arguments + are the name, the bit position, the bit size (may be zero if the + field is not packed), and the visibility. */ + bfd_boolean (*struct_field) + (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility); + + /* Finish building a struct, and push it onto the type stack. */ + bfd_boolean (*end_struct_type) (void *); + + /* Start building a class. This is followed by calls to several + functions: struct_field, class_static_member, class_baseclass, + class_start_method, class_method_variant, + class_static_method_variant, and class_end_method. The class is + finished by a call to end_class_type. The first five arguments + are the same as for start_struct_type. The sixth argument is + TRUE if there is a virtual function table; if there is, the + seventh argument is TRUE if the virtual function table can be + found in the type itself, and is FALSE if the type of the object + holding the virtual function table should be popped from the type + stack. */ + bfd_boolean (*start_class_type) + (void *, const char *, unsigned int, bfd_boolean, unsigned int, + bfd_boolean, bfd_boolean); + + /* Add a static member to the class currently being built. The + arguments are the field name, the physical name, and the + visibility. The type must be popped off the type stack. */ + bfd_boolean (*class_static_member) + (void *, const char *, const char *, enum debug_visibility); + + /* Add a baseclass to the class currently being built. The type of + the baseclass must be popped off the type stack. The arguments + are the bit position, whether the class is virtual, and the + visibility. */ + bfd_boolean (*class_baseclass) + (void *, bfd_vma, bfd_boolean, enum debug_visibility); + + /* Start adding a method to the class currently being built. This + is followed by calls to class_method_variant and + class_static_method_variant to describe different variants of the + method which take different arguments. The method is finished + with a call to class_end_method. The argument is the method + name. */ + bfd_boolean (*class_start_method) (void *, const char *); + + /* Describe a variant to the class method currently being built. + The type of the variant must be popped off the type stack. The + second argument is the physical name of the function. The + following arguments are the visibility, whether the variant is + const, whether the variant is volatile, the offset in the virtual + function table, and whether the context is on the type stack + (below the variant type). */ + bfd_boolean (*class_method_variant) + (void *, const char *, enum debug_visibility, bfd_boolean, + bfd_boolean, bfd_vma, bfd_boolean); + + /* Describe a static variant to the class method currently being + built. The arguments are the same as for class_method_variant, + except that the last two arguments are omitted. The type of the + variant must be popped off the type stack. */ + bfd_boolean (*class_static_method_variant) + (void *, const char *, enum debug_visibility, bfd_boolean, + bfd_boolean); + + /* Finish describing a class method. */ + bfd_boolean (*class_end_method) (void *); + + /* Finish describing a class, and push it onto the type stack. */ + bfd_boolean (*end_class_type) (void *); + + /* Push a type on the stack which was given a name by an earlier + call to typdef. */ + bfd_boolean (*typedef_type) (void *, const char *); + + /* Push a tagged type on the stack which was defined earlier. If + the second argument is not NULL, the type was defined by a call + to tag. If the second argument is NULL, the type was defined by + a call to start_struct_type or start_class_type with a tag of + NULL and the number of the third argument. Either way, the + fourth argument is the tag kind. Note that this may be called + for a struct (class) being defined, in between the call to + start_struct_type (start_class_type) and the call to + end_struct_type (end_class_type). */ + bfd_boolean (*tag_type) + (void *, const char *, unsigned int, enum debug_type_kind); + + /* Pop the type stack, and typedef it to the given name. */ + bfd_boolean (*typdef) (void *, const char *); + + /* Pop the type stack, and declare it as a tagged struct or union or + enum or whatever. The tag passed down here is redundant, since + was also passed when enum_type, start_struct_type, or + start_class_type was called. */ + bfd_boolean (*tag) (void *, const char *); + + /* This is called to record a named integer constant. */ + bfd_boolean (*int_constant) (void *, const char *, bfd_vma); + + /* This is called to record a named floating point constant. */ + bfd_boolean (*float_constant) (void *, const char *, double); + + /* This is called to record a typed integer constant. The type is + popped off the type stack. */ + bfd_boolean (*typed_constant) (void *, const char *, bfd_vma); + + /* This is called to record a variable. The type is popped off the + type stack. */ + bfd_boolean (*variable) + (void *, const char *, enum debug_var_kind, bfd_vma); + + /* Start writing out a function. The return type must be popped off + the stack. The bfd_boolean is TRUE if the function is global. This + is followed by calls to function_parameter, followed by block + information. */ + bfd_boolean (*start_function) (void *, const char *, bfd_boolean); + + /* Record a function parameter for the current function. The type + must be popped off the stack. */ + bfd_boolean (*function_parameter) + (void *, const char *, enum debug_parm_kind, bfd_vma); + + /* Start writing out a block. There is at least one top level block + per function. Blocks may be nested. The argument is the + starting address of the block. */ + bfd_boolean (*start_block) (void *, bfd_vma); + + /* Finish writing out a block. The argument is the ending address + of the block. */ + bfd_boolean (*end_block) (void *, bfd_vma); + + /* Finish writing out a function. */ + bfd_boolean (*end_function) (void *); + + /* Record line number information for the current compilation unit. */ + bfd_boolean (*lineno) (void *, const char *, unsigned long, bfd_vma); +}; + +/* Exported functions. */ + +/* The first argument to most of these functions is a handle. This + handle is returned by the debug_init function. The purpose of the + handle is to permit the debugging routines to not use static + variables, and hence to be reentrant. This would be useful for a + program which wanted to handle two executables simultaneously. */ + +/* Return a debugging handle. */ + +extern void *debug_init (void); + +/* Set the source filename. This implicitly starts a new compilation + unit. */ + +extern bfd_boolean debug_set_filename (void *, const char *); + +/* Change source files to the given file name. This is used for + include files in a single compilation unit. */ + +extern bfd_boolean debug_start_source (void *, const char *); + +/* Record a function definition. This implicitly starts a function + block. The debug_type argument is the type of the return value. + The bfd_boolean indicates whether the function is globally visible. + The bfd_vma is the address of the start of the function. Currently + the parameter types are specified by calls to + debug_record_parameter. */ + +extern bfd_boolean debug_record_function + (void *, const char *, debug_type, bfd_boolean, bfd_vma); + +/* Record a parameter for the current function. */ + +extern bfd_boolean debug_record_parameter + (void *, const char *, debug_type, enum debug_parm_kind, bfd_vma); + +/* End a function definition. The argument is the address where the + function ends. */ + +extern bfd_boolean debug_end_function (void *, bfd_vma); + +/* Start a block in a function. All local information will be + recorded in this block, until the matching call to debug_end_block. + debug_start_block and debug_end_block may be nested. The argument + is the address at which this block starts. */ + +extern bfd_boolean debug_start_block (void *, bfd_vma); + +/* Finish a block in a function. This matches the call to + debug_start_block. The argument is the address at which this block + ends. */ + +extern bfd_boolean debug_end_block (void *, bfd_vma); + +/* Associate a line number in the current source file with a given + address. */ + +extern bfd_boolean debug_record_line (void *, unsigned long, bfd_vma); + +/* Start a named common block. This is a block of variables that may + move in memory. */ + +extern bfd_boolean debug_start_common_block (void *, const char *); + +/* End a named common block. */ + +extern bfd_boolean debug_end_common_block (void *, const char *); + +/* Record a named integer constant. */ + +extern bfd_boolean debug_record_int_const (void *, const char *, bfd_vma); + +/* Record a named floating point constant. */ + +extern bfd_boolean debug_record_float_const (void *, const char *, double); + +/* Record a typed constant with an integral value. */ + +extern bfd_boolean debug_record_typed_const + (void *, const char *, debug_type, bfd_vma); + +/* Record a label. */ + +extern bfd_boolean debug_record_label + (void *, const char *, debug_type, bfd_vma); + +/* Record a variable. */ + +extern bfd_boolean debug_record_variable + (void *, const char *, debug_type, enum debug_var_kind, bfd_vma); + +/* Make an indirect type. The first argument is a pointer to the + location where the real type will be placed. The second argument + is the type tag, if there is one; this may be NULL; the only + purpose of this argument is so that debug_get_type_name can return + something useful. This function may be used when a type is + referenced before it is defined. */ + +extern debug_type debug_make_indirect_type + (void *, debug_type *, const char *); + +/* Make a void type. */ + +extern debug_type debug_make_void_type (void *); + +/* Make an integer type of a given size. The bfd_boolean argument is TRUE + if the integer is unsigned. */ + +extern debug_type debug_make_int_type (void *, unsigned int, bfd_boolean); + +/* Make a floating point type of a given size. FIXME: On some + platforms, like an Alpha, you probably need to be able to specify + the format. */ + +extern debug_type debug_make_float_type (void *, unsigned int); + +/* Make a boolean type of a given size. */ + +extern debug_type debug_make_bool_type (void *, unsigned int); + +/* Make a complex type of a given size. */ + +extern debug_type debug_make_complex_type (void *, unsigned int); + +/* Make a structure type. The second argument is TRUE for a struct, + FALSE for a union. The third argument is the size of the struct. + The fourth argument is a NULL terminated array of fields. */ + +extern debug_type debug_make_struct_type + (void *, bfd_boolean, bfd_vma, debug_field *); + +/* Make an object type. The first three arguments after the handle + are the same as for debug_make_struct_type. The next arguments are + a NULL terminated array of base classes, a NULL terminated array of + methods, the type of the object holding the virtual function table + if it is not this object, and a bfd_boolean which is TRUE if this + object has its own virtual function table. */ + +extern debug_type debug_make_object_type + (void *, bfd_boolean, bfd_vma, debug_field *, debug_baseclass *, + debug_method *, debug_type, bfd_boolean); + +/* Make an enumeration type. The arguments are a null terminated + array of strings, and an array of corresponding values. */ + +extern debug_type debug_make_enum_type + (void *, const char **, bfd_signed_vma *); + +/* Make a pointer to a given type. */ + +extern debug_type debug_make_pointer_type (void *, debug_type); + +/* Make a function type. The second argument is the return type. The + third argument is a NULL terminated array of argument types. The + fourth argument is TRUE if the function takes a variable number of + arguments. If the third argument is NULL, then the argument types + are unknown. */ + +extern debug_type debug_make_function_type + (void *, debug_type, debug_type *, bfd_boolean); + +/* Make a reference to a given type. */ + +extern debug_type debug_make_reference_type (void *, debug_type); + +/* Make a range of a given type from a lower to an upper bound. */ + +extern debug_type debug_make_range_type + (void *, debug_type, bfd_signed_vma, bfd_signed_vma); + +/* Make an array type. The second argument is the type of an element + of the array. The third argument is the type of a range of the + array. The fourth and fifth argument are the lower and upper + bounds, respectively (if the bounds are not known, lower should be + 0 and upper should be -1). The sixth argument is TRUE if this + array is actually a string, as in C. */ + +extern debug_type debug_make_array_type + (void *, debug_type, debug_type, bfd_signed_vma, bfd_signed_vma, + bfd_boolean); + +/* Make a set of a given type. For example, a Pascal set type. The + bfd_boolean argument is TRUE if this set is actually a bitstring, as in + CHILL. */ + +extern debug_type debug_make_set_type (void *, debug_type, bfd_boolean); + +/* Make a type for a pointer which is relative to an object. The + second argument is the type of the object to which the pointer is + relative. The third argument is the type that the pointer points + to. */ + +extern debug_type debug_make_offset_type (void *, debug_type, debug_type); + +/* Make a type for a method function. The second argument is the + return type. The third argument is the domain. The fourth + argument is a NULL terminated array of argument types. The fifth + argument is TRUE if the function takes a variable number of + arguments, in which case the array of argument types indicates the + types of the first arguments. The domain and the argument array + may be NULL, in which case this is a stub method and that + information is not available. Stabs debugging uses this, and gets + the argument types from the mangled name. */ + +extern debug_type debug_make_method_type + (void *, debug_type, debug_type, debug_type *, bfd_boolean); + +/* Make a const qualified version of a given type. */ + +extern debug_type debug_make_const_type (void *, debug_type); + +/* Make a volatile qualified version of a given type. */ + +extern debug_type debug_make_volatile_type (void *, debug_type); + +/* Make an undefined tagged type. For example, a struct which has + been mentioned, but not defined. */ + +extern debug_type debug_make_undefined_tagged_type + (void *, const char *, enum debug_type_kind); + +/* Make a base class for an object. The second argument is the base + class type. The third argument is the bit position of this base + class in the object. The fourth argument is whether this is a + virtual class. The fifth argument is the visibility of the base + class. */ + +extern debug_baseclass debug_make_baseclass + (void *, debug_type, bfd_vma, bfd_boolean, enum debug_visibility); + +/* Make a field for a struct. The second argument is the name. The + third argument is the type of the field. The fourth argument is + the bit position of the field. The fifth argument is the size of + the field (it may be zero). The sixth argument is the visibility + of the field. */ + +extern debug_field debug_make_field + (void *, const char *, debug_type, bfd_vma, bfd_vma, enum debug_visibility); + +/* Make a static member of an object. The second argument is the + name. The third argument is the type of the member. The fourth + argument is the physical name of the member (i.e., the name as a + global variable). The fifth argument is the visibility of the + member. */ + +extern debug_field debug_make_static_member + (void *, const char *, debug_type, const char *, enum debug_visibility); + +/* Make a method. The second argument is the name, and the third + argument is a NULL terminated array of method variants. Each + method variant is a method with this name but with different + argument types. */ + +extern debug_method debug_make_method + (void *, const char *, debug_method_variant *); + +/* Make a method variant. The second argument is the physical name of + the function. The third argument is the type of the function, + probably constructed by debug_make_method_type. The fourth + argument is the visibility. The fifth argument is whether this is + a const function. The sixth argument is whether this is a volatile + function. The seventh argument is the index in the virtual + function table, if any. The eighth argument is the virtual + function context. */ + +extern debug_method_variant debug_make_method_variant + (void *, const char *, debug_type, enum debug_visibility, bfd_boolean, + bfd_boolean, bfd_vma, debug_type); + +/* Make a static method argument. The arguments are the same as for + debug_make_method_variant, except that the last two are omitted + since a static method can not also be virtual. */ + +extern debug_method_variant debug_make_static_method_variant + (void *, const char *, debug_type, enum debug_visibility, bfd_boolean, + bfd_boolean); + +/* Name a type. This returns a new type with an attached name. */ + +extern debug_type debug_name_type (void *, const char *, debug_type); + +/* Give a tag to a type, such as a struct or union. This returns a + new type with an attached tag. */ + +extern debug_type debug_tag_type (void *, const char *, debug_type); + +/* Record the size of a given type. */ + +extern bfd_boolean debug_record_type_size (void *, debug_type, unsigned int); + +/* Find a named type. */ + +extern debug_type debug_find_named_type (void *, const char *); + +/* Find a tagged type. */ + +extern debug_type debug_find_tagged_type + (void *, const char *, enum debug_type_kind); + +/* Get the kind of a type. */ + +extern enum debug_type_kind debug_get_type_kind (void *, debug_type); + +/* Get the name of a type. */ + +extern const char *debug_get_type_name (void *, debug_type); + +/* Get the size of a type. */ + +extern bfd_vma debug_get_type_size (void *, debug_type); + +/* Get the return type of a function or method type. */ + +extern debug_type debug_get_return_type (void *, debug_type); + +/* Get the NULL terminated array of parameter types for a function or + method type (actually, parameter types are not currently stored for + function types). This may be used to determine whether a method + type is a stub method or not. The last argument points to a + bfd_boolean which is set to TRUE if the function takes a variable + number of arguments. */ + +extern const debug_type *debug_get_parameter_types + (void *, debug_type, bfd_boolean *); + +/* Get the target type of a pointer or reference or const or volatile + type. */ + +extern debug_type debug_get_target_type (void *, debug_type); + +/* Get the NULL terminated array of fields for a struct, union, or + class. */ + +extern const debug_field *debug_get_fields (void *, debug_type); + +/* Get the type of a field. */ + +extern debug_type debug_get_field_type (void *, debug_field); + +/* Get the name of a field. */ + +extern const char *debug_get_field_name (void *, debug_field); + +/* Get the bit position of a field within the containing structure. + If the field is a static member, this will return (bfd_vma) -1. */ + +extern bfd_vma debug_get_field_bitpos (void *, debug_field); + +/* Get the bit size of a field. If the field is a static member, this + will return (bfd_vma) -1. */ + +extern bfd_vma debug_get_field_bitsize (void *, debug_field); + +/* Get the visibility of a field. */ + +extern enum debug_visibility debug_get_field_visibility (void *, debug_field); + +/* Get the physical name of a field, if it is a static member. If the + field is not a static member, this will return NULL. */ + +extern const char *debug_get_field_physname (void *, debug_field); + +/* Write out the recorded debugging information. This takes a set of + function pointers which are called to do the actual writing. The + first void * is the debugging handle. The second void * is a handle + which is passed to the functions. */ + +extern bfd_boolean debug_write + (void *, const struct debug_write_fns *, void *); + +#endif /* DEBUG_H */ diff --git a/contrib/toolchain/binutils/binutils/filemode.c b/contrib/toolchain/binutils/binutils/filemode.c new file mode 100644 index 0000000000..8b29defc52 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/filemode.c @@ -0,0 +1,249 @@ +/* filemode.c -- make a string describing file modes + Copyright 1985, 1990, 1991, 1994, 1995, 1997, 1999, 2002, 2003, 2005, + 2007 Free Software Foundation, Inc. + + 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 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "bucomm.h" + +static char ftypelet (unsigned long); +static void setst (unsigned long, char *); + +/* filemodestring - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +/* Get definitions for the file permission bits. */ + +#ifndef S_IRWXU +#define S_IRWXU 0700 +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +#define S_IWUSR 0200 +#endif +#ifndef S_IXUSR +#define S_IXUSR 0100 +#endif + +#ifndef S_IRWXG +#define S_IRWXG 0070 +#endif +#ifndef S_IRGRP +#define S_IRGRP 0040 +#endif +#ifndef S_IWGRP +#define S_IWGRP 0020 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0010 +#endif + +#ifndef S_IRWXO +#define S_IRWXO 0007 +#endif +#ifndef S_IROTH +#define S_IROTH 0004 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0002 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0001 +#endif + +/* Like filemodestring, but only the relevant part of the `struct stat' + is given as an argument. */ + +void +mode_string (unsigned long mode, char *str) +{ + str[0] = ftypelet ((unsigned long) mode); + str[1] = (mode & S_IRUSR) != 0 ? 'r' : '-'; + str[2] = (mode & S_IWUSR) != 0 ? 'w' : '-'; + str[3] = (mode & S_IXUSR) != 0 ? 'x' : '-'; + str[4] = (mode & S_IRGRP) != 0 ? 'r' : '-'; + str[5] = (mode & S_IWGRP) != 0 ? 'w' : '-'; + str[6] = (mode & S_IXGRP) != 0 ? 'x' : '-'; + str[7] = (mode & S_IROTH) != 0 ? 'r' : '-'; + str[8] = (mode & S_IWOTH) != 0 ? 'w' : '-'; + str[9] = (mode & S_IXOTH) != 0 ? 'x' : '-'; + setst ((unsigned long) mode, str); +} + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'm' for multiplexer files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for any other file type. */ + +#ifndef S_ISDIR +#ifdef S_IFDIR +#define S_ISDIR(i) (((i) & S_IFMT) == S_IFDIR) +#else /* ! defined (S_IFDIR) */ +#define S_ISDIR(i) (((i) & 0170000) == 040000) +#endif /* ! defined (S_IFDIR) */ +#endif /* ! defined (S_ISDIR) */ + +#ifndef S_ISBLK +#ifdef S_IFBLK +#define S_ISBLK(i) (((i) & S_IFMT) == S_IFBLK) +#else /* ! defined (S_IFBLK) */ +#define S_ISBLK(i) 0 +#endif /* ! defined (S_IFBLK) */ +#endif /* ! defined (S_ISBLK) */ + +#ifndef S_ISCHR +#ifdef S_IFCHR +#define S_ISCHR(i) (((i) & S_IFMT) == S_IFCHR) +#else /* ! defined (S_IFCHR) */ +#define S_ISCHR(i) 0 +#endif /* ! defined (S_IFCHR) */ +#endif /* ! defined (S_ISCHR) */ + +#ifndef S_ISFIFO +#ifdef S_IFIFO +#define S_ISFIFO(i) (((i) & S_IFMT) == S_IFIFO) +#else /* ! defined (S_IFIFO) */ +#define S_ISFIFO(i) 0 +#endif /* ! defined (S_IFIFO) */ +#endif /* ! defined (S_ISFIFO) */ + +#ifndef S_ISSOCK +#ifdef S_IFSOCK +#define S_ISSOCK(i) (((i) & S_IFMT) == S_IFSOCK) +#else /* ! defined (S_IFSOCK) */ +#define S_ISSOCK(i) 0 +#endif /* ! defined (S_IFSOCK) */ +#endif /* ! defined (S_ISSOCK) */ + +#ifndef S_ISLNK +#ifdef S_IFLNK +#define S_ISLNK(i) (((i) & S_IFMT) == S_IFLNK) +#else /* ! defined (S_IFLNK) */ +#define S_ISLNK(i) 0 +#endif /* ! defined (S_IFLNK) */ +#endif /* ! defined (S_ISLNK) */ + +static char +ftypelet (unsigned long bits) +{ + if (S_ISDIR (bits)) + return 'd'; + if (S_ISLNK (bits)) + return 'l'; + if (S_ISBLK (bits)) + return 'b'; + if (S_ISCHR (bits)) + return 'c'; + if (S_ISSOCK (bits)) + return 's'; + if (S_ISFIFO (bits)) + return 'p'; + +#ifdef S_IFMT +#ifdef S_IFMPC + if ((bits & S_IFMT) == S_IFMPC + || (bits & S_IFMT) == S_IFMPB) + return 'm'; +#endif +#ifdef S_IFNWK + if ((bits & S_IFMT) == S_IFNWK) + return 'n'; +#endif +#endif + + return '-'; +} + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (unsigned long bits ATTRIBUTE_UNUSED, char *chars ATTRIBUTE_UNUSED) +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} diff --git a/contrib/toolchain/binutils/binutils/ieee.c b/contrib/toolchain/binutils/binutils/ieee.c new file mode 100644 index 0000000000..044da313dd --- /dev/null +++ b/contrib/toolchain/binutils/binutils/ieee.c @@ -0,0 +1,7411 @@ +/* ieee.c -- Read and write IEEE-695 debugging information. + Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, + 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This file reads and writes IEEE-695 debugging information. */ + +#include "sysdep.h" +#include +#include "bfd.h" +#include "ieee.h" +#include "libiberty.h" +#include "debug.h" +#include "budbg.h" +#include "filenames.h" + +/* This structure holds an entry on the block stack. */ + +struct ieee_block +{ + /* The kind of block. */ + int kind; + /* The source file name, for a BB5 block. */ + const char *filename; + /* The index of the function type, for a BB4 or BB6 block. */ + unsigned int fnindx; + /* TRUE if this function is being skipped. */ + bfd_boolean skip; +}; + +/* This structure is the block stack. */ + +#define BLOCKSTACK_SIZE (16) + +struct ieee_blockstack +{ + /* The stack pointer. */ + struct ieee_block *bsp; + /* The stack. */ + struct ieee_block stack[BLOCKSTACK_SIZE]; +}; + +/* This structure holds information for a variable. */ + +enum ieee_var_kind + { + IEEE_UNKNOWN, + IEEE_EXTERNAL, + IEEE_GLOBAL, + IEEE_STATIC, + IEEE_LOCAL, + IEEE_FUNCTION + }; + +struct ieee_var +{ + /* Start of name. */ + const char *name; + /* Length of name. */ + unsigned long namlen; + /* Type. */ + debug_type type; + /* Slot if we make an indirect type. */ + debug_type *pslot; + /* Kind of variable or function. */ + enum ieee_var_kind kind; +}; + +/* This structure holds all the variables. */ + +struct ieee_vars +{ + /* Number of slots allocated. */ + unsigned int alloc; + /* Variables. */ + struct ieee_var *vars; +}; + +/* This structure holds information for a type. We need this because + we don't want to represent bitfields as real types. */ + +struct ieee_type +{ + /* Type. */ + debug_type type; + /* Slot if this is type is referenced before it is defined. */ + debug_type *pslot; + /* Slots for arguments if we make indirect types for them. */ + debug_type *arg_slots; + /* If this is a bitfield, this is the size in bits. If this is not + a bitfield, this is zero. */ + unsigned long bitsize; +}; + +/* This structure holds all the type information. */ + +struct ieee_types +{ + /* Number of slots allocated. */ + unsigned int alloc; + /* Types. */ + struct ieee_type *types; + /* Builtin types. */ +#define BUILTIN_TYPE_COUNT (60) + debug_type builtins[BUILTIN_TYPE_COUNT]; +}; + +/* This structure holds a linked last of structs with their tag names, + so that we can convert them to C++ classes if necessary. */ + +struct ieee_tag +{ + /* Next tag. */ + struct ieee_tag *next; + /* This tag name. */ + const char *name; + /* The type of the tag. */ + debug_type type; + /* The tagged type is an indirect type pointing at this slot. */ + debug_type slot; + /* This is an array of slots used when a field type is converted + into a indirect type, in case it needs to be later converted into + a reference type. */ + debug_type *fslots; +}; + +/* This structure holds the information we pass around to the parsing + functions. */ + +struct ieee_info +{ + /* The debugging handle. */ + void *dhandle; + /* The BFD. */ + bfd *abfd; + /* The start of the bytes to be parsed. */ + const bfd_byte *bytes; + /* The end of the bytes to be parsed. */ + const bfd_byte *pend; + /* The block stack. */ + struct ieee_blockstack blockstack; + /* Whether we have seen a BB1 or BB2. */ + bfd_boolean saw_filename; + /* The variables. */ + struct ieee_vars vars; + /* The global variables, after a global typedef block. */ + struct ieee_vars *global_vars; + /* The types. */ + struct ieee_types types; + /* The global types, after a global typedef block. */ + struct ieee_types *global_types; + /* The list of tagged structs. */ + struct ieee_tag *tags; +}; + +/* Basic builtin types, not including the pointers. */ + +enum builtin_types +{ + builtin_unknown = 0, + builtin_void = 1, + builtin_signed_char = 2, + builtin_unsigned_char = 3, + builtin_signed_short_int = 4, + builtin_unsigned_short_int = 5, + builtin_signed_long = 6, + builtin_unsigned_long = 7, + builtin_signed_long_long = 8, + builtin_unsigned_long_long = 9, + builtin_float = 10, + builtin_double = 11, + builtin_long_double = 12, + builtin_long_long_double = 13, + builtin_quoted_string = 14, + builtin_instruction_address = 15, + builtin_int = 16, + builtin_unsigned = 17, + builtin_unsigned_int = 18, + builtin_char = 19, + builtin_long = 20, + builtin_short = 21, + builtin_unsigned_short = 22, + builtin_short_int = 23, + builtin_signed_short = 24, + builtin_bcd_float = 25 +}; + +/* These are the values found in the derivation flags of a 'b' + component record of a 'T' type extension record in a C++ pmisc + record. These are bitmasks. */ + +/* Set for a private base class, clear for a public base class. + Protected base classes are not supported. */ +#define BASEFLAGS_PRIVATE (0x1) +/* Set for a virtual base class. */ +#define BASEFLAGS_VIRTUAL (0x2) +/* Set for a friend class, clear for a base class. */ +#define BASEFLAGS_FRIEND (0x10) + +/* These are the values found in the specs flags of a 'd', 'm', or 'v' + component record of a 'T' type extension record in a C++ pmisc + record. The same flags are used for a 'M' record in a C++ pmisc + record. */ + +/* The lower two bits hold visibility information. */ +#define CXXFLAGS_VISIBILITY (0x3) +/* This value in the lower two bits indicates a public member. */ +#define CXXFLAGS_VISIBILITY_PUBLIC (0x0) +/* This value in the lower two bits indicates a private member. */ +#define CXXFLAGS_VISIBILITY_PRIVATE (0x1) +/* This value in the lower two bits indicates a protected member. */ +#define CXXFLAGS_VISIBILITY_PROTECTED (0x2) +/* Set for a static member. */ +#define CXXFLAGS_STATIC (0x4) +/* Set for a virtual override. */ +#define CXXFLAGS_OVERRIDE (0x8) +/* Set for a friend function. */ +#define CXXFLAGS_FRIEND (0x10) +/* Set for a const function. */ +#define CXXFLAGS_CONST (0x20) +/* Set for a volatile function. */ +#define CXXFLAGS_VOLATILE (0x40) +/* Set for an overloaded function. */ +#define CXXFLAGS_OVERLOADED (0x80) +/* Set for an operator function. */ +#define CXXFLAGS_OPERATOR (0x100) +/* Set for a constructor or destructor. */ +#define CXXFLAGS_CTORDTOR (0x400) +/* Set for a constructor. */ +#define CXXFLAGS_CTOR (0x200) +/* Set for an inline function. */ +#define CXXFLAGS_INLINE (0x800) + +/* Local functions. */ + +static void ieee_error (struct ieee_info *, const bfd_byte *, const char *); +static void ieee_eof (struct ieee_info *); +static char *savestring (const char *, unsigned long); +static bfd_boolean ieee_read_number + (struct ieee_info *, const bfd_byte **, bfd_vma *); +static bfd_boolean ieee_read_optional_number + (struct ieee_info *, const bfd_byte **, bfd_vma *, bfd_boolean *); +static bfd_boolean ieee_read_id + (struct ieee_info *, const bfd_byte **, const char **, unsigned long *); +static bfd_boolean ieee_read_optional_id + (struct ieee_info *, const bfd_byte **, const char **, unsigned long *, + bfd_boolean *); +static bfd_boolean ieee_read_expression + (struct ieee_info *, const bfd_byte **, bfd_vma *); +static debug_type ieee_builtin_type + (struct ieee_info *, const bfd_byte *, unsigned int); +static bfd_boolean ieee_alloc_type + (struct ieee_info *, unsigned int, bfd_boolean); +static bfd_boolean ieee_read_type_index + (struct ieee_info *, const bfd_byte **, debug_type *); +static int ieee_regno_to_genreg (bfd *, int); +static int ieee_genreg_to_regno (bfd *, int); +static bfd_boolean parse_ieee_bb (struct ieee_info *, const bfd_byte **); +static bfd_boolean parse_ieee_be (struct ieee_info *, const bfd_byte **); +static bfd_boolean parse_ieee_nn (struct ieee_info *, const bfd_byte **); +static bfd_boolean parse_ieee_ty (struct ieee_info *, const bfd_byte **); +static bfd_boolean parse_ieee_atn (struct ieee_info *, const bfd_byte **); +static bfd_boolean ieee_read_cxx_misc + (struct ieee_info *, const bfd_byte **, unsigned long); +static bfd_boolean ieee_read_cxx_class + (struct ieee_info *, const bfd_byte **, unsigned long); +static bfd_boolean ieee_read_cxx_defaults + (struct ieee_info *, const bfd_byte **, unsigned long); +static bfd_boolean ieee_read_reference + (struct ieee_info *, const bfd_byte **); +static bfd_boolean ieee_require_asn + (struct ieee_info *, const bfd_byte **, bfd_vma *); +static bfd_boolean ieee_require_atn65 + (struct ieee_info *, const bfd_byte **, const char **, unsigned long *); + +/* Report an error in the IEEE debugging information. */ + +static void +ieee_error (struct ieee_info *info, const bfd_byte *p, const char *s) +{ + if (p != NULL) + fprintf (stderr, "%s: 0x%lx: %s (0x%x)\n", bfd_get_filename (info->abfd), + (unsigned long) (p - info->bytes), s, *p); + else + fprintf (stderr, "%s: %s\n", bfd_get_filename (info->abfd), s); +} + +/* Report an unexpected EOF in the IEEE debugging information. */ + +static void +ieee_eof (struct ieee_info *info) +{ + ieee_error (info, (const bfd_byte *) NULL, + _("unexpected end of debugging information")); +} + +/* Save a string in memory. */ + +static char * +savestring (const char *start, unsigned long len) +{ + char *ret; + + ret = (char *) xmalloc (len + 1); + memcpy (ret, start, len); + ret[len] = '\0'; + return ret; +} + +/* Read a number which must be present in an IEEE file. */ + +static bfd_boolean +ieee_read_number (struct ieee_info *info, const bfd_byte **pp, bfd_vma *pv) +{ + return ieee_read_optional_number (info, pp, pv, (bfd_boolean *) NULL); +} + +/* Read a number in an IEEE file. If ppresent is not NULL, the number + need not be there. */ + +static bfd_boolean +ieee_read_optional_number (struct ieee_info *info, const bfd_byte **pp, + bfd_vma *pv, bfd_boolean *ppresent) +{ + ieee_record_enum_type b; + + if (*pp >= info->pend) + { + if (ppresent != NULL) + { + *ppresent = FALSE; + return TRUE; + } + ieee_eof (info); + return FALSE; + } + + b = (ieee_record_enum_type) **pp; + ++*pp; + + if (b <= ieee_number_end_enum) + { + *pv = (bfd_vma) b; + if (ppresent != NULL) + *ppresent = TRUE; + return TRUE; + } + + if (b >= ieee_number_repeat_start_enum && b <= ieee_number_repeat_end_enum) + { + unsigned int i; + + i = (int) b - (int) ieee_number_repeat_start_enum; + if (*pp + i - 1 >= info->pend) + { + ieee_eof (info); + return FALSE; + } + + *pv = 0; + for (; i > 0; i--) + { + *pv <<= 8; + *pv += **pp; + ++*pp; + } + + if (ppresent != NULL) + *ppresent = TRUE; + + return TRUE; + } + + if (ppresent != NULL) + { + --*pp; + *ppresent = FALSE; + return TRUE; + } + + ieee_error (info, *pp - 1, _("invalid number")); + return FALSE; +} + +/* Read a required string from an IEEE file. */ + +static bfd_boolean +ieee_read_id (struct ieee_info *info, const bfd_byte **pp, + const char **pname, unsigned long *pnamlen) +{ + return ieee_read_optional_id (info, pp, pname, pnamlen, (bfd_boolean *) NULL); +} + +/* Read a string from an IEEE file. If ppresent is not NULL, the + string is optional. */ + +static bfd_boolean +ieee_read_optional_id (struct ieee_info *info, const bfd_byte **pp, + const char **pname, unsigned long *pnamlen, + bfd_boolean *ppresent) +{ + bfd_byte b; + unsigned long len; + + if (*pp >= info->pend) + { + ieee_eof (info); + return FALSE; + } + + b = **pp; + ++*pp; + + if (b <= 0x7f) + len = b; + else if ((ieee_record_enum_type) b == ieee_extension_length_1_enum) + { + len = **pp; + ++*pp; + } + else if ((ieee_record_enum_type) b == ieee_extension_length_2_enum) + { + len = (**pp << 8) + (*pp)[1]; + *pp += 2; + } + else + { + if (ppresent != NULL) + { + --*pp; + *ppresent = FALSE; + return TRUE; + } + ieee_error (info, *pp - 1, _("invalid string length")); + return FALSE; + } + + if ((unsigned long) (info->pend - *pp) < len) + { + ieee_eof (info); + return FALSE; + } + + *pname = (const char *) *pp; + *pnamlen = len; + *pp += len; + + if (ppresent != NULL) + *ppresent = TRUE; + + return TRUE; +} + +/* Read an expression from an IEEE file. Since this code is only used + to parse debugging information, I haven't bothered to write a full + blown IEEE expression parser. I've only thrown in the things I've + seen in debugging information. This can be easily extended if + necessary. */ + +static bfd_boolean +ieee_read_expression (struct ieee_info *info, const bfd_byte **pp, + bfd_vma *pv) +{ + const bfd_byte *expr_start; +#define EXPR_STACK_SIZE (10) + bfd_vma expr_stack[EXPR_STACK_SIZE]; + bfd_vma *esp; + + expr_start = *pp; + + esp = expr_stack; + + while (1) + { + const bfd_byte *start; + bfd_vma val; + bfd_boolean present; + ieee_record_enum_type c; + + start = *pp; + + if (! ieee_read_optional_number (info, pp, &val, &present)) + return FALSE; + + if (present) + { + if (esp - expr_stack >= EXPR_STACK_SIZE) + { + ieee_error (info, start, _("expression stack overflow")); + return FALSE; + } + *esp++ = val; + continue; + } + + c = (ieee_record_enum_type) **pp; + + if (c >= ieee_module_beginning_enum) + break; + + ++*pp; + + if (c == ieee_comma) + break; + + switch (c) + { + default: + ieee_error (info, start, _("unsupported IEEE expression operator")); + break; + + case ieee_variable_R_enum: + { + bfd_vma indx; + asection *s; + + if (! ieee_read_number (info, pp, &indx)) + return FALSE; + for (s = info->abfd->sections; s != NULL; s = s->next) + if ((bfd_vma) s->target_index == indx) + break; + if (s == NULL) + { + ieee_error (info, start, _("unknown section")); + return FALSE; + } + + if (esp - expr_stack >= EXPR_STACK_SIZE) + { + ieee_error (info, start, _("expression stack overflow")); + return FALSE; + } + + *esp++ = bfd_get_section_vma (info->abfd, s); + } + break; + + case ieee_function_plus_enum: + case ieee_function_minus_enum: + { + bfd_vma v1, v2; + + if (esp - expr_stack < 2) + { + ieee_error (info, start, _("expression stack underflow")); + return FALSE; + } + + v1 = *--esp; + v2 = *--esp; + *esp++ = v1 + v2; + } + break; + } + } + + if (esp - 1 != expr_stack) + { + ieee_error (info, expr_start, _("expression stack mismatch")); + return FALSE; + } + + *pv = *--esp; + + return TRUE; +} + +/* Return an IEEE builtin type. */ + +static debug_type +ieee_builtin_type (struct ieee_info *info, const bfd_byte *p, + unsigned int indx) +{ + void *dhandle; + debug_type type; + const char *name; + + if (indx < BUILTIN_TYPE_COUNT + && info->types.builtins[indx] != DEBUG_TYPE_NULL) + return info->types.builtins[indx]; + + dhandle = info->dhandle; + + if (indx >= 32 && indx < 64) + { + type = debug_make_pointer_type (dhandle, + ieee_builtin_type (info, p, indx - 32)); + assert (indx < BUILTIN_TYPE_COUNT); + info->types.builtins[indx] = type; + return type; + } + + switch ((enum builtin_types) indx) + { + default: + ieee_error (info, p, _("unknown builtin type")); + return NULL; + + case builtin_unknown: + type = debug_make_void_type (dhandle); + name = NULL; + break; + + case builtin_void: + type = debug_make_void_type (dhandle); + name = "void"; + break; + + case builtin_signed_char: + type = debug_make_int_type (dhandle, 1, FALSE); + name = "signed char"; + break; + + case builtin_unsigned_char: + type = debug_make_int_type (dhandle, 1, TRUE); + name = "unsigned char"; + break; + + case builtin_signed_short_int: + type = debug_make_int_type (dhandle, 2, FALSE); + name = "signed short int"; + break; + + case builtin_unsigned_short_int: + type = debug_make_int_type (dhandle, 2, TRUE); + name = "unsigned short int"; + break; + + case builtin_signed_long: + type = debug_make_int_type (dhandle, 4, FALSE); + name = "signed long"; + break; + + case builtin_unsigned_long: + type = debug_make_int_type (dhandle, 4, TRUE); + name = "unsigned long"; + break; + + case builtin_signed_long_long: + type = debug_make_int_type (dhandle, 8, FALSE); + name = "signed long long"; + break; + + case builtin_unsigned_long_long: + type = debug_make_int_type (dhandle, 8, TRUE); + name = "unsigned long long"; + break; + + case builtin_float: + type = debug_make_float_type (dhandle, 4); + name = "float"; + break; + + case builtin_double: + type = debug_make_float_type (dhandle, 8); + name = "double"; + break; + + case builtin_long_double: + /* FIXME: The size for this type should depend upon the + processor. */ + type = debug_make_float_type (dhandle, 12); + name = "long double"; + break; + + case builtin_long_long_double: + type = debug_make_float_type (dhandle, 16); + name = "long long double"; + break; + + case builtin_quoted_string: + type = debug_make_array_type (dhandle, + ieee_builtin_type (info, p, + ((unsigned int) + builtin_char)), + ieee_builtin_type (info, p, + ((unsigned int) + builtin_int)), + 0, -1, TRUE); + name = "QUOTED STRING"; + break; + + case builtin_instruction_address: + /* FIXME: This should be a code address. */ + type = debug_make_int_type (dhandle, 4, TRUE); + name = "instruction address"; + break; + + case builtin_int: + /* FIXME: The size for this type should depend upon the + processor. */ + type = debug_make_int_type (dhandle, 4, FALSE); + name = "int"; + break; + + case builtin_unsigned: + /* FIXME: The size for this type should depend upon the + processor. */ + type = debug_make_int_type (dhandle, 4, TRUE); + name = "unsigned"; + break; + + case builtin_unsigned_int: + /* FIXME: The size for this type should depend upon the + processor. */ + type = debug_make_int_type (dhandle, 4, TRUE); + name = "unsigned int"; + break; + + case builtin_char: + type = debug_make_int_type (dhandle, 1, FALSE); + name = "char"; + break; + + case builtin_long: + type = debug_make_int_type (dhandle, 4, FALSE); + name = "long"; + break; + + case builtin_short: + type = debug_make_int_type (dhandle, 2, FALSE); + name = "short"; + break; + + case builtin_unsigned_short: + type = debug_make_int_type (dhandle, 2, TRUE); + name = "unsigned short"; + break; + + case builtin_short_int: + type = debug_make_int_type (dhandle, 2, FALSE); + name = "short int"; + break; + + case builtin_signed_short: + type = debug_make_int_type (dhandle, 2, FALSE); + name = "signed short"; + break; + + case builtin_bcd_float: + ieee_error (info, p, _("BCD float type not supported")); + return DEBUG_TYPE_NULL; + } + + if (name != NULL) + type = debug_name_type (dhandle, name, type); + + assert (indx < BUILTIN_TYPE_COUNT); + + info->types.builtins[indx] = type; + + return type; +} + +/* Allocate more space in the type table. If ref is TRUE, this is a + reference to the type; if it is not already defined, we should set + up an indirect type. */ + +static bfd_boolean +ieee_alloc_type (struct ieee_info *info, unsigned int indx, bfd_boolean ref) +{ + unsigned int nalloc; + register struct ieee_type *t; + struct ieee_type *tend; + + if (indx >= info->types.alloc) + { + nalloc = info->types.alloc; + if (nalloc == 0) + nalloc = 4; + while (indx >= nalloc) + nalloc *= 2; + + info->types.types = ((struct ieee_type *) + xrealloc (info->types.types, + nalloc * sizeof *info->types.types)); + + memset (info->types.types + info->types.alloc, 0, + (nalloc - info->types.alloc) * sizeof *info->types.types); + + tend = info->types.types + nalloc; + for (t = info->types.types + info->types.alloc; t < tend; t++) + t->type = DEBUG_TYPE_NULL; + + info->types.alloc = nalloc; + } + + if (ref) + { + t = info->types.types + indx; + if (t->type == NULL) + { + t->pslot = (debug_type *) xmalloc (sizeof *t->pslot); + *t->pslot = DEBUG_TYPE_NULL; + t->type = debug_make_indirect_type (info->dhandle, t->pslot, + (const char *) NULL); + if (t->type == NULL) + return FALSE; + } + } + + return TRUE; +} + +/* Read a type index and return the corresponding type. */ + +static bfd_boolean +ieee_read_type_index (struct ieee_info *info, const bfd_byte **pp, + debug_type *ptype) +{ + const bfd_byte *start; + bfd_vma indx; + + start = *pp; + + if (! ieee_read_number (info, pp, &indx)) + return FALSE; + + if (indx < 256) + { + *ptype = ieee_builtin_type (info, start, indx); + if (*ptype == NULL) + return FALSE; + return TRUE; + } + + indx -= 256; + if (! ieee_alloc_type (info, indx, TRUE)) + return FALSE; + + *ptype = info->types.types[indx].type; + + return TRUE; +} + +/* Parse IEEE debugging information for a file. This is passed the + bytes which compose the Debug Information Part of an IEEE file. */ + +bfd_boolean +parse_ieee (void *dhandle, bfd *abfd, const bfd_byte *bytes, bfd_size_type len) +{ + struct ieee_info info; + unsigned int i; + const bfd_byte *p, *pend; + + info.dhandle = dhandle; + info.abfd = abfd; + info.bytes = bytes; + info.pend = bytes + len; + info.blockstack.bsp = info.blockstack.stack; + info.saw_filename = FALSE; + info.vars.alloc = 0; + info.vars.vars = NULL; + info.global_vars = NULL; + info.types.alloc = 0; + info.types.types = NULL; + info.global_types = NULL; + info.tags = NULL; + for (i = 0; i < BUILTIN_TYPE_COUNT; i++) + info.types.builtins[i] = DEBUG_TYPE_NULL; + + p = bytes; + pend = info.pend; + while (p < pend) + { + const bfd_byte *record_start; + ieee_record_enum_type c; + + record_start = p; + + c = (ieee_record_enum_type) *p++; + + if (c == ieee_at_record_enum) + c = (ieee_record_enum_type) (((unsigned int) c << 8) | *p++); + + if (c <= ieee_number_repeat_end_enum) + { + ieee_error (&info, record_start, _("unexpected number")); + return FALSE; + } + + switch (c) + { + default: + ieee_error (&info, record_start, _("unexpected record type")); + return FALSE; + + case ieee_bb_record_enum: + if (! parse_ieee_bb (&info, &p)) + return FALSE; + break; + + case ieee_be_record_enum: + if (! parse_ieee_be (&info, &p)) + return FALSE; + break; + + case ieee_nn_record: + if (! parse_ieee_nn (&info, &p)) + return FALSE; + break; + + case ieee_ty_record_enum: + if (! parse_ieee_ty (&info, &p)) + return FALSE; + break; + + case ieee_atn_record_enum: + if (! parse_ieee_atn (&info, &p)) + return FALSE; + break; + } + } + + if (info.blockstack.bsp != info.blockstack.stack) + { + ieee_error (&info, (const bfd_byte *) NULL, + _("blocks left on stack at end")); + return FALSE; + } + + return TRUE; +} + +/* Handle an IEEE BB record. */ + +static bfd_boolean +parse_ieee_bb (struct ieee_info *info, const bfd_byte **pp) +{ + const bfd_byte *block_start; + bfd_byte b; + bfd_vma size; + const char *name; + unsigned long namlen; + char *namcopy = NULL; + unsigned int fnindx; + bfd_boolean skip; + + block_start = *pp; + + b = **pp; + ++*pp; + + if (! ieee_read_number (info, pp, &size) + || ! ieee_read_id (info, pp, &name, &namlen)) + return FALSE; + + fnindx = (unsigned int) -1; + skip = FALSE; + + switch (b) + { + case 1: + /* BB1: Type definitions local to a module. */ + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_set_filename (info->dhandle, namcopy)) + return FALSE; + info->saw_filename = TRUE; + + /* Discard any variables or types we may have seen before. */ + if (info->vars.vars != NULL) + free (info->vars.vars); + info->vars.vars = NULL; + info->vars.alloc = 0; + if (info->types.types != NULL) + free (info->types.types); + info->types.types = NULL; + info->types.alloc = 0; + + /* Initialize the types to the global types. */ + if (info->global_types != NULL) + { + info->types.alloc = info->global_types->alloc; + info->types.types = ((struct ieee_type *) + xmalloc (info->types.alloc + * sizeof (*info->types.types))); + memcpy (info->types.types, info->global_types->types, + info->types.alloc * sizeof (*info->types.types)); + } + + break; + + case 2: + /* BB2: Global type definitions. The name is supposed to be + empty, but we don't check. */ + if (! debug_set_filename (info->dhandle, "*global*")) + return FALSE; + info->saw_filename = TRUE; + break; + + case 3: + /* BB3: High level module block begin. We don't have to do + anything here. The name is supposed to be the same as for + the BB1, but we don't check. */ + break; + + case 4: + /* BB4: Global function. */ + { + bfd_vma stackspace, typindx, offset; + debug_type return_type; + + if (! ieee_read_number (info, pp, &stackspace) + || ! ieee_read_number (info, pp, &typindx) + || ! ieee_read_expression (info, pp, &offset)) + return FALSE; + + /* We have no way to record the stack space. FIXME. */ + + if (typindx < 256) + { + return_type = ieee_builtin_type (info, block_start, typindx); + if (return_type == DEBUG_TYPE_NULL) + return FALSE; + } + else + { + typindx -= 256; + if (! ieee_alloc_type (info, typindx, TRUE)) + return FALSE; + fnindx = typindx; + return_type = info->types.types[typindx].type; + if (debug_get_type_kind (info->dhandle, return_type) + == DEBUG_KIND_FUNCTION) + return_type = debug_get_return_type (info->dhandle, + return_type); + } + + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_record_function (info->dhandle, namcopy, return_type, + TRUE, offset)) + return FALSE; + } + break; + + case 5: + /* BB5: File name for source line numbers. */ + { + unsigned int i; + + /* We ignore the date and time. FIXME. */ + for (i = 0; i < 6; i++) + { + bfd_vma ignore; + bfd_boolean present; + + if (! ieee_read_optional_number (info, pp, &ignore, &present)) + return FALSE; + if (! present) + break; + } + + if (! info->saw_filename) + { + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_set_filename (info->dhandle, namcopy)) + return FALSE; + info->saw_filename = TRUE; + } + + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_start_source (info->dhandle, namcopy)) + return FALSE; + } + break; + + case 6: + /* BB6: Local function or block. */ + { + bfd_vma stackspace, typindx, offset; + + if (! ieee_read_number (info, pp, &stackspace) + || ! ieee_read_number (info, pp, &typindx) + || ! ieee_read_expression (info, pp, &offset)) + return FALSE; + + /* We have no way to record the stack space. FIXME. */ + + if (namlen == 0) + { + if (! debug_start_block (info->dhandle, offset)) + return FALSE; + /* Change b to indicate that this is a block + rather than a function. */ + b = 0x86; + } + else + { + /* The MRI C++ compiler will output a fake function named + __XRYCPP to hold C++ debugging information. We skip + that function. This is not crucial, but it makes + converting from IEEE to other debug formats work + better. */ + if (strncmp (name, "__XRYCPP", namlen) == 0) + skip = TRUE; + else + { + debug_type return_type; + + if (typindx < 256) + { + return_type = ieee_builtin_type (info, block_start, + typindx); + if (return_type == NULL) + return FALSE; + } + else + { + typindx -= 256; + if (! ieee_alloc_type (info, typindx, TRUE)) + return FALSE; + fnindx = typindx; + return_type = info->types.types[typindx].type; + if (debug_get_type_kind (info->dhandle, return_type) + == DEBUG_KIND_FUNCTION) + return_type = debug_get_return_type (info->dhandle, + return_type); + } + + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_record_function (info->dhandle, namcopy, + return_type, FALSE, offset)) + return FALSE; + } + } + } + break; + + case 10: + /* BB10: Assembler module scope. In the normal case, we + completely ignore all this information. FIXME. */ + { + const char *inam, *vstr; + unsigned long inamlen, vstrlen; + bfd_vma tool_type; + bfd_boolean present; + unsigned int i; + + if (! info->saw_filename) + { + namcopy = savestring (name, namlen); + if (namcopy == NULL) + return FALSE; + if (! debug_set_filename (info->dhandle, namcopy)) + return FALSE; + info->saw_filename = TRUE; + } + + if (! ieee_read_id (info, pp, &inam, &inamlen) + || ! ieee_read_number (info, pp, &tool_type) + || ! ieee_read_optional_id (info, pp, &vstr, &vstrlen, &present)) + return FALSE; + for (i = 0; i < 6; i++) + { + bfd_vma ignore; + + if (! ieee_read_optional_number (info, pp, &ignore, &present)) + return FALSE; + if (! present) + break; + } + } + break; + + case 11: + /* BB11: Module section. We completely ignore all this + information. FIXME. */ + { + bfd_vma sectype, secindx, offset, map; + bfd_boolean present; + + if (! ieee_read_number (info, pp, §ype) + || ! ieee_read_number (info, pp, &secindx) + || ! ieee_read_expression (info, pp, &offset) + || ! ieee_read_optional_number (info, pp, &map, &present)) + return FALSE; + } + break; + + default: + ieee_error (info, block_start, _("unknown BB type")); + return FALSE; + } + + + /* Push this block on the block stack. */ + + if (info->blockstack.bsp >= info->blockstack.stack + BLOCKSTACK_SIZE) + { + ieee_error (info, (const bfd_byte *) NULL, _("stack overflow")); + return FALSE; + } + + info->blockstack.bsp->kind = b; + if (b == 5) + info->blockstack.bsp->filename = namcopy; + info->blockstack.bsp->fnindx = fnindx; + info->blockstack.bsp->skip = skip; + ++info->blockstack.bsp; + + return TRUE; +} + +/* Handle an IEEE BE record. */ + +static bfd_boolean +parse_ieee_be (struct ieee_info *info, const bfd_byte **pp) +{ + bfd_vma offset; + + if (info->blockstack.bsp <= info->blockstack.stack) + { + ieee_error (info, *pp, _("stack underflow")); + return FALSE; + } + --info->blockstack.bsp; + + switch (info->blockstack.bsp->kind) + { + case 2: + /* When we end the global typedefs block, we copy out the + contents of info->vars. This is because the variable indices + may be reused in the local blocks. However, we need to + preserve them so that we can locate a function returning a + reference variable whose type is named in the global typedef + block. */ + info->global_vars = ((struct ieee_vars *) + xmalloc (sizeof *info->global_vars)); + info->global_vars->alloc = info->vars.alloc; + info->global_vars->vars = ((struct ieee_var *) + xmalloc (info->vars.alloc + * sizeof (*info->vars.vars))); + memcpy (info->global_vars->vars, info->vars.vars, + info->vars.alloc * sizeof (*info->vars.vars)); + + /* We also copy out the non builtin parts of info->types, since + the types are discarded when we start a new block. */ + info->global_types = ((struct ieee_types *) + xmalloc (sizeof *info->global_types)); + info->global_types->alloc = info->types.alloc; + info->global_types->types = ((struct ieee_type *) + xmalloc (info->types.alloc + * sizeof (*info->types.types))); + memcpy (info->global_types->types, info->types.types, + info->types.alloc * sizeof (*info->types.types)); + memset (info->global_types->builtins, 0, + sizeof (info->global_types->builtins)); + + break; + + case 4: + case 6: + if (! ieee_read_expression (info, pp, &offset)) + return FALSE; + if (! info->blockstack.bsp->skip) + { + if (! debug_end_function (info->dhandle, offset + 1)) + return FALSE; + } + break; + + case 0x86: + /* This is BE6 when BB6 started a block rather than a local + function. */ + if (! ieee_read_expression (info, pp, &offset)) + return FALSE; + if (! debug_end_block (info->dhandle, offset + 1)) + return FALSE; + break; + + case 5: + /* When we end a BB5, we look up the stack for the last BB5, if + there is one, so that we can call debug_start_source. */ + if (info->blockstack.bsp > info->blockstack.stack) + { + struct ieee_block *bl; + + bl = info->blockstack.bsp; + do + { + --bl; + if (bl->kind == 5) + { + if (! debug_start_source (info->dhandle, bl->filename)) + return FALSE; + break; + } + } + while (bl != info->blockstack.stack); + } + break; + + case 11: + if (! ieee_read_expression (info, pp, &offset)) + return FALSE; + /* We just ignore the module size. FIXME. */ + break; + + default: + /* Other block types do not have any trailing information. */ + break; + } + + return TRUE; +} + +/* Parse an NN record. */ + +static bfd_boolean +parse_ieee_nn (struct ieee_info *info, const bfd_byte **pp) +{ + const bfd_byte *nn_start; + bfd_vma varindx; + const char *name; + unsigned long namlen; + + nn_start = *pp; + + if (! ieee_read_number (info, pp, &varindx) + || ! ieee_read_id (info, pp, &name, &namlen)) + return FALSE; + + if (varindx < 32) + { + ieee_error (info, nn_start, _("illegal variable index")); + return FALSE; + } + varindx -= 32; + + if (varindx >= info->vars.alloc) + { + unsigned int alloc; + + alloc = info->vars.alloc; + if (alloc == 0) + alloc = 4; + while (varindx >= alloc) + alloc *= 2; + info->vars.vars = ((struct ieee_var *) + xrealloc (info->vars.vars, + alloc * sizeof *info->vars.vars)); + memset (info->vars.vars + info->vars.alloc, 0, + (alloc - info->vars.alloc) * sizeof *info->vars.vars); + info->vars.alloc = alloc; + } + + info->vars.vars[varindx].name = name; + info->vars.vars[varindx].namlen = namlen; + + return TRUE; +} + +/* Parse a TY record. */ + +static bfd_boolean +parse_ieee_ty (struct ieee_info *info, const bfd_byte **pp) +{ + const bfd_byte *ty_start, *ty_var_start, *ty_code_start; + bfd_vma typeindx, varindx, tc; + void *dhandle; + bfd_boolean tag, typdef; + debug_type *arg_slots; + unsigned long type_bitsize; + debug_type type; + + ty_start = *pp; + + if (! ieee_read_number (info, pp, &typeindx)) + return FALSE; + + if (typeindx < 256) + { + ieee_error (info, ty_start, _("illegal type index")); + return FALSE; + } + + typeindx -= 256; + if (! ieee_alloc_type (info, typeindx, FALSE)) + return FALSE; + + if (**pp != 0xce) + { + ieee_error (info, *pp, _("unknown TY code")); + return FALSE; + } + ++*pp; + + ty_var_start = *pp; + + if (! ieee_read_number (info, pp, &varindx)) + return FALSE; + + if (varindx < 32) + { + ieee_error (info, ty_var_start, _("illegal variable index")); + return FALSE; + } + varindx -= 32; + + if (varindx >= info->vars.alloc || info->vars.vars[varindx].name == NULL) + { + ieee_error (info, ty_var_start, _("undefined variable in TY")); + return FALSE; + } + + ty_code_start = *pp; + + if (! ieee_read_number (info, pp, &tc)) + return FALSE; + + dhandle = info->dhandle; + + tag = FALSE; + typdef = FALSE; + arg_slots = NULL; + type_bitsize = 0; + switch (tc) + { + default: + ieee_error (info, ty_code_start, _("unknown TY code")); + return FALSE; + + case '!': + /* Unknown type, with size. We treat it as int. FIXME. */ + { + bfd_vma size; + + if (! ieee_read_number (info, pp, &size)) + return FALSE; + type = debug_make_int_type (dhandle, size, FALSE); + } + break; + + case 'A': /* Array. */ + case 'a': /* FORTRAN array in column/row order. FIXME: Not + distinguished from normal array. */ + { + debug_type ele_type; + bfd_vma lower, upper; + + if (! ieee_read_type_index (info, pp, &ele_type) + || ! ieee_read_number (info, pp, &lower) + || ! ieee_read_number (info, pp, &upper)) + return FALSE; + type = debug_make_array_type (dhandle, ele_type, + ieee_builtin_type (info, ty_code_start, + ((unsigned int) + builtin_int)), + (bfd_signed_vma) lower, + (bfd_signed_vma) upper, + FALSE); + } + break; + + case 'E': + /* Simple enumeration. */ + { + bfd_vma size; + unsigned int alloc; + const char **names; + unsigned int c; + bfd_signed_vma *vals; + unsigned int i; + + if (! ieee_read_number (info, pp, &size)) + return FALSE; + /* FIXME: we ignore the enumeration size. */ + + alloc = 10; + names = (const char **) xmalloc (alloc * sizeof *names); + memset (names, 0, alloc * sizeof *names); + c = 0; + while (1) + { + const char *name; + unsigned long namlen; + bfd_boolean present; + + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + if (! present) + break; + + if (c + 1 >= alloc) + { + alloc += 10; + names = ((const char **) + xrealloc (names, alloc * sizeof *names)); + } + + names[c] = savestring (name, namlen); + if (names[c] == NULL) + return FALSE; + ++c; + } + + names[c] = NULL; + + vals = (bfd_signed_vma *) xmalloc (c * sizeof *vals); + for (i = 0; i < c; i++) + vals[i] = i; + + type = debug_make_enum_type (dhandle, names, vals); + tag = TRUE; + } + break; + + case 'G': + /* Struct with bit fields. */ + { + bfd_vma size; + unsigned int alloc; + debug_field *fields; + unsigned int c; + + if (! ieee_read_number (info, pp, &size)) + return FALSE; + + alloc = 10; + fields = (debug_field *) xmalloc (alloc * sizeof *fields); + c = 0; + while (1) + { + const char *name; + unsigned long namlen; + bfd_boolean present; + debug_type ftype; + bfd_vma bitpos, bitsize; + + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + if (! present) + break; + if (! ieee_read_type_index (info, pp, &ftype) + || ! ieee_read_number (info, pp, &bitpos) + || ! ieee_read_number (info, pp, &bitsize)) + return FALSE; + + if (c + 1 >= alloc) + { + alloc += 10; + fields = ((debug_field *) + xrealloc (fields, alloc * sizeof *fields)); + } + + fields[c] = debug_make_field (dhandle, savestring (name, namlen), + ftype, bitpos, bitsize, + DEBUG_VISIBILITY_PUBLIC); + if (fields[c] == NULL) + return FALSE; + ++c; + } + + fields[c] = NULL; + + type = debug_make_struct_type (dhandle, TRUE, size, fields); + tag = TRUE; + } + break; + + case 'N': + /* Enumeration. */ + { + unsigned int alloc; + const char **names; + bfd_signed_vma *vals; + unsigned int c; + + alloc = 10; + names = (const char **) xmalloc (alloc * sizeof *names); + vals = (bfd_signed_vma *) xmalloc (alloc * sizeof *names); + c = 0; + while (1) + { + const char *name; + unsigned long namlen; + bfd_boolean present; + bfd_vma val; + + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + if (! present) + break; + if (! ieee_read_number (info, pp, &val)) + return FALSE; + + /* If the length of the name is zero, then the value is + actually the size of the enum. We ignore this + information. FIXME. */ + if (namlen == 0) + continue; + + if (c + 1 >= alloc) + { + alloc += 10; + names = ((const char **) + xrealloc (names, alloc * sizeof *names)); + vals = ((bfd_signed_vma *) + xrealloc (vals, alloc * sizeof *vals)); + } + + names[c] = savestring (name, namlen); + if (names[c] == NULL) + return FALSE; + vals[c] = (bfd_signed_vma) val; + ++c; + } + + names[c] = NULL; + + type = debug_make_enum_type (dhandle, names, vals); + tag = TRUE; + } + break; + + case 'O': /* Small pointer. We don't distinguish small and large + pointers. FIXME. */ + case 'P': /* Large pointer. */ + { + debug_type t; + + if (! ieee_read_type_index (info, pp, &t)) + return FALSE; + type = debug_make_pointer_type (dhandle, t); + } + break; + + case 'R': + /* Range. */ + { + bfd_vma low, high, signedp, size; + + if (! ieee_read_number (info, pp, &low) + || ! ieee_read_number (info, pp, &high) + || ! ieee_read_number (info, pp, &signedp) + || ! ieee_read_number (info, pp, &size)) + return FALSE; + + type = debug_make_range_type (dhandle, + debug_make_int_type (dhandle, size, + ! signedp), + (bfd_signed_vma) low, + (bfd_signed_vma) high); + } + break; + + case 'S': /* Struct. */ + case 'U': /* Union. */ + { + bfd_vma size; + unsigned int alloc; + debug_field *fields; + unsigned int c; + + if (! ieee_read_number (info, pp, &size)) + return FALSE; + + alloc = 10; + fields = (debug_field *) xmalloc (alloc * sizeof *fields); + c = 0; + while (1) + { + const char *name; + unsigned long namlen; + bfd_boolean present; + bfd_vma tindx; + bfd_vma offset; + debug_type ftype; + bfd_vma bitsize; + + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + if (! present) + break; + if (! ieee_read_number (info, pp, &tindx) + || ! ieee_read_number (info, pp, &offset)) + return FALSE; + + if (tindx < 256) + { + ftype = ieee_builtin_type (info, ty_code_start, tindx); + bitsize = 0; + offset *= 8; + } + else + { + struct ieee_type *t; + + tindx -= 256; + if (! ieee_alloc_type (info, tindx, TRUE)) + return FALSE; + t = info->types.types + tindx; + ftype = t->type; + bitsize = t->bitsize; + if (bitsize == 0) + offset *= 8; + } + + if (c + 1 >= alloc) + { + alloc += 10; + fields = ((debug_field *) + xrealloc (fields, alloc * sizeof *fields)); + } + + fields[c] = debug_make_field (dhandle, savestring (name, namlen), + ftype, offset, bitsize, + DEBUG_VISIBILITY_PUBLIC); + if (fields[c] == NULL) + return FALSE; + ++c; + } + + fields[c] = NULL; + + type = debug_make_struct_type (dhandle, tc == 'S', size, fields); + tag = TRUE; + } + break; + + case 'T': + /* Typedef. */ + if (! ieee_read_type_index (info, pp, &type)) + return FALSE; + typdef = TRUE; + break; + + case 'X': + /* Procedure. FIXME: This is an extern declaration, which we + have no way of representing. */ + { + bfd_vma attr; + debug_type rtype; + bfd_vma nargs; + bfd_boolean present; + struct ieee_var *pv; + + /* FIXME: We ignore the attribute and the argument names. */ + + if (! ieee_read_number (info, pp, &attr) + || ! ieee_read_type_index (info, pp, &rtype) + || ! ieee_read_number (info, pp, &nargs)) + return FALSE; + do + { + const char *name; + unsigned long namlen; + + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + } + while (present); + + pv = info->vars.vars + varindx; + pv->kind = IEEE_EXTERNAL; + if (pv->namlen > 0 + && debug_get_type_kind (dhandle, rtype) == DEBUG_KIND_POINTER) + { + /* Set up the return type as an indirect type pointing to + the variable slot, so that we can change it to a + reference later if appropriate. */ + pv->pslot = (debug_type *) xmalloc (sizeof *pv->pslot); + *pv->pslot = rtype; + rtype = debug_make_indirect_type (dhandle, pv->pslot, + (const char *) NULL); + } + + type = debug_make_function_type (dhandle, rtype, (debug_type *) NULL, + FALSE); + } + break; + + case 'V': + case 'v': + /* Void. This is not documented, but the MRI compiler emits it. */ + type = debug_make_void_type (dhandle); + break; + + case 'Z': + /* Array with 0 lower bound. */ + { + debug_type etype; + bfd_vma high; + + if (! ieee_read_type_index (info, pp, &etype) + || ! ieee_read_number (info, pp, &high)) + return FALSE; + + type = debug_make_array_type (dhandle, etype, + ieee_builtin_type (info, ty_code_start, + ((unsigned int) + builtin_int)), + 0, (bfd_signed_vma) high, FALSE); + } + break; + + case 'c': /* Complex. */ + case 'd': /* Double complex. */ + { + const char *name; + unsigned long namlen; + + /* FIXME: I don't know what the name means. */ + + if (! ieee_read_id (info, pp, &name, &namlen)) + return FALSE; + + type = debug_make_complex_type (dhandle, tc == 'c' ? 4 : 8); + } + break; + + case 'f': + /* Pascal file name. FIXME. */ + ieee_error (info, ty_code_start, _("Pascal file name not supported")); + return FALSE; + + case 'g': + /* Bitfield type. */ + { + bfd_vma signedp, bitsize, dummy; + const bfd_byte *hold; + bfd_boolean present; + + if (! ieee_read_number (info, pp, &signedp) + || ! ieee_read_number (info, pp, &bitsize)) + return FALSE; + + /* I think the documentation says that there is a type index, + but some actual files do not have one. */ + hold = *pp; + if (! ieee_read_optional_number (info, pp, &dummy, &present)) + return FALSE; + if (! present) + { + /* FIXME: This is just a guess. */ + type = debug_make_int_type (dhandle, 4, + signedp ? FALSE : TRUE); + } + else + { + *pp = hold; + if (! ieee_read_type_index (info, pp, &type)) + return FALSE; + } + type_bitsize = bitsize; + } + break; + + case 'n': + /* Qualifier. */ + { + bfd_vma kind; + debug_type t; + + if (! ieee_read_number (info, pp, &kind) + || ! ieee_read_type_index (info, pp, &t)) + return FALSE; + + switch (kind) + { + default: + ieee_error (info, ty_start, _("unsupported qualifier")); + return FALSE; + + case 1: + type = debug_make_const_type (dhandle, t); + break; + + case 2: + type = debug_make_volatile_type (dhandle, t); + break; + } + } + break; + + case 's': + /* Set. */ + { + bfd_vma size; + debug_type etype; + + if (! ieee_read_number (info, pp, &size) + || ! ieee_read_type_index (info, pp, &etype)) + return FALSE; + + /* FIXME: We ignore the size. */ + + type = debug_make_set_type (dhandle, etype, FALSE); + } + break; + + case 'x': + /* Procedure with compiler dependencies. */ + { + struct ieee_var *pv; + bfd_vma attr, frame_type, push_mask, nargs, level, father; + debug_type rtype; + debug_type *arg_types; + bfd_boolean varargs; + bfd_boolean present; + + /* FIXME: We ignore some of this information. */ + + pv = info->vars.vars + varindx; + + if (! ieee_read_number (info, pp, &attr) + || ! ieee_read_number (info, pp, &frame_type) + || ! ieee_read_number (info, pp, &push_mask) + || ! ieee_read_type_index (info, pp, &rtype) + || ! ieee_read_number (info, pp, &nargs)) + return FALSE; + if (nargs == (bfd_vma) -1) + { + arg_types = NULL; + varargs = FALSE; + } + else + { + unsigned int i; + + arg_types = ((debug_type *) + xmalloc ((nargs + 1) * sizeof *arg_types)); + for (i = 0; i < nargs; i++) + if (! ieee_read_type_index (info, pp, arg_types + i)) + return FALSE; + + /* If the last type is pointer to void, this is really a + varargs function. */ + varargs = FALSE; + if (nargs > 0) + { + debug_type last; + + last = arg_types[nargs - 1]; + if (debug_get_type_kind (dhandle, last) == DEBUG_KIND_POINTER + && (debug_get_type_kind (dhandle, + debug_get_target_type (dhandle, + last)) + == DEBUG_KIND_VOID)) + { + --nargs; + varargs = TRUE; + } + } + + /* If there are any pointer arguments, turn them into + indirect types in case we later need to convert them to + reference types. */ + for (i = 0; i < nargs; i++) + { + if (debug_get_type_kind (dhandle, arg_types[i]) + == DEBUG_KIND_POINTER) + { + if (arg_slots == NULL) + { + arg_slots = ((debug_type *) + xmalloc (nargs * sizeof *arg_slots)); + memset (arg_slots, 0, nargs * sizeof *arg_slots); + } + arg_slots[i] = arg_types[i]; + arg_types[i] = + debug_make_indirect_type (dhandle, + arg_slots + i, + (const char *) NULL); + } + } + + arg_types[nargs] = DEBUG_TYPE_NULL; + } + if (! ieee_read_number (info, pp, &level) + || ! ieee_read_optional_number (info, pp, &father, &present)) + return FALSE; + + /* We can't distinguish between a global function and a static + function. */ + pv->kind = IEEE_FUNCTION; + + if (pv->namlen > 0 + && debug_get_type_kind (dhandle, rtype) == DEBUG_KIND_POINTER) + { + /* Set up the return type as an indirect type pointing to + the variable slot, so that we can change it to a + reference later if appropriate. */ + pv->pslot = (debug_type *) xmalloc (sizeof *pv->pslot); + *pv->pslot = rtype; + rtype = debug_make_indirect_type (dhandle, pv->pslot, + (const char *) NULL); + } + + type = debug_make_function_type (dhandle, rtype, arg_types, varargs); + } + break; + } + + /* Record the type in the table. */ + + if (type == DEBUG_TYPE_NULL) + return FALSE; + + info->vars.vars[varindx].type = type; + + if ((tag || typdef) + && info->vars.vars[varindx].namlen > 0) + { + const char *name; + + name = savestring (info->vars.vars[varindx].name, + info->vars.vars[varindx].namlen); + if (typdef) + type = debug_name_type (dhandle, name, type); + else if (tc == 'E' || tc == 'N') + type = debug_tag_type (dhandle, name, type); + else + { + struct ieee_tag *it; + + /* We must allocate all struct tags as indirect types, so + that if we later see a definition of the tag as a C++ + record we can update the indirect slot and automatically + change all the existing references. */ + it = (struct ieee_tag *) xmalloc (sizeof *it); + memset (it, 0, sizeof *it); + it->next = info->tags; + info->tags = it; + it->name = name; + it->slot = type; + + type = debug_make_indirect_type (dhandle, &it->slot, name); + type = debug_tag_type (dhandle, name, type); + + it->type = type; + } + if (type == NULL) + return FALSE; + } + + info->types.types[typeindx].type = type; + info->types.types[typeindx].arg_slots = arg_slots; + info->types.types[typeindx].bitsize = type_bitsize; + + /* We may have already allocated type as an indirect type pointing + to slot. It does no harm to replace the indirect type with the + real type. Filling in slot as well handles the indirect types + which are already hanging around. */ + if (info->types.types[typeindx].pslot != NULL) + *info->types.types[typeindx].pslot = type; + + return TRUE; +} + +/* Parse an ATN record. */ + +static bfd_boolean +parse_ieee_atn (struct ieee_info *info, const bfd_byte **pp) +{ + const bfd_byte *atn_start, *atn_code_start; + bfd_vma varindx; + struct ieee_var *pvar; + debug_type type; + bfd_vma atn_code; + void *dhandle; + bfd_vma v, v2, v3, v4, v5; + const char *name; + unsigned long namlen; + char *namcopy; + bfd_boolean present; + int blocktype; + + atn_start = *pp; + + if (! ieee_read_number (info, pp, &varindx) + || ! ieee_read_type_index (info, pp, &type)) + return FALSE; + + atn_code_start = *pp; + + if (! ieee_read_number (info, pp, &atn_code)) + return FALSE; + + if (varindx == 0) + { + pvar = NULL; + name = ""; + namlen = 0; + } + else if (varindx < 32) + { + /* The MRI compiler reportedly sometimes emits variable lifetime + information for a register. We just ignore it. */ + if (atn_code == 9) + return ieee_read_number (info, pp, &v); + + ieee_error (info, atn_start, _("illegal variable index")); + return FALSE; + } + else + { + varindx -= 32; + if (varindx >= info->vars.alloc + || info->vars.vars[varindx].name == NULL) + { + /* The MRI compiler or linker sometimes omits the NN record + for a pmisc record. */ + if (atn_code == 62) + { + if (varindx >= info->vars.alloc) + { + unsigned int alloc; + + alloc = info->vars.alloc; + if (alloc == 0) + alloc = 4; + while (varindx >= alloc) + alloc *= 2; + info->vars.vars = ((struct ieee_var *) + xrealloc (info->vars.vars, + (alloc + * sizeof *info->vars.vars))); + memset (info->vars.vars + info->vars.alloc, 0, + ((alloc - info->vars.alloc) + * sizeof *info->vars.vars)); + info->vars.alloc = alloc; + } + + pvar = info->vars.vars + varindx; + pvar->name = ""; + pvar->namlen = 0; + } + else + { + ieee_error (info, atn_start, _("undefined variable in ATN")); + return FALSE; + } + } + + pvar = info->vars.vars + varindx; + + pvar->type = type; + + name = pvar->name; + namlen = pvar->namlen; + } + + dhandle = info->dhandle; + + /* If we are going to call debug_record_variable with a pointer + type, change the type to an indirect type so that we can later + change it to a reference type if we encounter a C++ pmisc 'R' + record. */ + if (pvar != NULL + && type != DEBUG_TYPE_NULL + && debug_get_type_kind (dhandle, type) == DEBUG_KIND_POINTER) + { + switch (atn_code) + { + case 1: + case 2: + case 3: + case 5: + case 8: + case 10: + pvar->pslot = (debug_type *) xmalloc (sizeof *pvar->pslot); + *pvar->pslot = type; + type = debug_make_indirect_type (dhandle, pvar->pslot, + (const char *) NULL); + pvar->type = type; + break; + } + } + + switch (atn_code) + { + default: + ieee_error (info, atn_code_start, _("unknown ATN type")); + return FALSE; + + case 1: + /* Automatic variable. */ + if (! ieee_read_number (info, pp, &v)) + return FALSE; + namcopy = savestring (name, namlen); + if (type == NULL) + type = debug_make_void_type (dhandle); + if (pvar != NULL) + pvar->kind = IEEE_LOCAL; + return debug_record_variable (dhandle, namcopy, type, DEBUG_LOCAL, v); + + case 2: + /* Register variable. */ + if (! ieee_read_number (info, pp, &v)) + return FALSE; + namcopy = savestring (name, namlen); + if (type == NULL) + type = debug_make_void_type (dhandle); + if (pvar != NULL) + pvar->kind = IEEE_LOCAL; + return debug_record_variable (dhandle, namcopy, type, DEBUG_REGISTER, + ieee_regno_to_genreg (info->abfd, v)); + + case 3: + /* Static variable. */ + if (! ieee_require_asn (info, pp, &v)) + return FALSE; + namcopy = savestring (name, namlen); + if (type == NULL) + type = debug_make_void_type (dhandle); + if (info->blockstack.bsp <= info->blockstack.stack) + blocktype = 0; + else + blocktype = info->blockstack.bsp[-1].kind; + if (pvar != NULL) + { + if (blocktype == 4 || blocktype == 6) + pvar->kind = IEEE_LOCAL; + else + pvar->kind = IEEE_STATIC; + } + return debug_record_variable (dhandle, namcopy, type, + (blocktype == 4 || blocktype == 6 + ? DEBUG_LOCAL_STATIC + : DEBUG_STATIC), + v); + + case 4: + /* External function. We don't currently record these. FIXME. */ + if (pvar != NULL) + pvar->kind = IEEE_EXTERNAL; + return TRUE; + + case 5: + /* External variable. We don't currently record these. FIXME. */ + if (pvar != NULL) + pvar->kind = IEEE_EXTERNAL; + return TRUE; + + case 7: + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_number (info, pp, &v2) + || ! ieee_read_optional_number (info, pp, &v3, &present)) + return FALSE; + if (present) + { + if (! ieee_read_optional_number (info, pp, &v4, &present)) + return FALSE; + } + + /* We just ignore the two optional fields in v3 and v4, since + they are not defined. */ + + if (! ieee_require_asn (info, pp, &v3)) + return FALSE; + + /* We have no way to record the column number. FIXME. */ + + return debug_record_line (dhandle, v, v3); + + case 8: + /* Global variable. */ + if (! ieee_require_asn (info, pp, &v)) + return FALSE; + namcopy = savestring (name, namlen); + if (type == NULL) + type = debug_make_void_type (dhandle); + if (pvar != NULL) + pvar->kind = IEEE_GLOBAL; + return debug_record_variable (dhandle, namcopy, type, DEBUG_GLOBAL, v); + + case 9: + /* Variable lifetime information. */ + if (! ieee_read_number (info, pp, &v)) + return FALSE; + + /* We have no way to record this information. FIXME. */ + return TRUE; + + case 10: + /* Locked register. The spec says that there are two required + fields, but at least on occasion the MRI compiler only emits + one. */ + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_optional_number (info, pp, &v2, &present)) + return FALSE; + + /* I think this means a variable that is both in a register and + a frame slot. We ignore the frame slot. FIXME. */ + + namcopy = savestring (name, namlen); + if (type == NULL) + type = debug_make_void_type (dhandle); + if (pvar != NULL) + pvar->kind = IEEE_LOCAL; + return debug_record_variable (dhandle, namcopy, type, DEBUG_REGISTER, v); + + case 11: + /* Reserved for FORTRAN common. */ + ieee_error (info, atn_code_start, _("unsupported ATN11")); + + /* Return TRUE to keep going. */ + return TRUE; + + case 12: + /* Based variable. */ + v3 = 0; + v4 = 0x80; + v5 = 0; + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_number (info, pp, &v2) + || ! ieee_read_optional_number (info, pp, &v3, &present)) + return FALSE; + if (present) + { + if (! ieee_read_optional_number (info, pp, &v4, &present)) + return FALSE; + if (present) + { + if (! ieee_read_optional_number (info, pp, &v5, &present)) + return FALSE; + } + } + + /* We have no way to record this information. FIXME. */ + + ieee_error (info, atn_code_start, _("unsupported ATN12")); + + /* Return TRUE to keep going. */ + return TRUE; + + case 16: + /* Constant. The description of this that I have is ambiguous, + so I'm not going to try to implement it. */ + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_optional_number (info, pp, &v2, &present)) + return FALSE; + if (present) + { + if (! ieee_read_optional_number (info, pp, &v2, &present)) + return FALSE; + if (present) + { + if (! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + } + } + + if ((ieee_record_enum_type) **pp == ieee_e2_first_byte_enum) + { + if (! ieee_require_asn (info, pp, &v3)) + return FALSE; + } + + return TRUE; + + case 19: + /* Static variable from assembler. */ + v2 = 0; + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_optional_number (info, pp, &v2, &present) + || ! ieee_require_asn (info, pp, &v3)) + return FALSE; + namcopy = savestring (name, namlen); + /* We don't really handle this correctly. FIXME. */ + return debug_record_variable (dhandle, namcopy, + debug_make_void_type (dhandle), + v2 != 0 ? DEBUG_GLOBAL : DEBUG_STATIC, + v3); + + case 62: + /* Procedure miscellaneous information. */ + case 63: + /* Variable miscellaneous information. */ + case 64: + /* Module miscellaneous information. */ + if (! ieee_read_number (info, pp, &v) + || ! ieee_read_number (info, pp, &v2) + || ! ieee_read_optional_id (info, pp, &name, &namlen, &present)) + return FALSE; + + if (atn_code == 62 && v == 80) + { + if (present) + { + ieee_error (info, atn_code_start, + _("unexpected string in C++ misc")); + return FALSE; + } + return ieee_read_cxx_misc (info, pp, v2); + } + + /* We just ignore all of this stuff. FIXME. */ + + for (; v2 > 0; --v2) + { + switch ((ieee_record_enum_type) **pp) + { + default: + ieee_error (info, *pp, _("bad misc record")); + return FALSE; + + case ieee_at_record_enum: + if (! ieee_require_atn65 (info, pp, &name, &namlen)) + return FALSE; + break; + + case ieee_e2_first_byte_enum: + if (! ieee_require_asn (info, pp, &v3)) + return FALSE; + break; + } + } + + return TRUE; + } + + /*NOTREACHED*/ +} + +/* Handle C++ debugging miscellaneous records. This is called for + procedure miscellaneous records of type 80. */ + +static bfd_boolean +ieee_read_cxx_misc (struct ieee_info *info, const bfd_byte **pp, + unsigned long count) +{ + const bfd_byte *start; + bfd_vma category; + + start = *pp; + + /* Get the category of C++ misc record. */ + if (! ieee_require_asn (info, pp, &category)) + return FALSE; + --count; + + switch (category) + { + default: + ieee_error (info, start, _("unrecognized C++ misc record")); + return FALSE; + + case 'T': + if (! ieee_read_cxx_class (info, pp, count)) + return FALSE; + break; + + case 'M': + { + bfd_vma flags; + const char *name; + unsigned long namlen; + + /* The IEEE spec indicates that the 'M' record only has a + flags field. The MRI compiler also emits the name of the + function. */ + + if (! ieee_require_asn (info, pp, &flags)) + return FALSE; + if (*pp < info->pend + && (ieee_record_enum_type) **pp == ieee_at_record_enum) + { + if (! ieee_require_atn65 (info, pp, &name, &namlen)) + return FALSE; + } + + /* This is emitted for method functions, but I don't think we + care very much. It might help if it told us useful + information like the class with which this function is + associated, but it doesn't, so it isn't helpful. */ + } + break; + + case 'B': + if (! ieee_read_cxx_defaults (info, pp, count)) + return FALSE; + break; + + case 'z': + { + const char *name, *mangled, *cxx_class; + unsigned long namlen, mangledlen, classlen; + bfd_vma control; + + /* Pointer to member. */ + + if (! ieee_require_atn65 (info, pp, &name, &namlen) + || ! ieee_require_atn65 (info, pp, &mangled, &mangledlen) + || ! ieee_require_atn65 (info, pp, &cxx_class, &classlen) + || ! ieee_require_asn (info, pp, &control)) + return FALSE; + + /* FIXME: We should now track down name and change its type. */ + } + break; + + case 'R': + if (! ieee_read_reference (info, pp)) + return FALSE; + break; + } + + return TRUE; +} + +/* Read a C++ class definition. This is a pmisc type 80 record of + category 'T'. */ + +static bfd_boolean +ieee_read_cxx_class (struct ieee_info *info, const bfd_byte **pp, + unsigned long count) +{ + const bfd_byte *start; + bfd_vma cxx_class; + const char *tag; + unsigned long taglen; + struct ieee_tag *it; + void *dhandle; + debug_field *fields; + unsigned int field_count, field_alloc; + debug_baseclass *baseclasses; + unsigned int baseclasses_count, baseclasses_alloc; + const debug_field *structfields; + struct ieee_method + { + const char *name; + unsigned long namlen; + debug_method_variant *variants; + unsigned count; + unsigned int alloc; + } *methods; + unsigned int methods_count, methods_alloc; + debug_type vptrbase; + bfd_boolean ownvptr; + debug_method *dmethods; + + start = *pp; + + if (! ieee_require_asn (info, pp, &cxx_class)) + return FALSE; + --count; + + if (! ieee_require_atn65 (info, pp, &tag, &taglen)) + return FALSE; + --count; + + /* Find the C struct with this name. */ + for (it = info->tags; it != NULL; it = it->next) + if (it->name[0] == tag[0] + && strncmp (it->name, tag, taglen) == 0 + && strlen (it->name) == taglen) + break; + if (it == NULL) + { + ieee_error (info, start, _("undefined C++ object")); + return FALSE; + } + + dhandle = info->dhandle; + + fields = NULL; + field_count = 0; + field_alloc = 0; + baseclasses = NULL; + baseclasses_count = 0; + baseclasses_alloc = 0; + methods = NULL; + methods_count = 0; + methods_alloc = 0; + vptrbase = DEBUG_TYPE_NULL; + ownvptr = FALSE; + + structfields = debug_get_fields (dhandle, it->type); + + while (count > 0) + { + bfd_vma id; + const bfd_byte *spec_start; + + spec_start = *pp; + + if (! ieee_require_asn (info, pp, &id)) + return FALSE; + --count; + + switch (id) + { + default: + ieee_error (info, spec_start, _("unrecognized C++ object spec")); + return FALSE; + + case 'b': + { + bfd_vma flags, cinline; + const char *base, *fieldname; + unsigned long baselen, fieldlen; + char *basecopy; + debug_type basetype; + bfd_vma bitpos; + bfd_boolean virtualp; + enum debug_visibility visibility; + debug_baseclass baseclass; + + /* This represents a base or friend class. */ + + if (! ieee_require_asn (info, pp, &flags) + || ! ieee_require_atn65 (info, pp, &base, &baselen) + || ! ieee_require_asn (info, pp, &cinline) + || ! ieee_require_atn65 (info, pp, &fieldname, &fieldlen)) + return FALSE; + count -= 4; + + /* We have no way of recording friend information, so we + just ignore it. */ + if ((flags & BASEFLAGS_FRIEND) != 0) + break; + + /* I assume that either all of the members of the + baseclass are included in the object, starting at the + beginning of the object, or that none of them are + included. */ + + if ((fieldlen == 0) == (cinline == 0)) + { + ieee_error (info, start, _("unsupported C++ object type")); + return FALSE; + } + + basecopy = savestring (base, baselen); + basetype = debug_find_tagged_type (dhandle, basecopy, + DEBUG_KIND_ILLEGAL); + free (basecopy); + if (basetype == DEBUG_TYPE_NULL) + { + ieee_error (info, start, _("C++ base class not defined")); + return FALSE; + } + + if (fieldlen == 0) + bitpos = 0; + else + { + const debug_field *pf; + + if (structfields == NULL) + { + ieee_error (info, start, _("C++ object has no fields")); + return FALSE; + } + + for (pf = structfields; *pf != DEBUG_FIELD_NULL; pf++) + { + const char *fname; + + fname = debug_get_field_name (dhandle, *pf); + if (fname == NULL) + return FALSE; + if (fname[0] == fieldname[0] + && strncmp (fname, fieldname, fieldlen) == 0 + && strlen (fname) == fieldlen) + break; + } + if (*pf == DEBUG_FIELD_NULL) + { + ieee_error (info, start, + _("C++ base class not found in container")); + return FALSE; + } + + bitpos = debug_get_field_bitpos (dhandle, *pf); + } + + if ((flags & BASEFLAGS_VIRTUAL) != 0) + virtualp = TRUE; + else + virtualp = FALSE; + if ((flags & BASEFLAGS_PRIVATE) != 0) + visibility = DEBUG_VISIBILITY_PRIVATE; + else + visibility = DEBUG_VISIBILITY_PUBLIC; + + baseclass = debug_make_baseclass (dhandle, basetype, bitpos, + virtualp, visibility); + if (baseclass == DEBUG_BASECLASS_NULL) + return FALSE; + + if (baseclasses_count + 1 >= baseclasses_alloc) + { + baseclasses_alloc += 10; + baseclasses = ((debug_baseclass *) + xrealloc (baseclasses, + (baseclasses_alloc + * sizeof *baseclasses))); + } + + baseclasses[baseclasses_count] = baseclass; + ++baseclasses_count; + baseclasses[baseclasses_count] = DEBUG_BASECLASS_NULL; + } + break; + + case 'd': + { + bfd_vma flags; + const char *fieldname, *mangledname; + unsigned long fieldlen, mangledlen; + char *fieldcopy; + bfd_boolean staticp; + debug_type ftype; + const debug_field *pf = NULL; + enum debug_visibility visibility; + debug_field field; + + /* This represents a data member. */ + + if (! ieee_require_asn (info, pp, &flags) + || ! ieee_require_atn65 (info, pp, &fieldname, &fieldlen) + || ! ieee_require_atn65 (info, pp, &mangledname, &mangledlen)) + return FALSE; + count -= 3; + + fieldcopy = savestring (fieldname, fieldlen); + + staticp = (flags & CXXFLAGS_STATIC) != 0 ? TRUE : FALSE; + + if (staticp) + { + struct ieee_var *pv, *pvend; + + /* See if we can find a definition for this variable. */ + pv = info->vars.vars; + pvend = pv + info->vars.alloc; + for (; pv < pvend; pv++) + if (pv->namlen == mangledlen + && strncmp (pv->name, mangledname, mangledlen) == 0) + break; + if (pv < pvend) + ftype = pv->type; + else + { + /* This can happen if the variable is never used. */ + ftype = ieee_builtin_type (info, start, + (unsigned int) builtin_void); + } + } + else + { + unsigned int findx; + + if (structfields == NULL) + { + ieee_error (info, start, _("C++ object has no fields")); + return FALSE; + } + + for (pf = structfields, findx = 0; + *pf != DEBUG_FIELD_NULL; + pf++, findx++) + { + const char *fname; + + fname = debug_get_field_name (dhandle, *pf); + if (fname == NULL) + return FALSE; + if (fname[0] == mangledname[0] + && strncmp (fname, mangledname, mangledlen) == 0 + && strlen (fname) == mangledlen) + break; + } + if (*pf == DEBUG_FIELD_NULL) + { + ieee_error (info, start, + _("C++ data member not found in container")); + return FALSE; + } + + ftype = debug_get_field_type (dhandle, *pf); + + if (debug_get_type_kind (dhandle, ftype) == DEBUG_KIND_POINTER) + { + /* We might need to convert this field into a + reference type later on, so make it an indirect + type. */ + if (it->fslots == NULL) + { + unsigned int fcnt; + const debug_field *pfcnt; + + fcnt = 0; + for (pfcnt = structfields; + *pfcnt != DEBUG_FIELD_NULL; + pfcnt++) + ++fcnt; + it->fslots = ((debug_type *) + xmalloc (fcnt * sizeof *it->fslots)); + memset (it->fslots, 0, + fcnt * sizeof *it->fslots); + } + + if (ftype == DEBUG_TYPE_NULL) + return FALSE; + it->fslots[findx] = ftype; + ftype = debug_make_indirect_type (dhandle, + it->fslots + findx, + (const char *) NULL); + } + } + if (ftype == DEBUG_TYPE_NULL) + return FALSE; + + switch (flags & CXXFLAGS_VISIBILITY) + { + default: + ieee_error (info, start, _("unknown C++ visibility")); + return FALSE; + + case CXXFLAGS_VISIBILITY_PUBLIC: + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + + case CXXFLAGS_VISIBILITY_PRIVATE: + visibility = DEBUG_VISIBILITY_PRIVATE; + break; + + case CXXFLAGS_VISIBILITY_PROTECTED: + visibility = DEBUG_VISIBILITY_PROTECTED; + break; + } + + if (staticp) + { + char *mangledcopy; + + mangledcopy = savestring (mangledname, mangledlen); + + field = debug_make_static_member (dhandle, fieldcopy, + ftype, mangledcopy, + visibility); + } + else + { + bfd_vma bitpos, bitsize; + + bitpos = debug_get_field_bitpos (dhandle, *pf); + bitsize = debug_get_field_bitsize (dhandle, *pf); + if (bitpos == (bfd_vma) -1 || bitsize == (bfd_vma) -1) + { + ieee_error (info, start, _("bad C++ field bit pos or size")); + return FALSE; + } + field = debug_make_field (dhandle, fieldcopy, ftype, bitpos, + bitsize, visibility); + } + + if (field == DEBUG_FIELD_NULL) + return FALSE; + + if (field_count + 1 >= field_alloc) + { + field_alloc += 10; + fields = ((debug_field *) + xrealloc (fields, field_alloc * sizeof *fields)); + } + + fields[field_count] = field; + ++field_count; + fields[field_count] = DEBUG_FIELD_NULL; + } + break; + + case 'm': + case 'v': + { + bfd_vma flags, voffset, control; + const char *name, *mangled; + unsigned long namlen, mangledlen; + struct ieee_var *pv, *pvend; + debug_type type; + enum debug_visibility visibility; + bfd_boolean constp, volatilep; + char *mangledcopy; + debug_method_variant mv; + struct ieee_method *meth; + unsigned int im; + + if (! ieee_require_asn (info, pp, &flags) + || ! ieee_require_atn65 (info, pp, &name, &namlen) + || ! ieee_require_atn65 (info, pp, &mangled, &mangledlen)) + return FALSE; + count -= 3; + if (id != 'v') + voffset = 0; + else + { + if (! ieee_require_asn (info, pp, &voffset)) + return FALSE; + --count; + } + if (! ieee_require_asn (info, pp, &control)) + return FALSE; + --count; + + /* We just ignore the control information. */ + + /* We have no way to represent friend information, so we + just ignore it. */ + if ((flags & CXXFLAGS_FRIEND) != 0) + break; + + /* We should already have seen a type for the function. */ + pv = info->vars.vars; + pvend = pv + info->vars.alloc; + for (; pv < pvend; pv++) + if (pv->namlen == mangledlen + && strncmp (pv->name, mangled, mangledlen) == 0) + break; + + if (pv >= pvend) + { + /* We won't have type information for this function if + it is not included in this file. We don't try to + handle this case. FIXME. */ + type = (debug_make_function_type + (dhandle, + ieee_builtin_type (info, start, + (unsigned int) builtin_void), + (debug_type *) NULL, + FALSE)); + } + else + { + debug_type return_type; + const debug_type *arg_types; + bfd_boolean varargs; + + if (debug_get_type_kind (dhandle, pv->type) + != DEBUG_KIND_FUNCTION) + { + ieee_error (info, start, + _("bad type for C++ method function")); + return FALSE; + } + + return_type = debug_get_return_type (dhandle, pv->type); + arg_types = debug_get_parameter_types (dhandle, pv->type, + &varargs); + if (return_type == DEBUG_TYPE_NULL || arg_types == NULL) + { + ieee_error (info, start, + _("no type information for C++ method function")); + return FALSE; + } + + type = debug_make_method_type (dhandle, return_type, it->type, + (debug_type *) arg_types, + varargs); + } + if (type == DEBUG_TYPE_NULL) + return FALSE; + + switch (flags & CXXFLAGS_VISIBILITY) + { + default: + ieee_error (info, start, _("unknown C++ visibility")); + return FALSE; + + case CXXFLAGS_VISIBILITY_PUBLIC: + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + + case CXXFLAGS_VISIBILITY_PRIVATE: + visibility = DEBUG_VISIBILITY_PRIVATE; + break; + + case CXXFLAGS_VISIBILITY_PROTECTED: + visibility = DEBUG_VISIBILITY_PROTECTED; + break; + } + + constp = (flags & CXXFLAGS_CONST) != 0 ? TRUE : FALSE; + volatilep = (flags & CXXFLAGS_VOLATILE) != 0 ? TRUE : FALSE; + + mangledcopy = savestring (mangled, mangledlen); + + if ((flags & CXXFLAGS_STATIC) != 0) + { + if (id == 'v') + { + ieee_error (info, start, _("C++ static virtual method")); + return FALSE; + } + mv = debug_make_static_method_variant (dhandle, mangledcopy, + type, visibility, + constp, volatilep); + } + else + { + debug_type vcontext; + + if (id != 'v') + vcontext = DEBUG_TYPE_NULL; + else + { + /* FIXME: How can we calculate this correctly? */ + vcontext = it->type; + } + mv = debug_make_method_variant (dhandle, mangledcopy, type, + visibility, constp, + volatilep, voffset, + vcontext); + } + if (mv == DEBUG_METHOD_VARIANT_NULL) + return FALSE; + + for (meth = methods, im = 0; im < methods_count; meth++, im++) + if (meth->namlen == namlen + && strncmp (meth->name, name, namlen) == 0) + break; + if (im >= methods_count) + { + if (methods_count >= methods_alloc) + { + methods_alloc += 10; + methods = ((struct ieee_method *) + xrealloc (methods, + methods_alloc * sizeof *methods)); + } + methods[methods_count].name = name; + methods[methods_count].namlen = namlen; + methods[methods_count].variants = NULL; + methods[methods_count].count = 0; + methods[methods_count].alloc = 0; + meth = methods + methods_count; + ++methods_count; + } + + if (meth->count + 1 >= meth->alloc) + { + meth->alloc += 10; + meth->variants = ((debug_method_variant *) + xrealloc (meth->variants, + (meth->alloc + * sizeof *meth->variants))); + } + + meth->variants[meth->count] = mv; + ++meth->count; + meth->variants[meth->count] = DEBUG_METHOD_VARIANT_NULL; + } + break; + + case 'o': + { + bfd_vma spec; + + /* We have no way to store this information, so we just + ignore it. */ + if (! ieee_require_asn (info, pp, &spec)) + return FALSE; + --count; + if ((spec & 4) != 0) + { + const char *filename; + unsigned long filenamlen; + bfd_vma lineno; + + if (! ieee_require_atn65 (info, pp, &filename, &filenamlen) + || ! ieee_require_asn (info, pp, &lineno)) + return FALSE; + count -= 2; + } + else if ((spec & 8) != 0) + { + const char *mangled; + unsigned long mangledlen; + + if (! ieee_require_atn65 (info, pp, &mangled, &mangledlen)) + return FALSE; + --count; + } + else + { + ieee_error (info, start, + _("unrecognized C++ object overhead spec")); + return FALSE; + } + } + break; + + case 'z': + { + const char *vname, *base; + unsigned long vnamelen, baselen; + bfd_vma vsize, control; + + /* A virtual table pointer. */ + + if (! ieee_require_atn65 (info, pp, &vname, &vnamelen) + || ! ieee_require_asn (info, pp, &vsize) + || ! ieee_require_atn65 (info, pp, &base, &baselen) + || ! ieee_require_asn (info, pp, &control)) + return FALSE; + count -= 4; + + /* We just ignore the control number. We don't care what + the virtual table name is. We have no way to store the + virtual table size, and I don't think we care anyhow. */ + + /* FIXME: We can't handle multiple virtual table pointers. */ + + if (baselen == 0) + ownvptr = TRUE; + else + { + char *basecopy; + + basecopy = savestring (base, baselen); + vptrbase = debug_find_tagged_type (dhandle, basecopy, + DEBUG_KIND_ILLEGAL); + free (basecopy); + if (vptrbase == DEBUG_TYPE_NULL) + { + ieee_error (info, start, _("undefined C++ vtable")); + return FALSE; + } + } + } + break; + } + } + + /* Now that we have seen all the method variants, we can call + debug_make_method for each one. */ + + if (methods_count == 0) + dmethods = NULL; + else + { + unsigned int i; + + dmethods = ((debug_method *) + xmalloc ((methods_count + 1) * sizeof *dmethods)); + for (i = 0; i < methods_count; i++) + { + char *namcopy; + + namcopy = savestring (methods[i].name, methods[i].namlen); + dmethods[i] = debug_make_method (dhandle, namcopy, + methods[i].variants); + if (dmethods[i] == DEBUG_METHOD_NULL) + return FALSE; + } + dmethods[i] = DEBUG_METHOD_NULL; + free (methods); + } + + /* The struct type was created as an indirect type pointing at + it->slot. We update it->slot to automatically update all + references to this struct. */ + it->slot = debug_make_object_type (dhandle, + cxx_class != 'u', + debug_get_type_size (dhandle, + it->slot), + fields, baseclasses, dmethods, + vptrbase, ownvptr); + if (it->slot == DEBUG_TYPE_NULL) + return FALSE; + + return TRUE; +} + +/* Read C++ default argument value and reference type information. */ + +static bfd_boolean +ieee_read_cxx_defaults (struct ieee_info *info, const bfd_byte **pp, + unsigned long count) +{ + const bfd_byte *start; + const char *fnname; + unsigned long fnlen; + bfd_vma defcount; + + start = *pp; + + /* Giving the function name before the argument count is an addendum + to the spec. The function name is demangled, though, so this + record must always refer to the current function. */ + + if (info->blockstack.bsp <= info->blockstack.stack + || info->blockstack.bsp[-1].fnindx == (unsigned int) -1) + { + ieee_error (info, start, _("C++ default values not in a function")); + return FALSE; + } + + if (! ieee_require_atn65 (info, pp, &fnname, &fnlen) + || ! ieee_require_asn (info, pp, &defcount)) + return FALSE; + count -= 2; + + while (defcount-- > 0) + { + bfd_vma type, val; + const char *strval; + unsigned long strvallen; + + if (! ieee_require_asn (info, pp, &type)) + return FALSE; + --count; + + switch (type) + { + case 0: + case 4: + break; + + case 1: + case 2: + if (! ieee_require_asn (info, pp, &val)) + return FALSE; + --count; + break; + + case 3: + case 7: + if (! ieee_require_atn65 (info, pp, &strval, &strvallen)) + return FALSE; + --count; + break; + + default: + ieee_error (info, start, _("unrecognized C++ default type")); + return FALSE; + } + + /* We have no way to record the default argument values, so we + just ignore them. FIXME. */ + } + + /* Any remaining arguments are indices of parameters that are really + reference type. */ + if (count > 0) + { + void *dhandle; + debug_type *arg_slots; + + dhandle = info->dhandle; + arg_slots = info->types.types[info->blockstack.bsp[-1].fnindx].arg_slots; + while (count-- > 0) + { + bfd_vma indx; + debug_type target; + + if (! ieee_require_asn (info, pp, &indx)) + return FALSE; + /* The index is 1 based. */ + --indx; + if (arg_slots == NULL + || arg_slots[indx] == DEBUG_TYPE_NULL + || (debug_get_type_kind (dhandle, arg_slots[indx]) + != DEBUG_KIND_POINTER)) + { + ieee_error (info, start, _("reference parameter is not a pointer")); + return FALSE; + } + + target = debug_get_target_type (dhandle, arg_slots[indx]); + arg_slots[indx] = debug_make_reference_type (dhandle, target); + if (arg_slots[indx] == DEBUG_TYPE_NULL) + return FALSE; + } + } + + return TRUE; +} + +/* Read a C++ reference definition. */ + +static bfd_boolean +ieee_read_reference (struct ieee_info *info, const bfd_byte **pp) +{ + const bfd_byte *start; + bfd_vma flags; + const char *cxx_class, *name; + unsigned long classlen, namlen; + debug_type *pslot; + debug_type target; + + start = *pp; + + if (! ieee_require_asn (info, pp, &flags)) + return FALSE; + + /* Giving the class name before the member name is in an addendum to + the spec. */ + if (flags == 3) + { + if (! ieee_require_atn65 (info, pp, &cxx_class, &classlen)) + return FALSE; + } + + if (! ieee_require_atn65 (info, pp, &name, &namlen)) + return FALSE; + + pslot = NULL; + if (flags != 3) + { + int pass; + + /* We search from the last variable indices to the first in + hopes of finding local variables correctly. We search the + local variables on the first pass, and the global variables + on the second. FIXME: This probably won't work in all cases. + On the other hand, I don't know what will. */ + for (pass = 0; pass < 2; pass++) + { + struct ieee_vars *vars; + int i; + struct ieee_var *pv = NULL; + + if (pass == 0) + vars = &info->vars; + else + { + vars = info->global_vars; + if (vars == NULL) + break; + } + + for (i = (int) vars->alloc - 1; i >= 0; i--) + { + bfd_boolean found; + + pv = vars->vars + i; + + if (pv->pslot == NULL + || pv->namlen != namlen + || strncmp (pv->name, name, namlen) != 0) + continue; + + found = FALSE; + switch (flags) + { + default: + ieee_error (info, start, + _("unrecognized C++ reference type")); + return FALSE; + + case 0: + /* Global variable or function. */ + if (pv->kind == IEEE_GLOBAL + || pv->kind == IEEE_EXTERNAL + || pv->kind == IEEE_FUNCTION) + found = TRUE; + break; + + case 1: + /* Global static variable or function. */ + if (pv->kind == IEEE_STATIC + || pv->kind == IEEE_FUNCTION) + found = TRUE; + break; + + case 2: + /* Local variable. */ + if (pv->kind == IEEE_LOCAL) + found = TRUE; + break; + } + + if (found) + break; + } + + if (i >= 0) + { + pslot = pv->pslot; + break; + } + } + } + else + { + struct ieee_tag *it; + + for (it = info->tags; it != NULL; it = it->next) + { + if (it->name[0] == cxx_class[0] + && strncmp (it->name, cxx_class, classlen) == 0 + && strlen (it->name) == classlen) + { + if (it->fslots != NULL) + { + const debug_field *pf; + unsigned int findx; + + pf = debug_get_fields (info->dhandle, it->type); + if (pf == NULL) + { + ieee_error (info, start, + "C++ reference in class with no fields"); + return FALSE; + } + + for (findx = 0; *pf != DEBUG_FIELD_NULL; pf++, findx++) + { + const char *fname; + + fname = debug_get_field_name (info->dhandle, *pf); + if (fname == NULL) + return FALSE; + if (strncmp (fname, name, namlen) == 0 + && strlen (fname) == namlen) + { + pslot = it->fslots + findx; + break; + } + } + } + + break; + } + } + } + + if (pslot == NULL) + { + ieee_error (info, start, _("C++ reference not found")); + return FALSE; + } + + /* We allocated the type of the object as an indirect type pointing + to *pslot, which we can now update to be a reference type. */ + if (debug_get_type_kind (info->dhandle, *pslot) != DEBUG_KIND_POINTER) + { + ieee_error (info, start, _("C++ reference is not pointer")); + return FALSE; + } + + target = debug_get_target_type (info->dhandle, *pslot); + *pslot = debug_make_reference_type (info->dhandle, target); + if (*pslot == DEBUG_TYPE_NULL) + return FALSE; + + return TRUE; +} + +/* Require an ASN record. */ + +static bfd_boolean +ieee_require_asn (struct ieee_info *info, const bfd_byte **pp, bfd_vma *pv) +{ + const bfd_byte *start; + ieee_record_enum_type c; + bfd_vma varindx; + + start = *pp; + + c = (ieee_record_enum_type) **pp; + if (c != ieee_e2_first_byte_enum) + { + ieee_error (info, start, _("missing required ASN")); + return FALSE; + } + ++*pp; + + c = (ieee_record_enum_type) (((unsigned int) c << 8) | **pp); + if (c != ieee_asn_record_enum) + { + ieee_error (info, start, _("missing required ASN")); + return FALSE; + } + ++*pp; + + /* Just ignore the variable index. */ + if (! ieee_read_number (info, pp, &varindx)) + return FALSE; + + return ieee_read_expression (info, pp, pv); +} + +/* Require an ATN65 record. */ + +static bfd_boolean +ieee_require_atn65 (struct ieee_info *info, const bfd_byte **pp, + const char **pname, unsigned long *pnamlen) +{ + const bfd_byte *start; + ieee_record_enum_type c; + bfd_vma name_indx, type_indx, atn_code; + + start = *pp; + + c = (ieee_record_enum_type) **pp; + if (c != ieee_at_record_enum) + { + ieee_error (info, start, _("missing required ATN65")); + return FALSE; + } + ++*pp; + + c = (ieee_record_enum_type) (((unsigned int) c << 8) | **pp); + if (c != ieee_atn_record_enum) + { + ieee_error (info, start, _("missing required ATN65")); + return FALSE; + } + ++*pp; + + if (! ieee_read_number (info, pp, &name_indx) + || ! ieee_read_number (info, pp, &type_indx) + || ! ieee_read_number (info, pp, &atn_code)) + return FALSE; + + /* Just ignore name_indx. */ + + if (type_indx != 0 || atn_code != 65) + { + ieee_error (info, start, _("bad ATN65 record")); + return FALSE; + } + + return ieee_read_id (info, pp, pname, pnamlen); +} + +/* Convert a register number in IEEE debugging information into a + generic register number. */ + +static int +ieee_regno_to_genreg (bfd *abfd, int r) +{ + switch (bfd_get_arch (abfd)) + { + case bfd_arch_m68k: + /* For some reasons stabs adds 2 to the floating point register + numbers. */ + if (r >= 16) + r += 2; + break; + + case bfd_arch_i960: + /* Stabs uses 0 to 15 for r0 to r15, 16 to 31 for g0 to g15, and + 32 to 35 for fp0 to fp3. */ + --r; + break; + + default: + break; + } + + return r; +} + +/* Convert a generic register number to an IEEE specific one. */ + +static int +ieee_genreg_to_regno (bfd *abfd, int r) +{ + switch (bfd_get_arch (abfd)) + { + case bfd_arch_m68k: + /* For some reason stabs add 2 to the floating point register + numbers. */ + if (r >= 18) + r -= 2; + break; + + case bfd_arch_i960: + /* Stabs uses 0 to 15 for r0 to r15, 16 to 31 for g0 to g15, and + 32 to 35 for fp0 to fp3. */ + ++r; + break; + + default: + break; + } + + return r; +} + +/* These routines build IEEE debugging information out of the generic + debugging information. */ + +/* We build the IEEE debugging information byte by byte. Rather than + waste time copying data around, we use a linked list of buffers to + hold the data. */ + +#define IEEE_BUFSIZE (490) + +struct ieee_buf +{ + /* Next buffer. */ + struct ieee_buf *next; + /* Number of data bytes in this buffer. */ + unsigned int c; + /* Bytes. */ + bfd_byte buf[IEEE_BUFSIZE]; +}; + +/* A list of buffers. */ + +struct ieee_buflist +{ + /* Head of list. */ + struct ieee_buf *head; + /* Tail--last buffer on list. */ + struct ieee_buf *tail; +}; + +/* In order to generate the BB11 blocks required by the HP emulator, + we keep track of ranges of addresses which correspond to a given + compilation unit. */ + +struct ieee_range +{ + /* Next range. */ + struct ieee_range *next; + /* Low address. */ + bfd_vma low; + /* High address. */ + bfd_vma high; +}; + +/* This structure holds information for a class on the type stack. */ + +struct ieee_type_class +{ + /* The name index in the debugging information. */ + unsigned int indx; + /* The pmisc records for the class. */ + struct ieee_buflist pmiscbuf; + /* The number of pmisc records. */ + unsigned int pmisccount; + /* The name of the class holding the virtual table, if not this + class. */ + const char *vclass; + /* Whether this class holds its own virtual table. */ + bfd_boolean ownvptr; + /* The largest virtual table offset seen so far. */ + bfd_vma voffset; + /* The current method. */ + const char *method; + /* Additional pmisc records used to record fields of reference type. */ + struct ieee_buflist refs; +}; + +/* This is how we store types for the writing routines. Most types + are simply represented by a type index. */ + +struct ieee_write_type +{ + /* Type index. */ + unsigned int indx; + /* The size of the type, if known. */ + unsigned int size; + /* The name of the type, if any. */ + const char *name; + /* If this is a function or method type, we build the type here, and + only add it to the output buffers if we need it. */ + struct ieee_buflist fndef; + /* If this is a struct, this is where the struct definition is + built. */ + struct ieee_buflist strdef; + /* If this is a class, this is where the class information is built. */ + struct ieee_type_class *classdef; + /* Whether the type is unsigned. */ + unsigned int unsignedp : 1; + /* Whether this is a reference type. */ + unsigned int referencep : 1; + /* Whether this is in the local type block. */ + unsigned int localp : 1; + /* Whether this is a duplicate struct definition which we are + ignoring. */ + unsigned int ignorep : 1; +}; + +/* This is the type stack used by the debug writing routines. FIXME: + We could generate more efficient output if we remembered when we + have output a particular type before. */ + +struct ieee_type_stack +{ + /* Next entry on stack. */ + struct ieee_type_stack *next; + /* Type information. */ + struct ieee_write_type type; +}; + +/* This is a list of associations between a name and some types. + These are used for typedefs and tags. */ + +struct ieee_name_type +{ + /* Next type for this name. */ + struct ieee_name_type *next; + /* ID number. For a typedef, this is the index of the type to which + this name is typedefed. */ + unsigned int id; + /* Type. */ + struct ieee_write_type type; + /* If this is a tag which has not yet been defined, this is the + kind. If the tag has been defined, this is DEBUG_KIND_ILLEGAL. */ + enum debug_type_kind kind; +}; + +/* We use a hash table to associate names and types. */ + +struct ieee_name_type_hash_table +{ + struct bfd_hash_table root; +}; + +struct ieee_name_type_hash_entry +{ + struct bfd_hash_entry root; + /* Information for this name. */ + struct ieee_name_type *types; +}; + +/* This is a list of enums. */ + +struct ieee_defined_enum +{ + /* Next enum. */ + struct ieee_defined_enum *next; + /* Type index. */ + unsigned int indx; + /* Whether this enum has been defined. */ + bfd_boolean defined; + /* Tag. */ + const char *tag; + /* Names. */ + const char **names; + /* Values. */ + bfd_signed_vma *vals; +}; + +/* We keep a list of modified versions of types, so that we don't + output them more than once. */ + +struct ieee_modified_type +{ + /* Pointer to this type. */ + unsigned int pointer; + /* Function with unknown arguments returning this type. */ + unsigned int function; + /* Const version of this type. */ + unsigned int const_qualified; + /* Volatile version of this type. */ + unsigned int volatile_qualified; + /* List of arrays of this type of various bounds. */ + struct ieee_modified_array_type *arrays; +}; + +/* A list of arrays bounds. */ + +struct ieee_modified_array_type +{ + /* Next array bounds. */ + struct ieee_modified_array_type *next; + /* Type index with these bounds. */ + unsigned int indx; + /* Low bound. */ + bfd_signed_vma low; + /* High bound. */ + bfd_signed_vma high; +}; + +/* This is a list of pending function parameter information. We don't + output them until we see the first block. */ + +struct ieee_pending_parm +{ + /* Next pending parameter. */ + struct ieee_pending_parm *next; + /* Name. */ + const char *name; + /* Type index. */ + unsigned int type; + /* Whether the type is a reference. */ + bfd_boolean referencep; + /* Kind. */ + enum debug_parm_kind kind; + /* Value. */ + bfd_vma val; +}; + +/* This is the handle passed down by debug_write. */ + +struct ieee_handle +{ + /* BFD we are writing to. */ + bfd *abfd; + /* Whether we got an error in a subroutine called via traverse or + map_over_sections. */ + bfd_boolean error; + /* Current data buffer list. */ + struct ieee_buflist *current; + /* Current data buffer. */ + struct ieee_buf *curbuf; + /* Filename of current compilation unit. */ + const char *filename; + /* Module name of current compilation unit. */ + const char *modname; + /* List of buffer for global types. */ + struct ieee_buflist global_types; + /* List of finished data buffers. */ + struct ieee_buflist data; + /* List of buffers for typedefs in the current compilation unit. */ + struct ieee_buflist types; + /* List of buffers for variables and functions in the current + compilation unit. */ + struct ieee_buflist vars; + /* List of buffers for C++ class definitions in the current + compilation unit. */ + struct ieee_buflist cxx; + /* List of buffers for line numbers in the current compilation unit. */ + struct ieee_buflist linenos; + /* Ranges for the current compilation unit. */ + struct ieee_range *ranges; + /* Ranges for all debugging information. */ + struct ieee_range *global_ranges; + /* Nested pending ranges. */ + struct ieee_range *pending_ranges; + /* Type stack. */ + struct ieee_type_stack *type_stack; + /* Next unallocated type index. */ + unsigned int type_indx; + /* Next unallocated name index. */ + unsigned int name_indx; + /* Typedefs. */ + struct ieee_name_type_hash_table typedefs; + /* Tags. */ + struct ieee_name_type_hash_table tags; + /* Enums. */ + struct ieee_defined_enum *enums; + /* Modified versions of types. */ + struct ieee_modified_type *modified; + /* Number of entries allocated in modified. */ + unsigned int modified_alloc; + /* 4 byte complex type. */ + unsigned int complex_float_index; + /* 8 byte complex type. */ + unsigned int complex_double_index; + /* The depth of block nesting. This is 0 outside a function, and 1 + just after start_function is called. */ + unsigned int block_depth; + /* The name of the current function. */ + const char *fnname; + /* List of buffers for the type of the function we are currently + writing out. */ + struct ieee_buflist fntype; + /* List of buffers for the parameters of the function we are + currently writing out. */ + struct ieee_buflist fnargs; + /* Number of arguments written to fnargs. */ + unsigned int fnargcount; + /* Pending function parameters. */ + struct ieee_pending_parm *pending_parms; + /* Current line number filename. */ + const char *lineno_filename; + /* Line number name index. */ + unsigned int lineno_name_indx; + /* Filename of pending line number. */ + const char *pending_lineno_filename; + /* Pending line number. */ + unsigned long pending_lineno; + /* Address of pending line number. */ + bfd_vma pending_lineno_addr; + /* Highest address seen at end of procedure. */ + bfd_vma highaddr; +}; + +static bfd_boolean ieee_init_buffer + (struct ieee_handle *, struct ieee_buflist *); +static bfd_boolean ieee_change_buffer + (struct ieee_handle *, struct ieee_buflist *); +static bfd_boolean ieee_append_buffer + (struct ieee_handle *, struct ieee_buflist *, struct ieee_buflist *); +static bfd_boolean ieee_real_write_byte (struct ieee_handle *, int); +static bfd_boolean ieee_write_2bytes (struct ieee_handle *, int); +static bfd_boolean ieee_write_number (struct ieee_handle *, bfd_vma); +static bfd_boolean ieee_write_id (struct ieee_handle *, const char *); +static bfd_boolean ieee_write_asn + (struct ieee_handle *, unsigned int, bfd_vma); +static bfd_boolean ieee_write_atn65 + (struct ieee_handle *, unsigned int, const char *); +static bfd_boolean ieee_push_type + (struct ieee_handle *, unsigned int, unsigned int, bfd_boolean, + bfd_boolean); +static unsigned int ieee_pop_type (struct ieee_handle *); +static void ieee_pop_unused_type (struct ieee_handle *); +static unsigned int ieee_pop_type_used (struct ieee_handle *, bfd_boolean); +static bfd_boolean ieee_add_range + (struct ieee_handle *, bfd_boolean, bfd_vma, bfd_vma); +static bfd_boolean ieee_start_range (struct ieee_handle *, bfd_vma); +static bfd_boolean ieee_end_range (struct ieee_handle *, bfd_vma); +static bfd_boolean ieee_define_type + (struct ieee_handle *, unsigned int, bfd_boolean, bfd_boolean); +static bfd_boolean ieee_define_named_type + (struct ieee_handle *, const char *, unsigned int, unsigned int, + bfd_boolean, bfd_boolean, struct ieee_buflist *); +static struct ieee_modified_type *ieee_get_modified_info + (struct ieee_handle *, unsigned int); +static struct bfd_hash_entry *ieee_name_type_newfunc + (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); +static bfd_boolean ieee_write_undefined_tag + (struct ieee_name_type_hash_entry *, void *); +static bfd_boolean ieee_finish_compilation_unit (struct ieee_handle *); +static void ieee_add_bb11_blocks (bfd *, asection *, void *); +static bfd_boolean ieee_add_bb11 + (struct ieee_handle *, asection *, bfd_vma, bfd_vma); +static bfd_boolean ieee_output_pending_parms (struct ieee_handle *); +static unsigned int ieee_vis_to_flags (enum debug_visibility); +static bfd_boolean ieee_class_method_var + (struct ieee_handle *, const char *, enum debug_visibility, bfd_boolean, + bfd_boolean, bfd_boolean, bfd_vma, bfd_boolean); + +static bfd_boolean ieee_start_compilation_unit (void *, const char *); +static bfd_boolean ieee_start_source (void *, const char *); +static bfd_boolean ieee_empty_type (void *); +static bfd_boolean ieee_void_type (void *); +static bfd_boolean ieee_int_type (void *, unsigned int, bfd_boolean); +static bfd_boolean ieee_float_type (void *, unsigned int); +static bfd_boolean ieee_complex_type (void *, unsigned int); +static bfd_boolean ieee_bool_type (void *, unsigned int); +static bfd_boolean ieee_enum_type + (void *, const char *, const char **, bfd_signed_vma *); +static bfd_boolean ieee_pointer_type (void *); +static bfd_boolean ieee_function_type (void *, int, bfd_boolean); +static bfd_boolean ieee_reference_type (void *); +static bfd_boolean ieee_range_type (void *, bfd_signed_vma, bfd_signed_vma); +static bfd_boolean ieee_array_type + (void *, bfd_signed_vma, bfd_signed_vma, bfd_boolean); +static bfd_boolean ieee_set_type (void *, bfd_boolean); +static bfd_boolean ieee_offset_type (void *); +static bfd_boolean ieee_method_type (void *, bfd_boolean, int, bfd_boolean); +static bfd_boolean ieee_const_type (void *); +static bfd_boolean ieee_volatile_type (void *); +static bfd_boolean ieee_start_struct_type + (void *, const char *, unsigned int, bfd_boolean, unsigned int); +static bfd_boolean ieee_struct_field + (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility); +static bfd_boolean ieee_end_struct_type (void *); +static bfd_boolean ieee_start_class_type + (void *, const char *, unsigned int, bfd_boolean, unsigned int, bfd_boolean, + bfd_boolean); +static bfd_boolean ieee_class_static_member + (void *, const char *, const char *, enum debug_visibility); +static bfd_boolean ieee_class_baseclass + (void *, bfd_vma, bfd_boolean, enum debug_visibility); +static bfd_boolean ieee_class_start_method (void *, const char *); +static bfd_boolean ieee_class_method_variant + (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean, + bfd_vma, bfd_boolean); +static bfd_boolean ieee_class_static_method_variant + (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean); +static bfd_boolean ieee_class_end_method (void *); +static bfd_boolean ieee_end_class_type (void *); +static bfd_boolean ieee_typedef_type (void *, const char *); +static bfd_boolean ieee_tag_type + (void *, const char *, unsigned int, enum debug_type_kind); +static bfd_boolean ieee_typdef (void *, const char *); +static bfd_boolean ieee_tag (void *, const char *); +static bfd_boolean ieee_int_constant (void *, const char *, bfd_vma); +static bfd_boolean ieee_float_constant (void *, const char *, double); +static bfd_boolean ieee_typed_constant (void *, const char *, bfd_vma); +static bfd_boolean ieee_variable + (void *, const char *, enum debug_var_kind, bfd_vma); +static bfd_boolean ieee_start_function (void *, const char *, bfd_boolean); +static bfd_boolean ieee_function_parameter + (void *, const char *, enum debug_parm_kind, bfd_vma); +static bfd_boolean ieee_start_block (void *, bfd_vma); +static bfd_boolean ieee_end_block (void *, bfd_vma); +static bfd_boolean ieee_end_function (void *); +static bfd_boolean ieee_lineno (void *, const char *, unsigned long, bfd_vma); + +static const struct debug_write_fns ieee_fns = +{ + ieee_start_compilation_unit, + ieee_start_source, + ieee_empty_type, + ieee_void_type, + ieee_int_type, + ieee_float_type, + ieee_complex_type, + ieee_bool_type, + ieee_enum_type, + ieee_pointer_type, + ieee_function_type, + ieee_reference_type, + ieee_range_type, + ieee_array_type, + ieee_set_type, + ieee_offset_type, + ieee_method_type, + ieee_const_type, + ieee_volatile_type, + ieee_start_struct_type, + ieee_struct_field, + ieee_end_struct_type, + ieee_start_class_type, + ieee_class_static_member, + ieee_class_baseclass, + ieee_class_start_method, + ieee_class_method_variant, + ieee_class_static_method_variant, + ieee_class_end_method, + ieee_end_class_type, + ieee_typedef_type, + ieee_tag_type, + ieee_typdef, + ieee_tag, + ieee_int_constant, + ieee_float_constant, + ieee_typed_constant, + ieee_variable, + ieee_start_function, + ieee_function_parameter, + ieee_start_block, + ieee_end_block, + ieee_end_function, + ieee_lineno +}; + +/* Initialize a buffer to be empty. */ + +static bfd_boolean +ieee_init_buffer (struct ieee_handle *info ATTRIBUTE_UNUSED, + struct ieee_buflist *buflist) +{ + buflist->head = NULL; + buflist->tail = NULL; + return TRUE; +} + +/* See whether a buffer list has any data. */ + +#define ieee_buffer_emptyp(buflist) ((buflist)->head == NULL) + +/* Change the current buffer to a specified buffer chain. */ + +static bfd_boolean +ieee_change_buffer (struct ieee_handle *info, struct ieee_buflist *buflist) +{ + if (buflist->head == NULL) + { + struct ieee_buf *buf; + + buf = (struct ieee_buf *) xmalloc (sizeof *buf); + buf->next = NULL; + buf->c = 0; + buflist->head = buf; + buflist->tail = buf; + } + + info->current = buflist; + info->curbuf = buflist->tail; + + return TRUE; +} + +/* Append a buffer chain. */ + +static bfd_boolean +ieee_append_buffer (struct ieee_handle *info ATTRIBUTE_UNUSED, + struct ieee_buflist *mainbuf, + struct ieee_buflist *newbuf) +{ + if (newbuf->head != NULL) + { + if (mainbuf->head == NULL) + mainbuf->head = newbuf->head; + else + mainbuf->tail->next = newbuf->head; + mainbuf->tail = newbuf->tail; + } + return TRUE; +} + +/* Write a byte into the buffer. We use a macro for speed and a + function for the complex cases. */ + +#define ieee_write_byte(info, b) \ + ((info)->curbuf->c < IEEE_BUFSIZE \ + ? ((info)->curbuf->buf[(info)->curbuf->c++] = (b), TRUE) \ + : ieee_real_write_byte ((info), (b))) + +static bfd_boolean +ieee_real_write_byte (struct ieee_handle *info, int b) +{ + if (info->curbuf->c >= IEEE_BUFSIZE) + { + struct ieee_buf *n; + + n = (struct ieee_buf *) xmalloc (sizeof *n); + n->next = NULL; + n->c = 0; + if (info->current->head == NULL) + info->current->head = n; + else + info->current->tail->next = n; + info->current->tail = n; + info->curbuf = n; + } + + info->curbuf->buf[info->curbuf->c] = b; + ++info->curbuf->c; + + return TRUE; +} + +/* Write out two bytes. */ + +static bfd_boolean +ieee_write_2bytes (struct ieee_handle *info, int i) +{ + return (ieee_write_byte (info, i >> 8) + && ieee_write_byte (info, i & 0xff)); +} + +/* Write out an integer. */ + +static bfd_boolean +ieee_write_number (struct ieee_handle *info, bfd_vma v) +{ + bfd_vma t; + bfd_byte ab[20]; + bfd_byte *p; + unsigned int c; + + if (v <= (bfd_vma) ieee_number_end_enum) + return ieee_write_byte (info, (int) v); + + t = v; + p = ab + sizeof ab; + while (t != 0) + { + *--p = t & 0xff; + t >>= 8; + } + c = (ab + 20) - p; + + if (c > (unsigned int) (ieee_number_repeat_end_enum + - ieee_number_repeat_start_enum)) + { + fprintf (stderr, _("IEEE numeric overflow: 0x")); + fprintf_vma (stderr, v); + fprintf (stderr, "\n"); + return FALSE; + } + + if (! ieee_write_byte (info, (int) ieee_number_repeat_start_enum + c)) + return FALSE; + for (; c > 0; --c, ++p) + { + if (! ieee_write_byte (info, *p)) + return FALSE; + } + + return TRUE; +} + +/* Write out a string. */ + +static bfd_boolean +ieee_write_id (struct ieee_handle *info, const char *s) +{ + unsigned int len; + + len = strlen (s); + if (len <= 0x7f) + { + if (! ieee_write_byte (info, len)) + return FALSE; + } + else if (len <= 0xff) + { + if (! ieee_write_byte (info, (int) ieee_extension_length_1_enum) + || ! ieee_write_byte (info, len)) + return FALSE; + } + else if (len <= 0xffff) + { + if (! ieee_write_byte (info, (int) ieee_extension_length_2_enum) + || ! ieee_write_2bytes (info, len)) + return FALSE; + } + else + { + fprintf (stderr, _("IEEE string length overflow: %u\n"), len); + return FALSE; + } + + for (; *s != '\0'; s++) + if (! ieee_write_byte (info, *s)) + return FALSE; + + return TRUE; +} + +/* Write out an ASN record. */ + +static bfd_boolean +ieee_write_asn (struct ieee_handle *info, unsigned int indx, bfd_vma val) +{ + return (ieee_write_2bytes (info, (int) ieee_asn_record_enum) + && ieee_write_number (info, indx) + && ieee_write_number (info, val)); +} + +/* Write out an ATN65 record. */ + +static bfd_boolean +ieee_write_atn65 (struct ieee_handle *info, unsigned int indx, const char *s) +{ + return (ieee_write_2bytes (info, (int) ieee_atn_record_enum) + && ieee_write_number (info, indx) + && ieee_write_number (info, 0) + && ieee_write_number (info, 65) + && ieee_write_id (info, s)); +} + +/* Push a type index onto the type stack. */ + +static bfd_boolean +ieee_push_type (struct ieee_handle *info, unsigned int indx, + unsigned int size, bfd_boolean unsignedp, bfd_boolean localp) +{ + struct ieee_type_stack *ts; + + ts = (struct ieee_type_stack *) xmalloc (sizeof *ts); + memset (ts, 0, sizeof *ts); + + ts->type.indx = indx; + ts->type.size = size; + ts->type.unsignedp = unsignedp; + ts->type.localp = localp; + + ts->next = info->type_stack; + info->type_stack = ts; + + return TRUE; +} + +/* Pop a type index off the type stack. */ + +static unsigned int +ieee_pop_type (struct ieee_handle *info) +{ + return ieee_pop_type_used (info, TRUE); +} + +/* Pop an unused type index off the type stack. */ + +static void +ieee_pop_unused_type (struct ieee_handle *info) +{ + (void) ieee_pop_type_used (info, FALSE); +} + +/* Pop a used or unused type index off the type stack. */ + +static unsigned int +ieee_pop_type_used (struct ieee_handle *info, bfd_boolean used) +{ + struct ieee_type_stack *ts; + unsigned int ret; + + ts = info->type_stack; + assert (ts != NULL); + + /* If this is a function type, and we need it, we need to append the + actual definition to the typedef block now. */ + if (used && ! ieee_buffer_emptyp (&ts->type.fndef)) + { + struct ieee_buflist *buflist; + + if (ts->type.localp) + { + /* Make sure we have started the types block. */ + if (ieee_buffer_emptyp (&info->types)) + { + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 1) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + } + buflist = &info->types; + } + else + { + /* Make sure we started the global type block. */ + if (ieee_buffer_emptyp (&info->global_types)) + { + if (! ieee_change_buffer (info, &info->global_types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 2) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "")) + return FALSE; + } + buflist = &info->global_types; + } + + if (! ieee_append_buffer (info, buflist, &ts->type.fndef)) + return FALSE; + } + + ret = ts->type.indx; + info->type_stack = ts->next; + free (ts); + return ret; +} + +/* Add a range of bytes included in the current compilation unit. */ + +static bfd_boolean +ieee_add_range (struct ieee_handle *info, bfd_boolean global, bfd_vma low, + bfd_vma high) +{ + struct ieee_range **plist, *r, **pr; + + if (low == (bfd_vma) -1 || high == (bfd_vma) -1 || low == high) + return TRUE; + + if (global) + plist = &info->global_ranges; + else + plist = &info->ranges; + + for (r = *plist; r != NULL; r = r->next) + { + if (high >= r->low && low <= r->high) + { + /* The new range overlaps r. */ + if (low < r->low) + r->low = low; + if (high > r->high) + r->high = high; + pr = &r->next; + while (*pr != NULL && (*pr)->low <= r->high) + { + struct ieee_range *n; + + if ((*pr)->high > r->high) + r->high = (*pr)->high; + n = (*pr)->next; + free (*pr); + *pr = n; + } + return TRUE; + } + } + + r = (struct ieee_range *) xmalloc (sizeof *r); + memset (r, 0, sizeof *r); + + r->low = low; + r->high = high; + + /* Store the ranges sorted by address. */ + for (pr = plist; *pr != NULL; pr = &(*pr)->next) + if ((*pr)->low > high) + break; + r->next = *pr; + *pr = r; + + return TRUE; +} + +/* Start a new range for which we only have the low address. */ + +static bfd_boolean +ieee_start_range (struct ieee_handle *info, bfd_vma low) +{ + struct ieee_range *r; + + r = (struct ieee_range *) xmalloc (sizeof *r); + memset (r, 0, sizeof *r); + r->low = low; + r->next = info->pending_ranges; + info->pending_ranges = r; + return TRUE; +} + +/* Finish a range started by ieee_start_range. */ + +static bfd_boolean +ieee_end_range (struct ieee_handle *info, bfd_vma high) +{ + struct ieee_range *r; + bfd_vma low; + + assert (info->pending_ranges != NULL); + r = info->pending_ranges; + low = r->low; + info->pending_ranges = r->next; + free (r); + return ieee_add_range (info, FALSE, low, high); +} + +/* Start defining a type. */ + +static bfd_boolean +ieee_define_type (struct ieee_handle *info, unsigned int size, + bfd_boolean unsignedp, bfd_boolean localp) +{ + return ieee_define_named_type (info, (const char *) NULL, + (unsigned int) -1, size, unsignedp, + localp, (struct ieee_buflist *) NULL); +} + +/* Start defining a named type. */ + +static bfd_boolean +ieee_define_named_type (struct ieee_handle *info, const char *name, + unsigned int indx, unsigned int size, + bfd_boolean unsignedp, bfd_boolean localp, + struct ieee_buflist *buflist) +{ + unsigned int type_indx; + unsigned int name_indx; + + if (indx != (unsigned int) -1) + type_indx = indx; + else + { + type_indx = info->type_indx; + ++info->type_indx; + } + + name_indx = info->name_indx; + ++info->name_indx; + + if (name == NULL) + name = ""; + + /* If we were given a buffer, use it; otherwise, use either the + local or the global type information, and make sure that the type + block is started. */ + if (buflist != NULL) + { + if (! ieee_change_buffer (info, buflist)) + return FALSE; + } + else if (localp) + { + if (! ieee_buffer_emptyp (&info->types)) + { + if (! ieee_change_buffer (info, &info->types)) + return FALSE; + } + else + { + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 1) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + } + } + else + { + if (! ieee_buffer_emptyp (&info->global_types)) + { + if (! ieee_change_buffer (info, &info->global_types)) + return FALSE; + } + else + { + if (! ieee_change_buffer (info, &info->global_types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 2) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "")) + return FALSE; + } + } + + /* Push the new type on the type stack, write out an NN record, and + write out the start of a TY record. The caller will then finish + the TY record. */ + if (! ieee_push_type (info, type_indx, size, unsignedp, localp)) + return FALSE; + + return (ieee_write_byte (info, (int) ieee_nn_record) + && ieee_write_number (info, name_indx) + && ieee_write_id (info, name) + && ieee_write_byte (info, (int) ieee_ty_record_enum) + && ieee_write_number (info, type_indx) + && ieee_write_byte (info, 0xce) + && ieee_write_number (info, name_indx)); +} + +/* Get an entry to the list of modified versions of a type. */ + +static struct ieee_modified_type * +ieee_get_modified_info (struct ieee_handle *info, unsigned int indx) +{ + if (indx >= info->modified_alloc) + { + unsigned int nalloc; + + nalloc = info->modified_alloc; + if (nalloc == 0) + nalloc = 16; + while (indx >= nalloc) + nalloc *= 2; + info->modified = ((struct ieee_modified_type *) + xrealloc (info->modified, + nalloc * sizeof *info->modified)); + memset (info->modified + info->modified_alloc, 0, + (nalloc - info->modified_alloc) * sizeof *info->modified); + info->modified_alloc = nalloc; + } + + return info->modified + indx; +} + +/* Routines for the hash table mapping names to types. */ + +/* Initialize an entry in the hash table. */ + +static struct bfd_hash_entry * +ieee_name_type_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) +{ + struct ieee_name_type_hash_entry *ret = + (struct ieee_name_type_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + ret = ((struct ieee_name_type_hash_entry *) + bfd_hash_allocate (table, sizeof *ret)); + if (ret == NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct ieee_name_type_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + if (ret) + { + /* Set local fields. */ + ret->types = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Look up an entry in the hash table. */ + +#define ieee_name_type_hash_lookup(table, string, create, copy) \ + ((struct ieee_name_type_hash_entry *) \ + bfd_hash_lookup (&(table)->root, (string), (create), (copy))) + +/* Traverse the hash table. */ + +#define ieee_name_type_hash_traverse(table, func, info) \ + (bfd_hash_traverse \ + (&(table)->root, \ + (bfd_boolean (*) (struct bfd_hash_entry *, void *)) (func), \ + (info))) + +/* The general routine to write out IEEE debugging information. */ + +bfd_boolean +write_ieee_debugging_info (bfd *abfd, void *dhandle) +{ + struct ieee_handle info; + asection *s; + const char *err; + struct ieee_buf *b; + + memset (&info, 0, sizeof info); + info.abfd = abfd; + info.type_indx = 256; + info.name_indx = 32; + + if (!bfd_hash_table_init (&info.typedefs.root, ieee_name_type_newfunc, + sizeof (struct ieee_name_type_hash_entry)) + || !bfd_hash_table_init (&info.tags.root, ieee_name_type_newfunc, + sizeof (struct ieee_name_type_hash_entry))) + return FALSE; + + if (! ieee_init_buffer (&info, &info.global_types) + || ! ieee_init_buffer (&info, &info.data) + || ! ieee_init_buffer (&info, &info.types) + || ! ieee_init_buffer (&info, &info.vars) + || ! ieee_init_buffer (&info, &info.cxx) + || ! ieee_init_buffer (&info, &info.linenos) + || ! ieee_init_buffer (&info, &info.fntype) + || ! ieee_init_buffer (&info, &info.fnargs)) + return FALSE; + + if (! debug_write (dhandle, &ieee_fns, (void *) &info)) + return FALSE; + + if (info.filename != NULL) + { + if (! ieee_finish_compilation_unit (&info)) + return FALSE; + } + + /* Put any undefined tags in the global typedef information. */ + info.error = FALSE; + ieee_name_type_hash_traverse (&info.tags, + ieee_write_undefined_tag, + (void *) &info); + if (info.error) + return FALSE; + + /* Prepend the global typedef information to the other data. */ + if (! ieee_buffer_emptyp (&info.global_types)) + { + /* The HP debugger seems to have a bug in which it ignores the + last entry in the global types, so we add a dummy entry. */ + if (! ieee_change_buffer (&info, &info.global_types) + || ! ieee_write_byte (&info, (int) ieee_nn_record) + || ! ieee_write_number (&info, info.name_indx) + || ! ieee_write_id (&info, "") + || ! ieee_write_byte (&info, (int) ieee_ty_record_enum) + || ! ieee_write_number (&info, info.type_indx) + || ! ieee_write_byte (&info, 0xce) + || ! ieee_write_number (&info, info.name_indx) + || ! ieee_write_number (&info, 'P') + || ! ieee_write_number (&info, (int) builtin_void + 32) + || ! ieee_write_byte (&info, (int) ieee_be_record_enum)) + return FALSE; + + if (! ieee_append_buffer (&info, &info.global_types, &info.data)) + return FALSE; + info.data = info.global_types; + } + + /* Make sure that we have declare BB11 blocks for each range in the + file. They are added to info->vars. */ + info.error = FALSE; + if (! ieee_init_buffer (&info, &info.vars)) + return FALSE; + bfd_map_over_sections (abfd, ieee_add_bb11_blocks, (void *) &info); + if (info.error) + return FALSE; + if (! ieee_buffer_emptyp (&info.vars)) + { + if (! ieee_change_buffer (&info, &info.vars) + || ! ieee_write_byte (&info, (int) ieee_be_record_enum)) + return FALSE; + + if (! ieee_append_buffer (&info, &info.data, &info.vars)) + return FALSE; + } + + /* Now all the data is in info.data. Write it out to the BFD. We + normally would need to worry about whether all the other sections + are set up yet, but the IEEE backend will handle this particular + case correctly regardless. */ + if (ieee_buffer_emptyp (&info.data)) + { + /* There is no debugging information. */ + return TRUE; + } + err = NULL; + s = bfd_make_section_with_flags (abfd, ".debug", + SEC_DEBUGGING | SEC_HAS_CONTENTS); + if (s == NULL) + err = "bfd_make_section"; + if (err == NULL) + { + bfd_size_type size; + + size = 0; + for (b = info.data.head; b != NULL; b = b->next) + size += b->c; + if (! bfd_set_section_size (abfd, s, size)) + err = "bfd_set_section_size"; + } + if (err == NULL) + { + file_ptr offset; + + offset = 0; + for (b = info.data.head; b != NULL; b = b->next) + { + if (! bfd_set_section_contents (abfd, s, b->buf, offset, b->c)) + { + err = "bfd_set_section_contents"; + break; + } + offset += b->c; + } + } + + if (err != NULL) + { + fprintf (stderr, "%s: %s: %s\n", bfd_get_filename (abfd), err, + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + bfd_hash_table_free (&info.typedefs.root); + bfd_hash_table_free (&info.tags.root); + + return TRUE; +} + +/* Write out information for an undefined tag. This is called via + ieee_name_type_hash_traverse. */ + +static bfd_boolean +ieee_write_undefined_tag (struct ieee_name_type_hash_entry *h, void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_name_type *nt; + + for (nt = h->types; nt != NULL; nt = nt->next) + { + unsigned int name_indx; + char code; + + if (nt->kind == DEBUG_KIND_ILLEGAL) + continue; + + if (ieee_buffer_emptyp (&info->global_types)) + { + if (! ieee_change_buffer (info, &info->global_types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 2) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "")) + { + info->error = TRUE; + return FALSE; + } + } + else + { + if (! ieee_change_buffer (info, &info->global_types)) + { + info->error = TRUE; + return FALSE; + } + } + + name_indx = info->name_indx; + ++info->name_indx; + if (! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, name_indx) + || ! ieee_write_id (info, nt->type.name) + || ! ieee_write_byte (info, (int) ieee_ty_record_enum) + || ! ieee_write_number (info, nt->type.indx) + || ! ieee_write_byte (info, 0xce) + || ! ieee_write_number (info, name_indx)) + { + info->error = TRUE; + return FALSE; + } + + switch (nt->kind) + { + default: + abort (); + info->error = TRUE; + return FALSE; + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_CLASS: + code = 'S'; + break; + case DEBUG_KIND_UNION: + case DEBUG_KIND_UNION_CLASS: + code = 'U'; + break; + case DEBUG_KIND_ENUM: + code = 'E'; + break; + } + if (! ieee_write_number (info, code) + || ! ieee_write_number (info, 0)) + { + info->error = TRUE; + return FALSE; + } + } + + return TRUE; +} + +/* Start writing out information for a compilation unit. */ + +static bfd_boolean +ieee_start_compilation_unit (void *p, const char *filename) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + const char *modname; +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + const char *backslash; +#endif + char *c, *s; + + if (info->filename != NULL) + { + if (! ieee_finish_compilation_unit (info)) + return FALSE; + } + + info->filename = filename; + modname = strrchr (filename, '/'); +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + /* We could have a mixed forward/back slash case. */ + backslash = strrchr (filename, '\\'); + if (modname == NULL || (backslash != NULL && backslash > modname)) + modname = backslash; +#endif + + if (modname != NULL) + ++modname; +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + else if (filename[0] && filename[1] == ':') + modname = filename + 2; +#endif + else + modname = filename; + + c = xstrdup (modname); + s = strrchr (c, '.'); + if (s != NULL) + *s = '\0'; + info->modname = c; + + if (! ieee_init_buffer (info, &info->types) + || ! ieee_init_buffer (info, &info->vars) + || ! ieee_init_buffer (info, &info->cxx) + || ! ieee_init_buffer (info, &info->linenos)) + return FALSE; + info->ranges = NULL; + + /* Always include a BB1 and a BB3 block. That is what the output of + the MRI linker seems to look like. */ + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 1) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + + ++info->name_indx; + if (! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 3) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + + return TRUE; +} + +/* Finish up a compilation unit. */ + +static bfd_boolean +ieee_finish_compilation_unit (struct ieee_handle *info) +{ + struct ieee_range *r; + + if (! ieee_buffer_emptyp (&info->types)) + { + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + } + + if (! ieee_buffer_emptyp (&info->cxx)) + { + /* Append any C++ information to the global function and + variable information. */ + assert (! ieee_buffer_emptyp (&info->vars)); + if (! ieee_change_buffer (info, &info->vars)) + return FALSE; + + /* We put the pmisc records in a dummy procedure, just as the + MRI compiler does. */ + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 6) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "__XRYCPP") + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, info->highaddr - 1) + || ! ieee_append_buffer (info, &info->vars, &info->cxx) + || ! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_be_record_enum) + || ! ieee_write_number (info, info->highaddr - 1)) + return FALSE; + } + + if (! ieee_buffer_emptyp (&info->vars)) + { + if (! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + } + + if (info->pending_lineno_filename != NULL) + { + /* Force out the pending line number. */ + if (! ieee_lineno ((void *) info, (const char *) NULL, 0, (bfd_vma) -1)) + return FALSE; + } + if (! ieee_buffer_emptyp (&info->linenos)) + { + if (! ieee_change_buffer (info, &info->linenos) + || ! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + if (filename_cmp (info->filename, info->lineno_filename) != 0) + { + /* We were not in the main file. We just closed the + included line number block, and now we must close the + main line number block. */ + if (! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + } + } + + if (! ieee_append_buffer (info, &info->data, &info->types) + || ! ieee_append_buffer (info, &info->data, &info->vars) + || ! ieee_append_buffer (info, &info->data, &info->linenos)) + return FALSE; + + /* Build BB10/BB11 blocks based on the ranges we recorded. */ + if (! ieee_change_buffer (info, &info->data)) + return FALSE; + + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 10) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname) + || ! ieee_write_id (info, "") + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "GNU objcopy")) + return FALSE; + + for (r = info->ranges; r != NULL; r = r->next) + { + bfd_vma low, high; + asection *s; + int kind; + + low = r->low; + high = r->high; + + /* Find the section corresponding to this range. */ + for (s = info->abfd->sections; s != NULL; s = s->next) + { + if (bfd_get_section_vma (info->abfd, s) <= low + && high <= (bfd_get_section_vma (info->abfd, s) + + bfd_section_size (info->abfd, s))) + break; + } + + if (s == NULL) + { + /* Just ignore this range. */ + continue; + } + + /* Coalesce ranges if it seems reasonable. */ + while (r->next != NULL + && high + 0x1000 >= r->next->low + && (r->next->high + <= (bfd_get_section_vma (info->abfd, s) + + bfd_section_size (info->abfd, s)))) + { + r = r->next; + high = r->high; + } + + if ((s->flags & SEC_CODE) != 0) + kind = 1; + else if ((s->flags & SEC_READONLY) != 0) + kind = 3; + else + kind = 2; + + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 11) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "") + || ! ieee_write_number (info, kind) + || ! ieee_write_number (info, s->index + IEEE_SECTION_NUMBER_BASE) + || ! ieee_write_number (info, low) + || ! ieee_write_byte (info, (int) ieee_be_record_enum) + || ! ieee_write_number (info, high - low)) + return FALSE; + + /* Add this range to the list of global ranges. */ + if (! ieee_add_range (info, TRUE, low, high)) + return FALSE; + } + + if (! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + + return TRUE; +} + +/* Add BB11 blocks describing each range that we have not already + described. */ + +static void +ieee_add_bb11_blocks (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *data) +{ + struct ieee_handle *info = (struct ieee_handle *) data; + bfd_vma low, high; + struct ieee_range *r; + + low = bfd_get_section_vma (abfd, sec); + high = low + bfd_section_size (abfd, sec); + + /* Find the first range at or after this section. The ranges are + sorted by address. */ + for (r = info->global_ranges; r != NULL; r = r->next) + if (r->high > low) + break; + + while (low < high) + { + if (r == NULL || r->low >= high) + { + if (! ieee_add_bb11 (info, sec, low, high)) + info->error = TRUE; + return; + } + + if (low < r->low + && r->low - low > 0x100) + { + if (! ieee_add_bb11 (info, sec, low, r->low)) + { + info->error = TRUE; + return; + } + } + low = r->high; + + r = r->next; + } +} + +/* Add a single BB11 block for a range. We add it to info->vars. */ + +static bfd_boolean +ieee_add_bb11 (struct ieee_handle *info, asection *sec, bfd_vma low, + bfd_vma high) +{ + int kind; + + if (! ieee_buffer_emptyp (&info->vars)) + { + if (! ieee_change_buffer (info, &info->vars)) + return FALSE; + } + else + { + const char *filename, *modname; +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + const char *backslash; +#endif + char *c, *s; + + /* Start the enclosing BB10 block. */ + filename = bfd_get_filename (info->abfd); + modname = strrchr (filename, '/'); +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + backslash = strrchr (filename, '\\'); + if (modname == NULL || (backslash != NULL && backslash > modname)) + modname = backslash; +#endif + + if (modname != NULL) + ++modname; +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + else if (filename[0] && filename[1] == ':') + modname = filename + 2; +#endif + else + modname = filename; + + c = xstrdup (modname); + s = strrchr (c, '.'); + if (s != NULL) + *s = '\0'; + + if (! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 10) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, c) + || ! ieee_write_id (info, "") + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "GNU objcopy")) + { + free (c); + return FALSE; + } + + free (c); + } + + if ((sec->flags & SEC_CODE) != 0) + kind = 1; + else if ((sec->flags & SEC_READONLY) != 0) + kind = 3; + else + kind = 2; + + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 11) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "") + || ! ieee_write_number (info, kind) + || ! ieee_write_number (info, sec->index + IEEE_SECTION_NUMBER_BASE) + || ! ieee_write_number (info, low) + || ! ieee_write_byte (info, (int) ieee_be_record_enum) + || ! ieee_write_number (info, high - low)) + return FALSE; + + return TRUE; +} + +/* Start recording information from a particular source file. This is + used to record which file defined which types, variables, etc. It + is not used for line numbers, since the lineno entry point passes + down the file name anyhow. IEEE debugging information doesn't seem + to store this information anywhere. */ + +static bfd_boolean +ieee_start_source (void *p ATTRIBUTE_UNUSED, + const char *filename ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +/* Make an empty type. */ + +static bfd_boolean +ieee_empty_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + return ieee_push_type (info, (int) builtin_unknown, 0, FALSE, FALSE); +} + +/* Make a void type. */ + +static bfd_boolean +ieee_void_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + return ieee_push_type (info, (int) builtin_void, 0, FALSE, FALSE); +} + +/* Make an integer type. */ + +static bfd_boolean +ieee_int_type (void *p, unsigned int size, bfd_boolean unsignedp) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int indx; + + switch (size) + { + case 1: + indx = (int) builtin_signed_char; + break; + case 2: + indx = (int) builtin_signed_short_int; + break; + case 4: + indx = (int) builtin_signed_long; + break; + case 8: + indx = (int) builtin_signed_long_long; + break; + default: + fprintf (stderr, _("IEEE unsupported integer type size %u\n"), size); + return FALSE; + } + + if (unsignedp) + ++indx; + + return ieee_push_type (info, indx, size, unsignedp, FALSE); +} + +/* Make a floating point type. */ + +static bfd_boolean +ieee_float_type (void *p, unsigned int size) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int indx; + + switch (size) + { + case 4: + indx = (int) builtin_float; + break; + case 8: + indx = (int) builtin_double; + break; + case 12: + /* FIXME: This size really depends upon the processor. */ + indx = (int) builtin_long_double; + break; + case 16: + indx = (int) builtin_long_long_double; + break; + default: + fprintf (stderr, _("IEEE unsupported float type size %u\n"), size); + return FALSE; + } + + return ieee_push_type (info, indx, size, FALSE, FALSE); +} + +/* Make a complex type. */ + +static bfd_boolean +ieee_complex_type (void *p, unsigned int size) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + char code; + + switch (size) + { + case 4: + if (info->complex_float_index != 0) + return ieee_push_type (info, info->complex_float_index, size * 2, + FALSE, FALSE); + code = 'c'; + break; + case 12: + case 16: + /* These cases can be output by gcc -gstabs. Outputting the + wrong type is better than crashing. */ + case 8: + if (info->complex_double_index != 0) + return ieee_push_type (info, info->complex_double_index, size * 2, + FALSE, FALSE); + code = 'd'; + break; + default: + fprintf (stderr, _("IEEE unsupported complex type size %u\n"), size); + return FALSE; + } + + /* FIXME: I don't know what the string is for. */ + if (! ieee_define_type (info, size * 2, FALSE, FALSE) + || ! ieee_write_number (info, code) + || ! ieee_write_id (info, "")) + return FALSE; + + if (size == 4) + info->complex_float_index = info->type_stack->type.indx; + else + info->complex_double_index = info->type_stack->type.indx; + + return TRUE; +} + +/* Make a boolean type. IEEE doesn't support these, so we just make + an integer type instead. */ + +static bfd_boolean +ieee_bool_type (void *p, unsigned int size) +{ + return ieee_int_type (p, size, TRUE); +} + +/* Make an enumeration. */ + +static bfd_boolean +ieee_enum_type (void *p, const char *tag, const char **names, + bfd_signed_vma *vals) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_defined_enum *e; + bfd_boolean localp, simple; + unsigned int indx; + int i = 0; + + localp = FALSE; + indx = (unsigned int) -1; + for (e = info->enums; e != NULL; e = e->next) + { + if (tag == NULL) + { + if (e->tag != NULL) + continue; + } + else + { + if (e->tag == NULL + || tag[0] != e->tag[0] + || strcmp (tag, e->tag) != 0) + continue; + } + + if (! e->defined) + { + /* This enum tag has been seen but not defined. */ + indx = e->indx; + break; + } + + if (names != NULL && e->names != NULL) + { + for (i = 0; names[i] != NULL && e->names[i] != NULL; i++) + { + if (names[i][0] != e->names[i][0] + || vals[i] != e->vals[i] + || strcmp (names[i], e->names[i]) != 0) + break; + } + } + + if ((names == NULL && e->names == NULL) + || (names != NULL + && e->names != NULL + && names[i] == NULL + && e->names[i] == NULL)) + { + /* We've seen this enum before. */ + return ieee_push_type (info, e->indx, 0, TRUE, FALSE); + } + + if (tag != NULL) + { + /* We've already seen an enum of the same name, so we must make + sure to output this one locally. */ + localp = TRUE; + break; + } + } + + /* If this is a simple enumeration, in which the values start at 0 + and always increment by 1, we can use type E. Otherwise we must + use type N. */ + + simple = TRUE; + if (names != NULL) + { + for (i = 0; names[i] != NULL; i++) + { + if (vals[i] != i) + { + simple = FALSE; + break; + } + } + } + + if (! ieee_define_named_type (info, tag, indx, 0, TRUE, localp, + (struct ieee_buflist *) NULL) + || ! ieee_write_number (info, simple ? 'E' : 'N')) + return FALSE; + if (simple) + { + /* FIXME: This is supposed to be the enumeration size, but we + don't store that. */ + if (! ieee_write_number (info, 4)) + return FALSE; + } + if (names != NULL) + { + for (i = 0; names[i] != NULL; i++) + { + if (! ieee_write_id (info, names[i])) + return FALSE; + if (! simple) + { + if (! ieee_write_number (info, vals[i])) + return FALSE; + } + } + } + + if (! localp) + { + if (indx == (unsigned int) -1) + { + e = (struct ieee_defined_enum *) xmalloc (sizeof *e); + memset (e, 0, sizeof *e); + e->indx = info->type_stack->type.indx; + e->tag = tag; + + e->next = info->enums; + info->enums = e; + } + + e->names = names; + e->vals = vals; + e->defined = TRUE; + } + + return TRUE; +} + +/* Make a pointer type. */ + +static bfd_boolean +ieee_pointer_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean localp; + unsigned int indx; + struct ieee_modified_type *m = NULL; + + localp = info->type_stack->type.localp; + indx = ieee_pop_type (info); + + /* A pointer to a simple builtin type can be obtained by adding 32. + FIXME: Will this be a short pointer, and will that matter? */ + if (indx < 32) + return ieee_push_type (info, indx + 32, 0, TRUE, FALSE); + + if (! localp) + { + m = ieee_get_modified_info ((struct ieee_handle *) p, indx); + if (m == NULL) + return FALSE; + + /* FIXME: The size should depend upon the architecture. */ + if (m->pointer > 0) + return ieee_push_type (info, m->pointer, 4, TRUE, FALSE); + } + + if (! ieee_define_type (info, 4, TRUE, localp) + || ! ieee_write_number (info, 'P') + || ! ieee_write_number (info, indx)) + return FALSE; + + if (! localp) + m->pointer = info->type_stack->type.indx; + + return TRUE; +} + +/* Make a function type. This will be called for a method, but we + don't want to actually add it to the type table in that case. We + handle this by defining the type in a private buffer, and only + adding that buffer to the typedef block if we are going to use it. */ + +static bfd_boolean +ieee_function_type (void *p, int argcount, bfd_boolean varargs) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean localp; + unsigned int *args = NULL; + int i; + unsigned int retindx; + struct ieee_buflist fndef; + struct ieee_modified_type *m; + + localp = FALSE; + + if (argcount > 0) + { + args = (unsigned int *) xmalloc (argcount * sizeof *args); + for (i = argcount - 1; i >= 0; i--) + { + if (info->type_stack->type.localp) + localp = TRUE; + args[i] = ieee_pop_type (info); + } + } + else if (argcount < 0) + varargs = FALSE; + + if (info->type_stack->type.localp) + localp = TRUE; + retindx = ieee_pop_type (info); + + m = NULL; + if (argcount < 0 && ! localp) + { + m = ieee_get_modified_info ((struct ieee_handle *) p, retindx); + if (m == NULL) + return FALSE; + + if (m->function > 0) + return ieee_push_type (info, m->function, 0, TRUE, FALSE); + } + + /* An attribute of 0x41 means that the frame and push mask are + unknown. */ + if (! ieee_init_buffer (info, &fndef) + || ! ieee_define_named_type (info, (const char *) NULL, + (unsigned int) -1, 0, TRUE, localp, + &fndef) + || ! ieee_write_number (info, 'x') + || ! ieee_write_number (info, 0x41) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, retindx) + || ! ieee_write_number (info, (bfd_vma) argcount + (varargs ? 1 : 0))) + { + free (args); + return FALSE; + } + if (argcount > 0) + { + for (i = 0; i < argcount; i++) + if (! ieee_write_number (info, args[i])) + return FALSE; + free (args); + } + if (varargs) + { + /* A varargs function is represented by writing out the last + argument as type void *, although this makes little sense. */ + if (! ieee_write_number (info, (bfd_vma) builtin_void + 32)) + return FALSE; + } + + if (! ieee_write_number (info, 0)) + return FALSE; + + /* We wrote the information into fndef, in case we don't need it. + It will be appended to info->types by ieee_pop_type. */ + info->type_stack->type.fndef = fndef; + + if (m != NULL) + m->function = info->type_stack->type.indx; + + return TRUE; +} + +/* Make a reference type. */ + +static bfd_boolean +ieee_reference_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + /* IEEE appears to record a normal pointer type, and then use a + pmisc record to indicate that it is really a reference. */ + + if (! ieee_pointer_type (p)) + return FALSE; + info->type_stack->type.referencep = TRUE; + return TRUE; +} + +/* Make a range type. */ + +static bfd_boolean +ieee_range_type (void *p, bfd_signed_vma low, bfd_signed_vma high) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int size; + bfd_boolean unsignedp, localp; + + size = info->type_stack->type.size; + unsignedp = info->type_stack->type.unsignedp; + localp = info->type_stack->type.localp; + ieee_pop_unused_type (info); + return (ieee_define_type (info, size, unsignedp, localp) + && ieee_write_number (info, 'R') + && ieee_write_number (info, (bfd_vma) low) + && ieee_write_number (info, (bfd_vma) high) + && ieee_write_number (info, unsignedp ? 0 : 1) + && ieee_write_number (info, size)); +} + +/* Make an array type. */ + +static bfd_boolean +ieee_array_type (void *p, bfd_signed_vma low, bfd_signed_vma high, + bfd_boolean stringp ATTRIBUTE_UNUSED) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int eleindx; + bfd_boolean localp; + unsigned int size; + struct ieee_modified_type *m = NULL; + struct ieee_modified_array_type *a; + + /* IEEE does not store the range, so we just ignore it. */ + ieee_pop_unused_type (info); + localp = info->type_stack->type.localp; + size = info->type_stack->type.size; + eleindx = ieee_pop_type (info); + + /* If we don't know the range, treat the size as exactly one + element. */ + if (low < high) + size *= (high - low) + 1; + + if (! localp) + { + m = ieee_get_modified_info (info, eleindx); + if (m == NULL) + return FALSE; + + for (a = m->arrays; a != NULL; a = a->next) + { + if (a->low == low && a->high == high) + return ieee_push_type (info, a->indx, size, FALSE, FALSE); + } + } + + if (! ieee_define_type (info, size, FALSE, localp) + || ! ieee_write_number (info, low == 0 ? 'Z' : 'C') + || ! ieee_write_number (info, eleindx)) + return FALSE; + if (low != 0) + { + if (! ieee_write_number (info, low)) + return FALSE; + } + + if (! ieee_write_number (info, high + 1)) + return FALSE; + + if (! localp) + { + a = (struct ieee_modified_array_type *) xmalloc (sizeof *a); + memset (a, 0, sizeof *a); + + a->indx = info->type_stack->type.indx; + a->low = low; + a->high = high; + + a->next = m->arrays; + m->arrays = a; + } + + return TRUE; +} + +/* Make a set type. */ + +static bfd_boolean +ieee_set_type (void *p, bfd_boolean bitstringp ATTRIBUTE_UNUSED) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean localp; + unsigned int eleindx; + + localp = info->type_stack->type.localp; + eleindx = ieee_pop_type (info); + + /* FIXME: We don't know the size, so we just use 4. */ + + return (ieee_define_type (info, 0, TRUE, localp) + && ieee_write_number (info, 's') + && ieee_write_number (info, 4) + && ieee_write_number (info, eleindx)); +} + +/* Make an offset type. */ + +static bfd_boolean +ieee_offset_type (void *p) +{ + /* FIXME: The MRI C++ compiler does not appear to generate any + useful type information about an offset type. It just records a + pointer to member as an integer. The MRI/HP IEEE spec does + describe a pmisc record which can be used for a pointer to + member. Unfortunately, it does not describe the target type, + which seems pretty important. I'm going to punt this for now. */ + + return ieee_int_type (p, 4, TRUE); +} + +/* Make a method type. */ + +static bfd_boolean +ieee_method_type (void *p, bfd_boolean domain, int argcount, + bfd_boolean varargs) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + /* FIXME: The MRI/HP IEEE spec defines a pmisc record to use for a + method, but the definition is incomplete. We just output an 'x' + type. */ + + if (domain) + ieee_pop_unused_type (info); + + return ieee_function_type (p, argcount, varargs); +} + +/* Make a const qualified type. */ + +static bfd_boolean +ieee_const_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int size; + bfd_boolean unsignedp, localp; + unsigned int indx; + struct ieee_modified_type *m = NULL; + + size = info->type_stack->type.size; + unsignedp = info->type_stack->type.unsignedp; + localp = info->type_stack->type.localp; + indx = ieee_pop_type (info); + + if (! localp) + { + m = ieee_get_modified_info (info, indx); + if (m == NULL) + return FALSE; + + if (m->const_qualified > 0) + return ieee_push_type (info, m->const_qualified, size, unsignedp, + FALSE); + } + + if (! ieee_define_type (info, size, unsignedp, localp) + || ! ieee_write_number (info, 'n') + || ! ieee_write_number (info, 1) + || ! ieee_write_number (info, indx)) + return FALSE; + + if (! localp) + m->const_qualified = info->type_stack->type.indx; + + return TRUE; +} + +/* Make a volatile qualified type. */ + +static bfd_boolean +ieee_volatile_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int size; + bfd_boolean unsignedp, localp; + unsigned int indx; + struct ieee_modified_type *m = NULL; + + size = info->type_stack->type.size; + unsignedp = info->type_stack->type.unsignedp; + localp = info->type_stack->type.localp; + indx = ieee_pop_type (info); + + if (! localp) + { + m = ieee_get_modified_info (info, indx); + if (m == NULL) + return FALSE; + + if (m->volatile_qualified > 0) + return ieee_push_type (info, m->volatile_qualified, size, unsignedp, + FALSE); + } + + if (! ieee_define_type (info, size, unsignedp, localp) + || ! ieee_write_number (info, 'n') + || ! ieee_write_number (info, 2) + || ! ieee_write_number (info, indx)) + return FALSE; + + if (! localp) + m->volatile_qualified = info->type_stack->type.indx; + + return TRUE; +} + +/* Convert an enum debug_visibility into a CXXFLAGS value. */ + +static unsigned int +ieee_vis_to_flags (enum debug_visibility visibility) +{ + switch (visibility) + { + default: + abort (); + case DEBUG_VISIBILITY_PUBLIC: + return CXXFLAGS_VISIBILITY_PUBLIC; + case DEBUG_VISIBILITY_PRIVATE: + return CXXFLAGS_VISIBILITY_PRIVATE; + case DEBUG_VISIBILITY_PROTECTED: + return CXXFLAGS_VISIBILITY_PROTECTED; + } + /*NOTREACHED*/ +} + +/* Start defining a struct type. We build it in the strdef field on + the stack, to avoid confusing type definitions required by the + fields with the struct type itself. */ + +static bfd_boolean +ieee_start_struct_type (void *p, const char *tag, unsigned int id, + bfd_boolean structp, unsigned int size) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean localp, ignorep; + bfd_boolean copy; + char ab[20]; + const char *look; + struct ieee_name_type_hash_entry *h; + struct ieee_name_type *nt, *ntlook; + struct ieee_buflist strdef; + + localp = FALSE; + ignorep = FALSE; + + /* We need to create a tag for internal use even if we don't want + one for external use. This will let us refer to an anonymous + struct. */ + if (tag != NULL) + { + look = tag; + copy = FALSE; + } + else + { + sprintf (ab, "__anon%u", id); + look = ab; + copy = TRUE; + } + + /* If we already have references to the tag, we must use the + existing type index. */ + h = ieee_name_type_hash_lookup (&info->tags, look, TRUE, copy); + if (h == NULL) + return FALSE; + + nt = NULL; + for (ntlook = h->types; ntlook != NULL; ntlook = ntlook->next) + { + if (ntlook->id == id) + nt = ntlook; + else if (! ntlook->type.localp) + { + /* We are creating a duplicate definition of a globally + defined tag. Force it to be local to avoid + confusion. */ + localp = TRUE; + } + } + + if (nt != NULL) + { + assert (localp == nt->type.localp); + if (nt->kind == DEBUG_KIND_ILLEGAL && ! localp) + { + /* We've already seen a global definition of the type. + Ignore this new definition. */ + ignorep = TRUE; + } + } + else + { + nt = (struct ieee_name_type *) xmalloc (sizeof *nt); + memset (nt, 0, sizeof *nt); + nt->id = id; + nt->type.name = h->root.string; + nt->next = h->types; + h->types = nt; + nt->type.indx = info->type_indx; + ++info->type_indx; + } + + nt->kind = DEBUG_KIND_ILLEGAL; + + if (! ieee_init_buffer (info, &strdef) + || ! ieee_define_named_type (info, tag, nt->type.indx, size, TRUE, + localp, &strdef) + || ! ieee_write_number (info, structp ? 'S' : 'U') + || ! ieee_write_number (info, size)) + return FALSE; + + if (! ignorep) + { + const char *hold; + + /* We never want nt->type.name to be NULL. We want the rest of + the type to be the object set up on the type stack; it will + have a NULL name if tag is NULL. */ + hold = nt->type.name; + nt->type = info->type_stack->type; + nt->type.name = hold; + } + + info->type_stack->type.name = tag; + info->type_stack->type.strdef = strdef; + info->type_stack->type.ignorep = ignorep; + + return TRUE; +} + +/* Add a field to a struct. */ + +static bfd_boolean +ieee_struct_field (void *p, const char *name, bfd_vma bitpos, bfd_vma bitsize, + enum debug_visibility visibility) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int size; + bfd_boolean unsignedp; + bfd_boolean referencep; + bfd_boolean localp; + unsigned int indx; + bfd_vma offset; + + assert (info->type_stack != NULL + && info->type_stack->next != NULL + && ! ieee_buffer_emptyp (&info->type_stack->next->type.strdef)); + + /* If we are ignoring this struct definition, just pop and ignore + the type. */ + if (info->type_stack->next->type.ignorep) + { + ieee_pop_unused_type (info); + return TRUE; + } + + size = info->type_stack->type.size; + unsignedp = info->type_stack->type.unsignedp; + referencep = info->type_stack->type.referencep; + localp = info->type_stack->type.localp; + indx = ieee_pop_type (info); + + if (localp) + info->type_stack->type.localp = TRUE; + + if (info->type_stack->type.classdef != NULL) + { + unsigned int flags; + unsigned int nindx; + + /* This is a class. We must add a description of this field to + the class records we are building. */ + + flags = ieee_vis_to_flags (visibility); + nindx = info->type_stack->type.classdef->indx; + if (! ieee_change_buffer (info, + &info->type_stack->type.classdef->pmiscbuf) + || ! ieee_write_asn (info, nindx, 'd') + || ! ieee_write_asn (info, nindx, flags) + || ! ieee_write_atn65 (info, nindx, name) + || ! ieee_write_atn65 (info, nindx, name)) + return FALSE; + info->type_stack->type.classdef->pmisccount += 4; + + if (referencep) + { + /* We need to output a record recording that this field is + really of reference type. We put this on the refs field + of classdef, so that it can be appended to the C++ + records after the class is defined. */ + + nindx = info->name_indx; + ++info->name_indx; + + if (! ieee_change_buffer (info, + &info->type_stack->type.classdef->refs) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, nindx) + || ! ieee_write_id (info, "") + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, nindx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 62) + || ! ieee_write_number (info, 80) + || ! ieee_write_number (info, 4) + || ! ieee_write_asn (info, nindx, 'R') + || ! ieee_write_asn (info, nindx, 3) + || ! ieee_write_atn65 (info, nindx, info->type_stack->type.name) + || ! ieee_write_atn65 (info, nindx, name)) + return FALSE; + } + } + + /* If the bitsize doesn't match the expected size, we need to output + a bitfield type. */ + if (size == 0 || bitsize == 0 || bitsize == size * 8) + offset = bitpos / 8; + else + { + if (! ieee_define_type (info, 0, unsignedp, + info->type_stack->type.localp) + || ! ieee_write_number (info, 'g') + || ! ieee_write_number (info, unsignedp ? 0 : 1) + || ! ieee_write_number (info, bitsize) + || ! ieee_write_number (info, indx)) + return FALSE; + indx = ieee_pop_type (info); + offset = bitpos; + } + + /* Switch to the struct we are building in order to output this + field definition. */ + return (ieee_change_buffer (info, &info->type_stack->type.strdef) + && ieee_write_id (info, name) + && ieee_write_number (info, indx) + && ieee_write_number (info, offset)); +} + +/* Finish up a struct type. */ + +static bfd_boolean +ieee_end_struct_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_buflist *pb; + + assert (info->type_stack != NULL + && ! ieee_buffer_emptyp (&info->type_stack->type.strdef)); + + /* If we were ignoring this struct definition because it was a + duplicate definition, just through away whatever bytes we have + accumulated. Leave the type on the stack. */ + if (info->type_stack->type.ignorep) + return TRUE; + + /* If this is not a duplicate definition of this tag, then localp + will be FALSE, and we can put it in the global type block. + FIXME: We should avoid outputting duplicate definitions which are + the same. */ + if (! info->type_stack->type.localp) + { + /* Make sure we have started the global type block. */ + if (ieee_buffer_emptyp (&info->global_types)) + { + if (! ieee_change_buffer (info, &info->global_types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 2) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "")) + return FALSE; + } + pb = &info->global_types; + } + else + { + /* Make sure we have started the types block. */ + if (ieee_buffer_emptyp (&info->types)) + { + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 1) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + } + pb = &info->types; + } + + /* Append the struct definition to the types. */ + if (! ieee_append_buffer (info, pb, &info->type_stack->type.strdef) + || ! ieee_init_buffer (info, &info->type_stack->type.strdef)) + return FALSE; + + /* Leave the struct on the type stack. */ + + return TRUE; +} + +/* Start a class type. */ + +static bfd_boolean +ieee_start_class_type (void *p, const char *tag, unsigned int id, + bfd_boolean structp, unsigned int size, + bfd_boolean vptr, bfd_boolean ownvptr) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + const char *vclass; + struct ieee_buflist pmiscbuf; + unsigned int indx; + struct ieee_type_class *classdef; + + /* A C++ class is output as a C++ struct along with a set of pmisc + records describing the class. */ + + /* We need to have a name so that we can associate the struct and + the class. */ + if (tag == NULL) + { + char *t; + + t = (char *) xmalloc (20); + sprintf (t, "__anon%u", id); + tag = t; + } + + /* We can't write out the virtual table information until we have + finished the class, because we don't know the virtual table size. + We get the size from the largest voffset we see. */ + vclass = NULL; + if (vptr && ! ownvptr) + { + vclass = info->type_stack->type.name; + assert (vclass != NULL); + /* We don't call ieee_pop_unused_type, since the class should + get defined. */ + (void) ieee_pop_type (info); + } + + if (! ieee_start_struct_type (p, tag, id, structp, size)) + return FALSE; + + indx = info->name_indx; + ++info->name_indx; + + /* We write out pmisc records into the classdef field. We will + write out the pmisc start after we know the number of records we + need. */ + if (! ieee_init_buffer (info, &pmiscbuf) + || ! ieee_change_buffer (info, &pmiscbuf) + || ! ieee_write_asn (info, indx, 'T') + || ! ieee_write_asn (info, indx, structp ? 'o' : 'u') + || ! ieee_write_atn65 (info, indx, tag)) + return FALSE; + + classdef = (struct ieee_type_class *) xmalloc (sizeof *classdef); + memset (classdef, 0, sizeof *classdef); + + classdef->indx = indx; + classdef->pmiscbuf = pmiscbuf; + classdef->pmisccount = 3; + classdef->vclass = vclass; + classdef->ownvptr = ownvptr; + + info->type_stack->type.classdef = classdef; + + return TRUE; +} + +/* Add a static member to a class. */ + +static bfd_boolean +ieee_class_static_member (void *p, const char *name, const char *physname, + enum debug_visibility visibility) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int flags; + unsigned int nindx; + + /* We don't care about the type. Hopefully there will be a call to + ieee_variable declaring the physical name and the type, since + that is where an IEEE consumer must get the type. */ + ieee_pop_unused_type (info); + + assert (info->type_stack != NULL + && info->type_stack->type.classdef != NULL); + + flags = ieee_vis_to_flags (visibility); + flags |= CXXFLAGS_STATIC; + + nindx = info->type_stack->type.classdef->indx; + + if (! ieee_change_buffer (info, &info->type_stack->type.classdef->pmiscbuf) + || ! ieee_write_asn (info, nindx, 'd') + || ! ieee_write_asn (info, nindx, flags) + || ! ieee_write_atn65 (info, nindx, name) + || ! ieee_write_atn65 (info, nindx, physname)) + return FALSE; + info->type_stack->type.classdef->pmisccount += 4; + + return TRUE; +} + +/* Add a base class to a class. */ + +static bfd_boolean +ieee_class_baseclass (void *p, bfd_vma bitpos, bfd_boolean is_virtual, + enum debug_visibility visibility) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + const char *bname; + bfd_boolean localp; + unsigned int bindx; + char *fname; + unsigned int flags; + unsigned int nindx; + + assert (info->type_stack != NULL + && info->type_stack->type.name != NULL + && info->type_stack->next != NULL + && info->type_stack->next->type.classdef != NULL + && ! ieee_buffer_emptyp (&info->type_stack->next->type.strdef)); + + bname = info->type_stack->type.name; + localp = info->type_stack->type.localp; + bindx = ieee_pop_type (info); + + /* We are currently defining both a struct and a class. We must + write out a field definition in the struct which holds the base + class. The stabs debugging reader will create a field named + _vb$CLASS for a virtual base class, so we just use that. FIXME: + we should not depend upon a detail of stabs debugging. */ + if (is_virtual) + { + fname = (char *) xmalloc (strlen (bname) + sizeof "_vb$"); + sprintf (fname, "_vb$%s", bname); + flags = BASEFLAGS_VIRTUAL; + } + else + { + if (localp) + info->type_stack->type.localp = TRUE; + + fname = (char *) xmalloc (strlen (bname) + sizeof "_b$"); + sprintf (fname, "_b$%s", bname); + + if (! ieee_change_buffer (info, &info->type_stack->type.strdef) + || ! ieee_write_id (info, fname) + || ! ieee_write_number (info, bindx) + || ! ieee_write_number (info, bitpos / 8)) + { + free (fname); + return FALSE; + } + flags = 0; + } + + if (visibility == DEBUG_VISIBILITY_PRIVATE) + flags |= BASEFLAGS_PRIVATE; + + nindx = info->type_stack->type.classdef->indx; + + if (! ieee_change_buffer (info, &info->type_stack->type.classdef->pmiscbuf) + || ! ieee_write_asn (info, nindx, 'b') + || ! ieee_write_asn (info, nindx, flags) + || ! ieee_write_atn65 (info, nindx, bname) + || ! ieee_write_asn (info, nindx, 0) + || ! ieee_write_atn65 (info, nindx, fname)) + { + free (fname); + return FALSE; + } + info->type_stack->type.classdef->pmisccount += 5; + + free (fname); + + return TRUE; +} + +/* Start building a method for a class. */ + +static bfd_boolean +ieee_class_start_method (void *p, const char *name) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + assert (info->type_stack != NULL + && info->type_stack->type.classdef != NULL + && info->type_stack->type.classdef->method == NULL); + + info->type_stack->type.classdef->method = name; + + return TRUE; +} + +/* Define a new method variant, either static or not. */ + +static bfd_boolean +ieee_class_method_var (struct ieee_handle *info, const char *physname, + enum debug_visibility visibility, + bfd_boolean staticp, bfd_boolean constp, + bfd_boolean volatilep, bfd_vma voffset, + bfd_boolean context) +{ + unsigned int flags; + unsigned int nindx; + bfd_boolean is_virtual; + + /* We don't need the type of the method. An IEEE consumer which + wants the type must track down the function by the physical name + and get the type from that. */ + ieee_pop_unused_type (info); + + /* We don't use the context. FIXME: We probably ought to use it to + adjust the voffset somehow, but I don't really know how. */ + if (context) + ieee_pop_unused_type (info); + + assert (info->type_stack != NULL + && info->type_stack->type.classdef != NULL + && info->type_stack->type.classdef->method != NULL); + + flags = ieee_vis_to_flags (visibility); + + /* FIXME: We never set CXXFLAGS_OVERRIDE, CXXFLAGS_OPERATOR, + CXXFLAGS_CTORDTOR, CXXFLAGS_CTOR, or CXXFLAGS_INLINE. */ + + if (staticp) + flags |= CXXFLAGS_STATIC; + if (constp) + flags |= CXXFLAGS_CONST; + if (volatilep) + flags |= CXXFLAGS_VOLATILE; + + nindx = info->type_stack->type.classdef->indx; + + is_virtual = context || voffset > 0; + + if (! ieee_change_buffer (info, + &info->type_stack->type.classdef->pmiscbuf) + || ! ieee_write_asn (info, nindx, is_virtual ? 'v' : 'm') + || ! ieee_write_asn (info, nindx, flags) + || ! ieee_write_atn65 (info, nindx, + info->type_stack->type.classdef->method) + || ! ieee_write_atn65 (info, nindx, physname)) + return FALSE; + + if (is_virtual) + { + if (voffset > info->type_stack->type.classdef->voffset) + info->type_stack->type.classdef->voffset = voffset; + if (! ieee_write_asn (info, nindx, voffset)) + return FALSE; + ++info->type_stack->type.classdef->pmisccount; + } + + if (! ieee_write_asn (info, nindx, 0)) + return FALSE; + + info->type_stack->type.classdef->pmisccount += 5; + + return TRUE; +} + +/* Define a new method variant. */ + +static bfd_boolean +ieee_class_method_variant (void *p, const char *physname, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep, + bfd_vma voffset, bfd_boolean context) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + return ieee_class_method_var (info, physname, visibility, FALSE, constp, + volatilep, voffset, context); +} + +/* Define a new static method variant. */ + +static bfd_boolean +ieee_class_static_method_variant (void *p, const char *physname, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + return ieee_class_method_var (info, physname, visibility, TRUE, constp, + volatilep, 0, FALSE); +} + +/* Finish up a method. */ + +static bfd_boolean +ieee_class_end_method (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + assert (info->type_stack != NULL + && info->type_stack->type.classdef != NULL + && info->type_stack->type.classdef->method != NULL); + + info->type_stack->type.classdef->method = NULL; + + return TRUE; +} + +/* Finish up a class. */ + +static bfd_boolean +ieee_end_class_type (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int nindx; + + assert (info->type_stack != NULL + && info->type_stack->type.classdef != NULL); + + /* If we were ignoring this class definition because it was a + duplicate definition, just through away whatever bytes we have + accumulated. Leave the type on the stack. */ + if (info->type_stack->type.ignorep) + return TRUE; + + nindx = info->type_stack->type.classdef->indx; + + /* If we have a virtual table, we can write out the information now. */ + if (info->type_stack->type.classdef->vclass != NULL + || info->type_stack->type.classdef->ownvptr) + { + if (! ieee_change_buffer (info, + &info->type_stack->type.classdef->pmiscbuf) + || ! ieee_write_asn (info, nindx, 'z') + || ! ieee_write_atn65 (info, nindx, "") + || ! ieee_write_asn (info, nindx, + info->type_stack->type.classdef->voffset)) + return FALSE; + if (info->type_stack->type.classdef->ownvptr) + { + if (! ieee_write_atn65 (info, nindx, "")) + return FALSE; + } + else + { + if (! ieee_write_atn65 (info, nindx, + info->type_stack->type.classdef->vclass)) + return FALSE; + } + if (! ieee_write_asn (info, nindx, 0)) + return FALSE; + info->type_stack->type.classdef->pmisccount += 5; + } + + /* Now that we know the number of pmisc records, we can write out + the atn62 which starts the pmisc records, and append them to the + C++ buffers. */ + + if (! ieee_change_buffer (info, &info->cxx) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, nindx) + || ! ieee_write_id (info, "") + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, nindx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 62) + || ! ieee_write_number (info, 80) + || ! ieee_write_number (info, + info->type_stack->type.classdef->pmisccount)) + return FALSE; + + if (! ieee_append_buffer (info, &info->cxx, + &info->type_stack->type.classdef->pmiscbuf)) + return FALSE; + if (! ieee_buffer_emptyp (&info->type_stack->type.classdef->refs)) + { + if (! ieee_append_buffer (info, &info->cxx, + &info->type_stack->type.classdef->refs)) + return FALSE; + } + + return ieee_end_struct_type (p); +} + +/* Push a previously seen typedef onto the type stack. */ + +static bfd_boolean +ieee_typedef_type (void *p, const char *name) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_name_type_hash_entry *h; + struct ieee_name_type *nt; + + h = ieee_name_type_hash_lookup (&info->typedefs, name, FALSE, FALSE); + + /* h should never be NULL, since that would imply that the generic + debugging code has asked for a typedef which it has not yet + defined. */ + assert (h != NULL); + + /* We always use the most recently defined type for this name, which + will be the first one on the list. */ + + nt = h->types; + if (! ieee_push_type (info, nt->type.indx, nt->type.size, + nt->type.unsignedp, nt->type.localp)) + return FALSE; + + /* Copy over any other type information we may have. */ + info->type_stack->type = nt->type; + + return TRUE; +} + +/* Push a tagged type onto the type stack. */ + +static bfd_boolean +ieee_tag_type (void *p, const char *name, unsigned int id, + enum debug_type_kind kind) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean localp; + bfd_boolean copy; + char ab[20]; + struct ieee_name_type_hash_entry *h; + struct ieee_name_type *nt; + + if (kind == DEBUG_KIND_ENUM) + { + struct ieee_defined_enum *e; + + if (name == NULL) + abort (); + for (e = info->enums; e != NULL; e = e->next) + if (e->tag != NULL && strcmp (e->tag, name) == 0) + return ieee_push_type (info, e->indx, 0, TRUE, FALSE); + + e = (struct ieee_defined_enum *) xmalloc (sizeof *e); + memset (e, 0, sizeof *e); + + e->indx = info->type_indx; + ++info->type_indx; + e->tag = name; + e->defined = FALSE; + + e->next = info->enums; + info->enums = e; + + return ieee_push_type (info, e->indx, 0, TRUE, FALSE); + } + + localp = FALSE; + + copy = FALSE; + if (name == NULL) + { + sprintf (ab, "__anon%u", id); + name = ab; + copy = TRUE; + } + + h = ieee_name_type_hash_lookup (&info->tags, name, TRUE, copy); + if (h == NULL) + return FALSE; + + for (nt = h->types; nt != NULL; nt = nt->next) + { + if (nt->id == id) + { + if (! ieee_push_type (info, nt->type.indx, nt->type.size, + nt->type.unsignedp, nt->type.localp)) + return FALSE; + /* Copy over any other type information we may have. */ + info->type_stack->type = nt->type; + return TRUE; + } + + if (! nt->type.localp) + { + /* This is a duplicate of a global type, so it must be + local. */ + localp = TRUE; + } + } + + nt = (struct ieee_name_type *) xmalloc (sizeof *nt); + memset (nt, 0, sizeof *nt); + + nt->id = id; + nt->type.name = h->root.string; + nt->type.indx = info->type_indx; + nt->type.localp = localp; + ++info->type_indx; + nt->kind = kind; + + nt->next = h->types; + h->types = nt; + + if (! ieee_push_type (info, nt->type.indx, 0, FALSE, localp)) + return FALSE; + + info->type_stack->type.name = h->root.string; + + return TRUE; +} + +/* Output a typedef. */ + +static bfd_boolean +ieee_typdef (void *p, const char *name) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_write_type type; + unsigned int indx; + bfd_boolean found; + bfd_boolean localp; + struct ieee_name_type_hash_entry *h; + struct ieee_name_type *nt; + + type = info->type_stack->type; + indx = type.indx; + + /* If this is a simple builtin type using a builtin name, we don't + want to output the typedef itself. We also want to change the + type index to correspond to the name being used. We recognize + names used in stabs debugging output even if they don't exactly + correspond to the names used for the IEEE builtin types. */ + found = FALSE; + if (indx <= (unsigned int) builtin_bcd_float) + { + switch ((enum builtin_types) indx) + { + default: + break; + + case builtin_void: + if (strcmp (name, "void") == 0) + found = TRUE; + break; + + case builtin_signed_char: + case builtin_char: + if (strcmp (name, "signed char") == 0) + { + indx = (unsigned int) builtin_signed_char; + found = TRUE; + } + else if (strcmp (name, "char") == 0) + { + indx = (unsigned int) builtin_char; + found = TRUE; + } + break; + + case builtin_unsigned_char: + if (strcmp (name, "unsigned char") == 0) + found = TRUE; + break; + + case builtin_signed_short_int: + case builtin_short: + case builtin_short_int: + case builtin_signed_short: + if (strcmp (name, "signed short int") == 0) + { + indx = (unsigned int) builtin_signed_short_int; + found = TRUE; + } + else if (strcmp (name, "short") == 0) + { + indx = (unsigned int) builtin_short; + found = TRUE; + } + else if (strcmp (name, "short int") == 0) + { + indx = (unsigned int) builtin_short_int; + found = TRUE; + } + else if (strcmp (name, "signed short") == 0) + { + indx = (unsigned int) builtin_signed_short; + found = TRUE; + } + break; + + case builtin_unsigned_short_int: + case builtin_unsigned_short: + if (strcmp (name, "unsigned short int") == 0 + || strcmp (name, "short unsigned int") == 0) + { + indx = builtin_unsigned_short_int; + found = TRUE; + } + else if (strcmp (name, "unsigned short") == 0) + { + indx = builtin_unsigned_short; + found = TRUE; + } + break; + + case builtin_signed_long: + case builtin_int: /* FIXME: Size depends upon architecture. */ + case builtin_long: + if (strcmp (name, "signed long") == 0) + { + indx = builtin_signed_long; + found = TRUE; + } + else if (strcmp (name, "int") == 0) + { + indx = builtin_int; + found = TRUE; + } + else if (strcmp (name, "long") == 0 + || strcmp (name, "long int") == 0) + { + indx = builtin_long; + found = TRUE; + } + break; + + case builtin_unsigned_long: + case builtin_unsigned: /* FIXME: Size depends upon architecture. */ + case builtin_unsigned_int: /* FIXME: Like builtin_unsigned. */ + if (strcmp (name, "unsigned long") == 0 + || strcmp (name, "long unsigned int") == 0) + { + indx = builtin_unsigned_long; + found = TRUE; + } + else if (strcmp (name, "unsigned") == 0) + { + indx = builtin_unsigned; + found = TRUE; + } + else if (strcmp (name, "unsigned int") == 0) + { + indx = builtin_unsigned_int; + found = TRUE; + } + break; + + case builtin_signed_long_long: + if (strcmp (name, "signed long long") == 0 + || strcmp (name, "long long int") == 0) + found = TRUE; + break; + + case builtin_unsigned_long_long: + if (strcmp (name, "unsigned long long") == 0 + || strcmp (name, "long long unsigned int") == 0) + found = TRUE; + break; + + case builtin_float: + if (strcmp (name, "float") == 0) + found = TRUE; + break; + + case builtin_double: + if (strcmp (name, "double") == 0) + found = TRUE; + break; + + case builtin_long_double: + if (strcmp (name, "long double") == 0) + found = TRUE; + break; + + case builtin_long_long_double: + if (strcmp (name, "long long double") == 0) + found = TRUE; + break; + } + + if (found) + type.indx = indx; + } + + h = ieee_name_type_hash_lookup (&info->typedefs, name, TRUE, FALSE); + if (h == NULL) + return FALSE; + + /* See if we have already defined this type with this name. */ + localp = type.localp; + for (nt = h->types; nt != NULL; nt = nt->next) + { + if (nt->id == indx) + { + /* If this is a global definition, then we don't need to + do anything here. */ + if (! nt->type.localp) + { + ieee_pop_unused_type (info); + return TRUE; + } + } + else + { + /* This is a duplicate definition, so make this one local. */ + localp = TRUE; + } + } + + /* We need to add a new typedef for this type. */ + + nt = (struct ieee_name_type *) xmalloc (sizeof *nt); + memset (nt, 0, sizeof *nt); + nt->id = indx; + nt->type = type; + nt->type.name = name; + nt->type.localp = localp; + nt->kind = DEBUG_KIND_ILLEGAL; + + nt->next = h->types; + h->types = nt; + + if (found) + { + /* This is one of the builtin typedefs, so we don't need to + actually define it. */ + ieee_pop_unused_type (info); + return TRUE; + } + + indx = ieee_pop_type (info); + + if (! ieee_define_named_type (info, name, (unsigned int) -1, type.size, + type.unsignedp, localp, + (struct ieee_buflist *) NULL) + || ! ieee_write_number (info, 'T') + || ! ieee_write_number (info, indx)) + return FALSE; + + /* Remove the type we just added to the type stack. This should not + be ieee_pop_unused_type, since the type is used, we just don't + need it now. */ + (void) ieee_pop_type (info); + + return TRUE; +} + +/* Output a tag for a type. We don't have to do anything here. */ + +static bfd_boolean +ieee_tag (void *p, const char *name ATTRIBUTE_UNUSED) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + /* This should not be ieee_pop_unused_type, since we want the type + to be defined. */ + (void) ieee_pop_type (info); + return TRUE; +} + +/* Output an integer constant. */ + +static bfd_boolean +ieee_int_constant (void *p ATTRIBUTE_UNUSED, const char *name ATTRIBUTE_UNUSED, + bfd_vma val ATTRIBUTE_UNUSED) +{ + /* FIXME. */ + return TRUE; +} + +/* Output a floating point constant. */ + +static bfd_boolean +ieee_float_constant (void *p ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED, + double val ATTRIBUTE_UNUSED) +{ + /* FIXME. */ + return TRUE; +} + +/* Output a typed constant. */ + +static bfd_boolean +ieee_typed_constant (void *p, const char *name ATTRIBUTE_UNUSED, + bfd_vma val ATTRIBUTE_UNUSED) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + /* FIXME. */ + ieee_pop_unused_type (info); + return TRUE; +} + +/* Output a variable. */ + +static bfd_boolean +ieee_variable (void *p, const char *name, enum debug_var_kind kind, + bfd_vma val) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + unsigned int name_indx; + unsigned int size; + bfd_boolean referencep; + unsigned int type_indx; + bfd_boolean asn; + int refflag; + + size = info->type_stack->type.size; + referencep = info->type_stack->type.referencep; + type_indx = ieee_pop_type (info); + + assert (! ieee_buffer_emptyp (&info->vars)); + if (! ieee_change_buffer (info, &info->vars)) + return FALSE; + + name_indx = info->name_indx; + ++info->name_indx; + + /* Write out an NN and an ATN record for this variable. */ + if (! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, name_indx) + || ! ieee_write_id (info, name) + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, name_indx) + || ! ieee_write_number (info, type_indx)) + return FALSE; + switch (kind) + { + default: + abort (); + return FALSE; + case DEBUG_GLOBAL: + if (! ieee_write_number (info, 8) + || ! ieee_add_range (info, FALSE, val, val + size)) + return FALSE; + refflag = 0; + asn = TRUE; + break; + case DEBUG_STATIC: + if (! ieee_write_number (info, 3) + || ! ieee_add_range (info, FALSE, val, val + size)) + return FALSE; + refflag = 1; + asn = TRUE; + break; + case DEBUG_LOCAL_STATIC: + if (! ieee_write_number (info, 3) + || ! ieee_add_range (info, FALSE, val, val + size)) + return FALSE; + refflag = 2; + asn = TRUE; + break; + case DEBUG_LOCAL: + if (! ieee_write_number (info, 1) + || ! ieee_write_number (info, val)) + return FALSE; + refflag = 2; + asn = FALSE; + break; + case DEBUG_REGISTER: + if (! ieee_write_number (info, 2) + || ! ieee_write_number (info, + ieee_genreg_to_regno (info->abfd, val))) + return FALSE; + refflag = 2; + asn = FALSE; + break; + } + + if (asn) + { + if (! ieee_write_asn (info, name_indx, val)) + return FALSE; + } + + /* If this is really a reference type, then we just output it with + pointer type, and must now output a C++ record indicating that it + is really reference type. */ + if (referencep) + { + unsigned int nindx; + + nindx = info->name_indx; + ++info->name_indx; + + /* If this is a global variable, we want to output the misc + record in the C++ misc record block. Otherwise, we want to + output it just after the variable definition, which is where + the current buffer is. */ + if (refflag != 2) + { + if (! ieee_change_buffer (info, &info->cxx)) + return FALSE; + } + + if (! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, nindx) + || ! ieee_write_id (info, "") + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, nindx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 62) + || ! ieee_write_number (info, 80) + || ! ieee_write_number (info, 3) + || ! ieee_write_asn (info, nindx, 'R') + || ! ieee_write_asn (info, nindx, refflag) + || ! ieee_write_atn65 (info, nindx, name)) + return FALSE; + } + + return TRUE; +} + +/* Start outputting information for a function. */ + +static bfd_boolean +ieee_start_function (void *p, const char *name, bfd_boolean global) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + bfd_boolean referencep; + unsigned int retindx, typeindx; + + referencep = info->type_stack->type.referencep; + retindx = ieee_pop_type (info); + + /* Besides recording a BB4 or BB6 block, we record the type of the + function in the BB1 typedef block. We can't write out the full + type until we have seen all the parameters, so we accumulate it + in info->fntype and info->fnargs. */ + if (! ieee_buffer_emptyp (&info->fntype)) + { + /* FIXME: This might happen someday if we support nested + functions. */ + abort (); + } + + info->fnname = name; + + /* An attribute of 0x40 means that the push mask is unknown. */ + if (! ieee_define_named_type (info, name, (unsigned int) -1, 0, FALSE, TRUE, + &info->fntype) + || ! ieee_write_number (info, 'x') + || ! ieee_write_number (info, 0x40) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, retindx)) + return FALSE; + + typeindx = ieee_pop_type (info); + + if (! ieee_init_buffer (info, &info->fnargs)) + return FALSE; + info->fnargcount = 0; + + /* If the function return value is actually a reference type, we + must add a record indicating that. */ + if (referencep) + { + unsigned int nindx; + + nindx = info->name_indx; + ++info->name_indx; + if (! ieee_change_buffer (info, &info->cxx) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, nindx) + || ! ieee_write_id (info, "") + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, nindx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 62) + || ! ieee_write_number (info, 80) + || ! ieee_write_number (info, 3) + || ! ieee_write_asn (info, nindx, 'R') + || ! ieee_write_asn (info, nindx, global ? 0 : 1) + || ! ieee_write_atn65 (info, nindx, name)) + return FALSE; + } + + assert (! ieee_buffer_emptyp (&info->vars)); + if (! ieee_change_buffer (info, &info->vars)) + return FALSE; + + /* The address is written out as the first block. */ + + ++info->block_depth; + + return (ieee_write_byte (info, (int) ieee_bb_record_enum) + && ieee_write_byte (info, global ? 4 : 6) + && ieee_write_number (info, 0) + && ieee_write_id (info, name) + && ieee_write_number (info, 0) + && ieee_write_number (info, typeindx)); +} + +/* Add a function parameter. This will normally be called before the + first block, so we postpone them until we see the block. */ + +static bfd_boolean +ieee_function_parameter (void *p, const char *name, enum debug_parm_kind kind, + bfd_vma val) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + struct ieee_pending_parm *m, **pm; + + assert (info->block_depth == 1); + + m = (struct ieee_pending_parm *) xmalloc (sizeof *m); + memset (m, 0, sizeof *m); + + m->next = NULL; + m->name = name; + m->referencep = info->type_stack->type.referencep; + m->type = ieee_pop_type (info); + m->kind = kind; + m->val = val; + + for (pm = &info->pending_parms; *pm != NULL; pm = &(*pm)->next) + ; + *pm = m; + + /* Add the type to the fnargs list. */ + if (! ieee_change_buffer (info, &info->fnargs) + || ! ieee_write_number (info, m->type)) + return FALSE; + ++info->fnargcount; + + return TRUE; +} + +/* Output pending function parameters. */ + +static bfd_boolean +ieee_output_pending_parms (struct ieee_handle *info) +{ + struct ieee_pending_parm *m; + unsigned int refcount; + + refcount = 0; + for (m = info->pending_parms; m != NULL; m = m->next) + { + enum debug_var_kind vkind; + + switch (m->kind) + { + default: + abort (); + return FALSE; + case DEBUG_PARM_STACK: + case DEBUG_PARM_REFERENCE: + vkind = DEBUG_LOCAL; + break; + case DEBUG_PARM_REG: + case DEBUG_PARM_REF_REG: + vkind = DEBUG_REGISTER; + break; + } + + if (! ieee_push_type (info, m->type, 0, FALSE, FALSE)) + return FALSE; + info->type_stack->type.referencep = m->referencep; + if (m->referencep) + ++refcount; + if (! ieee_variable ((void *) info, m->name, vkind, m->val)) + return FALSE; + } + + /* If there are any reference parameters, we need to output a + miscellaneous record indicating them. */ + if (refcount > 0) + { + unsigned int nindx, varindx; + + /* FIXME: The MRI compiler outputs the demangled function name + here, but we are outputting the mangled name. */ + nindx = info->name_indx; + ++info->name_indx; + if (! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, nindx) + || ! ieee_write_id (info, "") + || ! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, nindx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 62) + || ! ieee_write_number (info, 80) + || ! ieee_write_number (info, refcount + 3) + || ! ieee_write_asn (info, nindx, 'B') + || ! ieee_write_atn65 (info, nindx, info->fnname) + || ! ieee_write_asn (info, nindx, 0)) + return FALSE; + for (m = info->pending_parms, varindx = 1; + m != NULL; + m = m->next, varindx++) + { + if (m->referencep) + { + if (! ieee_write_asn (info, nindx, varindx)) + return FALSE; + } + } + } + + m = info->pending_parms; + while (m != NULL) + { + struct ieee_pending_parm *next; + + next = m->next; + free (m); + m = next; + } + + info->pending_parms = NULL; + + return TRUE; +} + +/* Start a block. If this is the first block, we output the address + to finish the BB4 or BB6, and then output the function parameters. */ + +static bfd_boolean +ieee_start_block (void *p, bfd_vma addr) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + if (! ieee_change_buffer (info, &info->vars)) + return FALSE; + + if (info->block_depth == 1) + { + if (! ieee_write_number (info, addr) + || ! ieee_output_pending_parms (info)) + return FALSE; + } + else + { + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 6) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, "") + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, addr)) + return FALSE; + } + + if (! ieee_start_range (info, addr)) + return FALSE; + + ++info->block_depth; + + return TRUE; +} + +/* End a block. */ + +static bfd_boolean +ieee_end_block (void *p, bfd_vma addr) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + /* The address we are given is the end of the block, but IEEE seems + to want to the address of the last byte in the block, so we + subtract one. */ + if (! ieee_change_buffer (info, &info->vars) + || ! ieee_write_byte (info, (int) ieee_be_record_enum) + || ! ieee_write_number (info, addr - 1)) + return FALSE; + + if (! ieee_end_range (info, addr)) + return FALSE; + + --info->block_depth; + + if (addr > info->highaddr) + info->highaddr = addr; + + return TRUE; +} + +/* End a function. */ + +static bfd_boolean +ieee_end_function (void *p) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + assert (info->block_depth == 1); + + --info->block_depth; + + /* Now we can finish up fntype, and add it to the typdef section. + At this point, fntype is the 'x' type up to the argument count, + and fnargs is the argument types. We must add the argument + count, and we must add the level. FIXME: We don't record varargs + functions correctly. In fact, stabs debugging does not give us + enough information to do so. */ + if (! ieee_change_buffer (info, &info->fntype) + || ! ieee_write_number (info, info->fnargcount) + || ! ieee_change_buffer (info, &info->fnargs) + || ! ieee_write_number (info, 0)) + return FALSE; + + /* Make sure the typdef block has been started. */ + if (ieee_buffer_emptyp (&info->types)) + { + if (! ieee_change_buffer (info, &info->types) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 1) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->modname)) + return FALSE; + } + + if (! ieee_append_buffer (info, &info->types, &info->fntype) + || ! ieee_append_buffer (info, &info->types, &info->fnargs)) + return FALSE; + + info->fnname = NULL; + if (! ieee_init_buffer (info, &info->fntype) + || ! ieee_init_buffer (info, &info->fnargs)) + return FALSE; + info->fnargcount = 0; + + return TRUE; +} + +/* Record line number information. */ + +static bfd_boolean +ieee_lineno (void *p, const char *filename, unsigned long lineno, bfd_vma addr) +{ + struct ieee_handle *info = (struct ieee_handle *) p; + + assert (info->filename != NULL); + + /* The HP simulator seems to get confused when more than one line is + listed for the same address, at least if they are in different + files. We handle this by always listing the last line for a + given address, since that seems to be the one that gdb uses. */ + if (info->pending_lineno_filename != NULL + && addr != info->pending_lineno_addr) + { + /* Make sure we have a line number block. */ + if (! ieee_buffer_emptyp (&info->linenos)) + { + if (! ieee_change_buffer (info, &info->linenos)) + return FALSE; + } + else + { + info->lineno_name_indx = info->name_indx; + ++info->name_indx; + if (! ieee_change_buffer (info, &info->linenos) + || ! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 5) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->filename) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, info->lineno_name_indx) + || ! ieee_write_id (info, "")) + return FALSE; + info->lineno_filename = info->filename; + } + + if (filename_cmp (info->pending_lineno_filename, + info->lineno_filename) != 0) + { + if (filename_cmp (info->filename, info->lineno_filename) != 0) + { + /* We were not in the main file. Close the block for the + included file. */ + if (! ieee_write_byte (info, (int) ieee_be_record_enum)) + return FALSE; + if (filename_cmp (info->filename, + info->pending_lineno_filename) == 0) + { + /* We need a new NN record, and we aren't about to + output one. */ + info->lineno_name_indx = info->name_indx; + ++info->name_indx; + if (! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, info->lineno_name_indx) + || ! ieee_write_id (info, "")) + return FALSE; + } + } + if (filename_cmp (info->filename, + info->pending_lineno_filename) != 0) + { + /* We are not changing to the main file. Open a block for + the new included file. */ + info->lineno_name_indx = info->name_indx; + ++info->name_indx; + if (! ieee_write_byte (info, (int) ieee_bb_record_enum) + || ! ieee_write_byte (info, 5) + || ! ieee_write_number (info, 0) + || ! ieee_write_id (info, info->pending_lineno_filename) + || ! ieee_write_byte (info, (int) ieee_nn_record) + || ! ieee_write_number (info, info->lineno_name_indx) + || ! ieee_write_id (info, "")) + return FALSE; + } + info->lineno_filename = info->pending_lineno_filename; + } + + if (! ieee_write_2bytes (info, (int) ieee_atn_record_enum) + || ! ieee_write_number (info, info->lineno_name_indx) + || ! ieee_write_number (info, 0) + || ! ieee_write_number (info, 7) + || ! ieee_write_number (info, info->pending_lineno) + || ! ieee_write_number (info, 0) + || ! ieee_write_asn (info, info->lineno_name_indx, + info->pending_lineno_addr)) + return FALSE; + } + + info->pending_lineno_filename = filename; + info->pending_lineno = lineno; + info->pending_lineno_addr = addr; + + return TRUE; +} diff --git a/contrib/toolchain/binutils/binutils/not-strip.c b/contrib/toolchain/binutils/binutils/not-strip.c new file mode 100644 index 0000000000..8ffbba6b25 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/not-strip.c @@ -0,0 +1,23 @@ +/* Copyright 2007 Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Linked with objcopy.o to flag that this program is 'objcopy' (not + 'strip'). */ + +int is_strip = 0; diff --git a/contrib/toolchain/binutils/binutils/objcopy.c b/contrib/toolchain/binutils/binutils/objcopy.c new file mode 100644 index 0000000000..2d09d11210 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/objcopy.c @@ -0,0 +1,4293 @@ +/* objcopy.c -- copy object file from input to output, optionally massaging it. + Copyright 1991-2013 Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "progress.h" +#include "getopt.h" +#include "libiberty.h" +#include "bucomm.h" +#include "budbg.h" +#include "filenames.h" +#include "fnmatch.h" +#include "elf-bfd.h" +#include "libbfd.h" +#include "coff/internal.h" +#include "libcoff.h" + +/* FIXME: See bfd/peXXigen.c for why we include an architecture specific + header in generic PE code. */ +#include "coff/i386.h" +#include "coff/pe.h" + +static bfd_vma pe_file_alignment = (bfd_vma) -1; +static bfd_vma pe_heap_commit = (bfd_vma) -1; +static bfd_vma pe_heap_reserve = (bfd_vma) -1; +static bfd_vma pe_image_base = (bfd_vma) -1; +static bfd_vma pe_section_alignment = (bfd_vma) -1; +static bfd_vma pe_stack_commit = (bfd_vma) -1; +static bfd_vma pe_stack_reserve = (bfd_vma) -1; +static short pe_subsystem = -1; +static short pe_major_subsystem_version = -1; +static short pe_minor_subsystem_version = -1; + +struct is_specified_symbol_predicate_data +{ + const char *name; + bfd_boolean found; +}; + +/* A list to support redefine_sym. */ +struct redefine_node +{ + char *source; + char *target; + struct redefine_node *next; +}; + +typedef struct section_rename +{ + const char * old_name; + const char * new_name; + flagword flags; + struct section_rename * next; +} +section_rename; + +/* List of sections to be renamed. */ +static section_rename *section_rename_list; + +static asymbol **isympp = NULL; /* Input symbols. */ +static asymbol **osympp = NULL; /* Output symbols that survive stripping. */ + +/* If `copy_byte' >= 0, copy 'copy_width' byte(s) of every `interleave' bytes. */ +static int copy_byte = -1; +static int interleave = 0; /* Initialised to 4 in copy_main(). */ +static int copy_width = 1; + +static bfd_boolean verbose; /* Print file and target names. */ +static bfd_boolean preserve_dates; /* Preserve input file timestamp. */ +static int deterministic = -1; /* Enable deterministic archives. */ +static int status = 0; /* Exit status. */ + +enum strip_action + { + STRIP_UNDEF, + STRIP_NONE, /* Don't strip. */ + STRIP_DEBUG, /* Strip all debugger symbols. */ + STRIP_UNNEEDED, /* Strip unnecessary symbols. */ + STRIP_NONDEBUG, /* Strip everything but debug info. */ + STRIP_DWO, /* Strip all DWO info. */ + STRIP_NONDWO, /* Strip everything but DWO info. */ + STRIP_ALL /* Strip all symbols. */ + }; + +/* Which symbols to remove. */ +static enum strip_action strip_symbols = STRIP_UNDEF; + +enum locals_action + { + LOCALS_UNDEF, + LOCALS_START_L, /* Discard locals starting with L. */ + LOCALS_ALL /* Discard all locals. */ + }; + +/* Which local symbols to remove. Overrides STRIP_ALL. */ +static enum locals_action discard_locals; + +/* Structure used to hold lists of sections and actions to take. */ +struct section_list +{ + struct section_list * next; /* Next section to change. */ + const char * pattern; /* Section name pattern. */ + bfd_boolean used; /* Whether this entry was used. */ + + unsigned int context; /* What to do with matching sections. */ + /* Flag bits used in the context field. + COPY and REMOVE are mutually exlusive. SET and ALTER are mutually exclusive. */ +#define SECTION_CONTEXT_REMOVE (1 << 0) /* Remove this section. */ +#define SECTION_CONTEXT_COPY (1 << 1) /* Copy this section, delete all non-copied section. */ +#define SECTION_CONTEXT_SET_VMA (1 << 2) /* Set the sections' VMA address. */ +#define SECTION_CONTEXT_ALTER_VMA (1 << 3) /* Increment or decrement the section's VMA address. */ +#define SECTION_CONTEXT_SET_LMA (1 << 4) /* Set the sections' LMA address. */ +#define SECTION_CONTEXT_ALTER_LMA (1 << 5) /* Increment or decrement the section's LMA address. */ +#define SECTION_CONTEXT_SET_FLAGS (1 << 6) /* Set the section's flags. */ + + bfd_vma vma_val; /* Amount to change by or set to. */ + bfd_vma lma_val; /* Amount to change by or set to. */ + flagword flags; /* What to set the section flags to. */ +}; + +static struct section_list *change_sections; + +/* TRUE if some sections are to be removed. */ +static bfd_boolean sections_removed; + +/* TRUE if only some sections are to be copied. */ +static bfd_boolean sections_copied; + +/* Changes to the start address. */ +static bfd_vma change_start = 0; +static bfd_boolean set_start_set = FALSE; +static bfd_vma set_start; + +/* Changes to section addresses. */ +static bfd_vma change_section_address = 0; + +/* Filling gaps between sections. */ +static bfd_boolean gap_fill_set = FALSE; +static bfd_byte gap_fill = 0; + +/* Pad to a given address. */ +static bfd_boolean pad_to_set = FALSE; +static bfd_vma pad_to; + +/* Use alternative machine code? */ +static unsigned long use_alt_mach_code = 0; + +/* Output BFD flags user wants to set or clear */ +static flagword bfd_flags_to_set; +static flagword bfd_flags_to_clear; + +/* List of sections to add. */ +struct section_add +{ + /* Next section to add. */ + struct section_add *next; + /* Name of section to add. */ + const char *name; + /* Name of file holding section contents. */ + const char *filename; + /* Size of file. */ + size_t size; + /* Contents of file. */ + bfd_byte *contents; + /* BFD section, after it has been added. */ + asection *section; +}; + +/* List of sections to add to the output BFD. */ +static struct section_add *add_sections; + +/* If non-NULL the argument to --add-gnu-debuglink. + This should be the filename to store in the .gnu_debuglink section. */ +static const char * gnu_debuglink_filename = NULL; + +/* Whether to convert debugging information. */ +static bfd_boolean convert_debugging = FALSE; + +/* Whether to compress/decompress DWARF debug sections. */ +static enum +{ + nothing, + compress, + decompress +} do_debug_sections = nothing; + +/* Whether to change the leading character in symbol names. */ +static bfd_boolean change_leading_char = FALSE; + +/* Whether to remove the leading character from global symbol names. */ +static bfd_boolean remove_leading_char = FALSE; + +/* Whether to permit wildcard in symbol comparison. */ +static bfd_boolean wildcard = FALSE; + +/* True if --localize-hidden is in effect. */ +static bfd_boolean localize_hidden = FALSE; + +/* List of symbols to strip, keep, localize, keep-global, weaken, + or redefine. */ +static htab_t strip_specific_htab = NULL; +static htab_t strip_unneeded_htab = NULL; +static htab_t keep_specific_htab = NULL; +static htab_t localize_specific_htab = NULL; +static htab_t globalize_specific_htab = NULL; +static htab_t keepglobal_specific_htab = NULL; +static htab_t weaken_specific_htab = NULL; +static struct redefine_node *redefine_sym_list = NULL; + +/* If this is TRUE, we weaken global symbols (set BSF_WEAK). */ +static bfd_boolean weaken = FALSE; + +/* If this is TRUE, we retain BSF_FILE symbols. */ +static bfd_boolean keep_file_symbols = FALSE; + +/* Prefix symbols/sections. */ +static char *prefix_symbols_string = 0; +static char *prefix_sections_string = 0; +static char *prefix_alloc_sections_string = 0; + +/* True if --extract-symbol was passed on the command line. */ +static bfd_boolean extract_symbol = FALSE; + +/* If `reverse_bytes' is nonzero, then reverse the order of every chunk + of bytes within each output section. */ +static int reverse_bytes = 0; + +/* For Coff objects, we may want to allow or disallow long section names, + or preserve them where found in the inputs. Debug info relies on them. */ +enum long_section_name_handling + { + DISABLE, + ENABLE, + KEEP + }; + +/* The default long section handling mode is to preserve them. + This is also the only behaviour for 'strip'. */ +static enum long_section_name_handling long_section_names = KEEP; + +/* 150 isn't special; it's just an arbitrary non-ASCII char value. */ +enum command_line_switch + { + OPTION_ADD_SECTION=150, + OPTION_CHANGE_ADDRESSES, + OPTION_CHANGE_LEADING_CHAR, + OPTION_CHANGE_START, + OPTION_CHANGE_SECTION_ADDRESS, + OPTION_CHANGE_SECTION_LMA, + OPTION_CHANGE_SECTION_VMA, + OPTION_CHANGE_WARNINGS, + OPTION_COMPRESS_DEBUG_SECTIONS, + OPTION_DEBUGGING, + OPTION_DECOMPRESS_DEBUG_SECTIONS, + OPTION_GAP_FILL, + OPTION_NO_CHANGE_WARNINGS, + OPTION_PAD_TO, + OPTION_REMOVE_LEADING_CHAR, + OPTION_SET_SECTION_FLAGS, + OPTION_SET_START, + OPTION_STRIP_UNNEEDED, + OPTION_WEAKEN, + OPTION_REDEFINE_SYM, + OPTION_REDEFINE_SYMS, + OPTION_SREC_LEN, + OPTION_SREC_FORCES3, + OPTION_STRIP_SYMBOLS, + OPTION_STRIP_UNNEEDED_SYMBOL, + OPTION_STRIP_UNNEEDED_SYMBOLS, + OPTION_KEEP_SYMBOLS, + OPTION_LOCALIZE_HIDDEN, + OPTION_LOCALIZE_SYMBOLS, + OPTION_LONG_SECTION_NAMES, + OPTION_GLOBALIZE_SYMBOL, + OPTION_GLOBALIZE_SYMBOLS, + OPTION_KEEPGLOBAL_SYMBOLS, + OPTION_WEAKEN_SYMBOLS, + OPTION_RENAME_SECTION, + OPTION_ALT_MACH_CODE, + OPTION_PREFIX_SYMBOLS, + OPTION_PREFIX_SECTIONS, + OPTION_PREFIX_ALLOC_SECTIONS, + OPTION_FORMATS_INFO, + OPTION_ADD_GNU_DEBUGLINK, + OPTION_ONLY_KEEP_DEBUG, + OPTION_KEEP_FILE_SYMBOLS, + OPTION_READONLY_TEXT, + OPTION_WRITABLE_TEXT, + OPTION_PURE, + OPTION_IMPURE, + OPTION_EXTRACT_SYMBOL, + OPTION_REVERSE_BYTES, + OPTION_FILE_ALIGNMENT, + OPTION_HEAP, + OPTION_IMAGE_BASE, + OPTION_SECTION_ALIGNMENT, + OPTION_STACK, + OPTION_INTERLEAVE_WIDTH, + OPTION_SUBSYSTEM, + OPTION_EXTRACT_DWO, + OPTION_STRIP_DWO + }; + +/* Options to handle if running as "strip". */ + +static struct option strip_options[] = +{ + {"disable-deterministic-archives", no_argument, 0, 'U'}, + {"discard-all", no_argument, 0, 'x'}, + {"discard-locals", no_argument, 0, 'X'}, + {"enable-deterministic-archives", no_argument, 0, 'D'}, + {"format", required_argument, 0, 'F'}, /* Obsolete */ + {"help", no_argument, 0, 'h'}, + {"info", no_argument, 0, OPTION_FORMATS_INFO}, + {"input-format", required_argument, 0, 'I'}, /* Obsolete */ + {"input-target", required_argument, 0, 'I'}, + {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS}, + {"keep-symbol", required_argument, 0, 'K'}, + {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG}, + {"output-format", required_argument, 0, 'O'}, /* Obsolete */ + {"output-target", required_argument, 0, 'O'}, + {"output-file", required_argument, 0, 'o'}, + {"preserve-dates", no_argument, 0, 'p'}, + {"remove-section", required_argument, 0, 'R'}, + {"strip-all", no_argument, 0, 's'}, + {"strip-debug", no_argument, 0, 'S'}, + {"strip-dwo", no_argument, 0, OPTION_STRIP_DWO}, + {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED}, + {"strip-symbol", required_argument, 0, 'N'}, + {"target", required_argument, 0, 'F'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"wildcard", no_argument, 0, 'w'}, + {0, no_argument, 0, 0} +}; + +/* Options to handle if running as "objcopy". */ + +static struct option copy_options[] = +{ + {"add-gnu-debuglink", required_argument, 0, OPTION_ADD_GNU_DEBUGLINK}, + {"add-section", required_argument, 0, OPTION_ADD_SECTION}, + {"adjust-start", required_argument, 0, OPTION_CHANGE_START}, + {"adjust-vma", required_argument, 0, OPTION_CHANGE_ADDRESSES}, + {"adjust-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS}, + {"adjust-warnings", no_argument, 0, OPTION_CHANGE_WARNINGS}, + {"alt-machine-code", required_argument, 0, OPTION_ALT_MACH_CODE}, + {"binary-architecture", required_argument, 0, 'B'}, + {"byte", required_argument, 0, 'b'}, + {"change-addresses", required_argument, 0, OPTION_CHANGE_ADDRESSES}, + {"change-leading-char", no_argument, 0, OPTION_CHANGE_LEADING_CHAR}, + {"change-section-address", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS}, + {"change-section-lma", required_argument, 0, OPTION_CHANGE_SECTION_LMA}, + {"change-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_VMA}, + {"change-start", required_argument, 0, OPTION_CHANGE_START}, + {"change-warnings", no_argument, 0, OPTION_CHANGE_WARNINGS}, + {"compress-debug-sections", no_argument, 0, OPTION_COMPRESS_DEBUG_SECTIONS}, + {"debugging", no_argument, 0, OPTION_DEBUGGING}, + {"decompress-debug-sections", no_argument, 0, OPTION_DECOMPRESS_DEBUG_SECTIONS}, + {"disable-deterministic-archives", no_argument, 0, 'U'}, + {"discard-all", no_argument, 0, 'x'}, + {"discard-locals", no_argument, 0, 'X'}, + {"enable-deterministic-archives", no_argument, 0, 'D'}, + {"extract-dwo", no_argument, 0, OPTION_EXTRACT_DWO}, + {"extract-symbol", no_argument, 0, OPTION_EXTRACT_SYMBOL}, + {"format", required_argument, 0, 'F'}, /* Obsolete */ + {"gap-fill", required_argument, 0, OPTION_GAP_FILL}, + {"globalize-symbol", required_argument, 0, OPTION_GLOBALIZE_SYMBOL}, + {"globalize-symbols", required_argument, 0, OPTION_GLOBALIZE_SYMBOLS}, + {"help", no_argument, 0, 'h'}, + {"impure", no_argument, 0, OPTION_IMPURE}, + {"info", no_argument, 0, OPTION_FORMATS_INFO}, + {"input-format", required_argument, 0, 'I'}, /* Obsolete */ + {"input-target", required_argument, 0, 'I'}, + {"interleave", optional_argument, 0, 'i'}, + {"interleave-width", required_argument, 0, OPTION_INTERLEAVE_WIDTH}, + {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS}, + {"keep-global-symbol", required_argument, 0, 'G'}, + {"keep-global-symbols", required_argument, 0, OPTION_KEEPGLOBAL_SYMBOLS}, + {"keep-symbol", required_argument, 0, 'K'}, + {"keep-symbols", required_argument, 0, OPTION_KEEP_SYMBOLS}, + {"localize-hidden", no_argument, 0, OPTION_LOCALIZE_HIDDEN}, + {"localize-symbol", required_argument, 0, 'L'}, + {"localize-symbols", required_argument, 0, OPTION_LOCALIZE_SYMBOLS}, + {"long-section-names", required_argument, 0, OPTION_LONG_SECTION_NAMES}, + {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS}, + {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS}, + {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG}, + {"only-section", required_argument, 0, 'j'}, + {"output-format", required_argument, 0, 'O'}, /* Obsolete */ + {"output-target", required_argument, 0, 'O'}, + {"pad-to", required_argument, 0, OPTION_PAD_TO}, + {"prefix-symbols", required_argument, 0, OPTION_PREFIX_SYMBOLS}, + {"prefix-sections", required_argument, 0, OPTION_PREFIX_SECTIONS}, + {"prefix-alloc-sections", required_argument, 0, OPTION_PREFIX_ALLOC_SECTIONS}, + {"preserve-dates", no_argument, 0, 'p'}, + {"pure", no_argument, 0, OPTION_PURE}, + {"readonly-text", no_argument, 0, OPTION_READONLY_TEXT}, + {"redefine-sym", required_argument, 0, OPTION_REDEFINE_SYM}, + {"redefine-syms", required_argument, 0, OPTION_REDEFINE_SYMS}, + {"remove-leading-char", no_argument, 0, OPTION_REMOVE_LEADING_CHAR}, + {"remove-section", required_argument, 0, 'R'}, + {"rename-section", required_argument, 0, OPTION_RENAME_SECTION}, + {"reverse-bytes", required_argument, 0, OPTION_REVERSE_BYTES}, + {"set-section-flags", required_argument, 0, OPTION_SET_SECTION_FLAGS}, + {"set-start", required_argument, 0, OPTION_SET_START}, + {"srec-len", required_argument, 0, OPTION_SREC_LEN}, + {"srec-forceS3", no_argument, 0, OPTION_SREC_FORCES3}, + {"strip-all", no_argument, 0, 'S'}, + {"strip-debug", no_argument, 0, 'g'}, + {"strip-dwo", no_argument, 0, OPTION_STRIP_DWO}, + {"strip-unneeded", no_argument, 0, OPTION_STRIP_UNNEEDED}, + {"strip-unneeded-symbol", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOL}, + {"strip-unneeded-symbols", required_argument, 0, OPTION_STRIP_UNNEEDED_SYMBOLS}, + {"strip-symbol", required_argument, 0, 'N'}, + {"strip-symbols", required_argument, 0, OPTION_STRIP_SYMBOLS}, + {"target", required_argument, 0, 'F'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"weaken", no_argument, 0, OPTION_WEAKEN}, + {"weaken-symbol", required_argument, 0, 'W'}, + {"weaken-symbols", required_argument, 0, OPTION_WEAKEN_SYMBOLS}, + {"wildcard", no_argument, 0, 'w'}, + {"writable-text", no_argument, 0, OPTION_WRITABLE_TEXT}, + {"file-alignment", required_argument, 0, OPTION_FILE_ALIGNMENT}, + {"heap", required_argument, 0, OPTION_HEAP}, + {"image-base", required_argument, 0 , OPTION_IMAGE_BASE}, + {"section-alignment", required_argument, 0, OPTION_SECTION_ALIGNMENT}, + {"stack", required_argument, 0, OPTION_STACK}, + {"subsystem", required_argument, 0, OPTION_SUBSYSTEM}, + {0, no_argument, 0, 0} +}; + +/* IMPORTS */ +extern char *program_name; + +/* This flag distinguishes between strip and objcopy: + 1 means this is 'strip'; 0 means this is 'objcopy'. + -1 means if we should use argv[0] to decide. */ +extern int is_strip; + +/* The maximum length of an S record. This variable is declared in srec.c + and can be modified by the --srec-len parameter. */ +extern unsigned int Chunk; + +/* Restrict the generation of Srecords to type S3 only. + This variable is declare in bfd/srec.c and can be toggled + on by the --srec-forceS3 command line switch. */ +extern bfd_boolean S3Forced; + +/* Forward declarations. */ +static void setup_section (bfd *, asection *, void *); +static void setup_bfd_headers (bfd *, bfd *); +static void copy_relocations_in_section (bfd *, asection *, void *); +static void copy_section (bfd *, asection *, void *); +static void get_sections (bfd *, asection *, void *); +static int compare_section_lma (const void *, const void *); +static void mark_symbols_used_in_relocations (bfd *, asection *, void *); +static bfd_boolean write_debugging_info (bfd *, void *, long *, asymbol ***); +static const char *lookup_sym_redefinition (const char *); + +static void +copy_usage (FILE *stream, int exit_status) +{ + fprintf (stream, _("Usage: %s [option(s)] in-file [out-file]\n"), program_name); + fprintf (stream, _(" Copies a binary file, possibly transforming it in the process\n")); + fprintf (stream, _(" The options are:\n")); + fprintf (stream, _("\ + -I --input-target Assume input file is in format \n\ + -O --output-target Create an output file in format \n\ + -B --binary-architecture Set output arch, when input is arch-less\n\ + -F --target Set both input and output format to \n\ + --debugging Convert debugging information, if possible\n\ + -p --preserve-dates Copy modified/access timestamps to the output\n")); + if (DEFAULT_AR_DETERMINISTIC) + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives (default)\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior\n")); + else + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior (default)\n")); + fprintf (stream, _("\ + -j --only-section Only copy section into the output\n\ + --add-gnu-debuglink= Add section .gnu_debuglink linking to \n\ + -R --remove-section Remove section from the output\n\ + -S --strip-all Remove all symbol and relocation information\n\ + -g --strip-debug Remove all debugging symbols & sections\n\ + --strip-dwo Remove all DWO sections\n\ + --strip-unneeded Remove all symbols not needed by relocations\n\ + -N --strip-symbol Do not copy symbol \n\ + --strip-unneeded-symbol \n\ + Do not copy symbol unless needed by\n\ + relocations\n\ + --only-keep-debug Strip everything but the debug information\n\ + --extract-dwo Copy only DWO sections\n\ + --extract-symbol Remove section contents but keep symbols\n\ + -K --keep-symbol Do not strip symbol \n\ + --keep-file-symbols Do not strip file symbol(s)\n\ + --localize-hidden Turn all ELF hidden symbols into locals\n\ + -L --localize-symbol Force symbol to be marked as a local\n\ + --globalize-symbol Force symbol to be marked as a global\n\ + -G --keep-global-symbol Localize all symbols except \n\ + -W --weaken-symbol Force symbol to be marked as a weak\n\ + --weaken Force all global symbols to be marked as weak\n\ + -w --wildcard Permit wildcard in symbol comparison\n\ + -x --discard-all Remove all non-global symbols\n\ + -X --discard-locals Remove any compiler-generated symbols\n\ + -i --interleave [] Only copy N out of every bytes\n\ + --interleave-width Set N for --interleave\n\ + -b --byte Select byte in every interleaved block\n\ + --gap-fill Fill gaps between sections with \n\ + --pad-to Pad the last section up to address \n\ + --set-start Set the start address to \n\ + {--change-start|--adjust-start} \n\ + Add to the start address\n\ + {--change-addresses|--adjust-vma} \n\ + Add to LMA, VMA and start addresses\n\ + {--change-section-address|--adjust-section-vma} {=|+|-}\n\ + Change LMA and VMA of section by \n\ + --change-section-lma {=|+|-}\n\ + Change the LMA of section by \n\ + --change-section-vma {=|+|-}\n\ + Change the VMA of section by \n\ + {--[no-]change-warnings|--[no-]adjust-warnings}\n\ + Warn if a named section does not exist\n\ + --set-section-flags =\n\ + Set section 's properties to \n\ + --add-section = Add section found in to output\n\ + --rename-section =[,] Rename section to \n\ + --long-section-names {enable|disable|keep}\n\ + Handle long section names in Coff objects.\n\ + --change-leading-char Force output format's leading character style\n\ + --remove-leading-char Remove leading character from global symbols\n\ + --reverse-bytes= Reverse bytes at a time, in output sections with content\n\ + --redefine-sym = Redefine symbol name to \n\ + --redefine-syms --redefine-sym for all symbol pairs \n\ + listed in \n\ + --srec-len Restrict the length of generated Srecords\n\ + --srec-forceS3 Restrict the type of generated Srecords to S3\n\ + --strip-symbols -N for all symbols listed in \n\ + --strip-unneeded-symbols \n\ + --strip-unneeded-symbol for all symbols listed\n\ + in \n\ + --keep-symbols -K for all symbols listed in \n\ + --localize-symbols -L for all symbols listed in \n\ + --globalize-symbols --globalize-symbol for all in \n\ + --keep-global-symbols -G for all symbols listed in \n\ + --weaken-symbols -W for all symbols listed in \n\ + --alt-machine-code Use the target's 'th alternative machine\n\ + --writable-text Mark the output text as writable\n\ + --readonly-text Make the output text write protected\n\ + --pure Mark the output file as demand paged\n\ + --impure Mark the output file as impure\n\ + --prefix-symbols Add to start of every symbol name\n\ + --prefix-sections Add to start of every section name\n\ + --prefix-alloc-sections \n\ + Add to start of every allocatable\n\ + section name\n\ + --file-alignment Set PE file alignment to \n\ + --heap [,] Set PE reserve/commit heap to /\n\ + \n\ + --image-base
Set PE image base to
\n\ + --section-alignment Set PE section alignment to \n\ + --stack [,] Set PE reserve/commit stack to /\n\ + \n\ + --subsystem [:]\n\ + Set PE subsystem to [& ]\n\ + --compress-debug-sections Compress DWARF debug sections using zlib\n\ + --decompress-debug-sections Decompress DWARF debug sections using zlib\n\ + -v --verbose List all object files modified\n\ + @ Read options from \n\ + -V --version Display this program's version number\n\ + -h --help Display this output\n\ + --info List object formats & architectures supported\n\ +")); + list_supported_targets (program_name, stream); + if (REPORT_BUGS_TO[0] && exit_status == 0) + fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); + exit (exit_status); +} + +static void +strip_usage (FILE *stream, int exit_status) +{ + fprintf (stream, _("Usage: %s in-file(s)\n"), program_name); + fprintf (stream, _(" Removes symbols and sections from files\n")); + fprintf (stream, _(" The options are:\n")); + fprintf (stream, _("\ + -I --input-target= Assume input file is in format \n\ + -O --output-target= Create an output file in format \n\ + -F --target= Set both input and output format to \n\ + -p --preserve-dates Copy modified/access timestamps to the output\n\ +")); + if (DEFAULT_AR_DETERMINISTIC) + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives (default)\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior\n")); + else + fprintf (stream, _("\ + -D --enable-deterministic-archives\n\ + Produce deterministic output when stripping archives\n\ + -U --disable-deterministic-archives\n\ + Disable -D behavior (default)\n")); + fprintf (stream, _("\ + -R --remove-section= Remove section from the output\n\ + -s --strip-all Remove all symbol and relocation information\n\ + -g -S -d --strip-debug Remove all debugging symbols & sections\n\ + --strip-dwo Remove all DWO sections\n\ + --strip-unneeded Remove all symbols not needed by relocations\n\ + --only-keep-debug Strip everything but the debug information\n\ + -N --strip-symbol= Do not copy symbol \n\ + -K --keep-symbol= Do not strip symbol \n\ + --keep-file-symbols Do not strip file symbol(s)\n\ + -w --wildcard Permit wildcard in symbol comparison\n\ + -x --discard-all Remove all non-global symbols\n\ + -X --discard-locals Remove any compiler-generated symbols\n\ + -v --verbose List all object files modified\n\ + -V --version Display this program's version number\n\ + -h --help Display this output\n\ + --info List object formats & architectures supported\n\ + -o Place stripped output into \n\ +")); + + list_supported_targets (program_name, stream); + if (REPORT_BUGS_TO[0] && exit_status == 0) + fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); + exit (exit_status); +} + +/* Parse section flags into a flagword, with a fatal error if the + string can't be parsed. */ + +static flagword +parse_flags (const char *s) +{ + flagword ret; + const char *snext; + int len; + + ret = SEC_NO_FLAGS; + + do + { + snext = strchr (s, ','); + if (snext == NULL) + len = strlen (s); + else + { + len = snext - s; + ++snext; + } + + if (0) ; +#define PARSE_FLAG(fname,fval) \ + else if (strncasecmp (fname, s, len) == 0) ret |= fval + PARSE_FLAG ("alloc", SEC_ALLOC); + PARSE_FLAG ("load", SEC_LOAD); + PARSE_FLAG ("noload", SEC_NEVER_LOAD); + PARSE_FLAG ("readonly", SEC_READONLY); + PARSE_FLAG ("debug", SEC_DEBUGGING); + PARSE_FLAG ("code", SEC_CODE); + PARSE_FLAG ("data", SEC_DATA); + PARSE_FLAG ("rom", SEC_ROM); + PARSE_FLAG ("share", SEC_COFF_SHARED); + PARSE_FLAG ("contents", SEC_HAS_CONTENTS); + PARSE_FLAG ("merge", SEC_MERGE); + PARSE_FLAG ("strings", SEC_STRINGS); +#undef PARSE_FLAG + else + { + char *copy; + + copy = (char *) xmalloc (len + 1); + strncpy (copy, s, len); + copy[len] = '\0'; + non_fatal (_("unrecognized section flag `%s'"), copy); + fatal (_("supported flags: %s"), + "alloc, load, noload, readonly, debug, code, data, rom, share, contents, merge, strings"); + } + + s = snext; + } + while (s != NULL); + + return ret; +} + +/* Find and optionally add an entry in the change_sections list. + + We need to be careful in how we match section names because of the support + for wildcard characters. For example suppose that the user has invoked + objcopy like this: + + --set-section-flags .debug_*=debug + --set-section-flags .debug_str=readonly,debug + --change-section-address .debug_*ranges=0x1000 + + With the idea that all debug sections will receive the DEBUG flag, the + .debug_str section will also receive the READONLY flag and the + .debug_ranges and .debug_aranges sections will have their address set to + 0x1000. (This may not make much sense, but it is just an example). + + When adding the section name patterns to the section list we need to make + sure that previous entries do not match with the new entry, unless the + match is exact. (In which case we assume that the user is overriding + the previous entry with the new context). + + When matching real section names to the section list we make use of the + wildcard characters, but we must do so in context. Eg if we are setting + section addresses then we match for .debug_ranges but not for .debug_info. + + Finally, if ADD is false and we do find a match, we mark the section list + entry as used. */ + +static struct section_list * +find_section_list (const char *name, bfd_boolean add, unsigned int context) +{ + struct section_list *p; + + /* assert ((context & ((1 << 7) - 1)) != 0); */ + + for (p = change_sections; p != NULL; p = p->next) + { + if (add) + { + if (strcmp (p->pattern, name) == 0) + { + /* Check for context conflicts. */ + if (((p->context & SECTION_CONTEXT_REMOVE) + && (context & SECTION_CONTEXT_COPY)) + || ((context & SECTION_CONTEXT_REMOVE) + && (p->context & SECTION_CONTEXT_COPY))) + fatal (_("error: %s both copied and removed"), name); + + if (((p->context & SECTION_CONTEXT_SET_VMA) + && (context & SECTION_CONTEXT_ALTER_VMA)) + || ((context & SECTION_CONTEXT_SET_VMA) + && (context & SECTION_CONTEXT_ALTER_VMA))) + fatal (_("error: %s both sets and alters VMA"), name); + + if (((p->context & SECTION_CONTEXT_SET_LMA) + && (context & SECTION_CONTEXT_ALTER_LMA)) + || ((context & SECTION_CONTEXT_SET_LMA) + && (context & SECTION_CONTEXT_ALTER_LMA))) + fatal (_("error: %s both sets and alters LMA"), name); + + /* Extend the context. */ + p->context |= context; + return p; + } + } + /* If we are not adding a new name/pattern then + only check for a match if the context applies. */ + else if ((p->context & context) + /* We could check for the presence of wildchar characters + first and choose between calling strcmp and fnmatch, + but is that really worth it ? */ + && fnmatch (p->pattern, name, 0) == 0) + { + p->used = TRUE; + return p; + } + } + + if (! add) + return NULL; + + p = (struct section_list *) xmalloc (sizeof (struct section_list)); + p->pattern = name; + p->used = FALSE; + p->context = context; + p->vma_val = 0; + p->lma_val = 0; + p->flags = 0; + p->next = change_sections; + change_sections = p; + + return p; +} + +/* There is htab_hash_string but no htab_eq_string. Makes sense. */ + +static int +eq_string (const void *s1, const void *s2) +{ + return strcmp ((const char *) s1, (const char *) s2) == 0; +} + +static htab_t +create_symbol_htab (void) +{ + return htab_create_alloc (16, htab_hash_string, eq_string, NULL, xcalloc, free); +} + +static void +create_symbol_htabs (void) +{ + strip_specific_htab = create_symbol_htab (); + strip_unneeded_htab = create_symbol_htab (); + keep_specific_htab = create_symbol_htab (); + localize_specific_htab = create_symbol_htab (); + globalize_specific_htab = create_symbol_htab (); + keepglobal_specific_htab = create_symbol_htab (); + weaken_specific_htab = create_symbol_htab (); +} + +/* Add a symbol to strip_specific_list. */ + +static void +add_specific_symbol (const char *name, htab_t htab) +{ + *htab_find_slot (htab, name, INSERT) = (char *) name; +} + +/* Add symbols listed in `filename' to strip_specific_list. */ + +#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t') +#define IS_LINE_TERMINATOR(c) ((c) == '\n' || (c) == '\r' || (c) == '\0') + +static void +add_specific_symbols (const char *filename, htab_t htab) +{ + off_t size; + FILE * f; + char * line; + char * buffer; + unsigned int line_count; + + size = get_file_size (filename); + if (size == 0) + { + status = 1; + return; + } + + buffer = (char *) xmalloc (size + 2); + f = fopen (filename, FOPEN_RT); + if (f == NULL) + fatal (_("cannot open '%s': %s"), filename, strerror (errno)); + + if (fread (buffer, 1, size, f) == 0 || ferror (f)) + fatal (_("%s: fread failed"), filename); + + fclose (f); + buffer [size] = '\n'; + buffer [size + 1] = '\0'; + + line_count = 1; + + for (line = buffer; * line != '\0'; line ++) + { + char * eol; + char * name; + char * name_end; + int finished = FALSE; + + for (eol = line;; eol ++) + { + switch (* eol) + { + case '\n': + * eol = '\0'; + /* Cope with \n\r. */ + if (eol[1] == '\r') + ++ eol; + finished = TRUE; + break; + + case '\r': + * eol = '\0'; + /* Cope with \r\n. */ + if (eol[1] == '\n') + ++ eol; + finished = TRUE; + break; + + case 0: + finished = TRUE; + break; + + case '#': + /* Line comment, Terminate the line here, in case a + name is present and then allow the rest of the + loop to find the real end of the line. */ + * eol = '\0'; + break; + + default: + break; + } + + if (finished) + break; + } + + /* A name may now exist somewhere between 'line' and 'eol'. + Strip off leading whitespace and trailing whitespace, + then add it to the list. */ + for (name = line; IS_WHITESPACE (* name); name ++) + ; + for (name_end = name; + (! IS_WHITESPACE (* name_end)) + && (! IS_LINE_TERMINATOR (* name_end)); + name_end ++) + ; + + if (! IS_LINE_TERMINATOR (* name_end)) + { + char * extra; + + for (extra = name_end + 1; IS_WHITESPACE (* extra); extra ++) + ; + + if (! IS_LINE_TERMINATOR (* extra)) + non_fatal (_("%s:%d: Ignoring rubbish found on this line"), + filename, line_count); + } + + * name_end = '\0'; + + if (name_end > name) + add_specific_symbol (name, htab); + + /* Advance line pointer to end of line. The 'eol ++' in the for + loop above will then advance us to the start of the next line. */ + line = eol; + line_count ++; + } +} + +/* See whether a symbol should be stripped or kept + based on strip_specific_list and keep_symbols. */ + +static int +is_specified_symbol_predicate (void **slot, void *data) +{ + struct is_specified_symbol_predicate_data *d = + (struct is_specified_symbol_predicate_data *) data; + const char *slot_name = (char *) *slot; + + if (*slot_name != '!') + { + if (! fnmatch (slot_name, d->name, 0)) + { + d->found = TRUE; + /* Stop traversal. */ + return 0; + } + } + else + { + if (fnmatch (slot_name + 1, d->name, 0)) + { + d->found = TRUE; + /* Stop traversal. */ + return 0; + } + } + + /* Continue traversal. */ + return 1; +} + +static bfd_boolean +is_specified_symbol (const char *name, htab_t htab) +{ + if (wildcard) + { + struct is_specified_symbol_predicate_data data; + + data.name = name; + data.found = FALSE; + + htab_traverse (htab, is_specified_symbol_predicate, &data); + + return data.found; + } + + return htab_find (htab, name) != NULL; +} + +/* Return a pointer to the symbol used as a signature for GROUP. */ + +static asymbol * +group_signature (asection *group) +{ + bfd *abfd = group->owner; + Elf_Internal_Shdr *ghdr; + + if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) + return NULL; + + ghdr = &elf_section_data (group)->this_hdr; + if (ghdr->sh_link < elf_numsections (abfd)) + { + const struct elf_backend_data *bed = get_elf_backend_data (abfd); + Elf_Internal_Shdr *symhdr = elf_elfsections (abfd) [ghdr->sh_link]; + + if (symhdr->sh_type == SHT_SYMTAB + && ghdr->sh_info < symhdr->sh_size / bed->s->sizeof_sym) + return isympp[ghdr->sh_info - 1]; + } + return NULL; +} + +/* Return TRUE if the section is a DWO section. */ + +static bfd_boolean +is_dwo_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + const char *name = bfd_get_section_name (abfd, sec); + int len = strlen (name); + + return strncmp (name + len - 4, ".dwo", 4) == 0; +} + +/* See if a non-group section is being removed. */ + +static bfd_boolean +is_strip_section_1 (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + if (sections_removed || sections_copied) + { + struct section_list *p; + struct section_list *q; + + p = find_section_list (bfd_get_section_name (abfd, sec), FALSE, + SECTION_CONTEXT_REMOVE); + q = find_section_list (bfd_get_section_name (abfd, sec), FALSE, + SECTION_CONTEXT_COPY); + + if (p && q) + fatal (_("error: section %s matches both remove and copy options"), + bfd_get_section_name (abfd, sec)); + + if (p != NULL) + return TRUE; + if (sections_copied && q == NULL) + return TRUE; + } + + if ((bfd_get_section_flags (abfd, sec) & SEC_DEBUGGING) != 0) + { + if (strip_symbols == STRIP_DEBUG + || strip_symbols == STRIP_UNNEEDED + || strip_symbols == STRIP_ALL + || discard_locals == LOCALS_ALL + || convert_debugging) + { + /* By default we don't want to strip .reloc section. + This section has for pe-coff special meaning. See + pe-dll.c file in ld, and peXXigen.c in bfd for details. */ + if (strcmp (bfd_get_section_name (abfd, sec), ".reloc") != 0) + return TRUE; + } + + if (strip_symbols == STRIP_DWO) + return is_dwo_section (abfd, sec); + + if (strip_symbols == STRIP_NONDEBUG) + return FALSE; + } + + if (strip_symbols == STRIP_NONDWO) + return !is_dwo_section (abfd, sec); + + return FALSE; +} + +/* See if a section is being removed. */ + +static bfd_boolean +is_strip_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) +{ + if (is_strip_section_1 (abfd, sec)) + return TRUE; + + if ((bfd_get_section_flags (abfd, sec) & SEC_GROUP) != 0) + { + asymbol *gsym; + const char *gname; + asection *elt, *first; + + /* PR binutils/3181 + If we are going to strip the group signature symbol, then + strip the group section too. */ + gsym = group_signature (sec); + if (gsym != NULL) + gname = gsym->name; + else + gname = sec->name; + if ((strip_symbols == STRIP_ALL + && !is_specified_symbol (gname, keep_specific_htab)) + || is_specified_symbol (gname, strip_specific_htab)) + return TRUE; + + /* Remove the group section if all members are removed. */ + first = elt = elf_next_in_group (sec); + while (elt != NULL) + { + if (!is_strip_section_1 (abfd, elt)) + return FALSE; + elt = elf_next_in_group (elt); + if (elt == first) + break; + } + + return TRUE; + } + + return FALSE; +} + +/* Return true if SYM is a hidden symbol. */ + +static bfd_boolean +is_hidden_symbol (asymbol *sym) +{ + elf_symbol_type *elf_sym; + + elf_sym = elf_symbol_from (sym->the_bfd, sym); + if (elf_sym != NULL) + switch (ELF_ST_VISIBILITY (elf_sym->internal_elf_sym.st_other)) + { + case STV_HIDDEN: + case STV_INTERNAL: + return TRUE; + } + return FALSE; +} + +/* Choose which symbol entries to copy; put the result in OSYMS. + We don't copy in place, because that confuses the relocs. + Return the number of symbols to print. */ + +static unsigned int +filter_symbols (bfd *abfd, bfd *obfd, asymbol **osyms, + asymbol **isyms, long symcount) +{ + asymbol **from = isyms, **to = osyms; + long src_count = 0, dst_count = 0; + int relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0; + + for (; src_count < symcount; src_count++) + { + asymbol *sym = from[src_count]; + flagword flags = sym->flags; + char *name = (char *) bfd_asymbol_name (sym); + bfd_boolean keep; + bfd_boolean used_in_reloc = FALSE; + bfd_boolean undefined; + bfd_boolean rem_leading_char; + bfd_boolean add_leading_char; + + undefined = bfd_is_und_section (bfd_get_section (sym)); + + if (redefine_sym_list) + { + char *old_name, *new_name; + + old_name = (char *) bfd_asymbol_name (sym); + new_name = (char *) lookup_sym_redefinition (old_name); + bfd_asymbol_name (sym) = new_name; + name = new_name; + } + + /* Check if we will remove the current leading character. */ + rem_leading_char = + (name[0] == bfd_get_symbol_leading_char (abfd)) + && (change_leading_char + || (remove_leading_char + && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0 + || undefined + || bfd_is_com_section (bfd_get_section (sym))))); + + /* Check if we will add a new leading character. */ + add_leading_char = + change_leading_char + && (bfd_get_symbol_leading_char (obfd) != '\0') + && (bfd_get_symbol_leading_char (abfd) == '\0' + || (name[0] == bfd_get_symbol_leading_char (abfd))); + + /* Short circuit for change_leading_char if we can do it in-place. */ + if (rem_leading_char && add_leading_char && !prefix_symbols_string) + { + name[0] = bfd_get_symbol_leading_char (obfd); + bfd_asymbol_name (sym) = name; + rem_leading_char = FALSE; + add_leading_char = FALSE; + } + + /* Remove leading char. */ + if (rem_leading_char) + bfd_asymbol_name (sym) = ++name; + + /* Add new leading char and/or prefix. */ + if (add_leading_char || prefix_symbols_string) + { + char *n, *ptr; + + ptr = n = (char *) xmalloc (1 + strlen (prefix_symbols_string) + + strlen (name) + 1); + if (add_leading_char) + *ptr++ = bfd_get_symbol_leading_char (obfd); + + if (prefix_symbols_string) + { + strcpy (ptr, prefix_symbols_string); + ptr += strlen (prefix_symbols_string); + } + + strcpy (ptr, name); + bfd_asymbol_name (sym) = n; + name = n; + } + + if (strip_symbols == STRIP_ALL) + keep = FALSE; + else if ((flags & BSF_KEEP) != 0 /* Used in relocation. */ + || ((flags & BSF_SECTION_SYM) != 0 + && ((*bfd_get_section (sym)->symbol_ptr_ptr)->flags + & BSF_KEEP) != 0)) + { + keep = TRUE; + used_in_reloc = TRUE; + } + else if (relocatable /* Relocatable file. */ + && ((flags & (BSF_GLOBAL | BSF_WEAK)) != 0 + || bfd_is_com_section (bfd_get_section (sym)))) + keep = TRUE; + else if (bfd_decode_symclass (sym) == 'I') + /* Global symbols in $idata sections need to be retained + even if relocatable is FALSE. External users of the + library containing the $idata section may reference these + symbols. */ + keep = TRUE; + else if ((flags & BSF_GLOBAL) != 0 /* Global symbol. */ + || (flags & BSF_WEAK) != 0 + || undefined + || bfd_is_com_section (bfd_get_section (sym))) + keep = strip_symbols != STRIP_UNNEEDED; + else if ((flags & BSF_DEBUGGING) != 0) /* Debugging symbol. */ + keep = (strip_symbols != STRIP_DEBUG + && strip_symbols != STRIP_UNNEEDED + && ! convert_debugging); + else if (bfd_coff_get_comdat_section (abfd, bfd_get_section (sym))) + /* COMDAT sections store special information in local + symbols, so we cannot risk stripping any of them. */ + keep = TRUE; + else /* Local symbol. */ + keep = (strip_symbols != STRIP_UNNEEDED + && (discard_locals != LOCALS_ALL + && (discard_locals != LOCALS_START_L + || ! bfd_is_local_label (abfd, sym)))); + + if (keep && is_specified_symbol (name, strip_specific_htab)) + { + /* There are multiple ways to set 'keep' above, but if it + was the relocatable symbol case, then that's an error. */ + if (used_in_reloc) + { + non_fatal (_("not stripping symbol `%s' because it is named in a relocation"), name); + status = 1; + } + else + keep = FALSE; + } + + if (keep + && !(flags & BSF_KEEP) + && is_specified_symbol (name, strip_unneeded_htab)) + keep = FALSE; + + if (!keep + && ((keep_file_symbols && (flags & BSF_FILE)) + || is_specified_symbol (name, keep_specific_htab))) + keep = TRUE; + + if (keep && is_strip_section (abfd, bfd_get_section (sym))) + keep = FALSE; + + if (keep) + { + if ((flags & BSF_GLOBAL) != 0 + && (weaken || is_specified_symbol (name, weaken_specific_htab))) + { + sym->flags &= ~ BSF_GLOBAL; + sym->flags |= BSF_WEAK; + } + + if (!undefined + && (flags & (BSF_GLOBAL | BSF_WEAK)) + && (is_specified_symbol (name, localize_specific_htab) + || (htab_elements (keepglobal_specific_htab) != 0 + && ! is_specified_symbol (name, keepglobal_specific_htab)) + || (localize_hidden && is_hidden_symbol (sym)))) + { + sym->flags &= ~ (BSF_GLOBAL | BSF_WEAK); + sym->flags |= BSF_LOCAL; + } + + if (!undefined + && (flags & BSF_LOCAL) + && is_specified_symbol (name, globalize_specific_htab)) + { + sym->flags &= ~ BSF_LOCAL; + sym->flags |= BSF_GLOBAL; + } + + to[dst_count++] = sym; + } + } + + to[dst_count] = NULL; + + return dst_count; +} + +/* Find the redefined name of symbol SOURCE. */ + +static const char * +lookup_sym_redefinition (const char *source) +{ + struct redefine_node *list; + + for (list = redefine_sym_list; list != NULL; list = list->next) + if (strcmp (source, list->source) == 0) + return list->target; + + return source; +} + +/* Add a node to a symbol redefine list. */ + +static void +redefine_list_append (const char *cause, const char *source, const char *target) +{ + struct redefine_node **p; + struct redefine_node *list; + struct redefine_node *new_node; + + for (p = &redefine_sym_list; (list = *p) != NULL; p = &list->next) + { + if (strcmp (source, list->source) == 0) + fatal (_("%s: Multiple redefinition of symbol \"%s\""), + cause, source); + + if (strcmp (target, list->target) == 0) + fatal (_("%s: Symbol \"%s\" is target of more than one redefinition"), + cause, target); + } + + new_node = (struct redefine_node *) xmalloc (sizeof (struct redefine_node)); + + new_node->source = strdup (source); + new_node->target = strdup (target); + new_node->next = NULL; + + *p = new_node; +} + +/* Handle the --redefine-syms option. Read lines containing "old new" + from the file, and add them to the symbol redefine list. */ + +static void +add_redefine_syms_file (const char *filename) +{ + FILE *file; + char *buf; + size_t bufsize; + size_t len; + size_t outsym_off; + int c, lineno; + + file = fopen (filename, "r"); + if (file == NULL) + fatal (_("couldn't open symbol redefinition file %s (error: %s)"), + filename, strerror (errno)); + + bufsize = 100; + buf = (char *) xmalloc (bufsize + 1 /* For the terminating NUL. */); + + lineno = 1; + c = getc (file); + len = 0; + outsym_off = 0; + while (c != EOF) + { + /* Collect the input symbol name. */ + while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF) + { + if (c == '#') + goto comment; + buf[len++] = c; + if (len >= bufsize) + { + bufsize *= 2; + buf = (char *) xrealloc (buf, bufsize + 1); + } + c = getc (file); + } + buf[len++] = '\0'; + if (c == EOF) + break; + + /* Eat white space between the symbol names. */ + while (IS_WHITESPACE (c)) + c = getc (file); + if (c == '#' || IS_LINE_TERMINATOR (c)) + goto comment; + if (c == EOF) + break; + + /* Collect the output symbol name. */ + outsym_off = len; + while (! IS_WHITESPACE (c) && ! IS_LINE_TERMINATOR (c) && c != EOF) + { + if (c == '#') + goto comment; + buf[len++] = c; + if (len >= bufsize) + { + bufsize *= 2; + buf = (char *) xrealloc (buf, bufsize + 1); + } + c = getc (file); + } + buf[len++] = '\0'; + if (c == EOF) + break; + + /* Eat white space at end of line. */ + while (! IS_LINE_TERMINATOR(c) && c != EOF && IS_WHITESPACE (c)) + c = getc (file); + if (c == '#') + goto comment; + /* Handle \r\n. */ + if ((c == '\r' && (c = getc (file)) == '\n') + || c == '\n' || c == EOF) + { + end_of_line: + /* Append the redefinition to the list. */ + if (buf[0] != '\0') + redefine_list_append (filename, &buf[0], &buf[outsym_off]); + + lineno++; + len = 0; + outsym_off = 0; + if (c == EOF) + break; + c = getc (file); + continue; + } + else + fatal (_("%s:%d: garbage found at end of line"), filename, lineno); + comment: + if (len != 0 && (outsym_off == 0 || outsym_off == len)) + fatal (_("%s:%d: missing new symbol name"), filename, lineno); + buf[len++] = '\0'; + + /* Eat the rest of the line and finish it. */ + while (c != '\n' && c != EOF) + c = getc (file); + goto end_of_line; + } + + if (len != 0) + fatal (_("%s:%d: premature end of file"), filename, lineno); + + free (buf); +} + +/* Copy unkown object file IBFD onto OBFD. + Returns TRUE upon success, FALSE otherwise. */ + +static bfd_boolean +copy_unknown_object (bfd *ibfd, bfd *obfd) +{ + char *cbuf; + int tocopy; + long ncopied; + long size; + struct stat buf; + + if (bfd_stat_arch_elt (ibfd, &buf) != 0) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + return FALSE; + } + + size = buf.st_size; + if (size < 0) + { + non_fatal (_("stat returns negative size for `%s'"), + bfd_get_archive_filename (ibfd)); + return FALSE; + } + + if (bfd_seek (ibfd, (file_ptr) 0, SEEK_SET) != 0) + { + bfd_nonfatal (bfd_get_archive_filename (ibfd)); + return FALSE; + } + + if (verbose) + printf (_("copy from `%s' [unknown] to `%s' [unknown]\n"), + bfd_get_archive_filename (ibfd), bfd_get_filename (obfd)); + + cbuf = (char *) xmalloc (BUFSIZE); + ncopied = 0; + while (ncopied < size) + { + tocopy = size - ncopied; + if (tocopy > BUFSIZE) + tocopy = BUFSIZE; + + if (bfd_bread (cbuf, (bfd_size_type) tocopy, ibfd) + != (bfd_size_type) tocopy) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + free (cbuf); + return FALSE; + } + + if (bfd_bwrite (cbuf, (bfd_size_type) tocopy, obfd) + != (bfd_size_type) tocopy) + { + bfd_nonfatal_message (NULL, obfd, NULL, NULL); + free (cbuf); + return FALSE; + } + + ncopied += tocopy; + } + + /* We should at least to be able to read it back when copying an + unknown object in an archive. */ +// chmod (bfd_get_filename (obfd), buf.st_mode | S_IRUSR); + free (cbuf); + return TRUE; +} + +/* Copy object file IBFD onto OBFD. + Returns TRUE upon success, FALSE otherwise. */ + +static bfd_boolean +copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) +{ + bfd_vma start; + long symcount; + asection **osections = NULL; + asection *gnu_debuglink_section = NULL; + bfd_size_type *gaps = NULL; + bfd_size_type max_gap = 0; + long symsize; + void *dhandle; + enum bfd_architecture iarch; + unsigned int imach; + + if (ibfd->xvec->byteorder != obfd->xvec->byteorder + && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN + && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN) + fatal (_("Unable to change endianness of input file(s)")); + + if (!bfd_set_format (obfd, bfd_get_format (ibfd))) + { + bfd_nonfatal_message (NULL, obfd, NULL, NULL); + return FALSE; + } + + if (verbose) + printf (_("copy from `%s' [%s] to `%s' [%s]\n"), + bfd_get_archive_filename (ibfd), bfd_get_target (ibfd), + bfd_get_filename (obfd), bfd_get_target (obfd)); + + if (extract_symbol) + start = 0; + else + { + if (set_start_set) + start = set_start; + else + start = bfd_get_start_address (ibfd); + start += change_start; + } + + /* Neither the start address nor the flags + need to be set for a core file. */ + if (bfd_get_format (obfd) != bfd_core) + { + flagword flags; + + flags = bfd_get_file_flags (ibfd); + flags |= bfd_flags_to_set; + flags &= ~bfd_flags_to_clear; + flags &= bfd_applicable_file_flags (obfd); + + if (strip_symbols == STRIP_ALL) + flags &= ~HAS_RELOC; + + if (!bfd_set_start_address (obfd, start) + || !bfd_set_file_flags (obfd, flags)) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + return FALSE; + } + } + + /* Copy architecture of input file to output file. */ + iarch = bfd_get_arch (ibfd); + imach = bfd_get_mach (ibfd); + if (input_arch) + { + if (bfd_get_arch_info (ibfd) == NULL + || bfd_get_arch_info (ibfd)->arch == bfd_arch_unknown) + { + iarch = input_arch->arch; + imach = input_arch->mach; + } + else + non_fatal (_("Input file `%s' ignores binary architecture parameter."), + bfd_get_archive_filename (ibfd)); + } + if (!bfd_set_arch_mach (obfd, iarch, imach) + && (ibfd->target_defaulted + || bfd_get_arch (ibfd) != bfd_get_arch (obfd))) + { + if (bfd_get_arch (ibfd) == bfd_arch_unknown) + non_fatal (_("Unable to recognise the format of the input file `%s'"), + bfd_get_archive_filename (ibfd)); + else + non_fatal (_("Output file cannot represent architecture `%s'"), + bfd_printable_arch_mach (bfd_get_arch (ibfd), + bfd_get_mach (ibfd))); + return FALSE; + } + + if (!bfd_set_format (obfd, bfd_get_format (ibfd))) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + return FALSE; + } + + if (bfd_get_flavour (obfd) == bfd_target_coff_flavour + && bfd_pei_p (obfd)) + { + /* Set up PE parameters. */ + pe_data_type *pe = pe_data (obfd); + + /* Copy PE parameters before changing them. */ + if (ibfd->xvec->flavour == bfd_target_coff_flavour + && bfd_pei_p (ibfd)) + pe->pe_opthdr = pe_data (ibfd)->pe_opthdr; + + if (pe_file_alignment != (bfd_vma) -1) + pe->pe_opthdr.FileAlignment = pe_file_alignment; + else + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + + if (pe_heap_commit != (bfd_vma) -1) + pe->pe_opthdr.SizeOfHeapCommit = pe_heap_commit; + + if (pe_heap_reserve != (bfd_vma) -1) + pe->pe_opthdr.SizeOfHeapCommit = pe_heap_reserve; + + if (pe_image_base != (bfd_vma) -1) + pe->pe_opthdr.ImageBase = pe_image_base; + + if (pe_section_alignment != (bfd_vma) -1) + pe->pe_opthdr.SectionAlignment = pe_section_alignment; + else + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + + if (pe_stack_commit != (bfd_vma) -1) + pe->pe_opthdr.SizeOfStackCommit = pe_stack_commit; + + if (pe_stack_reserve != (bfd_vma) -1) + pe->pe_opthdr.SizeOfStackCommit = pe_stack_reserve; + + if (pe_subsystem != -1) + pe->pe_opthdr.Subsystem = pe_subsystem; + + if (pe_major_subsystem_version != -1) + pe->pe_opthdr.MajorSubsystemVersion = pe_major_subsystem_version; + + if (pe_minor_subsystem_version != -1) + pe->pe_opthdr.MinorSubsystemVersion = pe_minor_subsystem_version; + + if (pe_file_alignment > pe_section_alignment) + { + char file_alignment[20], section_alignment[20]; + + sprintf_vma (file_alignment, pe_file_alignment); + sprintf_vma (section_alignment, pe_section_alignment); + non_fatal (_("warning: file alignment (0x%s) > section alignment (0x%s)"), + + file_alignment, section_alignment); + } + } + + if (isympp) + free (isympp); + + if (osympp != isympp) + free (osympp); + + isympp = NULL; + osympp = NULL; + + symsize = bfd_get_symtab_upper_bound (ibfd); + if (symsize < 0) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + return FALSE; + } + + osympp = isympp = (asymbol **) xmalloc (symsize); + symcount = bfd_canonicalize_symtab (ibfd, isympp); + if (symcount < 0) + { + bfd_nonfatal_message (NULL, ibfd, NULL, NULL); + return FALSE; + } + + /* BFD mandates that all output sections be created and sizes set before + any output is done. Thus, we traverse all sections multiple times. */ + bfd_map_over_sections (ibfd, setup_section, obfd); + + if (!extract_symbol) + setup_bfd_headers (ibfd, obfd); + + if (add_sections != NULL) + { + struct section_add *padd; + struct section_list *pset; + + for (padd = add_sections; padd != NULL; padd = padd->next) + { + flagword flags; + + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_FLAGS); + if (pset != NULL) + flags = pset->flags | SEC_HAS_CONTENTS; + else + flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DATA; + + /* bfd_make_section_with_flags() does not return very helpful + error codes, so check for the most likely user error first. */ + if (bfd_get_section_by_name (obfd, padd->name)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't add section '%s'"), padd->name); + return FALSE; + } + else + { + /* We use LINKER_CREATED here so that the backend hooks + will create any special section type information, + instead of presuming we know what we're doing merely + because we set the flags. */ + padd->section = bfd_make_section_with_flags + (obfd, padd->name, flags | SEC_LINKER_CREATED); + if (padd->section == NULL) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't create section `%s'"), + padd->name); + return FALSE; + } + } + + if (! bfd_set_section_size (obfd, padd->section, padd->size)) + { + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_VMA | SECTION_CONTEXT_ALTER_VMA); + if (pset != NULL + && ! bfd_set_section_vma (obfd, padd->section, pset->vma_val)) + { + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + + pset = find_section_list (padd->name, FALSE, + SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_ALTER_LMA); + if (pset != NULL) + { + padd->section->lma = pset->lma_val; + + if (! bfd_set_section_alignment + (obfd, padd->section, + bfd_section_alignment (obfd, padd->section))) + { + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + } + } + } + + if (gnu_debuglink_filename != NULL) + { + /* PR 15125: Give a helpful warning message if + the debuglink section already exists, and + allow the rest of the copy to complete. */ + if (bfd_get_section_by_name (obfd, ".gnu_debuglink")) + { + non_fatal (_("%s: debuglink section already exists"), + bfd_get_filename (obfd)); + gnu_debuglink_filename = NULL; + } + else + { + gnu_debuglink_section = bfd_create_gnu_debuglink_section + (obfd, gnu_debuglink_filename); + + if (gnu_debuglink_section == NULL) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("cannot create debug link section `%s'"), + gnu_debuglink_filename); + return FALSE; + } + + /* Special processing for PE format files. We + have no way to distinguish PE from COFF here. */ + if (bfd_get_flavour (obfd) == bfd_target_coff_flavour) + { + bfd_vma debuglink_vma; + asection * highest_section; + asection * sec; + + /* The PE spec requires that all sections be adjacent and sorted + in ascending order of VMA. It also specifies that debug + sections should be last. This is despite the fact that debug + sections are not loaded into memory and so in theory have no + use for a VMA. + + This means that the debuglink section must be given a non-zero + VMA which makes it contiguous with other debug sections. So + walk the current section list, find the section with the + highest VMA and start the debuglink section after that one. */ + for (sec = obfd->sections, highest_section = NULL; + sec != NULL; + sec = sec->next) + if (sec->vma > 0 + && (highest_section == NULL + || sec->vma > highest_section->vma)) + highest_section = sec; + + if (highest_section) + debuglink_vma = BFD_ALIGN (highest_section->vma + + highest_section->size, + /* FIXME: We ought to be using + COFF_PAGE_SIZE here or maybe + bfd_get_section_alignment() (if it + was set) but since this is for PE + and we know the required alignment + it is easier just to hard code it. */ + 0x1000); + else + /* Umm, not sure what to do in this case. */ + debuglink_vma = 0x1000; + + bfd_set_section_vma (obfd, gnu_debuglink_section, debuglink_vma); + } + } + } + + if (bfd_count_sections (obfd) != 0 + && (gap_fill_set || pad_to_set)) + { + asection **set; + unsigned int c, i; + + /* We must fill in gaps between the sections and/or we must pad + the last section to a specified address. We do this by + grabbing a list of the sections, sorting them by VMA, and + increasing the section sizes as required to fill the gaps. + We write out the gap contents below. */ + + c = bfd_count_sections (obfd); + osections = (asection **) xmalloc (c * sizeof (asection *)); + set = osections; + bfd_map_over_sections (obfd, get_sections, &set); + + qsort (osections, c, sizeof (asection *), compare_section_lma); + + gaps = (bfd_size_type *) xmalloc (c * sizeof (bfd_size_type)); + memset (gaps, 0, c * sizeof (bfd_size_type)); + + if (gap_fill_set) + { + for (i = 0; i < c - 1; i++) + { + flagword flags; + bfd_size_type size; + bfd_vma gap_start, gap_stop; + + flags = bfd_get_section_flags (obfd, osections[i]); + if ((flags & SEC_HAS_CONTENTS) == 0 + || (flags & SEC_LOAD) == 0) + continue; + + size = bfd_section_size (obfd, osections[i]); + gap_start = bfd_section_lma (obfd, osections[i]) + size; + gap_stop = bfd_section_lma (obfd, osections[i + 1]); + if (gap_start < gap_stop) + { + if (! bfd_set_section_size (obfd, osections[i], + size + (gap_stop - gap_start))) + { + bfd_nonfatal_message (NULL, obfd, osections[i], + _("Can't fill gap after section")); + status = 1; + break; + } + gaps[i] = gap_stop - gap_start; + if (max_gap < gap_stop - gap_start) + max_gap = gap_stop - gap_start; + } + } + } + + if (pad_to_set) + { + bfd_vma lma; + bfd_size_type size; + + lma = bfd_section_lma (obfd, osections[c - 1]); + size = bfd_section_size (obfd, osections[c - 1]); + if (lma + size < pad_to) + { + if (! bfd_set_section_size (obfd, osections[c - 1], + pad_to - lma)) + { + bfd_nonfatal_message (NULL, obfd, osections[c - 1], + _("can't add padding")); + status = 1; + } + else + { + gaps[c - 1] = pad_to - (lma + size); + if (max_gap < pad_to - (lma + size)) + max_gap = pad_to - (lma + size); + } + } + } + } + + /* Symbol filtering must happen after the output sections + have been created, but before their contents are set. */ + dhandle = NULL; + if (convert_debugging) + dhandle = read_debugging_info (ibfd, isympp, symcount, FALSE); + + if (strip_symbols == STRIP_DEBUG + || strip_symbols == STRIP_ALL + || strip_symbols == STRIP_UNNEEDED + || strip_symbols == STRIP_NONDEBUG + || strip_symbols == STRIP_DWO + || strip_symbols == STRIP_NONDWO + || discard_locals != LOCALS_UNDEF + || localize_hidden + || htab_elements (strip_specific_htab) != 0 + || htab_elements (keep_specific_htab) != 0 + || htab_elements (localize_specific_htab) != 0 + || htab_elements (globalize_specific_htab) != 0 + || htab_elements (keepglobal_specific_htab) != 0 + || htab_elements (weaken_specific_htab) != 0 + || prefix_symbols_string + || sections_removed + || sections_copied + || convert_debugging + || change_leading_char + || remove_leading_char + || redefine_sym_list + || weaken) + { + /* Mark symbols used in output relocations so that they + are kept, even if they are local labels or static symbols. + + Note we iterate over the input sections examining their + relocations since the relocations for the output sections + haven't been set yet. mark_symbols_used_in_relocations will + ignore input sections which have no corresponding output + section. */ + if (strip_symbols != STRIP_ALL) + bfd_map_over_sections (ibfd, + mark_symbols_used_in_relocations, + isympp); + osympp = (asymbol **) xmalloc ((symcount + 1) * sizeof (asymbol *)); + symcount = filter_symbols (ibfd, obfd, osympp, isympp, symcount); + } + + if (convert_debugging && dhandle != NULL) + { + if (! write_debugging_info (obfd, dhandle, &symcount, &osympp)) + { + status = 1; + return FALSE; + } + } + + bfd_set_symtab (obfd, osympp, symcount); + + /* This has to happen before section positions are set. */ + bfd_map_over_sections (ibfd, copy_relocations_in_section, obfd); + + /* This has to happen after the symbol table has been set. */ + bfd_map_over_sections (ibfd, copy_section, obfd); + + if (add_sections != NULL) + { + struct section_add *padd; + + for (padd = add_sections; padd != NULL; padd = padd->next) + { + if (! bfd_set_section_contents (obfd, padd->section, padd->contents, + 0, padd->size)) + { + bfd_nonfatal_message (NULL, obfd, padd->section, NULL); + return FALSE; + } + } + } + + if (gnu_debuglink_filename != NULL) + { + if (! bfd_fill_in_gnu_debuglink_section + (obfd, gnu_debuglink_section, gnu_debuglink_filename)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("cannot fill debug link section `%s'"), + gnu_debuglink_filename); + return FALSE; + } + } + + if (gap_fill_set || pad_to_set) + { + bfd_byte *buf; + int c, i; + + /* Fill in the gaps. */ + if (max_gap > 8192) + max_gap = 8192; + buf = (bfd_byte *) xmalloc (max_gap); + memset (buf, gap_fill, max_gap); + + c = bfd_count_sections (obfd); + for (i = 0; i < c; i++) + { + if (gaps[i] != 0) + { + bfd_size_type left; + file_ptr off; + + left = gaps[i]; + off = bfd_section_size (obfd, osections[i]) - left; + + while (left > 0) + { + bfd_size_type now; + + if (left > 8192) + now = 8192; + else + now = left; + + if (! bfd_set_section_contents (obfd, osections[i], buf, + off, now)) + { + bfd_nonfatal_message (NULL, obfd, osections[i], NULL); + return FALSE; + } + + left -= now; + off += now; + } + } + } + } + + /* Do not copy backend data if --extract-symbol is passed; anything + that needs to look at the section contents will fail. */ + if (extract_symbol) + return TRUE; + + /* Allow the BFD backend to copy any private data it understands + from the input BFD to the output BFD. This is done last to + permit the routine to look at the filtered symbol table, which is + important for the ECOFF code at least. */ + if (! bfd_copy_private_bfd_data (ibfd, obfd)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("error copying private BFD data")); + return FALSE; + } + + /* Switch to the alternate machine code. We have to do this at the + very end, because we only initialize the header when we create + the first section. */ + if (use_alt_mach_code != 0) + { + if (! bfd_alt_mach_code (obfd, use_alt_mach_code)) + { + non_fatal (_("this target does not support %lu alternative machine codes"), + use_alt_mach_code); + if (bfd_get_flavour (obfd) == bfd_target_elf_flavour) + { + non_fatal (_("treating that number as an absolute e_machine value instead")); + elf_elfheader (obfd)->e_machine = use_alt_mach_code; + } + else + non_fatal (_("ignoring the alternative value")); + } + } + + return TRUE; +} + +/* Read each archive element in turn from IBFD, copy the + contents to temp file, and keep the temp file handle. + If 'force_output_target' is TRUE then make sure that + all elements in the new archive are of the type + 'output_target'. */ + +static void +copy_archive (bfd *ibfd, bfd *obfd, const char *output_target, + bfd_boolean force_output_target, + const bfd_arch_info_type *input_arch) +{ + struct name_list + { + struct name_list *next; + const char *name; + bfd *obfd; + } *list, *l; + bfd **ptr = &obfd->archive_head; + bfd *this_element; + char *dir; + const char *filename; + + /* Make a temp directory to hold the contents. */ + dir = make_tempdir (bfd_get_filename (obfd)); + if (dir == NULL) + fatal (_("cannot create tempdir for archive copying (error: %s)"), + strerror (errno)); + + if (strip_symbols == STRIP_ALL) + obfd->has_armap = FALSE; + else + obfd->has_armap = ibfd->has_armap; + obfd->is_thin_archive = ibfd->is_thin_archive; + + if (deterministic) + obfd->flags |= BFD_DETERMINISTIC_OUTPUT; + + list = NULL; + + this_element = bfd_openr_next_archived_file (ibfd, NULL); + + if (!bfd_set_format (obfd, bfd_get_format (ibfd))) + { + status = 1; + bfd_nonfatal_message (NULL, obfd, NULL, NULL); + return; + } + + while (!status && this_element != NULL) + { + char *output_name; + bfd *output_bfd; + bfd *last_element; + struct stat buf; + int stat_status = 0; + bfd_boolean del = TRUE; + bfd_boolean ok_object; + + /* Create an output file for this member. */ + output_name = concat (dir, "/", + bfd_get_filename (this_element), (char *) 0); + + /* If the file already exists, make another temp dir. */ + if (stat (output_name, &buf) >= 0) + { + output_name = make_tempdir (output_name); + if (output_name == NULL) + fatal (_("cannot create tempdir for archive copying (error: %s)"), + strerror (errno)); + + l = (struct name_list *) xmalloc (sizeof (struct name_list)); + l->name = output_name; + l->next = list; + l->obfd = NULL; + list = l; + output_name = concat (output_name, "/", + bfd_get_filename (this_element), (char *) 0); + } + + if (preserve_dates) + { + stat_status = bfd_stat_arch_elt (this_element, &buf); + + if (stat_status != 0) + non_fatal (_("internal stat error on %s"), + bfd_get_filename (this_element)); + } + + l = (struct name_list *) xmalloc (sizeof (struct name_list)); + l->name = output_name; + l->next = list; + l->obfd = NULL; + list = l; + + ok_object = bfd_check_format (this_element, bfd_object); + if (!ok_object) + bfd_nonfatal_message (NULL, this_element, NULL, + _("Unable to recognise the format of file")); + + /* PR binutils/3110: Cope with archives + containing multiple target types. */ + if (force_output_target || !ok_object) + output_bfd = bfd_openw (output_name, output_target); + else + output_bfd = bfd_openw (output_name, bfd_get_target (this_element)); + + if (output_bfd == NULL) + { + bfd_nonfatal_message (output_name, NULL, NULL, NULL); + status = 1; + return; + } + + if (ok_object) + { + del = !copy_object (this_element, output_bfd, input_arch); + + if (del && bfd_get_arch (this_element) == bfd_arch_unknown) + /* Try again as an unknown object file. */ + ok_object = FALSE; + else if (!bfd_close (output_bfd)) + { + bfd_nonfatal_message (output_name, NULL, NULL, NULL); + /* Error in new object file. Don't change archive. */ + status = 1; + } + } + + if (!ok_object) + { + del = !copy_unknown_object (this_element, output_bfd); + if (!bfd_close_all_done (output_bfd)) + { + bfd_nonfatal_message (output_name, NULL, NULL, NULL); + /* Error in new object file. Don't change archive. */ + status = 1; + } + } + + if (del) + { + unlink (output_name); + status = 1; + } + else + { + if (preserve_dates && stat_status == 0) + set_times (output_name, &buf); + + /* Open the newly output file and attach to our list. */ + output_bfd = bfd_openr (output_name, output_target); + + l->obfd = output_bfd; + + *ptr = output_bfd; + ptr = &output_bfd->archive_next; + + last_element = this_element; + + this_element = bfd_openr_next_archived_file (ibfd, last_element); + + bfd_close (last_element); + } + } + *ptr = NULL; + + filename = bfd_get_filename (obfd); + if (!bfd_close (obfd)) + { + status = 1; + bfd_nonfatal_message (filename, NULL, NULL, NULL); + return; + } + + filename = bfd_get_filename (ibfd); + if (!bfd_close (ibfd)) + { + status = 1; + bfd_nonfatal_message (filename, NULL, NULL, NULL); + return; + } + + /* Delete all the files that we opened. */ +#if 0 + for (l = list; l != NULL; l = l->next) + { + if (l->obfd == NULL) + rmdir (l->name); + else + { + bfd_close (l->obfd); + unlink (l->name); + } + } + rmdir (dir); +#endif + +} + +static void +set_long_section_mode (bfd *output_bfd, bfd *input_bfd, enum long_section_name_handling style) +{ + /* This is only relevant to Coff targets. */ + if (bfd_get_flavour (output_bfd) == bfd_target_coff_flavour) + { + if (style == KEEP + && bfd_get_flavour (input_bfd) == bfd_target_coff_flavour) + style = bfd_coff_long_section_names (input_bfd) ? ENABLE : DISABLE; + bfd_coff_set_long_section_names (output_bfd, style != DISABLE); + } +} + +/* The top-level control. */ + +static void +copy_file (const char *input_filename, const char *output_filename, + const char *input_target, const char *output_target, + const bfd_arch_info_type *input_arch) +{ + bfd *ibfd; + char **obj_matching; + char **core_matching; + off_t size = get_file_size (input_filename); + + if (size < 1) + { + if (size == 0) + non_fatal (_("error: the input file '%s' is empty"), + input_filename); + status = 1; + return; + } + + /* To allow us to do "strip *" without dying on the first + non-object file, failures are nonfatal. */ + ibfd = bfd_openr (input_filename, input_target); + if (ibfd == NULL) + { + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + status = 1; + return; + } + + switch (do_debug_sections) + { + case compress: + ibfd->flags |= BFD_COMPRESS; + break; + case decompress: + ibfd->flags |= BFD_DECOMPRESS; + break; + default: + break; + } + + if (bfd_check_format (ibfd, bfd_archive)) + { + bfd_boolean force_output_target; + bfd *obfd; + + /* bfd_get_target does not return the correct value until + bfd_check_format succeeds. */ + if (output_target == NULL) + { + output_target = bfd_get_target (ibfd); + force_output_target = FALSE; + } + else + force_output_target = TRUE; + + obfd = bfd_openw (output_filename, output_target); + if (obfd == NULL) + { + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; + } + /* This is a no-op on non-Coff targets. */ + set_long_section_mode (obfd, ibfd, long_section_names); + + copy_archive (ibfd, obfd, output_target, force_output_target, input_arch); + } + else if (bfd_check_format_matches (ibfd, bfd_object, &obj_matching)) + { + bfd *obfd; + do_copy: + + /* bfd_get_target does not return the correct value until + bfd_check_format succeeds. */ + if (output_target == NULL) + output_target = bfd_get_target (ibfd); + + obfd = bfd_openw (output_filename, output_target); + if (obfd == NULL) + { + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; + } + /* This is a no-op on non-Coff targets. */ + set_long_section_mode (obfd, ibfd, long_section_names); + + if (! copy_object (ibfd, obfd, input_arch)) + status = 1; + + if (!bfd_close (obfd)) + { + status = 1; + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + return; + } + + if (!bfd_close (ibfd)) + { + status = 1; + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + return; + } + } + else + { + bfd_error_type obj_error = bfd_get_error (); + bfd_error_type core_error; + + if (bfd_check_format_matches (ibfd, bfd_core, &core_matching)) + { + /* This probably can't happen.. */ + if (obj_error == bfd_error_file_ambiguously_recognized) + free (obj_matching); + goto do_copy; + } + + core_error = bfd_get_error (); + /* Report the object error in preference to the core error. */ + if (obj_error != core_error) + bfd_set_error (obj_error); + + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + + if (obj_error == bfd_error_file_ambiguously_recognized) + { + list_matching_formats (obj_matching); + free (obj_matching); + } + if (core_error == bfd_error_file_ambiguously_recognized) + { + list_matching_formats (core_matching); + free (core_matching); + } + + status = 1; + } +} + +/* Add a name to the section renaming list. */ + +static void +add_section_rename (const char * old_name, const char * new_name, + flagword flags) +{ + section_rename * srename; + + /* Check for conflicts first. */ + for (srename = section_rename_list; srename != NULL; srename = srename->next) + if (strcmp (srename->old_name, old_name) == 0) + { + /* Silently ignore duplicate definitions. */ + if (strcmp (srename->new_name, new_name) == 0 + && srename->flags == flags) + return; + + fatal (_("Multiple renames of section %s"), old_name); + } + + srename = (section_rename *) xmalloc (sizeof (* srename)); + + srename->old_name = old_name; + srename->new_name = new_name; + srename->flags = flags; + srename->next = section_rename_list; + + section_rename_list = srename; +} + +/* Check the section rename list for a new name of the input section + ISECTION. Return the new name if one is found. + Also set RETURNED_FLAGS to the flags to be used for this section. */ + +static const char * +find_section_rename (bfd * ibfd ATTRIBUTE_UNUSED, sec_ptr isection, + flagword * returned_flags) +{ + const char * old_name = bfd_section_name (ibfd, isection); + section_rename * srename; + + /* Default to using the flags of the input section. */ + * returned_flags = bfd_get_section_flags (ibfd, isection); + + for (srename = section_rename_list; srename != NULL; srename = srename->next) + if (strcmp (srename->old_name, old_name) == 0) + { + if (srename->flags != (flagword) -1) + * returned_flags = srename->flags; + + return srename->new_name; + } + + return old_name; +} + +/* Once each of the sections is copied, we may still need to do some + finalization work for private section headers. Do that here. */ + +static void +setup_bfd_headers (bfd *ibfd, bfd *obfd) +{ + /* Allow the BFD backend to copy any private data it understands + from the input section to the output section. */ + if (! bfd_copy_private_header_data (ibfd, obfd)) + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, NULL, + _("error in private header data")); + return; + } + + /* All went well. */ + return; +} + +/* Create a section in OBFD with the same + name and attributes as ISECTION in IBFD. */ + +static void +setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +{ + bfd *obfd = (bfd *) obfdarg; + struct section_list *p; + sec_ptr osection; + bfd_size_type size; + bfd_vma vma; + bfd_vma lma; + flagword flags; + const char *err; + const char * name; + char *prefix = NULL; + bfd_boolean make_nobits; + + if (is_strip_section (ibfd, isection)) + return; + + /* Get the, possibly new, name of the output section. */ + name = find_section_rename (ibfd, isection, & flags); + + /* Prefix sections. */ + if ((prefix_alloc_sections_string) + && (bfd_get_section_flags (ibfd, isection) & SEC_ALLOC)) + prefix = prefix_alloc_sections_string; + else if (prefix_sections_string) + prefix = prefix_sections_string; + + if (prefix) + { + char *n; + + n = (char *) xmalloc (strlen (prefix) + strlen (name) + 1); + strcpy (n, prefix); + strcat (n, name); + name = n; + } + + make_nobits = FALSE; + + p = find_section_list (bfd_section_name (ibfd, isection), FALSE, + SECTION_CONTEXT_SET_FLAGS); + if (p != NULL) + flags = p->flags | (flags & (SEC_HAS_CONTENTS | SEC_RELOC)); + else if (strip_symbols == STRIP_NONDEBUG + && (flags & (SEC_ALLOC | SEC_GROUP)) != 0 + && !(ibfd->xvec->flavour == bfd_target_elf_flavour + && elf_section_type (isection) == SHT_NOTE)) + { + flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD | SEC_GROUP); + if (obfd->xvec->flavour == bfd_target_elf_flavour) + { + make_nobits = TRUE; + + /* Twiddle the input section flags so that it seems to + elf.c:copy_private_bfd_data that section flags have not + changed between input and output sections. This hack + prevents wholesale rewriting of the program headers. */ + isection->flags &= ~(SEC_HAS_CONTENTS | SEC_LOAD | SEC_GROUP); + } + } + + osection = bfd_make_section_anyway_with_flags (obfd, name, flags); + + if (osection == NULL) + { + err = _("failed to create output section"); + goto loser; + } + + if (make_nobits) + elf_section_type (osection) = SHT_NOBITS; + + size = bfd_section_size (ibfd, isection); + if (copy_byte >= 0) + size = (size + interleave - 1) / interleave * copy_width; + else if (extract_symbol) + size = 0; + if (! bfd_set_section_size (obfd, osection, size)) + { + err = _("failed to set size"); + goto loser; + } + + vma = bfd_section_vma (ibfd, isection); + p = find_section_list (bfd_section_name (ibfd, isection), FALSE, + SECTION_CONTEXT_ALTER_VMA | SECTION_CONTEXT_SET_VMA); + if (p != NULL) + { + if (p->context & SECTION_CONTEXT_SET_VMA) + vma = p->vma_val; + else + vma += p->vma_val; + } + else + vma += change_section_address; + + if (! bfd_set_section_vma (obfd, osection, vma)) + { + err = _("failed to set vma"); + goto loser; + } + + lma = isection->lma; + p = find_section_list (bfd_section_name (ibfd, isection), FALSE, + SECTION_CONTEXT_ALTER_LMA | SECTION_CONTEXT_SET_LMA); + if (p != NULL) + { + if (p->context & SECTION_CONTEXT_ALTER_LMA) + lma += p->lma_val; + else + lma = p->lma_val; + } + else + lma += change_section_address; + + osection->lma = lma; + + /* FIXME: This is probably not enough. If we change the LMA we + may have to recompute the header for the file as well. */ + if (!bfd_set_section_alignment (obfd, + osection, + bfd_section_alignment (ibfd, isection))) + { + err = _("failed to set alignment"); + goto loser; + } + + /* Copy merge entity size. */ + osection->entsize = isection->entsize; + + /* This used to be mangle_section; we do here to avoid using + bfd_get_section_by_name since some formats allow multiple + sections with the same name. */ + isection->output_section = osection; + isection->output_offset = 0; + + /* Do not copy backend data if --extract-symbol is passed; anything + that needs to look at the section contents will fail. */ + if (extract_symbol) + return; + + if ((isection->flags & SEC_GROUP) != 0) + { + asymbol *gsym = group_signature (isection); + + if (gsym != NULL) + { + gsym->flags |= BSF_KEEP; + if (ibfd->xvec->flavour == bfd_target_elf_flavour) + elf_group_id (isection) = gsym; + } + } + + /* Allow the BFD backend to copy any private data it understands + from the input section to the output section. */ + if (!bfd_copy_private_section_data (ibfd, isection, obfd, osection)) + { + err = _("failed to copy private data"); + goto loser; + } + + /* All went well. */ + return; + +loser: + status = 1; + bfd_nonfatal_message (NULL, obfd, osection, err); +} + +/* Return TRUE if input section ISECTION should be skipped. */ + +static bfd_boolean +skip_section (bfd *ibfd, sec_ptr isection) +{ + sec_ptr osection; + bfd_size_type size; + flagword flags; + + /* If we have already failed earlier on, + do not keep on generating complaints now. */ + if (status != 0) + return TRUE; + + if (extract_symbol) + return TRUE; + + if (is_strip_section (ibfd, isection)) + return TRUE; + + flags = bfd_get_section_flags (ibfd, isection); + if ((flags & SEC_GROUP) != 0) + return TRUE; + + osection = isection->output_section; + size = bfd_get_section_size (isection); + + if (size == 0 || osection == 0) + return TRUE; + + return FALSE; +} + +/* Copy relocations in input section ISECTION of IBFD to an output + section with the same name in OBFDARG. If stripping then don't + copy any relocation info. */ + +static void +copy_relocations_in_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +{ + bfd *obfd = (bfd *) obfdarg; + long relsize; + arelent **relpp; + long relcount; + sec_ptr osection; + + if (skip_section (ibfd, isection)) + return; + + osection = isection->output_section; + + /* Core files and DWO files do not need to be relocated. */ + if (bfd_get_format (obfd) == bfd_core || strip_symbols == STRIP_NONDWO) + relsize = 0; + else + { + relsize = bfd_get_reloc_upper_bound (ibfd, isection); + + if (relsize < 0) + { + /* Do not complain if the target does not support relocations. */ + if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation) + relsize = 0; + else + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, NULL); + return; + } + } + } + + if (relsize == 0) + { + bfd_set_reloc (obfd, osection, NULL, 0); + osection->flags &= ~SEC_RELOC; + } + else + { + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp); + if (relcount < 0) + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, + _("relocation count is negative")); + return; + } + + if (strip_symbols == STRIP_ALL) + { + /* Remove relocations which are not in + keep_strip_specific_list. */ + arelent **temp_relpp; + long temp_relcount = 0; + long i; + + temp_relpp = (arelent **) xmalloc (relsize); + for (i = 0; i < relcount; i++) + if (is_specified_symbol (bfd_asymbol_name (*relpp[i]->sym_ptr_ptr), + keep_specific_htab)) + temp_relpp [temp_relcount++] = relpp [i]; + relcount = temp_relcount; + free (relpp); + relpp = temp_relpp; + } + + bfd_set_reloc (obfd, osection, relcount == 0 ? NULL : relpp, relcount); + if (relcount == 0) + { + osection->flags &= ~SEC_RELOC; + free (relpp); + } + } +} + +/* Copy the data of input section ISECTION of IBFD + to an output section with the same name in OBFD. */ + +static void +copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) +{ + bfd *obfd = (bfd *) obfdarg; + struct section_list *p; + sec_ptr osection; + bfd_size_type size; + + if (skip_section (ibfd, isection)) + return; + + osection = isection->output_section; + size = bfd_get_section_size (isection); + + if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS + && bfd_get_section_flags (obfd, osection) & SEC_HAS_CONTENTS) + { + bfd_byte *memhunk = NULL; + + if (!bfd_get_full_section_contents (ibfd, isection, &memhunk)) + { + status = 1; + bfd_nonfatal_message (NULL, ibfd, isection, NULL); + return; + } + + if (reverse_bytes) + { + /* We don't handle leftover bytes (too many possible behaviors, + and we don't know what the user wants). The section length + must be a multiple of the number of bytes to swap. */ + if ((size % reverse_bytes) == 0) + { + unsigned long i, j; + bfd_byte b; + + for (i = 0; i < size; i += reverse_bytes) + for (j = 0; j < (unsigned long)(reverse_bytes / 2); j++) + { + bfd_byte *m = (bfd_byte *) memhunk; + + b = m[i + j]; + m[i + j] = m[(i + reverse_bytes) - (j + 1)]; + m[(i + reverse_bytes) - (j + 1)] = b; + } + } + else + /* User must pad the section up in order to do this. */ + fatal (_("cannot reverse bytes: length of section %s must be evenly divisible by %d"), + bfd_section_name (ibfd, isection), reverse_bytes); + } + + if (copy_byte >= 0) + { + /* Keep only every `copy_byte'th byte in MEMHUNK. */ + char *from = (char *) memhunk + copy_byte; + char *to = (char *) memhunk; + char *end = (char *) memhunk + size; + int i; + + for (; from < end; from += interleave) + for (i = 0; i < copy_width; i++) + { + if (&from[i] >= end) + break; + *to++ = from[i]; + } + + size = (size + interleave - 1 - copy_byte) / interleave * copy_width; + osection->lma /= interleave; + } + + if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size)) + { + status = 1; + bfd_nonfatal_message (NULL, obfd, osection, NULL); + return; + } + free (memhunk); + } + else if ((p = find_section_list (bfd_get_section_name (ibfd, isection), + FALSE, SECTION_CONTEXT_SET_FLAGS)) != NULL + && (p->flags & SEC_HAS_CONTENTS) != 0) + { + void *memhunk = xmalloc (size); + + /* We don't permit the user to turn off the SEC_HAS_CONTENTS + flag--they can just remove the section entirely and add it + back again. However, we do permit them to turn on the + SEC_HAS_CONTENTS flag, and take it to mean that the section + contents should be zeroed out. */ + + memset (memhunk, 0, size); + if (! bfd_set_section_contents (obfd, osection, memhunk, 0, size)) + { + status = 1; + bfd_nonfatal_message (NULL, obfd, osection, NULL); + return; + } + free (memhunk); + } +} + +/* Get all the sections. This is used when --gap-fill or --pad-to is + used. */ + +static void +get_sections (bfd *obfd ATTRIBUTE_UNUSED, asection *osection, void *secppparg) +{ + asection ***secppp = (asection ***) secppparg; + + **secppp = osection; + ++(*secppp); +} + +/* Sort sections by VMA. This is called via qsort, and is used when + --gap-fill or --pad-to is used. We force non loadable or empty + sections to the front, where they are easier to ignore. */ + +static int +compare_section_lma (const void *arg1, const void *arg2) +{ + const asection *const *sec1 = (const asection * const *) arg1; + const asection *const *sec2 = (const asection * const *) arg2; + flagword flags1, flags2; + + /* Sort non loadable sections to the front. */ + flags1 = (*sec1)->flags; + flags2 = (*sec2)->flags; + if ((flags1 & SEC_HAS_CONTENTS) == 0 + || (flags1 & SEC_LOAD) == 0) + { + if ((flags2 & SEC_HAS_CONTENTS) != 0 + && (flags2 & SEC_LOAD) != 0) + return -1; + } + else + { + if ((flags2 & SEC_HAS_CONTENTS) == 0 + || (flags2 & SEC_LOAD) == 0) + return 1; + } + + /* Sort sections by LMA. */ + if ((*sec1)->lma > (*sec2)->lma) + return 1; + else if ((*sec1)->lma < (*sec2)->lma) + return -1; + + /* Sort sections with the same LMA by size. */ + if (bfd_get_section_size (*sec1) > bfd_get_section_size (*sec2)) + return 1; + else if (bfd_get_section_size (*sec1) < bfd_get_section_size (*sec2)) + return -1; + + return 0; +} + +/* Mark all the symbols which will be used in output relocations with + the BSF_KEEP flag so that those symbols will not be stripped. + + Ignore relocations which will not appear in the output file. */ + +static void +mark_symbols_used_in_relocations (bfd *ibfd, sec_ptr isection, void *symbolsarg) +{ + asymbol **symbols = (asymbol **) symbolsarg; + long relsize; + arelent **relpp; + long relcount, i; + + /* Ignore an input section with no corresponding output section. */ + if (isection->output_section == NULL) + return; + + relsize = bfd_get_reloc_upper_bound (ibfd, isection); + if (relsize < 0) + { + /* Do not complain if the target does not support relocations. */ + if (relsize == -1 && bfd_get_error () == bfd_error_invalid_operation) + return; + bfd_fatal (bfd_get_filename (ibfd)); + } + + if (relsize == 0) + return; + + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, symbols); + if (relcount < 0) + bfd_fatal (bfd_get_filename (ibfd)); + + /* Examine each symbol used in a relocation. If it's not one of the + special bfd section symbols, then mark it with BSF_KEEP. */ + for (i = 0; i < relcount; i++) + { + if (*relpp[i]->sym_ptr_ptr != bfd_com_section_ptr->symbol + && *relpp[i]->sym_ptr_ptr != bfd_abs_section_ptr->symbol + && *relpp[i]->sym_ptr_ptr != bfd_und_section_ptr->symbol) + (*relpp[i]->sym_ptr_ptr)->flags |= BSF_KEEP; + } + + if (relpp != NULL) + free (relpp); +} + +/* Write out debugging information. */ + +static bfd_boolean +write_debugging_info (bfd *obfd, void *dhandle, + long *symcountp ATTRIBUTE_UNUSED, + asymbol ***symppp ATTRIBUTE_UNUSED) +{ + if (bfd_get_flavour (obfd) == bfd_target_ieee_flavour) + return write_ieee_debugging_info (obfd, dhandle); + + if (bfd_get_flavour (obfd) == bfd_target_coff_flavour + || bfd_get_flavour (obfd) == bfd_target_elf_flavour) + { + bfd_byte *syms, *strings; + bfd_size_type symsize, stringsize; + asection *stabsec, *stabstrsec; + flagword flags; + + if (! write_stabs_in_sections_debugging_info (obfd, dhandle, &syms, + &symsize, &strings, + &stringsize)) + return FALSE; + + flags = SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING; + stabsec = bfd_make_section_with_flags (obfd, ".stab", flags); + stabstrsec = bfd_make_section_with_flags (obfd, ".stabstr", flags); + if (stabsec == NULL + || stabstrsec == NULL + || ! bfd_set_section_size (obfd, stabsec, symsize) + || ! bfd_set_section_size (obfd, stabstrsec, stringsize) + || ! bfd_set_section_alignment (obfd, stabsec, 2) + || ! bfd_set_section_alignment (obfd, stabstrsec, 0)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't create debugging section")); + return FALSE; + } + + /* We can get away with setting the section contents now because + the next thing the caller is going to do is copy over the + real sections. We may someday have to split the contents + setting out of this function. */ + if (! bfd_set_section_contents (obfd, stabsec, syms, 0, symsize) + || ! bfd_set_section_contents (obfd, stabstrsec, strings, 0, + stringsize)) + { + bfd_nonfatal_message (NULL, obfd, NULL, + _("can't set debugging section contents")); + return FALSE; + } + + return TRUE; + } + + bfd_nonfatal_message (NULL, obfd, NULL, + _("don't know how to write debugging information for %s"), + bfd_get_target (obfd)); + return FALSE; +} + +/* If neither -D nor -U was specified explicitly, + then use the configured default. */ +static void +default_deterministic (void) +{ + if (deterministic < 0) + deterministic = DEFAULT_AR_DETERMINISTIC; +} + +static int +strip_main (int argc, char *argv[]) +{ + char *input_target = NULL; + char *output_target = NULL; + bfd_boolean show_version = FALSE; + bfd_boolean formats_info = FALSE; + int c; + int i; + char *output_file = NULL; + + while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVvw", + strip_options, (int *) 0)) != EOF) + { + switch (c) + { + case 'I': + input_target = optarg; + break; + case 'O': + output_target = optarg; + break; + case 'F': + input_target = output_target = optarg; + break; + case 'R': + find_section_list (optarg, TRUE, SECTION_CONTEXT_REMOVE); + sections_removed = TRUE; + break; + case 's': + strip_symbols = STRIP_ALL; + break; + case 'S': + case 'g': + case 'd': /* Historic BSD alias for -g. Used by early NetBSD. */ + strip_symbols = STRIP_DEBUG; + break; + case OPTION_STRIP_DWO: + strip_symbols = STRIP_DWO; + break; + case OPTION_STRIP_UNNEEDED: + strip_symbols = STRIP_UNNEEDED; + break; + case 'K': + add_specific_symbol (optarg, keep_specific_htab); + break; + case 'N': + add_specific_symbol (optarg, strip_specific_htab); + break; + case 'o': + output_file = optarg; + break; + case 'p': + preserve_dates = TRUE; + break; + case 'D': + deterministic = TRUE; + break; + case 'U': + deterministic = FALSE; + break; + case 'x': + discard_locals = LOCALS_ALL; + break; + case 'X': + discard_locals = LOCALS_START_L; + break; + case 'v': + verbose = TRUE; + break; + case 'V': + show_version = TRUE; + break; + case OPTION_FORMATS_INFO: + formats_info = TRUE; + break; + case OPTION_ONLY_KEEP_DEBUG: + strip_symbols = STRIP_NONDEBUG; + break; + case OPTION_KEEP_FILE_SYMBOLS: + keep_file_symbols = 1; + break; + case 0: + /* We've been given a long option. */ + break; + case 'w': + wildcard = TRUE; + break; + case 'H': + case 'h': + strip_usage (stdout, 0); + default: + strip_usage (stderr, 1); + } + } + + if (formats_info) + { + display_info (); + return 0; + } + + if (show_version) + print_version ("strip"); + + default_deterministic (); + + /* Default is to strip all symbols. */ + if (strip_symbols == STRIP_UNDEF + && discard_locals == LOCALS_UNDEF + && htab_elements (strip_specific_htab) == 0) + strip_symbols = STRIP_ALL; + + if (output_target == NULL) + output_target = input_target; + + i = optind; + if (i == argc + || (output_file != NULL && (i + 1) < argc)) + strip_usage (stderr, 1); + + for (; i < argc; i++) + { + int hold_status = status; + struct stat statbuf; + char *tmpname; + + if (get_file_size (argv[i]) < 1) + { + status = 1; + continue; + } + + if (preserve_dates) + /* No need to check the return value of stat(). + It has already been checked in get_file_size(). */ + stat (argv[i], &statbuf); + + if (output_file == NULL + || filename_cmp (argv[i], output_file) == 0) + tmpname = make_tempname (argv[i]); + else + tmpname = output_file; + + if (tmpname == NULL) + { + bfd_nonfatal_message (argv[i], NULL, NULL, + _("could not create temporary file to hold stripped copy")); + status = 1; + continue; + } + + status = 0; + copy_file (argv[i], tmpname, input_target, output_target, NULL); + if (status == 0) + { + if (preserve_dates) + set_times (tmpname, &statbuf); + if (output_file != tmpname) + status = (smart_rename (tmpname, + output_file ? output_file : argv[i], + preserve_dates) != 0); + if (status == 0) + status = hold_status; + } + else + unlink_if_ordinary (tmpname); + if (output_file != tmpname) + free (tmpname); + } + + return status; +} + +/* Set up PE subsystem. */ + +static void +set_pe_subsystem (const char *s) +{ + const char *version, *subsystem; + size_t i; + static const struct + { + const char *name; + const char set_def; + const short value; + } + v[] = + { + { "native", 0, IMAGE_SUBSYSTEM_NATIVE }, + { "windows", 0, IMAGE_SUBSYSTEM_WINDOWS_GUI }, + { "console", 0, IMAGE_SUBSYSTEM_WINDOWS_CUI }, + { "posix", 0, IMAGE_SUBSYSTEM_POSIX_CUI }, + { "wince", 0, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI }, + { "efi-app", 1, IMAGE_SUBSYSTEM_EFI_APPLICATION }, + { "efi-bsd", 1, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER }, + { "efi-rtd", 1, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER }, + { "sal-rtd", 1, IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER }, + { "xbox", 0, IMAGE_SUBSYSTEM_XBOX } + }; + short value; + char *copy; + int set_def = -1; + + /* Check for the presence of a version number. */ + version = strchr (s, ':'); + if (version == NULL) + subsystem = s; + else + { + int len = version - s; + copy = xstrdup (s); + subsystem = copy; + copy[len] = '\0'; + version = copy + 1 + len; + pe_major_subsystem_version = strtoul (version, ©, 0); + if (*copy == '.') + pe_minor_subsystem_version = strtoul (copy + 1, ©, 0); + if (*copy != '\0') + non_fatal (_("%s: bad version in PE subsystem"), s); + } + + /* Check for numeric subsystem. */ + value = (short) strtol (subsystem, ©, 0); + if (*copy == '\0') + { + for (i = 0; i < ARRAY_SIZE (v); i++) + if (v[i].value == value) + { + pe_subsystem = value; + set_def = v[i].set_def; + break; + } + } + else + { + /* Search for subsystem by name. */ + for (i = 0; i < ARRAY_SIZE (v); i++) + if (strcmp (subsystem, v[i].name) == 0) + { + pe_subsystem = v[i].value; + set_def = v[i].set_def; + break; + } + } + + switch (set_def) + { + case -1: + fatal (_("unknown PE subsystem: %s"), s); + break; + case 0: + break; + default: + if (pe_file_alignment == (bfd_vma) -1) + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + if (pe_section_alignment == (bfd_vma) -1) + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + break; + } + if (s != subsystem) + free ((char *) subsystem); +} + +/* Convert EFI target to PEI target. */ + +static void +convert_efi_target (char *efi) +{ + efi[0] = 'p'; + efi[1] = 'e'; + efi[2] = 'i'; + + if (strcmp (efi + 4, "ia32") == 0) + { + /* Change ia32 to i386. */ + efi[5]= '3'; + efi[6]= '8'; + efi[7]= '6'; + } + else if (strcmp (efi + 4, "x86_64") == 0) + { + /* Change x86_64 to x86-64. */ + efi[7] = '-'; + } +} + +static int +copy_main (int argc, char *argv[]) +{ + char *input_filename = NULL; + char *output_filename = NULL; + char *tmpname; + char *input_target = NULL; + char *output_target = NULL; + bfd_boolean show_version = FALSE; + bfd_boolean change_warn = TRUE; + bfd_boolean formats_info = FALSE; + int c; + struct stat statbuf; + const bfd_arch_info_type *input_arch = NULL; + + while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:N:s:O:d:F:L:G:R:SpgxXHhVvW:w", + copy_options, (int *) 0)) != EOF) + { + switch (c) + { + case 'b': + copy_byte = atoi (optarg); + if (copy_byte < 0) + fatal (_("byte number must be non-negative")); + break; + + case 'B': + input_arch = bfd_scan_arch (optarg); + if (input_arch == NULL) + fatal (_("architecture %s unknown"), optarg); + break; + + case 'i': + if (optarg) + { + interleave = atoi (optarg); + if (interleave < 1) + fatal (_("interleave must be positive")); + } + else + interleave = 4; + break; + + case OPTION_INTERLEAVE_WIDTH: + copy_width = atoi (optarg); + if (copy_width < 1) + fatal(_("interleave width must be positive")); + break; + + case 'I': + case 's': /* "source" - 'I' is preferred */ + input_target = optarg; + break; + + case 'O': + case 'd': /* "destination" - 'O' is preferred */ + output_target = optarg; + break; + + case 'F': + input_target = output_target = optarg; + break; + + case 'j': + find_section_list (optarg, TRUE, SECTION_CONTEXT_COPY); + sections_copied = TRUE; + break; + + case 'R': + find_section_list (optarg, TRUE, SECTION_CONTEXT_REMOVE); + sections_removed = TRUE; + break; + + case 'S': + strip_symbols = STRIP_ALL; + break; + + case 'g': + strip_symbols = STRIP_DEBUG; + break; + + case OPTION_STRIP_DWO: + strip_symbols = STRIP_DWO; + break; + + case OPTION_STRIP_UNNEEDED: + strip_symbols = STRIP_UNNEEDED; + break; + + case OPTION_ONLY_KEEP_DEBUG: + strip_symbols = STRIP_NONDEBUG; + break; + + case OPTION_KEEP_FILE_SYMBOLS: + keep_file_symbols = 1; + break; + + case OPTION_ADD_GNU_DEBUGLINK: + long_section_names = ENABLE ; + gnu_debuglink_filename = optarg; + break; + + case 'K': + add_specific_symbol (optarg, keep_specific_htab); + break; + + case 'N': + add_specific_symbol (optarg, strip_specific_htab); + break; + + case OPTION_STRIP_UNNEEDED_SYMBOL: + add_specific_symbol (optarg, strip_unneeded_htab); + break; + + case 'L': + add_specific_symbol (optarg, localize_specific_htab); + break; + + case OPTION_GLOBALIZE_SYMBOL: + add_specific_symbol (optarg, globalize_specific_htab); + break; + + case 'G': + add_specific_symbol (optarg, keepglobal_specific_htab); + break; + + case 'W': + add_specific_symbol (optarg, weaken_specific_htab); + break; + + case 'p': + preserve_dates = TRUE; + break; + + case 'D': + deterministic = TRUE; + break; + + case 'U': + deterministic = FALSE; + break; + + case 'w': + wildcard = TRUE; + break; + + case 'x': + discard_locals = LOCALS_ALL; + break; + + case 'X': + discard_locals = LOCALS_START_L; + break; + + case 'v': + verbose = TRUE; + break; + + case 'V': + show_version = TRUE; + break; + + case OPTION_FORMATS_INFO: + formats_info = TRUE; + break; + + case OPTION_WEAKEN: + weaken = TRUE; + break; + + case OPTION_ADD_SECTION: + { + const char *s; + size_t off, alloc; + struct section_add *pa; + FILE *f; + + s = strchr (optarg, '='); + + if (s == NULL) + fatal (_("bad format for %s"), "--add-section"); + + pa = (struct section_add *) xmalloc (sizeof (struct section_add)); + pa->name = xstrndup (optarg, s - optarg); + pa->filename = s + 1; + + /* We don't use get_file_size so that we can do + --add-section .note.GNU_stack=/dev/null + get_file_size doesn't work on /dev/null. */ + + f = fopen (pa->filename, FOPEN_RB); + if (f == NULL) + fatal (_("cannot open: %s: %s"), + pa->filename, strerror (errno)); + + off = 0; + alloc = 4096; + pa->contents = (bfd_byte *) xmalloc (alloc); + while (!feof (f)) + { + off_t got; + + if (off == alloc) + { + alloc <<= 1; + pa->contents = (bfd_byte *) xrealloc (pa->contents, alloc); + } + + got = fread (pa->contents + off, 1, alloc - off, f); + if (ferror (f)) + fatal (_("%s: fread failed"), pa->filename); + + off += got; + } + + pa->size = off; + + fclose (f); + + pa->next = add_sections; + add_sections = pa; + } + break; + + case OPTION_CHANGE_START: + change_start = parse_vma (optarg, "--change-start"); + break; + + case OPTION_CHANGE_SECTION_ADDRESS: + case OPTION_CHANGE_SECTION_LMA: + case OPTION_CHANGE_SECTION_VMA: + { + struct section_list * p; + unsigned int context = 0; + const char *s; + int len; + char *name; + char *option = NULL; + bfd_vma val; + + switch (c) + { + case OPTION_CHANGE_SECTION_ADDRESS: + option = "--change-section-address"; + context = SECTION_CONTEXT_ALTER_LMA | SECTION_CONTEXT_ALTER_VMA; + break; + case OPTION_CHANGE_SECTION_LMA: + option = "--change-section-lma"; + context = SECTION_CONTEXT_ALTER_LMA; + break; + case OPTION_CHANGE_SECTION_VMA: + option = "--change-section-vma"; + context = SECTION_CONTEXT_ALTER_VMA; + break; + } + + s = strchr (optarg, '='); + if (s == NULL) + { + s = strchr (optarg, '+'); + if (s == NULL) + { + s = strchr (optarg, '-'); + if (s == NULL) + fatal (_("bad format for %s"), option); + } + } + else + { + /* Correct the context. */ + switch (c) + { + case OPTION_CHANGE_SECTION_ADDRESS: + context = SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_SET_VMA; + break; + case OPTION_CHANGE_SECTION_LMA: + context = SECTION_CONTEXT_SET_LMA; + break; + case OPTION_CHANGE_SECTION_VMA: + context = SECTION_CONTEXT_SET_VMA; + break; + } + } + + len = s - optarg; + name = (char *) xmalloc (len + 1); + strncpy (name, optarg, len); + name[len] = '\0'; + + p = find_section_list (name, TRUE, context); + + val = parse_vma (s + 1, option); + if (*s == '-') + val = - val; + + switch (c) + { + case OPTION_CHANGE_SECTION_ADDRESS: + p->vma_val = val; + /* Drop through. */ + + case OPTION_CHANGE_SECTION_LMA: + p->lma_val = val; + break; + + case OPTION_CHANGE_SECTION_VMA: + p->vma_val = val; + break; + } + } + break; + + case OPTION_CHANGE_ADDRESSES: + change_section_address = parse_vma (optarg, "--change-addresses"); + change_start = change_section_address; + break; + + case OPTION_CHANGE_WARNINGS: + change_warn = TRUE; + break; + + case OPTION_CHANGE_LEADING_CHAR: + change_leading_char = TRUE; + break; + + case OPTION_COMPRESS_DEBUG_SECTIONS: + do_debug_sections = compress; + break; + + case OPTION_DEBUGGING: + convert_debugging = TRUE; + break; + + case OPTION_DECOMPRESS_DEBUG_SECTIONS: + do_debug_sections = decompress; + break; + + case OPTION_GAP_FILL: + { + bfd_vma gap_fill_vma; + + gap_fill_vma = parse_vma (optarg, "--gap-fill"); + gap_fill = (bfd_byte) gap_fill_vma; + if ((bfd_vma) gap_fill != gap_fill_vma) + { + char buff[20]; + + sprintf_vma (buff, gap_fill_vma); + + non_fatal (_("Warning: truncating gap-fill from 0x%s to 0x%x"), + buff, gap_fill); + } + gap_fill_set = TRUE; + } + break; + + case OPTION_NO_CHANGE_WARNINGS: + change_warn = FALSE; + break; + + case OPTION_PAD_TO: + pad_to = parse_vma (optarg, "--pad-to"); + pad_to_set = TRUE; + break; + + case OPTION_REMOVE_LEADING_CHAR: + remove_leading_char = TRUE; + break; + + case OPTION_REDEFINE_SYM: + { + /* Push this redefinition onto redefine_symbol_list. */ + + int len; + const char *s; + const char *nextarg; + char *source, *target; + + s = strchr (optarg, '='); + if (s == NULL) + fatal (_("bad format for %s"), "--redefine-sym"); + + len = s - optarg; + source = (char *) xmalloc (len + 1); + strncpy (source, optarg, len); + source[len] = '\0'; + + nextarg = s + 1; + len = strlen (nextarg); + target = (char *) xmalloc (len + 1); + strcpy (target, nextarg); + + redefine_list_append ("--redefine-sym", source, target); + + free (source); + free (target); + } + break; + + case OPTION_REDEFINE_SYMS: + add_redefine_syms_file (optarg); + break; + + case OPTION_SET_SECTION_FLAGS: + { + struct section_list *p; + const char *s; + int len; + char *name; + + s = strchr (optarg, '='); + if (s == NULL) + fatal (_("bad format for %s"), "--set-section-flags"); + + len = s - optarg; + name = (char *) xmalloc (len + 1); + strncpy (name, optarg, len); + name[len] = '\0'; + + p = find_section_list (name, TRUE, SECTION_CONTEXT_SET_FLAGS); + + p->flags = parse_flags (s + 1); + } + break; + + case OPTION_RENAME_SECTION: + { + flagword flags; + const char *eq, *fl; + char *old_name; + char *new_name; + unsigned int len; + + eq = strchr (optarg, '='); + if (eq == NULL) + fatal (_("bad format for %s"), "--rename-section"); + + len = eq - optarg; + if (len == 0) + fatal (_("bad format for %s"), "--rename-section"); + + old_name = (char *) xmalloc (len + 1); + strncpy (old_name, optarg, len); + old_name[len] = 0; + + eq++; + fl = strchr (eq, ','); + if (fl) + { + flags = parse_flags (fl + 1); + len = fl - eq; + } + else + { + flags = -1; + len = strlen (eq); + } + + if (len == 0) + fatal (_("bad format for %s"), "--rename-section"); + + new_name = (char *) xmalloc (len + 1); + strncpy (new_name, eq, len); + new_name[len] = 0; + + add_section_rename (old_name, new_name, flags); + } + break; + + case OPTION_SET_START: + set_start = parse_vma (optarg, "--set-start"); + set_start_set = TRUE; + break; + + case OPTION_SREC_LEN: + Chunk = parse_vma (optarg, "--srec-len"); + break; + + case OPTION_SREC_FORCES3: + S3Forced = TRUE; + break; + + case OPTION_STRIP_SYMBOLS: + add_specific_symbols (optarg, strip_specific_htab); + break; + + case OPTION_STRIP_UNNEEDED_SYMBOLS: + add_specific_symbols (optarg, strip_unneeded_htab); + break; + + case OPTION_KEEP_SYMBOLS: + add_specific_symbols (optarg, keep_specific_htab); + break; + + case OPTION_LOCALIZE_HIDDEN: + localize_hidden = TRUE; + break; + + case OPTION_LOCALIZE_SYMBOLS: + add_specific_symbols (optarg, localize_specific_htab); + break; + + case OPTION_LONG_SECTION_NAMES: + if (!strcmp ("enable", optarg)) + long_section_names = ENABLE; + else if (!strcmp ("disable", optarg)) + long_section_names = DISABLE; + else if (!strcmp ("keep", optarg)) + long_section_names = KEEP; + else + fatal (_("unknown long section names option '%s'"), optarg); + break; + + case OPTION_GLOBALIZE_SYMBOLS: + add_specific_symbols (optarg, globalize_specific_htab); + break; + + case OPTION_KEEPGLOBAL_SYMBOLS: + add_specific_symbols (optarg, keepglobal_specific_htab); + break; + + case OPTION_WEAKEN_SYMBOLS: + add_specific_symbols (optarg, weaken_specific_htab); + break; + + case OPTION_ALT_MACH_CODE: + use_alt_mach_code = strtoul (optarg, NULL, 0); + if (use_alt_mach_code == 0) + fatal (_("unable to parse alternative machine code")); + break; + + case OPTION_PREFIX_SYMBOLS: + prefix_symbols_string = optarg; + break; + + case OPTION_PREFIX_SECTIONS: + prefix_sections_string = optarg; + break; + + case OPTION_PREFIX_ALLOC_SECTIONS: + prefix_alloc_sections_string = optarg; + break; + + case OPTION_READONLY_TEXT: + bfd_flags_to_set |= WP_TEXT; + bfd_flags_to_clear &= ~WP_TEXT; + break; + + case OPTION_WRITABLE_TEXT: + bfd_flags_to_clear |= WP_TEXT; + bfd_flags_to_set &= ~WP_TEXT; + break; + + case OPTION_PURE: + bfd_flags_to_set |= D_PAGED; + bfd_flags_to_clear &= ~D_PAGED; + break; + + case OPTION_IMPURE: + bfd_flags_to_clear |= D_PAGED; + bfd_flags_to_set &= ~D_PAGED; + break; + + case OPTION_EXTRACT_DWO: + strip_symbols = STRIP_NONDWO; + break; + + case OPTION_EXTRACT_SYMBOL: + extract_symbol = TRUE; + break; + + case OPTION_REVERSE_BYTES: + { + int prev = reverse_bytes; + + reverse_bytes = atoi (optarg); + if ((reverse_bytes <= 0) || ((reverse_bytes % 2) != 0)) + fatal (_("number of bytes to reverse must be positive and even")); + + if (prev && prev != reverse_bytes) + non_fatal (_("Warning: ignoring previous --reverse-bytes value of %d"), + prev); + break; + } + + case OPTION_FILE_ALIGNMENT: + pe_file_alignment = parse_vma (optarg, "--file-alignment"); + break; + + case OPTION_HEAP: + { + char *end; + pe_heap_reserve = strtoul (optarg, &end, 0); + if (end == optarg + || (*end != '.' && *end != '\0')) + non_fatal (_("%s: invalid reserve value for --heap"), + optarg); + else if (*end != '\0') + { + pe_heap_commit = strtoul (end + 1, &end, 0); + if (*end != '\0') + non_fatal (_("%s: invalid commit value for --heap"), + optarg); + } + } + break; + + case OPTION_IMAGE_BASE: + pe_image_base = parse_vma (optarg, "--image-base"); + break; + + case OPTION_SECTION_ALIGNMENT: + pe_section_alignment = parse_vma (optarg, + "--section-alignment"); + break; + + case OPTION_SUBSYSTEM: + set_pe_subsystem (optarg); + break; + + case OPTION_STACK: + { + char *end; + pe_stack_reserve = strtoul (optarg, &end, 0); + if (end == optarg + || (*end != '.' && *end != '\0')) + non_fatal (_("%s: invalid reserve value for --stack"), + optarg); + else if (*end != '\0') + { + pe_stack_commit = strtoul (end + 1, &end, 0); + if (*end != '\0') + non_fatal (_("%s: invalid commit value for --stack"), + optarg); + } + } + break; + + case 0: + /* We've been given a long option. */ + break; + + case 'H': + case 'h': + copy_usage (stdout, 0); + + default: + copy_usage (stderr, 1); + } + } + + if (formats_info) + { + display_info (); + return 0; + } + + if (show_version) + print_version ("objcopy"); + + if (interleave && copy_byte == -1) + fatal (_("interleave start byte must be set with --byte")); + + if (copy_byte >= interleave) + fatal (_("byte number must be less than interleave")); + + if (copy_width > interleave - copy_byte) + fatal (_("interleave width must be less than or equal to interleave - byte`")); + + if (optind == argc || optind + 2 < argc) + copy_usage (stderr, 1); + + input_filename = argv[optind]; + if (optind + 1 < argc) + output_filename = argv[optind + 1]; + + default_deterministic (); + + /* Default is to strip no symbols. */ + if (strip_symbols == STRIP_UNDEF && discard_locals == LOCALS_UNDEF) + strip_symbols = STRIP_NONE; + + if (output_target == NULL) + output_target = input_target; + + /* Convert input EFI target to PEI target. */ + if (input_target != NULL + && strncmp (input_target, "efi-", 4) == 0) + { + char *efi; + + efi = xstrdup (output_target + 4); + if (strncmp (efi, "bsdrv-", 6) == 0 + || strncmp (efi, "rtdrv-", 6) == 0) + efi += 2; + else if (strncmp (efi, "app-", 4) != 0) + fatal (_("unknown input EFI target: %s"), input_target); + + input_target = efi; + convert_efi_target (efi); + } + + /* Convert output EFI target to PEI target. */ + if (output_target != NULL + && strncmp (output_target, "efi-", 4) == 0) + { + char *efi; + + efi = xstrdup (output_target + 4); + if (strncmp (efi, "app-", 4) == 0) + { + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; + } + else if (strncmp (efi, "bsdrv-", 6) == 0) + { + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER; + efi += 2; + } + else if (strncmp (efi, "rtdrv-", 6) == 0) + { + if (pe_subsystem == -1) + pe_subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER; + efi += 2; + } + else + fatal (_("unknown output EFI target: %s"), output_target); + + if (pe_file_alignment == (bfd_vma) -1) + pe_file_alignment = PE_DEF_FILE_ALIGNMENT; + if (pe_section_alignment == (bfd_vma) -1) + pe_section_alignment = PE_DEF_SECTION_ALIGNMENT; + + output_target = efi; + convert_efi_target (efi); + } + + if (preserve_dates) + if (stat (input_filename, & statbuf) < 0) + fatal (_("warning: could not locate '%s'. System error message: %s"), + input_filename, strerror (errno)); + + /* If there is no destination file, or the source and destination files + are the same, then create a temp and rename the result into the input. */ + if (output_filename == NULL + || filename_cmp (input_filename, output_filename) == 0) + tmpname = make_tempname (input_filename); + else + tmpname = output_filename; + + if (tmpname == NULL) + fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), + input_filename, strerror (errno)); + + copy_file (input_filename, tmpname, input_target, output_target, input_arch); + if (status == 0) + { + if (preserve_dates) + set_times (tmpname, &statbuf); + if (tmpname != output_filename) + status = (smart_rename (tmpname, input_filename, + preserve_dates) != 0); + } + else + unlink_if_ordinary (tmpname); + + if (change_warn) + { + struct section_list *p; + + for (p = change_sections; p != NULL; p = p->next) + { + if (! p->used) + { + if (p->context & (SECTION_CONTEXT_SET_VMA | SECTION_CONTEXT_ALTER_VMA)) + { + char buff [20]; + + sprintf_vma (buff, p->vma_val); + + /* xgettext:c-format */ + non_fatal (_("%s %s%c0x%s never used"), + "--change-section-vma", + p->pattern, + p->context & SECTION_CONTEXT_SET_VMA ? '=' : '+', + buff); + } + + if (p->context & (SECTION_CONTEXT_SET_LMA | SECTION_CONTEXT_ALTER_LMA)) + { + char buff [20]; + + sprintf_vma (buff, p->lma_val); + + /* xgettext:c-format */ + non_fatal (_("%s %s%c0x%s never used"), + "--change-section-lma", + p->pattern, + p->context & SECTION_CONTEXT_SET_LMA ? '=' : '+', + buff); + } + } + } + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif +#if defined (HAVE_SETLOCALE) + setlocale (LC_CTYPE, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + program_name = argv[0]; + xmalloc_set_program_name (program_name); + + START_PROGRESS (program_name, 0); + + expandargv (&argc, &argv); + + strip_symbols = STRIP_UNDEF; + discard_locals = LOCALS_UNDEF; + + bfd_init (); + set_default_bfd_target (); + + if (is_strip < 0) + { + int i = strlen (program_name); +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + /* Drop the .exe suffix, if any. */ + if (i > 4 && FILENAME_CMP (program_name + i - 4, ".exe") == 0) + { + i -= 4; + program_name[i] = '\0'; + } +#endif + is_strip = (i >= 5 && FILENAME_CMP (program_name + i - 5, "strip") == 0); + } + + create_symbol_htabs (); + + if (is_strip) + strip_main (argc, argv); + else + copy_main (argc, argv); + + END_PROGRESS (program_name); + + return status; +} diff --git a/contrib/toolchain/binutils/binutils/rdcoff.c b/contrib/toolchain/binutils/binutils/rdcoff.c new file mode 100644 index 0000000000..473305e231 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/rdcoff.c @@ -0,0 +1,876 @@ +/* stabs.c -- Parse COFF debugging information + Copyright 1996, 1999, 2000, 2002, 2003, 2005, 2007 + Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This file contains code which parses COFF debugging information. */ + +#include "sysdep.h" +#include "bfd.h" +#include "coff/internal.h" +#include "libiberty.h" +#include "bucomm.h" +#include "debug.h" +#include "budbg.h" + +/* FIXME: We should not need this BFD internal file. We need it for + the N_BTMASK, etc., values. */ +#include "libcoff.h" + +/* These macros extract the right mask and shifts for this BFD. They + assume that there is a local variable named ABFD. This is so that + macros like ISFCN and DECREF, from coff/internal.h, will work + without modification. */ +#define N_BTMASK (coff_data (abfd)->local_n_btmask) +#define N_BTSHFT (coff_data (abfd)->local_n_btshft) +#define N_TMASK (coff_data (abfd)->local_n_tmask) +#define N_TSHIFT (coff_data (abfd)->local_n_tshift) + +/* This structure is used to hold the symbols, as well as the current + location within the symbols. */ + +struct coff_symbols +{ + /* The symbols. */ + asymbol **syms; + /* The number of symbols. */ + long symcount; + /* The index of the current symbol. */ + long symno; + /* The index of the current symbol in the COFF symbol table (where + each auxent counts as a symbol). */ + long coff_symno; +}; + +/* The largest basic type we are prepared to handle. */ + +#define T_MAX (T_LNGDBL) + +/* This structure is used to hold slots. */ + +struct coff_slots +{ + /* Next set of slots. */ + struct coff_slots *next; + /* Slots. */ +#define COFF_SLOTS (16) + debug_type slots[COFF_SLOTS]; +}; + +/* This structure is used to map symbol indices to types. */ + +struct coff_types +{ + /* Slots. */ + struct coff_slots *slots; + /* Basic types. */ + debug_type basic[T_MAX + 1]; +}; + +static debug_type *coff_get_slot (struct coff_types *, int); +static debug_type parse_coff_type + (bfd *, struct coff_symbols *, struct coff_types *, long, int, + union internal_auxent *, bfd_boolean, void *); +static debug_type parse_coff_base_type + (bfd *, struct coff_symbols *, struct coff_types *, long, int, + union internal_auxent *, void *); +static debug_type parse_coff_struct_type + (bfd *, struct coff_symbols *, struct coff_types *, int, + union internal_auxent *, void *); +static debug_type parse_coff_enum_type + (bfd *, struct coff_symbols *, struct coff_types *, + union internal_auxent *, void *); +static bfd_boolean parse_coff_symbol + (bfd *, struct coff_types *, asymbol *, long, struct internal_syment *, + void *, debug_type, bfd_boolean); +static bfd_boolean external_coff_symbol_p (int sym_class); + +/* Return the slot for a type. */ + +static debug_type * +coff_get_slot (struct coff_types *types, int indx) +{ + struct coff_slots **pps; + + pps = &types->slots; + + while (indx >= COFF_SLOTS) + { + if (*pps == NULL) + { + *pps = (struct coff_slots *) xmalloc (sizeof **pps); + memset (*pps, 0, sizeof **pps); + } + pps = &(*pps)->next; + indx -= COFF_SLOTS; + } + + if (*pps == NULL) + { + *pps = (struct coff_slots *) xmalloc (sizeof **pps); + memset (*pps, 0, sizeof **pps); + } + + return (*pps)->slots + indx; +} + +/* Parse a COFF type code in NTYPE. */ + +static debug_type +parse_coff_type (bfd *abfd, struct coff_symbols *symbols, + struct coff_types *types, long coff_symno, int ntype, + union internal_auxent *pauxent, bfd_boolean useaux, + void *dhandle) +{ + debug_type type; + + if ((ntype & ~N_BTMASK) != 0) + { + int newtype; + + newtype = DECREF (ntype); + + if (ISPTR (ntype)) + { + type = parse_coff_type (abfd, symbols, types, coff_symno, newtype, + pauxent, useaux, dhandle); + type = debug_make_pointer_type (dhandle, type); + } + else if (ISFCN (ntype)) + { + type = parse_coff_type (abfd, symbols, types, coff_symno, newtype, + pauxent, useaux, dhandle); + type = debug_make_function_type (dhandle, type, (debug_type *) NULL, + FALSE); + } + else if (ISARY (ntype)) + { + int n; + + if (pauxent == NULL) + n = 0; + else + { + unsigned short *dim; + int i; + + /* FIXME: If pauxent->x_sym.x_tagndx.l == 0, gdb sets + the c_naux field of the syment to 0. */ + + /* Move the dimensions down, so that the next array + picks up the next one. */ + dim = pauxent->x_sym.x_fcnary.x_ary.x_dimen; + n = dim[0]; + for (i = 0; *dim != 0 && i < DIMNUM - 1; i++, dim++) + *dim = *(dim + 1); + *dim = 0; + } + + type = parse_coff_type (abfd, symbols, types, coff_symno, newtype, + pauxent, FALSE, dhandle); + type = debug_make_array_type (dhandle, type, + parse_coff_base_type (abfd, symbols, + types, + coff_symno, + T_INT, + NULL, dhandle), + 0, n - 1, FALSE); + } + else + { + non_fatal (_("parse_coff_type: Bad type code 0x%x"), ntype); + return DEBUG_TYPE_NULL; + } + + return type; + } + + if (pauxent != NULL && pauxent->x_sym.x_tagndx.l > 0) + { + debug_type *slot; + + /* This is a reference to an existing type. FIXME: gdb checks + that the class is not C_STRTAG, nor C_UNTAG, nor C_ENTAG. */ + slot = coff_get_slot (types, pauxent->x_sym.x_tagndx.l); + if (*slot != DEBUG_TYPE_NULL) + return *slot; + else + return debug_make_indirect_type (dhandle, slot, (const char *) NULL); + } + + /* If the aux entry has already been used for something, useaux will + have been set to false, indicating that parse_coff_base_type + should not use it. We need to do it this way, rather than simply + passing pauxent as NULL, because we need to be able handle + multiple array dimensions while still discarding pauxent after + having handled all of them. */ + if (! useaux) + pauxent = NULL; + + return parse_coff_base_type (abfd, symbols, types, coff_symno, ntype, + pauxent, dhandle); +} + +/* Parse a basic COFF type in NTYPE. */ + +static debug_type +parse_coff_base_type (bfd *abfd, struct coff_symbols *symbols, + struct coff_types *types, long coff_symno, int ntype, + union internal_auxent *pauxent, void *dhandle) +{ + debug_type ret; + bfd_boolean set_basic; + const char *name; + debug_type *slot; + + if (ntype >= 0 + && ntype <= T_MAX + && types->basic[ntype] != DEBUG_TYPE_NULL) + return types->basic[ntype]; + + set_basic = TRUE; + name = NULL; + + switch (ntype) + { + default: + ret = debug_make_void_type (dhandle); + break; + + case T_NULL: + case T_VOID: + ret = debug_make_void_type (dhandle); + name = "void"; + break; + + case T_CHAR: + ret = debug_make_int_type (dhandle, 1, FALSE); + name = "char"; + break; + + case T_SHORT: + ret = debug_make_int_type (dhandle, 2, FALSE); + name = "short"; + break; + + case T_INT: + /* FIXME: Perhaps the size should depend upon the architecture. */ + ret = debug_make_int_type (dhandle, 4, FALSE); + name = "int"; + break; + + case T_LONG: + ret = debug_make_int_type (dhandle, 4, FALSE); + name = "long"; + break; + + case T_FLOAT: + ret = debug_make_float_type (dhandle, 4); + name = "float"; + break; + + case T_DOUBLE: + ret = debug_make_float_type (dhandle, 8); + name = "double"; + break; + + case T_LNGDBL: + ret = debug_make_float_type (dhandle, 12); + name = "long double"; + break; + + case T_UCHAR: + ret = debug_make_int_type (dhandle, 1, TRUE); + name = "unsigned char"; + break; + + case T_USHORT: + ret = debug_make_int_type (dhandle, 2, TRUE); + name = "unsigned short"; + break; + + case T_UINT: + ret = debug_make_int_type (dhandle, 4, TRUE); + name = "unsigned int"; + break; + + case T_ULONG: + ret = debug_make_int_type (dhandle, 4, TRUE); + name = "unsigned long"; + break; + + case T_STRUCT: + if (pauxent == NULL) + ret = debug_make_struct_type (dhandle, TRUE, 0, + (debug_field *) NULL); + else + ret = parse_coff_struct_type (abfd, symbols, types, ntype, pauxent, + dhandle); + + slot = coff_get_slot (types, coff_symno); + *slot = ret; + + set_basic = FALSE; + break; + + case T_UNION: + if (pauxent == NULL) + ret = debug_make_struct_type (dhandle, FALSE, 0, (debug_field *) NULL); + else + ret = parse_coff_struct_type (abfd, symbols, types, ntype, pauxent, + dhandle); + + slot = coff_get_slot (types, coff_symno); + *slot = ret; + + set_basic = FALSE; + break; + + case T_ENUM: + if (pauxent == NULL) + ret = debug_make_enum_type (dhandle, (const char **) NULL, + (bfd_signed_vma *) NULL); + else + ret = parse_coff_enum_type (abfd, symbols, types, pauxent, dhandle); + + slot = coff_get_slot (types, coff_symno); + *slot = ret; + + set_basic = FALSE; + break; + } + + if (name != NULL) + ret = debug_name_type (dhandle, name, ret); + + if (set_basic + && ntype >= 0 + && ntype <= T_MAX) + types->basic[ntype] = ret; + + return ret; +} + +/* Parse a struct type. */ + +static debug_type +parse_coff_struct_type (bfd *abfd, struct coff_symbols *symbols, + struct coff_types *types, int ntype, + union internal_auxent *pauxent, void *dhandle) +{ + long symend; + int alloc; + debug_field *fields; + int count; + bfd_boolean done; + + symend = pauxent->x_sym.x_fcnary.x_fcn.x_endndx.l; + + alloc = 10; + fields = (debug_field *) xmalloc (alloc * sizeof *fields); + count = 0; + + done = FALSE; + while (! done + && symbols->coff_symno < symend + && symbols->symno < symbols->symcount) + { + asymbol *sym; + long this_coff_symno; + struct internal_syment syment; + union internal_auxent auxent; + union internal_auxent *psubaux; + bfd_vma bitpos = 0, bitsize = 0; + + sym = symbols->syms[symbols->symno]; + + if (! bfd_coff_get_syment (abfd, sym, &syment)) + { + non_fatal (_("bfd_coff_get_syment failed: %s"), + bfd_errmsg (bfd_get_error ())); + return DEBUG_TYPE_NULL; + } + + this_coff_symno = symbols->coff_symno; + + ++symbols->symno; + symbols->coff_symno += 1 + syment.n_numaux; + + if (syment.n_numaux == 0) + psubaux = NULL; + else + { + if (! bfd_coff_get_auxent (abfd, sym, 0, &auxent)) + { + non_fatal (_("bfd_coff_get_auxent failed: %s"), + bfd_errmsg (bfd_get_error ())); + return DEBUG_TYPE_NULL; + } + psubaux = &auxent; + } + + switch (syment.n_sclass) + { + case C_MOS: + case C_MOU: + bitpos = 8 * bfd_asymbol_value (sym); + bitsize = 0; + break; + + case C_FIELD: + bitpos = bfd_asymbol_value (sym); + bitsize = auxent.x_sym.x_misc.x_lnsz.x_size; + break; + + case C_EOS: + done = TRUE; + break; + } + + if (! done) + { + debug_type ftype; + debug_field f; + + ftype = parse_coff_type (abfd, symbols, types, this_coff_symno, + syment.n_type, psubaux, TRUE, dhandle); + f = debug_make_field (dhandle, bfd_asymbol_name (sym), ftype, + bitpos, bitsize, DEBUG_VISIBILITY_PUBLIC); + if (f == DEBUG_FIELD_NULL) + return DEBUG_TYPE_NULL; + + if (count + 1 >= alloc) + { + alloc += 10; + fields = ((debug_field *) + xrealloc (fields, alloc * sizeof *fields)); + } + + fields[count] = f; + ++count; + } + } + + fields[count] = DEBUG_FIELD_NULL; + + return debug_make_struct_type (dhandle, ntype == T_STRUCT, + pauxent->x_sym.x_misc.x_lnsz.x_size, + fields); +} + +/* Parse an enum type. */ + +static debug_type +parse_coff_enum_type (bfd *abfd, struct coff_symbols *symbols, + struct coff_types *types ATTRIBUTE_UNUSED, + union internal_auxent *pauxent, void *dhandle) +{ + long symend; + int alloc; + const char **names; + bfd_signed_vma *vals; + int count; + bfd_boolean done; + + symend = pauxent->x_sym.x_fcnary.x_fcn.x_endndx.l; + + alloc = 10; + names = (const char **) xmalloc (alloc * sizeof *names); + vals = (bfd_signed_vma *) xmalloc (alloc * sizeof *vals); + count = 0; + + done = FALSE; + while (! done + && symbols->coff_symno < symend + && symbols->symno < symbols->symcount) + { + asymbol *sym; + struct internal_syment syment; + + sym = symbols->syms[symbols->symno]; + + if (! bfd_coff_get_syment (abfd, sym, &syment)) + { + non_fatal (_("bfd_coff_get_syment failed: %s"), + bfd_errmsg (bfd_get_error ())); + return DEBUG_TYPE_NULL; + } + + ++symbols->symno; + symbols->coff_symno += 1 + syment.n_numaux; + + switch (syment.n_sclass) + { + case C_MOE: + if (count + 1 >= alloc) + { + alloc += 10; + names = ((const char **) + xrealloc (names, alloc * sizeof *names)); + vals = ((bfd_signed_vma *) + xrealloc (vals, alloc * sizeof *vals)); + } + + names[count] = bfd_asymbol_name (sym); + vals[count] = bfd_asymbol_value (sym); + ++count; + break; + + case C_EOS: + done = TRUE; + break; + } + } + + names[count] = NULL; + + return debug_make_enum_type (dhandle, names, vals); +} + +/* Handle a single COFF symbol. */ + +static bfd_boolean +parse_coff_symbol (bfd *abfd ATTRIBUTE_UNUSED, struct coff_types *types, + asymbol *sym, long coff_symno, + struct internal_syment *psyment, void *dhandle, + debug_type type, bfd_boolean within_function) +{ + switch (psyment->n_sclass) + { + case C_NULL: + break; + + case C_AUTO: + if (! debug_record_variable (dhandle, bfd_asymbol_name (sym), type, + DEBUG_LOCAL, bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_WEAKEXT: + case C_EXT: + if (! debug_record_variable (dhandle, bfd_asymbol_name (sym), type, + DEBUG_GLOBAL, bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_STAT: + if (! debug_record_variable (dhandle, bfd_asymbol_name (sym), type, + (within_function + ? DEBUG_LOCAL_STATIC + : DEBUG_STATIC), + bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_REG: + /* FIXME: We may need to convert the register number. */ + if (! debug_record_variable (dhandle, bfd_asymbol_name (sym), type, + DEBUG_REGISTER, bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_LABEL: + break; + + case C_ARG: + if (! debug_record_parameter (dhandle, bfd_asymbol_name (sym), type, + DEBUG_PARM_STACK, bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_REGPARM: + /* FIXME: We may need to convert the register number. */ + if (! debug_record_parameter (dhandle, bfd_asymbol_name (sym), type, + DEBUG_PARM_REG, bfd_asymbol_value (sym))) + return FALSE; + break; + + case C_TPDEF: + type = debug_name_type (dhandle, bfd_asymbol_name (sym), type); + if (type == DEBUG_TYPE_NULL) + return FALSE; + break; + + case C_STRTAG: + case C_UNTAG: + case C_ENTAG: + { + debug_type *slot; + + type = debug_tag_type (dhandle, bfd_asymbol_name (sym), type); + if (type == DEBUG_TYPE_NULL) + return FALSE; + + /* Store the named type into the slot, so that references get + the name. */ + slot = coff_get_slot (types, coff_symno); + *slot = type; + } + break; + + default: + break; + } + + return TRUE; +} + +/* Determine if a symbol has external visibility. */ + +static bfd_boolean +external_coff_symbol_p (int sym_class) +{ + switch (sym_class) + { + case C_EXT: + case C_WEAKEXT: + return TRUE; + default: + break; + } + return FALSE; +} + +/* This is the main routine. It looks through all the symbols and + handles them. */ + +bfd_boolean +parse_coff (bfd *abfd, asymbol **syms, long symcount, void *dhandle) +{ + struct coff_symbols symbols; + struct coff_types types; + int i; + long next_c_file; + const char *fnname; + int fnclass; + int fntype; + bfd_vma fnend; + alent *linenos; + bfd_boolean within_function; + long this_coff_symno; + + symbols.syms = syms; + symbols.symcount = symcount; + symbols.symno = 0; + symbols.coff_symno = 0; + + types.slots = NULL; + for (i = 0; i <= T_MAX; i++) + types.basic[i] = DEBUG_TYPE_NULL; + + next_c_file = -1; + fnname = NULL; + fnclass = 0; + fntype = 0; + fnend = 0; + linenos = NULL; + within_function = FALSE; + + while (symbols.symno < symcount) + { + asymbol *sym; + const char *name; + struct internal_syment syment; + union internal_auxent auxent; + union internal_auxent *paux; + debug_type type; + + sym = syms[symbols.symno]; + + if (! bfd_coff_get_syment (abfd, sym, &syment)) + { + non_fatal (_("bfd_coff_get_syment failed: %s"), + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + name = bfd_asymbol_name (sym); + + this_coff_symno = symbols.coff_symno; + + ++symbols.symno; + symbols.coff_symno += 1 + syment.n_numaux; + + /* We only worry about the first auxent, because that is the + only one which is relevant for debugging information. */ + if (syment.n_numaux == 0) + paux = NULL; + else + { + if (! bfd_coff_get_auxent (abfd, sym, 0, &auxent)) + { + non_fatal (_("bfd_coff_get_auxent failed: %s"), + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + paux = &auxent; + } + + if (this_coff_symno == next_c_file && syment.n_sclass != C_FILE) + { + /* The last C_FILE symbol points to the first external + symbol. */ + if (! debug_set_filename (dhandle, "*globals*")) + return FALSE; + } + + switch (syment.n_sclass) + { + case C_EFCN: + case C_EXTDEF: + case C_ULABEL: + case C_USTATIC: + case C_LINE: + case C_ALIAS: + case C_HIDDEN: + /* Just ignore these classes. */ + break; + + case C_FILE: + next_c_file = syment.n_value; + if (! debug_set_filename (dhandle, name)) + return FALSE; + break; + + case C_STAT: + /* Ignore static symbols with a type of T_NULL. These + represent section entries. */ + if (syment.n_type == T_NULL) + break; + /* Fall through. */ + case C_WEAKEXT: + case C_EXT: + if (ISFCN (syment.n_type)) + { + fnname = name; + fnclass = syment.n_sclass; + fntype = syment.n_type; + if (syment.n_numaux > 0) + fnend = bfd_asymbol_value (sym) + auxent.x_sym.x_misc.x_fsize; + else + fnend = 0; + linenos = BFD_SEND (abfd, _get_lineno, (abfd, sym)); + break; + } + type = parse_coff_type (abfd, &symbols, &types, this_coff_symno, + syment.n_type, paux, TRUE, dhandle); + if (type == DEBUG_TYPE_NULL) + return FALSE; + if (! parse_coff_symbol (abfd, &types, sym, this_coff_symno, &syment, + dhandle, type, within_function)) + return FALSE; + break; + + case C_FCN: + if (strcmp (name, ".bf") == 0) + { + if (fnname == NULL) + { + non_fatal (_("%ld: .bf without preceding function"), + this_coff_symno); + return FALSE; + } + + type = parse_coff_type (abfd, &symbols, &types, this_coff_symno, + DECREF (fntype), paux, FALSE, dhandle); + if (type == DEBUG_TYPE_NULL) + return FALSE; + + if (! debug_record_function (dhandle, fnname, type, + external_coff_symbol_p (fnclass), + bfd_asymbol_value (sym))) + return FALSE; + + if (linenos != NULL) + { + int base; + bfd_vma addr; + + if (syment.n_numaux == 0) + base = 0; + else + base = auxent.x_sym.x_misc.x_lnsz.x_lnno - 1; + + addr = bfd_get_section_vma (abfd, bfd_get_section (sym)); + + ++linenos; + + while (linenos->line_number != 0) + { + if (! debug_record_line (dhandle, + linenos->line_number + base, + linenos->u.offset + addr)) + return FALSE; + ++linenos; + } + } + + fnname = NULL; + linenos = NULL; + fnclass = 0; + fntype = 0; + + within_function = TRUE; + } + else if (strcmp (name, ".ef") == 0) + { + if (! within_function) + { + non_fatal (_("%ld: unexpected .ef\n"), this_coff_symno); + return FALSE; + } + + if (bfd_asymbol_value (sym) > fnend) + fnend = bfd_asymbol_value (sym); + if (! debug_end_function (dhandle, fnend)) + return FALSE; + + fnend = 0; + within_function = FALSE; + } + break; + + case C_BLOCK: + if (strcmp (name, ".bb") == 0) + { + if (! debug_start_block (dhandle, bfd_asymbol_value (sym))) + return FALSE; + } + else if (strcmp (name, ".eb") == 0) + { + if (! debug_end_block (dhandle, bfd_asymbol_value (sym))) + return FALSE; + } + break; + + default: + type = parse_coff_type (abfd, &symbols, &types, this_coff_symno, + syment.n_type, paux, TRUE, dhandle); + if (type == DEBUG_TYPE_NULL) + return FALSE; + if (! parse_coff_symbol (abfd, &types, sym, this_coff_symno, &syment, + dhandle, type, within_function)) + return FALSE; + break; + } + } + + return TRUE; +} diff --git a/contrib/toolchain/binutils/binutils/rddbg.c b/contrib/toolchain/binutils/binutils/rddbg.c new file mode 100644 index 0000000000..27abd66a48 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/rddbg.c @@ -0,0 +1,450 @@ +/* rddbg.c -- Read debugging information into a generic form. + Copyright 1995, 1996, 1997, 2000, 2002, 2003, 2005, 2007, 2008, + 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + + +/* This file reads debugging information into a generic form. This + file knows how to dig the debugging information out of an object + file. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libiberty.h" +#include "bucomm.h" +#include "debug.h" +#include "budbg.h" + +static bfd_boolean read_section_stabs_debugging_info + (bfd *, asymbol **, long, void *, bfd_boolean *); +static bfd_boolean read_symbol_stabs_debugging_info + (bfd *, asymbol **, long, void *, bfd_boolean *); +static bfd_boolean read_ieee_debugging_info (bfd *, void *, bfd_boolean *); +static void save_stab (int, int, bfd_vma, const char *); +static void stab_context (void); +static void free_saved_stabs (void); + +/* Read debugging information from a BFD. Returns a generic debugging + pointer. */ + +void * +read_debugging_info (bfd *abfd, asymbol **syms, long symcount, bfd_boolean no_messages) +{ + void *dhandle; + bfd_boolean found; + + dhandle = debug_init (); + if (dhandle == NULL) + return NULL; + + if (! read_section_stabs_debugging_info (abfd, syms, symcount, dhandle, + &found)) + return NULL; + + if (bfd_get_flavour (abfd) == bfd_target_aout_flavour) + { + if (! read_symbol_stabs_debugging_info (abfd, syms, symcount, dhandle, + &found)) + return NULL; + } + + if (bfd_get_flavour (abfd) == bfd_target_ieee_flavour) + { + if (! read_ieee_debugging_info (abfd, dhandle, &found)) + return NULL; + } + + /* Try reading the COFF symbols if we didn't find any stabs in COFF + sections. */ + if (! found + && bfd_get_flavour (abfd) == bfd_target_coff_flavour + && symcount > 0) + { + if (! parse_coff (abfd, syms, symcount, dhandle)) + return NULL; + found = TRUE; + } + + if (! found) + { + if (! no_messages) + non_fatal (_("%s: no recognized debugging information"), + bfd_get_filename (abfd)); + return NULL; + } + + return dhandle; +} + +/* Read stabs in sections debugging information from a BFD. */ + +static bfd_boolean +read_section_stabs_debugging_info (bfd *abfd, asymbol **syms, long symcount, + void *dhandle, bfd_boolean *pfound) +{ + static struct + { + const char *secname; + const char *strsecname; + } + names[] = + { + { ".stab", ".stabstr" }, + { "LC_SYMTAB.stabs", "LC_SYMTAB.stabstr" }, + { "$GDB_SYMBOLS$", "$GDB_STRINGS$" } + }; + unsigned int i; + void *shandle; + + *pfound = FALSE; + shandle = NULL; + + for (i = 0; i < sizeof names / sizeof names[0]; i++) + { + asection *sec, *strsec; + + sec = bfd_get_section_by_name (abfd, names[i].secname); + strsec = bfd_get_section_by_name (abfd, names[i].strsecname); + if (sec != NULL && strsec != NULL) + { + bfd_size_type stabsize, strsize; + bfd_byte *stabs, *strings; + bfd_byte *stab; + bfd_size_type stroff, next_stroff; + + stabsize = bfd_section_size (abfd, sec); + stabs = (bfd_byte *) xmalloc (stabsize); + if (! bfd_get_section_contents (abfd, sec, stabs, 0, stabsize)) + { + fprintf (stderr, "%s: %s: %s\n", + bfd_get_filename (abfd), names[i].secname, + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + strsize = bfd_section_size (abfd, strsec); + strings = (bfd_byte *) xmalloc (strsize); + if (! bfd_get_section_contents (abfd, strsec, strings, 0, strsize)) + { + fprintf (stderr, "%s: %s: %s\n", + bfd_get_filename (abfd), names[i].strsecname, + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + if (shandle == NULL) + { + shandle = start_stab (dhandle, abfd, TRUE, syms, symcount); + if (shandle == NULL) + return FALSE; + } + + *pfound = TRUE; + + stroff = 0; + next_stroff = 0; + for (stab = stabs; stab < stabs + stabsize; stab += 12) + { + unsigned int strx; + int type; + int other ATTRIBUTE_UNUSED; + int desc; + bfd_vma value; + + /* This code presumes 32 bit values. */ + + strx = bfd_get_32 (abfd, stab); + type = bfd_get_8 (abfd, stab + 4); + other = bfd_get_8 (abfd, stab + 5); + desc = bfd_get_16 (abfd, stab + 6); + value = bfd_get_32 (abfd, stab + 8); + + if (type == 0) + { + /* Special type 0 stabs indicate the offset to the + next string table. */ + stroff = next_stroff; + next_stroff += value; + } + else + { + char *f, *s; + + f = NULL; + + if (stroff + strx > strsize) + { + fprintf (stderr, "%s: %s: stab entry %ld is corrupt, strx = 0x%x, type = %d\n", + bfd_get_filename (abfd), names[i].secname, + (long) (stab - stabs) / 12, strx, type); + continue; + } + + s = (char *) strings + stroff + strx; + + while (s[strlen (s) - 1] == '\\' + && stab + 12 < stabs + stabsize) + { + char *p; + + stab += 12; + p = s + strlen (s) - 1; + *p = '\0'; + s = concat (s, + ((char *) strings + + stroff + + bfd_get_32 (abfd, stab)), + (const char *) NULL); + + /* We have to restore the backslash, because, if + the linker is hashing stabs strings, we may + see the same string more than once. */ + *p = '\\'; + + if (f != NULL) + free (f); + f = s; + } + + save_stab (type, desc, value, s); + + if (! parse_stab (dhandle, shandle, type, desc, value, s)) + { + stab_context (); + free_saved_stabs (); + return FALSE; + } + + /* Don't free f, since I think the stabs code + expects strings to hang around. This should be + straightened out. FIXME. */ + } + } + + free_saved_stabs (); + free (stabs); + + /* Don't free strings, since I think the stabs code expects + the strings to hang around. This should be straightened + out. FIXME. */ + } + } + + if (shandle != NULL) + { + if (! finish_stab (dhandle, shandle)) + return FALSE; + } + + return TRUE; +} + +/* Read stabs in the symbol table. */ + +static bfd_boolean +read_symbol_stabs_debugging_info (bfd *abfd, asymbol **syms, long symcount, + void *dhandle, bfd_boolean *pfound) +{ + void *shandle; + asymbol **ps, **symend; + + shandle = NULL; + symend = syms + symcount; + for (ps = syms; ps < symend; ps++) + { + symbol_info i; + + bfd_get_symbol_info (abfd, *ps, &i); + + if (i.type == '-') + { + const char *s; + char *f; + + if (shandle == NULL) + { + shandle = start_stab (dhandle, abfd, FALSE, syms, symcount); + if (shandle == NULL) + return FALSE; + } + + *pfound = TRUE; + + s = i.name; + f = NULL; + while (s[strlen (s) - 1] == '\\' + && ps + 1 < symend) + { + char *sc, *n; + + ++ps; + sc = xstrdup (s); + sc[strlen (sc) - 1] = '\0'; + n = concat (sc, bfd_asymbol_name (*ps), (const char *) NULL); + free (sc); + if (f != NULL) + free (f); + f = n; + s = n; + } + + save_stab (i.stab_type, i.stab_desc, i.value, s); + + if (! parse_stab (dhandle, shandle, i.stab_type, i.stab_desc, + i.value, s)) + { + stab_context (); + free_saved_stabs (); + return FALSE; + } + + /* Don't free f, since I think the stabs code expects + strings to hang around. This should be straightened out. + FIXME. */ + } + } + + free_saved_stabs (); + + if (shandle != NULL) + { + if (! finish_stab (dhandle, shandle)) + return FALSE; + } + + return TRUE; +} + +/* Read IEEE debugging information. */ + +static bfd_boolean +read_ieee_debugging_info (bfd *abfd, void *dhandle, bfd_boolean *pfound) +{ + asection *dsec; + bfd_size_type size; + bfd_byte *contents; + + /* The BFD backend puts the debugging information into a section + named .debug. */ + + dsec = bfd_get_section_by_name (abfd, ".debug"); + if (dsec == NULL) + return TRUE; + + size = bfd_section_size (abfd, dsec); + contents = (bfd_byte *) xmalloc (size); + if (! bfd_get_section_contents (abfd, dsec, contents, 0, size)) + return FALSE; + + if (! parse_ieee (dhandle, abfd, contents, size)) + return FALSE; + + free (contents); + + *pfound = TRUE; + + return TRUE; +} + +/* Record stabs strings, so that we can give some context for errors. */ + +#define SAVE_STABS_COUNT (16) + +struct saved_stab +{ + int type; + int desc; + bfd_vma value; + char *string; +}; + +static struct saved_stab saved_stabs[SAVE_STABS_COUNT]; +static int saved_stabs_index; + +/* Save a stabs string. */ + +static void +save_stab (int type, int desc, bfd_vma value, const char *string) +{ + if (saved_stabs[saved_stabs_index].string != NULL) + free (saved_stabs[saved_stabs_index].string); + saved_stabs[saved_stabs_index].type = type; + saved_stabs[saved_stabs_index].desc = desc; + saved_stabs[saved_stabs_index].value = value; + saved_stabs[saved_stabs_index].string = xstrdup (string); + saved_stabs_index = (saved_stabs_index + 1) % SAVE_STABS_COUNT; +} + +/* Provide context for an error. */ + +static void +stab_context (void) +{ + int i; + + fprintf (stderr, _("Last stabs entries before error:\n")); + fprintf (stderr, "n_type n_desc n_value string\n"); + + i = saved_stabs_index; + do + { + struct saved_stab *stabp; + + stabp = saved_stabs + i; + if (stabp->string != NULL) + { + const char *s; + + s = bfd_get_stab_name (stabp->type); + if (s != NULL) + fprintf (stderr, "%-6s", s); + else if (stabp->type == 0) + fprintf (stderr, "HdrSym"); + else + fprintf (stderr, "%-6d", stabp->type); + fprintf (stderr, " %-6d ", stabp->desc); + fprintf_vma (stderr, stabp->value); + if (stabp->type != 0) + fprintf (stderr, " %s", stabp->string); + fprintf (stderr, "\n"); + } + i = (i + 1) % SAVE_STABS_COUNT; + } + while (i != saved_stabs_index); +} + +/* Free the saved stab strings. */ + +static void +free_saved_stabs (void) +{ + int i; + + for (i = 0; i < SAVE_STABS_COUNT; i++) + { + if (saved_stabs[i].string != NULL) + { + free (saved_stabs[i].string); + saved_stabs[i].string = NULL; + } + } + + saved_stabs_index = 0; +} diff --git a/contrib/toolchain/binutils/binutils/rename.c b/contrib/toolchain/binutils/binutils/rename.c new file mode 100644 index 0000000000..0cae099330 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/rename.c @@ -0,0 +1,212 @@ +/* rename.c -- rename a file, preserving symlinks. + Copyright 1999, 2002, 2003, 2005, 2007, 2008 Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "bucomm.h" + +#ifdef HAVE_GOOD_UTIME_H +#include +#else /* ! HAVE_GOOD_UTIME_H */ +#ifdef HAVE_UTIMES +#include +#endif /* HAVE_UTIMES */ +#endif /* ! HAVE_GOOD_UTIME_H */ + +#if ! defined (_WIN32) || defined (__CYGWIN32__) +static int simple_copy (const char *, const char *); + +/* The number of bytes to copy at once. */ +#define COPY_BUF 8192 + +/* Copy file FROM to file TO, performing no translations. + Return 0 if ok, -1 if error. */ + +static int +simple_copy (const char *from, const char *to) +{ + int fromfd, tofd, nread; + int saved; + char buf[COPY_BUF]; + + fromfd = open (from, O_RDONLY | O_BINARY); + if (fromfd < 0) + return -1; +#ifdef O_CREAT + tofd = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0777); +#else + tofd = creat (to, 0777); +#endif + if (tofd < 0) + { + saved = errno; + close (fromfd); + errno = saved; + return -1; + } + while ((nread = read (fromfd, buf, sizeof buf)) > 0) + { + if (write (tofd, buf, nread) != nread) + { + saved = errno; + close (fromfd); + close (tofd); + errno = saved; + return -1; + } + } + saved = errno; + close (fromfd); + close (tofd); + if (nread < 0) + { + errno = saved; + return -1; + } + return 0; +} +#endif /* __CYGWIN32__ or not _WIN32 */ + +/* Set the times of the file DESTINATION to be the same as those in + STATBUF. */ + +void +set_times (const char *destination, const struct stat *statbuf) +{ + int result; + + { +#ifdef HAVE_GOOD_UTIME_H + struct utimbuf tb; + + tb.actime = statbuf->st_atime; + tb.modtime = statbuf->st_mtime; +// result = utime (destination, &tb); +#else /* ! HAVE_GOOD_UTIME_H */ +#ifndef HAVE_UTIMES + long tb[2]; + + tb[0] = statbuf->st_atime; + tb[1] = statbuf->st_mtime; + result = utime (destination, tb); +#else /* HAVE_UTIMES */ + struct timeval tv[2]; + + tv[0].tv_sec = statbuf->st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = statbuf->st_mtime; + tv[1].tv_usec = 0; + result = utimes (destination, tv); +#endif /* HAVE_UTIMES */ +#endif /* ! HAVE_GOOD_UTIME_H */ + } + + if (result != 0) + non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno)); +} + +#ifndef S_ISLNK +#ifdef S_IFLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#else +#define S_ISLNK(m) 0 +#define lstat stat +#endif +#endif + +/* Rename FROM to TO, copying if TO is a link. + Return 0 if ok, -1 if error. */ + +int +smart_rename (const char *from, const char *to, int preserve_dates ATTRIBUTE_UNUSED) +{ + bfd_boolean exists; + struct stat s; + int ret = 0; + + exists = lstat (to, &s) == 0; + +#if 1 + /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but + fail instead. Also, chown is not present. */ + + if (exists) + remove (to); + + ret = rename (from, to); + if (ret != 0) + { + /* We have to clean up here. */ + non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno)); + unlink (from); + } +#else + /* Use rename only if TO is not a symbolic link and has + only one hard link, and we have permission to write to it. */ + if (! exists + || (!S_ISLNK (s.st_mode) + && S_ISREG (s.st_mode) + && (s.st_mode & S_IWUSR) + && s.st_nlink == 1) + ) + { + ret = rename (from, to); + if (ret == 0) + { + if (exists) + { + /* Try to preserve the permission bits and ownership of + TO. First get the mode right except for the setuid + bit. Then change the ownership. Then fix the setuid + bit. We do the chmod before the chown because if the + chown succeeds, and we are a normal user, we won't be + able to do the chmod afterward. We don't bother to + fix the setuid bit first because that might introduce + a fleeting security problem, and because the chown + will clear the setuid bit anyhow. We only fix the + setuid bit if the chown succeeds, because we don't + want to introduce an unexpected setuid file owned by + the user running objcopy. */ +// chmod (to, s.st_mode & 0777); +// if (chown (to, s.st_uid, s.st_gid) >= 0) +// chmod (to, s.st_mode & 07777); + } + } + else + { + /* We have to clean up here. */ + non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno)); + unlink (from); + } + } + else + { + ret = simple_copy (from, to); + if (ret != 0) + non_fatal (_("unable to copy file '%s'; reason: %s"), to, strerror (errno)); + + if (preserve_dates) + set_times (to, &s); + unlink (from); + } +#endif /* _WIN32 && !__CYGWIN32__ */ + + return ret; +} diff --git a/contrib/toolchain/binutils/binutils/stabs.c b/contrib/toolchain/binutils/binutils/stabs.c new file mode 100644 index 0000000000..8b4597780a --- /dev/null +++ b/contrib/toolchain/binutils/binutils/stabs.c @@ -0,0 +1,5433 @@ +/* stabs.c -- Parse stabs debugging information + Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This file contains code which parses stabs debugging information. + The organization of this code is based on the gdb stabs reading + code. The job it does is somewhat different, because it is not + trying to identify the correct address for anything. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "demangle.h" +#include "debug.h" +#include "budbg.h" +#include "filenames.h" +#include "aout/aout64.h" +#include "aout/stab_gnu.h" + +/* The number of predefined XCOFF types. */ + +#define XCOFF_TYPE_COUNT 34 + +/* This structure is used as a handle so that the stab parsing doesn't + need to use any static variables. */ + +struct stab_handle +{ + /* The BFD. */ + bfd *abfd; + /* TRUE if this is stabs in sections. */ + bfd_boolean sections; + /* The symbol table. */ + asymbol **syms; + /* The number of symbols. */ + long symcount; + /* The accumulated file name string. */ + char *so_string; + /* The value of the last N_SO symbol. */ + bfd_vma so_value; + /* The value of the start of the file, so that we can handle file + relative N_LBRAC and N_RBRAC symbols. */ + bfd_vma file_start_offset; + /* The offset of the start of the function, so that we can handle + function relative N_LBRAC and N_RBRAC symbols. */ + bfd_vma function_start_offset; + /* The version number of gcc which compiled the current compilation + unit, 0 if not compiled by gcc. */ + int gcc_compiled; + /* Whether an N_OPT symbol was seen that was not generated by gcc, + so that we can detect the SunPRO compiler. */ + bfd_boolean n_opt_found; + /* The main file name. */ + char *main_filename; + /* A stack of unfinished N_BINCL files. */ + struct bincl_file *bincl_stack; + /* A list of finished N_BINCL files. */ + struct bincl_file *bincl_list; + /* Whether we are inside a function or not. */ + bfd_boolean within_function; + /* The address of the end of the function, used if we have seen an + N_FUN symbol while in a function. This is -1 if we have not seen + an N_FUN (the normal case). */ + bfd_vma function_end; + /* The depth of block nesting. */ + int block_depth; + /* List of pending variable definitions. */ + struct stab_pending_var *pending; + /* Number of files for which we have types. */ + unsigned int files; + /* Lists of types per file. */ + struct stab_types **file_types; + /* Predefined XCOFF types. */ + debug_type xcoff_types[XCOFF_TYPE_COUNT]; + /* Undefined tags. */ + struct stab_tag *tags; + /* Set by parse_stab_type if it sees a structure defined as a cross + reference to itself. Reset by parse_stab_type otherwise. */ + bfd_boolean self_crossref; +}; + +/* A list of these structures is used to hold pending variable + definitions seen before the N_LBRAC of a block. */ + +struct stab_pending_var +{ + /* Next pending variable definition. */ + struct stab_pending_var *next; + /* Name. */ + const char *name; + /* Type. */ + debug_type type; + /* Kind. */ + enum debug_var_kind kind; + /* Value. */ + bfd_vma val; +}; + +/* A list of these structures is used to hold the types for a single + file. */ + +struct stab_types +{ + /* Next set of slots for this file. */ + struct stab_types *next; + /* Types indexed by type number. */ +#define STAB_TYPES_SLOTS (16) + debug_type types[STAB_TYPES_SLOTS]; +}; + +/* We keep a list of undefined tags that we encounter, so that we can + fill them in if the tag is later defined. */ + +struct stab_tag +{ + /* Next undefined tag. */ + struct stab_tag *next; + /* Tag name. */ + const char *name; + /* Type kind. */ + enum debug_type_kind kind; + /* Slot to hold real type when we discover it. If we don't, we fill + in an undefined tag type. */ + debug_type slot; + /* Indirect type we have created to point at slot. */ + debug_type type; +}; + +static char *savestring (const char *, int); +static bfd_vma parse_number (const char **, bfd_boolean *); +static void bad_stab (const char *); +static void warn_stab (const char *, const char *); +static bfd_boolean parse_stab_string + (void *, struct stab_handle *, int, int, bfd_vma, const char *); +static debug_type parse_stab_type + (void *, struct stab_handle *, const char *, const char **, debug_type **); +static bfd_boolean parse_stab_type_number (const char **, int *); +static debug_type parse_stab_range_type + (void *, struct stab_handle *, const char *, const char **, const int *); +static debug_type parse_stab_sun_builtin_type (void *, const char **); +static debug_type parse_stab_sun_floating_type (void *, const char **); +static debug_type parse_stab_enum_type (void *, const char **); +static debug_type parse_stab_struct_type + (void *, struct stab_handle *, const char *, const char **, + bfd_boolean, const int *); +static bfd_boolean parse_stab_baseclasses + (void *, struct stab_handle *, const char **, debug_baseclass **); +static bfd_boolean parse_stab_struct_fields + (void *, struct stab_handle *, const char **, debug_field **, bfd_boolean *); +static bfd_boolean parse_stab_cpp_abbrev + (void *, struct stab_handle *, const char **, debug_field *); +static bfd_boolean parse_stab_one_struct_field + (void *, struct stab_handle *, const char **, const char *, + debug_field *, bfd_boolean *); +static bfd_boolean parse_stab_members + (void *, struct stab_handle *, const char *, const char **, const int *, + debug_method **); +static debug_type parse_stab_argtypes + (void *, struct stab_handle *, debug_type, const char *, const char *, + debug_type, const char *, bfd_boolean, bfd_boolean, const char **); +static bfd_boolean parse_stab_tilde_field + (void *, struct stab_handle *, const char **, const int *, debug_type *, + bfd_boolean *); +static debug_type parse_stab_array_type + (void *, struct stab_handle *, const char **, bfd_boolean); +static void push_bincl (struct stab_handle *, const char *, bfd_vma); +static const char *pop_bincl (struct stab_handle *); +static bfd_boolean find_excl (struct stab_handle *, const char *, bfd_vma); +static bfd_boolean stab_record_variable + (void *, struct stab_handle *, const char *, debug_type, + enum debug_var_kind, bfd_vma); +static bfd_boolean stab_emit_pending_vars (void *, struct stab_handle *); +static debug_type *stab_find_slot (struct stab_handle *, const int *); +static debug_type stab_find_type (void *, struct stab_handle *, const int *); +static bfd_boolean stab_record_type + (void *, struct stab_handle *, const int *, debug_type); +static debug_type stab_xcoff_builtin_type + (void *, struct stab_handle *, int); +static debug_type stab_find_tagged_type + (void *, struct stab_handle *, const char *, int, enum debug_type_kind); +static debug_type *stab_demangle_argtypes + (void *, struct stab_handle *, const char *, bfd_boolean *, unsigned int); +static debug_type *stab_demangle_v3_argtypes + (void *, struct stab_handle *, const char *, bfd_boolean *); +static debug_type *stab_demangle_v3_arglist + (void *, struct stab_handle *, struct demangle_component *, bfd_boolean *); +static debug_type stab_demangle_v3_arg + (void *, struct stab_handle *, struct demangle_component *, debug_type, + bfd_boolean *); + +/* Save a string in memory. */ + +static char * +savestring (const char *start, int len) +{ + char *ret; + + ret = (char *) xmalloc (len + 1); + memcpy (ret, start, len); + ret[len] = '\0'; + return ret; +} + +/* Read a number from a string. */ + +static bfd_vma +parse_number (const char **pp, bfd_boolean *poverflow) +{ + unsigned long ul; + const char *orig; + + if (poverflow != NULL) + *poverflow = FALSE; + + orig = *pp; + + errno = 0; + ul = strtoul (*pp, (char **) pp, 0); + if (ul + 1 != 0 || errno == 0) + { + /* If bfd_vma is larger than unsigned long, and the number is + meant to be negative, we have to make sure that we sign + extend properly. */ + if (*orig == '-') + return (bfd_vma) (bfd_signed_vma) (long) ul; + return (bfd_vma) ul; + } + + /* Note that even though strtoul overflowed, it should have set *pp + to the end of the number, which is where we want it. */ + if (sizeof (bfd_vma) > sizeof (unsigned long)) + { + const char *p; + bfd_boolean neg; + int base; + bfd_vma over, lastdig; + bfd_boolean overflow; + bfd_vma v; + + /* Our own version of strtoul, for a bfd_vma. */ + p = orig; + + neg = FALSE; + if (*p == '+') + ++p; + else if (*p == '-') + { + neg = TRUE; + ++p; + } + + base = 10; + if (*p == '0') + { + if (p[1] == 'x' || p[1] == 'X') + { + base = 16; + p += 2; + } + else + { + base = 8; + ++p; + } + } + + over = ((bfd_vma) (bfd_signed_vma) -1) / (bfd_vma) base; + lastdig = ((bfd_vma) (bfd_signed_vma) -1) % (bfd_vma) base; + + overflow = FALSE; + v = 0; + while (1) + { + int d; + + d = *p++; + if (ISDIGIT (d)) + d -= '0'; + else if (ISUPPER (d)) + d -= 'A'; + else if (ISLOWER (d)) + d -= 'a'; + else + break; + + if (d >= base) + break; + + if (v > over || (v == over && (bfd_vma) d > lastdig)) + { + overflow = TRUE; + break; + } + } + + if (! overflow) + { + if (neg) + v = - v; + return v; + } + } + + /* If we get here, the number is too large to represent in a + bfd_vma. */ + if (poverflow != NULL) + *poverflow = TRUE; + else + warn_stab (orig, _("numeric overflow")); + + return 0; +} + +/* Give an error for a bad stab string. */ + +static void +bad_stab (const char *p) +{ + fprintf (stderr, _("Bad stab: %s\n"), p); +} + +/* Warn about something in a stab string. */ + +static void +warn_stab (const char *p, const char *err) +{ + fprintf (stderr, _("Warning: %s: %s\n"), err, p); +} + +/* Create a handle to parse stabs symbols with. */ + +void * +start_stab (void *dhandle ATTRIBUTE_UNUSED, bfd *abfd, bfd_boolean sections, + asymbol **syms, long symcount) +{ + struct stab_handle *ret; + + ret = (struct stab_handle *) xmalloc (sizeof *ret); + memset (ret, 0, sizeof *ret); + ret->abfd = abfd; + ret->sections = sections; + ret->syms = syms; + ret->symcount = symcount; + ret->files = 1; + ret->file_types = (struct stab_types **) xmalloc (sizeof *ret->file_types); + ret->file_types[0] = NULL; + ret->function_end = (bfd_vma) -1; + return (void *) ret; +} + +/* When we have processed all the stabs information, we need to go + through and fill in all the undefined tags. */ + +bfd_boolean +finish_stab (void *dhandle, void *handle) +{ + struct stab_handle *info = (struct stab_handle *) handle; + struct stab_tag *st; + + if (info->within_function) + { + if (! stab_emit_pending_vars (dhandle, info) + || ! debug_end_function (dhandle, info->function_end)) + return FALSE; + info->within_function = FALSE; + info->function_end = (bfd_vma) -1; + } + + for (st = info->tags; st != NULL; st = st->next) + { + enum debug_type_kind kind; + + kind = st->kind; + if (kind == DEBUG_KIND_ILLEGAL) + kind = DEBUG_KIND_STRUCT; + st->slot = debug_make_undefined_tagged_type (dhandle, st->name, kind); + if (st->slot == DEBUG_TYPE_NULL) + return FALSE; + } + + return TRUE; +} + +/* Handle a single stabs symbol. */ + +bfd_boolean +parse_stab (void *dhandle, void *handle, int type, int desc, bfd_vma value, + const char *string) +{ + struct stab_handle *info = (struct stab_handle *) handle; + + /* gcc will emit two N_SO strings per compilation unit, one for the + directory name and one for the file name. We just collect N_SO + strings as we see them, and start the new compilation unit when + we see a non N_SO symbol. */ + if (info->so_string != NULL + && (type != N_SO || *string == '\0' || value != info->so_value)) + { + if (! debug_set_filename (dhandle, info->so_string)) + return FALSE; + info->main_filename = info->so_string; + + info->gcc_compiled = 0; + info->n_opt_found = FALSE; + + /* Generally, for stabs in the symbol table, the N_LBRAC and + N_RBRAC symbols are relative to the N_SO symbol value. */ + if (! info->sections) + info->file_start_offset = info->so_value; + + /* We need to reset the mapping from type numbers to types. We + can't free the old mapping, because of the use of + debug_make_indirect_type. */ + info->files = 1; + info->file_types = ((struct stab_types **) + xmalloc (sizeof *info->file_types)); + info->file_types[0] = NULL; + + info->so_string = NULL; + + /* Now process whatever type we just got. */ + } + + switch (type) + { + case N_FN: + case N_FN_SEQ: + break; + + case N_LBRAC: + /* Ignore extra outermost context from SunPRO cc and acc. */ + if (info->n_opt_found && desc == 1) + break; + + if (! info->within_function) + { + fprintf (stderr, _("N_LBRAC not within function\n")); + return FALSE; + } + + /* Start an inner lexical block. */ + if (! debug_start_block (dhandle, + (value + + info->file_start_offset + + info->function_start_offset))) + return FALSE; + + /* Emit any pending variable definitions. */ + if (! stab_emit_pending_vars (dhandle, info)) + return FALSE; + + ++info->block_depth; + break; + + case N_RBRAC: + /* Ignore extra outermost context from SunPRO cc and acc. */ + if (info->n_opt_found && desc == 1) + break; + + /* We shouldn't have any pending variable definitions here, but, + if we do, we probably need to emit them before closing the + block. */ + if (! stab_emit_pending_vars (dhandle, info)) + return FALSE; + + /* End an inner lexical block. */ + if (! debug_end_block (dhandle, + (value + + info->file_start_offset + + info->function_start_offset))) + return FALSE; + + --info->block_depth; + if (info->block_depth < 0) + { + fprintf (stderr, _("Too many N_RBRACs\n")); + return FALSE; + } + break; + + case N_SO: + /* This always ends a function. */ + if (info->within_function) + { + bfd_vma endval; + + endval = value; + if (*string != '\0' + && info->function_end != (bfd_vma) -1 + && info->function_end < endval) + endval = info->function_end; + if (! stab_emit_pending_vars (dhandle, info) + || ! debug_end_function (dhandle, endval)) + return FALSE; + info->within_function = FALSE; + info->function_end = (bfd_vma) -1; + } + + /* An empty string is emitted by gcc at the end of a compilation + unit. */ + if (*string == '\0') + return TRUE; + + /* Just accumulate strings until we see a non N_SO symbol. If + the string starts with a directory separator or some other + form of absolute path specification, we discard the previously + accumulated strings. */ + if (info->so_string == NULL) + info->so_string = xstrdup (string); + else + { + char *f; + + f = info->so_string; + + if (IS_ABSOLUTE_PATH (string)) + info->so_string = xstrdup (string); + else + info->so_string = concat (info->so_string, string, + (const char *) NULL); + free (f); + } + + info->so_value = value; + + break; + + case N_SOL: + /* Start an include file. */ + if (! debug_start_source (dhandle, string)) + return FALSE; + break; + + case N_BINCL: + /* Start an include file which may be replaced. */ + push_bincl (info, string, value); + if (! debug_start_source (dhandle, string)) + return FALSE; + break; + + case N_EINCL: + /* End an N_BINCL include. */ + if (! debug_start_source (dhandle, pop_bincl (info))) + return FALSE; + break; + + case N_EXCL: + /* This is a duplicate of a header file named by N_BINCL which + was eliminated by the linker. */ + if (! find_excl (info, string, value)) + return FALSE; + break; + + case N_SLINE: + if (! debug_record_line (dhandle, desc, + value + (info->within_function + ? info->function_start_offset : 0))) + return FALSE; + break; + + case N_BCOMM: + if (! debug_start_common_block (dhandle, string)) + return FALSE; + break; + + case N_ECOMM: + if (! debug_end_common_block (dhandle, string)) + return FALSE; + break; + + case N_FUN: + if (*string == '\0') + { + if (info->within_function) + { + /* This always marks the end of a function; we don't + need to worry about info->function_end. */ + if (info->sections) + value += info->function_start_offset; + if (! stab_emit_pending_vars (dhandle, info) + || ! debug_end_function (dhandle, value)) + return FALSE; + info->within_function = FALSE; + info->function_end = (bfd_vma) -1; + } + break; + } + + /* A const static symbol in the .text section will have an N_FUN + entry. We need to use these to mark the end of the function, + in case we are looking at gcc output before it was changed to + always emit an empty N_FUN. We can't call debug_end_function + here, because it might be a local static symbol. */ + if (info->within_function + && (info->function_end == (bfd_vma) -1 + || value < info->function_end)) + info->function_end = value; + + /* Fall through. */ + /* FIXME: gdb checks the string for N_STSYM, N_LCSYM or N_ROSYM + symbols, and if it does not start with :S, gdb relocates the + value to the start of the section. gcc always seems to use + :S, so we don't worry about this. */ + /* Fall through. */ + default: + { + const char *colon; + + colon = strchr (string, ':'); + if (colon != NULL + && (colon[1] == 'f' || colon[1] == 'F')) + { + if (info->within_function) + { + bfd_vma endval; + + endval = value; + if (info->function_end != (bfd_vma) -1 + && info->function_end < endval) + endval = info->function_end; + if (! stab_emit_pending_vars (dhandle, info) + || ! debug_end_function (dhandle, endval)) + return FALSE; + info->function_end = (bfd_vma) -1; + } + /* For stabs in sections, line numbers and block addresses + are offsets from the start of the function. */ + if (info->sections) + info->function_start_offset = value; + info->within_function = TRUE; + } + + if (! parse_stab_string (dhandle, info, type, desc, value, string)) + return FALSE; + } + break; + + case N_OPT: + if (string != NULL && strcmp (string, "gcc2_compiled.") == 0) + info->gcc_compiled = 2; + else if (string != NULL && strcmp (string, "gcc_compiled.") == 0) + info->gcc_compiled = 1; + else + info->n_opt_found = TRUE; + break; + + case N_OBJ: + case N_ENDM: + case N_MAIN: + case N_WARNING: + break; + } + + return TRUE; +} + +/* Parse the stabs string. */ + +static bfd_boolean +parse_stab_string (void *dhandle, struct stab_handle *info, int stabtype, + int desc ATTRIBUTE_UNUSED, bfd_vma value, const char *string) +{ + const char *p; + char *name; + int type; + debug_type dtype; + bfd_boolean synonym; + bfd_boolean self_crossref; + debug_type *slot; + + p = strchr (string, ':'); + if (p == NULL) + return TRUE; + + while (p[1] == ':') + { + p += 2; + p = strchr (p, ':'); + if (p == NULL) + { + bad_stab (string); + return FALSE; + } + } + + /* FIXME: Sometimes the special C++ names start with '.'. */ + name = NULL; + if (string[0] == '$') + { + switch (string[1]) + { + case 't': + name = "this"; + break; + case 'v': + /* Was: name = "vptr"; */ + break; + case 'e': + name = "eh_throw"; + break; + case '_': + /* This was an anonymous type that was never fixed up. */ + break; + case 'X': + /* SunPRO (3.0 at least) static variable encoding. */ + break; + default: + warn_stab (string, _("unknown C++ encoded name")); + break; + } + } + + if (name == NULL) + { + if (p == string || (string[0] == ' ' && p == string + 1)) + name = NULL; + else + name = savestring (string, p - string); + } + + ++p; + if (ISDIGIT (*p) || *p == '(' || *p == '-') + type = 'l'; + else + type = *p++; + + switch (type) + { + case 'c': + /* c is a special case, not followed by a type-number. + SYMBOL:c=iVALUE for an integer constant symbol. + SYMBOL:c=rVALUE for a floating constant symbol. + SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol. + e.g. "b:c=e6,0" for "const b = blob1" + (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */ + if (*p != '=') + { + bad_stab (string); + return FALSE; + } + ++p; + switch (*p++) + { + case 'r': + /* Floating point constant. */ + if (! debug_record_float_const (dhandle, name, atof (p))) + return FALSE; + break; + case 'i': + /* Integer constant. */ + /* Defining integer constants this way is kind of silly, + since 'e' constants allows the compiler to give not only + the value, but the type as well. C has at least int, + long, unsigned int, and long long as constant types; + other languages probably should have at least unsigned as + well as signed constants. */ + if (! debug_record_int_const (dhandle, name, atoi (p))) + return FALSE; + break; + case 'e': + /* SYMBOL:c=eTYPE,INTVALUE for a constant symbol whose value + can be represented as integral. + e.g. "b:c=e6,0" for "const b = blob1" + (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, + &p, (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (*p != ',') + { + bad_stab (string); + return FALSE; + } + if (! debug_record_typed_const (dhandle, name, dtype, atoi (p))) + return FALSE; + break; + default: + bad_stab (string); + return FALSE; + } + + break; + + case 'C': + /* The name of a caught exception. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, + &p, (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_label (dhandle, name, dtype, value)) + return FALSE; + break; + + case 'f': + case 'F': + /* A function definition. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_function (dhandle, name, dtype, type == 'F', value)) + return FALSE; + + /* Sun acc puts declared types of arguments here. We don't care + about their actual types (FIXME -- we should remember the whole + function prototype), but the list may define some new types + that we have to remember, so we must scan it now. */ + while (*p == ';') + { + ++p; + if (parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL) + == DEBUG_TYPE_NULL) + return FALSE; + } + + break; + + case 'G': + { + char leading; + long c; + asymbol **ps; + + /* A global symbol. The value must be extracted from the + symbol table. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + leading = bfd_get_symbol_leading_char (info->abfd); + for (c = info->symcount, ps = info->syms; c > 0; --c, ++ps) + { + const char *n; + + n = bfd_asymbol_name (*ps); + if (leading != '\0' && *n == leading) + ++n; + if (*n == *name && strcmp (n, name) == 0) + break; + } + if (c > 0) + value = bfd_asymbol_value (*ps); + if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_GLOBAL, + value)) + return FALSE; + } + break; + + /* This case is faked by a conditional above, when there is no + code letter in the dbx data. Dbx data never actually + contains 'l'. */ + case 'l': + case 's': + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_LOCAL, + value)) + return FALSE; + break; + + case 'p': + /* A function parameter. */ + if (*p != 'F') + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + else + { + /* pF is a two-letter code that means a function parameter in + Fortran. The type-number specifies the type of the return + value. Translate it into a pointer-to-function type. */ + ++p; + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype != DEBUG_TYPE_NULL) + { + debug_type ftype; + + ftype = debug_make_function_type (dhandle, dtype, + (debug_type *) NULL, FALSE); + dtype = debug_make_pointer_type (dhandle, ftype); + } + } + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_STACK, + value)) + return FALSE; + + /* FIXME: At this point gdb considers rearranging the parameter + address on a big endian machine if it is smaller than an int. + We have no way to do that, since we don't really know much + about the target. */ + break; + + case 'P': + if (stabtype == N_FUN) + { + /* Prototype of a function referenced by this file. */ + while (*p == ';') + { + ++p; + if (parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL) + == DEBUG_TYPE_NULL) + return FALSE; + } + break; + } + /* Fall through. */ + case 'R': + /* Parameter which is in a register. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REG, + value)) + return FALSE; + break; + + case 'r': + /* Register variable (either global or local). */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_REGISTER, + value)) + return FALSE; + + /* FIXME: At this point gdb checks to combine pairs of 'p' and + 'r' stabs into a single 'P' stab. */ + break; + + case 'S': + /* Static symbol at top level of file. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_STATIC, + value)) + return FALSE; + break; + + case 't': + /* A typedef. */ + dtype = parse_stab_type (dhandle, info, name, &p, &slot); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (name == NULL) + { + /* A nameless type. Nothing to do. */ + return TRUE; + } + + dtype = debug_name_type (dhandle, name, dtype); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + + if (slot != NULL) + *slot = dtype; + + break; + + case 'T': + /* Struct, union, or enum tag. For GNU C++, this can be be followed + by 't' which means we are typedef'ing it as well. */ + if (*p != 't') + { + synonym = FALSE; + /* FIXME: gdb sets synonym to TRUE if the current language + is C++. */ + } + else + { + synonym = TRUE; + ++p; + } + + dtype = parse_stab_type (dhandle, info, name, &p, &slot); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (name == NULL) + return TRUE; + + /* INFO->SELF_CROSSREF is set by parse_stab_type if this type is + a cross reference to itself. These are generated by some + versions of g++. */ + self_crossref = info->self_crossref; + + dtype = debug_tag_type (dhandle, name, dtype); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (slot != NULL) + *slot = dtype; + + /* See if we have a cross reference to this tag which we can now + fill in. Avoid filling in a cross reference to ourselves, + because that would lead to circular debugging information. */ + if (! self_crossref) + { + register struct stab_tag **pst; + + for (pst = &info->tags; *pst != NULL; pst = &(*pst)->next) + { + if ((*pst)->name[0] == name[0] + && strcmp ((*pst)->name, name) == 0) + { + (*pst)->slot = dtype; + *pst = (*pst)->next; + break; + } + } + } + + if (synonym) + { + dtype = debug_name_type (dhandle, name, dtype); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + + if (slot != NULL) + *slot = dtype; + } + + break; + + case 'V': + /* Static symbol of local scope */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + /* FIXME: gdb checks os9k_stabs here. */ + if (! stab_record_variable (dhandle, info, name, dtype, + DEBUG_LOCAL_STATIC, value)) + return FALSE; + break; + + case 'v': + /* Reference parameter. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REFERENCE, + value)) + return FALSE; + break; + + case 'a': + /* Reference parameter which is in a register. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REF_REG, + value)) + return FALSE; + break; + + case 'X': + /* This is used by Sun FORTRAN for "function result value". + Sun claims ("dbx and dbxtool interfaces", 2nd ed) + that Pascal uses it too, but when I tried it Pascal used + "x:3" (local symbol) instead. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p, + (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return FALSE; + if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_LOCAL, + value)) + return FALSE; + break; + + case 'Y': + /* SUNPro C++ Namespace =Yn0. */ + /* Skip the namespace mapping, as it is not used now. */ + if (*(++p) == 'n' && *(++p) == '0') + { + /* =Yn0name; */ + while (*p != ';') + ++p; + ++p; + return TRUE; + } + /* TODO SUNPro C++ support: + Support default arguments after F,P parameters + Ya = Anonymous unions + YM,YD = Pointers to class members + YT,YI = Templates + YR = Run-time type information (RTTI) */ + + /* Fall through. */ + + default: + bad_stab (string); + return FALSE; + } + + /* FIXME: gdb converts structure values to structure pointers in a + couple of cases, depending upon the target. */ + + return TRUE; +} + +/* Parse a stabs type. The typename argument is non-NULL if this is a + typedef or a tag definition. The pp argument points to the stab + string, and is updated. The slotp argument points to a place to + store the slot used if the type is being defined. */ + +static debug_type +parse_stab_type (void *dhandle, struct stab_handle *info, const char *type_name, const char **pp, debug_type **slotp) +{ + const char *orig; + int typenums[2]; + int size; + bfd_boolean stringp; + int descriptor; + debug_type dtype; + + if (slotp != NULL) + *slotp = NULL; + + orig = *pp; + + size = -1; + stringp = FALSE; + + info->self_crossref = FALSE; + + /* Read type number if present. The type number may be omitted. + for instance in a two-dimensional array declared with type + "ar1;1;10;ar1;1;10;4". */ + if (! ISDIGIT (**pp) && **pp != '(' && **pp != '-') + { + /* 'typenums=' not present, type is anonymous. Read and return + the definition, but don't put it in the type vector. */ + typenums[0] = typenums[1] = -1; + } + else + { + if (! parse_stab_type_number (pp, typenums)) + return DEBUG_TYPE_NULL; + + if (**pp != '=') + /* Type is not being defined here. Either it already + exists, or this is a forward reference to it. */ + return stab_find_type (dhandle, info, typenums); + + /* Only set the slot if the type is being defined. This means + that the mapping from type numbers to types will only record + the name of the typedef which defines a type. If we don't do + this, then something like + typedef int foo; + int i; + will record that i is of type foo. Unfortunately, stabs + information is ambiguous about variable types. For this code, + typedef int foo; + int i; + foo j; + the stabs information records both i and j as having the same + type. This could be fixed by patching the compiler. */ + if (slotp != NULL && typenums[0] >= 0 && typenums[1] >= 0) + *slotp = stab_find_slot (info, typenums); + + /* Type is being defined here. */ + /* Skip the '='. */ + ++*pp; + + while (**pp == '@') + { + const char *p = *pp + 1; + const char *attr; + + if (ISDIGIT (*p) || *p == '(' || *p == '-') + /* Member type. */ + break; + + /* Type attributes. */ + attr = p; + + for (; *p != ';'; ++p) + { + if (*p == '\0') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + } + *pp = p + 1; + + switch (*attr) + { + case 's': + size = atoi (attr + 1); + size /= 8; /* Size is in bits. We store it in bytes. */ + if (size <= 0) + size = -1; + break; + + case 'S': + stringp = TRUE; + break; + + default: + /* Ignore unrecognized type attributes, so future + compilers can invent new ones. */ + break; + } + } + } + + descriptor = **pp; + ++*pp; + + switch (descriptor) + { + case 'x': + { + enum debug_type_kind code; + const char *q1, *q2, *p; + + /* A cross reference to another type. */ + switch (**pp) + { + case 's': + code = DEBUG_KIND_STRUCT; + break; + case 'u': + code = DEBUG_KIND_UNION; + break; + case 'e': + code = DEBUG_KIND_ENUM; + break; + default: + /* Complain and keep going, so compilers can invent new + cross-reference types. */ + warn_stab (orig, _("unrecognized cross reference type")); + code = DEBUG_KIND_STRUCT; + break; + } + ++*pp; + + q1 = strchr (*pp, '<'); + p = strchr (*pp, ':'); + if (p == NULL) + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + if (q1 != NULL && p > q1 && p[1] == ':') + { + int nest = 0; + + for (q2 = q1; *q2 != '\0'; ++q2) + { + if (*q2 == '<') + ++nest; + else if (*q2 == '>') + --nest; + else if (*q2 == ':' && nest == 0) + break; + } + p = q2; + if (*p != ':') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + } + + /* Some versions of g++ can emit stabs like + fleep:T20=xsfleep: + which define structures in terms of themselves. We need to + tell the caller to avoid building a circular structure. */ + if (type_name != NULL + && strncmp (type_name, *pp, p - *pp) == 0 + && type_name[p - *pp] == '\0') + info->self_crossref = TRUE; + + dtype = stab_find_tagged_type (dhandle, info, *pp, p - *pp, code); + + *pp = p + 1; + } + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '(': + { + const char *hold; + int xtypenums[2]; + + /* This type is defined as another type. */ + (*pp)--; + hold = *pp; + + /* Peek ahead at the number to detect void. */ + if (! parse_stab_type_number (pp, xtypenums)) + return DEBUG_TYPE_NULL; + + if (typenums[0] == xtypenums[0] && typenums[1] == xtypenums[1]) + { + /* This type is being defined as itself, which means that + it is void. */ + dtype = debug_make_void_type (dhandle); + } + else + { + *pp = hold; + + /* Go back to the number and have parse_stab_type get it. + This means that we can deal with something like + t(1,2)=(3,4)=... which the Lucid compiler uses. */ + dtype = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (dtype == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + } + + if (typenums[0] != -1) + { + if (! stab_record_type (dhandle, info, typenums, dtype)) + return DEBUG_TYPE_NULL; + } + + break; + } + + case '*': + dtype = debug_make_pointer_type (dhandle, + parse_stab_type (dhandle, info, + (const char *) NULL, + pp, + (debug_type **) NULL)); + break; + + case '&': + /* Reference to another type. */ + dtype = (debug_make_reference_type + (dhandle, + parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL))); + break; + + case 'f': + /* Function returning another type. */ + /* FIXME: gdb checks os9k_stabs here. */ + dtype = (debug_make_function_type + (dhandle, + parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL), + (debug_type *) NULL, FALSE)); + break; + + case 'k': + /* Const qualifier on some type (Sun). */ + /* FIXME: gdb accepts 'c' here if os9k_stabs. */ + dtype = debug_make_const_type (dhandle, + parse_stab_type (dhandle, info, + (const char *) NULL, + pp, + (debug_type **) NULL)); + break; + + case 'B': + /* Volatile qual on some type (Sun). */ + /* FIXME: gdb accepts 'i' here if os9k_stabs. */ + dtype = (debug_make_volatile_type + (dhandle, + parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL))); + break; + + case '@': + /* Offset (class & variable) type. This is used for a pointer + relative to an object. */ + { + debug_type domain; + debug_type memtype; + + /* Member type. */ + + domain = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (domain == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + if (**pp != ',') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + memtype = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (memtype == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + dtype = debug_make_offset_type (dhandle, domain, memtype); + } + break; + + case '#': + /* Method (class & fn) type. */ + if (**pp == '#') + { + debug_type return_type; + + ++*pp; + return_type = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (return_type == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + dtype = debug_make_method_type (dhandle, return_type, + DEBUG_TYPE_NULL, + (debug_type *) NULL, FALSE); + } + else + { + debug_type domain; + debug_type return_type; + debug_type *args; + unsigned int n; + unsigned int alloc; + bfd_boolean varargs; + + domain = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (domain == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + if (**pp != ',') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + return_type = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (return_type == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + alloc = 10; + args = (debug_type *) xmalloc (alloc * sizeof *args); + n = 0; + while (**pp != ';') + { + if (**pp != ',') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + if (n + 1 >= alloc) + { + alloc += 10; + args = ((debug_type *) + xrealloc (args, alloc * sizeof *args)); + } + + args[n] = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (args[n] == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + ++n; + } + ++*pp; + + /* If the last type is not void, then this function takes a + variable number of arguments. Otherwise, we must strip + the void type. */ + if (n == 0 + || debug_get_type_kind (dhandle, args[n - 1]) != DEBUG_KIND_VOID) + varargs = TRUE; + else + { + --n; + varargs = FALSE; + } + + args[n] = DEBUG_TYPE_NULL; + + dtype = debug_make_method_type (dhandle, return_type, domain, args, + varargs); + } + break; + + case 'r': + /* Range type. */ + dtype = parse_stab_range_type (dhandle, info, type_name, pp, typenums); + break; + + case 'b': + /* FIXME: gdb checks os9k_stabs here. */ + /* Sun ACC builtin int type. */ + dtype = parse_stab_sun_builtin_type (dhandle, pp); + break; + + case 'R': + /* Sun ACC builtin float type. */ + dtype = parse_stab_sun_floating_type (dhandle, pp); + break; + + case 'e': + /* Enumeration type. */ + dtype = parse_stab_enum_type (dhandle, pp); + break; + + case 's': + case 'u': + /* Struct or union type. */ + dtype = parse_stab_struct_type (dhandle, info, type_name, pp, + descriptor == 's', typenums); + break; + + case 'a': + /* Array type. */ + if (**pp != 'r') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + dtype = parse_stab_array_type (dhandle, info, pp, stringp); + break; + + case 'S': + dtype = debug_make_set_type (dhandle, + parse_stab_type (dhandle, info, + (const char *) NULL, + pp, + (debug_type **) NULL), + stringp); + break; + + default: + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + + if (dtype == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + if (typenums[0] != -1) + { + if (! stab_record_type (dhandle, info, typenums, dtype)) + return DEBUG_TYPE_NULL; + } + + if (size != -1) + { + if (! debug_record_type_size (dhandle, dtype, (unsigned int) size)) + return DEBUG_TYPE_NULL; + } + + return dtype; +} + +/* Read a number by which a type is referred to in dbx data, or + perhaps read a pair (FILENUM, TYPENUM) in parentheses. Just a + single number N is equivalent to (0,N). Return the two numbers by + storing them in the vector TYPENUMS. */ + +static bfd_boolean +parse_stab_type_number (const char **pp, int *typenums) +{ + const char *orig; + + orig = *pp; + + if (**pp != '(') + { + typenums[0] = 0; + typenums[1] = (int) parse_number (pp, (bfd_boolean *) NULL); + } + else + { + ++*pp; + typenums[0] = (int) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ',') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + typenums[1] = (int) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ')') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + } + + return TRUE; +} + +/* Parse a range type. */ + +static debug_type +parse_stab_range_type (void *dhandle, struct stab_handle *info, const char *type_name, const char **pp, const int *typenums) +{ + const char *orig; + int rangenums[2]; + bfd_boolean self_subrange; + debug_type index_type; + const char *s2, *s3; + bfd_signed_vma n2, n3; + bfd_boolean ov2, ov3; + + orig = *pp; + + index_type = DEBUG_TYPE_NULL; + + /* First comes a type we are a subrange of. + In C it is usually 0, 1 or the type being defined. */ + if (! parse_stab_type_number (pp, rangenums)) + return DEBUG_TYPE_NULL; + + self_subrange = (rangenums[0] == typenums[0] + && rangenums[1] == typenums[1]); + + if (**pp == '=') + { + *pp = orig; + index_type = parse_stab_type (dhandle, info, (const char *) NULL, + pp, (debug_type **) NULL); + if (index_type == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + } + + if (**pp == ';') + ++*pp; + + /* The remaining two operands are usually lower and upper bounds of + the range. But in some special cases they mean something else. */ + s2 = *pp; + n2 = parse_number (pp, &ov2); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + s3 = *pp; + n3 = parse_number (pp, &ov3); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + if (ov2 || ov3) + { + /* gcc will emit range stabs for long long types. Handle this + as a special case. FIXME: This needs to be more general. */ +#define LLLOW "01000000000000000000000;" +#define LLHIGH "0777777777777777777777;" +#define ULLHIGH "01777777777777777777777;" + if (index_type == DEBUG_TYPE_NULL) + { + if (CONST_STRNEQ (s2, LLLOW) + && CONST_STRNEQ (s3, LLHIGH)) + return debug_make_int_type (dhandle, 8, FALSE); + if (! ov2 + && n2 == 0 + && CONST_STRNEQ (s3, ULLHIGH)) + return debug_make_int_type (dhandle, 8, TRUE); + } + + warn_stab (orig, _("numeric overflow")); + } + + if (index_type == DEBUG_TYPE_NULL) + { + /* A type defined as a subrange of itself, with both bounds 0, + is void. */ + if (self_subrange && n2 == 0 && n3 == 0) + return debug_make_void_type (dhandle); + + /* A type defined as a subrange of itself, with n2 positive and + n3 zero, is a complex type, and n2 is the number of bytes. */ + if (self_subrange && n3 == 0 && n2 > 0) + return debug_make_complex_type (dhandle, n2); + + /* If n3 is zero and n2 is positive, this is a floating point + type, and n2 is the number of bytes. */ + if (n3 == 0 && n2 > 0) + return debug_make_float_type (dhandle, n2); + + /* If the upper bound is -1, this is an unsigned int. */ + if (n2 == 0 && n3 == -1) + { + /* When gcc is used with -gstabs, but not -gstabs+, it will emit + long long int:t6=r1;0;-1; + long long unsigned int:t7=r1;0;-1; + We hack here to handle this reasonably. */ + if (type_name != NULL) + { + if (strcmp (type_name, "long long int") == 0) + return debug_make_int_type (dhandle, 8, FALSE); + else if (strcmp (type_name, "long long unsigned int") == 0) + return debug_make_int_type (dhandle, 8, TRUE); + } + /* FIXME: The size here really depends upon the target. */ + return debug_make_int_type (dhandle, 4, TRUE); + } + + /* A range of 0 to 127 is char. */ + if (self_subrange && n2 == 0 && n3 == 127) + return debug_make_int_type (dhandle, 1, FALSE); + + /* FIXME: gdb checks for the language CHILL here. */ + + if (n2 == 0) + { + if (n3 < 0) + return debug_make_int_type (dhandle, - n3, TRUE); + else if (n3 == 0xff) + return debug_make_int_type (dhandle, 1, TRUE); + else if (n3 == 0xffff) + return debug_make_int_type (dhandle, 2, TRUE); + else if (n3 == (bfd_signed_vma) 0xffffffff) + return debug_make_int_type (dhandle, 4, TRUE); +#ifdef BFD64 + else if (n3 == ((((bfd_signed_vma) 0xffffffff) << 32) | 0xffffffff)) + return debug_make_int_type (dhandle, 8, TRUE); +#endif + } + else if (n3 == 0 + && n2 < 0 + && (self_subrange || n2 == -8)) + return debug_make_int_type (dhandle, - n2, TRUE); + else if (n2 == - n3 - 1 || n2 == n3 + 1) + { + if (n3 == 0x7f) + return debug_make_int_type (dhandle, 1, FALSE); + else if (n3 == 0x7fff) + return debug_make_int_type (dhandle, 2, FALSE); + else if (n3 == 0x7fffffff) + return debug_make_int_type (dhandle, 4, FALSE); +#ifdef BFD64 + else if (n3 == ((((bfd_vma) 0x7fffffff) << 32) | 0xffffffff)) + return debug_make_int_type (dhandle, 8, FALSE); +#endif + } + } + + /* At this point I don't have the faintest idea how to deal with a + self_subrange type; I'm going to assume that this is used as an + idiom, and that all of them are special cases. So . . . */ + if (self_subrange) + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + + index_type = stab_find_type (dhandle, info, rangenums); + if (index_type == DEBUG_TYPE_NULL) + { + /* Does this actually ever happen? Is that why we are worrying + about dealing with it rather than just calling error_type? */ + warn_stab (orig, _("missing index type")); + index_type = debug_make_int_type (dhandle, 4, FALSE); + } + + return debug_make_range_type (dhandle, index_type, n2, n3); +} + +/* Sun's ACC uses a somewhat saner method for specifying the builtin + typedefs in every file (for int, long, etc): + + type = b ; ; + signed = u or s. Possible c in addition to u or s (for char?). + offset = offset from high order bit to start bit of type. + width is # bytes in object of this type, nbits is # bits in type. + + The width/offset stuff appears to be for small objects stored in + larger ones (e.g. `shorts' in `int' registers). We ignore it for now, + FIXME. */ + +static debug_type +parse_stab_sun_builtin_type (void *dhandle, const char **pp) +{ + const char *orig; + bfd_boolean unsignedp; + bfd_vma bits; + + orig = *pp; + + switch (**pp) + { + case 's': + unsignedp = FALSE; + break; + case 'u': + unsignedp = TRUE; + break; + default: + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + /* OpenSolaris source code indicates that one of "cbv" characters + can come next and specify the intrinsic 'iformat' encoding. + 'c' is character encoding, 'b' is boolean encoding, and 'v' is + varargs encoding. This field can be safely ignored because + the type of the field is determined from the bitwidth extracted + below. */ + if (**pp == 'c' || **pp == 'b' || **pp == 'v') + ++*pp; + + /* The first number appears to be the number of bytes occupied + by this type, except that unsigned short is 4 instead of 2. + Since this information is redundant with the third number, + we will ignore it. */ + (void) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + /* The second number is always 0, so ignore it too. */ + (void) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + /* The third number is the number of bits for this type. */ + bits = parse_number (pp, (bfd_boolean *) NULL); + + /* The type *should* end with a semicolon. If it are embedded + in a larger type the semicolon may be the only way to know where + the type ends. If this type is at the end of the stabstring we + can deal with the omitted semicolon (but we don't have to like + it). Don't bother to complain(), Sun's compiler omits the semicolon + for "void". */ + if (**pp == ';') + ++*pp; + + if (bits == 0) + return debug_make_void_type (dhandle); + + return debug_make_int_type (dhandle, bits / 8, unsignedp); +} + +/* Parse a builtin floating type generated by the Sun compiler. */ + +static debug_type +parse_stab_sun_floating_type (void *dhandle, const char **pp) +{ + const char *orig; + bfd_vma details; + bfd_vma bytes; + + orig = *pp; + + /* The first number has more details about the type, for example + FN_COMPLEX. */ + details = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + + /* The second number is the number of bytes occupied by this type */ + bytes = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + + if (details == NF_COMPLEX + || details == NF_COMPLEX16 + || details == NF_COMPLEX32) + return debug_make_complex_type (dhandle, bytes); + + return debug_make_float_type (dhandle, bytes); +} + +/* Handle an enum type. */ + +static debug_type +parse_stab_enum_type (void *dhandle, const char **pp) +{ + const char *orig; + const char **names; + bfd_signed_vma *values; + unsigned int n; + unsigned int alloc; + + orig = *pp; + + /* FIXME: gdb checks os9k_stabs here. */ + + /* The aix4 compiler emits an extra field before the enum members; + my guess is it's a type of some sort. Just ignore it. */ + if (**pp == '-') + { + while (**pp != ':') + ++*pp; + ++*pp; + } + + /* Read the value-names and their values. + The input syntax is NAME:VALUE,NAME:VALUE, and so on. + A semicolon or comma instead of a NAME means the end. */ + alloc = 10; + names = (const char **) xmalloc (alloc * sizeof *names); + values = (bfd_signed_vma *) xmalloc (alloc * sizeof *values); + n = 0; + while (**pp != '\0' && **pp != ';' && **pp != ',') + { + const char *p; + char *name; + bfd_signed_vma val; + + p = *pp; + while (*p != ':') + ++p; + + name = savestring (*pp, p - *pp); + + *pp = p + 1; + val = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ',') + { + bad_stab (orig); + free (name); + free (names); + free (values); + return DEBUG_TYPE_NULL; + } + ++*pp; + + if (n + 1 >= alloc) + { + alloc += 10; + names = ((const char **) + xrealloc (names, alloc * sizeof *names)); + values = ((bfd_signed_vma *) + xrealloc (values, alloc * sizeof *values)); + } + + names[n] = name; + values[n] = val; + ++n; + } + + names[n] = NULL; + values[n] = 0; + + if (**pp == ';') + ++*pp; + + return debug_make_enum_type (dhandle, names, values); +} + +/* Read the description of a structure (or union type) and return an object + describing the type. + + PP points to a character pointer that points to the next unconsumed token + in the stabs string. For example, given stabs "A:T4=s4a:1,0,32;;", + *PP will point to "4a:1,0,32;;". */ + +static debug_type +parse_stab_struct_type (void *dhandle, struct stab_handle *info, + const char *tagname, const char **pp, + bfd_boolean structp, const int *typenums) +{ + bfd_vma size; + debug_baseclass *baseclasses; + debug_field *fields = NULL; + bfd_boolean statics; + debug_method *methods; + debug_type vptrbase; + bfd_boolean ownvptr; + + /* Get the size. */ + size = parse_number (pp, (bfd_boolean *) NULL); + + /* Get the other information. */ + if (! parse_stab_baseclasses (dhandle, info, pp, &baseclasses) + || ! parse_stab_struct_fields (dhandle, info, pp, &fields, &statics) + || ! parse_stab_members (dhandle, info, tagname, pp, typenums, &methods) + || ! parse_stab_tilde_field (dhandle, info, pp, typenums, &vptrbase, + &ownvptr)) + { + if (fields != NULL) + free (fields); + return DEBUG_TYPE_NULL; + } + + if (! statics + && baseclasses == NULL + && methods == NULL + && vptrbase == DEBUG_TYPE_NULL + && ! ownvptr) + return debug_make_struct_type (dhandle, structp, size, fields); + + return debug_make_object_type (dhandle, structp, size, fields, baseclasses, + methods, vptrbase, ownvptr); +} + +/* The stabs for C++ derived classes contain baseclass information which + is marked by a '!' character after the total size. This function is + called when we encounter the baseclass marker, and slurps up all the + baseclass information. + + Immediately following the '!' marker is the number of base classes that + the class is derived from, followed by information for each base class. + For each base class, there are two visibility specifiers, a bit offset + to the base class information within the derived class, a reference to + the type for the base class, and a terminating semicolon. + + A typical example, with two base classes, would be "!2,020,19;0264,21;". + ^^ ^ ^ ^ ^ ^ ^ + Baseclass information marker __________________|| | | | | | | + Number of baseclasses __________________________| | | | | | | + Visibility specifiers (2) ________________________| | | | | | + Offset in bits from start of class _________________| | | | | + Type number for base class ___________________________| | | | + Visibility specifiers (2) _______________________________| | | + Offset in bits from start of class ________________________| | + Type number of base class ____________________________________| + + Return TRUE for success, FALSE for failure. */ + +static bfd_boolean +parse_stab_baseclasses (void *dhandle, struct stab_handle *info, + const char **pp, debug_baseclass **retp) +{ + const char *orig; + unsigned int c, i; + debug_baseclass *classes; + + *retp = NULL; + + orig = *pp; + + if (**pp != '!') + { + /* No base classes. */ + return TRUE; + } + ++*pp; + + c = (unsigned int) parse_number (pp, (bfd_boolean *) NULL); + + if (**pp != ',') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + + classes = (debug_baseclass *) xmalloc ((c + 1) * sizeof (**retp)); + + for (i = 0; i < c; i++) + { + bfd_boolean is_virtual; + enum debug_visibility visibility; + bfd_vma bitpos; + debug_type type; + + switch (**pp) + { + case '0': + is_virtual = FALSE; + break; + case '1': + is_virtual = TRUE; + break; + default: + warn_stab (orig, _("unknown virtual character for baseclass")); + is_virtual = FALSE; + break; + } + ++*pp; + + switch (**pp) + { + case '0': + visibility = DEBUG_VISIBILITY_PRIVATE; + break; + case '1': + visibility = DEBUG_VISIBILITY_PROTECTED; + break; + case '2': + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + default: + warn_stab (orig, _("unknown visibility character for baseclass")); + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + } + ++*pp; + + /* The remaining value is the bit offset of the portion of the + object corresponding to this baseclass. Always zero in the + absence of multiple inheritance. */ + bitpos = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ',') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + + type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (type == DEBUG_TYPE_NULL) + return FALSE; + + classes[i] = debug_make_baseclass (dhandle, type, bitpos, is_virtual, + visibility); + if (classes[i] == DEBUG_BASECLASS_NULL) + return FALSE; + + if (**pp != ';') + return FALSE; + ++*pp; + } + + classes[i] = DEBUG_BASECLASS_NULL; + + *retp = classes; + + return TRUE; +} + +/* Read struct or class data fields. They have the form: + + NAME : [VISIBILITY] TYPENUM , BITPOS , BITSIZE ; + + At the end, we see a semicolon instead of a field. + + In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for + a static field. + + The optional VISIBILITY is one of: + + '/0' (VISIBILITY_PRIVATE) + '/1' (VISIBILITY_PROTECTED) + '/2' (VISIBILITY_PUBLIC) + '/9' (VISIBILITY_IGNORE) + + or nothing, for C style fields with public visibility. + + Returns 1 for success, 0 for failure. */ + +static bfd_boolean +parse_stab_struct_fields (void *dhandle, struct stab_handle *info, + const char **pp, debug_field **retp, + bfd_boolean *staticsp) +{ + const char *orig; + const char *p; + debug_field *fields; + unsigned int c; + unsigned int alloc; + + *retp = NULL; + *staticsp = FALSE; + + orig = *pp; + + c = 0; + alloc = 10; + fields = (debug_field *) xmalloc (alloc * sizeof *fields); + while (**pp != ';') + { + /* FIXME: gdb checks os9k_stabs here. */ + + p = *pp; + + /* Add 1 to c to leave room for NULL pointer at end. */ + if (c + 1 >= alloc) + { + alloc += 10; + fields = ((debug_field *) + xrealloc (fields, alloc * sizeof *fields)); + } + + /* If it starts with CPLUS_MARKER it is a special abbreviation, + unless the CPLUS_MARKER is followed by an underscore, in + which case it is just the name of an anonymous type, which we + should handle like any other type name. We accept either '$' + or '.', because a field name can never contain one of these + characters except as a CPLUS_MARKER. */ + + if ((*p == '$' || *p == '.') && p[1] != '_') + { + ++*pp; + if (! parse_stab_cpp_abbrev (dhandle, info, pp, fields + c)) + { + free (fields); + return FALSE; + } + ++c; + continue; + } + + /* Look for the ':' that separates the field name from the field + values. Data members are delimited by a single ':', while member + functions are delimited by a pair of ':'s. When we hit the member + functions (if any), terminate scan loop and return. */ + + p = strchr (p, ':'); + if (p == NULL) + { + bad_stab (orig); + free (fields); + return FALSE; + } + + if (p[1] == ':') + break; + + if (! parse_stab_one_struct_field (dhandle, info, pp, p, fields + c, + staticsp)) + return FALSE; + + ++c; + } + + fields[c] = DEBUG_FIELD_NULL; + + *retp = fields; + + return TRUE; +} + +/* Special GNU C++ name. */ + +static bfd_boolean +parse_stab_cpp_abbrev (void *dhandle, struct stab_handle *info, + const char **pp, debug_field *retp) +{ + const char *orig; + int cpp_abbrev; + debug_type context; + const char *name; + const char *type_name; + debug_type type; + bfd_vma bitpos; + + *retp = DEBUG_FIELD_NULL; + + orig = *pp; + + if (**pp != 'v') + { + bad_stab (*pp); + return FALSE; + } + ++*pp; + + cpp_abbrev = **pp; + ++*pp; + + /* At this point, *pp points to something like "22:23=*22...", where + the type number before the ':' is the "context" and everything + after is a regular type definition. Lookup the type, find it's + name, and construct the field name. */ + + context = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (context == DEBUG_TYPE_NULL) + return FALSE; + + switch (cpp_abbrev) + { + case 'f': + /* $vf -- a virtual function table pointer. */ + name = "_vptr$"; + break; + case 'b': + /* $vb -- a virtual bsomethingorother */ + type_name = debug_get_type_name (dhandle, context); + if (type_name == NULL) + { + warn_stab (orig, _("unnamed $vb type")); + type_name = "FOO"; + } + name = concat ("_vb$", type_name, (const char *) NULL); + break; + default: + warn_stab (orig, _("unrecognized C++ abbreviation")); + name = "INVALID_CPLUSPLUS_ABBREV"; + break; + } + + if (**pp != ':') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + + type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (**pp != ',') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + + bitpos = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return FALSE; + } + ++*pp; + + *retp = debug_make_field (dhandle, name, type, bitpos, 0, + DEBUG_VISIBILITY_PRIVATE); + if (*retp == DEBUG_FIELD_NULL) + return FALSE; + + return TRUE; +} + +/* Parse a single field in a struct or union. */ + +static bfd_boolean +parse_stab_one_struct_field (void *dhandle, struct stab_handle *info, + const char **pp, const char *p, + debug_field *retp, bfd_boolean *staticsp) +{ + const char *orig; + char *name; + enum debug_visibility visibility; + debug_type type; + bfd_vma bitpos; + bfd_vma bitsize; + + orig = *pp; + + /* FIXME: gdb checks ARM_DEMANGLING here. */ + + name = savestring (*pp, p - *pp); + + *pp = p + 1; + + if (**pp != '/') + visibility = DEBUG_VISIBILITY_PUBLIC; + else + { + ++*pp; + switch (**pp) + { + case '0': + visibility = DEBUG_VISIBILITY_PRIVATE; + break; + case '1': + visibility = DEBUG_VISIBILITY_PROTECTED; + break; + case '2': + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + default: + warn_stab (orig, _("unknown visibility character for field")); + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + } + ++*pp; + } + + type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (type == DEBUG_TYPE_NULL) + { + free (name); + return FALSE; + } + + if (**pp == ':') + { + char *varname; + + /* This is a static class member. */ + ++*pp; + p = strchr (*pp, ';'); + if (p == NULL) + { + bad_stab (orig); + free (name); + return FALSE; + } + + varname = savestring (*pp, p - *pp); + + *pp = p + 1; + + *retp = debug_make_static_member (dhandle, name, type, varname, + visibility); + *staticsp = TRUE; + + return TRUE; + } + + if (**pp != ',') + { + bad_stab (orig); + free (name); + return FALSE; + } + ++*pp; + + bitpos = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ',') + { + bad_stab (orig); + free (name); + return FALSE; + } + ++*pp; + + bitsize = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + free (name); + return FALSE; + } + ++*pp; + + if (bitpos == 0 && bitsize == 0) + { + /* This can happen in two cases: (1) at least for gcc 2.4.5 or + so, it is a field which has been optimized out. The correct + stab for this case is to use VISIBILITY_IGNORE, but that is a + recent invention. (2) It is a 0-size array. For example + union { int num; char str[0]; } foo. Printing "" + for str in "p foo" is OK, since foo.str (and thus foo.str[3]) + will continue to work, and a 0-size array as a whole doesn't + have any contents to print. + + I suspect this probably could also happen with gcc -gstabs + (not -gstabs+) for static fields, and perhaps other C++ + extensions. Hopefully few people use -gstabs with gdb, since + it is intended for dbx compatibility. */ + visibility = DEBUG_VISIBILITY_IGNORE; + } + + /* FIXME: gdb does some stuff here to mark fields as unpacked. */ + + *retp = debug_make_field (dhandle, name, type, bitpos, bitsize, visibility); + + return TRUE; +} + +/* Read member function stabs info for C++ classes. The form of each member + function data is: + + NAME :: TYPENUM[=type definition] ARGS : PHYSNAME ; + + An example with two member functions is: + + afunc1::20=##15;:i;2A.;afunc2::20:i;2A.; + + For the case of overloaded operators, the format is op$::*.funcs, where + $ is the CPLUS_MARKER (usually '$'), `*' holds the place for an operator + name (such as `+=') and `.' marks the end of the operator name. */ + +static bfd_boolean +parse_stab_members (void *dhandle, struct stab_handle *info, + const char *tagname, const char **pp, + const int *typenums, debug_method **retp) +{ + const char *orig; + debug_method *methods; + unsigned int c; + unsigned int alloc; + char *name = NULL; + debug_method_variant *variants = NULL; + char *argtypes = NULL; + + *retp = NULL; + + orig = *pp; + + alloc = 0; + methods = NULL; + c = 0; + + while (**pp != ';') + { + const char *p; + unsigned int cvars; + unsigned int allocvars; + debug_type look_ahead_type; + + p = strchr (*pp, ':'); + if (p == NULL || p[1] != ':') + break; + + /* FIXME: Some systems use something other than '$' here. */ + if ((*pp)[0] != 'o' || (*pp)[1] != 'p' || (*pp)[2] != '$') + { + name = savestring (*pp, p - *pp); + *pp = p + 2; + } + else + { + /* This is a completely weird case. In order to stuff in the + names that might contain colons (the usual name delimiter), + Mike Tiemann defined a different name format which is + signalled if the identifier is "op$". In that case, the + format is "op$::XXXX." where XXXX is the name. This is + used for names like "+" or "=". YUUUUUUUK! FIXME! */ + *pp = p + 2; + for (p = *pp; *p != '.' && *p != '\0'; p++) + ; + if (*p != '.') + { + bad_stab (orig); + goto fail; + } + name = savestring (*pp, p - *pp); + *pp = p + 1; + } + + allocvars = 10; + variants = ((debug_method_variant *) + xmalloc (allocvars * sizeof *variants)); + cvars = 0; + + look_ahead_type = DEBUG_TYPE_NULL; + + do + { + debug_type type; + bfd_boolean stub; + enum debug_visibility visibility; + bfd_boolean constp, volatilep, staticp; + bfd_vma voffset; + debug_type context; + const char *physname; + bfd_boolean varargs; + + if (look_ahead_type != DEBUG_TYPE_NULL) + { + /* g++ version 1 kludge */ + type = look_ahead_type; + look_ahead_type = DEBUG_TYPE_NULL; + } + else + { + type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (type == DEBUG_TYPE_NULL) + goto fail; + + if (**pp != ':') + { + bad_stab (orig); + goto fail; + } + } + + ++*pp; + p = strchr (*pp, ';'); + if (p == NULL) + { + bad_stab (orig); + goto fail; + } + + stub = FALSE; + if (debug_get_type_kind (dhandle, type) == DEBUG_KIND_METHOD + && debug_get_parameter_types (dhandle, type, &varargs) == NULL) + stub = TRUE; + + argtypes = savestring (*pp, p - *pp); + *pp = p + 1; + + switch (**pp) + { + case '0': + visibility = DEBUG_VISIBILITY_PRIVATE; + break; + case '1': + visibility = DEBUG_VISIBILITY_PROTECTED; + break; + default: + visibility = DEBUG_VISIBILITY_PUBLIC; + break; + } + ++*pp; + + constp = FALSE; + volatilep = FALSE; + switch (**pp) + { + case 'A': + /* Normal function. */ + ++*pp; + break; + case 'B': + /* const member function. */ + constp = TRUE; + ++*pp; + break; + case 'C': + /* volatile member function. */ + volatilep = TRUE; + ++*pp; + break; + case 'D': + /* const volatile member function. */ + constp = TRUE; + volatilep = TRUE; + ++*pp; + break; + case '*': + case '?': + case '.': + /* File compiled with g++ version 1; no information. */ + break; + default: + warn_stab (orig, _("const/volatile indicator missing")); + break; + } + + staticp = FALSE; + switch (**pp) + { + case '*': + /* virtual member function, followed by index. The sign + bit is supposedly set to distinguish + pointers-to-methods from virtual function indicies. */ + ++*pp; + voffset = parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + goto fail; + } + ++*pp; + voffset &= 0x7fffffff; + + if (**pp == ';' || *pp == '\0') + { + /* Must be g++ version 1. */ + context = DEBUG_TYPE_NULL; + } + else + { + /* Figure out from whence this virtual function + came. It may belong to virtual function table of + one of its baseclasses. */ + look_ahead_type = parse_stab_type (dhandle, info, + (const char *) NULL, + pp, + (debug_type **) NULL); + if (**pp == ':') + { + /* g++ version 1 overloaded methods. */ + context = DEBUG_TYPE_NULL; + } + else + { + context = look_ahead_type; + look_ahead_type = DEBUG_TYPE_NULL; + if (**pp != ';') + { + bad_stab (orig); + goto fail; + } + ++*pp; + } + } + break; + + case '?': + /* static member function. */ + ++*pp; + staticp = TRUE; + voffset = 0; + context = DEBUG_TYPE_NULL; + if (strncmp (argtypes, name, strlen (name)) != 0) + stub = TRUE; + break; + + default: + warn_stab (orig, "member function type missing"); + voffset = 0; + context = DEBUG_TYPE_NULL; + break; + + case '.': + ++*pp; + voffset = 0; + context = DEBUG_TYPE_NULL; + break; + } + + /* If the type is not a stub, then the argtypes string is + the physical name of the function. Otherwise the + argtypes string is the mangled form of the argument + types, and the full type and the physical name must be + extracted from them. */ + physname = argtypes; + if (stub) + { + debug_type class_type, return_type; + + class_type = stab_find_type (dhandle, info, typenums); + if (class_type == DEBUG_TYPE_NULL) + goto fail; + return_type = debug_get_return_type (dhandle, type); + if (return_type == DEBUG_TYPE_NULL) + { + bad_stab (orig); + goto fail; + } + type = parse_stab_argtypes (dhandle, info, class_type, name, + tagname, return_type, argtypes, + constp, volatilep, &physname); + if (type == DEBUG_TYPE_NULL) + goto fail; + } + + if (cvars + 1 >= allocvars) + { + allocvars += 10; + variants = ((debug_method_variant *) + xrealloc (variants, + allocvars * sizeof *variants)); + } + + if (! staticp) + variants[cvars] = debug_make_method_variant (dhandle, physname, + type, visibility, + constp, volatilep, + voffset, context); + else + variants[cvars] = debug_make_static_method_variant (dhandle, + physname, + type, + visibility, + constp, + volatilep); + if (variants[cvars] == DEBUG_METHOD_VARIANT_NULL) + goto fail; + + ++cvars; + } + while (**pp != ';' && **pp != '\0'); + + variants[cvars] = DEBUG_METHOD_VARIANT_NULL; + + if (**pp != '\0') + ++*pp; + + if (c + 1 >= alloc) + { + alloc += 10; + methods = ((debug_method *) + xrealloc (methods, alloc * sizeof *methods)); + } + + methods[c] = debug_make_method (dhandle, name, variants); + + ++c; + } + + if (methods != NULL) + methods[c] = DEBUG_METHOD_NULL; + + *retp = methods; + + return TRUE; + + fail: + if (name != NULL) + free (name); + if (variants != NULL) + free (variants); + if (argtypes != NULL) + free (argtypes); + return FALSE; +} + +/* Parse a string representing argument types for a method. Stabs + tries to save space by packing argument types into a mangled + string. This string should give us enough information to extract + both argument types and the physical name of the function, given + the tag name. */ + +static debug_type +parse_stab_argtypes (void *dhandle, struct stab_handle *info, + debug_type class_type, const char *fieldname, + const char *tagname, debug_type return_type, + const char *argtypes, bfd_boolean constp, + bfd_boolean volatilep, const char **pphysname) +{ + bfd_boolean is_full_physname_constructor; + bfd_boolean is_constructor; + bfd_boolean is_destructor; + bfd_boolean is_v3; + debug_type *args; + bfd_boolean varargs; + unsigned int physname_len = 0; + + /* Constructors are sometimes handled specially. */ + is_full_physname_constructor = ((argtypes[0] == '_' + && argtypes[1] == '_' + && (ISDIGIT (argtypes[2]) + || argtypes[2] == 'Q' + || argtypes[2] == 't')) + || CONST_STRNEQ (argtypes, "__ct")); + + is_constructor = (is_full_physname_constructor + || (tagname != NULL + && strcmp (fieldname, tagname) == 0)); + is_destructor = ((argtypes[0] == '_' + && (argtypes[1] == '$' || argtypes[1] == '.') + && argtypes[2] == '_') + || CONST_STRNEQ (argtypes, "__dt")); + is_v3 = argtypes[0] == '_' && argtypes[1] == 'Z'; + + if (!(is_destructor || is_full_physname_constructor || is_v3)) + { + unsigned int len; + const char *const_prefix; + const char *volatile_prefix; + char buf[20]; + unsigned int mangled_name_len; + char *physname; + + len = tagname == NULL ? 0 : strlen (tagname); + const_prefix = constp ? "C" : ""; + volatile_prefix = volatilep ? "V" : ""; + + if (len == 0) + sprintf (buf, "__%s%s", const_prefix, volatile_prefix); + else if (tagname != NULL && strchr (tagname, '<') != NULL) + { + /* Template methods are fully mangled. */ + sprintf (buf, "__%s%s", const_prefix, volatile_prefix); + tagname = NULL; + len = 0; + } + else + sprintf (buf, "__%s%s%d", const_prefix, volatile_prefix, len); + + mangled_name_len = ((is_constructor ? 0 : strlen (fieldname)) + + strlen (buf) + + len + + strlen (argtypes) + + 1); + + if (fieldname[0] == 'o' + && fieldname[1] == 'p' + && (fieldname[2] == '$' || fieldname[2] == '.')) + { + const char *opname; + + opname = cplus_mangle_opname (fieldname + 3, 0); + if (opname == NULL) + { + fprintf (stderr, _("No mangling for \"%s\"\n"), fieldname); + return DEBUG_TYPE_NULL; + } + mangled_name_len += strlen (opname); + physname = (char *) xmalloc (mangled_name_len); + strncpy (physname, fieldname, 3); + strcpy (physname + 3, opname); + } + else + { + physname = (char *) xmalloc (mangled_name_len); + if (is_constructor) + physname[0] = '\0'; + else + strcpy (physname, fieldname); + } + + physname_len = strlen (physname); + strcat (physname, buf); + if (tagname != NULL) + strcat (physname, tagname); + strcat (physname, argtypes); + + *pphysname = physname; + } + + if (*argtypes == '\0' || is_destructor) + { + args = (debug_type *) xmalloc (sizeof *args); + *args = NULL; + return debug_make_method_type (dhandle, return_type, class_type, args, + FALSE); + } + + args = stab_demangle_argtypes (dhandle, info, *pphysname, &varargs, physname_len); + if (args == NULL) + return DEBUG_TYPE_NULL; + + return debug_make_method_type (dhandle, return_type, class_type, args, + varargs); +} + +/* The tail end of stabs for C++ classes that contain a virtual function + pointer contains a tilde, a %, and a type number. + The type number refers to the base class (possibly this class itself) which + contains the vtable pointer for the current class. + + This function is called when we have parsed all the method declarations, + so we can look for the vptr base class info. */ + +static bfd_boolean +parse_stab_tilde_field (void *dhandle, struct stab_handle *info, + const char **pp, const int *typenums, + debug_type *retvptrbase, bfd_boolean *retownvptr) +{ + const char *orig; + const char *hold; + int vtypenums[2]; + + *retvptrbase = DEBUG_TYPE_NULL; + *retownvptr = FALSE; + + orig = *pp; + + /* If we are positioned at a ';', then skip it. */ + if (**pp == ';') + ++*pp; + + if (**pp != '~') + return TRUE; + + ++*pp; + + if (**pp == '=' || **pp == '+' || **pp == '-') + { + /* Obsolete flags that used to indicate the presence of + constructors and/or destructors. */ + ++*pp; + } + + if (**pp != '%') + return TRUE; + + ++*pp; + + hold = *pp; + + /* The next number is the type number of the base class (possibly + our own class) which supplies the vtable for this class. */ + if (! parse_stab_type_number (pp, vtypenums)) + return FALSE; + + if (vtypenums[0] == typenums[0] + && vtypenums[1] == typenums[1]) + *retownvptr = TRUE; + else + { + debug_type vtype; + const char *p; + + *pp = hold; + + vtype = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + for (p = *pp; *p != ';' && *p != '\0'; p++) + ; + if (*p != ';') + { + bad_stab (orig); + return FALSE; + } + + *retvptrbase = vtype; + + *pp = p + 1; + } + + return TRUE; +} + +/* Read a definition of an array type. */ + +static debug_type +parse_stab_array_type (void *dhandle, struct stab_handle *info, + const char **pp, bfd_boolean stringp) +{ + const char *orig; + const char *p; + int typenums[2]; + debug_type index_type; + bfd_boolean adjustable; + bfd_signed_vma lower, upper; + debug_type element_type; + + /* Format of an array type: + "ar;lower;upper;". + OS9000: "arlower,upper;". + + Fortran adjustable arrays use Adigits or Tdigits for lower or upper; + for these, produce a type like float[][]. */ + + orig = *pp; + + /* FIXME: gdb checks os9k_stabs here. */ + + /* If the index type is type 0, we take it as int. */ + p = *pp; + if (! parse_stab_type_number (&p, typenums)) + return DEBUG_TYPE_NULL; + if (typenums[0] == 0 && typenums[1] == 0 && **pp != '=') + { + index_type = debug_find_named_type (dhandle, "int"); + if (index_type == DEBUG_TYPE_NULL) + { + index_type = debug_make_int_type (dhandle, 4, FALSE); + if (index_type == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + } + *pp = p; + } + else + { + index_type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + } + + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + adjustable = FALSE; + + if (! ISDIGIT (**pp) && **pp != '-') + { + ++*pp; + adjustable = TRUE; + } + + lower = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + if (! ISDIGIT (**pp) && **pp != '-') + { + ++*pp; + adjustable = TRUE; + } + + upper = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL); + if (**pp != ';') + { + bad_stab (orig); + return DEBUG_TYPE_NULL; + } + ++*pp; + + element_type = parse_stab_type (dhandle, info, (const char *) NULL, pp, + (debug_type **) NULL); + if (element_type == DEBUG_TYPE_NULL) + return DEBUG_TYPE_NULL; + + if (adjustable) + { + lower = 0; + upper = -1; + } + + return debug_make_array_type (dhandle, element_type, index_type, lower, + upper, stringp); +} + +/* This struct holds information about files we have seen using + N_BINCL. */ + +struct bincl_file +{ + /* The next N_BINCL file. */ + struct bincl_file *next; + /* The next N_BINCL on the stack. */ + struct bincl_file *next_stack; + /* The file name. */ + const char *name; + /* The hash value. */ + bfd_vma hash; + /* The file index. */ + unsigned int file; + /* The list of types defined in this file. */ + struct stab_types *file_types; +}; + +/* Start a new N_BINCL file, pushing it onto the stack. */ + +static void +push_bincl (struct stab_handle *info, const char *name, bfd_vma hash) +{ + struct bincl_file *n; + + n = (struct bincl_file *) xmalloc (sizeof *n); + n->next = info->bincl_list; + n->next_stack = info->bincl_stack; + n->name = name; + n->hash = hash; + n->file = info->files; + n->file_types = NULL; + info->bincl_list = n; + info->bincl_stack = n; + + ++info->files; + info->file_types = ((struct stab_types **) + xrealloc (info->file_types, + (info->files + * sizeof *info->file_types))); + info->file_types[n->file] = NULL; +} + +/* Finish an N_BINCL file, at an N_EINCL, popping the name off the + stack. */ + +static const char * +pop_bincl (struct stab_handle *info) +{ + struct bincl_file *o; + + o = info->bincl_stack; + if (o == NULL) + return info->main_filename; + info->bincl_stack = o->next_stack; + + o->file_types = info->file_types[o->file]; + + if (info->bincl_stack == NULL) + return info->main_filename; + return info->bincl_stack->name; +} + +/* Handle an N_EXCL: get the types from the corresponding N_BINCL. */ + +static bfd_boolean +find_excl (struct stab_handle *info, const char *name, bfd_vma hash) +{ + struct bincl_file *l; + + ++info->files; + info->file_types = ((struct stab_types **) + xrealloc (info->file_types, + (info->files + * sizeof *info->file_types))); + + for (l = info->bincl_list; l != NULL; l = l->next) + if (l->hash == hash && strcmp (l->name, name) == 0) + break; + if (l == NULL) + { + warn_stab (name, _("Undefined N_EXCL")); + info->file_types[info->files - 1] = NULL; + return TRUE; + } + + info->file_types[info->files - 1] = l->file_types; + + return TRUE; +} + +/* Handle a variable definition. gcc emits variable definitions for a + block before the N_LBRAC, so we must hold onto them until we see + it. The SunPRO compiler emits variable definitions after the + N_LBRAC, so we can call debug_record_variable immediately. */ + +static bfd_boolean +stab_record_variable (void *dhandle, struct stab_handle *info, + const char *name, debug_type type, + enum debug_var_kind kind, bfd_vma val) +{ + struct stab_pending_var *v; + + if ((kind == DEBUG_GLOBAL || kind == DEBUG_STATIC) + || ! info->within_function + || (info->gcc_compiled == 0 && info->n_opt_found)) + return debug_record_variable (dhandle, name, type, kind, val); + + v = (struct stab_pending_var *) xmalloc (sizeof *v); + memset (v, 0, sizeof *v); + + v->next = info->pending; + v->name = name; + v->type = type; + v->kind = kind; + v->val = val; + info->pending = v; + + return TRUE; +} + +/* Emit pending variable definitions. This is called after we see the + N_LBRAC that starts the block. */ + +static bfd_boolean +stab_emit_pending_vars (void *dhandle, struct stab_handle *info) +{ + struct stab_pending_var *v; + + v = info->pending; + while (v != NULL) + { + struct stab_pending_var *next; + + if (! debug_record_variable (dhandle, v->name, v->type, v->kind, v->val)) + return FALSE; + + next = v->next; + free (v); + v = next; + } + + info->pending = NULL; + + return TRUE; +} + +/* Find the slot for a type in the database. */ + +static debug_type * +stab_find_slot (struct stab_handle *info, const int *typenums) +{ + int filenum; + int tindex; + struct stab_types **ps; + + filenum = typenums[0]; + tindex = typenums[1]; + + if (filenum < 0 || (unsigned int) filenum >= info->files) + { + fprintf (stderr, _("Type file number %d out of range\n"), filenum); + return NULL; + } + if (tindex < 0) + { + fprintf (stderr, _("Type index number %d out of range\n"), tindex); + return NULL; + } + + ps = info->file_types + filenum; + + while (tindex >= STAB_TYPES_SLOTS) + { + if (*ps == NULL) + { + *ps = (struct stab_types *) xmalloc (sizeof **ps); + memset (*ps, 0, sizeof **ps); + } + ps = &(*ps)->next; + tindex -= STAB_TYPES_SLOTS; + } + if (*ps == NULL) + { + *ps = (struct stab_types *) xmalloc (sizeof **ps); + memset (*ps, 0, sizeof **ps); + } + + return (*ps)->types + tindex; +} + +/* Find a type given a type number. If the type has not been + allocated yet, create an indirect type. */ + +static debug_type +stab_find_type (void *dhandle, struct stab_handle *info, const int *typenums) +{ + debug_type *slot; + + if (typenums[0] == 0 && typenums[1] < 0) + { + /* A negative type number indicates an XCOFF builtin type. */ + return stab_xcoff_builtin_type (dhandle, info, typenums[1]); + } + + slot = stab_find_slot (info, typenums); + if (slot == NULL) + return DEBUG_TYPE_NULL; + + if (*slot == DEBUG_TYPE_NULL) + return debug_make_indirect_type (dhandle, slot, (const char *) NULL); + + return *slot; +} + +/* Record that a given type number refers to a given type. */ + +static bfd_boolean +stab_record_type (void *dhandle ATTRIBUTE_UNUSED, struct stab_handle *info, + const int *typenums, debug_type type) +{ + debug_type *slot; + + slot = stab_find_slot (info, typenums); + if (slot == NULL) + return FALSE; + + /* gdb appears to ignore type redefinitions, so we do as well. */ + + *slot = type; + + return TRUE; +} + +/* Return an XCOFF builtin type. */ + +static debug_type +stab_xcoff_builtin_type (void *dhandle, struct stab_handle *info, + int typenum) +{ + debug_type rettype; + const char *name; + + if (typenum >= 0 || typenum < -XCOFF_TYPE_COUNT) + { + fprintf (stderr, _("Unrecognized XCOFF type %d\n"), typenum); + return DEBUG_TYPE_NULL; + } + if (info->xcoff_types[-typenum] != NULL) + return info->xcoff_types[-typenum]; + + switch (-typenum) + { + case 1: + /* The size of this and all the other types are fixed, defined + by the debugging format. */ + name = "int"; + rettype = debug_make_int_type (dhandle, 4, FALSE); + break; + case 2: + name = "char"; + rettype = debug_make_int_type (dhandle, 1, FALSE); + break; + case 3: + name = "short"; + rettype = debug_make_int_type (dhandle, 2, FALSE); + break; + case 4: + name = "long"; + rettype = debug_make_int_type (dhandle, 4, FALSE); + break; + case 5: + name = "unsigned char"; + rettype = debug_make_int_type (dhandle, 1, TRUE); + break; + case 6: + name = "signed char"; + rettype = debug_make_int_type (dhandle, 1, FALSE); + break; + case 7: + name = "unsigned short"; + rettype = debug_make_int_type (dhandle, 2, TRUE); + break; + case 8: + name = "unsigned int"; + rettype = debug_make_int_type (dhandle, 4, TRUE); + break; + case 9: + name = "unsigned"; + rettype = debug_make_int_type (dhandle, 4, TRUE); + case 10: + name = "unsigned long"; + rettype = debug_make_int_type (dhandle, 4, TRUE); + break; + case 11: + name = "void"; + rettype = debug_make_void_type (dhandle); + break; + case 12: + /* IEEE single precision (32 bit). */ + name = "float"; + rettype = debug_make_float_type (dhandle, 4); + break; + case 13: + /* IEEE double precision (64 bit). */ + name = "double"; + rettype = debug_make_float_type (dhandle, 8); + break; + case 14: + /* This is an IEEE double on the RS/6000, and different machines + with different sizes for "long double" should use different + negative type numbers. See stabs.texinfo. */ + name = "long double"; + rettype = debug_make_float_type (dhandle, 8); + break; + case 15: + name = "integer"; + rettype = debug_make_int_type (dhandle, 4, FALSE); + break; + case 16: + name = "boolean"; + rettype = debug_make_bool_type (dhandle, 4); + break; + case 17: + name = "short real"; + rettype = debug_make_float_type (dhandle, 4); + break; + case 18: + name = "real"; + rettype = debug_make_float_type (dhandle, 8); + break; + case 19: + /* FIXME */ + name = "stringptr"; + rettype = NULL; + break; + case 20: + /* FIXME */ + name = "character"; + rettype = debug_make_int_type (dhandle, 1, TRUE); + break; + case 21: + name = "logical*1"; + rettype = debug_make_bool_type (dhandle, 1); + break; + case 22: + name = "logical*2"; + rettype = debug_make_bool_type (dhandle, 2); + break; + case 23: + name = "logical*4"; + rettype = debug_make_bool_type (dhandle, 4); + break; + case 24: + name = "logical"; + rettype = debug_make_bool_type (dhandle, 4); + break; + case 25: + /* Complex type consisting of two IEEE single precision values. */ + name = "complex"; + rettype = debug_make_complex_type (dhandle, 8); + break; + case 26: + /* Complex type consisting of two IEEE double precision values. */ + name = "double complex"; + rettype = debug_make_complex_type (dhandle, 16); + break; + case 27: + name = "integer*1"; + rettype = debug_make_int_type (dhandle, 1, FALSE); + break; + case 28: + name = "integer*2"; + rettype = debug_make_int_type (dhandle, 2, FALSE); + break; + case 29: + name = "integer*4"; + rettype = debug_make_int_type (dhandle, 4, FALSE); + break; + case 30: + /* FIXME */ + name = "wchar"; + rettype = debug_make_int_type (dhandle, 2, FALSE); + break; + case 31: + name = "long long"; + rettype = debug_make_int_type (dhandle, 8, FALSE); + break; + case 32: + name = "unsigned long long"; + rettype = debug_make_int_type (dhandle, 8, TRUE); + break; + case 33: + name = "logical*8"; + rettype = debug_make_bool_type (dhandle, 8); + break; + case 34: + name = "integer*8"; + rettype = debug_make_int_type (dhandle, 8, FALSE); + break; + default: + abort (); + } + + rettype = debug_name_type (dhandle, name, rettype); + + info->xcoff_types[-typenum] = rettype; + + return rettype; +} + +/* Find or create a tagged type. */ + +static debug_type +stab_find_tagged_type (void *dhandle, struct stab_handle *info, + const char *p, int len, enum debug_type_kind kind) +{ + char *name; + debug_type dtype; + struct stab_tag *st; + + name = savestring (p, len); + + /* We pass DEBUG_KIND_ILLEGAL because we want all tags in the same + namespace. This is right for C, and I don't know how to handle + other languages. FIXME. */ + dtype = debug_find_tagged_type (dhandle, name, DEBUG_KIND_ILLEGAL); + if (dtype != DEBUG_TYPE_NULL) + { + free (name); + return dtype; + } + + /* We need to allocate an entry on the undefined tag list. */ + for (st = info->tags; st != NULL; st = st->next) + { + if (st->name[0] == name[0] + && strcmp (st->name, name) == 0) + { + if (st->kind == DEBUG_KIND_ILLEGAL) + st->kind = kind; + free (name); + break; + } + } + if (st == NULL) + { + st = (struct stab_tag *) xmalloc (sizeof *st); + memset (st, 0, sizeof *st); + + st->next = info->tags; + st->name = name; + st->kind = kind; + st->slot = DEBUG_TYPE_NULL; + st->type = debug_make_indirect_type (dhandle, &st->slot, name); + info->tags = st; + } + + return st->type; +} + +/* In order to get the correct argument types for a stubbed method, we + need to extract the argument types from a C++ mangled string. + Since the argument types can refer back to the return type, this + means that we must demangle the entire physical name. In gdb this + is done by calling cplus_demangle and running the results back + through the C++ expression parser. Since we have no expression + parser, we must duplicate much of the work of cplus_demangle here. + + We assume that GNU style demangling is used, since this is only + done for method stubs, and only g++ should output that form of + debugging information. */ + +/* This structure is used to hold a pointer to type information which + demangling a string. */ + +struct stab_demangle_typestring +{ + /* The start of the type. This is not null terminated. */ + const char *typestring; + /* The length of the type. */ + unsigned int len; +}; + +/* This structure is used to hold information while demangling a + string. */ + +struct stab_demangle_info +{ + /* The debugging information handle. */ + void *dhandle; + /* The stab information handle. */ + struct stab_handle *info; + /* The array of arguments we are building. */ + debug_type *args; + /* Whether the method takes a variable number of arguments. */ + bfd_boolean varargs; + /* The array of types we have remembered. */ + struct stab_demangle_typestring *typestrings; + /* The number of typestrings. */ + unsigned int typestring_count; + /* The number of typestring slots we have allocated. */ + unsigned int typestring_alloc; +}; + +static void stab_bad_demangle (const char *); +static unsigned int stab_demangle_count (const char **); +static bfd_boolean stab_demangle_get_count (const char **, unsigned int *); +static bfd_boolean stab_demangle_prefix + (struct stab_demangle_info *, const char **, unsigned int); +static bfd_boolean stab_demangle_function_name + (struct stab_demangle_info *, const char **, const char *); +static bfd_boolean stab_demangle_signature + (struct stab_demangle_info *, const char **); +static bfd_boolean stab_demangle_qualified + (struct stab_demangle_info *, const char **, debug_type *); +static bfd_boolean stab_demangle_template + (struct stab_demangle_info *, const char **, char **); +static bfd_boolean stab_demangle_class + (struct stab_demangle_info *, const char **, const char **); +static bfd_boolean stab_demangle_args + (struct stab_demangle_info *, const char **, debug_type **, bfd_boolean *); +static bfd_boolean stab_demangle_arg + (struct stab_demangle_info *, const char **, debug_type **, + unsigned int *, unsigned int *); +static bfd_boolean stab_demangle_type + (struct stab_demangle_info *, const char **, debug_type *); +static bfd_boolean stab_demangle_fund_type + (struct stab_demangle_info *, const char **, debug_type *); +static bfd_boolean stab_demangle_remember_type + (struct stab_demangle_info *, const char *, int); + +/* Warn about a bad demangling. */ + +static void +stab_bad_demangle (const char *s) +{ + fprintf (stderr, _("bad mangled name `%s'\n"), s); +} + +/* Get a count from a stab string. */ + +static unsigned int +stab_demangle_count (const char **pp) +{ + unsigned int count; + + count = 0; + while (ISDIGIT (**pp)) + { + count *= 10; + count += **pp - '0'; + ++*pp; + } + return count; +} + +/* Require a count in a string. The count may be multiple digits, in + which case it must end in an underscore. */ + +static bfd_boolean +stab_demangle_get_count (const char **pp, unsigned int *pi) +{ + if (! ISDIGIT (**pp)) + return FALSE; + + *pi = **pp - '0'; + ++*pp; + if (ISDIGIT (**pp)) + { + unsigned int count; + const char *p; + + count = *pi; + p = *pp; + do + { + count *= 10; + count += *p - '0'; + ++p; + } + while (ISDIGIT (*p)); + if (*p == '_') + { + *pp = p + 1; + *pi = count; + } + } + + return TRUE; +} + +/* This function demangles a physical name, returning a NULL + terminated array of argument types. */ + +static debug_type * +stab_demangle_argtypes (void *dhandle, struct stab_handle *info, + const char *physname, bfd_boolean *pvarargs, + unsigned int physname_len) +{ + struct stab_demangle_info minfo; + + /* Check for the g++ V3 ABI. */ + if (physname[0] == '_' && physname[1] == 'Z') + return stab_demangle_v3_argtypes (dhandle, info, physname, pvarargs); + + minfo.dhandle = dhandle; + minfo.info = info; + minfo.args = NULL; + minfo.varargs = FALSE; + minfo.typestring_alloc = 10; + minfo.typestrings = ((struct stab_demangle_typestring *) + xmalloc (minfo.typestring_alloc + * sizeof *minfo.typestrings)); + minfo.typestring_count = 0; + + /* cplus_demangle checks for special GNU mangled forms, but we can't + see any of them in mangled method argument types. */ + + if (! stab_demangle_prefix (&minfo, &physname, physname_len)) + goto error_return; + + if (*physname != '\0') + { + if (! stab_demangle_signature (&minfo, &physname)) + goto error_return; + } + + free (minfo.typestrings); + minfo.typestrings = NULL; + + if (minfo.args == NULL) + fprintf (stderr, _("no argument types in mangled string\n")); + + *pvarargs = minfo.varargs; + return minfo.args; + + error_return: + if (minfo.typestrings != NULL) + free (minfo.typestrings); + return NULL; +} + +/* Demangle the prefix of the mangled name. */ + +static bfd_boolean +stab_demangle_prefix (struct stab_demangle_info *minfo, const char **pp, + unsigned int physname_len) +{ + const char *scan; + unsigned int i; + + /* cplus_demangle checks for global constructors and destructors, + but we can't see them in mangled argument types. */ + + if (physname_len) + scan = *pp + physname_len; + else + { + /* Look for `__'. */ + scan = *pp; + do + scan = strchr (scan, '_'); + while (scan != NULL && *++scan != '_'); + + if (scan == NULL) + { + stab_bad_demangle (*pp); + return FALSE; + } + + --scan; + + /* We found `__'; move ahead to the last contiguous `__' pair. */ + i = strspn (scan, "_"); + if (i > 2) + scan += i - 2; + } + + if (scan == *pp + && (ISDIGIT (scan[2]) + || scan[2] == 'Q' + || scan[2] == 't')) + { + /* This is a GNU style constructor name. */ + *pp = scan + 2; + return TRUE; + } + else if (scan == *pp + && ! ISDIGIT (scan[2]) + && scan[2] != 't') + { + /* Look for the `__' that separates the prefix from the + signature. */ + while (*scan == '_') + ++scan; + scan = strstr (scan, "__"); + if (scan == NULL || scan[2] == '\0') + { + stab_bad_demangle (*pp); + return FALSE; + } + + return stab_demangle_function_name (minfo, pp, scan); + } + else if (scan[2] != '\0') + { + /* The name doesn't start with `__', but it does contain `__'. */ + return stab_demangle_function_name (minfo, pp, scan); + } + else + { + stab_bad_demangle (*pp); + return FALSE; + } + /*NOTREACHED*/ +} + +/* Demangle a function name prefix. The scan argument points to the + double underscore which separates the function name from the + signature. */ + +static bfd_boolean +stab_demangle_function_name (struct stab_demangle_info *minfo, + const char **pp, const char *scan) +{ + const char *name; + + /* The string from *pp to scan is the name of the function. We + don't care about the name, since we just looking for argument + types. However, for conversion operators, the name may include a + type which we must remember in order to handle backreferences. */ + + name = *pp; + *pp = scan + 2; + + if (*pp - name >= 5 + && CONST_STRNEQ (name, "type") + && (name[4] == '$' || name[4] == '.')) + { + const char *tem; + + /* This is a type conversion operator. */ + tem = name + 5; + if (! stab_demangle_type (minfo, &tem, (debug_type *) NULL)) + return FALSE; + } + else if (name[0] == '_' + && name[1] == '_' + && name[2] == 'o' + && name[3] == 'p') + { + const char *tem; + + /* This is a type conversion operator. */ + tem = name + 4; + if (! stab_demangle_type (minfo, &tem, (debug_type *) NULL)) + return FALSE; + } + + return TRUE; +} + +/* Demangle the signature. This is where the argument types are + found. */ + +static bfd_boolean +stab_demangle_signature (struct stab_demangle_info *minfo, const char **pp) +{ + const char *orig; + bfd_boolean expect_func, func_done; + const char *hold; + + orig = *pp; + + expect_func = FALSE; + func_done = FALSE; + hold = NULL; + + while (**pp != '\0') + { + switch (**pp) + { + case 'Q': + hold = *pp; + if (! stab_demangle_qualified (minfo, pp, (debug_type *) NULL) + || ! stab_demangle_remember_type (minfo, hold, *pp - hold)) + return FALSE; + expect_func = TRUE; + hold = NULL; + break; + + case 'S': + /* Static member function. FIXME: Can this happen? */ + if (hold == NULL) + hold = *pp; + ++*pp; + break; + + case 'C': + /* Const member function. */ + if (hold == NULL) + hold = *pp; + ++*pp; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (hold == NULL) + hold = *pp; + if (! stab_demangle_class (minfo, pp, (const char **) NULL) + || ! stab_demangle_remember_type (minfo, hold, *pp - hold)) + return FALSE; + expect_func = TRUE; + hold = NULL; + break; + + case 'F': + /* Function. I don't know if this actually happens with g++ + output. */ + hold = NULL; + func_done = TRUE; + ++*pp; + if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs)) + return FALSE; + break; + + case 't': + /* Template. */ + if (hold == NULL) + hold = *pp; + if (! stab_demangle_template (minfo, pp, (char **) NULL) + || ! stab_demangle_remember_type (minfo, hold, *pp - hold)) + return FALSE; + hold = NULL; + expect_func = TRUE; + break; + + case '_': + /* At the outermost level, we cannot have a return type + specified, so if we run into another '_' at this point we + are dealing with a mangled name that is either bogus, or + has been mangled by some algorithm we don't know how to + deal with. So just reject the entire demangling. */ + stab_bad_demangle (orig); + return FALSE; + + default: + /* Assume we have stumbled onto the first outermost function + argument token, and start processing args. */ + func_done = TRUE; + if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs)) + return FALSE; + break; + } + + if (expect_func) + { + func_done = TRUE; + if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs)) + return FALSE; + } + } + + if (! func_done) + { + /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and + bar__3fooi is 'foo::bar(int)'. We get here when we find the + first case, and need to ensure that the '(void)' gets added + to the current declp. */ + if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs)) + return FALSE; + } + + return TRUE; +} + +/* Demangle a qualified name, such as "Q25Outer5Inner" which is the + mangled form of "Outer::Inner". */ + +static bfd_boolean +stab_demangle_qualified (struct stab_demangle_info *minfo, const char **pp, + debug_type *ptype) +{ + const char *orig; + const char *p; + unsigned int qualifiers; + debug_type context; + + orig = *pp; + + switch ((*pp)[1]) + { + case '_': + /* GNU mangled name with more than 9 classes. The count is + preceded by an underscore (to distinguish it from the <= 9 + case) and followed by an underscore. */ + p = *pp + 2; + if (! ISDIGIT (*p) || *p == '0') + { + stab_bad_demangle (orig); + return FALSE; + } + qualifiers = atoi (p); + while (ISDIGIT (*p)) + ++p; + if (*p != '_') + { + stab_bad_demangle (orig); + return FALSE; + } + *pp = p + 1; + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + qualifiers = (*pp)[1] - '0'; + /* Skip an optional underscore after the count. */ + if ((*pp)[2] == '_') + ++*pp; + *pp += 2; + break; + + case '0': + default: + stab_bad_demangle (orig); + return FALSE; + } + + context = DEBUG_TYPE_NULL; + + /* Pick off the names. */ + while (qualifiers-- > 0) + { + if (**pp == '_') + ++*pp; + if (**pp == 't') + { + char *name; + + if (! stab_demangle_template (minfo, pp, + ptype != NULL ? &name : NULL)) + return FALSE; + + if (ptype != NULL) + { + context = stab_find_tagged_type (minfo->dhandle, minfo->info, + name, strlen (name), + DEBUG_KIND_CLASS); + free (name); + if (context == DEBUG_TYPE_NULL) + return FALSE; + } + } + else + { + unsigned int len; + + len = stab_demangle_count (pp); + if (strlen (*pp) < len) + { + stab_bad_demangle (orig); + return FALSE; + } + + if (ptype != NULL) + { + const debug_field *fields; + + fields = NULL; + if (context != DEBUG_TYPE_NULL) + fields = debug_get_fields (minfo->dhandle, context); + + context = DEBUG_TYPE_NULL; + + if (fields != NULL) + { + char *name; + + /* Try to find the type by looking through the + fields of context until we find a field with the + same type. This ought to work for a class + defined within a class, but it won't work for, + e.g., an enum defined within a class. stabs does + not give us enough information to figure out the + latter case. */ + + name = savestring (*pp, len); + + for (; *fields != DEBUG_FIELD_NULL; fields++) + { + debug_type ft; + const char *dn; + + ft = debug_get_field_type (minfo->dhandle, *fields); + if (ft == NULL) + { + free (name); + return FALSE; + } + dn = debug_get_type_name (minfo->dhandle, ft); + if (dn != NULL && strcmp (dn, name) == 0) + { + context = ft; + break; + } + } + + free (name); + } + + if (context == DEBUG_TYPE_NULL) + { + /* We have to fall back on finding the type by name. + If there are more types to come, then this must + be a class. Otherwise, it could be anything. */ + + if (qualifiers == 0) + { + char *name; + + name = savestring (*pp, len); + context = debug_find_named_type (minfo->dhandle, + name); + free (name); + } + + if (context == DEBUG_TYPE_NULL) + { + context = stab_find_tagged_type (minfo->dhandle, + minfo->info, + *pp, len, + (qualifiers == 0 + ? DEBUG_KIND_ILLEGAL + : DEBUG_KIND_CLASS)); + if (context == DEBUG_TYPE_NULL) + return FALSE; + } + } + } + + *pp += len; + } + } + + if (ptype != NULL) + *ptype = context; + + return TRUE; +} + +/* Demangle a template. If PNAME is not NULL, this sets *PNAME to a + string representation of the template. */ + +static bfd_boolean +stab_demangle_template (struct stab_demangle_info *minfo, const char **pp, + char **pname) +{ + const char *orig; + unsigned int r, i; + + orig = *pp; + + ++*pp; + + /* Skip the template name. */ + r = stab_demangle_count (pp); + if (r == 0 || strlen (*pp) < r) + { + stab_bad_demangle (orig); + return FALSE; + } + *pp += r; + + /* Get the size of the parameter list. */ + if (stab_demangle_get_count (pp, &r) == 0) + { + stab_bad_demangle (orig); + return FALSE; + } + + for (i = 0; i < r; i++) + { + if (**pp == 'Z') + { + /* This is a type parameter. */ + ++*pp; + if (! stab_demangle_type (minfo, pp, (debug_type *) NULL)) + return FALSE; + } + else + { + const char *old_p; + bfd_boolean pointerp, realp, integralp, charp, boolp; + bfd_boolean done; + + old_p = *pp; + pointerp = FALSE; + realp = FALSE; + integralp = FALSE; + charp = FALSE; + boolp = FALSE; + done = FALSE; + + /* This is a value parameter. */ + + if (! stab_demangle_type (minfo, pp, (debug_type *) NULL)) + return FALSE; + + while (*old_p != '\0' && ! done) + { + switch (*old_p) + { + case 'P': + case 'p': + case 'R': + pointerp = TRUE; + done = TRUE; + break; + case 'C': /* Const. */ + case 'S': /* Signed. */ + case 'U': /* Unsigned. */ + case 'V': /* Volatile. */ + case 'F': /* Function. */ + case 'M': /* Member function. */ + case 'O': /* ??? */ + ++old_p; + break; + case 'Q': /* Qualified name. */ + integralp = TRUE; + done = TRUE; + break; + case 'T': /* Remembered type. */ + abort (); + case 'v': /* Void. */ + abort (); + case 'x': /* Long long. */ + case 'l': /* Long. */ + case 'i': /* Int. */ + case 's': /* Short. */ + case 'w': /* Wchar_t. */ + integralp = TRUE; + done = TRUE; + break; + case 'b': /* Bool. */ + boolp = TRUE; + done = TRUE; + break; + case 'c': /* Char. */ + charp = TRUE; + done = TRUE; + break; + case 'r': /* Long double. */ + case 'd': /* Double. */ + case 'f': /* Float. */ + realp = TRUE; + done = TRUE; + break; + default: + /* Assume it's a user defined integral type. */ + integralp = TRUE; + done = TRUE; + break; + } + } + + if (integralp) + { + if (**pp == 'm') + ++*pp; + while (ISDIGIT (**pp)) + ++*pp; + } + else if (charp) + { + unsigned int val; + + if (**pp == 'm') + ++*pp; + val = stab_demangle_count (pp); + if (val == 0) + { + stab_bad_demangle (orig); + return FALSE; + } + } + else if (boolp) + { + unsigned int val; + + val = stab_demangle_count (pp); + if (val != 0 && val != 1) + { + stab_bad_demangle (orig); + return FALSE; + } + } + else if (realp) + { + if (**pp == 'm') + ++*pp; + while (ISDIGIT (**pp)) + ++*pp; + if (**pp == '.') + { + ++*pp; + while (ISDIGIT (**pp)) + ++*pp; + } + if (**pp == 'e') + { + ++*pp; + while (ISDIGIT (**pp)) + ++*pp; + } + } + else if (pointerp) + { + unsigned int len; + + len = stab_demangle_count (pp); + if (len == 0) + { + stab_bad_demangle (orig); + return FALSE; + } + *pp += len; + } + } + } + + /* We can translate this to a string fairly easily by invoking the + regular demangling routine. */ + if (pname != NULL) + { + char *s1, *s2, *s3, *s4 = NULL; + char *from, *to; + + s1 = savestring (orig, *pp - orig); + + s2 = concat ("NoSuchStrinG__", s1, (const char *) NULL); + + free (s1); + + s3 = cplus_demangle (s2, DMGL_ANSI); + + free (s2); + + if (s3 != NULL) + s4 = strstr (s3, "::NoSuchStrinG"); + if (s3 == NULL || s4 == NULL) + { + stab_bad_demangle (orig); + if (s3 != NULL) + free (s3); + return FALSE; + } + + /* Eliminating all spaces, except those between > characters, + makes it more likely that the demangled name will match the + name which g++ used as the structure name. */ + for (from = to = s3; from != s4; ++from) + if (*from != ' ' + || (from[1] == '>' && from > s3 && from[-1] == '>')) + *to++ = *from; + + *pname = savestring (s3, to - s3); + + free (s3); + } + + return TRUE; +} + +/* Demangle a class name. */ + +static bfd_boolean +stab_demangle_class (struct stab_demangle_info *minfo ATTRIBUTE_UNUSED, + const char **pp, const char **pstart) +{ + const char *orig; + unsigned int n; + + orig = *pp; + + n = stab_demangle_count (pp); + if (strlen (*pp) < n) + { + stab_bad_demangle (orig); + return FALSE; + } + + if (pstart != NULL) + *pstart = *pp; + + *pp += n; + + return TRUE; +} + +/* Demangle function arguments. If the pargs argument is not NULL, it + is set to a NULL terminated array holding the arguments. */ + +static bfd_boolean +stab_demangle_args (struct stab_demangle_info *minfo, const char **pp, + debug_type **pargs, bfd_boolean *pvarargs) +{ + const char *orig; + unsigned int alloc, count; + + orig = *pp; + + alloc = 10; + if (pargs != NULL) + { + *pargs = (debug_type *) xmalloc (alloc * sizeof **pargs); + *pvarargs = FALSE; + } + count = 0; + + while (**pp != '_' && **pp != '\0' && **pp != 'e') + { + if (**pp == 'N' || **pp == 'T') + { + char temptype; + unsigned int r, t; + + temptype = **pp; + ++*pp; + + if (temptype == 'T') + r = 1; + else + { + if (! stab_demangle_get_count (pp, &r)) + { + stab_bad_demangle (orig); + return FALSE; + } + } + + if (! stab_demangle_get_count (pp, &t)) + { + stab_bad_demangle (orig); + return FALSE; + } + + if (t >= minfo->typestring_count) + { + stab_bad_demangle (orig); + return FALSE; + } + while (r-- > 0) + { + const char *tem; + + tem = minfo->typestrings[t].typestring; + if (! stab_demangle_arg (minfo, &tem, pargs, &count, &alloc)) + return FALSE; + } + } + else + { + if (! stab_demangle_arg (minfo, pp, pargs, &count, &alloc)) + return FALSE; + } + } + + if (pargs != NULL) + (*pargs)[count] = DEBUG_TYPE_NULL; + + if (**pp == 'e') + { + if (pargs != NULL) + *pvarargs = TRUE; + ++*pp; + } + + return TRUE; +} + +/* Demangle a single argument. */ + +static bfd_boolean +stab_demangle_arg (struct stab_demangle_info *minfo, const char **pp, + debug_type **pargs, unsigned int *pcount, + unsigned int *palloc) +{ + const char *start; + debug_type type; + + start = *pp; + if (! stab_demangle_type (minfo, pp, + pargs == NULL ? (debug_type *) NULL : &type) + || ! stab_demangle_remember_type (minfo, start, *pp - start)) + return FALSE; + + if (pargs != NULL) + { + if (type == DEBUG_TYPE_NULL) + return FALSE; + + if (*pcount + 1 >= *palloc) + { + *palloc += 10; + *pargs = ((debug_type *) + xrealloc (*pargs, *palloc * sizeof **pargs)); + } + (*pargs)[*pcount] = type; + ++*pcount; + } + + return TRUE; +} + +/* Demangle a type. If the ptype argument is not NULL, *ptype is set + to the newly allocated type. */ + +static bfd_boolean +stab_demangle_type (struct stab_demangle_info *minfo, const char **pp, + debug_type *ptype) +{ + const char *orig; + + orig = *pp; + + switch (**pp) + { + case 'P': + case 'p': + /* A pointer type. */ + ++*pp; + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + if (ptype != NULL) + *ptype = debug_make_pointer_type (minfo->dhandle, *ptype); + break; + + case 'R': + /* A reference type. */ + ++*pp; + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + if (ptype != NULL) + *ptype = debug_make_reference_type (minfo->dhandle, *ptype); + break; + + case 'A': + /* An array. */ + { + unsigned long high; + + ++*pp; + high = 0; + while (**pp != '\0' && **pp != '_') + { + if (! ISDIGIT (**pp)) + { + stab_bad_demangle (orig); + return FALSE; + } + high *= 10; + high += **pp - '0'; + ++*pp; + } + if (**pp != '_') + { + stab_bad_demangle (orig); + return FALSE; + } + ++*pp; + + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + if (ptype != NULL) + { + debug_type int_type; + + int_type = debug_find_named_type (minfo->dhandle, "int"); + if (int_type == NULL) + int_type = debug_make_int_type (minfo->dhandle, 4, FALSE); + *ptype = debug_make_array_type (minfo->dhandle, *ptype, int_type, + 0, high, FALSE); + } + } + break; + + case 'T': + /* A back reference to a remembered type. */ + { + unsigned int i; + const char *p; + + ++*pp; + if (! stab_demangle_get_count (pp, &i)) + { + stab_bad_demangle (orig); + return FALSE; + } + if (i >= minfo->typestring_count) + { + stab_bad_demangle (orig); + return FALSE; + } + p = minfo->typestrings[i].typestring; + if (! stab_demangle_type (minfo, &p, ptype)) + return FALSE; + } + break; + + case 'F': + /* A function. */ + { + debug_type *args; + bfd_boolean varargs; + + ++*pp; + if (! stab_demangle_args (minfo, pp, + (ptype == NULL + ? (debug_type **) NULL + : &args), + (ptype == NULL + ? (bfd_boolean *) NULL + : &varargs))) + return FALSE; + if (**pp != '_') + { + /* cplus_demangle will accept a function without a return + type, but I don't know when that will happen, or what + to do if it does. */ + stab_bad_demangle (orig); + return FALSE; + } + ++*pp; + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + if (ptype != NULL) + *ptype = debug_make_function_type (minfo->dhandle, *ptype, args, + varargs); + + } + break; + + case 'M': + case 'O': + { + bfd_boolean memberp; + debug_type class_type = DEBUG_TYPE_NULL; + debug_type *args; + bfd_boolean varargs; + unsigned int n; + const char *name; + + memberp = **pp == 'M'; + args = NULL; + varargs = FALSE; + + ++*pp; + if (ISDIGIT (**pp)) + { + n = stab_demangle_count (pp); + if (strlen (*pp) < n) + { + stab_bad_demangle (orig); + return FALSE; + } + name = *pp; + *pp += n; + + if (ptype != NULL) + { + class_type = stab_find_tagged_type (minfo->dhandle, + minfo->info, + name, (int) n, + DEBUG_KIND_CLASS); + if (class_type == DEBUG_TYPE_NULL) + return FALSE; + } + } + else if (**pp == 'Q') + { + if (! stab_demangle_qualified (minfo, pp, + (ptype == NULL + ? (debug_type *) NULL + : &class_type))) + return FALSE; + } + else + { + stab_bad_demangle (orig); + return FALSE; + } + + if (memberp) + { + if (**pp == 'C') + { + ++*pp; + } + else if (**pp == 'V') + { + ++*pp; + } + if (**pp != 'F') + { + stab_bad_demangle (orig); + return FALSE; + } + ++*pp; + if (! stab_demangle_args (minfo, pp, + (ptype == NULL + ? (debug_type **) NULL + : &args), + (ptype == NULL + ? (bfd_boolean *) NULL + : &varargs))) + return FALSE; + } + + if (**pp != '_') + { + stab_bad_demangle (orig); + return FALSE; + } + ++*pp; + + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + + if (ptype != NULL) + { + if (! memberp) + *ptype = debug_make_offset_type (minfo->dhandle, class_type, + *ptype); + else + { + /* FIXME: We have no way to record constp or + volatilep. */ + *ptype = debug_make_method_type (minfo->dhandle, *ptype, + class_type, args, varargs); + } + } + } + break; + + case 'G': + ++*pp; + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + break; + + case 'C': + ++*pp; + if (! stab_demangle_type (minfo, pp, ptype)) + return FALSE; + if (ptype != NULL) + *ptype = debug_make_const_type (minfo->dhandle, *ptype); + break; + + case 'Q': + { + if (! stab_demangle_qualified (minfo, pp, ptype)) + return FALSE; + } + break; + + default: + if (! stab_demangle_fund_type (minfo, pp, ptype)) + return FALSE; + break; + } + + return TRUE; +} + +/* Demangle a fundamental type. If the ptype argument is not NULL, + *ptype is set to the newly allocated type. */ + +static bfd_boolean +stab_demangle_fund_type (struct stab_demangle_info *minfo, const char **pp, + debug_type *ptype) +{ + const char *orig; + bfd_boolean constp, volatilep, unsignedp, signedp; + bfd_boolean done; + + orig = *pp; + + constp = FALSE; + volatilep = FALSE; + unsignedp = FALSE; + signedp = FALSE; + + done = FALSE; + while (! done) + { + switch (**pp) + { + case 'C': + constp = TRUE; + ++*pp; + break; + + case 'U': + unsignedp = TRUE; + ++*pp; + break; + + case 'S': + signedp = TRUE; + ++*pp; + break; + + case 'V': + volatilep = TRUE; + ++*pp; + break; + + default: + done = TRUE; + break; + } + } + + switch (**pp) + { + case '\0': + case '_': + /* cplus_demangle permits this, but I don't know what it means. */ + stab_bad_demangle (orig); + break; + + case 'v': /* void */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "void"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_void_type (minfo->dhandle); + } + ++*pp; + break; + + case 'x': /* long long */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, + (unsignedp + ? "long long unsigned int" + : "long long int")); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 8, unsignedp); + } + ++*pp; + break; + + case 'l': /* long */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, + (unsignedp + ? "long unsigned int" + : "long int")); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 4, unsignedp); + } + ++*pp; + break; + + case 'i': /* int */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, + (unsignedp + ? "unsigned int" + : "int")); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 4, unsignedp); + } + ++*pp; + break; + + case 's': /* short */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, + (unsignedp + ? "short unsigned int" + : "short int")); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 2, unsignedp); + } + ++*pp; + break; + + case 'b': /* bool */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "bool"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_bool_type (minfo->dhandle, 4); + } + ++*pp; + break; + + case 'c': /* char */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, + (unsignedp + ? "unsigned char" + : (signedp + ? "signed char" + : "char"))); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 1, unsignedp); + } + ++*pp; + break; + + case 'w': /* wchar_t */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "__wchar_t"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_int_type (minfo->dhandle, 2, TRUE); + } + ++*pp; + break; + + case 'r': /* long double */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "long double"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_float_type (minfo->dhandle, 8); + } + ++*pp; + break; + + case 'd': /* double */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "double"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_float_type (minfo->dhandle, 8); + } + ++*pp; + break; + + case 'f': /* float */ + if (ptype != NULL) + { + *ptype = debug_find_named_type (minfo->dhandle, "float"); + if (*ptype == DEBUG_TYPE_NULL) + *ptype = debug_make_float_type (minfo->dhandle, 4); + } + ++*pp; + break; + + case 'G': + ++*pp; + if (! ISDIGIT (**pp)) + { + stab_bad_demangle (orig); + return FALSE; + } + /* Fall through. */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + const char *hold; + + if (! stab_demangle_class (minfo, pp, &hold)) + return FALSE; + if (ptype != NULL) + { + char *name; + + name = savestring (hold, *pp - hold); + *ptype = debug_find_named_type (minfo->dhandle, name); + free (name); + if (*ptype == DEBUG_TYPE_NULL) + { + /* FIXME: It is probably incorrect to assume that + undefined types are tagged types. */ + *ptype = stab_find_tagged_type (minfo->dhandle, minfo->info, + hold, *pp - hold, + DEBUG_KIND_ILLEGAL); + if (*ptype == DEBUG_TYPE_NULL) + return FALSE; + } + } + } + break; + + case 't': + { + char *name; + + if (! stab_demangle_template (minfo, pp, + ptype != NULL ? &name : NULL)) + return FALSE; + if (ptype != NULL) + { + *ptype = stab_find_tagged_type (minfo->dhandle, minfo->info, + name, strlen (name), + DEBUG_KIND_CLASS); + free (name); + if (*ptype == DEBUG_TYPE_NULL) + return FALSE; + } + } + break; + + default: + stab_bad_demangle (orig); + return FALSE; + } + + if (ptype != NULL) + { + if (constp) + *ptype = debug_make_const_type (minfo->dhandle, *ptype); + if (volatilep) + *ptype = debug_make_volatile_type (minfo->dhandle, *ptype); + } + + return TRUE; +} + +/* Remember a type string in a demangled string. */ + +static bfd_boolean +stab_demangle_remember_type (struct stab_demangle_info *minfo, + const char *p, int len) +{ + if (minfo->typestring_count >= minfo->typestring_alloc) + { + minfo->typestring_alloc += 10; + minfo->typestrings = ((struct stab_demangle_typestring *) + xrealloc (minfo->typestrings, + (minfo->typestring_alloc + * sizeof *minfo->typestrings))); + } + + minfo->typestrings[minfo->typestring_count].typestring = p; + minfo->typestrings[minfo->typestring_count].len = (unsigned int) len; + ++minfo->typestring_count; + + return TRUE; +} + +/* Demangle names encoded using the g++ V3 ABI. The newer versions of + g++ which use this ABI do not encode ordinary method argument types + in a mangled name; they simply output the argument types. However, + for a static method, g++ simply outputs the return type and the + physical name. So in that case we need to demangle the name here. + Here PHYSNAME is the physical name of the function, and we set the + variable pointed at by PVARARGS to indicate whether this function + is varargs. This returns NULL, or a NULL terminated array of + argument types. */ + +static debug_type * +stab_demangle_v3_argtypes (void *dhandle, struct stab_handle *info, + const char *physname, bfd_boolean *pvarargs) +{ + struct demangle_component *dc; + void *mem; + debug_type *pargs; + + dc = cplus_demangle_v3_components (physname, DMGL_PARAMS | DMGL_ANSI, &mem); + if (dc == NULL) + { + stab_bad_demangle (physname); + return NULL; + } + + /* We expect to see TYPED_NAME, and the right subtree describes the + function type. */ + if (dc->type != DEMANGLE_COMPONENT_TYPED_NAME + || dc->u.s_binary.right->type != DEMANGLE_COMPONENT_FUNCTION_TYPE) + { + fprintf (stderr, _("Demangled name is not a function\n")); + free (mem); + return NULL; + } + + pargs = stab_demangle_v3_arglist (dhandle, info, + dc->u.s_binary.right->u.s_binary.right, + pvarargs); + + free (mem); + + return pargs; +} + +/* Demangle an argument list in a struct demangle_component tree. + Returns a DEBUG_TYPE_NULL terminated array of argument types, and + sets *PVARARGS to indicate whether this is a varargs function. */ + +static debug_type * +stab_demangle_v3_arglist (void *dhandle, struct stab_handle *info, + struct demangle_component *arglist, + bfd_boolean *pvarargs) +{ + struct demangle_component *dc; + unsigned int alloc, count; + debug_type *pargs; + + alloc = 10; + pargs = (debug_type *) xmalloc (alloc * sizeof *pargs); + *pvarargs = FALSE; + + count = 0; + + for (dc = arglist; + dc != NULL; + dc = dc->u.s_binary.right) + { + debug_type arg; + bfd_boolean varargs; + + if (dc->type != DEMANGLE_COMPONENT_ARGLIST) + { + fprintf (stderr, _("Unexpected type in v3 arglist demangling\n")); + free (pargs); + return NULL; + } + + /* PR 13925: Cope if the demangler returns an empty + context for a function with no arguments. */ + if (dc->u.s_binary.left == NULL) + break; + + arg = stab_demangle_v3_arg (dhandle, info, dc->u.s_binary.left, + NULL, &varargs); + if (arg == NULL) + { + if (varargs) + { + *pvarargs = TRUE; + continue; + } + free (pargs); + return NULL; + } + + if (count + 1 >= alloc) + { + alloc += 10; + pargs = (debug_type *) xrealloc (pargs, alloc * sizeof *pargs); + } + + pargs[count] = arg; + ++count; + } + + pargs[count] = DEBUG_TYPE_NULL; + + return pargs; +} + +/* Convert a struct demangle_component tree describing an argument + type into a debug_type. */ + +static debug_type +stab_demangle_v3_arg (void *dhandle, struct stab_handle *info, + struct demangle_component *dc, debug_type context, + bfd_boolean *pvarargs) +{ + debug_type dt; + + if (pvarargs != NULL) + *pvarargs = FALSE; + + switch (dc->type) + { + /* FIXME: These are demangle component types which we probably + need to handle one way or another. */ + case DEMANGLE_COMPONENT_LOCAL_NAME: + case DEMANGLE_COMPONENT_TYPED_NAME: + case DEMANGLE_COMPONENT_TEMPLATE_PARAM: + case DEMANGLE_COMPONENT_CTOR: + case DEMANGLE_COMPONENT_DTOR: + case DEMANGLE_COMPONENT_JAVA_CLASS: + case DEMANGLE_COMPONENT_RESTRICT_THIS: + case DEMANGLE_COMPONENT_VOLATILE_THIS: + case DEMANGLE_COMPONENT_CONST_THIS: + case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: + case DEMANGLE_COMPONENT_COMPLEX: + case DEMANGLE_COMPONENT_IMAGINARY: + case DEMANGLE_COMPONENT_VENDOR_TYPE: + case DEMANGLE_COMPONENT_ARRAY_TYPE: + case DEMANGLE_COMPONENT_PTRMEM_TYPE: + case DEMANGLE_COMPONENT_ARGLIST: + default: + fprintf (stderr, _("Unrecognized demangle component %d\n"), + (int) dc->type); + return NULL; + + case DEMANGLE_COMPONENT_NAME: + if (context != NULL) + { + const debug_field *fields; + + fields = debug_get_fields (dhandle, context); + if (fields != NULL) + { + /* Try to find this type by looking through the context + class. */ + for (; *fields != DEBUG_FIELD_NULL; fields++) + { + debug_type ft; + const char *dn; + + ft = debug_get_field_type (dhandle, *fields); + if (ft == NULL) + return NULL; + dn = debug_get_type_name (dhandle, ft); + if (dn != NULL + && (int) strlen (dn) == dc->u.s_name.len + && strncmp (dn, dc->u.s_name.s, dc->u.s_name.len) == 0) + return ft; + } + } + } + return stab_find_tagged_type (dhandle, info, dc->u.s_name.s, + dc->u.s_name.len, DEBUG_KIND_ILLEGAL); + + case DEMANGLE_COMPONENT_QUAL_NAME: + context = stab_demangle_v3_arg (dhandle, info, dc->u.s_binary.left, + context, NULL); + if (context == NULL) + return NULL; + return stab_demangle_v3_arg (dhandle, info, dc->u.s_binary.right, + context, NULL); + + case DEMANGLE_COMPONENT_TEMPLATE: + { + char *p; + size_t alc; + + /* We print this component to get a class name which we can + use. FIXME: This probably won't work if the template uses + template parameters which refer to an outer template. */ + p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc); + if (p == NULL) + { + fprintf (stderr, _("Failed to print demangled template\n")); + return NULL; + } + dt = stab_find_tagged_type (dhandle, info, p, strlen (p), + DEBUG_KIND_CLASS); + free (p); + return dt; + } + + case DEMANGLE_COMPONENT_SUB_STD: + return stab_find_tagged_type (dhandle, info, dc->u.s_string.string, + dc->u.s_string.len, DEBUG_KIND_ILLEGAL); + + case DEMANGLE_COMPONENT_RESTRICT: + case DEMANGLE_COMPONENT_VOLATILE: + case DEMANGLE_COMPONENT_CONST: + case DEMANGLE_COMPONENT_POINTER: + case DEMANGLE_COMPONENT_REFERENCE: + dt = stab_demangle_v3_arg (dhandle, info, dc->u.s_binary.left, NULL, + NULL); + if (dt == NULL) + return NULL; + + switch (dc->type) + { + default: + abort (); + case DEMANGLE_COMPONENT_RESTRICT: + /* FIXME: We have no way to represent restrict. */ + return dt; + case DEMANGLE_COMPONENT_VOLATILE: + return debug_make_volatile_type (dhandle, dt); + case DEMANGLE_COMPONENT_CONST: + return debug_make_const_type (dhandle, dt); + case DEMANGLE_COMPONENT_POINTER: + return debug_make_pointer_type (dhandle, dt); + case DEMANGLE_COMPONENT_REFERENCE: + return debug_make_reference_type (dhandle, dt); + } + + case DEMANGLE_COMPONENT_FUNCTION_TYPE: + { + debug_type *pargs; + bfd_boolean varargs; + + if (dc->u.s_binary.left == NULL) + { + /* In this case the return type is actually unknown. + However, I'm not sure this will ever arise in practice; + normally an unknown return type would only appear at + the top level, which is handled above. */ + dt = debug_make_void_type (dhandle); + } + else + dt = stab_demangle_v3_arg (dhandle, info, dc->u.s_binary.left, NULL, + NULL); + if (dt == NULL) + return NULL; + + pargs = stab_demangle_v3_arglist (dhandle, info, + dc->u.s_binary.right, + &varargs); + if (pargs == NULL) + return NULL; + + return debug_make_function_type (dhandle, dt, pargs, varargs); + } + + case DEMANGLE_COMPONENT_BUILTIN_TYPE: + { + char *p; + size_t alc; + debug_type ret; + + /* We print this component in order to find out the type name. + FIXME: Should we instead expose the + demangle_builtin_type_info structure? */ + p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc); + if (p == NULL) + { + fprintf (stderr, _("Couldn't get demangled builtin type\n")); + return NULL; + } + + /* The mangling is based on the type, but does not itself + indicate what the sizes are. So we have to guess. */ + if (strcmp (p, "signed char") == 0) + ret = debug_make_int_type (dhandle, 1, FALSE); + else if (strcmp (p, "bool") == 0) + ret = debug_make_bool_type (dhandle, 1); + else if (strcmp (p, "char") == 0) + ret = debug_make_int_type (dhandle, 1, FALSE); + else if (strcmp (p, "double") == 0) + ret = debug_make_float_type (dhandle, 8); + else if (strcmp (p, "long double") == 0) + ret = debug_make_float_type (dhandle, 8); + else if (strcmp (p, "float") == 0) + ret = debug_make_float_type (dhandle, 4); + else if (strcmp (p, "__float128") == 0) + ret = debug_make_float_type (dhandle, 16); + else if (strcmp (p, "unsigned char") == 0) + ret = debug_make_int_type (dhandle, 1, TRUE); + else if (strcmp (p, "int") == 0) + ret = debug_make_int_type (dhandle, 4, FALSE); + else if (strcmp (p, "unsigned int") == 0) + ret = debug_make_int_type (dhandle, 4, TRUE); + else if (strcmp (p, "long") == 0) + ret = debug_make_int_type (dhandle, 4, FALSE); + else if (strcmp (p, "unsigned long") == 0) + ret = debug_make_int_type (dhandle, 4, TRUE); + else if (strcmp (p, "__int128") == 0) + ret = debug_make_int_type (dhandle, 16, FALSE); + else if (strcmp (p, "unsigned __int128") == 0) + ret = debug_make_int_type (dhandle, 16, TRUE); + else if (strcmp (p, "short") == 0) + ret = debug_make_int_type (dhandle, 2, FALSE); + else if (strcmp (p, "unsigned short") == 0) + ret = debug_make_int_type (dhandle, 2, TRUE); + else if (strcmp (p, "void") == 0) + ret = debug_make_void_type (dhandle); + else if (strcmp (p, "wchar_t") == 0) + ret = debug_make_int_type (dhandle, 4, TRUE); + else if (strcmp (p, "long long") == 0) + ret = debug_make_int_type (dhandle, 8, FALSE); + else if (strcmp (p, "unsigned long long") == 0) + ret = debug_make_int_type (dhandle, 8, TRUE); + else if (strcmp (p, "...") == 0) + { + if (pvarargs == NULL) + fprintf (stderr, _("Unexpected demangled varargs\n")); + else + *pvarargs = TRUE; + ret = NULL; + } + else + { + fprintf (stderr, _("Unrecognized demangled builtin type\n")); + ret = NULL; + } + + free (p); + + return ret; + } + } +} diff --git a/contrib/toolchain/binutils/binutils/sysdep.h b/contrib/toolchain/binutils/binutils/sysdep.h new file mode 100644 index 0000000000..5164e791c5 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/sysdep.h @@ -0,0 +1,188 @@ +/* sysdep.h -- handle host dependencies for binutils + Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2012 + Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _BIN_SYSDEP_H +#define _BIN_SYSDEP_H + +#include "alloca-conf.h" +#include "ansidecl.h" +#include +#include + +#include "bfdver.h" + +#include + +#ifdef USE_BINARY_FOPEN +#include "fopen-bin.h" +#else +#include "fopen-same.h" +#endif + +#include +#ifndef errno +extern int errno; +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef STRING_WITH_STRINGS +#include +#include +#else +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#else +extern char *strchr (); +extern char *strrchr (); +#endif +#endif +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#else +#ifdef HAVE_SYS_FILE_H +#include +#endif +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include "binary-io.h" + +#if !HAVE_DECL_STPCPY +extern char *stpcpy (char *, const char *); +#endif + +#if !HAVE_DECL_STRSTR +extern char *strstr (); +#endif + +#ifdef HAVE_SBRK +#if !HAVE_DECL_SBRK +extern char *sbrk (); +#endif +#endif + +#if !HAVE_DECL_GETENV +extern char *getenv (); +#endif + +#if !HAVE_DECL_ENVIRON +extern char **environ; +#endif + +#if !HAVE_DECL_FPRINTF +extern int fprintf (FILE *, const char *, ...); +#endif + +#if !HAVE_DECL_SNPRINTF +extern int snprintf(char *, size_t, const char *, ...); +#endif + +#if !HAVE_DECL_VSNPRINTF +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +#ifndef O_RDWR +#define O_RDWR 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifdef HAVE_LOCALE_H +# ifndef ENABLE_NLS + /* The Solaris version of locale.h always includes libintl.h. If we have + been configured with --disable-nls then ENABLE_NLS will not be defined + and the dummy definitions of bindtextdomain (et al) below will conflict + with the defintions in libintl.h. So we define these values to prevent + the bogus inclusion of libintl.h. */ +# define _LIBINTL_H +# define _LIBGETTEXT_H +# endif +# include +#endif + +#ifdef ENABLE_NLS +# include +# define _(String) gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) while (0) /* nothing */ +# define bindtextdomain(Domainname, Dirname) while (0) /* nothing */ +# define _(String) (String) +# define N_(String) (String) +#endif + +/* Used by ar.c and objcopy.c. */ +#define BUFSIZE 8192 + +/* For PATH_MAX. */ +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifndef PATH_MAX +/* For MAXPATHLEN. */ +# ifdef HAVE_SYS_PARAM_H +# include +# endif +# ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +# endif +#endif + +#endif /* _BIN_SYSDEP_H */ diff --git a/contrib/toolchain/binutils/binutils/version.c b/contrib/toolchain/binutils/binutils/version.c new file mode 100644 index 0000000000..332dff1785 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/version.c @@ -0,0 +1,42 @@ +/* version.c -- binutils version information + Copyright 1991, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 + Free Software Foundation, Inc. + + This file is part of GNU Binutils. + + 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "bucomm.h" + +/* Print the version number and copyright information, and exit. + This implements the --version option for the various programs. */ + +void +print_version (const char *name) +{ + /* This output is intended to follow the GNU standards document. */ + /* xgettext:c-format */ + printf ("GNU %s %s\n", name, BFD_VERSION_STRING); + printf (_("Copyright 2013 Free Software Foundation, Inc.\n")); + printf (_("\ +This program is free software; you may redistribute it under the terms of\n\ +the GNU General Public License version 3 or (at your option) any later version.\n\ +This program has absolutely no warranty.\n")); + exit (0); +} diff --git a/contrib/toolchain/binutils/binutils/wrstabs.c b/contrib/toolchain/binutils/binutils/wrstabs.c new file mode 100644 index 0000000000..bbf257e5f5 --- /dev/null +++ b/contrib/toolchain/binutils/binutils/wrstabs.c @@ -0,0 +1,2272 @@ +/* wrstabs.c -- Output stabs debugging information + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, + 2007, 2009 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This file contains code which writes out stabs debugging + information. */ + +#include "sysdep.h" +#include +#include "bfd.h" +#include "libiberty.h" +#include "filenames.h" +#include "safe-ctype.h" +#include "bucomm.h" +#include "debug.h" +#include "budbg.h" +#include "aout/aout64.h" +#include "aout/stab_gnu.h" + +/* The size of a stabs symbol. This presumes 32 bit values. */ + +#define STAB_SYMBOL_SIZE (12) + +/* An entry in a string hash table. */ + +struct string_hash_entry +{ + struct bfd_hash_entry root; + /* Next string in this table. */ + struct string_hash_entry *next; + /* Index in string table. */ + long index; + /* Size of type if this is a typedef. */ + unsigned int size; +}; + +/* A string hash table. */ + +struct string_hash_table +{ + struct bfd_hash_table table; +}; + +/* The type stack. Each element on the stack is a string. */ + +struct stab_type_stack +{ + /* The next element on the stack. */ + struct stab_type_stack *next; + /* This element as a string. */ + char *string; + /* The type index of this element. */ + long index; + /* The size of the type. */ + unsigned int size; + /* Whether type string defines a new type. */ + bfd_boolean definition; + /* String defining struct fields. */ + char *fields; + /* NULL terminated array of strings defining base classes for a + class. */ + char **baseclasses; + /* String defining class methods. */ + char *methods; + /* String defining vtable pointer for a class. */ + char *vtable; +}; + +/* This structure is used to keep track of type indices for tagged + types. */ + +struct stab_tag +{ + /* The type index. */ + long index; + /* The tag name. */ + const char *tag; + /* The kind of type. This is set to DEBUG_KIND_ILLEGAL when the + type is defined. */ + enum debug_type_kind kind; + /* The size of the struct. */ + unsigned int size; +}; + +/* We remember various sorts of type indices. They are not related, + but, for convenience, we keep all the information in this + structure. */ + +struct stab_type_cache +{ + /* The void type index. */ + long void_type; + /* Signed integer type indices, indexed by size - 1. */ + long signed_integer_types[8]; + /* Unsigned integer type indices, indexed by size - 1. */ + long unsigned_integer_types[8]; + /* Floating point types, indexed by size - 1. */ + long float_types[16]; + /* Pointers to types, indexed by the type index. */ + long *pointer_types; + size_t pointer_types_alloc; + /* Functions returning types, indexed by the type index. */ + long *function_types; + size_t function_types_alloc; + /* References to types, indexed by the type index. */ + long *reference_types; + size_t reference_types_alloc; + /* Struct/union/class type indices, indexed by the struct id. */ + struct stab_tag *struct_types; + size_t struct_types_alloc; +}; + +/* This is the handle passed through debug_write. */ + +struct stab_write_handle +{ + /* The BFD. */ + bfd *abfd; + /* This buffer holds the symbols. */ + bfd_byte *symbols; + size_t symbols_size; + size_t symbols_alloc; + /* This is a list of hash table entries for the strings. */ + struct string_hash_entry *strings; + /* The last string hash table entry. */ + struct string_hash_entry *last_string; + /* The size of the strings. */ + size_t strings_size; + /* This hash table eliminates duplicate strings. */ + struct string_hash_table strhash; + /* The type stack. */ + struct stab_type_stack *type_stack; + /* The next type index. */ + long type_index; + /* The type cache. */ + struct stab_type_cache type_cache; + /* A mapping from typedef names to type indices. */ + struct string_hash_table typedef_hash; + /* If this is not -1, it is the offset to the most recent N_SO + symbol, and the value of that symbol needs to be set. */ + long so_offset; + /* If this is not -1, it is the offset to the most recent N_FUN + symbol, and the value of that symbol needs to be set. */ + long fun_offset; + /* The last text section address seen. */ + bfd_vma last_text_address; + /* The block nesting depth. */ + unsigned int nesting; + /* The function address. */ + bfd_vma fnaddr; + /* A pending LBRAC symbol. */ + bfd_vma pending_lbrac; + /* The current line number file name. */ + const char *lineno_filename; +}; + +static struct bfd_hash_entry *string_hash_newfunc + (struct bfd_hash_entry *, struct bfd_hash_table *, const char *); +static bfd_boolean stab_write_symbol + (struct stab_write_handle *, int, int, bfd_vma, const char *); +static bfd_boolean stab_push_string + (struct stab_write_handle *, const char *, long, bfd_boolean, unsigned int); +static bfd_boolean stab_push_defined_type + (struct stab_write_handle *, long, unsigned int); +static char *stab_pop_type (struct stab_write_handle *); +static bfd_boolean stab_modify_type + (struct stab_write_handle *, int, unsigned int, long **, size_t *); +static long stab_get_struct_index + (struct stab_write_handle *, const char *, unsigned int, + enum debug_type_kind, unsigned int *); +static bfd_boolean stab_class_method_var + (struct stab_write_handle *, const char *, enum debug_visibility, + bfd_boolean, bfd_boolean, bfd_boolean, bfd_vma, bfd_boolean); +static bfd_boolean stab_start_compilation_unit (void *, const char *); +static bfd_boolean stab_start_source (void *, const char *); +static bfd_boolean stab_empty_type (void *); +static bfd_boolean stab_void_type (void *); +static bfd_boolean stab_int_type (void *, unsigned int, bfd_boolean); +static bfd_boolean stab_float_type (void *, unsigned int); +static bfd_boolean stab_complex_type (void *, unsigned int); +static bfd_boolean stab_bool_type (void *, unsigned int); +static bfd_boolean stab_enum_type + (void *, const char *, const char **, bfd_signed_vma *); +static bfd_boolean stab_pointer_type (void *); +static bfd_boolean stab_function_type (void *, int, bfd_boolean); +static bfd_boolean stab_reference_type (void *); +static bfd_boolean stab_range_type (void *, bfd_signed_vma, bfd_signed_vma); +static bfd_boolean stab_array_type + (void *, bfd_signed_vma, bfd_signed_vma, bfd_boolean); +static bfd_boolean stab_set_type (void *, bfd_boolean); +static bfd_boolean stab_offset_type (void *); +static bfd_boolean stab_method_type (void *, bfd_boolean, int, bfd_boolean); +static bfd_boolean stab_const_type (void *); +static bfd_boolean stab_volatile_type (void *); +static bfd_boolean stab_start_struct_type + (void *, const char *, unsigned int, bfd_boolean, unsigned int); +static bfd_boolean stab_struct_field + (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility); +static bfd_boolean stab_end_struct_type (void *); +static bfd_boolean stab_start_class_type + (void *, const char *, unsigned int, bfd_boolean, unsigned int, + bfd_boolean, bfd_boolean); +static bfd_boolean stab_class_static_member + (void *, const char *, const char *, enum debug_visibility); +static bfd_boolean stab_class_baseclass + (void *, bfd_vma, bfd_boolean, enum debug_visibility); +static bfd_boolean stab_class_start_method (void *, const char *); +static bfd_boolean stab_class_method_variant + (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean, + bfd_vma, bfd_boolean); +static bfd_boolean stab_class_static_method_variant + (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean); +static bfd_boolean stab_class_end_method (void *); +static bfd_boolean stab_end_class_type (void *); +static bfd_boolean stab_typedef_type (void *, const char *); +static bfd_boolean stab_tag_type + (void *, const char *, unsigned int, enum debug_type_kind); +static bfd_boolean stab_typdef (void *, const char *); +static bfd_boolean stab_tag (void *, const char *); +static bfd_boolean stab_int_constant (void *, const char *, bfd_vma); +static bfd_boolean stab_float_constant (void *, const char *, double); +static bfd_boolean stab_typed_constant (void *, const char *, bfd_vma); +static bfd_boolean stab_variable + (void *, const char *, enum debug_var_kind, bfd_vma); +static bfd_boolean stab_start_function (void *, const char *, bfd_boolean); +static bfd_boolean stab_function_parameter + (void *, const char *, enum debug_parm_kind, bfd_vma); +static bfd_boolean stab_start_block (void *, bfd_vma); +static bfd_boolean stab_end_block (void *, bfd_vma); +static bfd_boolean stab_end_function (void *); +static bfd_boolean stab_lineno (void *, const char *, unsigned long, bfd_vma); + +static const struct debug_write_fns stab_fns = +{ + stab_start_compilation_unit, + stab_start_source, + stab_empty_type, + stab_void_type, + stab_int_type, + stab_float_type, + stab_complex_type, + stab_bool_type, + stab_enum_type, + stab_pointer_type, + stab_function_type, + stab_reference_type, + stab_range_type, + stab_array_type, + stab_set_type, + stab_offset_type, + stab_method_type, + stab_const_type, + stab_volatile_type, + stab_start_struct_type, + stab_struct_field, + stab_end_struct_type, + stab_start_class_type, + stab_class_static_member, + stab_class_baseclass, + stab_class_start_method, + stab_class_method_variant, + stab_class_static_method_variant, + stab_class_end_method, + stab_end_class_type, + stab_typedef_type, + stab_tag_type, + stab_typdef, + stab_tag, + stab_int_constant, + stab_float_constant, + stab_typed_constant, + stab_variable, + stab_start_function, + stab_function_parameter, + stab_start_block, + stab_end_block, + stab_end_function, + stab_lineno +}; + +/* Routine to create an entry in a string hash table. */ + +static struct bfd_hash_entry * +string_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) +{ + struct string_hash_entry *ret = (struct string_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct string_hash_entry *) NULL) + ret = ((struct string_hash_entry *) + bfd_hash_allocate (table, sizeof (struct string_hash_entry))); + if (ret == (struct string_hash_entry *) NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct string_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + if (ret) + { + /* Initialize the local fields. */ + ret->next = NULL; + ret->index = -1; + ret->size = 0; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Look up an entry in a string hash table. */ + +#define string_hash_lookup(t, string, create, copy) \ + ((struct string_hash_entry *) \ + bfd_hash_lookup (&(t)->table, (string), (create), (copy))) + +/* Add a symbol to the stabs debugging information we are building. */ + +static bfd_boolean +stab_write_symbol (struct stab_write_handle *info, int type, int desc, + bfd_vma value, const char *string) +{ + bfd_size_type strx; + bfd_byte sym[STAB_SYMBOL_SIZE]; + + if (string == NULL) + strx = 0; + else + { + struct string_hash_entry *h; + + h = string_hash_lookup (&info->strhash, string, TRUE, TRUE); + if (h == NULL) + { + non_fatal (_("string_hash_lookup failed: %s"), + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + if (h->index != -1) + strx = h->index; + else + { + strx = info->strings_size; + h->index = strx; + if (info->last_string == NULL) + info->strings = h; + else + info->last_string->next = h; + info->last_string = h; + info->strings_size += strlen (string) + 1; + } + } + + /* This presumes 32 bit values. */ + bfd_put_32 (info->abfd, strx, sym); + bfd_put_8 (info->abfd, type, sym + 4); + bfd_put_8 (info->abfd, 0, sym + 5); + bfd_put_16 (info->abfd, desc, sym + 6); + bfd_put_32 (info->abfd, value, sym + 8); + + if (info->symbols_size + STAB_SYMBOL_SIZE > info->symbols_alloc) + { + info->symbols_alloc *= 2; + info->symbols = (bfd_byte *) xrealloc (info->symbols, + info->symbols_alloc); + } + + memcpy (info->symbols + info->symbols_size, sym, STAB_SYMBOL_SIZE); + + info->symbols_size += STAB_SYMBOL_SIZE; + + return TRUE; +} + +/* Push a string on to the type stack. */ + +static bfd_boolean +stab_push_string (struct stab_write_handle *info, const char *string, + long tindex, bfd_boolean definition, unsigned int size) +{ + struct stab_type_stack *s; + + s = (struct stab_type_stack *) xmalloc (sizeof *s); + s->string = xstrdup (string); + s->index = tindex; + s->definition = definition; + s->size = size; + + s->fields = NULL; + s->baseclasses = NULL; + s->methods = NULL; + s->vtable = NULL; + + s->next = info->type_stack; + info->type_stack = s; + + return TRUE; +} + +/* Push a type index which has already been defined. */ + +static bfd_boolean +stab_push_defined_type (struct stab_write_handle *info, long tindex, + unsigned int size) +{ + char buf[20]; + + sprintf (buf, "%ld", tindex); + return stab_push_string (info, buf, tindex, FALSE, size); +} + +/* Pop a type off the type stack. The caller is responsible for + freeing the string. */ + +static char * +stab_pop_type (struct stab_write_handle *info) +{ + struct stab_type_stack *s; + char *ret; + + s = info->type_stack; + assert (s != NULL); + + info->type_stack = s->next; + + ret = s->string; + + free (s); + + return ret; +} + +/* The general routine to write out stabs in sections debugging + information. This accumulates the stabs symbols and the strings in + two obstacks. We can't easily write out the information as we go + along, because we need to know the section sizes before we can + write out the section contents. ABFD is the BFD and DHANDLE is the + handle for the debugging information. This sets *PSYMS to point to + the symbols, *PSYMSIZE the size of the symbols, *PSTRINGS to the + strings, and *PSTRINGSIZE to the size of the strings. */ + +bfd_boolean +write_stabs_in_sections_debugging_info (bfd *abfd, void *dhandle, + bfd_byte **psyms, + bfd_size_type *psymsize, + bfd_byte **pstrings, + bfd_size_type *pstringsize) +{ + struct stab_write_handle info; + struct string_hash_entry *h; + bfd_byte *p; + + info.abfd = abfd; + + info.symbols_size = 0; + info.symbols_alloc = 500; + info.symbols = (bfd_byte *) xmalloc (info.symbols_alloc); + + info.strings = NULL; + info.last_string = NULL; + /* Reserve 1 byte for a null byte. */ + info.strings_size = 1; + + if (!bfd_hash_table_init (&info.strhash.table, string_hash_newfunc, + sizeof (struct string_hash_entry)) + || !bfd_hash_table_init (&info.typedef_hash.table, string_hash_newfunc, + sizeof (struct string_hash_entry))) + { + non_fatal ("bfd_hash_table_init_failed: %s", + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + info.type_stack = NULL; + info.type_index = 1; + memset (&info.type_cache, 0, sizeof info.type_cache); + info.so_offset = -1; + info.fun_offset = -1; + info.last_text_address = 0; + info.nesting = 0; + info.fnaddr = 0; + info.pending_lbrac = (bfd_vma) -1; + + /* The initial symbol holds the string size. */ + if (! stab_write_symbol (&info, 0, 0, 0, (const char *) NULL)) + return FALSE; + + /* Output an initial N_SO symbol. */ + info.so_offset = info.symbols_size; + if (! stab_write_symbol (&info, N_SO, 0, 0, bfd_get_filename (abfd))) + return FALSE; + + if (! debug_write (dhandle, &stab_fns, (void *) &info)) + return FALSE; + + assert (info.pending_lbrac == (bfd_vma) -1); + + /* Output a trailing N_SO. */ + if (! stab_write_symbol (&info, N_SO, 0, info.last_text_address, + (const char *) NULL)) + return FALSE; + + /* Put the string size in the initial symbol. */ + bfd_put_32 (abfd, info.strings_size, info.symbols + 8); + + *psyms = info.symbols; + *psymsize = info.symbols_size; + + *pstringsize = info.strings_size; + *pstrings = (bfd_byte *) xmalloc (info.strings_size); + + p = *pstrings; + *p++ = '\0'; + for (h = info.strings; h != NULL; h = h->next) + { + strcpy ((char *) p, h->root.string); + p += strlen ((char *) p) + 1; + } + + return TRUE; +} + +/* Start writing out information for a compilation unit. */ + +static bfd_boolean +stab_start_compilation_unit (void *p, const char *filename) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* We would normally output an N_SO symbol here. However, that + would force us to reset all of our type information. I think we + will be better off just outputting an N_SOL symbol, and not + worrying about splitting information between files. */ + + info->lineno_filename = filename; + + return stab_write_symbol (info, N_SOL, 0, 0, filename); +} + +/* Start writing out information for a particular source file. */ + +static bfd_boolean +stab_start_source (void *p, const char *filename) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The symbol's value is supposed to be the text section + address. However, we would have to fill it in later, and gdb + doesn't care, so we don't bother with it. */ + + info->lineno_filename = filename; + + return stab_write_symbol (info, N_SOL, 0, 0, filename); +} + +/* Push an empty type. This shouldn't normally happen. We just use a + void type. */ + +static bfd_boolean +stab_empty_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* We don't call stab_void_type if the type is not yet defined, + because that might screw up the typedef. */ + + if (info->type_cache.void_type != 0) + return stab_push_defined_type (info, info->type_cache.void_type, 0); + else + { + long tindex; + char buf[40]; + + tindex = info->type_index; + ++info->type_index; + + sprintf (buf, "%ld=%ld", tindex, tindex); + + return stab_push_string (info, buf, tindex, FALSE, 0); + } +} + +/* Push a void type. */ + +static bfd_boolean +stab_void_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (info->type_cache.void_type != 0) + return stab_push_defined_type (info, info->type_cache.void_type, 0); + else + { + long tindex; + char buf[40]; + + tindex = info->type_index; + ++info->type_index; + + info->type_cache.void_type = tindex; + + sprintf (buf, "%ld=%ld", tindex, tindex); + + return stab_push_string (info, buf, tindex, TRUE, 0); + } +} + +/* Push an integer type. */ + +static bfd_boolean +stab_int_type (void *p, unsigned int size, bfd_boolean unsignedp) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long *cache; + + if (size <= 0 || (size > sizeof (long) && size != 8)) + { + non_fatal (_("stab_int_type: bad size %u"), size); + return FALSE; + } + + if (unsignedp) + cache = info->type_cache.signed_integer_types; + else + cache = info->type_cache.unsigned_integer_types; + + if (cache[size - 1] != 0) + return stab_push_defined_type (info, cache[size - 1], size); + else + { + long tindex; + char buf[100]; + + tindex = info->type_index; + ++info->type_index; + + cache[size - 1] = tindex; + + sprintf (buf, "%ld=r%ld;", tindex, tindex); + if (unsignedp) + { + strcat (buf, "0;"); + if (size < sizeof (long)) + sprintf (buf + strlen (buf), "%ld;", ((long) 1 << (size * 8)) - 1); + else if (size == sizeof (long)) + strcat (buf, "-1;"); + else if (size == 8) + strcat (buf, "01777777777777777777777;"); + else + abort (); + } + else + { + if (size <= sizeof (long)) + sprintf (buf + strlen (buf), "%ld;%ld;", + (long) - ((unsigned long) 1 << (size * 8 - 1)), + (long) (((unsigned long) 1 << (size * 8 - 1)) - 1)); + else if (size == 8) + strcat (buf, "01000000000000000000000;0777777777777777777777;"); + else + abort (); + } + + return stab_push_string (info, buf, tindex, TRUE, size); + } +} + +/* Push a floating point type. */ + +static bfd_boolean +stab_float_type (void *p, unsigned int size) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (size > 0 + && size - 1 < (sizeof info->type_cache.float_types + / sizeof info->type_cache.float_types[0]) + && info->type_cache.float_types[size - 1] != 0) + return stab_push_defined_type (info, + info->type_cache.float_types[size - 1], + size); + else + { + long tindex; + char *int_type; + char buf[50]; + + /* Floats are defined as a subrange of int. */ + if (! stab_int_type (info, 4, FALSE)) + return FALSE; + int_type = stab_pop_type (info); + + tindex = info->type_index; + ++info->type_index; + + if (size > 0 + && size - 1 < (sizeof info->type_cache.float_types + / sizeof info->type_cache.float_types[0])) + info->type_cache.float_types[size - 1] = tindex; + + sprintf (buf, "%ld=r%s;%u;0;", tindex, int_type, size); + + free (int_type); + + return stab_push_string (info, buf, tindex, TRUE, size); + } +} + +/* Push a complex type. */ + +static bfd_boolean +stab_complex_type (void *p, unsigned int size) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char buf[50]; + long tindex; + + tindex = info->type_index; + ++info->type_index; + + sprintf (buf, "%ld=r%ld;%u;0;", tindex, tindex, size); + + return stab_push_string (info, buf, tindex, TRUE, size * 2); +} + +/* Push a bfd_boolean type. We use an XCOFF predefined type, since gdb + always recognizes them. */ + +static bfd_boolean +stab_bool_type (void *p, unsigned int size) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long tindex; + + switch (size) + { + case 1: + tindex = -21; + break; + + case 2: + tindex = -22; + break; + + default: + case 4: + tindex = -16; + break; + + case 8: + tindex = -33; + break; + } + + return stab_push_defined_type (info, tindex, size); +} + +/* Push an enum type. */ + +static bfd_boolean +stab_enum_type (void *p, const char *tag, const char **names, + bfd_signed_vma *vals) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + size_t len; + const char **pn; + char *buf; + long tindex = 0; + bfd_signed_vma *pv; + + if (names == NULL) + { + assert (tag != NULL); + + buf = (char *) xmalloc (10 + strlen (tag)); + sprintf (buf, "xe%s:", tag); + /* FIXME: The size is just a guess. */ + if (! stab_push_string (info, buf, 0, FALSE, 4)) + return FALSE; + free (buf); + return TRUE; + } + + len = 10; + if (tag != NULL) + len += strlen (tag); + for (pn = names; *pn != NULL; pn++) + len += strlen (*pn) + 20; + + buf = (char *) xmalloc (len); + + if (tag == NULL) + strcpy (buf, "e"); + else + { + tindex = info->type_index; + ++info->type_index; + sprintf (buf, "%s:T%ld=e", tag, tindex); + } + + for (pn = names, pv = vals; *pn != NULL; pn++, pv++) + sprintf (buf + strlen (buf), "%s:%ld,", *pn, (long) *pv); + strcat (buf, ";"); + + if (tag == NULL) + { + /* FIXME: The size is just a guess. */ + if (! stab_push_string (info, buf, 0, FALSE, 4)) + return FALSE; + } + else + { + /* FIXME: The size is just a guess. */ + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf) + || ! stab_push_defined_type (info, tindex, 4)) + return FALSE; + } + + free (buf); + + return TRUE; +} + +/* Push a modification of the top type on the stack. Cache the + results in CACHE and CACHE_ALLOC. */ + +static bfd_boolean +stab_modify_type (struct stab_write_handle *info, int mod, + unsigned int size, long **cache, size_t *cache_alloc) +{ + long targindex; + long tindex; + char *s, *buf; + + assert (info->type_stack != NULL); + targindex = info->type_stack->index; + + if (targindex <= 0 + || cache == NULL) + { + bfd_boolean definition; + + /* Either the target type has no index, or we aren't caching + this modifier. Either way we have no way of recording the + new type, so we don't bother to define one. */ + definition = info->type_stack->definition; + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 2); + sprintf (buf, "%c%s", mod, s); + free (s); + if (! stab_push_string (info, buf, 0, definition, size)) + return FALSE; + free (buf); + } + else + { + if ((size_t) targindex >= *cache_alloc) + { + size_t alloc; + + alloc = *cache_alloc; + if (alloc == 0) + alloc = 10; + while ((size_t) targindex >= alloc) + alloc *= 2; + *cache = (long *) xrealloc (*cache, alloc * sizeof (long)); + memset (*cache + *cache_alloc, 0, + (alloc - *cache_alloc) * sizeof (long)); + *cache_alloc = alloc; + } + + tindex = (*cache)[targindex]; + if (tindex != 0 && ! info->type_stack->definition) + { + /* We have already defined a modification of this type, and + the entry on the type stack is not a definition, so we + can safely discard it (we may have a definition on the + stack, even if we already defined a modification, if it + is a struct which we did not define at the time it was + referenced). */ + free (stab_pop_type (info)); + if (! stab_push_defined_type (info, tindex, size)) + return FALSE; + } + else + { + tindex = info->type_index; + ++info->type_index; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 20); + sprintf (buf, "%ld=%c%s", tindex, mod, s); + free (s); + + (*cache)[targindex] = tindex; + + if (! stab_push_string (info, buf, tindex, TRUE, size)) + return FALSE; + + free (buf); + } + } + + return TRUE; +} + +/* Push a pointer type. */ + +static bfd_boolean +stab_pointer_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The size should depend upon the architecture. */ + return stab_modify_type (info, '*', 4, &info->type_cache.pointer_types, + &info->type_cache.pointer_types_alloc); +} + +/* Push a function type. */ + +static bfd_boolean +stab_function_type (void *p, int argcount, + bfd_boolean varargs ATTRIBUTE_UNUSED) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + int i; + + /* We have no way to represent the argument types, so we just + discard them. However, if they define new types, we must output + them. We do this by producing empty typedefs. */ + for (i = 0; i < argcount; i++) + { + if (! info->type_stack->definition) + free (stab_pop_type (info)); + else + { + char *s, *buf; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (s) + 3); + sprintf (buf, ":t%s", s); + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + } + } + + return stab_modify_type (info, 'f', 0, &info->type_cache.function_types, + &info->type_cache.function_types_alloc); +} + +/* Push a reference type. */ + +static bfd_boolean +stab_reference_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The size should depend upon the architecture. */ + return stab_modify_type (info, '&', 4, &info->type_cache.reference_types, + &info->type_cache.reference_types_alloc); +} + +/* Push a range type. */ + +static bfd_boolean +stab_range_type (void *p, bfd_signed_vma low, bfd_signed_vma high) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + unsigned int size; + char *s, *buf; + + definition = info->type_stack->definition; + size = info->type_stack->size; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 100); + sprintf (buf, "r%s;%ld;%ld;", s, (long) low, (long) high); + free (s); + + if (! stab_push_string (info, buf, 0, definition, size)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Push an array type. */ + +static bfd_boolean +stab_array_type (void *p, bfd_signed_vma low, bfd_signed_vma high, + bfd_boolean stringp) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + unsigned int element_size; + char *range, *element, *buf; + long tindex; + unsigned int size; + + definition = info->type_stack->definition; + range = stab_pop_type (info); + + definition = definition || info->type_stack->definition; + element_size = info->type_stack->size; + element = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (range) + strlen (element) + 100); + + if (! stringp) + { + tindex = 0; + *buf = '\0'; + } + else + { + /* We need to define a type in order to include the string + attribute. */ + tindex = info->type_index; + ++info->type_index; + definition = TRUE; + sprintf (buf, "%ld=@S;", tindex); + } + + sprintf (buf + strlen (buf), "ar%s;%ld;%ld;%s", + range, (long) low, (long) high, element); + free (range); + free (element); + + if (high < low) + size = 0; + else + size = element_size * ((high - low) + 1); + if (! stab_push_string (info, buf, tindex, definition, size)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Push a set type. */ + +static bfd_boolean +stab_set_type (void *p, bfd_boolean bitstringp) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *s, *buf; + long tindex; + + definition = info->type_stack->definition; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 30); + + if (! bitstringp) + { + *buf = '\0'; + tindex = 0; + } + else + { + /* We need to define a type in order to include the string + attribute. */ + tindex = info->type_index; + ++info->type_index; + definition = TRUE; + sprintf (buf, "%ld=@S;", tindex); + } + + sprintf (buf + strlen (buf), "S%s", s); + free (s); + + if (! stab_push_string (info, buf, tindex, definition, 0)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Push an offset type. */ + +static bfd_boolean +stab_offset_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *target, *base, *buf; + + definition = info->type_stack->definition; + target = stab_pop_type (info); + + definition = definition || info->type_stack->definition; + base = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (target) + strlen (base) + 3); + sprintf (buf, "@%s,%s", base, target); + free (base); + free (target); + + if (! stab_push_string (info, buf, 0, definition, 0)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Push a method type. */ + +static bfd_boolean +stab_method_type (void *p, bfd_boolean domainp, int argcount, + bfd_boolean varargs) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *domain, *return_type, *buf; + char **args; + int i; + size_t len; + + /* We don't bother with stub method types, because that would + require a mangler for C++ argument types. This will waste space + in the debugging output. */ + + /* We need a domain. I'm not sure DOMAINP can ever be false, + anyhow. */ + if (! domainp) + { + if (! stab_empty_type (p)) + return FALSE; + } + + definition = info->type_stack->definition; + domain = stab_pop_type (info); + + /* A non-varargs function is indicated by making the last parameter + type be void. */ + + if (argcount < 0) + { + args = NULL; + argcount = 0; + } + else if (argcount == 0) + { + if (varargs) + args = NULL; + else + { + args = (char **) xmalloc (1 * sizeof (*args)); + if (! stab_empty_type (p)) + return FALSE; + definition = definition || info->type_stack->definition; + args[0] = stab_pop_type (info); + argcount = 1; + } + } + else + { + args = (char **) xmalloc ((argcount + 1) * sizeof (*args)); + for (i = argcount - 1; i >= 0; i--) + { + definition = definition || info->type_stack->definition; + args[i] = stab_pop_type (info); + } + if (! varargs) + { + if (! stab_empty_type (p)) + return FALSE; + definition = definition || info->type_stack->definition; + args[argcount] = stab_pop_type (info); + ++argcount; + } + } + + definition = definition || info->type_stack->definition; + return_type = stab_pop_type (info); + + len = strlen (domain) + strlen (return_type) + 10; + for (i = 0; i < argcount; i++) + len += strlen (args[i]); + + buf = (char *) xmalloc (len); + + sprintf (buf, "#%s,%s", domain, return_type); + free (domain); + free (return_type); + for (i = 0; i < argcount; i++) + { + strcat (buf, ","); + strcat (buf, args[i]); + free (args[i]); + } + strcat (buf, ";"); + + if (args != NULL) + free (args); + + if (! stab_push_string (info, buf, 0, definition, 0)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Push a const version of a type. */ + +static bfd_boolean +stab_const_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_modify_type (info, 'k', info->type_stack->size, + (long **) NULL, (size_t *) NULL); +} + +/* Push a volatile version of a type. */ + +static bfd_boolean +stab_volatile_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_modify_type (info, 'B', info->type_stack->size, + (long **) NULL, (size_t *) NULL); +} + +/* Get the type index to use for a struct/union/class ID. This should + return -1 if it fails. */ + +static long +stab_get_struct_index (struct stab_write_handle *info, const char *tag, + unsigned int id, enum debug_type_kind kind, + unsigned int *psize) +{ + if (id >= info->type_cache.struct_types_alloc) + { + size_t alloc; + + alloc = info->type_cache.struct_types_alloc; + if (alloc == 0) + alloc = 10; + while (id >= alloc) + alloc *= 2; + info->type_cache.struct_types = + (struct stab_tag *) xrealloc (info->type_cache.struct_types, + alloc * sizeof (struct stab_tag)); + memset ((info->type_cache.struct_types + + info->type_cache.struct_types_alloc), + 0, + ((alloc - info->type_cache.struct_types_alloc) + * sizeof (struct stab_tag))); + info->type_cache.struct_types_alloc = alloc; + } + + if (info->type_cache.struct_types[id].index == 0) + { + info->type_cache.struct_types[id].index = info->type_index; + ++info->type_index; + info->type_cache.struct_types[id].tag = tag; + info->type_cache.struct_types[id].kind = kind; + } + + if (kind == DEBUG_KIND_ILLEGAL) + { + /* This is a definition of the struct. */ + info->type_cache.struct_types[id].kind = kind; + info->type_cache.struct_types[id].size = *psize; + } + else + *psize = info->type_cache.struct_types[id].size; + + return info->type_cache.struct_types[id].index; +} + +/* Start outputting a struct. We ignore the tag, and handle it in + stab_tag. */ + +static bfd_boolean +stab_start_struct_type (void *p, const char *tag, unsigned int id, + bfd_boolean structp, unsigned int size) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long tindex; + bfd_boolean definition; + char buf[40]; + + if (id == 0) + { + tindex = 0; + *buf = '\0'; + definition = FALSE; + } + else + { + tindex = stab_get_struct_index (info, tag, id, DEBUG_KIND_ILLEGAL, + &size); + if (tindex < 0) + return FALSE; + sprintf (buf, "%ld=", tindex); + definition = TRUE; + } + + sprintf (buf + strlen (buf), "%c%u", + structp ? 's' : 'u', + size); + + if (! stab_push_string (info, buf, tindex, definition, size)) + return FALSE; + + info->type_stack->fields = (char *) xmalloc (1); + info->type_stack->fields[0] = '\0'; + + return TRUE; +} + +/* Add a field to a struct. */ + +static bfd_boolean +stab_struct_field (void *p, const char *name, bfd_vma bitpos, + bfd_vma bitsize, enum debug_visibility visibility) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + unsigned int size; + char *s, *n; + const char *vis; + + definition = info->type_stack->definition; + size = info->type_stack->size; + s = stab_pop_type (info); + + /* Add this field to the end of the current struct fields, which is + currently on the top of the stack. */ + + assert (info->type_stack->fields != NULL); + n = (char *) xmalloc (strlen (info->type_stack->fields) + + strlen (name) + + strlen (s) + + 50); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PUBLIC: + vis = ""; + break; + + case DEBUG_VISIBILITY_PRIVATE: + vis = "/0"; + break; + + case DEBUG_VISIBILITY_PROTECTED: + vis = "/1"; + break; + } + + if (bitsize == 0) + { + bitsize = size * 8; + if (bitsize == 0) + non_fatal (_("%s: warning: unknown size for field `%s' in struct"), + bfd_get_filename (info->abfd), name); + } + + sprintf (n, "%s%s:%s%s,%ld,%ld;", info->type_stack->fields, name, vis, s, + (long) bitpos, (long) bitsize); + + free (info->type_stack->fields); + info->type_stack->fields = n; + + if (definition) + info->type_stack->definition = TRUE; + + return TRUE; +} + +/* Finish up a struct. */ + +static bfd_boolean +stab_end_struct_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + long tindex; + unsigned int size; + char *fields, *first, *buf; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + definition = info->type_stack->definition; + tindex = info->type_stack->index; + size = info->type_stack->size; + fields = info->type_stack->fields; + first = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (first) + strlen (fields) + 2); + sprintf (buf, "%s%s;", first, fields); + free (first); + free (fields); + + if (! stab_push_string (info, buf, tindex, definition, size)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Start outputting a class. */ + +static bfd_boolean +stab_start_class_type (void *p, const char *tag, unsigned int id, bfd_boolean structp, unsigned int size, bfd_boolean vptr, bfd_boolean ownvptr) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *vstring; + + if (! vptr || ownvptr) + { + definition = FALSE; + vstring = NULL; + } + else + { + definition = info->type_stack->definition; + vstring = stab_pop_type (info); + } + + if (! stab_start_struct_type (p, tag, id, structp, size)) + return FALSE; + + if (vptr) + { + char *vtable; + + if (ownvptr) + { + assert (info->type_stack->index > 0); + vtable = (char *) xmalloc (20); + sprintf (vtable, "~%%%ld", info->type_stack->index); + } + else + { + vtable = (char *) xmalloc (strlen (vstring) + 3); + sprintf (vtable, "~%%%s", vstring); + free (vstring); + } + + info->type_stack->vtable = vtable; + } + + if (definition) + info->type_stack->definition = TRUE; + + return TRUE; +} + +/* Add a static member to the class on the type stack. */ + +static bfd_boolean +stab_class_static_member (void *p, const char *name, const char *physname, + enum debug_visibility visibility) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *s, *n; + const char *vis; + + definition = info->type_stack->definition; + s = stab_pop_type (info); + + /* Add this field to the end of the current struct fields, which is + currently on the top of the stack. */ + + assert (info->type_stack->fields != NULL); + n = (char *) xmalloc (strlen (info->type_stack->fields) + + strlen (name) + + strlen (s) + + strlen (physname) + + 10); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PUBLIC: + vis = ""; + break; + + case DEBUG_VISIBILITY_PRIVATE: + vis = "/0"; + break; + + case DEBUG_VISIBILITY_PROTECTED: + vis = "/1"; + break; + } + + sprintf (n, "%s%s:%s%s:%s;", info->type_stack->fields, name, vis, s, + physname); + + free (info->type_stack->fields); + info->type_stack->fields = n; + + if (definition) + info->type_stack->definition = TRUE; + + return TRUE; +} + +/* Add a base class to the class on the type stack. */ + +static bfd_boolean +stab_class_baseclass (void *p, bfd_vma bitpos, bfd_boolean is_virtual, + enum debug_visibility visibility) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + bfd_boolean definition; + char *s; + char *buf; + unsigned int c; + char **baseclasses; + + definition = info->type_stack->definition; + s = stab_pop_type (info); + + /* Build the base class specifier. */ + + buf = (char *) xmalloc (strlen (s) + 25); + buf[0] = is_virtual ? '1' : '0'; + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PRIVATE: + buf[1] = '0'; + break; + + case DEBUG_VISIBILITY_PROTECTED: + buf[1] = '1'; + break; + + case DEBUG_VISIBILITY_PUBLIC: + buf[1] = '2'; + break; + } + + sprintf (buf + 2, "%ld,%s;", (long) bitpos, s); + free (s); + + /* Add the new baseclass to the existing ones. */ + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + if (info->type_stack->baseclasses == NULL) + c = 0; + else + { + c = 0; + while (info->type_stack->baseclasses[c] != NULL) + ++c; + } + + baseclasses = (char **) xrealloc (info->type_stack->baseclasses, + (c + 2) * sizeof (*baseclasses)); + baseclasses[c] = buf; + baseclasses[c + 1] = NULL; + + info->type_stack->baseclasses = baseclasses; + + if (definition) + info->type_stack->definition = TRUE; + + return TRUE; +} + +/* Start adding a method to the class on the type stack. */ + +static bfd_boolean +stab_class_start_method (void *p, const char *name) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *m; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + if (info->type_stack->methods == NULL) + { + m = (char *) xmalloc (strlen (name) + 3); + *m = '\0'; + } + else + { + m = (char *) xrealloc (info->type_stack->methods, + (strlen (info->type_stack->methods) + + strlen (name) + + 4)); + } + + sprintf (m + strlen (m), "%s::", name); + + info->type_stack->methods = m; + + return TRUE; +} + +/* Add a variant, either static or not, to the current method. */ + +static bfd_boolean +stab_class_method_var (struct stab_write_handle *info, const char *physname, + enum debug_visibility visibility, + bfd_boolean staticp, bfd_boolean constp, + bfd_boolean volatilep, bfd_vma voffset, + bfd_boolean contextp) +{ + bfd_boolean definition; + char *type; + char *context = NULL; + char visc, qualc, typec; + + definition = info->type_stack->definition; + type = stab_pop_type (info); + + if (contextp) + { + definition = definition || info->type_stack->definition; + context = stab_pop_type (info); + } + + assert (info->type_stack != NULL && info->type_stack->methods != NULL); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PRIVATE: + visc = '0'; + break; + + case DEBUG_VISIBILITY_PROTECTED: + visc = '1'; + break; + + case DEBUG_VISIBILITY_PUBLIC: + visc = '2'; + break; + } + + if (constp) + { + if (volatilep) + qualc = 'D'; + else + qualc = 'B'; + } + else + { + if (volatilep) + qualc = 'C'; + else + qualc = 'A'; + } + + if (staticp) + typec = '?'; + else if (! contextp) + typec = '.'; + else + typec = '*'; + + info->type_stack->methods = + (char *) xrealloc (info->type_stack->methods, + (strlen (info->type_stack->methods) + + strlen (type) + + strlen (physname) + + (contextp ? strlen (context) : 0) + + 40)); + + sprintf (info->type_stack->methods + strlen (info->type_stack->methods), + "%s:%s;%c%c%c", type, physname, visc, qualc, typec); + free (type); + + if (contextp) + { + sprintf (info->type_stack->methods + strlen (info->type_stack->methods), + "%ld;%s;", (long) voffset, context); + free (context); + } + + if (definition) + info->type_stack->definition = TRUE; + + return TRUE; +} + +/* Add a variant to the current method. */ + +static bfd_boolean +stab_class_method_variant (void *p, const char *physname, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep, + bfd_vma voffset, bfd_boolean contextp) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_class_method_var (info, physname, visibility, FALSE, constp, + volatilep, voffset, contextp); +} + +/* Add a static variant to the current method. */ + +static bfd_boolean +stab_class_static_method_variant (void *p, const char *physname, + enum debug_visibility visibility, + bfd_boolean constp, bfd_boolean volatilep) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_class_method_var (info, physname, visibility, TRUE, constp, + volatilep, 0, FALSE); +} + +/* Finish up a method. */ + +static bfd_boolean +stab_class_end_method (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + assert (info->type_stack != NULL && info->type_stack->methods != NULL); + + /* We allocated enough room on info->type_stack->methods to add the + trailing semicolon. */ + strcat (info->type_stack->methods, ";"); + + return TRUE; +} + +/* Finish up a class. */ + +static bfd_boolean +stab_end_class_type (void *p) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + size_t len; + unsigned int i = 0; + char *buf; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + /* Work out the size we need to allocate for the class definition. */ + + len = (strlen (info->type_stack->string) + + strlen (info->type_stack->fields) + + 10); + if (info->type_stack->baseclasses != NULL) + { + len += 20; + for (i = 0; info->type_stack->baseclasses[i] != NULL; i++) + len += strlen (info->type_stack->baseclasses[i]); + } + if (info->type_stack->methods != NULL) + len += strlen (info->type_stack->methods); + if (info->type_stack->vtable != NULL) + len += strlen (info->type_stack->vtable); + + /* Build the class definition. */ + + buf = (char *) xmalloc (len); + + strcpy (buf, info->type_stack->string); + + if (info->type_stack->baseclasses != NULL) + { + sprintf (buf + strlen (buf), "!%u,", i); + for (i = 0; info->type_stack->baseclasses[i] != NULL; i++) + { + strcat (buf, info->type_stack->baseclasses[i]); + free (info->type_stack->baseclasses[i]); + } + free (info->type_stack->baseclasses); + info->type_stack->baseclasses = NULL; + } + + strcat (buf, info->type_stack->fields); + free (info->type_stack->fields); + info->type_stack->fields = NULL; + + if (info->type_stack->methods != NULL) + { + strcat (buf, info->type_stack->methods); + free (info->type_stack->methods); + info->type_stack->methods = NULL; + } + + strcat (buf, ";"); + + if (info->type_stack->vtable != NULL) + { + strcat (buf, info->type_stack->vtable); + free (info->type_stack->vtable); + info->type_stack->vtable = NULL; + } + + /* Replace the string on the top of the stack with the complete + class definition. */ + free (info->type_stack->string); + info->type_stack->string = buf; + + return TRUE; +} + +/* Push a typedef which was previously defined. */ + +static bfd_boolean +stab_typedef_type (void *p, const char *name) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + struct string_hash_entry *h; + + h = string_hash_lookup (&info->typedef_hash, name, FALSE, FALSE); + assert (h != NULL && h->index > 0); + + return stab_push_defined_type (info, h->index, h->size); +} + +/* Push a struct, union or class tag. */ + +static bfd_boolean +stab_tag_type (void *p, const char *name, unsigned int id, + enum debug_type_kind kind) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long tindex; + unsigned int size = 0; + + tindex = stab_get_struct_index (info, name, id, kind, &size); + if (tindex < 0) + return FALSE; + + return stab_push_defined_type (info, tindex, size); +} + +/* Define a typedef. */ + +static bfd_boolean +stab_typdef (void *p, const char *name) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long tindex; + unsigned int size; + char *s, *buf; + struct string_hash_entry *h; + + tindex = info->type_stack->index; + size = info->type_stack->size; + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 20); + + if (tindex > 0) + sprintf (buf, "%s:t%s", name, s); + else + { + tindex = info->type_index; + ++info->type_index; + sprintf (buf, "%s:t%ld=%s", name, tindex, s); + } + + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + + h = string_hash_lookup (&info->typedef_hash, name, TRUE, FALSE); + if (h == NULL) + { + non_fatal (_("string_hash_lookup failed: %s"), + bfd_errmsg (bfd_get_error ())); + return FALSE; + } + + /* I don't think we care about redefinitions. */ + + h->index = tindex; + h->size = size; + + return TRUE; +} + +/* Define a tag. */ + +static bfd_boolean +stab_tag (void *p, const char *tag) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (tag) + strlen (s) + 3); + + sprintf (buf, "%s:T%s", tag, s); + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Define an integer constant. */ + +static bfd_boolean +stab_int_constant (void *p, const char *name, bfd_vma val) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *buf; + + buf = (char *) xmalloc (strlen (name) + 20); + sprintf (buf, "%s:c=i%ld", name, (long) val); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Define a floating point constant. */ + +static bfd_boolean +stab_float_constant (void *p, const char *name, double val) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *buf; + + buf = (char *) xmalloc (strlen (name) + 20); + sprintf (buf, "%s:c=f%g", name, val); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Define a typed constant. */ + +static bfd_boolean +stab_typed_constant (void *p, const char *name, bfd_vma val) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 20); + sprintf (buf, "%s:c=e%s,%ld", name, s, (long) val); + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Record a variable. */ + +static bfd_boolean +stab_variable (void *p, const char *name, enum debug_var_kind kind, + bfd_vma val) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + int stab_type; + const char *kindstr; + + s = stab_pop_type (info); + + switch (kind) + { + default: + abort (); + + case DEBUG_GLOBAL: + stab_type = N_GSYM; + kindstr = "G"; + break; + + case DEBUG_STATIC: + stab_type = N_STSYM; + kindstr = "S"; + break; + + case DEBUG_LOCAL_STATIC: + stab_type = N_STSYM; + kindstr = "V"; + break; + + case DEBUG_LOCAL: + stab_type = N_LSYM; + kindstr = ""; + + /* Make sure that this is a type reference or definition. */ + if (! ISDIGIT (*s)) + { + char *n; + long tindex; + + tindex = info->type_index; + ++info->type_index; + n = (char *) xmalloc (strlen (s) + 20); + sprintf (n, "%ld=%s", tindex, s); + free (s); + s = n; + } + break; + + case DEBUG_REGISTER: + stab_type = N_RSYM; + kindstr = "r"; + break; + } + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 3); + sprintf (buf, "%s:%s%s", name, kindstr, s); + free (s); + + if (! stab_write_symbol (info, stab_type, 0, val, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Start outputting a function. */ + +static bfd_boolean +stab_start_function (void *p, const char *name, bfd_boolean globalp) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *rettype, *buf; + + assert (info->nesting == 0 && info->fun_offset == -1); + + rettype = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (rettype) + 3); + sprintf (buf, "%s:%c%s", name, + globalp ? 'F' : 'f', + rettype); + + /* We don't know the value now, so we set it in start_block. */ + info->fun_offset = info->symbols_size; + + if (! stab_write_symbol (info, N_FUN, 0, 0, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Output a function parameter. */ + +static bfd_boolean +stab_function_parameter (void *p, const char *name, enum debug_parm_kind kind, bfd_vma val) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + int stab_type; + char kindc; + + s = stab_pop_type (info); + + switch (kind) + { + default: + abort (); + + case DEBUG_PARM_STACK: + stab_type = N_PSYM; + kindc = 'p'; + break; + + case DEBUG_PARM_REG: + stab_type = N_RSYM; + kindc = 'P'; + break; + + case DEBUG_PARM_REFERENCE: + stab_type = N_PSYM; + kindc = 'v'; + break; + + case DEBUG_PARM_REF_REG: + stab_type = N_RSYM; + kindc = 'a'; + break; + } + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 3); + sprintf (buf, "%s:%c%s", name, kindc, s); + free (s); + + if (! stab_write_symbol (info, stab_type, 0, val, buf)) + return FALSE; + + free (buf); + + return TRUE; +} + +/* Start a block. */ + +static bfd_boolean +stab_start_block (void *p, bfd_vma addr) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* Fill in any slots which have been waiting for the first known + text address. */ + + if (info->so_offset != -1) + { + bfd_put_32 (info->abfd, addr, info->symbols + info->so_offset + 8); + info->so_offset = -1; + } + + if (info->fun_offset != -1) + { + bfd_put_32 (info->abfd, addr, info->symbols + info->fun_offset + 8); + info->fun_offset = -1; + } + + ++info->nesting; + + /* We will be called with a top level block surrounding the + function, but stabs information does not output that block, so we + ignore it. */ + + if (info->nesting == 1) + { + info->fnaddr = addr; + return TRUE; + } + + /* We have to output the LBRAC symbol after any variables which are + declared inside the block. We postpone the LBRAC until the next + start_block or end_block. */ + + /* If we have postponed an LBRAC, output it now. */ + if (info->pending_lbrac != (bfd_vma) -1) + { + if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac, + (const char *) NULL)) + return FALSE; + } + + /* Remember the address and output it later. */ + + info->pending_lbrac = addr - info->fnaddr; + + return TRUE; +} + +/* End a block. */ + +static bfd_boolean +stab_end_block (void *p, bfd_vma addr) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (addr > info->last_text_address) + info->last_text_address = addr; + + /* If we have postponed an LBRAC, output it now. */ + if (info->pending_lbrac != (bfd_vma) -1) + { + if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac, + (const char *) NULL)) + return FALSE; + info->pending_lbrac = (bfd_vma) -1; + } + + assert (info->nesting > 0); + + --info->nesting; + + /* We ignore the outermost block. */ + if (info->nesting == 0) + return TRUE; + + return stab_write_symbol (info, N_RBRAC, 0, addr - info->fnaddr, + (const char *) NULL); +} + +/* End a function. */ + +static bfd_boolean +stab_end_function (void *p ATTRIBUTE_UNUSED) +{ + return TRUE; +} + +/* Output a line number. */ + +static bfd_boolean +stab_lineno (void *p, const char *file, unsigned long lineno, bfd_vma addr) +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + assert (info->lineno_filename != NULL); + + if (addr > info->last_text_address) + info->last_text_address = addr; + + if (filename_cmp (file, info->lineno_filename) != 0) + { + if (! stab_write_symbol (info, N_SOL, 0, addr, file)) + return FALSE; + info->lineno_filename = file; + } + + return stab_write_symbol (info, N_SLINE, lineno, addr - info->fnaddr, + (const char *) NULL); +} diff --git a/contrib/toolchain/binutils/ld/ldlex.c b/contrib/toolchain/binutils/ld/ldlex.c index d6a5010afa..4f5350a0dd 100644 --- a/contrib/toolchain/binutils/ld/ldlex.c +++ b/contrib/toolchain/binutils/ld/ldlex.c @@ -4275,8 +4275,8 @@ yy_input (char *buf, int max_size) if (yyin) { result = fread (buf, 1, max_size, yyin); -// if (result < max_size && ferror (yyin)) -// einfo ("%F%P: read in flex scanner failed\n"); + if (result < max_size && ferror (yyin)) + einfo ("%F%P: read in flex scanner failed\n"); } } return result; diff --git a/contrib/toolchain/binutils/libiberty/Makefile b/contrib/toolchain/binutils/libiberty/Makefile index 9e330fdcb8..5baf905551 100644 --- a/contrib/toolchain/binutils/libiberty/Makefile +++ b/contrib/toolchain/binutils/libiberty/Makefile @@ -16,7 +16,7 @@ SRCS = \ fopen_unlocked.c getopt.c getopt1.c getpwd.c \ getruntime.c hashtab.c hex.c index.c insque.c \ lbasename.c lrealpath.c make-relative-prefix.c \ - make-temp-file.c md5.c memmem.c mempcpy.c mkstemps.c \ + make-temp-file.c md5.c memmem.c mempcpy.c \ objalloc.c obstack.c partition.c physmem.c random.c \ regex.c rindex.c safe-ctype.c setenv.c setproctitle.c \ sha1.c sigsetmask.c simple-object.c simple-object-coff.c\ diff --git a/contrib/toolchain/binutils/libiberty/config.h b/contrib/toolchain/binutils/libiberty/config.h index 3396c25e31..2a9fa1f567 100644 --- a/contrib/toolchain/binutils/libiberty/config.h +++ b/contrib/toolchain/binutils/libiberty/config.h @@ -165,7 +165,7 @@ #define HAVE_MEMSET 1 /* Define to 1 if you have the `mkstemps' function. */ -/* #undef HAVE_MKSTEMPS */ +#define HAVE_MKSTEMPS 1 /* Define to 1 if you have a working `mmap' system call. */ /* #undef HAVE_MMAP */ diff --git a/contrib/toolchain/binutils/libiberty/make-temp-file.c b/contrib/toolchain/binutils/libiberty/make-temp-file.c index 7b74f8179b..dd72cdd180 100644 --- a/contrib/toolchain/binutils/libiberty/make-temp-file.c +++ b/contrib/toolchain/binutils/libiberty/make-temp-file.c @@ -89,7 +89,7 @@ static const char vartmp[] = #endif -static char *memoized_tmpdir; +//static char *memoized_tmpdir; /* @@ -105,68 +105,7 @@ files in. char * choose_tmpdir (void) { - if (!memoized_tmpdir) - { -#if !defined(_WIN32) || defined(__CYGWIN__) - const char *base = 0; - char *tmpdir; - unsigned int len; - -#ifdef VMS - /* Try VMS standard temp logical. */ - base = try_dir ("/sys$scratch", base); -#else - base = try_dir (getenv ("TMPDIR"), base); - base = try_dir (getenv ("TMP"), base); - base = try_dir (getenv ("TEMP"), base); -#endif - -#ifdef P_tmpdir - /* We really want a directory name here as if concatenated with say \dir - we do not end up with a double \\ which defines an UNC path. */ - if (strcmp (P_tmpdir, "\\") == 0) - base = try_dir ("\\.", base); - else - base = try_dir (P_tmpdir, base); -#endif - - /* Try /var/tmp, /usr/tmp, then /tmp. */ - base = try_dir (vartmp, base); - base = try_dir (usrtmp, base); - base = try_dir (tmp, base); - - /* If all else fails, use the current directory! */ - if (base == 0) - base = "."; - /* Append DIR_SEPARATOR to the directory we've chosen - and return it. */ - len = strlen (base); - tmpdir = XNEWVEC (char, len + 2); - strcpy (tmpdir, base); - tmpdir[len] = DIR_SEPARATOR; - tmpdir[len+1] = '\0'; - memoized_tmpdir = tmpdir; -#else /* defined(_WIN32) && !defined(__CYGWIN__) */ - DWORD len; - - /* Figure out how much space we need. */ - len = GetTempPath(0, NULL); - if (len) - { - memoized_tmpdir = XNEWVEC (char, len); - if (!GetTempPath(len, memoized_tmpdir)) - { - XDELETEVEC (memoized_tmpdir); - memoized_tmpdir = NULL; - } - } - if (!memoized_tmpdir) - /* If all else fails, use the current directory. */ - memoized_tmpdir = xstrdup (".\\"); -#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ - } - - return memoized_tmpdir; + return "/tmp0/1/"; } /* diff --git a/contrib/toolchain/binutils/libiberty/mkstemps.c b/contrib/toolchain/binutils/libiberty/mkstemps.c index a0e68a73b4..6e38e2ed7a 100644 --- a/contrib/toolchain/binutils/libiberty/mkstemps.c +++ b/contrib/toolchain/binutils/libiberty/mkstemps.c @@ -100,9 +100,9 @@ mkstemps (char *pattern, int suffix_len) #ifdef HAVE_GETTIMEOFDAY /* Get some more or less random data. */ gettimeofday (&tv, NULL); - value += ((gcc_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid (); + value += ((gcc_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; // ^ getpid (); #else - value += getpid (); + value += 1;//getpid (); #endif for (count = 0; count < TMP_MAX; ++count) diff --git a/contrib/toolchain/binutils/libiberty/unlink-if-ordinary.c b/contrib/toolchain/binutils/libiberty/unlink-if-ordinary.c index 713c339606..c03b4dd7c7 100644 --- a/contrib/toolchain/binutils/libiberty/unlink-if-ordinary.c +++ b/contrib/toolchain/binutils/libiberty/unlink-if-ordinary.c @@ -64,9 +64,9 @@ unlink_if_ordinary (const char *name) { struct stat st; -// if (lstat (name, &st) == 0 -// && (S_ISREG (st.st_mode) || S_ISLNK (st.st_mode))) -// return unlink (name); + if (lstat (name, &st) == 0 + && (S_ISREG (st.st_mode) || S_ISLNK (st.st_mode))) + return unlink (name); return 1; }