+\f
+/* ECOFF linker code. */
+
+static struct bfd_hash_entry *ecoff_link_hash_newfunc
+ PARAMS ((struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string));
+static boolean ecoff_link_add_archive_symbols
+ PARAMS ((bfd *, struct bfd_link_info *));
+static boolean ecoff_link_check_archive_element
+ PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded));
+static boolean ecoff_link_add_object_symbols
+ PARAMS ((bfd *, struct bfd_link_info *));
+static boolean ecoff_link_add_externals
+ PARAMS ((bfd *, struct bfd_link_info *, PTR, char *));
+
+/* Routine to create an entry in an ECOFF link hash table. */
+
+static struct bfd_hash_entry *
+ecoff_link_hash_newfunc (entry, table, string)
+ struct bfd_hash_entry *entry;
+ struct bfd_hash_table *table;
+ const char *string;
+{
+ struct ecoff_link_hash_entry *ret = (struct ecoff_link_hash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (ret == (struct ecoff_link_hash_entry *) NULL)
+ ret = ((struct ecoff_link_hash_entry *)
+ bfd_hash_allocate (table, sizeof (struct ecoff_link_hash_entry)));
+
+ /* Call the allocation method of the superclass. */
+ ret = ((struct ecoff_link_hash_entry *)
+ _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+ table, string));
+
+ /* Set local fields. */
+ ret->indx = -1;
+ ret->abfd = NULL;
+ memset (&ret->esym, 0, sizeof ret->esym);
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+/* Create an ECOFF link hash table. */
+
+struct bfd_link_hash_table *
+ecoff_bfd_link_hash_table_create (abfd)
+ bfd *abfd;
+{
+ struct ecoff_link_hash_table *ret;
+
+ ret = ((struct ecoff_link_hash_table *)
+ bfd_xmalloc (sizeof (struct ecoff_link_hash_table)));
+ if (! _bfd_link_hash_table_init (&ret->root, abfd,
+ ecoff_link_hash_newfunc))
+ {
+ free (ret);
+ return (struct bfd_link_hash_table *) NULL;
+ }
+ return &ret->root;
+}
+
+/* Look up an entry in an ECOFF link hash table. */
+
+#define ecoff_link_hash_lookup(table, string, create, copy, follow) \
+ ((struct ecoff_link_hash_entry *) \
+ bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow)))
+
+/* Traverse an ECOFF link hash table. */
+
+#define ecoff_link_hash_traverse(table, func, info) \
+ (bfd_link_hash_traverse \
+ (&(table)->root, \
+ (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
+ (info)))
+
+/* Get the ECOFF link hash table from the info structure. This is
+ just a cast. */
+
+#define ecoff_hash_table(p) ((struct ecoff_link_hash_table *) ((p)->hash))
+
+/* Given an ECOFF BFD, add symbols to the global hash table as
+ appropriate. */
+
+boolean
+ecoff_bfd_link_add_symbols (abfd, info)
+ 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_error = wrong_format;
+ return false;
+ }
+}
+
+/* Add the symbols from an archive file to the global hash table.
+ This looks through the undefined symbols, looks each one up in the
+ archive hash table, and adds any associated object file. We do not
+ use _bfd_generic_link_add_archive_symbols because ECOFF archives
+ already have a hash table, so there is no reason to construct
+ another one. */
+
+static boolean
+ecoff_link_add_archive_symbols (abfd, info)
+ bfd *abfd;
+ struct bfd_link_info *info;
+{
+ const bfd_byte *raw_armap;
+ struct bfd_link_hash_entry **pundef;
+ unsigned int armap_count;
+ unsigned int armap_log;
+ unsigned int i;
+ const bfd_byte *hashtable;
+ const char *stringbase;
+
+ if (! bfd_has_map (abfd))
+ {
+ bfd_error = no_symbols;
+ return false;
+ }
+
+ /* 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 == (bfd_byte *) NULL)
+ return (_bfd_generic_link_add_archive_symbols
+ (abfd, info, ecoff_link_check_archive_element));
+
+ armap_count = bfd_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 != (struct bfd_link_hash_entry *) NULL)
+ {
+ struct bfd_link_hash_entry *h;
+ unsigned int hash, rehash;
+ 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)->next;
+ else
+ pundef = &(*pundef)->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)->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 = bfd_h_get_32 (abfd, hashtable + (hash * 8) + 4);
+ if (file_offset == 0)
+ {
+ /* Nothing in this slot. */
+ pundef = &(*pundef)->next;
+ continue;
+ }
+
+ name = stringbase + bfd_h_get_32 (abfd, hashtable + (hash * 8));
+ if (name[0] != h->root.string[0]
+ || strcmp (name, h->root.string) != 0)
+ {
+ unsigned int srch;
+ 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 = bfd_h_get_32 (abfd, hashtable + (srch * 8) + 4);
+ if (file_offset == 0)
+ break;
+ name = stringbase + bfd_h_get_32 (abfd, hashtable + (srch * 8));
+ if (name[0] == h->root.string[0]
+ && strcmp (name, h->root.string) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ pundef = &(*pundef)->next;
+ continue;
+ }
+
+ hash = srch;
+ }
+
+ element = _bfd_get_elt_at_filepos (abfd, file_offset);
+ if (element == (bfd *) 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))
+ return false;
+ if (! ecoff_link_add_object_symbols (element, info))
+ return false;
+
+ pundef = &(*pundef)->next;
+ }
+
+ return true;
+}
+
+/* This is called if we used _bfd_generic_link_add_archive_symbols
+ because we were not dealing with an ECOFF archive. */
+
+static boolean
+ecoff_link_check_archive_element (abfd, info, pneeded)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ boolean *pneeded;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+ = backend->debug_swap.swap_ext_in;
+ HDRR *symhdr;
+ bfd_size_type external_ext_size;
+ PTR external_ext;
+ size_t esize;
+ char *ssext;
+ char *ext_ptr;
+ char *ext_end;
+
+ *pneeded = false;
+
+ if (! ecoff_slurp_symbolic_header (abfd))
+ return false;
+
+ /* If there are no symbols, we don't want it. */
+ if (bfd_get_symcount (abfd) == 0)
+ return true;
+
+ symhdr = &ecoff_data (abfd)->debug_info.symbolic_header;
+
+ /* Read in the external symbols and external strings. */
+ external_ext_size = backend->debug_swap.external_ext_size;
+ esize = symhdr->iextMax * external_ext_size;
+ external_ext = (PTR) alloca (esize);
+ if (bfd_seek (abfd, symhdr->cbExtOffset, SEEK_SET) != 0
+ || bfd_read (external_ext, 1, esize, abfd) != esize)
+ return false;
+
+ ssext = (char *) alloca (symhdr->issExtMax);
+ if (bfd_seek (abfd, symhdr->cbSsExtOffset, SEEK_SET) != 0
+ || bfd_read (ssext, 1, symhdr->issExtMax, abfd) != symhdr->issExtMax)
+ return false;
+
+ /* Look through the external symbols to see if they define some
+ symbol that is currently undefined. */
+ ext_ptr = (char *) external_ext;
+ ext_end = ext_ptr + esize;
+ for (; ext_ptr < ext_end; ext_ptr += external_ext_size)
+ {
+ EXTR esym;
+ boolean def;
+ const char *name;
+ struct bfd_link_hash_entry *h;
+
+ (*swap_ext_in) (abfd, (PTR) ext_ptr, &esym);
+
+ /* See if this symbol defines something. */
+ if (esym.asym.st != stGlobal
+ && esym.asym.st != stLabel
+ && esym.asym.st != stProc)
+ continue;
+
+ switch (esym.asym.sc)
+ {
+ case scText:
+ case scData:
+ case scBss:
+ case scAbs:
+ case scSData:
+ case scSBss:
+ case scRData:
+ case scCommon:
+ case scSCommon:
+ case scInit:
+ case scFini:
+ def = true;
+ break;
+ default:
+ def = false;
+ break;
+ }
+
+ if (! def)
+ continue;
+
+ name = ssext + esym.asym.iss;
+ h = bfd_link_hash_lookup (info->hash, name, false, false, true);
+
+ /* Unlike the generic linker, we do not pull in elements because
+ of common symbols. */
+ if (h == (struct bfd_link_hash_entry *) NULL
+ || h->type != bfd_link_hash_undefined)
+ continue;
+
+ /* Include this element. */
+ if (! (*info->callbacks->add_archive_element) (info, abfd, name))
+ return false;
+ if (! ecoff_link_add_externals (abfd, info, external_ext, ssext))
+ return false;
+
+ *pneeded = true;
+ return true;
+ }
+
+ return true;
+}
+
+/* Add symbols from an ECOFF object file to the global linker hash
+ table. */
+
+static boolean
+ecoff_link_add_object_symbols (abfd, info)
+ bfd *abfd;
+ struct bfd_link_info *info;
+{
+ HDRR *symhdr;
+ bfd_size_type external_ext_size;
+ PTR external_ext;
+ size_t esize;
+ char *ssext;
+
+ if (! ecoff_slurp_symbolic_header (abfd))
+ return false;
+
+ /* If there are no symbols, we don't want it. */
+ if (bfd_get_symcount (abfd) == 0)
+ return true;
+
+ symhdr = &ecoff_data (abfd)->debug_info.symbolic_header;
+
+ /* Read in the external symbols and external strings. */
+ external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size;
+ esize = symhdr->iextMax * external_ext_size;
+ external_ext = (PTR) alloca (esize);
+ if (bfd_seek (abfd, symhdr->cbExtOffset, SEEK_SET) != 0
+ || bfd_read (external_ext, 1, esize, abfd) != esize)
+ return false;
+
+ ssext = (char *) alloca (symhdr->issExtMax);
+ if (bfd_seek (abfd, symhdr->cbSsExtOffset, SEEK_SET) != 0
+ || bfd_read (ssext, 1, symhdr->issExtMax, abfd) != symhdr->issExtMax)
+ return false;
+
+ return ecoff_link_add_externals (abfd, info, external_ext, ssext);
+}
+
+/* Add the external symbols of an object file to the global linker
+ hash table. The external symbols and strings we are passed are
+ just allocated on the stack, and will be discarded. We must
+ explicitly save any information we may need later on in the link.
+ We do not want to read the external symbol information again. */
+
+static boolean
+ecoff_link_add_externals (abfd, info, external_ext, ssext)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ PTR external_ext;
+ char *ssext;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+ = backend->debug_swap.swap_ext_in;
+ bfd_size_type external_ext_size = backend->debug_swap.external_ext_size;
+ unsigned long ext_count;
+ struct ecoff_link_hash_entry **sym_hash;
+ char *ext_ptr;
+ char *ext_end;
+
+ ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
+
+ sym_hash = ((struct ecoff_link_hash_entry **)
+ bfd_alloc (abfd,
+ ext_count * sizeof (struct bfd_link_hash_entry *)));
+ ecoff_data (abfd)->sym_hashes = sym_hash;
+
+ ext_ptr = (char *) external_ext;
+ ext_end = ext_ptr + ext_count * external_ext_size;
+ for (; ext_ptr < ext_end; ext_ptr += external_ext_size, sym_hash++)
+ {
+ EXTR esym;
+ boolean skip;
+ bfd_vma value;
+ asection *section;
+ const char *name;
+ struct ecoff_link_hash_entry *h;
+
+ *sym_hash = NULL;
+
+ (*swap_ext_in) (abfd, (PTR) ext_ptr, &esym);
+
+ /* Skip debugging symbols. */
+ skip = false;
+ switch (esym.asym.st)
+ {
+ case stGlobal:
+ case stStatic:
+ case stLabel:
+ case stProc:
+ case stStaticProc:
+ break;
+ default:
+ skip = true;
+ break;
+ }
+
+ if (skip)
+ continue;
+
+ /* Get the information for this symbol. */
+ value = esym.asym.value;
+ switch (esym.asym.sc)
+ {
+ default:
+ case scNil:
+ case scRegister:
+ case scCdbLocal:
+ case scBits:
+ case scCdbSystem:
+ case scRegImage:
+ case scInfo:
+ case scUserStruct:
+ case scVar:
+ case scVarRegister:
+ case scVariant:
+ case scBasedVar:
+ case scXData:
+ case scPData:
+ section = NULL;
+ break;
+ case scText:
+ section = bfd_make_section_old_way (abfd, ".text");
+ value -= section->vma;
+ break;
+ case scData:
+ section = bfd_make_section_old_way (abfd, ".data");
+ value -= section->vma;
+ break;
+ case scBss:
+ section = bfd_make_section_old_way (abfd, ".bss");
+ value -= section->vma;
+ break;
+ case scAbs:
+ section = &bfd_abs_section;
+ break;
+ case scUndefined:
+ section = &bfd_und_section;
+ break;
+ case scSData:
+ section = bfd_make_section_old_way (abfd, ".sdata");
+ value -= section->vma;
+ break;
+ case scSBss:
+ section = bfd_make_section_old_way (abfd, ".sbss");
+ value -= section->vma;
+ break;
+ case scRData:
+ section = bfd_make_section_old_way (abfd, ".rdata");
+ value -= section->vma;
+ break;
+ case scCommon:
+ if (value > ecoff_data (abfd)->gp_size)
+ {
+ section = &bfd_com_section;
+ break;
+ }
+ /* Fall through. */
+ case scSCommon:
+ if (ecoff_scom_section.name == NULL)
+ {
+ /* Initialize the small common section. */
+ ecoff_scom_section.name = SCOMMON;
+ ecoff_scom_section.flags = SEC_IS_COMMON;
+ ecoff_scom_section.output_section = &ecoff_scom_section;
+ ecoff_scom_section.symbol = &ecoff_scom_symbol;
+ ecoff_scom_section.symbol_ptr_ptr = &ecoff_scom_symbol_ptr;
+ ecoff_scom_symbol.name = SCOMMON;
+ ecoff_scom_symbol.flags = BSF_SECTION_SYM;
+ ecoff_scom_symbol.section = &ecoff_scom_section;
+ ecoff_scom_symbol_ptr = &ecoff_scom_symbol;
+ }
+ section = &ecoff_scom_section;
+ break;
+ case scSUndefined:
+ section = &bfd_und_section;
+ break;
+ case scInit:
+ section = bfd_make_section_old_way (abfd, ".init");
+ value -= section->vma;
+ break;
+ case scFini:
+ section = bfd_make_section_old_way (abfd, ".fini");
+ value -= section->vma;
+ break;
+ }
+
+ if (section == (asection *) NULL)
+ continue;
+
+ name = ssext + esym.asym.iss;
+
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, name, BSF_GLOBAL, section, value,
+ (const char *) NULL, true, true, backend->constructor_bitsize,
+ (struct bfd_link_hash_entry **) &h)))
+ return false;
+
+ *sym_hash = h;
+
+ /* If we are building an ECOFF hash table, save the external
+ symbol information. */
+ if (info->hash->creator->flavour == bfd_get_flavour (abfd))
+ {
+ if (h->abfd == (bfd *) NULL
+ || (section != &bfd_und_section
+ && (! bfd_is_com_section (section)
+ || h->root.type != bfd_link_hash_defined)))
+ {
+ h->abfd = abfd;
+ h->esym = esym;
+ }
+ }
+ }
+
+ return true;
+}
+\f
+/* ECOFF final link routines. */
+
+static boolean ecoff_final_link_debug_accumulate
+ PARAMS ((bfd *output_bfd, bfd *input_bfd, struct bfd_link_info *));
+static boolean ecoff_link_write_external
+ PARAMS ((struct ecoff_link_hash_entry *, PTR));
+static boolean ecoff_indirect_link_order
+ PARAMS ((bfd *, struct bfd_link_info *, asection *,
+ struct bfd_link_order *));
+
+/* 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. */
+
+boolean
+ecoff_bfd_final_link (abfd, info)
+ 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;
+ register bfd *input_bfd;
+ asection *o;
+ struct bfd_link_order *p;
+
+ /* We accumulate the debugging information counts in the symbolic
+ header. */
+ symhdr = &debug->symbolic_header;
+ symhdr->magic = backend->debug_swap.sym_magic;
+ /* FIXME: What should the version stamp be? */
+ 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 = debug->line_end = NULL;
+ debug->external_dnr = debug->external_dnr_end = NULL;
+ debug->external_pdr = debug->external_pdr_end = NULL;
+ debug->external_sym = debug->external_sym_end = NULL;
+ debug->external_opt = debug->external_opt_end = NULL;
+ debug->external_aux = debug->external_aux_end = NULL;
+ debug->ss = debug->ss_end = NULL;
+ debug->ssext = debug->ssext_end = NULL;
+ debug->external_fdr = debug->external_fdr_end = NULL;
+ debug->external_rfd = debug->external_rfd_end = NULL;
+ debug->external_ext = debug->external_ext_end = NULL;
+
+ /* Accumulate the debugging symbols from each input BFD. */
+ for (input_bfd = info->input_bfds;
+ input_bfd != (bfd *) NULL;
+ input_bfd = input_bfd->link_next)
+ {
+ boolean ret;
+
+ /* If we might be using the C based alloca function, dump memory
+ allocated by ecoff_final_link_debug_accumulate. */
+#ifndef __GNUC__
+#ifndef alloca
+ (void) alloca (0);
+#endif
+#endif
+
+ if (bfd_get_flavour (input_bfd) == bfd_target_ecoff_flavour)
+ ret = ecoff_final_link_debug_accumulate (abfd, input_bfd, info);
+ else
+ ret = bfd_ecoff_debug_link_other (abfd,
+ debug,
+ &backend->debug_swap,
+ input_bfd);
+ 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. */
+ ecoff_link_hash_traverse (ecoff_hash_table (info),
+ ecoff_link_write_external,
+ (PTR) abfd);
+
+ if (info->relocateable)
+ {
+ /* 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 != (asection *) NULL; o = o->next)
+ {
+ o->reloc_count = 0;
+ for (p = o->link_order_head;
+ p != (struct bfd_link_order *) NULL;
+ p = p->next)
+ if (p->type == bfd_indirect_link_order)
+ o->reloc_count += p->u.indirect.section->reloc_count;
+ }
+
+ ecoff_compute_reloc_file_positions (abfd);
+
+ /* 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 != (asection *) 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 != (struct bfd_link_hash_entry *) 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->relocateable)
+ {
+ bfd_vma lo;
+
+ /* Make up a value. */
+ lo = (bfd_vma) -1;
+ for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+ {
+ if (o->vma < lo
+ && (strcmp (o->name, _SBSS) == 0
+ || strcmp (o->name, _SDATA) == 0
+ || strcmp (o->name, _LIT4) == 0
+ || strcmp (o->name, _LIT8) == 0
+ || strcmp (o->name, _LITA) == 0))
+ 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 != (asection *) NULL; o = o->next)
+ {
+ /* Ignore any link_orders for the .reginfo section, which does
+ not really exist. */
+ if (strcmp (o->name, REGINFO) == 0)
+ continue;
+
+ for (p = o->link_order_head;
+ p != (struct bfd_link_order *) NULL;
+ p = p->next)
+ {
+ /* If we might be using the C based alloca function, we need
+ to dump the memory allocated by the function
+ ecoff_indirect_link_order. */
+#ifndef __GNUC__
+#ifndef alloca
+ (void) alloca (0);
+#endif
+#endif
+ 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 (! _bfd_default_link_order (abfd, info, o, p))
+ return false;
+ }
+ }
+ }
+
+ bfd_get_symcount (abfd) = symhdr->iextMax + symhdr->isymMax;
+
+ return true;
+}
+
+/* Accumulate the debugging information for an input BFD into the
+ output BFD. This must read in the symbolic information of the
+ input BFD. */
+
+static boolean
+ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info)
+ bfd *output_bfd;
+ bfd *input_bfd;
+ struct bfd_link_info *info;
+{
+ 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;
+ boolean ret;
+
+#define READ(ptr, offset, count, size, type) \
+ if (symhdr->count == 0) \
+ debug->ptr = NULL; \
+ else \
+ { \
+ debug->ptr = (type) alloca (size * symhdr->count); \
+ if ((bfd_seek (input_bfd, (file_ptr) symhdr->offset, SEEK_SET) \
+ != 0) \
+ || (bfd_read (debug->ptr, size, symhdr->count, \
+ input_bfd) != size * symhdr->count)) \
+ return false; \
+ }
+
+ READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
+ READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR);
+ READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR);
+ READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR);
+ READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR);
+ 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, PTR);
+ READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
+#undef READ
+
+ /* We do not read the external strings or the external symbols. */
+
+ ret = (bfd_ecoff_debug_accumulate
+ (output_bfd, &ecoff_data (output_bfd)->debug_info,
+ &ecoff_backend (output_bfd)->debug_swap,
+ input_bfd, debug, swap, info->relocateable));
+
+ /* Make sure we don't accidentally follow one of these pointers on
+ to the stack. */
+ 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;
+}
+
+/* Put out information for an external symbol. These come only from
+ the hash table. */
+
+static boolean
+ecoff_link_write_external (h, data)
+ struct ecoff_link_hash_entry *h;
+ PTR data;
+{
+ bfd *output_bfd = (bfd *) data;
+
+ /* FIXME: We should check if this symbol is being stripped. */
+
+ if (h->root.written)
+ return true;
+
+ if (h->abfd == (bfd *) 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;
+ /* FIXME: we can do better than this for st and sc. */
+ h->esym.asym.st = stGlobal;
+ h->esym.asym.sc = scAbs;
+ h->esym.asym.reserved = 0;
+ h->esym.asym.index = indexNil;
+ }
+ else
+ {
+ /* Adjust the FDR index for the symbol by that used for the
+ input BFD. */
+ h->esym.ifd += ecoff_data (h->abfd)->debug_info.ifdbase;
+ }
+
+ switch (h->root.type)
+ {
+ default:
+ case bfd_link_hash_new:
+ abort ();
+ case bfd_link_hash_undefined:
+ case bfd_link_hash_weak:
+ if (h->esym.asym.st != scUndefined
+ && h->esym.asym.st != scSUndefined)
+ h->esym.asym.st = scUndefined;
+ break;
+ case bfd_link_hash_defined:
+ 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:
+ case bfd_link_hash_warning:
+ /* FIXME: Ignore these for now. The circumstances under which
+ they should be written out are not clear to me. */
+ 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->root.written = true;
+
+ 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));
+}
+
+/* Relocate and write an ECOFF section into an ECOFF output file. */
+
+static boolean
+ecoff_indirect_link_order (output_bfd, info, output_section, 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_size_type input_size;
+ bfd_byte *contents;
+ bfd_size_type external_reloc_size;
+ bfd_size_type external_relocs_size;
+ PTR external_relocs;
+
+ BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0);
+
+ if (link_order->size == 0)
+ return true;
+
+ input_section = link_order->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ BFD_ASSERT (input_section->output_section == output_section);
+ BFD_ASSERT (input_section->output_offset == link_order->offset);
+ BFD_ASSERT (bfd_section_size (input_bfd, input_section) == link_order->size);
+
+ /* Get the section contents. */
+ input_size = bfd_section_size (input_bfd, input_section);
+ contents = (bfd_byte *) alloca (input_size);
+ if (! bfd_get_section_contents (input_bfd, input_section, (PTR) contents,
+ (file_ptr) 0, input_size))
+ return false;
+
+ /* Get the relocs. */
+ external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size;
+ external_relocs_size = external_reloc_size * input_section->reloc_count;
+ external_relocs = (PTR) alloca (external_relocs_size);
+ if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
+ || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd)
+ != external_relocs_size))
+ return false;
+
+ /* Relocate the section contents. */
+ if (! ((*ecoff_backend (input_bfd)->relocate_section)
+ (output_bfd, info, input_bfd, input_section, contents,
+ external_relocs)))
+ return false;
+
+ /* Write out the relocated section. */
+ if (! bfd_set_section_contents (output_bfd,
+ output_section,
+ (PTR) contents,
+ input_section->output_offset,
+ input_size))
+ return false;
+
+ /* If we are producing relocateable 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->relocateable)
+ {
+ if (bfd_seek (output_bfd,
+ (output_section->rel_filepos +
+ output_section->reloc_count * external_reloc_size),
+ SEEK_SET) != 0
+ || (bfd_write (external_relocs, 1, external_relocs_size, output_bfd)
+ != external_relocs_size))
+ return false;
+ output_section->reloc_count += input_section->reloc_count;
+ }
+
+ return true;
+}