+ /* If we don't have any raw data for this archive, as can happen on
+ Irix 4.0.5F, we call the generic routine.
+ FIXME: We should be more clever about this, since someday tdata
+ may get to something for a generic archive. */
+ raw_armap = (const bfd_byte *) bfd_ardata (abfd)->tdata;
+ if (raw_armap == NULL)
+ return (_bfd_generic_link_add_archive_symbols
+ (abfd, info, ecoff_link_check_archive_element));
+
+ armap_count = H_GET_32 (abfd, raw_armap);
+
+ armap_log = 0;
+ for (i = 1; i < armap_count; i <<= 1)
+ armap_log++;
+ BFD_ASSERT (i == armap_count);
+
+ hashtable = raw_armap + 4;
+ stringbase = (const char *) raw_armap + armap_count * 8 + 8;
+
+ /* Look through the list of undefined symbols. */
+ pundef = &info->hash->undefs;
+ while (*pundef != NULL)
+ {
+ struct bfd_link_hash_entry *h;
+ unsigned int hash, rehash = 0;
+ unsigned int file_offset;
+ const char *name;
+ bfd *element;
+
+ h = *pundef;
+
+ /* When a symbol is defined, it is not necessarily removed from
+ the list. */
+ if (h->type != bfd_link_hash_undefined
+ && h->type != bfd_link_hash_common)
+ {
+ /* Remove this entry from the list, for general cleanliness
+ and because we are going to look through the list again
+ if we search any more libraries. We can't remove the
+ entry if it is the tail, because that would lose any
+ entries we add to the list later on. */
+ if (*pundef != info->hash->undefs_tail)
+ *pundef = (*pundef)->u.undef.next;
+ else
+ pundef = &(*pundef)->u.undef.next;
+ continue;
+ }
+
+ /* Native ECOFF linkers do not pull in archive elements merely
+ to satisfy common definitions, so neither do we. We leave
+ them on the list, though, in case we are linking against some
+ other object format. */
+ if (h->type != bfd_link_hash_undefined)
+ {
+ pundef = &(*pundef)->u.undef.next;
+ continue;
+ }
+
+ /* Look for this symbol in the archive hash table. */
+ hash = ecoff_armap_hash (h->root.string, &rehash, armap_count,
+ armap_log);
+
+ file_offset = H_GET_32 (abfd, hashtable + (hash * 8) + 4);
+ if (file_offset == 0)
+ {
+ /* Nothing in this slot. */
+ pundef = &(*pundef)->u.undef.next;
+ continue;
+ }
+
+ name = stringbase + H_GET_32 (abfd, hashtable + (hash * 8));
+ if (name[0] != h->root.string[0]
+ || ! streq (name, h->root.string))
+ {
+ unsigned int srch;
+ bfd_boolean found;
+
+ /* That was the wrong symbol. Try rehashing. */
+ found = FALSE;
+ for (srch = (hash + rehash) & (armap_count - 1);
+ srch != hash;
+ srch = (srch + rehash) & (armap_count - 1))
+ {
+ file_offset = H_GET_32 (abfd, hashtable + (srch * 8) + 4);
+ if (file_offset == 0)
+ break;
+ name = stringbase + H_GET_32 (abfd, hashtable + (srch * 8));
+ if (name[0] == h->root.string[0]
+ && streq (name, h->root.string))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ pundef = &(*pundef)->u.undef.next;
+ continue;
+ }
+
+ hash = srch;
+ }
+
+ element = (*backend->get_elt_at_filepos) (abfd, (file_ptr) file_offset);
+ if (element == NULL)
+ return FALSE;
+
+ if (! bfd_check_format (element, bfd_object))
+ return FALSE;
+
+ /* Unlike the generic linker, we know that this element provides
+ a definition for an undefined symbol and we know that we want
+ to include it. We don't need to check anything. */
+ if (!(*info->callbacks
+ ->add_archive_element) (info, element, name, &element))
+ return FALSE;
+ if (! ecoff_link_add_object_symbols (element, info))
+ return FALSE;
+
+ pundef = &(*pundef)->u.undef.next;
+ }
+
+ return TRUE;
+}
+
+/* Given an ECOFF BFD, add symbols to the global hash table as
+ appropriate. */
+
+bfd_boolean
+_bfd_ecoff_bfd_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
+{
+ switch (bfd_get_format (abfd))
+ {
+ case bfd_object:
+ return ecoff_link_add_object_symbols (abfd, info);
+ case bfd_archive:
+ return ecoff_link_add_archive_symbols (abfd, info);
+ default:
+ bfd_set_error (bfd_error_wrong_format);
+ return FALSE;
+ }
+}
+
+\f
+/* ECOFF final link routines. */
+
+/* Structure used to pass information to ecoff_link_write_external. */
+
+struct extsym_info
+{
+ bfd *abfd;
+ struct bfd_link_info *info;
+};
+
+/* Accumulate the debugging information for an input BFD into the
+ output BFD. This must read in the symbolic information of the
+ input BFD. */
+
+static bfd_boolean
+ecoff_final_link_debug_accumulate (bfd *output_bfd,
+ bfd *input_bfd,
+ struct bfd_link_info *info,
+ void * handle)
+{
+ struct ecoff_debug_info * const debug = &ecoff_data (input_bfd)->debug_info;
+ const struct ecoff_debug_swap * const swap =
+ &ecoff_backend (input_bfd)->debug_swap;
+ HDRR *symhdr = &debug->symbolic_header;
+ bfd_boolean ret;
+
+#define READ(ptr, offset, count, size, type) \
+ if (symhdr->count == 0) \
+ debug->ptr = NULL; \
+ else \
+ { \
+ bfd_size_type amt = (bfd_size_type) size * symhdr->count; \
+ debug->ptr = (type) bfd_malloc (amt); \
+ if (debug->ptr == NULL) \
+ { \
+ ret = FALSE; \
+ goto return_something; \
+ } \
+ if (bfd_seek (input_bfd, (file_ptr) symhdr->offset, SEEK_SET) != 0 \
+ || bfd_bread (debug->ptr, amt, input_bfd) != amt) \
+ { \
+ ret = FALSE; \
+ goto return_something; \
+ } \
+ }
+
+ /* If raw_syments is not NULL, then the data was already by read by
+ _bfd_ecoff_slurp_symbolic_info. */
+ if (ecoff_data (input_bfd)->raw_syments == NULL)
+ {
+ READ (line, cbLineOffset, cbLine, sizeof (unsigned char),
+ unsigned char *);
+ READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, void *);
+ READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, void *);
+ READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, void *);
+ READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, void *);
+ READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
+ union aux_ext *);
+ READ (ss, cbSsOffset, issMax, sizeof (char), char *);
+ READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, void *);
+ READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, void *);
+ }
+#undef READ
+
+ /* We do not read the external strings or the external symbols. */
+
+ ret = (bfd_ecoff_debug_accumulate
+ (handle, output_bfd, &ecoff_data (output_bfd)->debug_info,
+ &ecoff_backend (output_bfd)->debug_swap,
+ input_bfd, debug, swap, info));
+
+ return_something:
+ if (ecoff_data (input_bfd)->raw_syments == NULL)
+ {
+ if (debug->line != NULL)
+ free (debug->line);
+ if (debug->external_dnr != NULL)
+ free (debug->external_dnr);
+ if (debug->external_pdr != NULL)
+ free (debug->external_pdr);
+ if (debug->external_sym != NULL)
+ free (debug->external_sym);
+ if (debug->external_opt != NULL)
+ free (debug->external_opt);
+ if (debug->external_aux != NULL)
+ free (debug->external_aux);
+ if (debug->ss != NULL)
+ free (debug->ss);
+ if (debug->external_fdr != NULL)
+ free (debug->external_fdr);
+ if (debug->external_rfd != NULL)
+ free (debug->external_rfd);
+
+ /* Make sure we don't accidentally follow one of these pointers
+ into freed memory. */
+ debug->line = NULL;
+ debug->external_dnr = NULL;
+ debug->external_pdr = NULL;
+ debug->external_sym = NULL;
+ debug->external_opt = NULL;
+ debug->external_aux = NULL;
+ debug->ss = NULL;
+ debug->external_fdr = NULL;
+ debug->external_rfd = NULL;
+ }
+
+ return ret;
+}
+
+/* Relocate and write an ECOFF section into an ECOFF output file. */
+
+static bfd_boolean
+ecoff_indirect_link_order (bfd *output_bfd,
+ struct bfd_link_info *info,
+ asection *output_section,
+ struct bfd_link_order *link_order)
+{
+ asection *input_section;
+ bfd *input_bfd;
+ bfd_byte *contents = NULL;
+ bfd_size_type external_reloc_size;
+ bfd_size_type external_relocs_size;
+ void * external_relocs = NULL;
+
+ BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0);
+
+ input_section = link_order->u.indirect.section;
+ input_bfd = input_section->owner;
+ if (input_section->size == 0)
+ return TRUE;
+
+ BFD_ASSERT (input_section->output_section == output_section);
+ BFD_ASSERT (input_section->output_offset == link_order->offset);
+ BFD_ASSERT (input_section->size == link_order->size);
+
+ /* Get the section contents. */
+ if (!bfd_malloc_and_get_section (input_bfd, input_section, &contents))
+ goto error_return;
+
+ /* Get the relocs. If we are relaxing MIPS code, they will already
+ have been read in. Otherwise, we read them in now. */
+ external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size;
+ external_relocs_size = external_reloc_size * input_section->reloc_count;
+
+ external_relocs = bfd_malloc (external_relocs_size);
+ if (external_relocs == NULL && external_relocs_size != 0)
+ goto error_return;
+
+ if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
+ || (bfd_bread (external_relocs, external_relocs_size, input_bfd)
+ != external_relocs_size))
+ goto error_return;
+
+ /* Relocate the section contents. */
+ if (! ((*ecoff_backend (input_bfd)->relocate_section)
+ (output_bfd, info, input_bfd, input_section, contents,
+ external_relocs)))
+ goto error_return;
+
+ /* Write out the relocated section. */
+ if (! bfd_set_section_contents (output_bfd,
+ output_section,
+ contents,
+ input_section->output_offset,
+ input_section->size))
+ goto error_return;
+
+ /* If we are producing relocatable output, the relocs were
+ modified, and we write them out now. We use the reloc_count
+ field of output_section to keep track of the number of relocs we
+ have output so far. */
+ if (info->relocatable)
+ {
+ file_ptr pos = (output_section->rel_filepos
+ + output_section->reloc_count * external_reloc_size);
+ if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+ || (bfd_bwrite (external_relocs, external_relocs_size, output_bfd)
+ != external_relocs_size))
+ goto error_return;
+ output_section->reloc_count += input_section->reloc_count;
+ }
+
+ if (contents != NULL)
+ free (contents);
+ if (external_relocs != NULL)
+ free (external_relocs);
+ return TRUE;
+
+ error_return:
+ if (contents != NULL)
+ free (contents);
+ if (external_relocs != NULL)
+ free (external_relocs);
+ return FALSE;
+}
+
+/* Generate a reloc when linking an ECOFF file. This is a reloc
+ requested by the linker, and does come from any input file. This
+ is used to build constructor and destructor tables when linking
+ with -Ur. */
+
+static bfd_boolean
+ecoff_reloc_link_order (bfd *output_bfd,
+ struct bfd_link_info *info,
+ asection *output_section,
+ struct bfd_link_order *link_order)
+{
+ enum bfd_link_order_type type;
+ asection *section;
+ bfd_vma addend;
+ arelent rel;
+ struct internal_reloc in;
+ bfd_size_type external_reloc_size;
+ bfd_byte *rbuf;
+ bfd_boolean ok;
+ file_ptr pos;
+
+ type = link_order->type;
+ section = NULL;
+ addend = link_order->u.reloc.p->addend;
+
+ /* We set up an arelent to pass to the backend adjust_reloc_out
+ routine. */
+ rel.address = link_order->offset;
+
+ rel.howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc);
+ if (rel.howto == 0)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ if (type == bfd_section_reloc_link_order)
+ {
+ section = link_order->u.reloc.p->u.section;
+ rel.sym_ptr_ptr = section->symbol_ptr_ptr;
+ }
+ else
+ {
+ struct bfd_link_hash_entry *h;
+
+ /* Treat a reloc against a defined symbol as though it were
+ actually against the section. */
+ h = bfd_wrapped_link_hash_lookup (output_bfd, info,
+ link_order->u.reloc.p->u.name,
+ FALSE, FALSE, FALSE);
+ if (h != NULL
+ && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak))