/contrib/toolchain/binutils/Makefile |
---|
15,7 → 15,7 |
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 |
/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 $@ $< |
/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 <time.h> /* ctime, maybe time_t */ |
#include <assert.h> |
#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; |
} |
/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 */ |
/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 <ian@cygnus.com>. |
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 |
/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 <alloca.h> 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 <unistd.h> 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 <dlfcn.h> 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 <fcntl.h> header file. */ |
#define HAVE_FCNTL_H 1 |
/* Define to 1 if you have the `getc_unlocked' function. */ |
/* #undef HAVE_GETC_UNLOCKED */ |
/* Does <utime.h> 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 <inttypes.h> header file. */ |
#define HAVE_INTTYPES_H 1 |
/* Define if your <locale.h> file defines LC_MESSAGES. */ |
/* #undef HAVE_LC_MESSAGES */ |
/* Define to 1 if you have the <limits.h> header file. */ |
#define HAVE_LIMITS_H 1 |
/* Define to 1 if you have the <locale.h> 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 <memory.h> 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 <stdint.h> header file. */ |
#define HAVE_STDINT_H 1 |
/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */ |
#define HAVE_STRINGS_H 1 |
/* Define to 1 if you have the <string.h> header file. */ |
#define HAVE_STRING_H 1 |
/* Define to 1 if you have the <sys/file.h> header file. */ |
#define HAVE_SYS_FILE_H 1 |
/* Define to 1 if you have the <sys/param.h> header file. */ |
#define HAVE_SYS_PARAM_H 1 |
/* Define to 1 if you have the <sys/stat.h> header file. */ |
#define HAVE_SYS_STAT_H 1 |
/* Define to 1 if you have the <sys/types.h> header file. */ |
#define HAVE_SYS_TYPES_H 1 |
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ |
/* #undef HAVE_SYS_WAIT_H */ |
/* Is the type time_t defined in <time.h>? */ |
#define HAVE_TIME_T_IN_TIME_H 1 |
/* Is the type time_t defined in <sys/types.h>? */ |
#define HAVE_TIME_T_IN_TYPES_H 1 |
/* Define to 1 if you have the <unistd.h> 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 <wchar.h> header file. */ |
//#define HAVE_WCHAR_H 1 |
/* Define to 1 if you have the <zlib.h> 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 <string.h> and <strings.h>. */ |
#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 */ |
/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 <ian@cygnus.com>. |
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 <assert.h> |
#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; |
} |
/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 <ian@cygnus.com>. |
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 */ |
/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 |
} |
/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 <ian@cygnus.com>. |
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 <assert.h> |
#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; |
} |
/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; |
/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 <reverse_bytes> 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 <bfdname> Assume input file is in format <bfdname>\n\ |
-O --output-target <bfdname> Create an output file in format <bfdname>\n\ |
-B --binary-architecture <arch> Set output arch, when input is arch-less\n\ |
-F --target <bfdname> Set both input and output format to <bfdname>\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 <name> Only copy section <name> into the output\n\ |
--add-gnu-debuglink=<file> Add section .gnu_debuglink linking to <file>\n\ |
-R --remove-section <name> Remove section <name> 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 <name> Do not copy symbol <name>\n\ |
--strip-unneeded-symbol <name>\n\ |
Do not copy symbol <name> 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 <name> Do not strip symbol <name>\n\ |
--keep-file-symbols Do not strip file symbol(s)\n\ |
--localize-hidden Turn all ELF hidden symbols into locals\n\ |
-L --localize-symbol <name> Force symbol <name> to be marked as a local\n\ |
--globalize-symbol <name> Force symbol <name> to be marked as a global\n\ |
-G --keep-global-symbol <name> Localize all symbols except <name>\n\ |
-W --weaken-symbol <name> Force symbol <name> 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 [<number>] Only copy N out of every <number> bytes\n\ |
--interleave-width <number> Set N for --interleave\n\ |
-b --byte <num> Select byte <num> in every interleaved block\n\ |
--gap-fill <val> Fill gaps between sections with <val>\n\ |
--pad-to <addr> Pad the last section up to address <addr>\n\ |
--set-start <addr> Set the start address to <addr>\n\ |
{--change-start|--adjust-start} <incr>\n\ |
Add <incr> to the start address\n\ |
{--change-addresses|--adjust-vma} <incr>\n\ |
Add <incr> to LMA, VMA and start addresses\n\ |
{--change-section-address|--adjust-section-vma} <name>{=|+|-}<val>\n\ |
Change LMA and VMA of section <name> by <val>\n\ |
--change-section-lma <name>{=|+|-}<val>\n\ |
Change the LMA of section <name> by <val>\n\ |
--change-section-vma <name>{=|+|-}<val>\n\ |
Change the VMA of section <name> by <val>\n\ |
{--[no-]change-warnings|--[no-]adjust-warnings}\n\ |
Warn if a named section does not exist\n\ |
--set-section-flags <name>=<flags>\n\ |
Set section <name>'s properties to <flags>\n\ |
--add-section <name>=<file> Add section <name> found in <file> to output\n\ |
--rename-section <old>=<new>[,<flags>] Rename section <old> to <new>\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=<num> Reverse <num> bytes at a time, in output sections with content\n\ |
--redefine-sym <old>=<new> Redefine symbol name <old> to <new>\n\ |
--redefine-syms <file> --redefine-sym for all symbol pairs \n\ |
listed in <file>\n\ |
--srec-len <number> Restrict the length of generated Srecords\n\ |
--srec-forceS3 Restrict the type of generated Srecords to S3\n\ |
--strip-symbols <file> -N for all symbols listed in <file>\n\ |
--strip-unneeded-symbols <file>\n\ |
--strip-unneeded-symbol for all symbols listed\n\ |
in <file>\n\ |
--keep-symbols <file> -K for all symbols listed in <file>\n\ |
--localize-symbols <file> -L for all symbols listed in <file>\n\ |
--globalize-symbols <file> --globalize-symbol for all in <file>\n\ |
--keep-global-symbols <file> -G for all symbols listed in <file>\n\ |
--weaken-symbols <file> -W for all symbols listed in <file>\n\ |
--alt-machine-code <index> Use the target's <index>'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 <prefix> Add <prefix> to start of every symbol name\n\ |
--prefix-sections <prefix> Add <prefix> to start of every section name\n\ |
--prefix-alloc-sections <prefix>\n\ |
Add <prefix> to start of every allocatable\n\ |
section name\n\ |
--file-alignment <num> Set PE file alignment to <num>\n\ |
--heap <reserve>[,<commit>] Set PE reserve/commit heap to <reserve>/\n\ |
<commit>\n\ |
--image-base <address> Set PE image base to <address>\n\ |
--section-alignment <num> Set PE section alignment to <num>\n\ |
--stack <reserve>[,<commit>] Set PE reserve/commit stack to <reserve>/\n\ |
<commit>\n\ |
--subsystem <name>[:<version>]\n\ |
Set PE subsystem to <name> [& <version>]\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\ |
@<file> Read options from <file>\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 <option(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=<bfdname> Assume input file is in format <bfdname>\n\ |
-O --output-target=<bfdname> Create an output file in format <bfdname>\n\ |
-F --target=<bfdname> Set both input and output format to <bfdname>\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=<name> Remove section <name> 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=<name> Do not copy symbol <name>\n\ |
-K --keep-symbol=<name> Do not strip symbol <name>\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 <file> Place stripped output into <file>\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; |
} |
/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 <ian@cygnus.com>. |
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; |
} |
/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 <ian@cygnus.com>. |
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; |
} |
/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 <utime.h> |
#else /* ! HAVE_GOOD_UTIME_H */ |
#ifdef HAVE_UTIMES |
#include <sys/time.h> |
#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; |
} |
/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 <ian@cygnus.com>. |
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> <width>; <offset>; <nbits> |
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 "<no value>" |
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<index type>;lower;upper;<array_contents_type>". |
OS9000: "arlower,upper;<array_contents_type>". |
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; |
} |
} |
} |
/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 <stdio.h> |
#include <sys/types.h> |
#include "bfdver.h" |
#include <stdarg.h> |
#ifdef USE_BINARY_FOPEN |
#include "fopen-bin.h" |
#else |
#include "fopen-same.h" |
#endif |
#include <errno.h> |
#ifndef errno |
extern int errno; |
#endif |
#ifdef HAVE_UNISTD_H |
#include <unistd.h> |
#endif |
#ifdef STRING_WITH_STRINGS |
#include <string.h> |
#include <strings.h> |
#else |
#ifdef HAVE_STRING_H |
#include <string.h> |
#else |
#ifdef HAVE_STRINGS_H |
#include <strings.h> |
#else |
extern char *strchr (); |
extern char *strrchr (); |
#endif |
#endif |
#endif |
#ifdef HAVE_STDLIB_H |
#include <stdlib.h> |
#endif |
#ifdef HAVE_FCNTL_H |
#include <fcntl.h> |
#else |
#ifdef HAVE_SYS_FILE_H |
#include <sys/file.h> |
#endif |
#endif |
#ifdef HAVE_SYS_STAT_H |
#include <sys/stat.h> |
#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 <locale.h> |
#endif |
#ifdef ENABLE_NLS |
# include <libintl.h> |
# 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 <limits.h> |
#endif |
#ifndef PATH_MAX |
/* For MAXPATHLEN. */ |
# ifdef HAVE_SYS_PARAM_H |
# include <sys/param.h> |
# endif |
# ifndef PATH_MAX |
# ifdef MAXPATHLEN |
# define PATH_MAX MAXPATHLEN |
# else |
# define PATH_MAX 1024 |
# endif |
# endif |
#endif |
#endif /* _BIN_SYSDEP_H */ |
/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); |
} |
/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 <ian@cygnus.com>. |
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 <assert.h> |
#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); |
} |
/contrib/toolchain/binutils/ld/ldlex.c |
---|
4275,8 → 4275,8 |
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; |
/contrib/toolchain/binutils/libiberty/Makefile |
---|
16,7 → 16,7 |
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\ |
/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 */ |
/contrib/toolchain/binutils/libiberty/make-temp-file.c |
---|
89,7 → 89,7 |
#endif |
static char *memoized_tmpdir; |
//static char *memoized_tmpdir; |
/* |
105,70 → 105,9 |
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; |
return "/tmp0/1/"; |
} |
} |
if (!memoized_tmpdir) |
/* If all else fails, use the current directory. */ |
memoized_tmpdir = xstrdup (".\\"); |
#endif /* defined(_WIN32) && !defined(__CYGWIN__) */ |
} |
return memoized_tmpdir; |
} |
/* |
@deftypefn Replacement char* make_temp_file (const char *@var{suffix}) |
/contrib/toolchain/binutils/libiberty/mkstemps.c |
---|
100,9 → 100,9 |
#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) |
/contrib/toolchain/binutils/libiberty/unlink-if-ordinary.c |
---|
64,9 → 64,9 |
{ |
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; |
} |