From 5c6725cf5985ece328806c371196dccdc8d51ee5 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 10 Oct 1995 22:47:49 +0000 Subject: [PATCH] * cofflink.c (_bfd_coff_link_hash_newfunc): Rename from coff_link_hash_newfunc and make non-static. (_bfd_coff_link_hash_table_init): New function, broken out of _bfd_coff_link_hash_table_create. (_bfd_coff_link_hash_table_create): Use it. (process_embedded_commands): Make static. * libcoff-in.h ((_bfd_coff_link_hash_newfunc): Declare. (_bfd_coff_link_hash_table_init): Declare. * libcoff-in.h (struct xcoff_tdata): Add cputype field. * libcoff.h: Rebuild. --- bfd/cofflink.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 314 insertions(+), 14 deletions(-) diff --git a/bfd/cofflink.c b/bfd/cofflink.c index 81cefb02f43..ab1685f8a4e 100644 --- a/bfd/cofflink.c +++ b/bfd/cofflink.c @@ -29,6 +29,77 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define STRING_SIZE_SIZE (4) +/* We use a hash table to merge identical enum, struct, and union + definitions in the linker. */ + +/* Information we keep for a single element (an enum value, a + structure or union field) in the debug merge hash table. */ + +struct coff_debug_merge_element +{ + /* Next element. */ + struct coff_debug_merge_element *next; + + /* Name. */ + const char *name; + + /* Type. */ + unsigned int type; + + /* Symbol index for complex type. */ + long tagndx; +}; + +/* A linked list of debug merge entries for a given name. */ + +struct coff_debug_merge_type +{ + /* Next type with the same name. */ + struct coff_debug_merge_type *next; + + /* Class of type. */ + int class; + + /* Symbol index where this type is defined. */ + long indx; + + /* List of elements. */ + struct coff_debug_merge_element *elements; +}; + +/* Information we store in the debug merge hash table. */ + +struct coff_debug_merge_hash_entry +{ + struct bfd_hash_entry root; + + /* A list of types with this name. */ + struct coff_debug_merge_type *types; +}; + +/* The debug merge hash table. */ + +struct coff_debug_merge_hash_table +{ + struct bfd_hash_table root; +}; + +/* Initialize a COFF debug merge hash table. */ + +#define coff_debug_merge_hash_table_init(table) \ + (bfd_hash_table_init (&(table)->root, coff_debug_merge_hash_newfunc)) + +/* Free a COFF debug merge hash table. */ + +#define coff_debug_merge_hash_table_free(table) \ + (bfd_hash_table_free (&(table)->root)) + +/* Look up an entry in a COFF debug merge hash table. */ + +#define coff_debug_merge_hash_lookup(table, string, create, copy) \ + ((struct coff_debug_merge_hash_entry *) \ + bfd_hash_lookup (&(table)->root, (string), (create), (copy))) + /* Information we keep for each section in the output file when doing a relocateable link. */ @@ -51,7 +122,7 @@ struct coff_final_link_info bfd *output_bfd; /* Used to indicate failure in traversal routine. */ boolean failed; - /* Hash table for long symbol name. */ + /* Hash table for long symbol names. */ struct bfd_strtab_hash *strtab; /* When doing a relocateable link, an array of information kept for each output section, indexed by the target_index field. */ @@ -60,6 +131,8 @@ struct coff_final_link_info long last_file_index; /* Contents of last C_FILE symbol. */ struct internal_syment last_file; + /* Hash table used to merge debug information. */ + struct coff_debug_merge_hash_table debug_merge; /* Buffer large enough to hold swapped symbols of any input file. */ struct internal_syment *internal_syms; /* Buffer large enough to hold sections of symbols of any input file. */ @@ -78,10 +151,9 @@ struct coff_final_link_info bfd_byte *external_relocs; /* Buffer large enough to hold swapped relocs of any input section. */ struct internal_reloc *internal_relocs; - }; -static struct bfd_hash_entry *coff_link_hash_newfunc +static struct bfd_hash_entry *coff_debug_merge_hash_newfunc PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); static boolean coff_link_add_object_symbols PARAMS ((bfd *, struct bfd_link_info *)); @@ -100,8 +172,8 @@ static boolean coff_reloc_link_order /* Create an entry in a COFF linker hash table. */ -static struct bfd_hash_entry * -coff_link_hash_newfunc (entry, table, string) +struct bfd_hash_entry * +_bfd_coff_link_hash_newfunc (entry, table, string) struct bfd_hash_entry *entry; struct bfd_hash_table *table; const char *string; @@ -137,6 +209,19 @@ coff_link_hash_newfunc (entry, table, string) return (struct bfd_hash_entry *) ret; } +/* Initialize a COFF linker hash table. */ + +boolean +_bfd_coff_link_hash_table_init (table, abfd, newfunc) + struct coff_link_hash_table *table; + bfd *abfd; + struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *)); +{ + return _bfd_link_hash_table_init (&table->root, abfd, newfunc); +} + /* Create a COFF linker hash table. */ struct bfd_link_hash_table * @@ -152,15 +237,50 @@ _bfd_coff_link_hash_table_create (abfd) bfd_set_error (bfd_error_no_memory); return NULL; } - if (! _bfd_link_hash_table_init (&ret->root, abfd, - coff_link_hash_newfunc)) + if (! _bfd_coff_link_hash_table_init (ret, abfd, + _bfd_coff_link_hash_newfunc)) { - free (ret); + bfd_release (abfd, ret); return (struct bfd_link_hash_table *) NULL; } return &ret->root; } +/* Create an entry in a COFF debug merge hash table. */ + +static struct bfd_hash_entry * +coff_debug_merge_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct coff_debug_merge_hash_entry *ret = + (struct coff_debug_merge_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct coff_debug_merge_hash_entry *) NULL) + ret = ((struct coff_debug_merge_hash_entry *) + bfd_hash_allocate (table, + sizeof (struct coff_debug_merge_hash_entry))); + if (ret == (struct coff_debug_merge_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return (struct bfd_hash_entry *) ret; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct coff_debug_merge_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + if (ret != (struct coff_debug_merge_hash_entry *) NULL) + { + /* Set local fields. */ + ret->types = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + /* Given a COFF BFD, add symbols to the global hash table as appropriate. */ @@ -477,8 +597,7 @@ coff_link_add_symbols (abfd, info) return true; } - - + /* Do the final link step. */ boolean @@ -488,6 +607,7 @@ _bfd_coff_final_link (abfd, info) { bfd_size_type symesz; struct coff_final_link_info finfo; + boolean debug_merge_allocated; asection *o; struct bfd_link_order *p; size_t max_contents_size; @@ -518,6 +638,7 @@ _bfd_coff_final_link (abfd, info) finfo.contents = NULL; finfo.external_relocs = NULL; finfo.internal_relocs = NULL; + debug_merge_allocated = false; coff_data (abfd)->link_info = info; @@ -525,6 +646,10 @@ _bfd_coff_final_link (abfd, info) if (finfo.strtab == NULL) goto error_return; + if (! coff_debug_merge_hash_table_init (&finfo.debug_merge)) + goto error_return; + debug_merge_allocated = true; + /* Compute the file positions for all the sections. */ if (! abfd->output_has_begun) bfd_coff_compute_section_file_positions (abfd); @@ -746,6 +871,10 @@ _bfd_coff_final_link (abfd, info) } /* Free up the buffers used by coff_link_input_bfd. */ + + coff_debug_merge_hash_table_free (&finfo.debug_merge); + debug_merge_allocated = false; + if (finfo.internal_syms != NULL) { free (finfo.internal_syms); @@ -905,6 +1034,8 @@ _bfd_coff_final_link (abfd, info) return true; error_return: + if (debug_merge_allocated) + coff_debug_merge_hash_table_free (&finfo.debug_merge); if (finfo.strtab != NULL) _bfd_stringtab_free (finfo.strtab); if (finfo.section_info != NULL) @@ -1047,8 +1178,6 @@ _bfd_coff_read_internal_relocs (abfd, sec, cache, external_relocs, return NULL; } - - /* parse out a -heap , line */ static char * @@ -1089,9 +1218,10 @@ char **dst; *ptr = 0; return ptr+1; } + /* Process any magic embedded commands in a section called .drectve */ -int +static int process_embedded_commands (output_bfd, info, abfd) bfd *output_bfd; struct bfd_link_info *info; @@ -1338,6 +1468,175 @@ coff_link_input_bfd (finfo, input_bfd) skip = true; } + /* If this is an enum, struct, or union tag, see if we have + already output an identical type. */ + if (! skip + && (finfo->output_bfd->flags & BFD_TRADITIONAL_FORMAT) == 0 + && (isym.n_sclass == C_ENTAG + || isym.n_sclass == C_STRTAG + || isym.n_sclass == C_UNTAG) + && isym.n_numaux == 1) + { + const char *name; + char buf[SYMNMLEN + 1]; + struct coff_debug_merge_hash_entry *mh; + struct coff_debug_merge_type *mt; + union internal_auxent aux; + struct coff_debug_merge_element **epp; + bfd_byte *esl, *eslend; + struct internal_syment *islp; + struct coff_debug_merge_type *mtl; + + name = _bfd_coff_internal_syment_name (input_bfd, &isym, buf); + if (name == NULL) + return false; + + /* Ignore fake names invented by compiler; treat them all as + the same name. */ + if (*name == '~' || *name == '.' + || (*name == bfd_get_symbol_leading_char (input_bfd) + && (name[1] == '~' || name[1] == '.'))) + name = ""; + + mh = coff_debug_merge_hash_lookup (&finfo->debug_merge, name, + true, true); + if (mh == NULL) + return false; + + /* Allocate memory to hold type information. If this turns + out to be a duplicate, we pass this address to + bfd_release. */ + mt = ((struct coff_debug_merge_type *) + bfd_alloc (input_bfd, + sizeof (struct coff_debug_merge_type))); + if (mt == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + mt->class = isym.n_sclass; + + /* Pick up the aux entry, which points to the end of the tag + entries. */ + bfd_coff_swap_aux_in (input_bfd, (PTR) (esym + isymesz), + isym.n_type, isym.n_sclass, 0, isym.n_numaux, + (PTR) &aux); + + /* Gather the elements. */ + epp = &mt->elements; + mt->elements = NULL; + islp = isymp + 2; + esl = esym + 2 * isymesz; + eslend = ((bfd_byte *) obj_coff_external_syms (input_bfd) + + aux.x_sym.x_fcnary.x_fcn.x_endndx.l * isymesz); + while (esl < eslend) + { + const char *elename; + char elebuf[SYMNMLEN + 1]; + char *copy; + + bfd_coff_swap_sym_in (input_bfd, (PTR) esl, (PTR) islp); + + *epp = ((struct coff_debug_merge_element *) + bfd_alloc (input_bfd, + sizeof (struct coff_debug_merge_element))); + if (*epp == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + elename = _bfd_coff_internal_syment_name (input_bfd, islp, + elebuf); + if (elename == NULL) + return false; + + copy = (char *) bfd_alloc (input_bfd, strlen (elename) + 1); + if (copy == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + strcpy (copy, elename); + + (*epp)->name = copy; + (*epp)->type = islp->n_type; + (*epp)->tagndx = 0; + if (islp->n_numaux >= 1 + && islp->n_type != T_NULL + && islp->n_sclass != C_EOS) + { + union internal_auxent eleaux; + long indx; + + bfd_coff_swap_aux_in (input_bfd, (PTR) (esl + isymesz), + islp->n_type, islp->n_sclass, 0, + islp->n_numaux, (PTR) &eleaux); + indx = eleaux.x_sym.x_tagndx.l; + + /* FIXME: If this tagndx entry refers to a symbol + defined later in this file, we just ignore it. + Handling this correctly would be tedious, and may + not be required. */ + + if (indx > 0 + && (indx + < ((esym - + (bfd_byte *) obj_coff_external_syms (input_bfd)) + / (long) isymesz))) + { + (*epp)->tagndx = finfo->sym_indices[indx]; + if ((*epp)->tagndx < 0) + (*epp)->tagndx = 0; + } + } + epp = &(*epp)->next; + *epp = NULL; + + esl += (islp->n_numaux + 1) * isymesz; + islp += islp->n_numaux + 1; + } + + /* See if we already have a definition which matches this + type. */ + for (mtl = mh->types; mtl != NULL; mtl = mtl->next) + { + struct coff_debug_merge_element *me, *mel; + + if (mtl->class != mt->class) + continue; + + for (me = mt->elements, mel = mtl->elements; + me != NULL && mel != NULL; + me = me->next, mel = mel->next) + { + if (strcmp (me->name, mel->name) != 0 + || me->type != mel->type + || me->tagndx != mel->tagndx) + break; + } + + if (me == NULL && mel == NULL) + break; + } + + if (mtl == NULL || (bfd_size_type) mtl->indx >= syment_base) + { + /* This is the first definition of this type. */ + mt->indx = output_index; + mt->next = mh->types; + mh->types = mt; + } + else + { + /* This is a redefinition which can be merged. */ + bfd_release (input_bfd, (PTR) mt); + *indexp = mtl->indx; + add = (eslend - esym) / isymesz; + skip = true; + } + } + /* We now know whether we are to skip this symbol or not. */ if (! skip) { @@ -1469,7 +1768,8 @@ coff_link_input_bfd (finfo, input_bfd) add = 1 + isymp->n_numaux; - if (*indexp < 0 + if ((*indexp < 0 + || (bfd_size_type) *indexp < syment_base) && (*sym_hash == NULL || (*sym_hash)->auxbfd != input_bfd)) esym += add * isymesz; -- 2.30.2