+
+/* Put out information for an external symbol. These come only from
+ the hash table. */
+
+static bfd_boolean
+ecoff_link_write_external (struct bfd_hash_entry *bh, void * data)
+{
+ struct ecoff_link_hash_entry *h = (struct ecoff_link_hash_entry *) bh;
+ struct extsym_info *einfo = (struct extsym_info *) data;
+ bfd *output_bfd = einfo->abfd;
+ bfd_boolean strip;
+
+ if (h->root.type == bfd_link_hash_warning)
+ {
+ h = (struct ecoff_link_hash_entry *) h->root.u.i.link;
+ if (h->root.type == bfd_link_hash_new)
+ return TRUE;
+ }
+
+ /* We need to check if this symbol is being stripped. */
+ if (h->root.type == bfd_link_hash_undefined
+ || h->root.type == bfd_link_hash_undefweak)
+ strip = FALSE;
+ else if (einfo->info->strip == strip_all
+ || (einfo->info->strip == strip_some
+ && bfd_hash_lookup (einfo->info->keep_hash,
+ h->root.root.string,
+ FALSE, FALSE) == NULL))
+ strip = TRUE;
+ else
+ strip = FALSE;
+
+ if (strip || h->written)
+ return TRUE;
+
+ if (h->abfd == NULL)
+ {
+ h->esym.jmptbl = 0;
+ h->esym.cobol_main = 0;
+ h->esym.weakext = 0;
+ h->esym.reserved = 0;
+ h->esym.ifd = ifdNil;
+ h->esym.asym.value = 0;
+ h->esym.asym.st = stGlobal;
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ h->esym.asym.sc = scAbs;
+ else
+ {
+ asection *output_section;
+ const char *name;
+ unsigned int i;
+ static struct
+ {
+ const char * name;
+ int sc;
+ }
+ section_storage_classes [] =
+ {
+ { _TEXT, scText },
+ { _DATA, scData },
+ { _SDATA, scSData },
+ { _RDATA, scRData },
+ { _BSS, scBss },
+ { _SBSS, scSBss },
+ { _INIT, scInit },
+ { _FINI, scFini },
+ { _PDATA, scPData },
+ { _XDATA, scXData },
+ { _RCONST, scRConst }
+ };
+
+ output_section = h->root.u.def.section->output_section;
+ name = bfd_section_name (output_section->owner, output_section);
+
+ for (i = 0; i < ARRAY_SIZE (section_storage_classes); i++)
+ if (streq (name, section_storage_classes[i].name))
+ {
+ h->esym.asym.sc = section_storage_classes[i].sc;
+ break;
+ }
+
+ if (i == ARRAY_SIZE (section_storage_classes))
+ h->esym.asym.sc = scAbs;
+ }
+
+ h->esym.asym.reserved = 0;
+ h->esym.asym.index = indexNil;
+ }
+ else if (h->esym.ifd != -1)
+ {
+ struct ecoff_debug_info *debug;
+
+ /* Adjust the FDR index for the symbol by that used for the
+ input BFD. */
+ debug = &ecoff_data (h->abfd)->debug_info;
+ BFD_ASSERT (h->esym.ifd >= 0
+ && h->esym.ifd < debug->symbolic_header.ifdMax);
+ h->esym.ifd = debug->ifdmap[h->esym.ifd];
+ }
+
+ switch (h->root.type)
+ {
+ default:
+ case bfd_link_hash_warning:
+ case bfd_link_hash_new:
+ abort ();
+ case bfd_link_hash_undefined:
+ case bfd_link_hash_undefweak:
+ if (h->esym.asym.sc != scUndefined
+ && h->esym.asym.sc != scSUndefined)
+ h->esym.asym.sc = scUndefined;
+ break;
+ case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
+ if (h->esym.asym.sc == scUndefined
+ || h->esym.asym.sc == scSUndefined)
+ h->esym.asym.sc = scAbs;
+ else if (h->esym.asym.sc == scCommon)
+ h->esym.asym.sc = scBss;
+ else if (h->esym.asym.sc == scSCommon)
+ h->esym.asym.sc = scSBss;
+ h->esym.asym.value = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ break;
+ case bfd_link_hash_common:
+ if (h->esym.asym.sc != scCommon
+ && h->esym.asym.sc != scSCommon)
+ h->esym.asym.sc = scCommon;
+ h->esym.asym.value = h->root.u.c.size;
+ break;
+ case bfd_link_hash_indirect:
+ /* We ignore these symbols, since the indirected symbol is
+ already in the hash table. */
+ return TRUE;
+ }
+
+ /* bfd_ecoff_debug_one_external uses iextMax to keep track of the
+ symbol number. */
+ h->indx = ecoff_data (output_bfd)->debug_info.symbolic_header.iextMax;
+ h->written = 1;
+
+ return (bfd_ecoff_debug_one_external
+ (output_bfd, &ecoff_data (output_bfd)->debug_info,
+ &ecoff_backend (output_bfd)->debug_swap, h->root.root.string,
+ &h->esym));
+}
+
+/* ECOFF final link routine. This looks through all the input BFDs
+ and gathers together all the debugging information, and then
+ processes all the link order information. This may cause it to
+ close and reopen some input BFDs; I'll see how bad this is. */
+
+bfd_boolean
+_bfd_ecoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ struct ecoff_debug_info * const debug = &ecoff_data (abfd)->debug_info;
+ HDRR *symhdr;
+ void * handle;
+ bfd *input_bfd;
+ asection *o;
+ struct bfd_link_order *p;
+ struct extsym_info einfo;
+
+ /* We accumulate the debugging information counts in the symbolic
+ header. */
+ symhdr = &debug->symbolic_header;
+ symhdr->vstamp = 0;
+ symhdr->ilineMax = 0;
+ symhdr->cbLine = 0;
+ symhdr->idnMax = 0;
+ symhdr->ipdMax = 0;
+ symhdr->isymMax = 0;
+ symhdr->ioptMax = 0;
+ symhdr->iauxMax = 0;
+ symhdr->issMax = 0;
+ symhdr->issExtMax = 0;
+ symhdr->ifdMax = 0;
+ symhdr->crfd = 0;
+ symhdr->iextMax = 0;
+
+ /* We accumulate the debugging information itself in the debug_info
+ structure. */
+ 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->ssext = debug->ssext_end = NULL;
+ debug->external_fdr = NULL;
+ debug->external_rfd = NULL;
+ debug->external_ext = debug->external_ext_end = NULL;
+
+ handle = bfd_ecoff_debug_init (abfd, debug, &backend->debug_swap, info);
+ if (handle == NULL)
+ return FALSE;
+
+ /* Accumulate the debugging symbols from each input BFD. */
+ for (input_bfd = info->input_bfds;
+ input_bfd != NULL;
+ input_bfd = input_bfd->link.next)
+ {
+ bfd_boolean ret;
+
+ if (bfd_get_flavour (input_bfd) == bfd_target_ecoff_flavour)
+ {
+ /* Arbitrarily set the symbolic header vstamp to the vstamp
+ of the first object file in the link. */
+ if (symhdr->vstamp == 0)
+ symhdr->vstamp
+ = ecoff_data (input_bfd)->debug_info.symbolic_header.vstamp;
+ ret = ecoff_final_link_debug_accumulate (abfd, input_bfd, info,
+ handle);
+ }
+ else
+ ret = bfd_ecoff_debug_accumulate_other (handle, abfd,
+ debug, &backend->debug_swap,
+ input_bfd, info);
+ if (! ret)
+ return FALSE;
+
+ /* Combine the register masks. */
+ ecoff_data (abfd)->gprmask |= ecoff_data (input_bfd)->gprmask;
+ ecoff_data (abfd)->fprmask |= ecoff_data (input_bfd)->fprmask;
+ ecoff_data (abfd)->cprmask[0] |= ecoff_data (input_bfd)->cprmask[0];
+ ecoff_data (abfd)->cprmask[1] |= ecoff_data (input_bfd)->cprmask[1];
+ ecoff_data (abfd)->cprmask[2] |= ecoff_data (input_bfd)->cprmask[2];
+ ecoff_data (abfd)->cprmask[3] |= ecoff_data (input_bfd)->cprmask[3];
+ }
+
+ /* Write out the external symbols. */
+ einfo.abfd = abfd;
+ einfo.info = info;
+ bfd_hash_traverse (&info->hash->table, ecoff_link_write_external, &einfo);
+
+ if (info->relocatable)
+ {
+ /* We need to make a pass over the link_orders to count up the
+ number of relocations we will need to output, so that we know
+ how much space they will take up. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ o->reloc_count = 0;
+ for (p = o->map_head.link_order;
+ p != NULL;
+ p = p->next)
+ if (p->type == bfd_indirect_link_order)
+ o->reloc_count += p->u.indirect.section->reloc_count;
+ else if (p->type == bfd_section_reloc_link_order
+ || p->type == bfd_symbol_reloc_link_order)
+ ++o->reloc_count;
+ }
+ }
+
+ /* Compute the reloc and symbol file positions. */
+ ecoff_compute_reloc_file_positions (abfd);
+
+ /* Write out the debugging information. */
+ if (! bfd_ecoff_write_accumulated_debug (handle, abfd, debug,
+ &backend->debug_swap, info,
+ ecoff_data (abfd)->sym_filepos))
+ return FALSE;
+
+ bfd_ecoff_debug_free (handle, abfd, debug, &backend->debug_swap, info);
+
+ if (info->relocatable)
+ {
+ /* Now reset the reloc_count field of the sections in the output
+ BFD to 0, so that we can use them to keep track of how many
+ relocs we have output thus far. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ o->reloc_count = 0;
+ }
+
+ /* Get a value for the GP register. */
+ if (ecoff_data (abfd)->gp == 0)
+ {
+ struct bfd_link_hash_entry *h;
+
+ h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
+ if (h != NULL
+ && h->type == bfd_link_hash_defined)
+ ecoff_data (abfd)->gp = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ else if (info->relocatable)
+ {
+ bfd_vma lo;
+
+ /* Make up a value. */
+ lo = (bfd_vma) -1;
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ if (o->vma < lo
+ && (streq (o->name, _SBSS)
+ || streq (o->name, _SDATA)
+ || streq (o->name, _LIT4)
+ || streq (o->name, _LIT8)
+ || streq (o->name, _LITA)))
+ lo = o->vma;
+ }
+ ecoff_data (abfd)->gp = lo + 0x8000;
+ }
+ else
+ {
+ /* If the relocate_section function needs to do a reloc
+ involving the GP value, it should make a reloc_dangerous
+ callback to warn that GP is not defined. */
+ }
+ }
+
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ for (p = o->map_head.link_order;
+ p != NULL;
+ p = p->next)
+ {
+ if (p->type == bfd_indirect_link_order
+ && (bfd_get_flavour (p->u.indirect.section->owner)
+ == bfd_target_ecoff_flavour))
+ {
+ if (! ecoff_indirect_link_order (abfd, info, o, p))
+ return FALSE;
+ }
+ else if (p->type == bfd_section_reloc_link_order
+ || p->type == bfd_symbol_reloc_link_order)
+ {
+ if (! ecoff_reloc_link_order (abfd, info, o, p))
+ return FALSE;
+ }
+ else
+ {
+ if (! _bfd_default_link_order (abfd, info, o, p))
+ return FALSE;
+ }
+ }
+ }
+
+ bfd_get_symcount (abfd) = symhdr->iextMax + symhdr->isymMax;
+
+ ecoff_data (abfd)->linker = TRUE;
+
+ return TRUE;
+}