$OpenBSD: patch-binutils_wrcoff_c,v 1.3 2010/06/27 20:58:10 ckuethe Exp $ --- binutils/wrcoff.c.orig Sat Jun 26 11:31:17 2010 +++ binutils/wrcoff.c Sat Jun 26 11:31:17 2010 @@ -0,0 +1,3410 @@ +/* wrcoff.c -- Generate (AVR) COFF debugging information + Copyright 2003 Free Software Foundation, Inc. + + Written by Joerg Wunsch. + + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file contains code which writes out COFF debugging + information. By now, this has only been tested on the AVR + platform, though any attempt has been made to keep the conversion + applicable to possible other COFF debugging consumers as well. */ + +#include +#include + +#include "sysdep.h" +#include "bfd.h" +#include "coff/internal.h" +#include "bucomm.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "debug.h" +#include "budbg.h" + +/* Enabling COFF_DEBUG will trace the internal callback functions and + their parameters as debug_write() calls them. */ +//#define COFF_DEBUG 1 + +#include "libcoff.h" + +#define N_TMASK (coff_data (info->abfd)->local_n_tmask) +#define N_BTSHFT (coff_data (info->abfd)->local_n_btshft) +#define N_BTMASK (coff_data (info->abfd)->local_n_btmask) +#define N_TSHIFT (coff_data (info->abfd)->local_n_tshift) + +/* Structure of local symbols per compilation unit. */ +struct coff_compilation_unit +{ + const char *fname; + asymbol **syms; + long nsyms, totsyms; +}; + +enum ts_kind +{ + TS_EMPTY, + TS_VOID, + TS_INT, + TS_FLOAT, + TS_COMPLEX, + TS_ENUM, + TS_POINTER, + TS_FUNC, + TS_ARRAY, + TS_STRUCT, + TS_NONE = -1 +}; + +/* Structure defining the pre-defined types. */ +struct coff_predef_type +{ + enum ts_kind kind; + unsigned int size; /* in bytes */ + bfd_boolean isunsigned; + int slot; +}; + +struct coff_type_stack; +struct coff_hash_entry; + +struct coff_struct_fields +{ + const char *name; + bfd_vma bitpos; + bfd_vma bitsize; + enum debug_visibility visibility; + struct coff_type_stack *types; +}; + +/* Our type stack. */ +struct coff_type_stack +{ + struct coff_type_stack *next; + enum ts_kind tsk; + union + { + /* TS_INT */ + struct + { + unsigned int size; + bfd_boolean isunsigned; + } + ts_int; + + /* TS_FLOAT */ + struct + { + unsigned int size; + } + ts_float; + + /* TS_ENUM */ + struct + { + union + { + const char *fixtag; + char *malloctag; + } + tag; + bfd_boolean tagismalloced; + const char **names; + bfd_signed_vma *vals; + struct coff_enum_hash_entry *ehash; + } + ts_enum; + + /* TS_FUNC */ + struct + { + struct coff_type_stack *savedts; + } + ts_func; + + /* TS_ARRAY */ + struct + { + bfd_signed_vma low; + bfd_signed_vma high; + } + ts_array; + + /* TS_STRUCT */ + struct + { + union + { + const char *fixtag; + char *malloctag; + } + tag; + bfd_boolean tagismalloced; + unsigned int id; + bfd_boolean isstruct; + unsigned int size; + long nfields; + struct coff_struct_fields *fields; + struct coff_type_stack *savedts; + struct coff_struct_hash_entry *shash; + } + ts_struct; + } + u; +}; + +struct coff_name_type_hash_table +{ + struct bfd_hash_table root; +}; + +struct coff_name_type_hash_entry +{ + struct bfd_hash_entry root; + /* Information for this name. */ + struct coff_type_stack *types; + bfd_boolean emitted; +}; + +struct coff_struct_hash_table +{ + struct bfd_hash_table root; +}; + +struct coff_struct_hash_entry +{ + struct bfd_hash_entry root; + /* Information for this name. */ + struct coff_type_stack *types; + bfd_boolean emitted; + combined_entry_type *native; + /* list of symbol indices that need fixing */ + long *fixidxs; + unsigned nfixidxs; +}; + +struct coff_enum_hash_table +{ + struct bfd_hash_table root; +}; + +struct coff_enum_hash_entry +{ + struct bfd_hash_entry root; + /* Information for this name. */ + struct coff_type_stack *types; + bfd_boolean emitted; + combined_entry_type *native; + /* list of symbol indices that need fixing */ + long *fixidxs; + unsigned nfixidxs; +}; + +/* COFF private symbol data. Used as a cookie to pass data around + between various processing stages. The generic COFF handling code + doesn't use any private data. */ +struct coff_private_symdata +{ + unsigned int size; /* size of symbol, used in AVR register + translation */ + struct coff_struct_hash_entry *shash; /* TS_STRUCT hash for fixups */ + struct coff_enum_hash_entry *ehash; /* TS_ENUM hash for fixups */ +}; + +/* Stack of tags that need endndx fixing. */ +struct coff_fix_stack +{ + struct coff_fix_stack *next; + combined_entry_type *native; +}; + +/* This is the handle passed through debug_write. */ + +struct coff_write_handle +{ + /* The BFD. */ + bfd *abfd; + /* Pointers to .text and .data sections, can be used as defaults if + no other information is available. */ + asection *textsect; + asection *datasect; + /* Some special flags. */ + unsigned long flags; + /* Flags describing architecture options. */ +#define COFF_FL_AVR 0x0001 /* COFF is for AVR platform. */ +#define COFF_FL_EXT_AVR 0x0002 /* AVR "extended" COFF */ + /* Flags describing internal status information. */ +#define COFF_FL_FIX_ENDNDX 0x10000 /* apply endndx fix at next symbol */ +#define COFF_FL_START_FCN 0x20000 /* begin of function pending */ +#define COFF_FL_FIX_BB 0x40000 /* fix last ".bb" symbol */ + /* List of our compilation units, from input symbol table. */ + struct coff_compilation_unit *units; + long nunits; + struct coff_compilation_unit *currentfile; + /* Global symbols from input symbol table. */ + asymbol **globals; + long nglobals; + /* Section syms for named sections. */ + coff_symbol_type **secsyms; + long nsecsyms; + /* Our COFF symbols. */ + asymbol **syms; + long nsyms; + /* Total line number count. */ + unsigned long totlnos; + /* Size of standard objects on this arch. */ + unsigned int pointersize; + unsigned int enumsize; + /* Pending information when starting a function. We have to defer + almost everything, some actions can be taken when seeing the + starting block of that function, some will even have to wait + until we see the end of the function. */ + const char *funname; /* name of function */ + bfd_boolean funglobal; /* global/local function? */ + unsigned int lastlno; /* last line number seen so far */ + long funcindex; /* index of ".func" symbol in syms */ + unsigned int nlnos; /* line numbers recorded for this function*/ + bfd_vma endaddr; /* last .eb address we have seen so far */ + unsigned int funlno; /* first line number in function */ + coff_symbol_type **fargs; /* function arguments */ + unsigned int nfargs; + asection *funcsection; /* section the current function is using */ + /* Type information */ + struct coff_type_stack *tstack; + struct coff_name_type_hash_table types; + struct coff_struct_hash_table structs; + struct coff_enum_hash_table enums; + unsigned nenums; /* counter for anonymous enum tags */ + /* Stack of pending endndx fixes, see coff_record_symbol(). */ + struct coff_fix_stack *fixes; +}; + +/* Predefined types, default to usual 32-bit architectures. + Arch-dependant different byte sizes will be tuned upon entering + write_coff_debugging_info(). The table is looked up from front to + end, so we put `more popular' types that might have the same size + as other types first (e. g. "int" precedes "long" and "short"). */ +static struct coff_predef_type coff_predef_types[] = +{ + { TS_INT, 4, FALSE, 4 }, /* signed int */ + { TS_INT, 1, FALSE, 2 }, /* signed char */ + { TS_INT, 2, FALSE, 3 }, /* signed short */ + { TS_INT, 4, FALSE, 5 }, /* long int */ + { TS_FLOAT, 8, FALSE, 7 }, /* double */ + { TS_FLOAT, 4, FALSE, 6 }, /* float */ + { TS_INT, 4, TRUE, 14 }, /* unsigned int */ + { TS_INT, 1, TRUE, 12 }, /* unsigned char */ + { TS_INT, 2, TRUE, 13 }, /* unsigned short */ + { TS_INT, 4, TRUE, 15 }, /* unsigned long */ +}; + +static bfd_boolean coff_copy_symbols + PARAMS ((struct coff_write_handle *, long, asymbol **)); +static asymbol *coff_find_symbol + PARAMS ((struct coff_write_handle *, const char *, bfd_boolean, bfd_boolean)); +static void coff_record_symbol + PARAMS ((struct coff_write_handle *, coff_symbol_type *)); +static symvalue coff_fixup_avr_register PARAMS ((symvalue, int)); +static struct bfd_hash_entry *coff_name_type_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static bfd_boolean coff_free_type_info + PARAMS ((struct coff_name_type_hash_entry *, PTR)); +static struct bfd_hash_entry *coff_struct_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static bfd_boolean coff_free_struct_info + PARAMS ((struct coff_struct_hash_entry *, PTR)); +static struct bfd_hash_entry *coff_enum_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static bfd_boolean coff_free_enum_info + PARAMS ((struct coff_enum_hash_entry *, PTR)); +static unsigned int coff_get_fundamental_type + PARAMS ((struct coff_write_handle *, struct coff_type_stack *)); +static bfd_boolean coff_make_typed_symbol + PARAMS ((struct coff_write_handle *, coff_symbol_type **, enum ts_kind)); +static bfd_boolean coff_emit_struct + PARAMS ((struct coff_write_handle *, struct coff_type_stack *, + struct coff_struct_hash_entry *)); +static bfd_boolean coff_emit_enum + PARAMS ((struct coff_write_handle *, struct coff_type_stack *, + struct coff_enum_hash_entry *)); +static bfd_boolean coff_emit_ndebug_sym + PARAMS ((struct coff_write_handle *, asymbol *, bfd_boolean)); + +static bfd_boolean coff_start_compilation_unit PARAMS ((PTR, const char *)); +static bfd_boolean coff_start_source PARAMS ((PTR, const char *)); +static bfd_boolean coff_empty_type PARAMS ((PTR)); +static bfd_boolean coff_void_type PARAMS ((PTR)); +static bfd_boolean coff_int_type PARAMS ((PTR, unsigned int, bfd_boolean)); +static bfd_boolean coff_float_type PARAMS ((PTR, unsigned int)); +static bfd_boolean coff_complex_type PARAMS ((PTR, unsigned int)); +static bfd_boolean coff_bool_type PARAMS ((PTR, unsigned int)); +static bfd_boolean coff_enum_type + PARAMS ((PTR, const char *, const char **, bfd_signed_vma *)); +static bfd_boolean coff_pointer_type PARAMS ((PTR)); +static bfd_boolean coff_function_type PARAMS ((PTR, int, bfd_boolean)); +static bfd_boolean coff_reference_type PARAMS ((PTR)); +static bfd_boolean coff_range_type PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma)); +static bfd_boolean coff_array_type + PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma, bfd_boolean)); +static bfd_boolean coff_set_type PARAMS ((PTR, bfd_boolean)); +static bfd_boolean coff_offset_type PARAMS ((PTR)); +static bfd_boolean coff_method_type PARAMS ((PTR, bfd_boolean, int, bfd_boolean)); +static bfd_boolean coff_const_type PARAMS ((PTR)); +static bfd_boolean coff_volatile_type PARAMS ((PTR)); +static bfd_boolean coff_start_struct_type + PARAMS ((PTR, const char *, unsigned int, bfd_boolean, unsigned int)); +static bfd_boolean coff_struct_field + PARAMS ((PTR, const char *, bfd_vma, bfd_vma, enum debug_visibility)); +static bfd_boolean coff_end_struct_type PARAMS ((PTR)); +static bfd_boolean coff_start_class_type + PARAMS ((PTR, const char *, unsigned int, bfd_boolean, unsigned int, bfd_boolean, + bfd_boolean)); +static bfd_boolean coff_class_static_member + PARAMS ((PTR, const char *, const char *, enum debug_visibility)); +static bfd_boolean coff_class_baseclass + PARAMS ((PTR, bfd_vma, bfd_boolean, enum debug_visibility)); +static bfd_boolean coff_class_start_method PARAMS ((PTR, const char *)); +static bfd_boolean coff_class_method_variant + PARAMS ((PTR, const char *, enum debug_visibility, bfd_boolean, bfd_boolean, + bfd_vma, bfd_boolean)); +static bfd_boolean coff_class_static_method_variant + PARAMS ((PTR, const char *, enum debug_visibility, bfd_boolean, bfd_boolean)); +static bfd_boolean coff_class_end_method PARAMS ((PTR)); +static bfd_boolean coff_end_class_type PARAMS ((PTR)); +static bfd_boolean coff_typedef_type PARAMS ((PTR, const char *)); +static bfd_boolean coff_tag_type + PARAMS ((PTR, const char *, unsigned int, enum debug_type_kind)); +static bfd_boolean coff_typdef PARAMS ((PTR, const char *)); +static bfd_boolean coff_tag PARAMS ((PTR, const char *)); +static bfd_boolean coff_int_constant PARAMS ((PTR, const char *, bfd_vma)); +static bfd_boolean coff_float_constant PARAMS ((PTR, const char *, double)); +static bfd_boolean coff_typed_constant PARAMS ((PTR, const char *, bfd_vma)); +static bfd_boolean coff_variable + PARAMS ((PTR, const char *, enum debug_var_kind, bfd_vma)); +static bfd_boolean coff_start_function PARAMS ((PTR, const char *, bfd_boolean)); +static bfd_boolean coff_function_parameter + PARAMS ((PTR, const char *, enum debug_parm_kind, bfd_vma)); +static bfd_boolean coff_start_block PARAMS ((PTR, bfd_vma)); +static bfd_boolean coff_end_block PARAMS ((PTR, bfd_vma)); +static bfd_boolean coff_end_function PARAMS ((PTR)); +static bfd_boolean coff_lineno + PARAMS ((PTR, const char *, unsigned long, bfd_vma)); + +static const struct debug_write_fns coff_fns = +{ + coff_start_compilation_unit, + coff_start_source, + coff_empty_type, + coff_void_type, + coff_int_type, + coff_float_type, + coff_complex_type, + coff_bool_type, + coff_enum_type, + coff_pointer_type, + coff_function_type, + coff_reference_type, + coff_range_type, + coff_array_type, + coff_set_type, + coff_offset_type, + coff_method_type, + coff_const_type, + coff_volatile_type, + coff_start_struct_type, + coff_struct_field, + coff_end_struct_type, + coff_start_class_type, + coff_class_static_member, + coff_class_baseclass, + coff_class_start_method, + coff_class_method_variant, + coff_class_static_method_variant, + coff_class_end_method, + coff_end_class_type, + coff_typedef_type, + coff_tag_type, + coff_typdef, + coff_tag, + coff_int_constant, + coff_float_constant, + coff_typed_constant, + coff_variable, + coff_start_function, + coff_function_parameter, + coff_start_block, + coff_end_block, + coff_end_function, + coff_lineno +}; + +/* + * Copy our input (non-debugging) symbols. Local symbols will be + * maintained in one bucket per each compilation unit, global (and + * weak) symbols will be kept in a simple array. + */ +static bfd_boolean +coff_copy_symbols (info, count, sympp) + struct coff_write_handle *info; + long count; + asymbol **sympp; +{ + asymbol *osym; + long i; + struct coff_compilation_unit *up; + + up = NULL; + + for (i = 0; i < count; i++) + { + osym = sympp[i]; + + /* Try to figure out the .text and .data sections from our input + symbols as we walk them. Unfortunately, this ought to be the + /input/ section pointers, so their ->output_section is + non-NULL. That's why we can't simply walk through all the + sections of our abfd since this is describing the output + only. */ + if (info->textsect == NULL && osym->section->flags & SEC_CODE) + /* Assume this to be our .text section. */ + info->textsect = osym->section; + else if (info->datasect == NULL && osym->section->flags & SEC_DATA) + /* Assume this to be our .data section. */ + info->datasect = osym->section; + + if (osym->flags & BSF_FILE) + { + /* New file name. */ + long l; + + up = NULL; + + /* Well, maybe an old one actually? If so, append it there. + This can happen for files that contribute to multiple + (input) sections that were concatenated by the linker + (like crt1.S). */ + for (l = 0; l < info->nunits; l++) + { + if (strcmp (info->units[l].fname, osym->name) == 0) + { + up = info->units + l; + break; + } + } + + if (up == NULL) + { + info->units = (struct coff_compilation_unit *) + xrealloc (info->units, + ++info->nunits * sizeof(struct coff_compilation_unit)); + up = info->units + (info->nunits - 1); + up->fname = osym->name; + up->syms = NULL; + up->nsyms = up->totsyms = 0; + } + } + else if (osym->flags & (BSF_GLOBAL | BSF_WEAK)) + { + /* Global (or weak) symbols are recorded outside compilation + units. */ + info->globals = (asymbol **) + xrealloc (info->globals, ++info->nglobals * sizeof(asymbol *)); + info->globals[info->nglobals - 1] = osym; + continue; + } + else if (!bfd_is_const_section(osym->section)) + { + if (osym->flags & BSF_SECTION_SYM) + { + coff_symbol_type *csymp; + /* Just record them by now, they'll be fixed up later. */ + + if (info->nsyms == 0 && (info->flags & COFF_FL_AVR) == 0) + { + /* Very first symbol, fake a compilation unit name + for it. Historical precedence seems to dictate + this, but AVR COFF does not use that. */ + csymp = (coff_symbol_type *) + coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = xstrdup (""); + csymp->symbol.value = 0; + csymp->symbol.udata.p = NULL; + csymp->native->u.syment.n_sclass = C_FILE; + /* force filename into aux entry */ + csymp->native->u.syment.n_numaux = 1; + coff_record_symbol (info, csymp); + } + + /* convert to COFF native section symbol */ + csymp = (coff_symbol_type *) + coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = xstrdup (osym->section->name); + csymp->symbol.value = osym->section->output_section->vma; + csymp->symbol.flags = BSF_DEBUGGING | BSF_SECTION_SYM; + csymp->symbol.section = osym->section; + csymp->symbol.udata.p = NULL; + csymp->native->fix_scnlen = 1; + csymp->native->u.syment.n_sclass = C_STAT; + csymp->native->u.syment.n_type = T_NULL; + csymp->native->u.syment.n_numaux = 1; + + coff_record_symbol (info, csymp); + + info->secsyms = (coff_symbol_type **) + xrealloc (info->secsyms, + ++info->nsecsyms * sizeof(coff_symbol_type *)); + info->secsyms[info->nsecsyms - 1] = csymp; + } + else + { + /* Local symbol in a named section, will be recorded + within the respective compilation unit. */ + if (up == NULL) + { + fprintf (stderr, + _("Discarding local symbol outside any compilation unit")); + if (osym->name) + fprintf (stderr, ": %s", osym->name); + putc ('\n', stderr); + } + else + { + up->syms = (asymbol **) + xrealloc (up->syms, ++up->nsyms * sizeof(asymbol *)); + up->syms[up->nsyms - 1] = osym; + up->totsyms = up->nsyms; + continue; + } + } + } + } + + return TRUE; +} + +/* Find a name in the symbol table. If found, the respective entry in + the symbol vector is zeroed, so after processing all debugging + symbols, only non-debugging symbols will remain. */ +static asymbol * +coff_find_symbol (info, name, isfunction, global) + struct coff_write_handle *info; + const char *name; + bfd_boolean isfunction; + bfd_boolean global; +{ + asymbol *symp; + long i; + size_t namelen; + + if (global) + { + for (i = 0; i < info->nglobals; i++) + { + symp = info->globals[i]; + if (symp == NULL) + continue; + if (strcmp (name, symp->name) == 0 + && ((symp->flags & BSF_FUNCTION) != 0) == (isfunction == TRUE)) + { + info->globals[i] = NULL; + return symp; + } + } + return NULL; + } + + if (info->currentfile == NULL) + return NULL; + + /* For local symbols, the match optionally stops at a dot in the + symtab symbol's name; this is used by gcc to indicate + function-scope static symbols (e. g. symbol "foo" will become + "foo.1" in function scope). */ + namelen = strlen (name); + for (i = 0; i < info->currentfile->nsyms; i++) + { + symp = info->currentfile->syms[i]; + if (symp == NULL) + continue; + if (strncmp (name, symp->name, namelen) == 0 + && (symp->name[namelen] == '\0' || symp->name[namelen] == '.') + && ((symp->flags & BSF_FUNCTION) != 0) == (isfunction == TRUE)) + { + info->currentfile->syms[i] = NULL; + info->currentfile->totsyms--; + return symp; + } + } + return NULL; +} + +static void +coff_record_symbol (info, csymp) + struct coff_write_handle *info; + coff_symbol_type *csymp; +{ + struct coff_private_symdata *priv; + + info->syms = (asymbol **) xrealloc (info->syms, + ++info->nsyms * sizeof (asymbol *)); + info->syms[info->nsyms - 1] = (asymbol *)csymp; + + if ((priv = csymp->symbol.udata.p) != NULL) + { + if (priv->shash != NULL) + { + struct coff_struct_hash_entry *shash = priv->shash; + shash->fixidxs = (long *) + xrealloc (shash->fixidxs, ++shash->nfixidxs * sizeof (long)); + shash->fixidxs[shash->nfixidxs - 1] = info->nsyms - 1; + } + if (priv->ehash != NULL) + { + struct coff_enum_hash_entry *ehash = priv->ehash; + ehash->fixidxs = (long *) + xrealloc (ehash->fixidxs, ++ehash->nfixidxs * sizeof (long)); + ehash->fixidxs[ehash->nfixidxs - 1] = info->nsyms - 1; + } + free (priv); + csymp->symbol.udata.p = NULL; + } + + /* If there are any pending endndx fixes, pop the last element from + that stack, and record the current symbol for fixing. We need to + do this here since we need to record our current csymp->native + (where that csymp is completely unrelated to whatever symbol was + previously generated that requested the fixup). The stack of + pending fixes is required since several endndx fixes could be + nested, e. g. the start of a function has a pending fix that + needs to point to the first symbol after the function, but there + could be an anonymous struct definition inside that function's + local variables where the endndx needs to point after the last + symbol of this struct. Also, structs and unions could be nested. + + Each call to coff_record_symbol() can fix at most one endndx + (even if more are pending in the stack), but that's OK. + + Note that bfd/coffgen.c converts that csymp->native into a + symtable slot number after coff_renumber_symbols() has been + run. */ + if (info->flags & COFF_FL_FIX_ENDNDX) + { + struct coff_fix_stack *fsp, *ofsp; + union internal_auxent *aux; + + assert (info->fixes != NULL); + + fsp = info->fixes; + ofsp = NULL; + while (fsp->next != NULL) + { + ofsp = fsp; + fsp = fsp->next; + } + if (ofsp == NULL) + info->fixes = NULL; + else + ofsp->next = NULL; + + aux = &(fsp->native->u.auxent); + fsp->native->fix_end = 1; + aux->x_sym.x_fcnary.x_fcn.x_endndx.p = csymp->native; + free (fsp); + + info->flags &= ~COFF_FL_FIX_ENDNDX; + } +} + +/* Fixup AVR COFF register handling: they don't only mention the + starting register number, but all registers, each within one byte + of the value. Unused register positions are filled up with + 0xff. */ +static symvalue +coff_fixup_avr_register (val, size) + symvalue val; + int size; +{ + union + { + unsigned char c[4]; + symvalue v; + } u; + + u.c[1] = u.c[2] = u.c[3] = 0xff; + u.c[0] = val; + if (size > 8) + u.c[1] = val + 1; + if (size > 16) + { + u.c[2] = val + 2; + u.c[3] = val + 3; + } + + return u.v; +} + +/* Initialize an entry in the hash tables. */ + +static struct bfd_hash_entry * +coff_name_type_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct coff_name_type_hash_entry *ret = + (struct coff_name_type_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + ret = ((struct coff_name_type_hash_entry *) + bfd_hash_allocate (table, sizeof *ret)); + if (ret == NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct coff_name_type_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + if (ret) + { + /* Set local fields. */ + ret->types = NULL; + ret->emitted = FALSE; + } + + return (struct bfd_hash_entry *) ret; +} + +static struct bfd_hash_entry * +coff_struct_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct coff_struct_hash_entry *ret = + (struct coff_struct_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + ret = ((struct coff_struct_hash_entry *) + bfd_hash_allocate (table, sizeof *ret)); + if (ret == NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct coff_struct_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + if (ret) + { + /* Set local fields. */ + ret->types = NULL; + ret->emitted = FALSE; + ret->fixidxs = NULL; + ret->nfixidxs = 0; + ret->native = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +static struct bfd_hash_entry * +coff_enum_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct coff_enum_hash_entry *ret = + (struct coff_enum_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + ret = ((struct coff_enum_hash_entry *) + bfd_hash_allocate (table, sizeof *ret)); + if (ret == NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct coff_enum_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + if (ret) + { + /* Set local fields. */ + ret->types = NULL; + ret->emitted = FALSE; + ret->fixidxs = NULL; + ret->nfixidxs = 0; + ret->native = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Look up an entry in the hash tables. */ + +#define coff_name_type_hash_lookup(table, string, create, copy) \ + ((struct coff_name_type_hash_entry *) \ + bfd_hash_lookup (&(table)->root, (string), (create), (copy))) + +/* Traverse the hash table. */ + +#define coff_name_type_hash_traverse(table, func, info) \ + (bfd_hash_traverse \ + (&(table)->root, \ + (bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func), \ + (info))) + +#define coff_struct_hash_lookup(table, string, create, copy) \ + ((struct coff_struct_hash_entry *) \ + bfd_hash_lookup (&(table)->root, (string), (create), (copy))) + +/* Traverse the hash table. */ + +#define coff_struct_hash_traverse(table, func, info) \ + (bfd_hash_traverse \ + (&(table)->root, \ + (bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func), \ + (info))) + +#define coff_enum_hash_lookup(table, string, create, copy) \ + ((struct coff_enum_hash_entry *) \ + bfd_hash_lookup (&(table)->root, (string), (create), (copy))) + +/* Traverse the hash table. */ + +#define coff_enum_hash_traverse(table, func, info) \ + (bfd_hash_traverse \ + (&(table)->root, \ + (bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) (func), \ + (info))) + +#define coff_push_type(kind) \ + tst = (struct coff_type_stack *) xmalloc (sizeof (struct coff_type_stack)); \ + memset (tst, 0, sizeof (*tst)); \ + tst->next = info->tstack; \ + tst->tsk = kind; \ + info->tstack = tst + +#define coff_pop_type() \ + tst = info->tstack; \ + if (tst == NULL) { \ + fprintf (stderr, _("empty type stack in coff_pop_type()\n")); \ + return FALSE; \ + } \ + info->tstack = tst->next; \ + tst->next = NULL + +#define coff_complain_unsupp(s) \ + fprintf (stderr, _("%s type not supported in %s\n"), \ + s, info->abfd->xvec->name); \ + return FALSE + +/* These function is called via the hash traverse routine when freeing + a hash table (at the end of a translation unit). */ +static bfd_boolean +coff_free_type_info (h, p) + struct coff_name_type_hash_entry *h; + PTR p ATTRIBUTE_UNUSED; +{ + struct coff_type_stack *tst, *otst; + + for (tst = h->types; tst != NULL;) + { + otst = tst; + tst = tst->next; + free (otst); + } + return TRUE; +} + +static bfd_boolean +coff_free_struct_info (h, p) + struct coff_struct_hash_entry *h; + PTR p ATTRIBUTE_UNUSED; +{ + struct coff_type_stack *tst, *otst, *xtst, *xotst; + struct coff_struct_fields *fp; + long i; + + for (tst = h->types; tst != NULL;) + { + otst = tst; + if (tst->u.ts_struct.tagismalloced) + free (tst->u.ts_struct.tag.malloctag); + for (i = 0, fp = tst->u.ts_struct.fields; + i < tst->u.ts_struct.nfields; + i++, fp++) + { + xtst = fp->types; + while (xtst != NULL) + { + xotst = xtst->next; + free (xtst); + xtst = xotst; + } + } + free (tst->u.ts_struct.fields); + tst = tst->next; + free (otst); + } + return TRUE; +} + +static bfd_boolean +coff_free_enum_info (h, p) + struct coff_enum_hash_entry *h; + PTR p ATTRIBUTE_UNUSED; +{ + struct coff_type_stack *tst, *otst; + + for (tst = h->types; tst != NULL;) + { + otst = tst; + if (tst->u.ts_enum.tagismalloced) + free (tst->u.ts_enum.tag.malloctag); + tst = tst->next; + free (otst); + } + return TRUE; +} + +static unsigned int +coff_get_fundamental_type (info, tst) + struct coff_write_handle *info ATTRIBUTE_UNUSED; + struct coff_type_stack *tst; +{ + size_t i; + + /* See if one of our predefined types will fit. */ + if (tst->tsk == TS_INT) + { + for (i = 0; + i < sizeof coff_predef_types / sizeof (struct coff_predef_type); + i++) + { + if (coff_predef_types[i].kind == TS_INT + && coff_predef_types[i].size == tst->u.ts_int.size + && coff_predef_types[i].isunsigned == tst->u.ts_int.isunsigned) + return coff_predef_types[i].slot; + } + fprintf (stderr, + _("%ssigned %d-bit integer type not available in COFF\n"), + tst->u.ts_int.isunsigned? "un": "", tst->u.ts_int.size * 8); + } + else + { + for (i = 0; + i < sizeof coff_predef_types / sizeof (struct coff_predef_type); + i++) + { + if (coff_predef_types[i].kind == TS_FLOAT + && coff_predef_types[i].size == tst->u.ts_float.size) + return coff_predef_types[i].slot; + } + fprintf (stderr, _("%d-bit float type not available in COFF\n"), + tst->u.ts_float.size * 8); + } + + return T_NULL; +} + +static bfd_boolean +coff_make_typed_symbol (info, csympp, stopat) + struct coff_write_handle *info; + coff_symbol_type **csympp; + enum ts_kind stopat; +{ + struct coff_type_stack *tst; + union internal_auxent *aux; + struct coff_struct_hash_entry *shash; + struct coff_enum_hash_entry *ehash; + struct coff_private_symdata *priv; + unsigned int type, numaux, arydim, size, i, nele, nderived; + const char *name; + bfd_boolean oldavrcoff = (info->flags & (COFF_FL_AVR | COFF_FL_EXT_AVR)) + == COFF_FL_AVR; + + /* Synthesize a new internal COFF symbol. */ + *csympp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (*csympp == NULL) + return FALSE; + + priv = (struct coff_private_symdata *) xmalloc (sizeof *priv); + memset (priv, 0, sizeof *priv); + + type = arydim = size = nderived = 0; + + aux = &(((*csympp)->native + 1)->u.auxent); + + /* Now, walk the type stack, and see how we could convert the info + we've got to what COFF understands. */ + for (;;) + { + if (info->tstack == NULL) + break; + + /* If we have been advised to not pop the entire stack, stop + here. */ + if (info->tstack->tsk == stopat && info->tstack->next == NULL) + break; + + coff_pop_type (); + + switch (tst->tsk) + { + case TS_NONE: + /* cannot happen */ + break; + + case TS_EMPTY: + if (info->tstack != NULL && info->tstack->tsk != stopat) + fprintf (stderr, _("empty type not last on type stack\n")); + /* type |= T_NULL; */ + break; + + case TS_VOID: + if (info->tstack != NULL && info->tstack->tsk != stopat) + fprintf (stderr, _("void type not last on type stack\n")); + type |= T_VOID; + break; + + case TS_INT: + if (info->tstack != NULL && info->tstack->tsk != stopat) + fprintf (stderr, _("int type not last on type stack\n")); + type |= coff_get_fundamental_type (info, tst); + if (size == 0) + size = tst->u.ts_int.size; + break; + + case TS_FLOAT: + if (info->tstack != NULL && info->tstack->tsk != stopat) + fprintf (stderr, _("float type not last on type stack\n")); + type |= coff_get_fundamental_type (info, tst); + if (size == 0) + size = tst->u.ts_float.size; + break; + + case TS_POINTER: + nderived++; + type = ((type & ~N_BTMASK) << N_TSHIFT) | (DT_PTR << N_BTSHFT); + size = info->pointersize; + break; + + case TS_FUNC: + nderived++; + type = ((type & ~N_BTMASK) << N_TSHIFT) | (DT_FCN << N_BTSHFT); + /* AUX entry for DT_FCN will be filled in elsewhere. */ + break; + + case TS_ARRAY: + /* We need to limit arydim so the assignment below won't + overwrite random locations. */ + if (arydim >= DIMNUM) + { + fprintf (stderr, + _("More than %d array dimensions, result is invalid.\n"), + DIMNUM); + arydim = DIMNUM - 1; + } + nderived++; + type = ((type & ~N_BTMASK) << N_TSHIFT) | (DT_ARY << N_BTSHFT); + aux->x_sym.x_fcnary.x_ary.x_dimen[arydim++] = + tst->u.ts_array.high - tst->u.ts_array.low + 1; + + break; + + case TS_COMPLEX: + coff_complain_unsupp (_("complex")); + + case TS_ENUM: + type |= T_ENUM; + if (size == 0) + size = info->enumsize; + + if (tst->u.ts_enum.ehash != NULL) + { + /* enum tag will be fixed later. */ + priv->ehash = tst->u.ts_enum.ehash; + break; + } + if (tst->u.ts_enum.tagismalloced) + name = tst->u.ts_enum.tag.malloctag; + else + name = tst->u.ts_enum.tag.fixtag; + ehash = coff_enum_hash_lookup (&info->enums, name, + TRUE, tst->u.ts_enum.tagismalloced); + if (ehash == NULL) + return FALSE; + if (!ehash->emitted) + { + if (ehash->types == NULL) + { + ehash->types = (struct coff_type_stack *) + xmalloc (sizeof (struct coff_type_stack)); + memcpy (ehash->types, tst, sizeof (struct coff_type_stack)); + } + ehash->emitted = TRUE; + coff_emit_enum (info, tst, ehash); + if (ehash->nfixidxs != 0) + { + coff_symbol_type *symp; + unsigned i; + + for (i = 0; i < ehash->nfixidxs; i++) + { + combined_entry_type *np; + + symp = (coff_symbol_type *) info->syms[ehash->fixidxs[i]]; + symp->native->u.syment.n_type &= ~N_BTMASK; + symp->native->u.syment.n_type |= T_ENUM; + + if (oldavrcoff) + continue; + + np = symp->native + 1; + np->fix_tag = 1; + np->u.auxent.x_sym.x_tagndx.p = ehash->native; + if (np->u.auxent.x_sym.x_misc.x_fsize == 0) + np->u.auxent.x_sym.x_misc.x_lnsz.x_size = size; + } + + free (ehash->fixidxs); + ehash->nfixidxs = 0; + } + } + if (!oldavrcoff) + { + ((*csympp)->native + 1)->fix_tag = 1; + aux->x_sym.x_tagndx.p = ehash->native; + if (aux->x_sym.x_misc.x_fsize == 0) + aux->x_sym.x_misc.x_lnsz.x_size = size; + } + break; + + case TS_STRUCT: + if (tst->u.ts_struct.isstruct) + type |= T_STRUCT; + else + type |= T_UNION; + if (size == 0) + size = tst->u.ts_struct.size; + + if (tst->u.ts_struct.shash != NULL) + { + /* struct tag will be fixed later. */ + priv->shash = tst->u.ts_struct.shash; + break; + } + if (tst->u.ts_struct.tagismalloced) + name = tst->u.ts_struct.tag.malloctag; + else + name = tst->u.ts_struct.tag.fixtag; + shash = coff_struct_hash_lookup (&info->structs, name, + TRUE, tst->u.ts_struct.tagismalloced); + if (shash == NULL) + return FALSE; + if (!shash->emitted) + { + if (shash->types == NULL) + { + shash->types = (struct coff_type_stack *) + xmalloc (sizeof (struct coff_type_stack)); + memcpy (shash->types, tst, sizeof (struct coff_type_stack)); + } + shash->emitted = TRUE; + coff_emit_struct (info, tst, shash); + if (shash->nfixidxs != 0) + { + coff_symbol_type *symp; + unsigned i; + + for (i = 0; i < shash->nfixidxs; i++) + { + combined_entry_type *np; + + symp = (coff_symbol_type *) info->syms[shash->fixidxs[i]]; + symp->native->u.syment.n_type &= ~N_BTMASK; + if (tst->u.ts_struct.isstruct) + symp->native->u.syment.n_type |= T_STRUCT; + else + symp->native->u.syment.n_type |= T_UNION; + + if (oldavrcoff) + continue; + + np = symp->native + 1; + np->fix_tag = 1; + np->u.auxent.x_sym.x_tagndx.p = shash->native; + if (np->u.auxent.x_sym.x_misc.x_fsize == 0) + np->u.auxent.x_sym.x_misc.x_lnsz.x_size = size; + } + + free (shash->fixidxs); + shash->nfixidxs = 0; + } + } + if (!oldavrcoff) + { + ((*csympp)->native + 1)->fix_tag = 1; + aux->x_sym.x_tagndx.p = shash->native; + if (aux->x_sym.x_misc.x_fsize == 0) + aux->x_sym.x_misc.x_lnsz.x_size = size; + } + break; + } + free (tst); + } + + if (nderived > 6) + fprintf (stderr, + _("More than 6 derived type specifiers, result is invalid.\n")); + + /* Our type computation so far used the reverse order for derived + type specifiers. Fix this here if there was more than one + derived type specifier. */ + if (nderived > 1) + { + unsigned int nty, bty; + bty = type & N_BTMASK; + type = type >> N_BTSHFT; + nty = 0; + while (nderived-- > 0) + { + nty = (nty << N_TSHIFT) | (type & (N_TMASK >> N_BTSHFT)); + type >>= N_TSHIFT; + } + type = (nty << N_BTSHFT) | bty; + } + + if (ISARY (type)) + { + /* Compute size of entire array. */ + for (i = 0, nele = 1; i < arydim; i++) + nele *= aux->x_sym.x_fcnary.x_ary.x_dimen[i]; + aux->x_sym.x_misc.x_lnsz.x_size = size * nele; + } + + numaux = 0; + if (ISARY (type) || ISFCN (type)) + numaux++; + if ((BTYPE (type) == T_STRUCT || BTYPE (type) == T_UNION + || BTYPE (type) == T_ENUM) + && !oldavrcoff) + numaux++; + /* Only AVR COFF uses multiple AUX entries. */ + if (numaux > 1 && (info->flags & COFF_FL_AVR) == 0) + numaux = 1; + + priv->size = size; + (*csympp)->symbol.udata.p = priv; + (*csympp)->native->u.syment.n_type = type; + (*csympp)->native->u.syment.n_numaux = numaux; + + /* If the fundamental type comes out as T_NULL, this means we don't + have any type information. Just don't emit any aux entries in + that case, and drop any derived type information as well. */ + if (BTYPE (type) == T_NULL) + { + printf ("coff_make_typed_symbol() -> T_NULL\n"); + //(*csympp)->native->u.syment.n_type = T_NULL; + (*csympp)->native->u.syment.n_numaux = 0; + } + + return TRUE; +} + +static bfd_boolean coff_emit_struct (info, tst, shash) + struct coff_write_handle *info; + struct coff_type_stack *tst; + struct coff_struct_hash_entry *shash; +{ + coff_symbol_type *csymp, *scsymp, *ecsymp; + union internal_auxent *aux; + struct coff_fix_stack *fixp, *ofp; + bfd_boolean isstruct = tst->u.ts_struct.isstruct; + bfd_boolean isbitfield = FALSE; + struct coff_type_stack *savedtst; + struct coff_struct_fields *fp; + unsigned short sclass; + long i; + + if ((info->flags & (COFF_FL_AVR | COFF_FL_EXT_AVR)) == + COFF_FL_AVR) + /* old AVR COFF doesn't support struct debugging */ + return TRUE; + + /* Synthesize a new internal COFF symbol for the struct/union. */ + scsymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (scsymp == NULL) + return FALSE; + + if (tst->u.ts_struct.tagismalloced) + scsymp->symbol.name = xstrdup (tst->u.ts_struct.tag.malloctag); + else + scsymp->symbol.name = tst->u.ts_struct.tag.fixtag; + scsymp->symbol.flags = BSF_NOT_AT_END; + scsymp->symbol.section = bfd_und_section_ptr; + scsymp->native->u.syment.n_sclass = isstruct? C_STRTAG: C_UNTAG; + scsymp->native->u.syment.n_type = isstruct? T_STRUCT: T_UNION; + scsymp->native->u.syment.n_numaux = 1; + scsymp->symbol.udata.p = NULL; + scsymp->symbol.value = 0; + + shash->native = scsymp->native; + + /* Synthesize a new internal COFF symbol for the end of struct/union. */ + ecsymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (ecsymp == NULL) + return FALSE; + + ecsymp->symbol.name = ".eos"; + ecsymp->symbol.flags = BSF_NOT_AT_END; + /* We need to use the com section here since bfd/coffgen.c + translates this into an N_UNDEF one without clobbering the + value. */ + ecsymp->symbol.section = bfd_com_section_ptr; + ecsymp->native->u.syment.n_sclass = C_EOS; + ecsymp->symbol.udata.p = NULL; + ecsymp->symbol.value = tst->u.ts_struct.size; + ecsymp->native->u.syment.n_numaux = 1; + (ecsymp->native + 1)->fix_tag = 1; + aux = &((ecsymp->native + 1)->u.auxent); + aux->x_sym.x_tagndx.p = scsymp->native; + aux->x_sym.x_misc.x_lnsz.x_size = tst->u.ts_struct.size; + + coff_record_symbol (info, scsymp); + + savedtst = info->tstack; + + if (isstruct) + { + /* First, make a quick walk along all the fields, and figure out + * whether we've got a genuine struct or a bitfield struct. */ + for (i = 0, fp = tst->u.ts_struct.fields; + i < tst->u.ts_struct.nfields; + i++, fp++) + if (fp->bitsize % 8 != 0) + { + isbitfield = TRUE; + break; + } + } + + sclass = isstruct? (isbitfield? C_FIELD: C_MOS): C_MOU; + + for (i = 0, fp = tst->u.ts_struct.fields; + i < tst->u.ts_struct.nfields; + i++, fp++) + { + if (strlen (fp->name) == 0) + { + /* empty name could happen inside bitfield */ + fp->types = NULL; + continue; + } + + info->tstack = fp->types; + if (!coff_make_typed_symbol (info, &csymp, TS_NONE)) + return FALSE; + + csymp->symbol.name = xstrdup (fp->name); + csymp->symbol.flags = BSF_NOT_AT_END; + csymp->symbol.section = bfd_com_section_ptr; + csymp->native->u.syment.n_sclass = sclass; + csymp->symbol.value = isbitfield? fp->bitpos: fp->bitpos / 8; + if (isbitfield) + { + csymp->native->u.syment.n_numaux = 1; + aux = &((csymp->native + 1)->u.auxent); + aux->x_sym.x_misc.x_lnsz.x_size = fp->bitsize; + } + + coff_record_symbol (info, csymp); + + fp->types = NULL; + } + + info->tstack = savedtst; + + /* Record our endndx field for later fixing. */ + fixp = (struct coff_fix_stack *) xmalloc (sizeof (struct coff_fix_stack)); + fixp->native = scsymp->native + 1; /* points to first AUX */ + fixp->next = NULL; + if (info->fixes == NULL) + info->fixes = fixp; + else + { + for (ofp = info->fixes; ofp->next != NULL;) + ofp = ofp->next; + ofp->next = fixp; + } + + coff_record_symbol (info, ecsymp); + info->flags |= COFF_FL_FIX_ENDNDX; + + return TRUE; +} + +static bfd_boolean coff_emit_enum (info, tst, ehash) + struct coff_write_handle *info; + struct coff_type_stack *tst; + struct coff_enum_hash_entry *ehash; +{ + coff_symbol_type *csymp, *scsymp, *ecsymp; + union internal_auxent *aux; + struct coff_fix_stack *fixp, *ofp; + int i; + + if ((info->flags & (COFF_FL_AVR | COFF_FL_EXT_AVR)) == + COFF_FL_AVR) + /* old AVR COFF doesn't support enum debugging */ + return TRUE; + + /* Synthesize a new internal COFF symbol for the enum. */ + scsymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (scsymp == NULL) + return FALSE; + + if (tst->u.ts_enum.tagismalloced) + scsymp->symbol.name = xstrdup (tst->u.ts_enum.tag.malloctag); + else + scsymp->symbol.name = tst->u.ts_enum.tag.fixtag; + scsymp->symbol.flags = BSF_NOT_AT_END; + scsymp->symbol.section = bfd_und_section_ptr; + scsymp->native->u.syment.n_sclass = C_ENTAG; + scsymp->native->u.syment.n_type = T_ENUM; + scsymp->native->u.syment.n_numaux = 1; + scsymp->symbol.udata.p = NULL; + scsymp->symbol.value = 0; + + ehash->native = scsymp->native; + + /* Synthesize a new internal COFF symbol for the end of struct/union. */ + ecsymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (ecsymp == NULL) + return FALSE; + + ecsymp->symbol.name = ".eos"; + ecsymp->symbol.flags = BSF_NOT_AT_END; + /* We need to use the com section here since bfd/coffgen.c + translates this into an N_UNDEF one without clobbering the + value. */ + ecsymp->symbol.section = bfd_com_section_ptr; + ecsymp->native->u.syment.n_sclass = C_EOS; + ecsymp->symbol.udata.p = NULL; + ecsymp->symbol.value = info->enumsize; + ecsymp->native->u.syment.n_numaux = 1; + (ecsymp->native + 1)->fix_tag = 1; + aux = &((ecsymp->native + 1)->u.auxent); + aux->x_sym.x_tagndx.p = scsymp->native; + aux->x_sym.x_misc.x_lnsz.x_size = info->enumsize; + + coff_record_symbol (info, scsymp); + + for (i = 0;; i++) + { + const char *name = tst->u.ts_enum.names[i]; + if (name == NULL) + break; + + /* Synthesize a new internal COFF symbol for the enum. */ + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = xstrdup (name); + csymp->symbol.flags = BSF_NOT_AT_END; + csymp->symbol.section = bfd_com_section_ptr; + csymp->native->u.syment.n_sclass = C_MOE; + csymp->symbol.udata.p = NULL; + csymp->symbol.value = tst->u.ts_enum.vals[i]; + + coff_record_symbol (info, csymp); + } + + /* Record our endndx field for later fixing. */ + fixp = (struct coff_fix_stack *) xmalloc (sizeof (struct coff_fix_stack)); + fixp->native = scsymp->native + 1; /* points to first AUX */ + fixp->next = NULL; + if (info->fixes == NULL) + info->fixes = fixp; + else + { + for (ofp = info->fixes; ofp->next != NULL;) + ofp = ofp->next; + ofp->next = fixp; + } + + coff_record_symbol (info, ecsymp); + info->flags |= COFF_FL_FIX_ENDNDX; + + return TRUE; +} + +/* Emit a non-debugging symbol that came from the input symbol table, + and has not been claimed by one of the debugging symbols. */ +static bfd_boolean +coff_emit_ndebug_sym (info, osymp, localp) + struct coff_write_handle *info; + asymbol *osymp; + bfd_boolean localp; +{ + coff_symbol_type *csymp; + + /* Create new COFF symbol. */ + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = xstrdup (osymp->name); + csymp->symbol.value = osymp->value; + csymp->symbol.flags = localp? BSF_LOCAL: BSF_GLOBAL; + csymp->symbol.section = osymp->section; + csymp->symbol.udata.p = NULL; + csymp->native->u.syment.n_sclass = localp? C_STAT: C_EXT; + csymp->native->u.syment.n_type = T_NULL; + + coff_record_symbol (info, csymp); + + return TRUE; +} + +/* The general routine to write out COFF debugging information. This + synthesizes and accumulates the COFF symbols. Actual symbol table + output is performed later on by the BFD functions. ABFD is the BFD + and DHANDLE is the handle for the debugging information. symcountp + and symppp point to the incoming (parsed) symbol list on entry, and + will be updated to point to the new symbol table's values upon + exit. */ + +bfd_boolean +write_coff_debugging_info (abfd, dhandle, symcountp, symppp) + bfd *abfd; + PTR dhandle; + long *symcountp; + asymbol ***symppp; +{ + struct coff_write_handle info; + long i, l; + asymbol *symp; + struct coff_compilation_unit *up; + coff_symbol_type *csymp; + + memset ((void *)&info, 0, sizeof info); + + info.abfd = abfd; + + info.pointersize = info.enumsize = 4; + + switch (bfd_get_arch (abfd)) + { + case bfd_arch_avr: + info.flags |= COFF_FL_AVR; + if (strcmp (abfd->xvec->name, "coff-ext-avr") == 0) + info.flags |= COFF_FL_EXT_AVR; + /* Fix the builtin type sizes. */ + coff_predef_types[0].size = 2; /* sizeof(int) == 2 */ + coff_predef_types[4].size = 4; /* sizeof(double) == 4 */ + coff_predef_types[6].size = 2; /* sizeof(unsigned int) == 2 */ + info.pointersize = info.enumsize = 2; + break; + + default: + ; + } + + coff_copy_symbols(&info, *symcountp, *symppp); + + if (info.textsect == NULL) + { + fprintf (stderr, _("Warning: no \"text\" section found in output file\n")); + info.textsect = bfd_abs_section_ptr; + } + if (info.datasect == NULL) + { + fprintf (stderr, _("Warning: no \"data\" section found in output file\n")); + info.datasect = bfd_abs_section_ptr; + } + + if (! bfd_hash_table_init (&info.types.root, coff_name_type_newfunc, + sizeof(struct coff_name_type_hash_entry))) + return FALSE; + + if (! bfd_hash_table_init (&info.structs.root, coff_struct_newfunc, + sizeof(struct coff_struct_hash_entry))) + return FALSE; + + if (! bfd_hash_table_init (&info.enums.root, coff_enum_newfunc, + sizeof(struct coff_enum_hash_entry))) + return FALSE; + + if (! debug_write (dhandle, &coff_fns, (PTR) &info)) + return FALSE; + + /* If there is an old compilation unit that has got any local + non-debugging symbols left over, send them out now. */ + if (info.currentfile != NULL && info.currentfile->totsyms != 0) + for (i = 0; i < info.currentfile->nsyms; i++) + { + up = info.currentfile; + + if (up->syms[i] != NULL) + { + coff_emit_ndebug_sym (&info, up->syms[i], TRUE); + up->syms[i] = NULL; + up->totsyms--; + } + } + + /* See whether there are any non-debugging symbols left from the + input symbol table. First look at all local symbols which must + be from entire compilation units we didn't see yet in the + debugging information, because anything else has already been + handled at the end of each compilation unit (like in the loop + immediately above). Any compilation unit that has already been + processed that way is supposed to have its "totsyms" counted down + to 0 now, so we can skip them. + + Finally, put out all remaining global non-debugging symbols. */ + for (l = 0; l < info.nunits; l++) + { + const char *bn; + + up = info.units + l; + if (up->totsyms == 0) + continue; + + /* Create COFF symbol for this compilation unit. */ + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info.abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + bn = bu_basename (up->fname); + + if (bfd_coff_long_filenames (info.abfd)) + csymp->symbol.name = up->fname; + else + csymp->symbol.name = bn; + + csymp->symbol.value = 0; + csymp->symbol.udata.p = NULL; + csymp->native->u.syment.n_sclass = C_FILE; + csymp->native->u.syment.n_numaux = 1; /* force filename into aux entry */ + coff_record_symbol (&info, csymp); + + for (i = 0; i < up->nsyms; i++) + { + symp = up->syms[i]; + if (symp == NULL) + continue; + + coff_emit_ndebug_sym (&info, symp, TRUE); + } + } + + for (i = 0; i < info.nglobals; i++) + { + symp = info.globals[i]; + if (symp == NULL) + continue; + + coff_emit_ndebug_sym (&info, symp, FALSE); + } + + /* Fixup the AUX entries for the section symbols we have emitted + earlier (so they are guaranteed to be at the beginning of the + symbol table). In particular, the line number count (which we + only have for the text section) is known right now. */ + for (i = 0; i < info.nsecsyms; i++) + { + union internal_auxent *aux; + + csymp = info.secsyms[i]; + + aux = &((csymp->native + 1)->u.auxent); + aux->x_scn.x_scnlen = csymp->symbol.section->output_section->rawsize; + aux->x_scn.x_nreloc = csymp->symbol.section->reloc_count; + if (csymp->symbol.section == info.textsect) + aux->x_scn.x_nlinno = info.totlnos; + } + free (info.secsyms); + + coff_name_type_hash_traverse (&info.types, coff_free_type_info, NULL); + bfd_hash_table_free (&info.types.root); + + coff_struct_hash_traverse (&info.structs, coff_free_struct_info, NULL); + bfd_hash_table_free (&info.structs.root); + + coff_enum_hash_traverse (&info.enums, coff_free_enum_info, NULL); + bfd_hash_table_free (&info.enums.root); + + /* FIXME: free all the other stuff remembered in "info". */ + + free (*symppp); + + *symcountp = info.nsyms; + *symppp = (asymbol **)info.syms; + + return TRUE; +} + +/* Start writing out information for a compilation unit. */ + +static bfd_boolean +coff_start_compilation_unit (p, filename) + PTR p; + const char *filename; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + long i; + const char *bn; + bfd_boolean found; + coff_symbol_type *csymp; + +#if COFF_DEBUG + printf ("coff_start_compilation_unit(%s)\n", filename); +#endif + + /* If there is an old compilation unit that has got any local + non-debugging symbols left over, send them out now. */ + if (info->currentfile != NULL && info->currentfile->totsyms != 0) + for (i = 0; i < info->currentfile->nsyms; i++) + { + struct coff_compilation_unit *up = info->currentfile; + + if (up->syms[i] != NULL) + { + coff_emit_ndebug_sym (info, up->syms[i], TRUE); + up->syms[i] = NULL; + up->totsyms--; + } + } + + /* symtab (and thus COFF debugging) symbols can only transfer the + basename of the file, so strip the dirname */ + bn = bu_basename (filename); + + for (i = 0, found = FALSE; i < info->nunits; i++) + { + if (strcmp (info->units[i].fname, bn) == 0) + { + info->currentfile = info->units + i; + found = TRUE; + break; + } + } + if (!found) + { + fprintf(stderr, + _("Warning: file %s not found in symbol table, ignoring\n"), + filename); + info->currentfile = NULL; + return TRUE; + } + + /* Synthesize a new internal COFF symbol. */ + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + /* Note that coff_fix_symbol_name() [coffgen.c] will fix this for + us: the symbol name will be replaced by ".file", and the filename + will be moved to the aux entries. We use the long name obtained + from the debugging information (that includes the full path) if + our COFF format supports long filenames, otherwise we only use + the basename of the file. */ + if (bfd_coff_long_filenames (info->abfd)) + csymp->symbol.name = filename; + else + csymp->symbol.name = bn; + csymp->symbol.value = 0; + csymp->symbol.udata.p = NULL; + csymp->native->u.syment.n_sclass = C_FILE; + csymp->native->u.syment.n_numaux = 1; /* force filename into aux entry */ + coff_record_symbol (info, csymp); + + return TRUE; +} + +/* Start writing out information for a particular source file. */ + +static bfd_boolean +coff_start_source (p, filename) + PTR p ATTRIBUTE_UNUSED; + const char *filename ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_start_source(%s)\n", filename); +#endif + + /* COFF cannot handle include filenames. */ + + return TRUE; +} + +/* Push an empty type. This shouldn't normally happen. */ + +static bfd_boolean +coff_empty_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_empty_type()\n"); +#endif + + coff_push_type (TS_EMPTY); + + return TRUE; +} + +/* Push a void type. */ + +static bfd_boolean +coff_void_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_void_type()\n"); +#endif + + coff_push_type (TS_VOID); + + return TRUE; +} + +/* Push an integer type. */ + +static bfd_boolean +coff_int_type (p, size, unsignedp) + PTR p; + unsigned int size; + bfd_boolean unsignedp; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_int_type(%d, %d)\n", size, unsignedp); +#endif + + coff_push_type (TS_INT); + tst->u.ts_int.size = size; + tst->u.ts_int.isunsigned = unsignedp; + + return TRUE; +} + +/* Push a floating point type. */ + +static bfd_boolean +coff_float_type (p, size) + PTR p; + unsigned int size; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_float_type(%d)\n", size); +#endif + + coff_push_type (TS_FLOAT); + tst->u.ts_float.size = size; + + return TRUE; +} + +/* Push a complex type. */ + +static bfd_boolean +coff_complex_type (p, size) + PTR p; + unsigned int size ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_complex_type(%d)\n", size); +#endif + + coff_push_type (TS_COMPLEX); + + return TRUE; +} + +/* Push a bfd_boolean type. */ + +static bfd_boolean +coff_bool_type (p, size) + PTR p; + unsigned int size; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_bool_type(%d)\n", size); +#endif + + coff_push_type (TS_INT); + tst->u.ts_int.size = size; + tst->u.ts_int.isunsigned = TRUE; + + return TRUE; +} + +/* Push an enum type. */ + +static bfd_boolean +coff_enum_type (p, tag, names, vals) + PTR p; + const char *tag; + const char **names; + bfd_signed_vma *vals; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + char buf[20]; + +#if COFF_DEBUG + int idx; + printf ("coff_enum_type(%s [", tag); + for (idx = 0; names[idx] != NULL; idx++) + printf ("%s -> %d, ", names[idx], (int)vals[idx]); + printf ("])\n"); +#endif + + coff_push_type (TS_ENUM); + + if (tag == NULL) + { + sprintf(buf, ".%dfake", info->nenums++); + tst->u.ts_enum.tag.malloctag = xstrdup (buf); + tst->u.ts_enum.tagismalloced = TRUE; + } + else + tst->u.ts_enum.tag.fixtag = tag; + tst->u.ts_enum.names = names; + tst->u.ts_enum.vals = vals; + + return TRUE; +} + +/* Push a pointer type. */ + +static bfd_boolean +coff_pointer_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_pointer_type()\n"); +#endif + + coff_push_type (TS_POINTER); + + return TRUE; +} + +/* Push a function type. */ + +static bfd_boolean +coff_function_type (p, argcount, varargs) + PTR p; + int argcount; + bfd_boolean varargs ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_function_type(%d, %d)\n", argcount, varargs); +#endif + + coff_push_type (TS_FUNC); + + /* FIXME should properly discard function arguments */ + if (argcount > -1) + { + fprintf (stderr, + _("coff_function_type() called with positive argcount\n")); + return FALSE; + } + + return TRUE; +} + +/* Push a reference type. */ + +static bfd_boolean +coff_reference_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_reference_type()\n"); +#endif + + coff_complain_unsupp (_("reference")); + + return TRUE; +} + +/* Push a range type. */ + +static bfd_boolean +coff_range_type (p, low, high) + PTR p; + bfd_signed_vma low ATTRIBUTE_UNUSED; + bfd_signed_vma high ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_range_type([%d..%d)\n", (int)low, (int)high); +#endif + + coff_complain_unsupp (_("range")); + + return TRUE; +} + +/* Push an array type. */ + +static bfd_boolean +coff_array_type (p, low, high, stringp) + PTR p; + bfd_signed_vma low; + bfd_signed_vma high; + bfd_boolean stringp; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst; + +#if COFF_DEBUG + printf ("coff_array_type([%d..%d], %d)\n", + (int)low, (int)high, stringp); +#endif + + /* Pop the range type, but ignore it. COFF doesn't use it. */ + coff_pop_type (); + + /* FIXME What to do here? */ + if (stringp) + { + fprintf(stderr, _("coff_array_type(): stringp == TRUE\n")); + return FALSE; + } + + coff_push_type (TS_ARRAY); + tst->u.ts_array.low = low; + tst->u.ts_array.high = high; + + return TRUE; +} + +/* Push a set type. */ + +static bfd_boolean +coff_set_type (p, bitstringp) + PTR p; + bfd_boolean bitstringp ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_set_type(%d)\n", bitstringp); +#endif + + coff_complain_unsupp (_("set")); + + return TRUE; +} + +/* Push an offset type. */ + +static bfd_boolean +coff_offset_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_offset_type()\n"); +#endif + + coff_complain_unsupp (_("offset")); + + return TRUE; +} + +/* Push a method type. */ + +static bfd_boolean +coff_method_type (p, domainp, argcount, varargs) + PTR p; + bfd_boolean domainp ATTRIBUTE_UNUSED; + int argcount ATTRIBUTE_UNUSED; + bfd_boolean varargs ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_method_type(%d, %d, %d)\n", + domainp, argcount, varargs); +#endif + + coff_complain_unsupp (_("method")); + + return TRUE; +} + +/* Push a const version of a type. */ + +static bfd_boolean +coff_const_type (p) + PTR p ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_const_type()\n"); +#endif + + /* const modifier is ignored by COFF */ + + return TRUE; +} + +/* Push a volatile version of a type. */ + +static bfd_boolean +coff_volatile_type (p) + PTR p ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_volatile_type()\n"); +#endif + + /* volatile modifier is ignored by COFF */ + + return TRUE; +} + +/* Start outputting a struct. */ + +static bfd_boolean +coff_start_struct_type (p, tag, id, structp, size) + PTR p; + const char *tag; + unsigned int id; + bfd_boolean structp; + unsigned int size; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *savedts; + struct coff_struct_hash_entry *shash; + char buf[20]; + const char *name; + +#if COFF_DEBUG + printf ("coff_start_struct_type(%s, %d, %d, %d)\n", + tag, id, structp, size); +#endif + + savedts = info->tstack; + info->tstack = NULL; + + coff_push_type (TS_STRUCT); + + if (tag == NULL) + { + sprintf(buf, ".%dfake", id); + name = tst->u.ts_struct.tag.malloctag = xstrdup (buf); + tst->u.ts_struct.tagismalloced = TRUE; + } + else + name = tst->u.ts_struct.tag.fixtag = tag; + tst->u.ts_struct.id = id; + tst->u.ts_struct.isstruct = structp; + tst->u.ts_struct.size = size; + tst->u.ts_struct.savedts = savedts; + + shash = coff_struct_hash_lookup (&info->structs, name, FALSE, FALSE); + if (shash != NULL && shash->types != NULL) + { +#if COFF_DEBUG + printf ("new %s definition for %s\n", + tst->u.ts_struct.isstruct? "struct": "union", name); +#endif + coff_free_struct_info (shash, NULL); + shash->types = NULL; + shash->emitted = FALSE; + } + else + (void)coff_struct_hash_lookup (&info->structs, name, + TRUE, tst->u.ts_struct.tagismalloced); + + return TRUE; +} + +/* Add a field to a struct. */ + +static bfd_boolean +coff_struct_field (p, name, bitpos, bitsize, visibility) + PTR p; + const char *name; + bfd_vma bitpos; + bfd_vma bitsize; + enum debug_visibility visibility; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *otst; + struct coff_struct_fields *fp; + struct coff_struct_hash_entry *shash; + struct coff_enum_hash_entry *ehash; + const char *tag; + +#if COFF_DEBUG + printf ("coff_struct_field(%s, %d, %d, %d)\n", + name, (int)bitpos, (int)bitsize, (int)visibility); +#endif + + /* Find the last element on the type stack. */ + assert (info->tstack != NULL); + for (tst = info->tstack, otst = NULL; tst->next != NULL;) + { + otst = tst; + tst = tst->next; + } + if (otst != NULL) + otst->next = NULL; + + if (tst->tsk != TS_STRUCT) + { + fprintf (stderr, "coff_struct_field() not within structure definition\n"); + return FALSE; + } + tst->u.ts_struct.fields = (struct coff_struct_fields *) + xrealloc (tst->u.ts_struct.fields, + ++tst->u.ts_struct.nfields * sizeof (struct coff_struct_fields)); + fp = tst->u.ts_struct.fields + (tst->u.ts_struct.nfields - 1); + fp->name = name; + fp->bitpos = bitpos; + fp->bitsize = bitsize; + fp->visibility = visibility; + otst = fp->types = info->tstack; + while (otst->next != NULL) + otst = otst->next; + if (otst->tsk == TS_STRUCT && otst->u.ts_struct.shash == NULL) + { + if (otst->u.ts_struct.tagismalloced) + tag = otst->u.ts_struct.tag.malloctag; + else + tag = otst->u.ts_struct.tag.fixtag; + shash = coff_struct_hash_lookup (&info->structs, tag, FALSE, FALSE); + assert (shash != NULL); + if (!shash->emitted) + { + if (shash->types == NULL) + { + shash->types = (struct coff_type_stack *) + xmalloc (sizeof (struct coff_type_stack)); + memcpy (shash->types, otst, sizeof (struct coff_type_stack)); + } + shash->emitted = TRUE; + coff_emit_struct (info, otst, shash); + } + } + else if (otst->tsk == TS_ENUM) + { + if (otst->u.ts_enum.tagismalloced) + tag = otst->u.ts_enum.tag.malloctag; + else + tag = otst->u.ts_enum.tag.fixtag; + ehash = coff_enum_hash_lookup (&info->enums, tag, TRUE, FALSE); + assert (ehash != NULL); + if (!ehash->emitted) + { + if (ehash->types == NULL) + { + ehash->types = (struct coff_type_stack *) + xmalloc (sizeof (struct coff_type_stack)); + memcpy (ehash->types, otst, sizeof (struct coff_type_stack)); + } + ehash->emitted = TRUE; + coff_emit_enum (info, otst, ehash); + } + } + + info->tstack = tst; + + return TRUE; +} + +/* Finish up a struct. */ + +static bfd_boolean +coff_end_struct_type (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *savedts; + +#if COFF_DEBUG + printf ("coff_end_struct_type()\n"); +#endif + + /* Our struct definition should be the only type stack element by + now. */ + assert (info->tstack != NULL); + tst = info->tstack; + if (tst->tsk != TS_STRUCT || tst->next != NULL) + { + fprintf (stderr, "coff_struct_field() not within structure definition\n"); + return FALSE; + } + + /* Restore saved type stack, and push our now complete struct + definition on top. */ + savedts = tst->u.ts_struct.savedts; + tst->u.ts_struct.savedts = info->tstack; + info->tstack = savedts; + tst->next = info->tstack; + info->tstack = tst; + + return TRUE; +} + +/* Start outputting a class. */ + +static bfd_boolean +coff_start_class_type (p, tag, id, structp, size, vptr, ownvptr) + PTR p; + const char *tag ATTRIBUTE_UNUSED; + unsigned int id ATTRIBUTE_UNUSED; + bfd_boolean structp ATTRIBUTE_UNUSED; + unsigned int size ATTRIBUTE_UNUSED; + bfd_boolean vptr ATTRIBUTE_UNUSED; + bfd_boolean ownvptr ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_start_class_type(%s, %d, %d, %d, %d, %d)\n", + tag, id, structp, size, vptr, ownvptr); +#endif + + coff_complain_unsupp (_("class")); + + return TRUE; +} + +/* Add a static member to the class on the type stack. */ + +static bfd_boolean +coff_class_static_member (p, name, physname, visibility) + PTR p ATTRIBUTE_UNUSED; + const char *name ATTRIBUTE_UNUSED; + const char *physname ATTRIBUTE_UNUSED; + enum debug_visibility visibility ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_static_member(%s, %s, %d)\n", + name, physname, (int)visibility); +#endif + + return TRUE; +} + +/* Add a base class to the class on the type stack. */ + +static bfd_boolean +coff_class_baseclass (p, bitpos, virtual, visibility) + PTR p ATTRIBUTE_UNUSED; + bfd_vma bitpos ATTRIBUTE_UNUSED; + bfd_boolean virtual ATTRIBUTE_UNUSED; + enum debug_visibility visibility ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_baseclass(%d, %d, %d)\n", + (int)bitpos, virtual, (int)visibility); +#endif + + return TRUE; +} + +/* Start adding a method to the class on the type stack. */ + +static bfd_boolean +coff_class_start_method (p, name) + PTR p ATTRIBUTE_UNUSED; + const char *name ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_start_method(%s)\n", name); +#endif + + return TRUE; +} + +/* Add a variant to the current method. */ + +static bfd_boolean +coff_class_method_variant (p, physname, visibility, constp, volatilep, + voffset, contextp) + PTR p ATTRIBUTE_UNUSED; + const char *physname ATTRIBUTE_UNUSED; + enum debug_visibility visibility ATTRIBUTE_UNUSED; + bfd_boolean constp ATTRIBUTE_UNUSED; + bfd_boolean volatilep ATTRIBUTE_UNUSED; + bfd_vma voffset ATTRIBUTE_UNUSED; + bfd_boolean contextp ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_method_variant(%s, %d, %d, %d, %d, %d)\n", + physname, (int)visibility, constp, volatilep, + (int)voffset, contextp); +#endif + + return TRUE; +} + +/* Add a static variant to the current method. */ + +static bfd_boolean +coff_class_static_method_variant (p, physname, visibility, constp, volatilep) + PTR p ATTRIBUTE_UNUSED; + const char *physname ATTRIBUTE_UNUSED; + enum debug_visibility visibility ATTRIBUTE_UNUSED; + bfd_boolean constp ATTRIBUTE_UNUSED; + bfd_boolean volatilep ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_static_method_variant(%s, %d, %d, %d)\n", + physname, (int)visibility, constp, volatilep); +#endif + + return TRUE; +} + +/* Finish up a method. */ + +static bfd_boolean +coff_class_end_method (p) + PTR p ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_class_end_method()\n"); +#endif + + return TRUE; +} + +/* Finish up a class. */ + +static bfd_boolean +coff_end_class_type (p) + PTR p ATTRIBUTE_UNUSED; +{ + +#if COFF_DEBUG + printf ("coff_end_class_type()\n"); +#endif + + return TRUE; +} + +/* Push a typedef which was previously defined. */ + +static bfd_boolean +coff_typedef_type (p, name) + PTR p; + const char *name; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_name_type_hash_entry *nthash; + struct coff_type_stack *tst, *newchain, *newst, *temp; + +#if COFF_DEBUG + printf ("coff_typedef_type(%s)\n", name); +#endif + + nthash = coff_name_type_hash_lookup (&info->types, name, FALSE, FALSE); + + /* nthash 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 (nthash != NULL); + + /* Just push the entire type stack snapshot we've got on top of the + existing typestack. See coff_typdef() below for how this + works. We need to copy over each element however, since anybody + popping elements off the typestack is supposed to free() each of + them. */ + + for (tst = nthash->types, temp = newst = newchain = NULL; tst != NULL;) + { + temp = newst; + newst = (struct coff_type_stack *) xmalloc (sizeof (*newst)); + if (newchain == NULL) + newchain = newst; + memcpy (newst, tst, sizeof (*newst)); + if (temp != NULL) + temp->next = newst; + + tst = tst->next; + } + newst->next = info->tstack; + info->tstack = newchain; + + return TRUE; +} + +/* Push a struct, union or class tag. */ + +static bfd_boolean +coff_tag_type (p, name, id, kind) + PTR p; + const char *name; + unsigned int id ATTRIBUTE_UNUSED; + enum debug_type_kind kind; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *newchain, *newst, *temp; + struct coff_struct_hash_entry *shash; + struct coff_enum_hash_entry *ehash; + char buf[20]; + bfd_boolean needcopy = FALSE; + bfd_boolean isstruct = TRUE; + +#if COFF_DEBUG + printf ("coff_tag_type(%s, %d, %d)\n", + name, id, kind); +#endif + + if (name == NULL) + { + sprintf(buf, ".%dfake", id); + needcopy = TRUE; + } + + switch (kind) + { + case DEBUG_KIND_UNION: + case DEBUG_KIND_UNION_CLASS: + isstruct = FALSE; + /* FALLTHROUGH */ + case DEBUG_KIND_STRUCT: + case DEBUG_KIND_CLASS: + shash = coff_struct_hash_lookup (&info->structs, + name == NULL? buf: name, TRUE, needcopy); + assert (shash != NULL); + tst = shash->types; + if (tst == NULL) + { + /* This is a reference to a tag that has not yet been + defined (i. e., a forward reference). Synthesize a + ts_struct entry by now, and mark it for later fixup. */ + tst = (struct coff_type_stack *) xmalloc (sizeof *tst); + memset (tst, 0, sizeof *tst); + tst->tsk = TS_STRUCT; + tst->u.ts_struct.isstruct = isstruct; + tst->u.ts_struct.shash = shash; + } + docopystack: + /* Just push the entire type stack snapshot we've got on top of the + existing typestack. See coff_typdef() below for how this + works. We need to copy over each element however, since anybody + popping elements off the typestack is supposed to free() each of + them. */ + for (temp = newst = newchain = NULL; tst != NULL;) + { + temp = newst; + newst = (struct coff_type_stack *) xmalloc (sizeof (*newst)); + if (newchain == NULL) + newchain = newst; + memcpy (newst, tst, sizeof (*newst)); + if (temp != NULL) + temp->next = newst; + + tst = tst->next; + } + if (newst) + { + newst->next = info->tstack; + info->tstack = newchain; + } + break; + + case DEBUG_KIND_ENUM: + ehash = coff_enum_hash_lookup (&info->enums, + name == NULL? buf: name, TRUE, needcopy); + assert (ehash != NULL); + tst = ehash->types; + if (tst == NULL) + { + /* This is a reference to a tag that has not yet been + defined (i. e., a forward reference). Synthesize a + ts_enum entry by now, and mark it for later fixup. */ + tst = (struct coff_type_stack *) xmalloc (sizeof *tst); + memset (tst, 0, sizeof *tst); + tst->tsk = TS_ENUM; + tst->u.ts_enum.ehash = ehash; + } + goto docopystack; + + default: + fprintf (stderr, _("illegal kind %d in coff_tag_type()\n"), + (int)kind); + return FALSE; + } + return TRUE; +} + +/* Define a typedef. */ + +static bfd_boolean +coff_typdef (p, name) + PTR p; + const char *name; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_name_type_hash_entry *nthash; + +#if COFF_DEBUG + printf ("coff_typdef(%s)\n", name); +#endif + + /* COFF cannot really handle typedefs. While there is the option to + mark a symbol using the storage class C_TPDEF (so the COFF reader + will know that name), there is no way to place a reference to + that typedef into the just 16 bits COFF reserves for all of its + type information. Thus, any use of the typedef must always fully + dereference the typedef again. We do this by "snapshotting" the + current type stack under the name of our typedef, and later on, + when BFD debugging tells us to make use of the typedef (in + coff_typedef_type()), we just look it up, and push all we've got + completely onto the type stack again. */ + + if (info->tstack == NULL) + { + fprintf (stderr, _("coff_typdef() on an empty type stack\n")); + return FALSE; + } + + nthash = coff_name_type_hash_lookup (&info->types, name, FALSE, FALSE); + if (nthash != NULL) + { +#if COFF_DEBUG + printf ("new typedef for %s\n", name); +#endif + coff_free_type_info (nthash, NULL); + } + else + nthash = coff_name_type_hash_lookup (&info->types, name, TRUE, FALSE); + if (nthash == NULL) + return FALSE; + nthash->types = info->tstack; + + /* If the typestack is "sufficiently complex", emit a C_TPDEF symbol + for it. We assume it to be sufficiently complex if there are + either at least two derived types, or one derived type where the + base type is not a simple scalar one. */ + if (!nthash->emitted + && info->tstack->next != NULL + && (info->tstack->next->next != NULL || info->tstack->next->tsk >= TS_ENUM)) + { + struct coff_type_stack *newchain, *otst, *tst, *ntst; + coff_symbol_type *csymp; + + nthash->emitted = TRUE; + + for (tst = info->tstack, newchain = otst = NULL; + tst != NULL; + tst = tst->next) + { + ntst = (struct coff_type_stack *) + xmalloc (sizeof (struct coff_type_stack)); + memcpy (ntst, tst, sizeof (struct coff_type_stack)); + if (otst == NULL) + newchain = ntst; + else + otst->next = ntst; + otst = ntst; + } + info->tstack = newchain; + if (!coff_make_typed_symbol (info, &csymp, TS_NONE)) + return FALSE; + + csymp->symbol.name = xstrdup (name); + csymp->symbol.flags = BSF_NOT_AT_END; + csymp->symbol.section = bfd_com_section_ptr; + csymp->native->u.syment.n_sclass = C_TPDEF; + csymp->symbol.value = 0; + + coff_record_symbol (info, csymp); + } + info->tstack = NULL; + + return TRUE; +} + +/* Define a tag. */ + +static bfd_boolean +coff_tag (p, tag) + PTR p; + const char *tag; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst = NULL; + struct coff_struct_hash_entry *shash; + struct coff_enum_hash_entry *ehash; + + +#if COFF_DEBUG + printf ("coff_tag(%s)\n", tag); +#endif + + if (info->tstack == NULL) + { + fprintf (stderr, _("coff_tag() called on an empty typestack\n")); + return FALSE; + } + + switch (info->tstack->tsk) + { + case TS_STRUCT: + shash = coff_struct_hash_lookup (&info->structs, tag, FALSE, FALSE); + assert (shash != NULL); + shash->types = info->tstack; + info->tstack = NULL; + break; + + case TS_ENUM: + ehash = coff_enum_hash_lookup (&info->enums, tag, FALSE, FALSE); + if (ehash != NULL && ehash->types != NULL) + { +#if COFF_DEBUG + printf ("new enum definition for %s\n", tag); +#endif + coff_free_enum_info (ehash, NULL); + } + else + ehash = coff_enum_hash_lookup (&info->enums, tag, TRUE, FALSE); + if (ehash == NULL) + return FALSE; + ehash->types = info->tstack; + info->tstack = NULL; + break; + + default: + fprintf (stderr, _("Illegal typestack (%d) in coff_tag()\n"), tst->tsk); + return FALSE; + } + + return TRUE; +} + +/* Define an integer constant. */ + +static bfd_boolean +coff_int_constant (p, name, val) + PTR p; + const char *name ATTRIBUTE_UNUSED; + bfd_vma val ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_int_constant(%s, %d)\n", name, (int)val); +#endif + + coff_complain_unsupp (_("int constant")); + + return TRUE; +} + +/* Define a floating point constant. */ + +static bfd_boolean +coff_float_constant (p, name, val) + PTR p; + const char *name ATTRIBUTE_UNUSED; + double val ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_float_constant(%s, %g)\n", name, val); +#endif + + coff_complain_unsupp (_("float constant")); + + return TRUE; +} + +/* Define a typed constant. */ + +static bfd_boolean +coff_typed_constant (p, name, val) + PTR p; + const char *name ATTRIBUTE_UNUSED; + bfd_vma val ATTRIBUTE_UNUSED; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + +#if COFF_DEBUG + printf ("coff_typed_constant(%s, %d)\n", name, (int)val); +#endif + + coff_complain_unsupp (_("typed constant")); + + return TRUE; +} + +/* Record a variable. */ + +static bfd_boolean +coff_variable (p, name, kind, val) + PTR p; + const char *name; + enum debug_var_kind kind; + bfd_vma val; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + unsigned char class; + asymbol *symp = NULL; + coff_symbol_type *csymp; + bfd_boolean global = FALSE; + flagword flags = BSF_LOCAL; + bfd_vma vmadiff = 0; + +#if COFF_DEBUG + printf ("coff_variable(%s, %d, %d)\n", + name, (int)kind, (int)val); +#endif + + switch (kind) + { + default: + abort (); + + case DEBUG_GLOBAL: + flags = BSF_GLOBAL; + global = TRUE; + /* AVR COFF historically used C_EXTDEF for global variables, and + C_EXT for global functions. Since some AVR COFF consumers + apparently depend on this, we mimic this behaviour as + well. */ + class = info->flags & COFF_FL_AVR? C_EXTDEF: C_EXT; + break; + + case DEBUG_STATIC: + case DEBUG_LOCAL_STATIC: + class = C_STAT; + break; + + case DEBUG_LOCAL: + class = C_AUTO; + break; + + case DEBUG_REGISTER: + class = C_REG; + break; + } + + if (!coff_make_typed_symbol (info, &csymp, TS_NONE)) + return FALSE; + + if (class == C_REG && (info->flags & COFF_FL_AVR) != 0) + { + struct coff_private_symdata *priv = (struct coff_private_symdata *) + csymp->symbol.udata.p; + val = coff_fixup_avr_register (val, priv->size * 8); + } + + csymp->symbol.name = name; + csymp->symbol.flags = flags; /* Note: this clears BSF_DEBUGGING. */ + + /* Match the debugging symbol against the input symtab symbols. If + we found one, use the section information from it. Otherwise, we + are lost here and just use the absolute section that was + predeclared by coff_bfd_make_debug_symbol(). C_REG and C_AUTO + symbols (which we do not attempt to lookup in the symtab symbols + at all) go into the ABS section anyway. */ + if (class != C_REG && class != C_AUTO) + { + symp = coff_find_symbol (info, name, FALSE, global); + if (symp) + { + csymp->symbol.section = symp->section; + vmadiff = symp->section->vma; + } + } + + /* Symbols are relative to section vma. */ + csymp->symbol.value = val - vmadiff; + csymp->native->u.syment.n_sclass = class; + coff_record_symbol (info, csymp); + + return TRUE; +} + +/* Start outputting a function. */ + +static bfd_boolean +coff_start_function (p, name, globalp) + PTR p; + const char *name; + bfd_boolean globalp; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *savedts; + +#if COFF_DEBUG + printf ("coff_start_function(%s, %d)\n", + name, globalp); +#endif + + savedts = info->tstack; + info->tstack = NULL; + + coff_push_type (TS_FUNC); + + if (info->funname != NULL) + { + fprintf (stderr, + _("coff_start_function() called twice, pending %s, new %s\n"), + info->funname, name); + return FALSE; + } + info->funname = name; + info->funglobal = globalp; + info->flags |= COFF_FL_START_FCN; + tst->u.ts_func.savedts = savedts; + + return TRUE; +} + +/* Output a function parameter. */ + +static bfd_boolean +coff_function_parameter (p, name, kind, val) + PTR p; + const char *name; + enum debug_parm_kind kind; + bfd_vma val; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + coff_symbol_type *csymp; + unsigned char class; + +#if COFF_DEBUG + printf ("coff_function_parameter(%s, %d, %d)\n", + name, (int)kind, (int)val); +#endif + + switch (kind) + { + default: + abort (); + + case DEBUG_PARM_STACK: + class = C_ARG; + break; + + case DEBUG_PARM_REG: + class = C_REGPARM; + break; + + case DEBUG_PARM_REFERENCE: + case DEBUG_PARM_REF_REG: + fprintf (stderr, _("Reference parameters not available in COFF\n")); + return TRUE; + } + + if (!coff_make_typed_symbol (info, &csymp, TS_FUNC)) + return FALSE; + + if (class == C_REGPARM && (info->flags & COFF_FL_AVR) != 0) + { + struct coff_private_symdata *priv = (struct coff_private_symdata *) + csymp->symbol.udata.p; + val = coff_fixup_avr_register (val, priv->size * 8); + } + + csymp->symbol.name = name; + csymp->symbol.value = val; + csymp->symbol.flags |= BSF_LOCAL; + csymp->native->u.syment.n_sclass = class; + + /* Since function parameters precede the actual function definition, + defer their output until the function has been created. */ + info->fargs = (coff_symbol_type **) + xrealloc (info->fargs, ++info->nfargs * sizeof (coff_symbol_type *)); + info->fargs[info->nfargs - 1] = csymp; + + return TRUE; +} + +/* Start a block. */ + +static bfd_boolean +coff_start_block (p, addr) + PTR p; + bfd_vma addr; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + struct coff_type_stack *tst, *otst; + struct coff_fix_stack *fixp, *ofp; + asymbol *symp; + coff_symbol_type *csymp; + unsigned int i; + bfd_boolean is_start_fcn; + +#if COFF_DEBUG + printf ("coff_start_block(%#x)\n", (int)addr); +#endif + + is_start_fcn = info->flags & COFF_FL_START_FCN; + + if (is_start_fcn) + { + /* This is the starting block of a function. We are going to + write three symbols here, one for the function itself, one + ".bf" symbol to indicate the begin of the function, and + finally one ".bb" for the first block inside the function. */ + info->flags &= ~COFF_FL_START_FCN; + + /* Our function definition should be the only type stack element + by now. */ + assert (info->tstack != NULL); + tst = info->tstack; + if (tst->tsk != TS_FUNC || tst->next != NULL) + { + fprintf (stderr, + _("coff_start_block() not within function definition\n")); + return FALSE; + } + + /* Restore saved type stack, and push our now complete function + definition on top. */ + info->tstack = tst->u.ts_func.savedts; + tst->next = info->tstack; + info->tstack = tst; + + if (info->currentfile == NULL) + { + fprintf (stderr, + _("Warning: ignoring function %s() outside any compilation unit\n"), + info->funname); + for (tst = info->tstack, otst = NULL; tst != NULL;) + { + otst = tst; + tst = otst->next; + if (otst->tsk == TS_ENUM && + otst->u.ts_enum.tagismalloced) + free (otst->u.ts_enum.tag.malloctag); + else if (otst->tsk == TS_STRUCT && + otst->u.ts_struct.tagismalloced) + free (otst->u.ts_struct.tag.malloctag); + free (otst); + } + info->tstack = NULL; + info->funname = NULL; + + return TRUE; + } + + if (!coff_make_typed_symbol (info, &csymp, TS_NONE)) + return FALSE; + + csymp->symbol.name = info->funname; + csymp->symbol.flags = BSF_FUNCTION | + (info->funglobal? BSF_GLOBAL: BSF_LOCAL); + symp = coff_find_symbol (info, info->funname, TRUE, info->funglobal); + if (symp == NULL) + { + fprintf (stderr, + _("function %s not found in symbol table, defaulting to \"text\" section\n"), + info->funname); + csymp->symbol.section = info->funcsection = info->textsect; + } + else + csymp->symbol.section = info->funcsection = symp->section; + + /* Symbol addresses are relative to section vma. */ + csymp->symbol.value = addr - info->funcsection->vma; + csymp->native->u.syment.n_sclass = info->funglobal? C_EXT: C_STAT; + /* Create two initial line number entries. The first one holds + the function symbol, the second one is the trailing record + that is required by coffgen.c::coff_write_native_symbol() to + have a line number of zero. */ + csymp->lineno = (alent *) xmalloc (2 * sizeof (alent)); + memset (csymp->lineno, 0, 2 * sizeof (alent)); + info->nlnos = 2; + info->totlnos++; + csymp->lineno[0].u.sym = (asymbol *)csymp; + coff_record_symbol (info, csymp); + info->funcindex = info->nsyms - 1; /* remember for later */ + /* Record our endndx field for later fixing. */ + fixp = (struct coff_fix_stack *) xmalloc (sizeof (struct coff_fix_stack)); + fixp->native = csymp->native + 1; /* points to first AUX */ + fixp->next = NULL; + if (info->fixes == NULL) + info->fixes = fixp; + else + { + for (ofp = info->fixes; ofp->next != NULL;) + ofp = ofp->next; + ofp->next = fixp; + } + + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = ".bf"; + csymp->native->u.syment.n_sclass = C_FCN; + csymp->native->u.syment.n_numaux = 1; + csymp->symbol.value = addr - info->funcsection->vma; + csymp->symbol.section = info->funcsection; + csymp->symbol.udata.p = NULL; + coff_record_symbol (info, csymp); + } + + if (info->funname == NULL) + return TRUE; + + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = ".bb"; + csymp->native->u.syment.n_sclass = C_BLOCK; + csymp->native->u.syment.n_numaux = 1; + csymp->symbol.value = addr - info->funcsection->vma; + csymp->symbol.section = info->funcsection; + csymp->symbol.udata.p = NULL; + coff_record_symbol (info, csymp); + + info->flags |= COFF_FL_FIX_BB; + + /* Output any pending function parameters, if any. */ + if (is_start_fcn && info->nfargs) + { + for (i = 0; i < info->nfargs; i++) + coff_record_symbol (info, info->fargs[i]); + + free (info->fargs); + info->fargs = NULL; + info->nfargs = 0; + } + + return TRUE; +} + +/* End a block. */ + +static bfd_boolean +coff_end_block (p, addr) + PTR p; + bfd_vma addr; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + coff_symbol_type *csymp; + union internal_auxent *aux; + +#if COFF_DEBUG + printf ("coff_end_block(%#x)\n", (int)addr); +#endif + + if (info->funname == NULL) + return TRUE; + + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = ".eb"; + csymp->symbol.value = addr - info->funcsection->vma; + csymp->native->u.syment.n_sclass = C_BLOCK; + csymp->native->u.syment.n_numaux = 1; + csymp->symbol.udata.p = NULL; + csymp->symbol.section = info->funcsection; + aux = &((csymp->native + 1)->u.auxent); + aux->x_sym.x_misc.x_lnsz.x_lnno = info->lastlno; + coff_record_symbol (info, csymp); + + info->endaddr = addr; + + return TRUE; +} + +/* End a function. */ + +static bfd_boolean +coff_end_function (p) + PTR p; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + coff_symbol_type *csymp; + union internal_auxent *aux; + +#if COFF_DEBUG + printf ("coff_end_function()\n"); +#endif + + if (info->funname == NULL) + return TRUE; + + csymp = (coff_symbol_type *) coff_bfd_make_debug_symbol (info->abfd, 0, 0); + if (csymp == NULL) + return FALSE; + + csymp->symbol.name = ".ef"; + csymp->symbol.value = info->endaddr - info->funcsection->vma; + csymp->native->u.syment.n_sclass = C_FCN; + csymp->native->u.syment.n_numaux = 1; + csymp->symbol.udata.p = NULL; + csymp->symbol.section = info->funcsection; + aux = &((csymp->native + 1)->u.auxent); + aux->x_sym.x_misc.x_lnsz.x_lnno = info->lastlno; + + coff_record_symbol (info, csymp); + + csymp = (coff_symbol_type *) info->syms[info->funcindex]; + aux = &((csymp->native + 1)->u.auxent); + aux->x_sym.x_misc.x_fsize = info->endaddr - csymp->symbol.value; + + info->flags |= COFF_FL_FIX_ENDNDX; + info->funname = NULL; + + return TRUE; +} + +/* Output a line number. */ + +static bfd_boolean +coff_lineno (p, file, lineno, addr) + PTR p; + const char *file ATTRIBUTE_UNUSED; + unsigned long lineno; + bfd_vma addr; +{ + struct coff_write_handle *info = (struct coff_write_handle *) p; + coff_symbol_type *csymp; + union internal_auxent *aux; + long i; + +#if COFF_DEBUG + printf ("coff_lineno(%s, %ld, %d)\n", + file, lineno, (int)addr); +#endif + + /* COFF can inherently only handle line numbers inside of functions. + If we are not inside a function, punt. */ + if (info->funname == NULL) + return TRUE; + + if (info->nlnos == 2) + { + /* This is the first line number of this function. Fix the line + number for the .bf symbol immediately following the start of + function. We also have to remember the starting line number + of our function since all line number entries are relative to + it in COFF. Since regular line numbers must always be + non-zero, we artificially force the function to start one + line earlier. */ + csymp = (coff_symbol_type *) info->syms[info->funcindex + 1]; + aux = &((csymp->native + 1)->u.auxent); + aux->x_sym.x_misc.x_lnsz.x_lnno = lineno; + info->funlno = lineno - 1; + } + + if (info->flags & COFF_FL_FIX_BB) + { + /* This is the first line number after one (or more) .bb + symbols. Fix them. In order to cope with multiple blocks + starting at the same line number, we walk back the list of + symbols until we find a C_BLOCK one that had already been + fixed, or until we find a C_FCN symbol (presumably, the start + of our current function). */ + info->flags &= ~COFF_FL_FIX_BB; + + for (i = info->nsyms - 1; i >= 0; i--) + { + csymp = (coff_symbol_type *) info->syms[i]; + if (csymp->native->u.syment.n_sclass == C_FCN) + break; + if (csymp->native->u.syment.n_sclass == C_BLOCK) + { + aux = &((csymp->native + 1)->u.auxent); + if (aux->x_sym.x_misc.x_lnsz.x_lnno != 0) + /* already set up properly */ + break; + aux->x_sym.x_misc.x_lnsz.x_lnno = lineno; + } + } + } + + csymp = (coff_symbol_type *) info->syms[info->funcindex]; + csymp->lineno = (alent *) xrealloc (csymp->lineno, + ++info->nlnos * sizeof (alent)); + memset (csymp->lineno + info->nlnos - 1, 0, sizeof (alent)); + if (lineno > info->funlno) + csymp->lineno[info->nlnos - 2].line_number = lineno - info->funlno; + else + /* Line number unreasonable. Can e. g. happen for a line number + from an include file, which we cannot process in COFF. Just + set it to the first line, to avoid generating a large unsigned + short (~ 65000) line number. */ + csymp->lineno[info->nlnos - 2].line_number = 1; + csymp->lineno[info->nlnos - 2].u.offset = addr; + + info->lastlno = lineno; + info->totlnos++; + + return TRUE; +}