| /* Generic ECOFF (Extended-COFF) routines. | 
 |    Copyright (C) 1990-2024 Free Software Foundation, Inc. | 
 |    Original version by Per Bothner. | 
 |    Full support added by Ian Lance Taylor, ian@cygnus.com. | 
 |  | 
 |    This file is part of BFD, the Binary File Descriptor library. | 
 |  | 
 |    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 3 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., 51 Franklin Street - Fifth Floor, Boston, | 
 |    MA 02110-1301, USA.  */ | 
 |  | 
 | #include "sysdep.h" | 
 | #include "bfd.h" | 
 | #include "bfdlink.h" | 
 | #include "libbfd.h" | 
 | #include "ecoff-bfd.h" | 
 | #include "aout/ar.h" | 
 | #include "aout/stab_gnu.h" | 
 |  | 
 | /* FIXME: We need the definitions of N_SET[ADTB], but aout64.h defines | 
 |    some other stuff which we don't want and which conflicts with stuff | 
 |    we do want.  */ | 
 | #include "libaout.h" | 
 | #include "aout/aout64.h" | 
 | #undef N_ABS | 
 | #undef exec_hdr | 
 | #undef obj_sym_filepos | 
 |  | 
 | #include "coff/internal.h" | 
 | #include "coff/sym.h" | 
 | #include "coff/symconst.h" | 
 | #include "coff/ecoff.h" | 
 | #include "libcoff.h" | 
 | #include "libecoff.h" | 
 | #include "libiberty.h" | 
 |  | 
 | #define streq(a, b)	(strcmp ((a), (b)) == 0) | 
 |  | 
 |  | 
 | /* This stuff is somewhat copied from coffcode.h.  */ | 
 | static asection bfd_debug_section = | 
 |   BFD_FAKE_SECTION (bfd_debug_section, NULL, "*DEBUG*", 0, 0); | 
 |  | 
 | /* Create an ECOFF object.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_mkobject (bfd *abfd) | 
 | { | 
 |   size_t amt = sizeof (ecoff_data_type); | 
 |  | 
 |   abfd->tdata.ecoff_obj_data = (struct ecoff_tdata *) bfd_zalloc (abfd, amt); | 
 |   if (abfd->tdata.ecoff_obj_data == NULL) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* This is a hook called by coff_real_object_p to create any backend | 
 |    specific information.  */ | 
 |  | 
 | void * | 
 | _bfd_ecoff_mkobject_hook (bfd *abfd, void * filehdr, void * aouthdr) | 
 | { | 
 |   struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; | 
 |   struct internal_aouthdr *internal_a = (struct internal_aouthdr *) aouthdr; | 
 |   ecoff_data_type *ecoff; | 
 |  | 
 |   if (! _bfd_ecoff_mkobject (abfd)) | 
 |     return NULL; | 
 |  | 
 |   ecoff = ecoff_data (abfd); | 
 |   ecoff->gp_size = 8; | 
 |   ecoff->sym_filepos = internal_f->f_symptr; | 
 |  | 
 |   if (internal_a != NULL) | 
 |     { | 
 |       int i; | 
 |  | 
 |       ecoff->text_start = internal_a->text_start; | 
 |       ecoff->text_end = internal_a->text_start + internal_a->tsize; | 
 |       ecoff->gp = internal_a->gp_value; | 
 |       ecoff->gprmask = internal_a->gprmask; | 
 |       for (i = 0; i < 4; i++) | 
 | 	ecoff->cprmask[i] = internal_a->cprmask[i]; | 
 |       ecoff->fprmask = internal_a->fprmask; | 
 |       if (internal_a->magic == ECOFF_AOUT_ZMAGIC) | 
 | 	abfd->flags |= D_PAGED; | 
 |       else | 
 | 	abfd->flags &=~ D_PAGED; | 
 |     } | 
 |  | 
 |   /* It turns out that no special action is required by the MIPS or | 
 |      Alpha ECOFF backends.  They have different information in the | 
 |      a.out header, but we just copy it all (e.g., gprmask, cprmask and | 
 |      fprmask) and let the swapping routines ensure that only relevant | 
 |      information is written out.  */ | 
 |  | 
 |   return (void *) ecoff; | 
 | } | 
 |  | 
 | bool | 
 | _bfd_ecoff_bfd_free_cached_info (bfd *abfd) | 
 | { | 
 |   struct ecoff_tdata *tdata; | 
 |  | 
 |   if ((bfd_get_format (abfd) == bfd_object | 
 |        || bfd_get_format (abfd) == bfd_core) | 
 |       && (tdata = ecoff_data (abfd)) != NULL) | 
 |     { | 
 |       while (tdata->mips_refhi_list != NULL) | 
 | 	{ | 
 | 	  struct mips_hi *ref = tdata->mips_refhi_list; | 
 | 	  tdata->mips_refhi_list = ref->next; | 
 | 	  free (ref); | 
 | 	} | 
 |       _bfd_ecoff_free_ecoff_debug_info (&tdata->debug_info); | 
 |     } | 
 |   return _bfd_generic_bfd_free_cached_info (abfd); | 
 | } | 
 |  | 
 | /* Initialize a new section.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_new_section_hook (bfd *abfd, asection *section) | 
 | { | 
 |   unsigned int i; | 
 |   static struct | 
 |   { | 
 |     const char * name; | 
 |     flagword flags; | 
 |   } | 
 |   section_flags [] = | 
 |   { | 
 |     { _TEXT,   SEC_ALLOC | SEC_CODE | SEC_LOAD }, | 
 |     { _INIT,   SEC_ALLOC | SEC_CODE | SEC_LOAD }, | 
 |     { _FINI,   SEC_ALLOC | SEC_CODE | SEC_LOAD }, | 
 |     { _DATA,   SEC_ALLOC | SEC_DATA | SEC_LOAD }, | 
 |     { _SDATA,  SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_SMALL_DATA }, | 
 |     { _RDATA,  SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | 
 |     { _LIT8,   SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_SMALL_DATA}, | 
 |     { _LIT4,   SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_SMALL_DATA}, | 
 |     { _RCONST, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | 
 |     { _PDATA,  SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | 
 |     { _BSS,    SEC_ALLOC}, | 
 |     { _SBSS,   SEC_ALLOC | SEC_SMALL_DATA}, | 
 |     /* An Irix 4 shared libary.  */ | 
 |     { _LIB,    SEC_COFF_SHARED_LIBRARY} | 
 |   }; | 
 |  | 
 |   section->alignment_power = 4; | 
 |  | 
 |   for (i = 0; i < ARRAY_SIZE (section_flags); i++) | 
 |     if (streq (section->name, section_flags[i].name)) | 
 |       { | 
 | 	section->flags |= section_flags[i].flags; | 
 | 	break; | 
 |       } | 
 |  | 
 |  | 
 |   /* Probably any other section name is SEC_NEVER_LOAD, but I'm | 
 |      uncertain about .init on some systems and I don't know how shared | 
 |      libraries work.  */ | 
 |  | 
 |   return _bfd_generic_new_section_hook (abfd, section); | 
 | } | 
 |  | 
 | void | 
 | _bfd_ecoff_set_alignment_hook (bfd *abfd ATTRIBUTE_UNUSED, | 
 | 			       asection *section ATTRIBUTE_UNUSED, | 
 | 			       void *scnhdr ATTRIBUTE_UNUSED) | 
 | { | 
 | } | 
 |  | 
 | /* Determine the machine architecture and type.  This is called from | 
 |    the generic COFF routines.  It is the inverse of ecoff_get_magic, | 
 |    below.  This could be an ECOFF backend routine, with one version | 
 |    for each target, but there aren't all that many ECOFF targets.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_set_arch_mach_hook (bfd *abfd, void * filehdr) | 
 | { | 
 |   struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; | 
 |   enum bfd_architecture arch; | 
 |   unsigned long mach; | 
 |  | 
 |   switch (internal_f->f_magic) | 
 |     { | 
 |     case MIPS_MAGIC_1: | 
 |     case MIPS_MAGIC_LITTLE: | 
 |     case MIPS_MAGIC_BIG: | 
 |       arch = bfd_arch_mips; | 
 |       mach = bfd_mach_mips3000; | 
 |       break; | 
 |  | 
 |     case MIPS_MAGIC_LITTLE2: | 
 |     case MIPS_MAGIC_BIG2: | 
 |       /* MIPS ISA level 2: the r6000.  */ | 
 |       arch = bfd_arch_mips; | 
 |       mach = bfd_mach_mips6000; | 
 |       break; | 
 |  | 
 |     case MIPS_MAGIC_LITTLE3: | 
 |     case MIPS_MAGIC_BIG3: | 
 |       /* MIPS ISA level 3: the r4000.  */ | 
 |       arch = bfd_arch_mips; | 
 |       mach = bfd_mach_mips4000; | 
 |       break; | 
 |  | 
 |     case ALPHA_MAGIC: | 
 |       arch = bfd_arch_alpha; | 
 |       mach = 0; | 
 |       break; | 
 |  | 
 |     default: | 
 |       arch = bfd_arch_obscure; | 
 |       mach = 0; | 
 |       break; | 
 |     } | 
 |  | 
 |   return bfd_default_set_arch_mach (abfd, arch, mach); | 
 | } | 
 |  | 
 | bool | 
 | _bfd_ecoff_no_long_sections (bfd *abfd, int enable) | 
 | { | 
 |   (void) abfd; | 
 |   (void) enable; | 
 |   return false; | 
 | } | 
 |  | 
 | /* Get the magic number to use based on the architecture and machine. | 
 |    This is the inverse of _bfd_ecoff_set_arch_mach_hook, above.  */ | 
 |  | 
 | static int | 
 | ecoff_get_magic (bfd *abfd) | 
 | { | 
 |   int big, little; | 
 |  | 
 |   switch (bfd_get_arch (abfd)) | 
 |     { | 
 |     case bfd_arch_mips: | 
 |       switch (bfd_get_mach (abfd)) | 
 | 	{ | 
 | 	default: | 
 | 	case 0: | 
 | 	case bfd_mach_mips3000: | 
 | 	  big = MIPS_MAGIC_BIG; | 
 | 	  little = MIPS_MAGIC_LITTLE; | 
 | 	  break; | 
 |  | 
 | 	case bfd_mach_mips6000: | 
 | 	  big = MIPS_MAGIC_BIG2; | 
 | 	  little = MIPS_MAGIC_LITTLE2; | 
 | 	  break; | 
 |  | 
 | 	case bfd_mach_mips4000: | 
 | 	  big = MIPS_MAGIC_BIG3; | 
 | 	  little = MIPS_MAGIC_LITTLE3; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       return bfd_big_endian (abfd) ? big : little; | 
 |  | 
 |     case bfd_arch_alpha: | 
 |       return ALPHA_MAGIC; | 
 |  | 
 |     default: | 
 |       abort (); | 
 |       return 0; | 
 |     } | 
 | } | 
 |  | 
 | /* Get the section s_flags to use for a section.  */ | 
 |  | 
 | static long | 
 | ecoff_sec_to_styp_flags (const char *name, flagword flags) | 
 | { | 
 |   unsigned int i; | 
 |   static struct | 
 |   { | 
 |     const char * name; | 
 |     long flags; | 
 |   } | 
 |   styp_flags [] = | 
 |   { | 
 |     { _TEXT,	STYP_TEXT	}, | 
 |     { _DATA,	STYP_DATA	}, | 
 |     { _SDATA,	STYP_SDATA	}, | 
 |     { _RDATA,	STYP_RDATA	}, | 
 |     { _LITA,	STYP_LITA	}, | 
 |     { _LIT8,	STYP_LIT8	}, | 
 |     { _LIT4,	STYP_LIT4	}, | 
 |     { _BSS,	STYP_BSS	}, | 
 |     { _SBSS,	STYP_SBSS	}, | 
 |     { _INIT,	STYP_ECOFF_INIT }, | 
 |     { _FINI,	STYP_ECOFF_FINI }, | 
 |     { _PDATA,	STYP_PDATA	}, | 
 |     { _XDATA,	STYP_XDATA	}, | 
 |     { _LIB,	STYP_ECOFF_LIB	}, | 
 |     { _GOT,	STYP_GOT	}, | 
 |     { _HASH,	STYP_HASH	}, | 
 |     { _DYNAMIC, STYP_DYNAMIC	}, | 
 |     { _LIBLIST, STYP_LIBLIST	}, | 
 |     { _RELDYN,	STYP_RELDYN	}, | 
 |     { _CONFLIC, STYP_CONFLIC	}, | 
 |     { _DYNSTR,	STYP_DYNSTR	}, | 
 |     { _DYNSYM,	STYP_DYNSYM	}, | 
 |     { _RCONST,	STYP_RCONST	} | 
 |   }; | 
 |   long styp = 0; | 
 |  | 
 |   for (i = 0; i < ARRAY_SIZE (styp_flags); i++) | 
 |     if (streq (name, styp_flags[i].name)) | 
 |       { | 
 | 	styp = styp_flags[i].flags; | 
 | 	break; | 
 |       } | 
 |  | 
 |   if (styp == 0) | 
 |     { | 
 |       if (streq (name, _COMMENT)) | 
 | 	{ | 
 | 	  styp = STYP_COMMENT; | 
 | 	  flags &=~ SEC_NEVER_LOAD; | 
 | 	} | 
 |       else if (flags & SEC_CODE) | 
 | 	styp = STYP_TEXT; | 
 |       else if (flags & SEC_DATA) | 
 | 	styp = STYP_DATA; | 
 |       else if (flags & SEC_READONLY) | 
 | 	styp = STYP_RDATA; | 
 |       else if (flags & SEC_LOAD) | 
 | 	styp = STYP_REG; | 
 |       else | 
 | 	styp = STYP_BSS; | 
 |     } | 
 |  | 
 |   if (flags & SEC_NEVER_LOAD) | 
 |     styp |= STYP_NOLOAD; | 
 |  | 
 |   return styp; | 
 | } | 
 |  | 
 | /* Get the BFD flags to use for a section.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_styp_to_sec_flags (bfd *abfd ATTRIBUTE_UNUSED, | 
 | 			      void * hdr, | 
 | 			      const char *name ATTRIBUTE_UNUSED, | 
 | 			      asection *section ATTRIBUTE_UNUSED, | 
 | 			      flagword * flags_ptr) | 
 | { | 
 |   struct internal_scnhdr *internal_s = (struct internal_scnhdr *) hdr; | 
 |   long styp_flags = internal_s->s_flags; | 
 |   flagword sec_flags = 0; | 
 |  | 
 |   if (styp_flags & STYP_NOLOAD) | 
 |     sec_flags |= SEC_NEVER_LOAD; | 
 |  | 
 |   /* For 386 COFF, at least, an unloadable text or data section is | 
 |      actually a shared library section.  */ | 
 |   if ((styp_flags & STYP_TEXT) | 
 |       || (styp_flags & STYP_ECOFF_INIT) | 
 |       || (styp_flags & STYP_ECOFF_FINI) | 
 |       || (styp_flags & STYP_DYNAMIC) | 
 |       || (styp_flags & STYP_LIBLIST) | 
 |       || (styp_flags & STYP_RELDYN) | 
 |       || styp_flags == STYP_CONFLIC | 
 |       || (styp_flags & STYP_DYNSTR) | 
 |       || (styp_flags & STYP_DYNSYM) | 
 |       || (styp_flags & STYP_HASH)) | 
 |     { | 
 |       if (sec_flags & SEC_NEVER_LOAD) | 
 | 	sec_flags |= SEC_CODE | SEC_COFF_SHARED_LIBRARY; | 
 |       else | 
 | 	sec_flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC; | 
 |     } | 
 |   else if ((styp_flags & STYP_DATA) | 
 | 	   || (styp_flags & STYP_RDATA) | 
 | 	   || (styp_flags & STYP_SDATA) | 
 | 	   || styp_flags == STYP_PDATA | 
 | 	   || styp_flags == STYP_XDATA | 
 | 	   || (styp_flags & STYP_GOT) | 
 | 	   || styp_flags == STYP_RCONST) | 
 |     { | 
 |       if (sec_flags & SEC_NEVER_LOAD) | 
 | 	sec_flags |= SEC_DATA | SEC_COFF_SHARED_LIBRARY; | 
 |       else | 
 | 	sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC; | 
 |       if ((styp_flags & STYP_RDATA) | 
 | 	  || styp_flags == STYP_PDATA | 
 | 	  || styp_flags == STYP_RCONST) | 
 | 	sec_flags |= SEC_READONLY; | 
 |       if (styp_flags & STYP_SDATA) | 
 | 	sec_flags |= SEC_SMALL_DATA; | 
 |     } | 
 |   else if (styp_flags & STYP_SBSS) | 
 |     sec_flags |= SEC_ALLOC | SEC_SMALL_DATA; | 
 |   else if (styp_flags & STYP_BSS) | 
 |     sec_flags |= SEC_ALLOC; | 
 |   else if ((styp_flags & STYP_INFO) || styp_flags == STYP_COMMENT) | 
 |     sec_flags |= SEC_NEVER_LOAD; | 
 |   else if ((styp_flags & STYP_LITA) | 
 | 	   || (styp_flags & STYP_LIT8) | 
 | 	   || (styp_flags & STYP_LIT4)) | 
 |     sec_flags |= SEC_DATA |SEC_SMALL_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY; | 
 |   else if (styp_flags & STYP_ECOFF_LIB) | 
 |     sec_flags |= SEC_COFF_SHARED_LIBRARY; | 
 |   else | 
 |     sec_flags |= SEC_ALLOC | SEC_LOAD; | 
 |  | 
 |   * flags_ptr = sec_flags; | 
 |   return true; | 
 | } | 
 |  | 
 | /* Read in the symbolic header for an ECOFF object file.  */ | 
 |  | 
 | static bool | 
 | ecoff_slurp_symbolic_header (bfd *abfd) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   bfd_size_type external_hdr_size; | 
 |   void * raw = NULL; | 
 |   HDRR *internal_symhdr; | 
 |  | 
 |   /* See if we've already read it in.  */ | 
 |   if (ecoff_data (abfd)->debug_info.symbolic_header.magic == | 
 |       backend->debug_swap.sym_magic) | 
 |     return true; | 
 |  | 
 |   /* See whether there is a symbolic header.  */ | 
 |   if (ecoff_data (abfd)->sym_filepos == 0) | 
 |     { | 
 |       abfd->symcount = 0; | 
 |       return true; | 
 |     } | 
 |  | 
 |   /* At this point bfd_get_symcount (abfd) holds the number of symbols | 
 |      as read from the file header, but on ECOFF this is always the | 
 |      size of the symbolic information header.  It would be cleaner to | 
 |      handle this when we first read the file in coffgen.c.  */ | 
 |   external_hdr_size = backend->debug_swap.external_hdr_size; | 
 |   if (bfd_get_symcount (abfd) != external_hdr_size) | 
 |     { | 
 |       bfd_set_error (bfd_error_bad_value); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* Read the symbolic information header.  */ | 
 |   if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) != 0) | 
 |     goto error_return; | 
 |   raw = _bfd_malloc_and_read (abfd, external_hdr_size, external_hdr_size); | 
 |   if (raw == NULL) | 
 |     goto error_return; | 
 |  | 
 |   internal_symhdr = &ecoff_data (abfd)->debug_info.symbolic_header; | 
 |   (*backend->debug_swap.swap_hdr_in) (abfd, raw, internal_symhdr); | 
 |  | 
 |   if (internal_symhdr->magic != backend->debug_swap.sym_magic) | 
 |     { | 
 |       bfd_set_error (bfd_error_bad_value); | 
 |       goto error_return; | 
 |     } | 
 |  | 
 | #define FIX(start, count) \ | 
 |   if (internal_symhdr->start == 0)	\ | 
 |     internal_symhdr->count = 0; | 
 |  | 
 |   FIX (cbLineOffset, cbLine); | 
 |   FIX (cbDnOffset, idnMax); | 
 |   FIX (cbPdOffset, ipdMax); | 
 |   FIX (cbSymOffset, isymMax); | 
 |   FIX (cbOptOffset, ioptMax); | 
 |   FIX (cbAuxOffset, iauxMax); | 
 |   FIX (cbSsOffset, issMax); | 
 |   FIX (cbSsExtOffset, issExtMax); | 
 |   FIX (cbFdOffset, ifdMax); | 
 |   FIX (cbRfdOffset, crfd); | 
 |   FIX (cbExtOffset, iextMax); | 
 | #undef FIX | 
 |  | 
 |   /* Now we can get the correct number of symbols.  */ | 
 |   abfd->symcount = internal_symhdr->isymMax + internal_symhdr->iextMax; | 
 |  | 
 |   free (raw); | 
 |   return true; | 
 |  error_return: | 
 |   free (raw); | 
 |   return false; | 
 | } | 
 |  | 
 | /* Read in and swap the important symbolic information for an ECOFF | 
 |    object file.  This is called by gdb via the read_debug_info entry | 
 |    point in the backend structure.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_slurp_symbolic_info (bfd *abfd, | 
 | 				asection *ignore ATTRIBUTE_UNUSED, | 
 | 				struct ecoff_debug_info *debug) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   HDRR *internal_symhdr; | 
 |   bfd_size_type raw_base; | 
 |   bfd_size_type raw_size; | 
 |   void * raw; | 
 |   bfd_size_type external_fdr_size; | 
 |   char *fraw_src; | 
 |   char *fraw_end; | 
 |   struct fdr *fdr_ptr; | 
 |   bfd_size_type raw_end; | 
 |   bfd_size_type cb_end; | 
 |   file_ptr pos; | 
 |   size_t amt; | 
 |  | 
 |   BFD_ASSERT (debug == &ecoff_data (abfd)->debug_info); | 
 |  | 
 |   /* Check whether we've already gotten it, and whether there's any to | 
 |      get.  */ | 
 |   if (debug->alloc_syments) | 
 |     return true; | 
 |   if (ecoff_data (abfd)->sym_filepos == 0) | 
 |     { | 
 |       abfd->symcount = 0; | 
 |       return true; | 
 |     } | 
 |  | 
 |   if (! ecoff_slurp_symbolic_header (abfd)) | 
 |     return false; | 
 |  | 
 |   internal_symhdr = &debug->symbolic_header; | 
 |  | 
 |   /* Read all the symbolic information at once.  */ | 
 |   raw_base = (ecoff_data (abfd)->sym_filepos | 
 | 	      + backend->debug_swap.external_hdr_size); | 
 |  | 
 |   /* Alpha ecoff makes the determination of raw_size difficult. It has | 
 |      an undocumented debug data section between the symhdr and the first | 
 |      documented section. And the ordering of the sections varies between | 
 |      statically and dynamically linked executables. | 
 |      If bfd supports SEEK_END someday, this code could be simplified.  */ | 
 |   raw_end = raw_base; | 
 |  | 
 | #define UPDATE_RAW_END(start, count, size) \ | 
 |   do									\ | 
 |     if (internal_symhdr->count != 0)					\ | 
 |       {									\ | 
 | 	if (internal_symhdr->start < raw_base)				\ | 
 | 	  goto err;							\ | 
 | 	if (_bfd_mul_overflow ((unsigned long) internal_symhdr->count,	\ | 
 | 			       (size), &amt))				\ | 
 | 	  goto err;							\ | 
 | 	cb_end = internal_symhdr->start + amt;				\ | 
 | 	if (cb_end < internal_symhdr->start)				\ | 
 | 	  goto err;							\ | 
 | 	if (cb_end > raw_end)						\ | 
 | 	  raw_end = cb_end;						\ | 
 |       }									\ | 
 |   while (0) | 
 |  | 
 |   UPDATE_RAW_END (cbLineOffset, cbLine, sizeof (unsigned char)); | 
 |   UPDATE_RAW_END (cbDnOffset, idnMax, backend->debug_swap.external_dnr_size); | 
 |   UPDATE_RAW_END (cbPdOffset, ipdMax, backend->debug_swap.external_pdr_size); | 
 |   UPDATE_RAW_END (cbSymOffset, isymMax, backend->debug_swap.external_sym_size); | 
 |   /* eraxxon@alumni.rice.edu: ioptMax refers to the size of the | 
 |      optimization symtab, not the number of entries.  */ | 
 |   UPDATE_RAW_END (cbOptOffset, ioptMax, sizeof (char)); | 
 |   UPDATE_RAW_END (cbAuxOffset, iauxMax, sizeof (union aux_ext)); | 
 |   UPDATE_RAW_END (cbSsOffset, issMax, sizeof (char)); | 
 |   UPDATE_RAW_END (cbSsExtOffset, issExtMax, sizeof (char)); | 
 |   UPDATE_RAW_END (cbFdOffset, ifdMax, backend->debug_swap.external_fdr_size); | 
 |   UPDATE_RAW_END (cbRfdOffset, crfd, backend->debug_swap.external_rfd_size); | 
 |   UPDATE_RAW_END (cbExtOffset, iextMax, backend->debug_swap.external_ext_size); | 
 |  | 
 | #undef UPDATE_RAW_END | 
 |  | 
 |   raw_size = raw_end - raw_base; | 
 |   if (raw_size == 0) | 
 |     { | 
 |       ecoff_data (abfd)->sym_filepos = 0; | 
 |       return true; | 
 |     } | 
 |   pos = ecoff_data (abfd)->sym_filepos; | 
 |   pos += backend->debug_swap.external_hdr_size; | 
 |   if (bfd_seek (abfd, pos, SEEK_SET) != 0) | 
 |     return false; | 
 |   raw = _bfd_alloc_and_read (abfd, raw_size, raw_size); | 
 |   if (raw == NULL) | 
 |     return false; | 
 |  | 
 |   debug->alloc_syments = true; | 
 |  | 
 |   /* Get pointers for the numeric offsets in the HDRR structure.  */ | 
 | #define FIX(start, count, ptr, type) \ | 
 |   if (internal_symhdr->count == 0)					\ | 
 |     debug->ptr = NULL;							\ | 
 |   else									\ | 
 |     debug->ptr = (type) ((char *) raw					\ | 
 | 			 + (internal_symhdr->start - raw_base)) | 
 |  | 
 |   FIX (cbLineOffset, cbLine, line, unsigned char *); | 
 |   FIX (cbDnOffset, idnMax, external_dnr, void *); | 
 |   FIX (cbPdOffset, ipdMax, external_pdr, void *); | 
 |   FIX (cbSymOffset, isymMax, external_sym, void *); | 
 |   FIX (cbOptOffset, ioptMax, external_opt, void *); | 
 |   FIX (cbAuxOffset, iauxMax, external_aux, union aux_ext *); | 
 |   FIX (cbSsOffset, issMax, ss, char *); | 
 |   FIX (cbSsExtOffset, issExtMax, ssext, char *); | 
 |   FIX (cbFdOffset, ifdMax, external_fdr, void *); | 
 |   FIX (cbRfdOffset, crfd, external_rfd, void *); | 
 |   FIX (cbExtOffset, iextMax, external_ext, void *); | 
 | #undef FIX | 
 |  | 
 |   /* Ensure string sections are zero terminated.  */ | 
 |   if (debug->ss) | 
 |     debug->ss[internal_symhdr->issMax - 1] = 0; | 
 |   if (debug->ssext) | 
 |     debug->ssext[internal_symhdr->issExtMax - 1] = 0; | 
 |  | 
 |   /* I don't want to always swap all the data, because it will just | 
 |      waste time and most programs will never look at it.  The only | 
 |      time the linker needs most of the debugging information swapped | 
 |      is when linking big-endian and little-endian MIPS object files | 
 |      together, which is not a common occurrence. | 
 |  | 
 |      We need to look at the fdr to deal with a lot of information in | 
 |      the symbols, so we swap them here.  */ | 
 |   if (_bfd_mul_overflow ((unsigned long) internal_symhdr->ifdMax, | 
 | 			 sizeof (struct fdr), &amt)) | 
 |     { | 
 |     err: | 
 |       bfd_set_error (bfd_error_file_too_big); | 
 |       return false; | 
 |     } | 
 |   debug->fdr = (FDR *) bfd_alloc (abfd, amt); | 
 |   if (debug->fdr == NULL) | 
 |     return false; | 
 |   external_fdr_size = backend->debug_swap.external_fdr_size; | 
 |   fdr_ptr = debug->fdr; | 
 |   fraw_src = (char *) debug->external_fdr; | 
 |   /* PR 17512: file: 3372-1243-0.004.  */ | 
 |   if (fraw_src == NULL && internal_symhdr->ifdMax > 0) | 
 |     return false; | 
 |   fraw_end = fraw_src + internal_symhdr->ifdMax * external_fdr_size; | 
 |   for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++) | 
 |     (*backend->debug_swap.swap_fdr_in) (abfd, (void *) fraw_src, fdr_ptr); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* ECOFF symbol table routines.  The ECOFF symbol table is described | 
 |    in gcc/mips-tfile.c.  */ | 
 |  | 
 | /* ECOFF uses two common sections.  One is the usual one, and the | 
 |    other is for small objects.  All the small objects are kept | 
 |    together, and then referenced via the gp pointer, which yields | 
 |    faster assembler code.  This is what we use for the small common | 
 |    section.  */ | 
 | static asection ecoff_scom_section; | 
 | static const asymbol ecoff_scom_symbol = | 
 |   GLOBAL_SYM_INIT (SCOMMON, &ecoff_scom_section); | 
 | static asection ecoff_scom_section = | 
 |   BFD_FAKE_SECTION (ecoff_scom_section, &ecoff_scom_symbol, | 
 | 		    SCOMMON, 0, SEC_IS_COMMON | SEC_SMALL_DATA); | 
 |  | 
 | /* Create an empty symbol.  */ | 
 |  | 
 | asymbol * | 
 | _bfd_ecoff_make_empty_symbol (bfd *abfd) | 
 | { | 
 |   ecoff_symbol_type *new_symbol; | 
 |   size_t amt = sizeof (ecoff_symbol_type); | 
 |  | 
 |   new_symbol = (ecoff_symbol_type *) bfd_zalloc (abfd, amt); | 
 |   if (new_symbol == NULL) | 
 |     return NULL; | 
 |   new_symbol->symbol.section = NULL; | 
 |   new_symbol->fdr = NULL; | 
 |   new_symbol->local = false; | 
 |   new_symbol->native = NULL; | 
 |   new_symbol->symbol.the_bfd = abfd; | 
 |   return &new_symbol->symbol; | 
 | } | 
 |  | 
 | /* Set the BFD flags and section for an ECOFF symbol.  */ | 
 |  | 
 | static bool | 
 | ecoff_set_symbol_info (bfd *abfd, | 
 | 		       SYMR *ecoff_sym, | 
 | 		       asymbol *asym, | 
 | 		       int ext, | 
 | 		       int weak) | 
 | { | 
 |   asym->the_bfd = abfd; | 
 |   asym->value = ecoff_sym->value; | 
 |   asym->section = &bfd_debug_section; | 
 |   asym->udata.i = 0; | 
 |  | 
 |   /* Most symbol types are just for debugging.  */ | 
 |   switch (ecoff_sym->st) | 
 |     { | 
 |     case stGlobal: | 
 |     case stStatic: | 
 |     case stLabel: | 
 |     case stProc: | 
 |     case stStaticProc: | 
 |       break; | 
 |     case stNil: | 
 |       if (ECOFF_IS_STAB (ecoff_sym)) | 
 | 	{ | 
 | 	  asym->flags = BSF_DEBUGGING; | 
 | 	  return true; | 
 | 	} | 
 |       break; | 
 |     default: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       return true; | 
 |     } | 
 |  | 
 |   if (weak) | 
 |     asym->flags = BSF_EXPORT | BSF_WEAK; | 
 |   else if (ext) | 
 |     asym->flags = BSF_EXPORT | BSF_GLOBAL; | 
 |   else | 
 |     { | 
 |       asym->flags = BSF_LOCAL; | 
 |       /* Normally, a local stProc symbol will have a corresponding | 
 | 	 external symbol.  We mark the local symbol as a debugging | 
 | 	 symbol, in order to prevent nm from printing both out. | 
 | 	 Similarly, we mark stLabel and stabs symbols as debugging | 
 | 	 symbols.  In both cases, we do want to set the value | 
 | 	 correctly based on the symbol class.  */ | 
 |       if (ecoff_sym->st == stProc | 
 | 	  || ecoff_sym->st == stLabel | 
 | 	  || ECOFF_IS_STAB (ecoff_sym)) | 
 | 	asym->flags |= BSF_DEBUGGING; | 
 |     } | 
 |  | 
 |   if (ecoff_sym->st == stProc || ecoff_sym->st == stStaticProc) | 
 |     asym->flags |= BSF_FUNCTION; | 
 |  | 
 |   switch (ecoff_sym->sc) | 
 |     { | 
 |     case scNil: | 
 |       /* Used for compiler generated labels.  Leave them in the | 
 | 	 debugging section, and mark them as local.  If BSF_DEBUGGING | 
 | 	 is set, then nm does not display them for some reason.  If no | 
 | 	 flags are set then the linker whines about them.  */ | 
 |       asym->flags = BSF_LOCAL; | 
 |       break; | 
 |     case scText: | 
 |       asym->section = bfd_make_section_old_way (abfd, _TEXT); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scData: | 
 |       asym->section = bfd_make_section_old_way (abfd, _DATA); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scBss: | 
 |       asym->section = bfd_make_section_old_way (abfd, _BSS); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scRegister: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       break; | 
 |     case scAbs: | 
 |       asym->section = bfd_abs_section_ptr; | 
 |       break; | 
 |     case scUndefined: | 
 |       asym->section = bfd_und_section_ptr; | 
 |       asym->flags = 0; | 
 |       asym->value = 0; | 
 |       break; | 
 |     case scCdbLocal: | 
 |     case scBits: | 
 |     case scCdbSystem: | 
 |     case scRegImage: | 
 |     case scInfo: | 
 |     case scUserStruct: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       break; | 
 |     case scSData: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".sdata"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scSBss: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".sbss"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scRData: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".rdata"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scVar: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       break; | 
 |     case scCommon: | 
 |       if (asym->value > ecoff_data (abfd)->gp_size) | 
 | 	{ | 
 | 	  asym->section = bfd_com_section_ptr; | 
 | 	  asym->flags = 0; | 
 | 	  break; | 
 | 	} | 
 |       /* Fall through.  */ | 
 |     case scSCommon: | 
 |       asym->section = &ecoff_scom_section; | 
 |       asym->flags = 0; | 
 |       break; | 
 |     case scVarRegister: | 
 |     case scVariant: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       break; | 
 |     case scSUndefined: | 
 |       asym->section = bfd_und_section_ptr; | 
 |       asym->flags = 0; | 
 |       asym->value = 0; | 
 |       break; | 
 |     case scInit: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".init"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scBasedVar: | 
 |     case scXData: | 
 |     case scPData: | 
 |       asym->flags = BSF_DEBUGGING; | 
 |       break; | 
 |     case scFini: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".fini"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     case scRConst: | 
 |       asym->section = bfd_make_section_old_way (abfd, ".rconst"); | 
 |       asym->value -= asym->section->vma; | 
 |       break; | 
 |     default: | 
 |       break; | 
 |     } | 
 |  | 
 |   /* Look for special constructors symbols and make relocation entries | 
 |      in a special construction section.  These are produced by the | 
 |      -fgnu-linker argument to g++.  */ | 
 |   if (ECOFF_IS_STAB (ecoff_sym)) | 
 |     { | 
 |       switch (ECOFF_UNMARK_STAB (ecoff_sym->index)) | 
 | 	{ | 
 | 	default: | 
 | 	  break; | 
 |  | 
 | 	case N_SETA: | 
 | 	case N_SETT: | 
 | 	case N_SETD: | 
 | 	case N_SETB: | 
 | 	  /* Mark the symbol as a constructor.  */ | 
 | 	  asym->flags |= BSF_CONSTRUCTOR; | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* Read an ECOFF symbol table.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_slurp_symbol_table (bfd *abfd) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   const bfd_size_type external_ext_size | 
 |     = backend->debug_swap.external_ext_size; | 
 |   const bfd_size_type external_sym_size | 
 |     = backend->debug_swap.external_sym_size; | 
 |   void (* const swap_ext_in) (bfd *, void *, EXTR *) | 
 |     = backend->debug_swap.swap_ext_in; | 
 |   void (* const swap_sym_in) (bfd *, void *, SYMR *) | 
 |     = backend->debug_swap.swap_sym_in; | 
 |   ecoff_symbol_type *internal; | 
 |   ecoff_symbol_type *internal_ptr; | 
 |   char *eraw_src; | 
 |   char *eraw_end; | 
 |   FDR *fdr_ptr; | 
 |   FDR *fdr_end; | 
 |   size_t amt; | 
 |  | 
 |   /* If we've already read in the symbol table, do nothing.  */ | 
 |   if (ecoff_data (abfd)->canonical_symbols != NULL) | 
 |     return true; | 
 |  | 
 |   /* Get the symbolic information.  */ | 
 |   if (! _bfd_ecoff_slurp_symbolic_info (abfd, NULL, | 
 | 					&ecoff_data (abfd)->debug_info)) | 
 |     return false; | 
 |   if (bfd_get_symcount (abfd) == 0) | 
 |     return true; | 
 |  | 
 |   if (_bfd_mul_overflow (bfd_get_symcount (abfd), | 
 | 			 sizeof (ecoff_symbol_type), &amt)) | 
 |     { | 
 |       bfd_set_error (bfd_error_file_too_big); | 
 |       return false; | 
 |     } | 
 |   internal = (ecoff_symbol_type *) bfd_alloc (abfd, amt); | 
 |   if (internal == NULL) | 
 |     return false; | 
 |  | 
 |   internal_ptr = internal; | 
 |   eraw_src = (char *) ecoff_data (abfd)->debug_info.external_ext; | 
 |   eraw_end = (eraw_src | 
 | 	      + (ecoff_data (abfd)->debug_info.symbolic_header.iextMax | 
 | 		 * external_ext_size)); | 
 |   for (; eraw_src < eraw_end; eraw_src += external_ext_size, internal_ptr++) | 
 |     { | 
 |       EXTR internal_esym; | 
 |  | 
 |       (*swap_ext_in) (abfd, (void *) eraw_src, &internal_esym); | 
 |  | 
 |       /* PR 17512: file: 3372-1000-0.004.  */ | 
 |       HDRR *symhdr = &ecoff_data (abfd)->debug_info.symbolic_header; | 
 |       if (internal_esym.asym.iss >= symhdr->issExtMax | 
 | 	  || internal_esym.asym.iss < 0) | 
 | 	{ | 
 | 	  bfd_set_error (bfd_error_bad_value); | 
 | 	  return false; | 
 | 	} | 
 |  | 
 |       internal_ptr->symbol.name = (ecoff_data (abfd)->debug_info.ssext | 
 | 				   + internal_esym.asym.iss); | 
 |  | 
 |       if (!ecoff_set_symbol_info (abfd, &internal_esym.asym, | 
 | 				  &internal_ptr->symbol, 1, | 
 | 				  internal_esym.weakext)) | 
 | 	return false; | 
 |  | 
 |       /* The alpha uses a negative ifd field for section symbols.  */ | 
 |       /* PR 17512: file: 3372-1983-0.004.  */ | 
 |       if (internal_esym.ifd >= symhdr->ifdMax | 
 | 	  || internal_esym.ifd < 0) | 
 | 	internal_ptr->fdr = NULL; | 
 |       else | 
 | 	internal_ptr->fdr = (ecoff_data (abfd)->debug_info.fdr | 
 | 			     + internal_esym.ifd); | 
 |       internal_ptr->local = false; | 
 |       internal_ptr->native = (void *) eraw_src; | 
 |     } | 
 |  | 
 |   /* The local symbols must be accessed via the fdr's, because the | 
 |      string and aux indices are relative to the fdr information.  */ | 
 |   fdr_ptr = ecoff_data (abfd)->debug_info.fdr; | 
 |   fdr_end = fdr_ptr + ecoff_data (abfd)->debug_info.symbolic_header.ifdMax; | 
 |   for (; fdr_ptr < fdr_end; fdr_ptr++) | 
 |     { | 
 |       char *lraw_src; | 
 |       char *lraw_end; | 
 |       HDRR *symhdr = &ecoff_data (abfd)->debug_info.symbolic_header; | 
 |  | 
 |       if (fdr_ptr->csym == 0) | 
 | 	continue; | 
 |       if (fdr_ptr->isymBase < 0 | 
 | 	  || fdr_ptr->isymBase > symhdr->isymMax | 
 | 	  || fdr_ptr->csym < 0 | 
 | 	  || fdr_ptr->csym > symhdr->isymMax - fdr_ptr->isymBase | 
 | 	  || fdr_ptr->csym > ((long) bfd_get_symcount (abfd) | 
 | 			      - (internal_ptr - internal)) | 
 | 	  || fdr_ptr->issBase < 0 | 
 | 	  || fdr_ptr->issBase > symhdr->issMax) | 
 | 	{ | 
 | 	  bfd_set_error (bfd_error_bad_value); | 
 | 	  return false; | 
 | 	} | 
 |       lraw_src = ((char *) ecoff_data (abfd)->debug_info.external_sym | 
 | 		  + fdr_ptr->isymBase * external_sym_size); | 
 |       lraw_end = lraw_src + fdr_ptr->csym * external_sym_size; | 
 |       for (; | 
 | 	   lraw_src < lraw_end; | 
 | 	   lraw_src += external_sym_size, internal_ptr++) | 
 | 	{ | 
 | 	  SYMR internal_sym; | 
 |  | 
 | 	  (*swap_sym_in) (abfd, (void *) lraw_src, &internal_sym); | 
 |  | 
 | 	  if (internal_sym.iss >= symhdr->issMax - fdr_ptr->issBase | 
 | 	      || internal_sym.iss < 0) | 
 | 	    { | 
 | 	      bfd_set_error (bfd_error_bad_value); | 
 | 	      return false; | 
 | 	    } | 
 | 	  internal_ptr->symbol.name = (ecoff_data (abfd)->debug_info.ss | 
 | 				       + fdr_ptr->issBase | 
 | 				       + internal_sym.iss); | 
 | 	  if (!ecoff_set_symbol_info (abfd, &internal_sym, | 
 | 				      &internal_ptr->symbol, 0, 0)) | 
 | 	    return false; | 
 | 	  internal_ptr->fdr = fdr_ptr; | 
 | 	  internal_ptr->local = true; | 
 | 	  internal_ptr->native = (void *) lraw_src; | 
 | 	} | 
 |     } | 
 |  | 
 |   /* PR 17512: file: 3372-3080-0.004. | 
 |      A discrepancy between ecoff_data (abfd)->debug_info.symbolic_header.isymMax | 
 |      and ecoff_data (abfd)->debug_info.symbolic_header.ifdMax can mean that | 
 |      we have fewer symbols than we were expecting.  Allow for this by updating | 
 |      the symbol count and warning the user.  */ | 
 |   if (internal_ptr - internal < (ptrdiff_t) bfd_get_symcount (abfd)) | 
 |     { | 
 |       abfd->symcount = internal_ptr - internal; | 
 |       _bfd_error_handler | 
 | 	/* xgettext:c-format */ | 
 | 	(_("%pB: warning: isymMax (%ld) is greater than ifdMax (%ld)"), | 
 | 	 abfd, ecoff_data (abfd)->debug_info.symbolic_header.isymMax, | 
 | 	 ecoff_data (abfd)->debug_info.symbolic_header.ifdMax); | 
 |     } | 
 |  | 
 |   ecoff_data (abfd)->canonical_symbols = internal; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Return the amount of space needed for the canonical symbols.  */ | 
 |  | 
 | long | 
 | _bfd_ecoff_get_symtab_upper_bound (bfd *abfd) | 
 | { | 
 |   if (! _bfd_ecoff_slurp_symbolic_info (abfd, NULL, | 
 | 					&ecoff_data (abfd)->debug_info)) | 
 |     return -1; | 
 |  | 
 |   if (bfd_get_symcount (abfd) == 0) | 
 |     return 0; | 
 |  | 
 |   return (bfd_get_symcount (abfd) + 1) * (sizeof (ecoff_symbol_type *)); | 
 | } | 
 |  | 
 | /* Get the canonical symbols.  */ | 
 |  | 
 | long | 
 | _bfd_ecoff_canonicalize_symtab (bfd *abfd, asymbol **alocation) | 
 | { | 
 |   unsigned int counter = 0; | 
 |   ecoff_symbol_type *symbase; | 
 |   ecoff_symbol_type **location = (ecoff_symbol_type **) alocation; | 
 |  | 
 |   if (! _bfd_ecoff_slurp_symbol_table (abfd)) | 
 |     return -1; | 
 |   if (bfd_get_symcount (abfd) == 0) | 
 |     return 0; | 
 |  | 
 |   symbase = ecoff_data (abfd)->canonical_symbols; | 
 |   while (counter < bfd_get_symcount (abfd)) | 
 |     { | 
 |       *(location++) = symbase++; | 
 |       counter++; | 
 |     } | 
 |   *location++ = NULL; | 
 |   return bfd_get_symcount (abfd); | 
 | } | 
 |  | 
 | /* Turn ECOFF type information into a printable string. | 
 |    ecoff_emit_aggregate and ecoff_type_to_string are from | 
 |    gcc/mips-tdump.c, with swapping added and used_ptr removed.  */ | 
 |  | 
 | /* Write aggregate information to a string.  */ | 
 |  | 
 | static void | 
 | ecoff_emit_aggregate (bfd *abfd, | 
 | 		      FDR *fdr, | 
 | 		      char *string, | 
 | 		      RNDXR *rndx, | 
 | 		      long isym, | 
 | 		      const char *which) | 
 | { | 
 |   const struct ecoff_debug_swap * const debug_swap = | 
 |     &ecoff_backend (abfd)->debug_swap; | 
 |   struct ecoff_debug_info * const debug_info = &ecoff_data (abfd)->debug_info; | 
 |   unsigned int ifd = rndx->rfd; | 
 |   unsigned int indx = rndx->index; | 
 |   const char *name; | 
 |  | 
 |   if (ifd == 0xfff) | 
 |     ifd = isym; | 
 |  | 
 |   /* An ifd of -1 is an opaque type.  An escaped index of 0 is a | 
 |      struct return type of a procedure compiled without -g.  */ | 
 |   if (ifd == 0xffffffff | 
 |       || (rndx->rfd == 0xfff && indx == 0)) | 
 |     name = "<undefined>"; | 
 |   else if (indx == indexNil) | 
 |     name = "<no name>"; | 
 |   else | 
 |     { | 
 |       SYMR sym; | 
 |  | 
 |       if (debug_info->external_rfd == NULL) | 
 | 	fdr = debug_info->fdr + ifd; | 
 |       else | 
 | 	{ | 
 | 	  RFDT rfd; | 
 |  | 
 | 	  (*debug_swap->swap_rfd_in) (abfd, | 
 | 				      ((char *) debug_info->external_rfd | 
 | 				       + ((fdr->rfdBase + ifd) | 
 | 					  * debug_swap->external_rfd_size)), | 
 | 				      &rfd); | 
 | 	  fdr = debug_info->fdr + rfd; | 
 | 	} | 
 |  | 
 |       indx += fdr->isymBase; | 
 |  | 
 |       (*debug_swap->swap_sym_in) (abfd, | 
 | 				  ((char *) debug_info->external_sym | 
 | 				   + indx * debug_swap->external_sym_size), | 
 | 				  &sym); | 
 |  | 
 |       name = debug_info->ss + fdr->issBase + sym.iss; | 
 |     } | 
 |  | 
 |   sprintf (string, | 
 | 	   "%s %s { ifd = %u, index = %lu }", | 
 | 	   which, name, ifd, | 
 | 	   ((unsigned long) indx | 
 | 	    + debug_info->symbolic_header.iextMax)); | 
 | } | 
 |  | 
 | /* Convert the type information to string format.  */ | 
 |  | 
 | static char * | 
 | ecoff_type_to_string (bfd *abfd, FDR *fdr, unsigned int indx, char *buff) | 
 | { | 
 |   union aux_ext *aux_ptr; | 
 |   int bigendian; | 
 |   AUXU u; | 
 |   struct qual | 
 |   { | 
 |     unsigned int  type; | 
 |     int  low_bound; | 
 |     int  high_bound; | 
 |     int  stride; | 
 |   } qualifiers[7]; | 
 |   unsigned int basic_type; | 
 |   int i; | 
 |   char buffer1[1024]; | 
 |   char *p1 = buffer1; | 
 |   char *p2 = buff; | 
 |   RNDXR rndx; | 
 |  | 
 |   aux_ptr = ecoff_data (abfd)->debug_info.external_aux + fdr->iauxBase; | 
 |   bigendian = fdr->fBigendian; | 
 |  | 
 |   for (i = 0; i < 7; i++) | 
 |     { | 
 |       qualifiers[i].low_bound = 0; | 
 |       qualifiers[i].high_bound = 0; | 
 |       qualifiers[i].stride = 0; | 
 |     } | 
 |  | 
 |   if (AUX_GET_ISYM (bigendian, &aux_ptr[indx]) == (bfd_vma) -1) | 
 |     return "-1 (no type)"; | 
 |   _bfd_ecoff_swap_tir_in (bigendian, &aux_ptr[indx++].a_ti, &u.ti); | 
 |  | 
 |   basic_type = u.ti.bt; | 
 |   qualifiers[0].type = u.ti.tq0; | 
 |   qualifiers[1].type = u.ti.tq1; | 
 |   qualifiers[2].type = u.ti.tq2; | 
 |   qualifiers[3].type = u.ti.tq3; | 
 |   qualifiers[4].type = u.ti.tq4; | 
 |   qualifiers[5].type = u.ti.tq5; | 
 |   qualifiers[6].type = tqNil; | 
 |  | 
 |   /* Go get the basic type.  */ | 
 |   switch (basic_type) | 
 |     { | 
 |     case btNil:			/* Undefined.  */ | 
 |       strcpy (p1, "nil"); | 
 |       break; | 
 |  | 
 |     case btAdr:			/* Address - integer same size as pointer.  */ | 
 |       strcpy (p1, "address"); | 
 |       break; | 
 |  | 
 |     case btChar:		/* Character.  */ | 
 |       strcpy (p1, "char"); | 
 |       break; | 
 |  | 
 |     case btUChar:		/* Unsigned character.  */ | 
 |       strcpy (p1, "unsigned char"); | 
 |       break; | 
 |  | 
 |     case btShort:		/* Short.  */ | 
 |       strcpy (p1, "short"); | 
 |       break; | 
 |  | 
 |     case btUShort:		/* Unsigned short.  */ | 
 |       strcpy (p1, "unsigned short"); | 
 |       break; | 
 |  | 
 |     case btInt:			/* Int.  */ | 
 |       strcpy (p1, "int"); | 
 |       break; | 
 |  | 
 |     case btUInt:		/* Unsigned int.  */ | 
 |       strcpy (p1, "unsigned int"); | 
 |       break; | 
 |  | 
 |     case btLong:		/* Long.  */ | 
 |       strcpy (p1, "long"); | 
 |       break; | 
 |  | 
 |     case btULong:		/* Unsigned long.  */ | 
 |       strcpy (p1, "unsigned long"); | 
 |       break; | 
 |  | 
 |     case btFloat:		/* Float (real).  */ | 
 |       strcpy (p1, "float"); | 
 |       break; | 
 |  | 
 |     case btDouble:		/* Double (real).  */ | 
 |       strcpy (p1, "double"); | 
 |       break; | 
 |  | 
 |       /* Structures add 1-2 aux words: | 
 | 	 1st word is [ST_RFDESCAPE, offset] pointer to struct def; | 
 | 	 2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */ | 
 |  | 
 |     case btStruct:		/* Structure (Record).  */ | 
 |       _bfd_ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); | 
 |       ecoff_emit_aggregate (abfd, fdr, p1, &rndx, | 
 | 			    (long) AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), | 
 | 			    "struct"); | 
 |       indx++;			/* Skip aux words.  */ | 
 |       break; | 
 |  | 
 |       /* Unions add 1-2 aux words: | 
 | 	 1st word is [ST_RFDESCAPE, offset] pointer to union def; | 
 | 	 2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */ | 
 |  | 
 |     case btUnion:		/* Union.  */ | 
 |       _bfd_ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); | 
 |       ecoff_emit_aggregate (abfd, fdr, p1, &rndx, | 
 | 			    (long) AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), | 
 | 			    "union"); | 
 |       indx++;			/* Skip aux words.  */ | 
 |       break; | 
 |  | 
 |       /* Enumerations add 1-2 aux words: | 
 | 	 1st word is [ST_RFDESCAPE, offset] pointer to enum def; | 
 | 	 2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */ | 
 |  | 
 |     case btEnum:		/* Enumeration.  */ | 
 |       _bfd_ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); | 
 |       ecoff_emit_aggregate (abfd, fdr, p1, &rndx, | 
 | 			    (long) AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), | 
 | 			    "enum"); | 
 |       indx++;			/* Skip aux words.  */ | 
 |       break; | 
 |  | 
 |     case btTypedef:		/* Defined via a typedef, isymRef points.  */ | 
 |       strcpy (p1, "typedef"); | 
 |       break; | 
 |  | 
 |     case btRange:		/* Subrange of int.  */ | 
 |       strcpy (p1, "subrange"); | 
 |       break; | 
 |  | 
 |     case btSet:			/* Pascal sets.  */ | 
 |       strcpy (p1, "set"); | 
 |       break; | 
 |  | 
 |     case btComplex:		/* Fortran complex.  */ | 
 |       strcpy (p1, "complex"); | 
 |       break; | 
 |  | 
 |     case btDComplex:		/* Fortran double complex.  */ | 
 |       strcpy (p1, "double complex"); | 
 |       break; | 
 |  | 
 |     case btIndirect:		/* Forward or unnamed typedef.  */ | 
 |       strcpy (p1, "forward/unamed typedef"); | 
 |       break; | 
 |  | 
 |     case btFixedDec:		/* Fixed Decimal.  */ | 
 |       strcpy (p1, "fixed decimal"); | 
 |       break; | 
 |  | 
 |     case btFloatDec:		/* Float Decimal.  */ | 
 |       strcpy (p1, "float decimal"); | 
 |       break; | 
 |  | 
 |     case btString:		/* Varying Length Character String.  */ | 
 |       strcpy (p1, "string"); | 
 |       break; | 
 |  | 
 |     case btBit:			/* Aligned Bit String.  */ | 
 |       strcpy (p1, "bit"); | 
 |       break; | 
 |  | 
 |     case btPicture:		/* Picture.  */ | 
 |       strcpy (p1, "picture"); | 
 |       break; | 
 |  | 
 |     case btVoid:		/* Void.  */ | 
 |       strcpy (p1, "void"); | 
 |       break; | 
 |  | 
 |     default: | 
 |       sprintf (p1, _("unknown basic type %d"), (int) basic_type); | 
 |       break; | 
 |     } | 
 |  | 
 |   p1 += strlen (p1); | 
 |  | 
 |   /* If this is a bitfield, get the bitsize.  */ | 
 |   if (u.ti.fBitfield) | 
 |     { | 
 |       int bitsize; | 
 |  | 
 |       bitsize = AUX_GET_WIDTH (bigendian, &aux_ptr[indx++]); | 
 |       sprintf (p1, " : %d", bitsize); | 
 |     } | 
 |  | 
 |   /* Deal with any qualifiers.  */ | 
 |   if (qualifiers[0].type != tqNil) | 
 |     { | 
 |       /* Snarf up any array bounds in the correct order.  Arrays | 
 | 	 store 5 successive words in the aux. table: | 
 | 		word 0	RNDXR to type of the bounds (ie, int) | 
 | 		word 1	Current file descriptor index | 
 | 		word 2	low bound | 
 | 		word 3	high bound (or -1 if []) | 
 | 		word 4	stride size in bits.  */ | 
 |       for (i = 0; i < 7; i++) | 
 | 	{ | 
 | 	  if (qualifiers[i].type == tqArray) | 
 | 	    { | 
 | 	      qualifiers[i].low_bound = | 
 | 		AUX_GET_DNLOW (bigendian, &aux_ptr[indx+2]); | 
 | 	      qualifiers[i].high_bound = | 
 | 		AUX_GET_DNHIGH (bigendian, &aux_ptr[indx+3]); | 
 | 	      qualifiers[i].stride = | 
 | 		AUX_GET_WIDTH (bigendian, &aux_ptr[indx+4]); | 
 | 	      indx += 5; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       /* Now print out the qualifiers.  */ | 
 |       for (i = 0; i < 6; i++) | 
 | 	{ | 
 | 	  switch (qualifiers[i].type) | 
 | 	    { | 
 | 	    case tqNil: | 
 | 	    case tqMax: | 
 | 	      break; | 
 |  | 
 | 	    case tqPtr: | 
 | 	      strcpy (p2, "ptr to "); | 
 | 	      p2 += sizeof ("ptr to ")-1; | 
 | 	      break; | 
 |  | 
 | 	    case tqVol: | 
 | 	      strcpy (p2, "volatile "); | 
 | 	      p2 += sizeof ("volatile ")-1; | 
 | 	      break; | 
 |  | 
 | 	    case tqFar: | 
 | 	      strcpy (p2, "far "); | 
 | 	      p2 += sizeof ("far ")-1; | 
 | 	      break; | 
 |  | 
 | 	    case tqProc: | 
 | 	      strcpy (p2, "func. ret. "); | 
 | 	      p2 += sizeof ("func. ret. "); | 
 | 	      break; | 
 |  | 
 | 	    case tqArray: | 
 | 	      { | 
 | 		int first_array = i; | 
 | 		int j; | 
 |  | 
 | 		/* Print array bounds reversed (ie, in the order the C | 
 | 		   programmer writes them).  C is such a fun language....  */ | 
 | 		while (i < 5 && qualifiers[i+1].type == tqArray) | 
 | 		  i++; | 
 |  | 
 | 		for (j = i; j >= first_array; j--) | 
 | 		  { | 
 | 		    strcpy (p2, "array ["); | 
 | 		    p2 += sizeof ("array [")-1; | 
 | 		    if (qualifiers[j].low_bound != 0) | 
 | 		      sprintf (p2, | 
 | 			       "%ld:%ld {%ld bits}", | 
 | 			       (long) qualifiers[j].low_bound, | 
 | 			       (long) qualifiers[j].high_bound, | 
 | 			       (long) qualifiers[j].stride); | 
 |  | 
 | 		    else if (qualifiers[j].high_bound != -1) | 
 | 		      sprintf (p2, | 
 | 			       "%ld {%ld bits}", | 
 | 			       (long) (qualifiers[j].high_bound + 1), | 
 | 			       (long) (qualifiers[j].stride)); | 
 |  | 
 | 		    else | 
 | 		      sprintf (p2, " {%ld bits}", (long) qualifiers[j].stride); | 
 |  | 
 | 		    p2 += strlen (p2); | 
 | 		    strcpy (p2, "] of "); | 
 | 		    p2 += sizeof ("] of ")-1; | 
 | 		  } | 
 | 	      } | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   strcpy (p2, buffer1); | 
 |   return buff; | 
 | } | 
 |  | 
 | /* Return information about ECOFF symbol SYMBOL in RET.  */ | 
 |  | 
 | void | 
 | _bfd_ecoff_get_symbol_info (bfd *abfd ATTRIBUTE_UNUSED, | 
 | 			    asymbol *symbol, | 
 | 			    symbol_info *ret) | 
 | { | 
 |   bfd_symbol_info (symbol, ret); | 
 | } | 
 |  | 
 | /* Return whether this is a local label.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_bfd_is_local_label_name (bfd *abfd ATTRIBUTE_UNUSED, | 
 | 				    const char *name) | 
 | { | 
 |   return name[0] == '$'; | 
 | } | 
 |  | 
 | /* Print information about an ECOFF symbol.  */ | 
 |  | 
 | void | 
 | _bfd_ecoff_print_symbol (bfd *abfd, | 
 | 			 void * filep, | 
 | 			 asymbol *symbol, | 
 | 			 bfd_print_symbol_type how) | 
 | { | 
 |   const struct ecoff_debug_swap * const debug_swap | 
 |     = &ecoff_backend (abfd)->debug_swap; | 
 |   FILE *file = (FILE *)filep; | 
 |  | 
 |   switch (how) | 
 |     { | 
 |     case bfd_print_symbol_name: | 
 |       fprintf (file, "%s", symbol->name); | 
 |       break; | 
 |     case bfd_print_symbol_more: | 
 |       if (ecoffsymbol (symbol)->local) | 
 | 	{ | 
 | 	  SYMR ecoff_sym; | 
 |  | 
 | 	  (*debug_swap->swap_sym_in) (abfd, ecoffsymbol (symbol)->native, | 
 | 				      &ecoff_sym); | 
 | 	  fprintf (file, "ecoff local "); | 
 | 	  bfd_fprintf_vma (abfd, file, ecoff_sym.value); | 
 | 	  fprintf (file, " %x %x", (unsigned) ecoff_sym.st, | 
 | 		   (unsigned) ecoff_sym.sc); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  EXTR ecoff_ext; | 
 |  | 
 | 	  (*debug_swap->swap_ext_in) (abfd, ecoffsymbol (symbol)->native, | 
 | 				      &ecoff_ext); | 
 | 	  fprintf (file, "ecoff extern "); | 
 | 	  bfd_fprintf_vma (abfd, file, ecoff_ext.asym.value); | 
 | 	  fprintf (file, " %x %x", (unsigned) ecoff_ext.asym.st, | 
 | 		   (unsigned) ecoff_ext.asym.sc); | 
 | 	} | 
 |       break; | 
 |     case bfd_print_symbol_all: | 
 |       /* Print out the symbols in a reasonable way.  */ | 
 |       { | 
 | 	char type; | 
 | 	int pos; | 
 | 	EXTR ecoff_ext; | 
 | 	char jmptbl; | 
 | 	char cobol_main; | 
 | 	char weakext; | 
 |  | 
 | 	if (ecoffsymbol (symbol)->local) | 
 | 	  { | 
 | 	    (*debug_swap->swap_sym_in) (abfd, ecoffsymbol (symbol)->native, | 
 | 					&ecoff_ext.asym); | 
 | 	    type = 'l'; | 
 | 	    pos = ((((char *) ecoffsymbol (symbol)->native | 
 | 		     - (char *) ecoff_data (abfd)->debug_info.external_sym) | 
 | 		    / debug_swap->external_sym_size) | 
 | 		   + ecoff_data (abfd)->debug_info.symbolic_header.iextMax); | 
 | 	    jmptbl = ' '; | 
 | 	    cobol_main = ' '; | 
 | 	    weakext = ' '; | 
 | 	  } | 
 | 	else | 
 | 	  { | 
 | 	    (*debug_swap->swap_ext_in) (abfd, ecoffsymbol (symbol)->native, | 
 | 					&ecoff_ext); | 
 | 	    type = 'e'; | 
 | 	    pos = (((char *) ecoffsymbol (symbol)->native | 
 | 		    - (char *) ecoff_data (abfd)->debug_info.external_ext) | 
 | 		   / debug_swap->external_ext_size); | 
 | 	    jmptbl = ecoff_ext.jmptbl ? 'j' : ' '; | 
 | 	    cobol_main = ecoff_ext.cobol_main ? 'c' : ' '; | 
 | 	    weakext = ecoff_ext.weakext ? 'w' : ' '; | 
 | 	  } | 
 |  | 
 | 	fprintf (file, "[%3d] %c ", | 
 | 		 pos, type); | 
 | 	bfd_fprintf_vma (abfd, file, ecoff_ext.asym.value); | 
 | 	fprintf (file, " st %x sc %x indx %x %c%c%c %s", | 
 | 		 (unsigned) ecoff_ext.asym.st, | 
 | 		 (unsigned) ecoff_ext.asym.sc, | 
 | 		 (unsigned) ecoff_ext.asym.index, | 
 | 		 jmptbl, cobol_main, weakext, | 
 | 		 symbol->name); | 
 |  | 
 | 	if (ecoffsymbol (symbol)->fdr != NULL | 
 | 	    && ecoff_ext.asym.index != indexNil) | 
 | 	  { | 
 | 	    FDR *fdr; | 
 | 	    unsigned int indx; | 
 | 	    int bigendian; | 
 | 	    bfd_size_type sym_base; | 
 | 	    union aux_ext *aux_base; | 
 |  | 
 | 	    fdr = ecoffsymbol (symbol)->fdr; | 
 | 	    indx = ecoff_ext.asym.index; | 
 |  | 
 | 	    /* sym_base is used to map the fdr relative indices which | 
 | 	       appear in the file to the position number which we are | 
 | 	       using.  */ | 
 | 	    sym_base = fdr->isymBase; | 
 | 	    if (ecoffsymbol (symbol)->local) | 
 | 	      sym_base += | 
 | 		ecoff_data (abfd)->debug_info.symbolic_header.iextMax; | 
 |  | 
 | 	    /* aux_base is the start of the aux entries for this file; | 
 | 	       asym.index is an offset from this.  */ | 
 | 	    aux_base = (ecoff_data (abfd)->debug_info.external_aux | 
 | 			+ fdr->iauxBase); | 
 |  | 
 | 	    /* The aux entries are stored in host byte order; the | 
 | 	       order is indicated by a bit in the fdr.  */ | 
 | 	    bigendian = fdr->fBigendian; | 
 |  | 
 | 	    /* This switch is basically from gcc/mips-tdump.c.  */ | 
 | 	    switch (ecoff_ext.asym.st) | 
 | 	      { | 
 | 	      case stNil: | 
 | 	      case stLabel: | 
 | 		break; | 
 |  | 
 | 	      case stFile: | 
 | 	      case stBlock: | 
 | 		fprintf (file, _("\n      End+1 symbol: %ld"), | 
 | 			 (long) (indx + sym_base)); | 
 | 		break; | 
 |  | 
 | 	      case stEnd: | 
 | 		if (ecoff_ext.asym.sc == scText | 
 | 		    || ecoff_ext.asym.sc == scInfo) | 
 | 		  fprintf (file, _("\n      First symbol: %ld"), | 
 | 			   (long) (indx + sym_base)); | 
 | 		else | 
 | 		  fprintf (file, _("\n      First symbol: %ld"), | 
 | 			   ((long) | 
 | 			    (AUX_GET_ISYM (bigendian, | 
 | 					   &aux_base[ecoff_ext.asym.index]) | 
 | 			     + sym_base))); | 
 | 		break; | 
 |  | 
 | 	      case stProc: | 
 | 	      case stStaticProc: | 
 | 		if (ECOFF_IS_STAB (&ecoff_ext.asym)) | 
 | 		  ; | 
 | 		else if (ecoffsymbol (symbol)->local) | 
 | 		  { | 
 | 		    char buff[1024]; | 
 | 		    /* xgettext:c-format */ | 
 | 		    fprintf (file, _("\n      End+1 symbol: %-7ld   Type:  %s"), | 
 | 			     ((long) | 
 | 			      (AUX_GET_ISYM (bigendian, | 
 | 					     &aux_base[ecoff_ext.asym.index]) | 
 | 			       + sym_base)), | 
 | 			     ecoff_type_to_string (abfd, fdr, indx + 1, buff)); | 
 | 		  } | 
 | 		else | 
 | 		  fprintf (file, _("\n      Local symbol: %ld"), | 
 | 			   ((long) indx | 
 | 			    + (long) sym_base | 
 | 			    + (ecoff_data (abfd) | 
 | 			       ->debug_info.symbolic_header.iextMax))); | 
 | 		break; | 
 |  | 
 | 	      case stStruct: | 
 | 		fprintf (file, _("\n      struct; End+1 symbol: %ld"), | 
 | 			 (long) (indx + sym_base)); | 
 | 		break; | 
 |  | 
 | 	      case stUnion: | 
 | 		fprintf (file, _("\n      union; End+1 symbol: %ld"), | 
 | 			 (long) (indx + sym_base)); | 
 | 		break; | 
 |  | 
 | 	      case stEnum: | 
 | 		fprintf (file, _("\n      enum; End+1 symbol: %ld"), | 
 | 			 (long) (indx + sym_base)); | 
 | 		break; | 
 |  | 
 | 	      default: | 
 | 		if (! ECOFF_IS_STAB (&ecoff_ext.asym)) | 
 | 		  { | 
 | 		    char buff[1024]; | 
 | 		    fprintf (file, _("\n      Type: %s"), | 
 | 			     ecoff_type_to_string (abfd, fdr, indx, buff)); | 
 | 		  } | 
 | 		break; | 
 | 	      } | 
 | 	  } | 
 |       } | 
 |       break; | 
 |     } | 
 | } | 
 |  | 
 | /* Read in the relocs for a section.  */ | 
 |  | 
 | static bool | 
 | ecoff_slurp_reloc_table (bfd *abfd, | 
 | 			 asection *section, | 
 | 			 asymbol **symbols) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   arelent *internal_relocs; | 
 |   bfd_size_type external_reloc_size; | 
 |   bfd_size_type amt; | 
 |   bfd_byte *external_relocs; | 
 |   arelent *rptr; | 
 |   unsigned int i; | 
 |  | 
 |   if (section->relocation != NULL | 
 |       || section->reloc_count == 0 | 
 |       || (section->flags & SEC_CONSTRUCTOR) != 0) | 
 |     return true; | 
 |  | 
 |   if (! _bfd_ecoff_slurp_symbol_table (abfd)) | 
 |     return false; | 
 |  | 
 |   external_reloc_size = backend->external_reloc_size; | 
 |   amt = external_reloc_size * section->reloc_count; | 
 |   if (bfd_seek (abfd, section->rel_filepos, SEEK_SET) != 0) | 
 |     return false; | 
 |   external_relocs = _bfd_malloc_and_read (abfd, amt, amt); | 
 |   if (external_relocs == NULL) | 
 |     return false; | 
 |  | 
 |   amt = section->reloc_count; | 
 |   amt *= sizeof (arelent); | 
 |   internal_relocs = (arelent *) bfd_alloc (abfd, amt); | 
 |   if (internal_relocs == NULL) | 
 |     { | 
 |       free (external_relocs); | 
 |       return false; | 
 |     } | 
 |  | 
 |   for (i = 0, rptr = internal_relocs; i < section->reloc_count; i++, rptr++) | 
 |     { | 
 |       struct internal_reloc intern; | 
 |  | 
 |       (*backend->swap_reloc_in) (abfd, | 
 | 				 external_relocs + i * external_reloc_size, | 
 | 				 &intern); | 
 |       rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; | 
 |       rptr->addend = 0; | 
 |  | 
 |       if (intern.r_extern) | 
 | 	{ | 
 | 	  /* r_symndx is an index into the external symbols.  */ | 
 | 	  if (symbols != NULL | 
 | 	      && intern.r_symndx >= 0 | 
 | 	      && (intern.r_symndx | 
 | 		  < (ecoff_data (abfd)->debug_info.symbolic_header.iextMax))) | 
 | 	    rptr->sym_ptr_ptr = symbols + intern.r_symndx; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  const char *sec_name; | 
 | 	  asection *sec; | 
 |  | 
 | 	  /* r_symndx is a section key.  */ | 
 | 	  switch (intern.r_symndx) | 
 | 	    { | 
 | 	    case RELOC_SECTION_TEXT:  sec_name = _TEXT;  break; | 
 | 	    case RELOC_SECTION_RDATA: sec_name = _RDATA; break; | 
 | 	    case RELOC_SECTION_DATA:  sec_name = _DATA;  break; | 
 | 	    case RELOC_SECTION_SDATA: sec_name = _SDATA; break; | 
 | 	    case RELOC_SECTION_SBSS:  sec_name = _SBSS;  break; | 
 | 	    case RELOC_SECTION_BSS:   sec_name = _BSS;   break; | 
 | 	    case RELOC_SECTION_INIT:  sec_name = _INIT;  break; | 
 | 	    case RELOC_SECTION_LIT8:  sec_name = _LIT8;  break; | 
 | 	    case RELOC_SECTION_LIT4:  sec_name = _LIT4;  break; | 
 | 	    case RELOC_SECTION_XDATA: sec_name = _XDATA; break; | 
 | 	    case RELOC_SECTION_PDATA: sec_name = _PDATA; break; | 
 | 	    case RELOC_SECTION_FINI:  sec_name = _FINI;  break; | 
 | 	    case RELOC_SECTION_LITA:  sec_name = _LITA;  break; | 
 | 	    case RELOC_SECTION_RCONST: sec_name = _RCONST; break; | 
 | 	    default: | 
 | 	      sec_name = NULL; | 
 | 	      break; | 
 | 	    } | 
 |  | 
 | 	  if (sec_name != NULL) | 
 | 	    { | 
 | 	      sec = bfd_get_section_by_name (abfd, sec_name); | 
 | 	      if (sec != NULL) | 
 | 		{ | 
 | 		  rptr->sym_ptr_ptr = sec->symbol_ptr_ptr; | 
 | 		  rptr->addend = - bfd_section_vma (sec); | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |  | 
 |       rptr->address = intern.r_vaddr - bfd_section_vma (section); | 
 |  | 
 |       /* Let the backend select the howto field and do any other | 
 | 	 required processing.  */ | 
 |       (*backend->adjust_reloc_in) (abfd, &intern, rptr); | 
 |     } | 
 |  | 
 |   free (external_relocs); | 
 |  | 
 |   section->relocation = internal_relocs; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Get a canonical list of relocs.  */ | 
 |  | 
 | long | 
 | _bfd_ecoff_canonicalize_reloc (bfd *abfd, | 
 | 			       asection *section, | 
 | 			       arelent **relptr, | 
 | 			       asymbol **symbols) | 
 | { | 
 |   unsigned int count; | 
 |  | 
 |   if (section->flags & SEC_CONSTRUCTOR) | 
 |     { | 
 |       arelent_chain *chain; | 
 |  | 
 |       /* This section has relocs made up by us, not the file, so take | 
 | 	 them out of their chain and place them into the data area | 
 | 	 provided.  */ | 
 |       for (count = 0, chain = section->constructor_chain; | 
 | 	   count < section->reloc_count; | 
 | 	   count++, chain = chain->next) | 
 | 	*relptr++ = &chain->relent; | 
 |     } | 
 |   else | 
 |     { | 
 |       arelent *tblptr; | 
 |  | 
 |       if (! ecoff_slurp_reloc_table (abfd, section, symbols)) | 
 | 	return -1; | 
 |  | 
 |       tblptr = section->relocation; | 
 |  | 
 |       for (count = 0; count < section->reloc_count; count++) | 
 | 	*relptr++ = tblptr++; | 
 |     } | 
 |  | 
 |   *relptr = NULL; | 
 |  | 
 |   return section->reloc_count; | 
 | } | 
 |  | 
 | /* Provided a BFD, a section and an offset into the section, calculate | 
 |    and return the name of the source file and the line nearest to the | 
 |    wanted location.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_find_nearest_line (bfd *abfd, | 
 | 			      asymbol **symbols ATTRIBUTE_UNUSED, | 
 | 			      asection *section, | 
 | 			      bfd_vma offset, | 
 | 			      const char **filename_ptr, | 
 | 			      const char **functionname_ptr, | 
 | 			      unsigned int *retline_ptr, | 
 | 			      unsigned int *discriminator_ptr) | 
 | { | 
 |   const struct ecoff_debug_swap * const debug_swap | 
 |     = &ecoff_backend (abfd)->debug_swap; | 
 |   struct ecoff_debug_info * const debug_info = &ecoff_data (abfd)->debug_info; | 
 |   struct ecoff_find_line *line_info; | 
 |  | 
 |   /* Make sure we have the FDR's.  */ | 
 |   if (! _bfd_ecoff_slurp_symbolic_info (abfd, NULL, debug_info) | 
 |       || bfd_get_symcount (abfd) == 0) | 
 |     return false; | 
 |  | 
 |   if (ecoff_data (abfd)->find_line_info == NULL) | 
 |     { | 
 |       size_t amt = sizeof (struct ecoff_find_line); | 
 |  | 
 |       ecoff_data (abfd)->find_line_info = | 
 | 	  (struct ecoff_find_line *) bfd_zalloc (abfd, amt); | 
 |       if (ecoff_data (abfd)->find_line_info == NULL) | 
 | 	return false; | 
 |     } | 
 |  | 
 |   if (discriminator_ptr) | 
 |     *discriminator_ptr = 0; | 
 |   line_info = ecoff_data (abfd)->find_line_info; | 
 |   return _bfd_ecoff_locate_line (abfd, section, offset, debug_info, | 
 | 				 debug_swap, line_info, filename_ptr, | 
 | 				 functionname_ptr, retline_ptr); | 
 | } | 
 |  | 
 | /* Copy private BFD data.  This is called by objcopy and strip.  We | 
 |    use it to copy the ECOFF debugging information from one BFD to the | 
 |    other.  It would be theoretically possible to represent the ECOFF | 
 |    debugging information in the symbol table.  However, it would be a | 
 |    lot of work, and there would be little gain (gas, gdb, and ld | 
 |    already access the ECOFF debugging information via the | 
 |    ecoff_debug_info structure, and that structure would have to be | 
 |    retained in order to support ECOFF debugging in MIPS ELF). | 
 |  | 
 |    The debugging information for the ECOFF external symbols comes from | 
 |    the symbol table, so this function only handles the other debugging | 
 |    information.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_bfd_copy_private_bfd_data (bfd *ibfd, bfd *obfd) | 
 | { | 
 |   struct ecoff_debug_info *iinfo = &ecoff_data (ibfd)->debug_info; | 
 |   struct ecoff_debug_info *oinfo = &ecoff_data (obfd)->debug_info; | 
 |   int i; | 
 |   asymbol **sym_ptr_ptr; | 
 |   size_t c; | 
 |   bool local; | 
 |  | 
 |   /* We only want to copy information over if both BFD's use ECOFF | 
 |      format.  */ | 
 |   if (bfd_get_flavour (ibfd) != bfd_target_ecoff_flavour | 
 |       || bfd_get_flavour (obfd) != bfd_target_ecoff_flavour) | 
 |     return true; | 
 |  | 
 |   /* Copy the GP value and the register masks.  */ | 
 |   ecoff_data (obfd)->gp = ecoff_data (ibfd)->gp; | 
 |   ecoff_data (obfd)->gprmask = ecoff_data (ibfd)->gprmask; | 
 |   ecoff_data (obfd)->fprmask = ecoff_data (ibfd)->fprmask; | 
 |   for (i = 0; i < 3; i++) | 
 |     ecoff_data (obfd)->cprmask[i] = ecoff_data (ibfd)->cprmask[i]; | 
 |  | 
 |   /* Copy the version stamp.  */ | 
 |   oinfo->symbolic_header.vstamp = iinfo->symbolic_header.vstamp; | 
 |  | 
 |   /* If there are no symbols, don't copy any debugging information.  */ | 
 |   c = bfd_get_symcount (obfd); | 
 |   sym_ptr_ptr = bfd_get_outsymbols (obfd); | 
 |   if (c == 0 || sym_ptr_ptr == NULL) | 
 |     return true; | 
 |  | 
 |   /* See if there are any local symbols.  */ | 
 |   local = false; | 
 |   for (; c > 0; c--, sym_ptr_ptr++) | 
 |     { | 
 |       if (ecoffsymbol (*sym_ptr_ptr)->local) | 
 | 	{ | 
 | 	  local = true; | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (local) | 
 |     { | 
 |       /* There are some local symbols.  We just bring over all the | 
 | 	 debugging information.  FIXME: This is not quite the right | 
 | 	 thing to do.  If the user has asked us to discard all | 
 | 	 debugging information, then we are probably going to wind up | 
 | 	 keeping it because there will probably be some local symbol | 
 | 	 which objcopy did not discard.  We should actually break | 
 | 	 apart the debugging information and only keep that which | 
 | 	 applies to the symbols we want to keep.  */ | 
 |       oinfo->symbolic_header.ilineMax = iinfo->symbolic_header.ilineMax; | 
 |       oinfo->symbolic_header.cbLine = iinfo->symbolic_header.cbLine; | 
 |       oinfo->line = iinfo->line; | 
 |  | 
 |       oinfo->symbolic_header.idnMax = iinfo->symbolic_header.idnMax; | 
 |       oinfo->external_dnr = iinfo->external_dnr; | 
 |  | 
 |       oinfo->symbolic_header.ipdMax = iinfo->symbolic_header.ipdMax; | 
 |       oinfo->external_pdr = iinfo->external_pdr; | 
 |  | 
 |       oinfo->symbolic_header.isymMax = iinfo->symbolic_header.isymMax; | 
 |       oinfo->external_sym = iinfo->external_sym; | 
 |  | 
 |       oinfo->symbolic_header.ioptMax = iinfo->symbolic_header.ioptMax; | 
 |       oinfo->external_opt = iinfo->external_opt; | 
 |  | 
 |       oinfo->symbolic_header.iauxMax = iinfo->symbolic_header.iauxMax; | 
 |       oinfo->external_aux = iinfo->external_aux; | 
 |  | 
 |       oinfo->symbolic_header.issMax = iinfo->symbolic_header.issMax; | 
 |       oinfo->ss = iinfo->ss; | 
 |  | 
 |       oinfo->symbolic_header.ifdMax = iinfo->symbolic_header.ifdMax; | 
 |       oinfo->external_fdr = iinfo->external_fdr; | 
 |  | 
 |       oinfo->symbolic_header.crfd = iinfo->symbolic_header.crfd; | 
 |       oinfo->external_rfd = iinfo->external_rfd; | 
 |  | 
 |       /* Flag that oinfo entries should not be freed.  */ | 
 |       oinfo->alloc_syments = true; | 
 |     } | 
 |   else | 
 |     { | 
 |       /* We are discarding all the local symbol information.  Look | 
 | 	 through the external symbols and remove all references to FDR | 
 | 	 or aux information.  */ | 
 |       c = bfd_get_symcount (obfd); | 
 |       sym_ptr_ptr = bfd_get_outsymbols (obfd); | 
 |       for (; c > 0; c--, sym_ptr_ptr++) | 
 | 	{ | 
 | 	  EXTR esym; | 
 |  | 
 | 	  (*(ecoff_backend (obfd)->debug_swap.swap_ext_in)) | 
 | 	    (obfd, ecoffsymbol (*sym_ptr_ptr)->native, &esym); | 
 | 	  esym.ifd = ifdNil; | 
 | 	  esym.asym.index = indexNil; | 
 | 	  (*(ecoff_backend (obfd)->debug_swap.swap_ext_out)) | 
 | 	    (obfd, &esym, ecoffsymbol (*sym_ptr_ptr)->native); | 
 | 	} | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Set the architecture.  The supported architecture is stored in the | 
 |    backend pointer.  We always set the architecture anyhow, since many | 
 |    callers ignore the return value.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_set_arch_mach (bfd *abfd, | 
 | 			  enum bfd_architecture arch, | 
 | 			  unsigned long machine) | 
 | { | 
 |   bfd_default_set_arch_mach (abfd, arch, machine); | 
 |   return arch == ecoff_backend (abfd)->arch; | 
 | } | 
 |  | 
 | /* Get the size of the section headers.  */ | 
 |  | 
 | int | 
 | _bfd_ecoff_sizeof_headers (bfd *abfd, | 
 | 			   struct bfd_link_info *info ATTRIBUTE_UNUSED) | 
 | { | 
 |   asection *current; | 
 |   int c; | 
 |   int ret; | 
 |  | 
 |   c = 0; | 
 |   for (current = abfd->sections; | 
 |        current != NULL; | 
 |        current = current->next) | 
 |     ++c; | 
 |  | 
 |   ret = (bfd_coff_filhsz (abfd) | 
 | 	 + bfd_coff_aoutsz (abfd) | 
 | 	 + c * bfd_coff_scnhsz (abfd)); | 
 |   return (int) BFD_ALIGN (ret, 16); | 
 | } | 
 |  | 
 | /* Get the contents of a section.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_get_section_contents (bfd *abfd, | 
 | 				 asection *section, | 
 | 				 void * location, | 
 | 				 file_ptr offset, | 
 | 				 bfd_size_type count) | 
 | { | 
 |   return _bfd_generic_get_section_contents (abfd, section, location, | 
 | 					    offset, count); | 
 | } | 
 |  | 
 | /* Sort sections by VMA, but put SEC_ALLOC sections first.  This is | 
 |    called via qsort.  */ | 
 |  | 
 | static int | 
 | ecoff_sort_hdrs (const void * arg1, const void * arg2) | 
 | { | 
 |   const asection *hdr1 = *(const asection **) arg1; | 
 |   const asection *hdr2 = *(const asection **) arg2; | 
 |  | 
 |   if ((hdr1->flags & SEC_ALLOC) != 0) | 
 |     { | 
 |       if ((hdr2->flags & SEC_ALLOC) == 0) | 
 | 	return -1; | 
 |     } | 
 |   else | 
 |     { | 
 |       if ((hdr2->flags & SEC_ALLOC) != 0) | 
 | 	return 1; | 
 |     } | 
 |   if (hdr1->vma < hdr2->vma) | 
 |     return -1; | 
 |   else if (hdr1->vma > hdr2->vma) | 
 |     return 1; | 
 |   else | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Calculate the file position for each section, and set | 
 |    reloc_filepos.  */ | 
 |  | 
 | static bool | 
 | ecoff_compute_section_file_positions (bfd *abfd) | 
 | { | 
 |   file_ptr sofar, file_sofar; | 
 |   asection **sorted_hdrs; | 
 |   asection *current; | 
 |   unsigned int i; | 
 |   file_ptr old_sofar; | 
 |   bool rdata_in_text; | 
 |   bool first_data, first_nonalloc; | 
 |   const bfd_vma round = ecoff_backend (abfd)->round; | 
 |   bfd_size_type amt; | 
 |  | 
 |   sofar = _bfd_ecoff_sizeof_headers (abfd, NULL); | 
 |   file_sofar = sofar; | 
 |  | 
 |   /* Sort the sections by VMA.  */ | 
 |   amt = abfd->section_count; | 
 |   amt *= sizeof (asection *); | 
 |   sorted_hdrs = (asection **) bfd_malloc (amt); | 
 |   if (sorted_hdrs == NULL) | 
 |     return false; | 
 |   for (current = abfd->sections, i = 0; | 
 |        current != NULL; | 
 |        current = current->next, i++) | 
 |     sorted_hdrs[i] = current; | 
 |   BFD_ASSERT (i == abfd->section_count); | 
 |  | 
 |   qsort (sorted_hdrs, abfd->section_count, sizeof (asection *), | 
 | 	 ecoff_sort_hdrs); | 
 |  | 
 |   /* Some versions of the OSF linker put the .rdata section in the | 
 |      text segment, and some do not.  */ | 
 |   rdata_in_text = ecoff_backend (abfd)->rdata_in_text; | 
 |   if (rdata_in_text) | 
 |     { | 
 |       for (i = 0; i < abfd->section_count; i++) | 
 | 	{ | 
 | 	  current = sorted_hdrs[i]; | 
 | 	  if (streq (current->name, _RDATA)) | 
 | 	    break; | 
 | 	  if ((current->flags & SEC_CODE) == 0 | 
 | 	      && ! streq (current->name, _PDATA) | 
 | 	      && ! streq (current->name, _RCONST)) | 
 | 	    { | 
 | 	      rdata_in_text = false; | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |   ecoff_data (abfd)->rdata_in_text = rdata_in_text; | 
 |  | 
 |   first_data = true; | 
 |   first_nonalloc = true; | 
 |   for (i = 0; i < abfd->section_count; i++) | 
 |     { | 
 |       unsigned int alignment_power; | 
 |  | 
 |       current = sorted_hdrs[i]; | 
 |  | 
 |       /* For the Alpha ECOFF .pdata section the lnnoptr field is | 
 | 	 supposed to indicate the number of .pdata entries that are | 
 | 	 really in the section.  Each entry is 8 bytes.  We store this | 
 | 	 away in line_filepos before increasing the section size.  */ | 
 |       if (streq (current->name, _PDATA)) | 
 | 	current->line_filepos = current->size / 8; | 
 |  | 
 |       alignment_power = current->alignment_power; | 
 |  | 
 |       /* On Ultrix, the data sections in an executable file must be | 
 | 	 aligned to a page boundary within the file.  This does not | 
 | 	 affect the section size, though.  FIXME: Does this work for | 
 | 	 other platforms?  It requires some modification for the | 
 | 	 Alpha, because .rdata on the Alpha goes with the text, not | 
 | 	 the data.  */ | 
 |       if ((abfd->flags & EXEC_P) != 0 | 
 | 	  && (abfd->flags & D_PAGED) != 0 | 
 | 	  && ! first_data | 
 | 	  && (current->flags & SEC_CODE) == 0 | 
 | 	  && (! rdata_in_text | 
 | 	      || ! streq (current->name, _RDATA)) | 
 | 	  && ! streq (current->name, _PDATA) | 
 | 	  && ! streq (current->name, _RCONST)) | 
 | 	{ | 
 | 	  sofar = (sofar + round - 1) &~ (round - 1); | 
 | 	  file_sofar = (file_sofar + round - 1) &~ (round - 1); | 
 | 	  first_data = false; | 
 | 	} | 
 |       else if (streq (current->name, _LIB)) | 
 | 	{ | 
 | 	  /* On Irix 4, the location of contents of the .lib section | 
 | 	     from a shared library section is also rounded up to a | 
 | 	     page boundary.  */ | 
 |  | 
 | 	  sofar = (sofar + round - 1) &~ (round - 1); | 
 | 	  file_sofar = (file_sofar + round - 1) &~ (round - 1); | 
 | 	} | 
 |       else if (first_nonalloc | 
 | 	       && (current->flags & SEC_ALLOC) == 0 | 
 | 	       && (abfd->flags & D_PAGED) != 0) | 
 | 	{ | 
 | 	  /* Skip up to the next page for an unallocated section, such | 
 | 	     as the .comment section on the Alpha.  This leaves room | 
 | 	     for the .bss section.  */ | 
 | 	  first_nonalloc = false; | 
 | 	  sofar = (sofar + round - 1) &~ (round - 1); | 
 | 	  file_sofar = (file_sofar + round - 1) &~ (round - 1); | 
 | 	} | 
 |  | 
 |       /* Align the sections in the file to the same boundary on | 
 | 	 which they are aligned in virtual memory.  */ | 
 |       sofar = BFD_ALIGN (sofar, 1 << alignment_power); | 
 |       if ((current->flags & SEC_HAS_CONTENTS) != 0) | 
 | 	file_sofar = BFD_ALIGN (file_sofar, 1 << alignment_power); | 
 |  | 
 |       if ((abfd->flags & D_PAGED) != 0 | 
 | 	  && (current->flags & SEC_ALLOC) != 0) | 
 | 	{ | 
 | 	  sofar += (current->vma - sofar) % round; | 
 | 	  if ((current->flags & SEC_HAS_CONTENTS) != 0) | 
 | 	    file_sofar += (current->vma - file_sofar) % round; | 
 | 	} | 
 |  | 
 |       if ((current->flags & (SEC_HAS_CONTENTS | SEC_LOAD)) != 0) | 
 | 	current->filepos = file_sofar; | 
 |  | 
 |       sofar += current->size; | 
 |       if ((current->flags & SEC_HAS_CONTENTS) != 0) | 
 | 	file_sofar += current->size; | 
 |  | 
 |       /* Make sure that this section is of the right size too.  */ | 
 |       old_sofar = sofar; | 
 |       sofar = BFD_ALIGN (sofar, 1 << alignment_power); | 
 |       if ((current->flags & SEC_HAS_CONTENTS) != 0) | 
 | 	file_sofar = BFD_ALIGN (file_sofar, 1 << alignment_power); | 
 |       current->size += sofar - old_sofar; | 
 |     } | 
 |  | 
 |   free (sorted_hdrs); | 
 |   sorted_hdrs = NULL; | 
 |  | 
 |   ecoff_data (abfd)->reloc_filepos = file_sofar; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Determine the location of the relocs for all the sections in the | 
 |    output file, as well as the location of the symbolic debugging | 
 |    information.  */ | 
 |  | 
 | static bfd_size_type | 
 | ecoff_compute_reloc_file_positions (bfd *abfd) | 
 | { | 
 |   const bfd_size_type external_reloc_size = | 
 |     ecoff_backend (abfd)->external_reloc_size; | 
 |   file_ptr reloc_base; | 
 |   bfd_size_type reloc_size; | 
 |   asection *current; | 
 |   file_ptr sym_base; | 
 |  | 
 |   if (! abfd->output_has_begun) | 
 |     { | 
 |       if (! ecoff_compute_section_file_positions (abfd)) | 
 | 	abort (); | 
 |       abfd->output_has_begun = true; | 
 |     } | 
 |  | 
 |   reloc_base = ecoff_data (abfd)->reloc_filepos; | 
 |  | 
 |   reloc_size = 0; | 
 |   for (current = abfd->sections; | 
 |        current != NULL; | 
 |        current = current->next) | 
 |     { | 
 |       if (current->reloc_count == 0) | 
 | 	current->rel_filepos = 0; | 
 |       else | 
 | 	{ | 
 | 	  bfd_size_type relsize; | 
 |  | 
 | 	  current->rel_filepos = reloc_base; | 
 | 	  relsize = current->reloc_count * external_reloc_size; | 
 | 	  reloc_size += relsize; | 
 | 	  reloc_base += relsize; | 
 | 	} | 
 |     } | 
 |  | 
 |   sym_base = ecoff_data (abfd)->reloc_filepos + reloc_size; | 
 |  | 
 |   /* At least on Ultrix, the symbol table of an executable file must | 
 |      be aligned to a page boundary.  FIXME: Is this true on other | 
 |      platforms?  */ | 
 |   if ((abfd->flags & EXEC_P) != 0 | 
 |       && (abfd->flags & D_PAGED) != 0) | 
 |     sym_base = ((sym_base + ecoff_backend (abfd)->round - 1) | 
 | 		&~ (ecoff_backend (abfd)->round - 1)); | 
 |  | 
 |   ecoff_data (abfd)->sym_filepos = sym_base; | 
 |  | 
 |   return reloc_size; | 
 | } | 
 |  | 
 | /* Set the contents of a section.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_set_section_contents (bfd *abfd, | 
 | 				 asection *section, | 
 | 				 const void * location, | 
 | 				 file_ptr offset, | 
 | 				 bfd_size_type count) | 
 | { | 
 |   file_ptr pos; | 
 |  | 
 |   /* This must be done first, because bfd_set_section_contents is | 
 |      going to set output_has_begun to TRUE.  */ | 
 |   if (! abfd->output_has_begun | 
 |       && ! ecoff_compute_section_file_positions (abfd)) | 
 |     return false; | 
 |  | 
 |   /* Handle the .lib section specially so that Irix 4 shared libraries | 
 |      work out.  See coff_set_section_contents in coffcode.h.  */ | 
 |   if (streq (section->name, _LIB)) | 
 |     { | 
 |       bfd_byte *rec, *recend; | 
 |  | 
 |       rec = (bfd_byte *) location; | 
 |       recend = rec + count; | 
 |       while (rec < recend) | 
 | 	{ | 
 | 	  ++section->lma; | 
 | 	  rec += bfd_get_32 (abfd, rec) * 4; | 
 | 	} | 
 |  | 
 |       BFD_ASSERT (rec == recend); | 
 |     } | 
 |  | 
 |   if (count == 0) | 
 |     return true; | 
 |  | 
 |   pos = section->filepos + offset; | 
 |   if (bfd_seek (abfd, pos, SEEK_SET) != 0 | 
 |       || bfd_write (location, count, abfd) != count) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Set the GP value for an ECOFF file.  This is a hook used by the | 
 |    assembler.  */ | 
 |  | 
 | bool | 
 | bfd_ecoff_set_gp_value (bfd *abfd, bfd_vma gp_value) | 
 | { | 
 |   if (bfd_get_flavour (abfd) != bfd_target_ecoff_flavour | 
 |       || bfd_get_format (abfd) != bfd_object) | 
 |     { | 
 |       bfd_set_error (bfd_error_invalid_operation); | 
 |       return false; | 
 |     } | 
 |  | 
 |   ecoff_data (abfd)->gp = gp_value; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Set the register masks for an ECOFF file.  This is a hook used by | 
 |    the assembler.  */ | 
 |  | 
 | bool | 
 | bfd_ecoff_set_regmasks (bfd *abfd, | 
 | 			unsigned long gprmask, | 
 | 			unsigned long fprmask, | 
 | 			unsigned long *cprmask) | 
 | { | 
 |   ecoff_data_type *tdata; | 
 |  | 
 |   if (bfd_get_flavour (abfd) != bfd_target_ecoff_flavour | 
 |       || bfd_get_format (abfd) != bfd_object) | 
 |     { | 
 |       bfd_set_error (bfd_error_invalid_operation); | 
 |       return false; | 
 |     } | 
 |  | 
 |   tdata = ecoff_data (abfd); | 
 |   tdata->gprmask = gprmask; | 
 |   tdata->fprmask = fprmask; | 
 |   if (cprmask != NULL) | 
 |     { | 
 |       int i; | 
 |  | 
 |       for (i = 0; i < 3; i++) | 
 | 	tdata->cprmask[i] = cprmask[i]; | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Get ECOFF EXTR information for an external symbol.  This function | 
 |    is passed to bfd_ecoff_debug_externals.  */ | 
 |  | 
 | static bool | 
 | ecoff_get_extr (asymbol *sym, EXTR *esym) | 
 | { | 
 |   ecoff_symbol_type *ecoff_sym_ptr; | 
 |   bfd *input_bfd; | 
 |  | 
 |   if (bfd_asymbol_flavour (sym) != bfd_target_ecoff_flavour | 
 |       || ecoffsymbol (sym)->native == NULL) | 
 |     { | 
 |       /* Don't include debugging, local, or section symbols.  */ | 
 |       if ((sym->flags & BSF_DEBUGGING) != 0 | 
 | 	  || (sym->flags & BSF_LOCAL) != 0 | 
 | 	  || (sym->flags & BSF_SECTION_SYM) != 0) | 
 | 	return false; | 
 |  | 
 |       esym->jmptbl = 0; | 
 |       esym->cobol_main = 0; | 
 |       esym->weakext = (sym->flags & BSF_WEAK) != 0; | 
 |       esym->reserved = 0; | 
 |       esym->ifd = ifdNil; | 
 |       /* FIXME: we can do better than this for st and sc.  */ | 
 |       esym->asym.st = stGlobal; | 
 |       esym->asym.sc = scAbs; | 
 |       esym->asym.reserved = 0; | 
 |       esym->asym.index = indexNil; | 
 |       return true; | 
 |     } | 
 |  | 
 |   ecoff_sym_ptr = ecoffsymbol (sym); | 
 |  | 
 |   if (ecoff_sym_ptr->local) | 
 |     return false; | 
 |  | 
 |   input_bfd = bfd_asymbol_bfd (sym); | 
 |   (*(ecoff_backend (input_bfd)->debug_swap.swap_ext_in)) | 
 |     (input_bfd, ecoff_sym_ptr->native, esym); | 
 |  | 
 |   /* If the symbol was defined by the linker, then esym will be | 
 |      undefined but sym will not be.  Get a better class for such a | 
 |      symbol.  */ | 
 |   if ((esym->asym.sc == scUndefined | 
 |        || esym->asym.sc == scSUndefined) | 
 |       && ! bfd_is_und_section (bfd_asymbol_section (sym))) | 
 |     esym->asym.sc = scAbs; | 
 |  | 
 |   /* Adjust the FDR index for the symbol by that used for the input | 
 |      BFD.  */ | 
 |   if (esym->ifd != -1) | 
 |     { | 
 |       struct ecoff_debug_info *input_debug; | 
 |  | 
 |       input_debug = &ecoff_data (input_bfd)->debug_info; | 
 |       BFD_ASSERT (esym->ifd < input_debug->symbolic_header.ifdMax); | 
 |       if (input_debug->ifdmap != NULL) | 
 | 	esym->ifd = input_debug->ifdmap[esym->ifd]; | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Set the external symbol index.  This routine is passed to | 
 |    bfd_ecoff_debug_externals.  */ | 
 |  | 
 | static void | 
 | ecoff_set_index (asymbol *sym, bfd_size_type indx) | 
 | { | 
 |   ecoff_set_sym_index (sym, indx); | 
 | } | 
 |  | 
 | /* Write out an ECOFF file.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_write_object_contents (bfd *abfd) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   const bfd_vma round = backend->round; | 
 |   const bfd_size_type filhsz = bfd_coff_filhsz (abfd); | 
 |   const bfd_size_type aoutsz = bfd_coff_aoutsz (abfd); | 
 |   const bfd_size_type scnhsz = bfd_coff_scnhsz (abfd); | 
 |   const bfd_size_type external_hdr_size | 
 |     = backend->debug_swap.external_hdr_size; | 
 |   const bfd_size_type external_reloc_size = backend->external_reloc_size; | 
 |   void (* const adjust_reloc_out) (bfd *, const arelent *, struct internal_reloc *) | 
 |     = backend->adjust_reloc_out; | 
 |   void (* const swap_reloc_out) (bfd *, const struct internal_reloc *, void *) | 
 |     = backend->swap_reloc_out; | 
 |   struct ecoff_debug_info * const debug = &ecoff_data (abfd)->debug_info; | 
 |   HDRR * const symhdr = &debug->symbolic_header; | 
 |   asection *current; | 
 |   unsigned int count; | 
 |   bfd_size_type reloc_size; | 
 |   bfd_size_type text_size; | 
 |   bfd_vma text_start; | 
 |   bool set_text_start; | 
 |   bfd_size_type data_size; | 
 |   bfd_vma data_start; | 
 |   bool set_data_start; | 
 |   bfd_size_type bss_size; | 
 |   void * buff = NULL; | 
 |   void * reloc_buff = NULL; | 
 |   struct internal_filehdr internal_f; | 
 |   struct internal_aouthdr internal_a; | 
 |   int i; | 
 |  | 
 |   /* Determine where the sections and relocs will go in the output | 
 |      file.  */ | 
 |   reloc_size = ecoff_compute_reloc_file_positions (abfd); | 
 |  | 
 |   count = 1; | 
 |   for (current = abfd->sections; | 
 |        current != NULL; | 
 |        current = current->next) | 
 |     { | 
 |       current->target_index = count; | 
 |       ++count; | 
 |     } | 
 |  | 
 |   if ((abfd->flags & D_PAGED) != 0) | 
 |     text_size = _bfd_ecoff_sizeof_headers (abfd, NULL); | 
 |   else | 
 |     text_size = 0; | 
 |   text_start = 0; | 
 |   set_text_start = false; | 
 |   data_size = 0; | 
 |   data_start = 0; | 
 |   set_data_start = false; | 
 |   bss_size = 0; | 
 |  | 
 |   /* Write section headers to the file.  */ | 
 |  | 
 |   /* Allocate buff big enough to hold a section header, | 
 |      file header, or a.out header.  */ | 
 |   { | 
 |     bfd_size_type siz; | 
 |  | 
 |     siz = scnhsz; | 
 |     if (siz < filhsz) | 
 |       siz = filhsz; | 
 |     if (siz < aoutsz) | 
 |       siz = aoutsz; | 
 |     buff = bfd_malloc (siz); | 
 |     if (buff == NULL) | 
 |       goto error_return; | 
 |   } | 
 |  | 
 |   internal_f.f_nscns = 0; | 
 |   if (bfd_seek (abfd, filhsz + aoutsz, SEEK_SET) != 0) | 
 |     goto error_return; | 
 |  | 
 |   for (current = abfd->sections; | 
 |        current != NULL; | 
 |        current = current->next) | 
 |     { | 
 |       struct internal_scnhdr section; | 
 |       bfd_vma vma; | 
 |  | 
 |       ++internal_f.f_nscns; | 
 |  | 
 |       strncpy (section.s_name, current->name, sizeof section.s_name); | 
 |  | 
 |       /* This seems to be correct for Irix 4 shared libraries.  */ | 
 |       vma = bfd_section_vma (current); | 
 |       if (streq (current->name, _LIB)) | 
 | 	section.s_vaddr = 0; | 
 |       else | 
 | 	section.s_vaddr = vma; | 
 |  | 
 |       section.s_paddr = current->lma; | 
 |       section.s_size = current->size; | 
 |  | 
 |       /* If this section is unloadable then the scnptr will be 0.  */ | 
 |       if ((current->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) | 
 | 	section.s_scnptr = 0; | 
 |       else | 
 | 	section.s_scnptr = current->filepos; | 
 |       section.s_relptr = current->rel_filepos; | 
 |  | 
 |       /* FIXME: the lnnoptr of the .sbss or .sdata section of an | 
 | 	 object file produced by the assembler is supposed to point to | 
 | 	 information about how much room is required by objects of | 
 | 	 various different sizes.  I think this only matters if we | 
 | 	 want the linker to compute the best size to use, or | 
 | 	 something.  I don't know what happens if the information is | 
 | 	 not present.  */ | 
 |       if (! streq (current->name, _PDATA)) | 
 | 	section.s_lnnoptr = 0; | 
 |       else | 
 | 	{ | 
 | 	  /* The Alpha ECOFF .pdata section uses the lnnoptr field to | 
 | 	     hold the number of entries in the section (each entry is | 
 | 	     8 bytes).  We stored this in the line_filepos field in | 
 | 	     ecoff_compute_section_file_positions.  */ | 
 | 	  section.s_lnnoptr = current->line_filepos; | 
 | 	} | 
 |  | 
 |       section.s_nreloc = current->reloc_count; | 
 |       section.s_nlnno = 0; | 
 |       section.s_flags = ecoff_sec_to_styp_flags (current->name, | 
 | 						 current->flags); | 
 |  | 
 |       if (bfd_coff_swap_scnhdr_out (abfd, (void *) §ion, buff) == 0 | 
 | 	  || bfd_write (buff, scnhsz, abfd) != scnhsz) | 
 | 	goto error_return; | 
 |  | 
 |       if ((section.s_flags & STYP_TEXT) != 0 | 
 | 	  || ((section.s_flags & STYP_RDATA) != 0 | 
 | 	      && ecoff_data (abfd)->rdata_in_text) | 
 | 	  || section.s_flags == STYP_PDATA | 
 | 	  || (section.s_flags & STYP_DYNAMIC) != 0 | 
 | 	  || (section.s_flags & STYP_LIBLIST) != 0 | 
 | 	  || (section.s_flags & STYP_RELDYN) != 0 | 
 | 	  || section.s_flags == STYP_CONFLIC | 
 | 	  || (section.s_flags & STYP_DYNSTR) != 0 | 
 | 	  || (section.s_flags & STYP_DYNSYM) != 0 | 
 | 	  || (section.s_flags & STYP_HASH) != 0 | 
 | 	  || (section.s_flags & STYP_ECOFF_INIT) != 0 | 
 | 	  || (section.s_flags & STYP_ECOFF_FINI) != 0 | 
 | 	  || section.s_flags == STYP_RCONST) | 
 | 	{ | 
 | 	  text_size += current->size; | 
 | 	  if (! set_text_start || text_start > vma) | 
 | 	    { | 
 | 	      text_start = vma; | 
 | 	      set_text_start = true; | 
 | 	    } | 
 | 	} | 
 |       else if ((section.s_flags & STYP_RDATA) != 0 | 
 | 	       || (section.s_flags & STYP_DATA) != 0 | 
 | 	       || (section.s_flags & STYP_LITA) != 0 | 
 | 	       || (section.s_flags & STYP_LIT8) != 0 | 
 | 	       || (section.s_flags & STYP_LIT4) != 0 | 
 | 	       || (section.s_flags & STYP_SDATA) != 0 | 
 | 	       || section.s_flags == STYP_XDATA | 
 | 	       || (section.s_flags & STYP_GOT) != 0) | 
 | 	{ | 
 | 	  data_size += current->size; | 
 | 	  if (! set_data_start || data_start > vma) | 
 | 	    { | 
 | 	      data_start = vma; | 
 | 	      set_data_start = true; | 
 | 	    } | 
 | 	} | 
 |       else if ((section.s_flags & STYP_BSS) != 0 | 
 | 	       || (section.s_flags & STYP_SBSS) != 0) | 
 | 	bss_size += current->size; | 
 |       else if (section.s_flags == 0 | 
 | 	       || (section.s_flags & STYP_ECOFF_LIB) != 0 | 
 | 	       || section.s_flags == STYP_COMMENT) | 
 | 	/* Do nothing.  */ ; | 
 |       else | 
 | 	abort (); | 
 |     } | 
 |  | 
 |   /* Set up the file header.  */ | 
 |   internal_f.f_magic = ecoff_get_magic (abfd); | 
 |  | 
 |   /* We will NOT put a fucking timestamp in the header here. Every | 
 |      time you put it back, I will come in and take it out again.  I'm | 
 |      sorry.  This field does not belong here.  We fill it with a 0 so | 
 |      it compares the same but is not a reasonable time. -- | 
 |      gnu@cygnus.com.  */ | 
 |   internal_f.f_timdat = 0; | 
 |  | 
 |   if (bfd_get_symcount (abfd) != 0) | 
 |     { | 
 |       /* The ECOFF f_nsyms field is not actually the number of | 
 | 	 symbols, it's the size of symbolic information header.  */ | 
 |       internal_f.f_nsyms = external_hdr_size; | 
 |       internal_f.f_symptr = ecoff_data (abfd)->sym_filepos; | 
 |     } | 
 |   else | 
 |     { | 
 |       internal_f.f_nsyms = 0; | 
 |       internal_f.f_symptr = 0; | 
 |     } | 
 |  | 
 |   internal_f.f_opthdr = aoutsz; | 
 |  | 
 |   internal_f.f_flags = F_LNNO; | 
 |   if (reloc_size == 0) | 
 |     internal_f.f_flags |= F_RELFLG; | 
 |   if (bfd_get_symcount (abfd) == 0) | 
 |     internal_f.f_flags |= F_LSYMS; | 
 |   if (abfd->flags & EXEC_P) | 
 |     internal_f.f_flags |= F_EXEC; | 
 |  | 
 |   if (bfd_little_endian (abfd)) | 
 |     internal_f.f_flags |= F_AR32WR; | 
 |   else | 
 |     internal_f.f_flags |= F_AR32W; | 
 |  | 
 |   /* Set up the ``optional'' header.  */ | 
 |   if ((abfd->flags & D_PAGED) != 0) | 
 |     internal_a.magic = ECOFF_AOUT_ZMAGIC; | 
 |   else | 
 |     internal_a.magic = ECOFF_AOUT_OMAGIC; | 
 |  | 
 |   /* FIXME: Is this really correct?  */ | 
 |   internal_a.vstamp = symhdr->vstamp; | 
 |  | 
 |   /* At least on Ultrix, these have to be rounded to page boundaries. | 
 |      FIXME: Is this true on other platforms?  */ | 
 |   if ((abfd->flags & D_PAGED) != 0) | 
 |     { | 
 |       internal_a.tsize = (text_size + round - 1) &~ (round - 1); | 
 |       internal_a.text_start = text_start &~ (round - 1); | 
 |       internal_a.dsize = (data_size + round - 1) &~ (round - 1); | 
 |       internal_a.data_start = data_start &~ (round - 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       internal_a.tsize = text_size; | 
 |       internal_a.text_start = text_start; | 
 |       internal_a.dsize = data_size; | 
 |       internal_a.data_start = data_start; | 
 |     } | 
 |  | 
 |   /* On Ultrix, the initial portions of the .sbss and .bss segments | 
 |      are at the end of the data section.  The bsize field in the | 
 |      optional header records how many bss bytes are required beyond | 
 |      those in the data section.  The value is not rounded to a page | 
 |      boundary.  */ | 
 |   if (bss_size < internal_a.dsize - data_size) | 
 |     bss_size = 0; | 
 |   else | 
 |     bss_size -= internal_a.dsize - data_size; | 
 |   internal_a.bsize = bss_size; | 
 |   internal_a.bss_start = internal_a.data_start + internal_a.dsize; | 
 |  | 
 |   internal_a.entry = bfd_get_start_address (abfd); | 
 |  | 
 |   internal_a.gp_value = ecoff_data (abfd)->gp; | 
 |  | 
 |   internal_a.gprmask = ecoff_data (abfd)->gprmask; | 
 |   internal_a.fprmask = ecoff_data (abfd)->fprmask; | 
 |   for (i = 0; i < 4; i++) | 
 |     internal_a.cprmask[i] = ecoff_data (abfd)->cprmask[i]; | 
 |  | 
 |   /* Let the backend adjust the headers if necessary.  */ | 
 |   if (backend->adjust_headers) | 
 |     { | 
 |       if (! (*backend->adjust_headers) (abfd, &internal_f, &internal_a)) | 
 | 	goto error_return; | 
 |     } | 
 |  | 
 |   /* Write out the file header and the optional header.  */ | 
 |   if (bfd_seek (abfd, 0, SEEK_SET) != 0) | 
 |     goto error_return; | 
 |  | 
 |   bfd_coff_swap_filehdr_out (abfd, (void *) &internal_f, buff); | 
 |   if (bfd_write (buff, filhsz, abfd) != filhsz) | 
 |     goto error_return; | 
 |  | 
 |   bfd_coff_swap_aouthdr_out (abfd, (void *) &internal_a, buff); | 
 |   if (bfd_write (buff, aoutsz, abfd) != aoutsz) | 
 |     goto error_return; | 
 |  | 
 |   /* Build the external symbol information.  This must be done before | 
 |      writing out the relocs so that we know the symbol indices.  We | 
 |      don't do this if this BFD was created by the backend linker, | 
 |      since it will have already handled the symbols and relocs.  */ | 
 |   if (! ecoff_data (abfd)->linker) | 
 |     { | 
 |       symhdr->iextMax = 0; | 
 |       symhdr->issExtMax = 0; | 
 |       debug->external_ext = debug->external_ext_end = NULL; | 
 |       debug->ssext = debug->ssext_end = NULL; | 
 |       if (! bfd_ecoff_debug_externals (abfd, debug, &backend->debug_swap, | 
 | 				       (abfd->flags & EXEC_P) == 0, | 
 | 				       ecoff_get_extr, ecoff_set_index)) | 
 | 	goto error_return; | 
 |  | 
 |       /* Write out the relocs.  */ | 
 |       for (current = abfd->sections; | 
 | 	   current != NULL; | 
 | 	   current = current->next) | 
 | 	{ | 
 | 	  arelent **reloc_ptr_ptr; | 
 | 	  arelent **reloc_end; | 
 | 	  char *out_ptr; | 
 | 	  bfd_size_type amt; | 
 |  | 
 | 	  if (current->reloc_count == 0) | 
 | 	    continue; | 
 |  | 
 | 	  amt = current->reloc_count * external_reloc_size; | 
 | 	  reloc_buff = bfd_zalloc (abfd, amt); | 
 | 	  if (reloc_buff == NULL) | 
 | 	    goto error_return; | 
 |  | 
 | 	  reloc_ptr_ptr = current->orelocation; | 
 | 	  reloc_end = reloc_ptr_ptr + current->reloc_count; | 
 | 	  out_ptr = (char *) reloc_buff; | 
 |  | 
 | 	  for (; | 
 | 	       reloc_ptr_ptr < reloc_end; | 
 | 	       reloc_ptr_ptr++, out_ptr += external_reloc_size) | 
 | 	    { | 
 | 	      arelent *reloc; | 
 | 	      asymbol *sym; | 
 | 	      struct internal_reloc in; | 
 |  | 
 | 	      memset ((void *) &in, 0, sizeof in); | 
 |  | 
 | 	      reloc = *reloc_ptr_ptr; | 
 | 	      sym = *reloc->sym_ptr_ptr; | 
 |  | 
 | 	      /* If the howto field has not been initialised then skip this reloc. | 
 | 		 This assumes that an error message has been issued elsewhere.  */ | 
 | 	      if (reloc->howto == NULL) | 
 | 		continue; | 
 |  | 
 | 	      in.r_vaddr = reloc->address + bfd_section_vma (current); | 
 | 	      in.r_type = reloc->howto->type; | 
 |  | 
 | 	      if ((sym->flags & BSF_SECTION_SYM) == 0) | 
 | 		{ | 
 | 		  in.r_symndx = ecoff_get_sym_index (*reloc->sym_ptr_ptr); | 
 | 		  in.r_extern = 1; | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  const char *name; | 
 | 		  unsigned int j; | 
 | 		  static struct | 
 | 		  { | 
 | 		    const char * name; | 
 | 		    long r_symndx; | 
 | 		  } | 
 | 		  section_symndx [] = | 
 | 		  { | 
 | 		    { _TEXT,   RELOC_SECTION_TEXT   }, | 
 | 		    { _RDATA,  RELOC_SECTION_RDATA  }, | 
 | 		    { _DATA,   RELOC_SECTION_DATA   }, | 
 | 		    { _SDATA,  RELOC_SECTION_SDATA  }, | 
 | 		    { _SBSS,   RELOC_SECTION_SBSS   }, | 
 | 		    { _BSS,    RELOC_SECTION_BSS    }, | 
 | 		    { _INIT,   RELOC_SECTION_INIT   }, | 
 | 		    { _LIT8,   RELOC_SECTION_LIT8   }, | 
 | 		    { _LIT4,   RELOC_SECTION_LIT4   }, | 
 | 		    { _XDATA,  RELOC_SECTION_XDATA  }, | 
 | 		    { _PDATA,  RELOC_SECTION_PDATA  }, | 
 | 		    { _FINI,   RELOC_SECTION_FINI   }, | 
 | 		    { _LITA,   RELOC_SECTION_LITA   }, | 
 | 		    { "*ABS*", RELOC_SECTION_ABS    }, | 
 | 		    { _RCONST, RELOC_SECTION_RCONST } | 
 | 		  }; | 
 |  | 
 | 		  name = bfd_section_name (bfd_asymbol_section (sym)); | 
 |  | 
 | 		  for (j = 0; j < ARRAY_SIZE (section_symndx); j++) | 
 | 		    if (streq (name, section_symndx[j].name)) | 
 | 		      { | 
 | 			in.r_symndx = section_symndx[j].r_symndx; | 
 | 			break; | 
 | 		      } | 
 |  | 
 | 		  if (j == ARRAY_SIZE (section_symndx)) | 
 | 		    abort (); | 
 | 		  in.r_extern = 0; | 
 | 		} | 
 |  | 
 | 	      (*adjust_reloc_out) (abfd, reloc, &in); | 
 |  | 
 | 	      (*swap_reloc_out) (abfd, &in, (void *) out_ptr); | 
 | 	    } | 
 |  | 
 | 	  if (bfd_seek (abfd, current->rel_filepos, SEEK_SET) != 0) | 
 | 	    goto error_return; | 
 | 	  amt = current->reloc_count * external_reloc_size; | 
 | 	  if (bfd_write (reloc_buff, amt, abfd) != amt) | 
 | 	    goto error_return; | 
 | 	  bfd_release (abfd, reloc_buff); | 
 | 	  reloc_buff = NULL; | 
 | 	} | 
 |  | 
 |       /* Write out the symbolic debugging information.  */ | 
 |       if (bfd_get_symcount (abfd) > 0) | 
 | 	{ | 
 | 	  /* Write out the debugging information.  */ | 
 | 	  if (! bfd_ecoff_write_debug (abfd, debug, &backend->debug_swap, | 
 | 				       ecoff_data (abfd)->sym_filepos)) | 
 | 	    goto error_return; | 
 | 	} | 
 |     } | 
 |  | 
 |   /* The .bss section of a demand paged executable must receive an | 
 |      entire page.  If there are symbols, the symbols will start on the | 
 |      next page.  If there are no symbols, we must fill out the page by | 
 |      hand.  */ | 
 |   if (bfd_get_symcount (abfd) == 0 | 
 |       && (abfd->flags & EXEC_P) != 0 | 
 |       && (abfd->flags & D_PAGED) != 0) | 
 |     { | 
 |       char c; | 
 |  | 
 |       if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos - 1, SEEK_SET) != 0) | 
 | 	goto error_return; | 
 |       if (bfd_read (&c, 1, abfd) == 0) | 
 | 	c = 0; | 
 |       if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos - 1, SEEK_SET) != 0) | 
 | 	goto error_return; | 
 |       if (bfd_write (&c, 1, abfd) != 1) | 
 | 	goto error_return; | 
 |     } | 
 |  | 
 |   if (reloc_buff != NULL) | 
 |     bfd_release (abfd, reloc_buff); | 
 |   free (buff); | 
 |   return true; | 
 |  error_return: | 
 |   if (reloc_buff != NULL) | 
 |     bfd_release (abfd, reloc_buff); | 
 |   free (buff); | 
 |   return false; | 
 | } | 
 |  | 
 | /* Archive handling.  ECOFF uses what appears to be a unique type of | 
 |    archive header (armap).  The byte ordering of the armap and the | 
 |    contents are encoded in the name of the armap itself.  At least for | 
 |    now, we only support archives with the same byte ordering in the | 
 |    armap and the contents. | 
 |  | 
 |    The first four bytes in the armap are the number of symbol | 
 |    definitions.  This is always a power of two. | 
 |  | 
 |    This is followed by the symbol definitions.  Each symbol definition | 
 |    occupies 8 bytes.  The first four bytes are the offset from the | 
 |    start of the armap strings to the null-terminated string naming | 
 |    this symbol.  The second four bytes are the file offset to the | 
 |    archive member which defines this symbol.  If the second four bytes | 
 |    are 0, then this is not actually a symbol definition, and it should | 
 |    be ignored. | 
 |  | 
 |    The symbols are hashed into the armap with a closed hashing scheme. | 
 |    See the functions below for the details of the algorithm. | 
 |  | 
 |    After the symbol definitions comes four bytes holding the size of | 
 |    the string table, followed by the string table itself.  */ | 
 |  | 
 | /* The name of an archive headers looks like this: | 
 |    __________E[BL]E[BL]_ (with a trailing space). | 
 |    The trailing space is changed to an X if the archive is changed to | 
 |    indicate that the armap is out of date. | 
 |  | 
 |    The Alpha seems to use ________64E[BL]E[BL]_.  */ | 
 |  | 
 | #define ARMAP_BIG_ENDIAN		'B' | 
 | #define ARMAP_LITTLE_ENDIAN		'L' | 
 | #define ARMAP_MARKER			'E' | 
 | #define ARMAP_START_LENGTH		10 | 
 | #define ARMAP_HEADER_MARKER_INDEX	10 | 
 | #define ARMAP_HEADER_ENDIAN_INDEX	11 | 
 | #define ARMAP_OBJECT_MARKER_INDEX	12 | 
 | #define ARMAP_OBJECT_ENDIAN_INDEX	13 | 
 | #define ARMAP_END_INDEX			14 | 
 | #define ARMAP_END			"_ " | 
 |  | 
 | /* This is a magic number used in the hashing algorithm.  */ | 
 | #define ARMAP_HASH_MAGIC		0x9dd68ab5 | 
 |  | 
 | /* This returns the hash value to use for a string.  It also sets | 
 |    *REHASH to the rehash adjustment if the first slot is taken.  SIZE | 
 |    is the number of entries in the hash table, and HLOG is the log | 
 |    base 2 of SIZE.  */ | 
 |  | 
 | static unsigned int | 
 | ecoff_armap_hash (const char *s, | 
 | 		  unsigned int *rehash, | 
 | 		  unsigned int size, | 
 | 		  unsigned int hlog) | 
 | { | 
 |   unsigned int hash; | 
 |  | 
 |   if (hlog == 0) | 
 |     return 0; | 
 |   hash = *s++; | 
 |   while (*s != '\0') | 
 |     hash = ((hash >> 27) | (hash << 5)) + *s++; | 
 |   hash *= ARMAP_HASH_MAGIC; | 
 |   *rehash = (hash & (size - 1)) | 1; | 
 |   return hash >> (32 - hlog); | 
 | } | 
 |  | 
 | /* Read in the armap.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_slurp_armap (bfd *abfd) | 
 | { | 
 |   char nextname[17]; | 
 |   unsigned int i; | 
 |   struct areltdata *mapdata; | 
 |   bfd_size_type parsed_size, stringsize; | 
 |   char *raw_armap; | 
 |   struct artdata *ardata; | 
 |   unsigned int count; | 
 |   char *raw_ptr; | 
 |   carsym *symdef_ptr; | 
 |   char *stringbase; | 
 |   bfd_size_type amt; | 
 |  | 
 |   /* Get the name of the first element.  */ | 
 |   i = bfd_read (nextname, 16, abfd); | 
 |   if (i == 0) | 
 |     return true; | 
 |   if (i != 16) | 
 |     return false; | 
 |  | 
 |   if (bfd_seek (abfd, -16, SEEK_CUR) != 0) | 
 |     return false; | 
 |  | 
 |   /* Irix 4.0.5F apparently can use either an ECOFF armap or a | 
 |      standard COFF armap.  We could move the ECOFF armap stuff into | 
 |      bfd_slurp_armap, but that seems inappropriate since no other | 
 |      target uses this format.  Instead, we check directly for a COFF | 
 |      armap.  */ | 
 |   if (startswith (nextname, "/               ")) | 
 |     return bfd_slurp_armap (abfd); | 
 |  | 
 |   /* See if the first element is an armap.  */ | 
 |   if (strncmp (nextname, ecoff_backend (abfd)->armap_start, ARMAP_START_LENGTH) != 0 | 
 |       || nextname[ARMAP_HEADER_MARKER_INDEX] != ARMAP_MARKER | 
 |       || (nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN | 
 | 	  && nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN) | 
 |       || nextname[ARMAP_OBJECT_MARKER_INDEX] != ARMAP_MARKER | 
 |       || (nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN | 
 | 	  && nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN) | 
 |       || strncmp (nextname + ARMAP_END_INDEX, ARMAP_END, sizeof ARMAP_END - 1) != 0) | 
 |     { | 
 |       abfd->has_armap = false; | 
 |       return true; | 
 |     } | 
 |  | 
 |   /* Make sure we have the right byte ordering.  */ | 
 |   if (((nextname[ARMAP_HEADER_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN) | 
 |        ^ (bfd_header_big_endian (abfd))) | 
 |       || ((nextname[ARMAP_OBJECT_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN) | 
 | 	  ^ (bfd_big_endian (abfd)))) | 
 |     { | 
 |       bfd_set_error (bfd_error_wrong_format); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* Read in the armap.  */ | 
 |   ardata = bfd_ardata (abfd); | 
 |   mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd); | 
 |   if (mapdata == NULL) | 
 |     return false; | 
 |   parsed_size = mapdata->parsed_size; | 
 |   free (mapdata); | 
 |  | 
 |   if (parsed_size + 1 < 9) | 
 |     { | 
 |       bfd_set_error (bfd_error_malformed_archive); | 
 |       return false; | 
 |     } | 
 |  | 
 |   raw_armap = (char *) _bfd_alloc_and_read (abfd, parsed_size + 1, parsed_size); | 
 |   if (raw_armap == NULL) | 
 |     return false; | 
 |   raw_armap[parsed_size] = 0; | 
 |  | 
 |   ardata->tdata = (void *) raw_armap; | 
 |  | 
 |   count = H_GET_32 (abfd, raw_armap); | 
 |   if ((parsed_size - 8) / 8 < count) | 
 |     goto error_malformed; | 
 |  | 
 |   ardata->symdef_count = 0; | 
 |   ardata->cache = NULL; | 
 |  | 
 |   /* This code used to overlay the symdefs over the raw archive data, | 
 |      but that doesn't work on a 64 bit host.  */ | 
 |   stringbase = raw_armap + count * 8 + 8; | 
 |   stringsize = parsed_size - (count * 8 + 8); | 
 |  | 
 | #ifdef CHECK_ARMAP_HASH | 
 |   { | 
 |     unsigned int hlog; | 
 |  | 
 |     /* Double check that I have the hashing algorithm right by making | 
 |        sure that every symbol can be looked up successfully.  */ | 
 |     hlog = 0; | 
 |     for (i = 1; i < count; i <<= 1) | 
 |       hlog++; | 
 |     BFD_ASSERT (i == count); | 
 |  | 
 |     raw_ptr = raw_armap + 4; | 
 |     for (i = 0; i < count; i++, raw_ptr += 8) | 
 |       { | 
 | 	unsigned int name_offset, file_offset; | 
 | 	unsigned int hash, rehash, srch; | 
 |  | 
 | 	name_offset = H_GET_32 (abfd, raw_ptr); | 
 | 	file_offset = H_GET_32 (abfd, (raw_ptr + 4)); | 
 | 	if (file_offset == 0) | 
 | 	  continue; | 
 | 	hash = ecoff_armap_hash (stringbase + name_offset, &rehash, count, | 
 | 				 hlog); | 
 | 	if (hash == i) | 
 | 	  continue; | 
 |  | 
 | 	/* See if we can rehash to this location.  */ | 
 | 	for (srch = (hash + rehash) & (count - 1); | 
 | 	     srch != hash && srch != i; | 
 | 	     srch = (srch + rehash) & (count - 1)) | 
 | 	  BFD_ASSERT (H_GET_32 (abfd, (raw_armap + 8 + srch * 8)) != 0); | 
 | 	BFD_ASSERT (srch == i); | 
 |       } | 
 |   } | 
 |  | 
 | #endif /* CHECK_ARMAP_HASH */ | 
 |  | 
 |   raw_ptr = raw_armap + 4; | 
 |   for (i = 0; i < count; i++, raw_ptr += 8) | 
 |     if (H_GET_32 (abfd, (raw_ptr + 4)) != 0) | 
 |       ++ardata->symdef_count; | 
 |  | 
 |   amt = ardata->symdef_count; | 
 |   amt *= sizeof (carsym); | 
 |   symdef_ptr = (carsym *) bfd_alloc (abfd, amt); | 
 |   if (!symdef_ptr) | 
 |     goto error_exit; | 
 |  | 
 |   ardata->symdefs = symdef_ptr; | 
 |  | 
 |   raw_ptr = raw_armap + 4; | 
 |   for (i = 0; i < count; i++, raw_ptr += 8) | 
 |     { | 
 |       unsigned int name_offset, file_offset; | 
 |  | 
 |       file_offset = H_GET_32 (abfd, (raw_ptr + 4)); | 
 |       if (file_offset == 0) | 
 | 	continue; | 
 |       name_offset = H_GET_32 (abfd, raw_ptr); | 
 |       if (name_offset > stringsize) | 
 | 	goto error_malformed; | 
 |       symdef_ptr->name = stringbase + name_offset; | 
 |       symdef_ptr->file_offset = file_offset; | 
 |       ++symdef_ptr; | 
 |     } | 
 |  | 
 |   ardata->first_file_filepos = bfd_tell (abfd); | 
 |   /* Pad to an even boundary.  */ | 
 |   ardata->first_file_filepos += ardata->first_file_filepos % 2; | 
 |   abfd->has_armap = true; | 
 |   return true; | 
 |  | 
 |  error_malformed: | 
 |   bfd_set_error (bfd_error_malformed_archive); | 
 |  error_exit: | 
 |   ardata->symdef_count = 0; | 
 |   ardata->symdefs = NULL; | 
 |   ardata->tdata = NULL; | 
 |   bfd_release (abfd, raw_armap); | 
 |   return false; | 
 | } | 
 |  | 
 | /* Write out an armap.  */ | 
 |  | 
 | bool | 
 | _bfd_ecoff_write_armap (bfd *abfd, | 
 | 			unsigned int elength, | 
 | 			struct orl *map, | 
 | 			unsigned int orl_count, | 
 | 			int stridx) | 
 | { | 
 |   unsigned int hashsize, hashlog; | 
 |   bfd_size_type symdefsize; | 
 |   int padit; | 
 |   unsigned int stringsize; | 
 |   unsigned int mapsize; | 
 |   file_ptr firstreal; | 
 |   struct ar_hdr hdr; | 
 |   struct stat statbuf; | 
 |   unsigned int i; | 
 |   bfd_byte temp[4]; | 
 |   bfd_byte *hashtable; | 
 |   bfd *current; | 
 |   bfd *last_elt; | 
 |  | 
 |   /* Ultrix appears to use as a hash table size the least power of two | 
 |      greater than twice the number of entries.  */ | 
 |   for (hashlog = 0; ((unsigned int) 1 << hashlog) <= 2 * orl_count; hashlog++) | 
 |     ; | 
 |   hashsize = 1 << hashlog; | 
 |  | 
 |   symdefsize = hashsize * 8; | 
 |   padit = stridx % 2; | 
 |   stringsize = stridx + padit; | 
 |  | 
 |   /* Include 8 bytes to store symdefsize and stringsize in output.  */ | 
 |   mapsize = symdefsize + stringsize + 8; | 
 |  | 
 |   firstreal = SARMAG + sizeof (struct ar_hdr) + mapsize + elength; | 
 |  | 
 |   memset ((void *) &hdr, 0, sizeof hdr); | 
 |  | 
 |   /* Work out the ECOFF armap name.  */ | 
 |   strcpy (hdr.ar_name, ecoff_backend (abfd)->armap_start); | 
 |   hdr.ar_name[ARMAP_HEADER_MARKER_INDEX] = ARMAP_MARKER; | 
 |   hdr.ar_name[ARMAP_HEADER_ENDIAN_INDEX] = | 
 |     (bfd_header_big_endian (abfd) | 
 |      ? ARMAP_BIG_ENDIAN | 
 |      : ARMAP_LITTLE_ENDIAN); | 
 |   hdr.ar_name[ARMAP_OBJECT_MARKER_INDEX] = ARMAP_MARKER; | 
 |   hdr.ar_name[ARMAP_OBJECT_ENDIAN_INDEX] = | 
 |     bfd_big_endian (abfd) ? ARMAP_BIG_ENDIAN : ARMAP_LITTLE_ENDIAN; | 
 |   memcpy (hdr.ar_name + ARMAP_END_INDEX, ARMAP_END, sizeof ARMAP_END - 1); | 
 |  | 
 |   /* Write the timestamp of the archive header to be just a little bit | 
 |      later than the timestamp of the file, otherwise the linker will | 
 |      complain that the index is out of date.  Actually, the Ultrix | 
 |      linker just checks the archive name; the GNU linker may check the | 
 |      date.  */ | 
 |   stat (bfd_get_filename (abfd), &statbuf); | 
 |   _bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld", | 
 | 		    (long) (statbuf.st_mtime + 60)); | 
 |  | 
 |   /* The DECstation uses zeroes for the uid, gid and mode of the | 
 |      armap.  */ | 
 |   hdr.ar_uid[0] = '0'; | 
 |   hdr.ar_gid[0] = '0'; | 
 |   /* Building gcc ends up extracting the armap as a file - twice.  */ | 
 |   hdr.ar_mode[0] = '6'; | 
 |   hdr.ar_mode[1] = '4'; | 
 |   hdr.ar_mode[2] = '4'; | 
 |  | 
 |   _bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld", mapsize); | 
 |  | 
 |   hdr.ar_fmag[0] = '`'; | 
 |   hdr.ar_fmag[1] = '\012'; | 
 |  | 
 |   /* Turn all null bytes in the header into spaces.  */ | 
 |   for (i = 0; i < sizeof (struct ar_hdr); i++) | 
 |    if (((char *) (&hdr))[i] == '\0') | 
 |      (((char *) (&hdr))[i]) = ' '; | 
 |  | 
 |   if (bfd_write (&hdr, sizeof (struct ar_hdr), abfd) != sizeof (struct ar_hdr)) | 
 |     return false; | 
 |  | 
 |   H_PUT_32 (abfd, hashsize, temp); | 
 |   if (bfd_write (temp, 4, abfd) != 4) | 
 |     return false; | 
 |  | 
 |   hashtable = (bfd_byte *) bfd_zalloc (abfd, symdefsize); | 
 |   if (!hashtable) | 
 |     return false; | 
 |  | 
 |   current = abfd->archive_head; | 
 |   last_elt = current; | 
 |   for (i = 0; i < orl_count; i++) | 
 |     { | 
 |       unsigned int hash, rehash = 0; | 
 |  | 
 |       /* Advance firstreal to the file position of this archive | 
 | 	 element.  */ | 
 |       if (map[i].u.abfd != last_elt) | 
 | 	{ | 
 | 	  do | 
 | 	    { | 
 | 	      firstreal += arelt_size (current) + sizeof (struct ar_hdr); | 
 | 	      firstreal += firstreal % 2; | 
 | 	      current = current->archive_next; | 
 | 	    } | 
 | 	  while (current != map[i].u.abfd); | 
 | 	} | 
 |  | 
 |       last_elt = current; | 
 |  | 
 |       hash = ecoff_armap_hash (*map[i].name, &rehash, hashsize, hashlog); | 
 |       if (H_GET_32 (abfd, (hashtable + (hash * 8) + 4)) != 0) | 
 | 	{ | 
 | 	  unsigned int srch; | 
 |  | 
 | 	  /* The desired slot is already taken.  */ | 
 | 	  for (srch = (hash + rehash) & (hashsize - 1); | 
 | 	       srch != hash; | 
 | 	       srch = (srch + rehash) & (hashsize - 1)) | 
 | 	    if (H_GET_32 (abfd, (hashtable + (srch * 8) + 4)) == 0) | 
 | 	      break; | 
 |  | 
 | 	  BFD_ASSERT (srch != hash); | 
 |  | 
 | 	  hash = srch; | 
 | 	} | 
 |  | 
 |       H_PUT_32 (abfd, map[i].namidx, (hashtable + hash * 8)); | 
 |       H_PUT_32 (abfd, firstreal, (hashtable + hash * 8 + 4)); | 
 |     } | 
 |  | 
 |   if (bfd_write (hashtable, symdefsize, abfd) != symdefsize) | 
 |     return false; | 
 |  | 
 |   bfd_release (abfd, hashtable); | 
 |  | 
 |   /* Now write the strings.  */ | 
 |   H_PUT_32 (abfd, stringsize, temp); | 
 |   if (bfd_write (temp, 4, abfd) != 4) | 
 |     return false; | 
 |   for (i = 0; i < orl_count; i++) | 
 |     { | 
 |       bfd_size_type len; | 
 |  | 
 |       len = strlen (*map[i].name) + 1; | 
 |       if (bfd_write (*map[i].name, len, abfd) != len) | 
 | 	return false; | 
 |     } | 
 |  | 
 |   /* The spec sez this should be a newline.  But in order to be | 
 |      bug-compatible for DECstation ar we use a null.  */ | 
 |   if (padit) | 
 |     { | 
 |       if (bfd_write ("", 1, abfd) != 1) | 
 | 	return false; | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* ECOFF linker code.  */ | 
 |  | 
 | /* Routine to create an entry in an ECOFF link hash table.  */ | 
 |  | 
 | static struct bfd_hash_entry * | 
 | ecoff_link_hash_newfunc (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 == NULL) | 
 |     ret = ((struct ecoff_link_hash_entry *) | 
 | 	   bfd_hash_allocate (table, sizeof (struct ecoff_link_hash_entry))); | 
 |   if (ret == NULL) | 
 |     return NULL; | 
 |  | 
 |   /* Call the allocation method of the superclass.  */ | 
 |   ret = ((struct ecoff_link_hash_entry *) | 
 | 	 _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret, | 
 | 				 table, string)); | 
 |  | 
 |   if (ret) | 
 |     { | 
 |       /* Set local fields.  */ | 
 |       ret->indx = -1; | 
 |       ret->abfd = NULL; | 
 |       ret->written = 0; | 
 |       ret->small = 0; | 
 |     } | 
 |   memset ((void *) &ret->esym, 0, sizeof ret->esym); | 
 |  | 
 |   return (struct bfd_hash_entry *) ret; | 
 | } | 
 |  | 
 | /* Create an ECOFF link hash table.  */ | 
 |  | 
 | struct bfd_link_hash_table * | 
 | _bfd_ecoff_bfd_link_hash_table_create (bfd *abfd) | 
 | { | 
 |   struct ecoff_link_hash_table *ret; | 
 |   size_t amt = sizeof (struct ecoff_link_hash_table); | 
 |  | 
 |   ret = (struct ecoff_link_hash_table *) bfd_malloc (amt); | 
 |   if (ret == NULL) | 
 |     return NULL; | 
 |   if (!_bfd_link_hash_table_init (&ret->root, abfd, | 
 | 				  ecoff_link_hash_newfunc, | 
 | 				  sizeof (struct ecoff_link_hash_entry))) | 
 |     { | 
 |       free (ret); | 
 |       return 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))) | 
 |  | 
 | /* 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)) | 
 |  | 
 | /* 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 bool | 
 | ecoff_link_add_externals (bfd *abfd, | 
 | 			  struct bfd_link_info *info, | 
 | 			  void * external_ext, | 
 | 			  char *ssext) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   void (* const swap_ext_in) (bfd *, void *, EXTR *) | 
 |     = backend->debug_swap.swap_ext_in; | 
 |   bfd_size_type external_ext_size = backend->debug_swap.external_ext_size; | 
 |   unsigned long ext_count; | 
 |   struct bfd_link_hash_entry **sym_hash; | 
 |   char *ext_ptr; | 
 |   char *ext_end; | 
 |   bfd_size_type amt; | 
 |  | 
 |   ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax; | 
 |  | 
 |   amt = ext_count; | 
 |   amt *= sizeof (struct bfd_link_hash_entry *); | 
 |   sym_hash = (struct bfd_link_hash_entry **) bfd_alloc (abfd, amt); | 
 |   if (!sym_hash) | 
 |     return false; | 
 |   ecoff_data (abfd)->sym_hashes = (struct ecoff_link_hash_entry **) 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; | 
 |       bool skip; | 
 |       bfd_vma value; | 
 |       asection *section; | 
 |       const char *name; | 
 |       struct ecoff_link_hash_entry *h; | 
 |  | 
 |       *sym_hash = NULL; | 
 |  | 
 |       (*swap_ext_in) (abfd, (void *) 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_ptr; | 
 | 	  break; | 
 | 	case scUndefined: | 
 | 	  section = bfd_und_section_ptr; | 
 | 	  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_ptr; | 
 | 	      break; | 
 | 	    } | 
 | 	  /* Fall through.  */ | 
 | 	case scSCommon: | 
 | 	  section = &ecoff_scom_section; | 
 | 	  break; | 
 | 	case scSUndefined: | 
 | 	  section = bfd_und_section_ptr; | 
 | 	  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; | 
 | 	case scRConst: | 
 | 	  section = bfd_make_section_old_way (abfd, _RCONST); | 
 | 	  value -= section->vma; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       if (section == NULL) | 
 | 	continue; | 
 |  | 
 |       name = ssext + esym.asym.iss; | 
 |  | 
 |       if (! (_bfd_generic_link_add_one_symbol | 
 | 	     (info, abfd, name, | 
 | 	      (flagword) (esym.weakext ? BSF_WEAK : BSF_GLOBAL), | 
 | 	      section, value, NULL, true, true, sym_hash))) | 
 | 	return false; | 
 |  | 
 |       h = (struct ecoff_link_hash_entry *) *sym_hash; | 
 |  | 
 |       /* If we are building an ECOFF hash table, save the external | 
 | 	 symbol information.  */ | 
 |       if (bfd_get_flavour (info->output_bfd) == bfd_get_flavour (abfd)) | 
 | 	{ | 
 | 	  if (h->abfd == NULL | 
 | 	      || (! bfd_is_und_section (section) | 
 | 		  && (! bfd_is_com_section (section) | 
 | 		      || (h->root.type != bfd_link_hash_defined | 
 | 			  && h->root.type != bfd_link_hash_defweak)))) | 
 | 	    { | 
 | 	      h->abfd = abfd; | 
 | 	      h->esym = esym; | 
 | 	    } | 
 |  | 
 | 	  /* Remember whether this symbol was small undefined.  */ | 
 | 	  if (esym.asym.sc == scSUndefined) | 
 | 	    h->small = 1; | 
 |  | 
 | 	  /* If this symbol was ever small undefined, it needs to wind | 
 | 	     up in a GP relative section.  We can't control the | 
 | 	     section of a defined symbol, but we can control the | 
 | 	     section of a common symbol.  This case is actually needed | 
 | 	     on Ultrix 4.2 to handle the symbol cred in -lckrb.  */ | 
 | 	  if (h->small | 
 | 	      && h->root.type == bfd_link_hash_common | 
 | 	      && streq (h->root.u.c.p->section->name, SCOMMON)) | 
 | 	    { | 
 | 	      h->root.u.c.p->section = bfd_make_section_old_way (abfd, | 
 | 								 SCOMMON); | 
 | 	      h->root.u.c.p->section->flags = SEC_ALLOC; | 
 | 	      if (h->esym.asym.sc == scCommon) | 
 | 		h->esym.asym.sc = scSCommon; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* Add symbols from an ECOFF object file to the global linker hash | 
 |    table.  */ | 
 |  | 
 | static bool | 
 | ecoff_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info) | 
 | { | 
 |   HDRR *symhdr; | 
 |   bfd_size_type external_ext_size; | 
 |   void * external_ext = NULL; | 
 |   bfd_size_type esize; | 
 |   char *ssext = NULL; | 
 |   bool result; | 
 |  | 
 |   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.  */ | 
 |   if (bfd_seek (abfd, symhdr->cbExtOffset, SEEK_SET) != 0) | 
 |     return false; | 
 |   external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size; | 
 |   esize = symhdr->iextMax * external_ext_size; | 
 |   external_ext = _bfd_malloc_and_read (abfd, esize, esize); | 
 |   if (external_ext == NULL && esize != 0) | 
 |     goto error_return; | 
 |  | 
 |   if (bfd_seek (abfd, symhdr->cbSsExtOffset, SEEK_SET) != 0) | 
 |     goto error_return; | 
 |   ssext = (char *) _bfd_malloc_and_read (abfd, symhdr->issExtMax, | 
 | 					 symhdr->issExtMax); | 
 |   if (ssext == NULL && symhdr->issExtMax != 0) | 
 |     goto error_return; | 
 |  | 
 |   result = ecoff_link_add_externals (abfd, info, external_ext, ssext); | 
 |  | 
 |   free (ssext); | 
 |   free (external_ext); | 
 |   return result; | 
 |  | 
 |  error_return: | 
 |   free (ssext); | 
 |   free (external_ext); | 
 |   return false; | 
 | } | 
 |  | 
 | /* This is called if we used _bfd_generic_link_add_archive_symbols | 
 |    because we were not dealing with an ECOFF archive.  */ | 
 |  | 
 | static bool | 
 | ecoff_link_check_archive_element (bfd *abfd, | 
 | 				  struct bfd_link_info *info, | 
 | 				  struct bfd_link_hash_entry *h, | 
 | 				  const char *name, | 
 | 				  bool *pneeded) | 
 | { | 
 |   *pneeded = false; | 
 |  | 
 |   /* Unlike the generic linker, we do not pull in elements because | 
 |      of common symbols.  */ | 
 |   if (h->type != bfd_link_hash_undefined) | 
 |     return true; | 
 |  | 
 |   /* Include this element?  */ | 
 |   if (!(*info->callbacks->add_archive_element) (info, abfd, name, &abfd)) | 
 |     return true; | 
 |   *pneeded = true; | 
 |  | 
 |   return ecoff_link_add_object_symbols (abfd, info); | 
 | } | 
 |  | 
 | /* 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 bool | 
 | ecoff_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info) | 
 | { | 
 |   const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | 
 |   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)) | 
 |     { | 
 |       /* An empty archive is a special case.  */ | 
 |       if (bfd_openr_next_archived_file (abfd, NULL) == NULL) | 
 | 	return true; | 
 |       bfd_set_error (bfd_error_no_armap); | 
 |       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 == 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; | 
 | 	  bool 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, | 
 | 						info); | 
 |       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.  */ | 
 |  | 
 | bool | 
 | _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; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /* 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 bool | 
 | 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; | 
 |   bool ret; | 
 |  | 
 | #define READ(ptr, offset, count, size)					\ | 
 |   do									\ | 
 |     {									\ | 
 |       size_t amt;							\ | 
 |       debug->ptr = NULL;						\ | 
 |       if (symhdr->count == 0)						\ | 
 | 	break;								\ | 
 |       if (_bfd_mul_overflow (size, symhdr->count, &amt))		\ | 
 | 	{								\ | 
 | 	  bfd_set_error (bfd_error_file_too_big);			\ | 
 | 	  ret = false;							\ | 
 | 	  goto return_something;					\ | 
 | 	}								\ | 
 |       if (bfd_seek (input_bfd, symhdr->offset, SEEK_SET) != 0)		\ | 
 | 	{								\ | 
 | 	  ret = false;							\ | 
 | 	  goto return_something;					\ | 
 | 	}								\ | 
 |       debug->ptr = _bfd_malloc_and_read (input_bfd, amt + 1, amt);	\ | 
 |       if (debug->ptr == NULL)						\ | 
 | 	{								\ | 
 | 	  ret = false;							\ | 
 | 	  goto return_something;					\ | 
 | 	}								\ | 
 |       ((char *) debug->ptr)[amt] = 0;					\ | 
 |     } while (0) | 
 |  | 
 |   /* If alloc_syments is true, then the data was already by read by | 
 |      _bfd_ecoff_slurp_symbolic_info.  */ | 
 |   if (!debug->alloc_syments) | 
 |     { | 
 |       READ (line, cbLineOffset, cbLine, sizeof (unsigned char)); | 
 |       READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size); | 
 |       READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size); | 
 |       READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size); | 
 |       READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size); | 
 |       READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext)); | 
 |       READ (ss, cbSsOffset, issMax, sizeof (char)); | 
 |       READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size); | 
 |       READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size); | 
 |     } | 
 | #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: | 
 |   _bfd_ecoff_free_ecoff_debug_info (debug); | 
 |   return ret; | 
 | } | 
 |  | 
 | /* Relocate and write an ECOFF section into an ECOFF output file.  */ | 
 |  | 
 | static bool | 
 | 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; | 
 |  | 
 |   if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0) | 
 |     goto error_return; | 
 |   external_relocs = _bfd_malloc_and_read (input_bfd, external_relocs_size, | 
 | 					  external_relocs_size); | 
 |   if (external_relocs == NULL && external_relocs_size != 0) | 
 |     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 (bfd_link_relocatable (info)) | 
 |     { | 
 |       file_ptr pos = (output_section->rel_filepos | 
 | 		      + output_section->reloc_count * external_reloc_size); | 
 |       if (bfd_seek (output_bfd, pos, SEEK_SET) != 0 | 
 | 	  || (bfd_write (external_relocs, external_relocs_size, output_bfd) | 
 | 	      != external_relocs_size)) | 
 | 	goto error_return; | 
 |       output_section->reloc_count += input_section->reloc_count; | 
 |     } | 
 |  | 
 |   free (contents); | 
 |   free (external_relocs); | 
 |   return true; | 
 |  | 
 |  error_return: | 
 |   free (contents); | 
 |   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 bool | 
 | 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; | 
 |   bool 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)) | 
 | 	{ | 
 | 	  type = bfd_section_reloc_link_order; | 
 | 	  section = h->u.def.section->output_section; | 
 | 	  /* It seems that we ought to add the symbol value to the | 
 | 	     addend here, but in practice it has already been added | 
 | 	     because it was passed to constructor_callback.  */ | 
 | 	  addend += section->vma + h->u.def.section->output_offset; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* We can't set up a reloc against a symbol correctly, | 
 | 	     because we have no asymbol structure.  Currently no | 
 | 	     adjust_reloc_out routine cares.  */ | 
 | 	  rel.sym_ptr_ptr = NULL; | 
 | 	} | 
 |     } | 
 |  | 
 |   /* All ECOFF relocs are in-place.  Put the addend into the object | 
 |      file.  */ | 
 |  | 
 |   BFD_ASSERT (rel.howto->partial_inplace); | 
 |   if (addend != 0) | 
 |     { | 
 |       bfd_size_type size; | 
 |       bfd_reloc_status_type rstat; | 
 |       bfd_byte *buf; | 
 |  | 
 |       size = bfd_get_reloc_size (rel.howto); | 
 |       buf = (bfd_byte *) bfd_zmalloc (size); | 
 |       if (buf == NULL && size != 0) | 
 | 	return false; | 
 |       rstat = _bfd_relocate_contents (rel.howto, output_bfd, | 
 | 				      (bfd_vma) addend, buf); | 
 |       switch (rstat) | 
 | 	{ | 
 | 	case bfd_reloc_ok: | 
 | 	  break; | 
 | 	default: | 
 | 	case bfd_reloc_outofrange: | 
 | 	  abort (); | 
 | 	case bfd_reloc_overflow: | 
 | 	  (*info->callbacks->reloc_overflow) | 
 | 	    (info, NULL, | 
 | 	     (link_order->type == bfd_section_reloc_link_order | 
 | 	      ? bfd_section_name (section) | 
 | 	      : link_order->u.reloc.p->u.name), | 
 | 	     rel.howto->name, addend, NULL, NULL, (bfd_vma) 0); | 
 | 	  break; | 
 | 	} | 
 |       ok = bfd_set_section_contents (output_bfd, output_section, (void *) buf, | 
 | 				     (file_ptr) link_order->offset, size); | 
 |       free (buf); | 
 |       if (! ok) | 
 | 	return false; | 
 |     } | 
 |  | 
 |   rel.addend = 0; | 
 |  | 
 |   /* Move the information into an internal_reloc structure.  */ | 
 |   in.r_vaddr = rel.address + bfd_section_vma (output_section); | 
 |   in.r_type = rel.howto->type; | 
 |  | 
 |   if (type == bfd_symbol_reloc_link_order) | 
 |     { | 
 |       struct ecoff_link_hash_entry *h; | 
 |  | 
 |       h = ((struct ecoff_link_hash_entry *) | 
 | 	   bfd_wrapped_link_hash_lookup (output_bfd, info, | 
 | 					 link_order->u.reloc.p->u.name, | 
 | 					 false, false, true)); | 
 |       if (h != NULL | 
 | 	  && h->indx != -1) | 
 | 	in.r_symndx = h->indx; | 
 |       else | 
 | 	{ | 
 | 	  (*info->callbacks->unattached_reloc) | 
 | 	    (info, link_order->u.reloc.p->u.name, NULL, NULL, (bfd_vma) 0); | 
 | 	  in.r_symndx = 0; | 
 | 	} | 
 |       in.r_extern = 1; | 
 |     } | 
 |   else | 
 |     { | 
 |       const char *name; | 
 |       unsigned int i; | 
 |       static struct | 
 |       { | 
 | 	const char * name; | 
 | 	long r_symndx; | 
 |       } | 
 |       section_symndx [] = | 
 |       { | 
 | 	{ _TEXT,   RELOC_SECTION_TEXT   }, | 
 | 	{ _RDATA,  RELOC_SECTION_RDATA  }, | 
 | 	{ _DATA,   RELOC_SECTION_DATA   }, | 
 | 	{ _SDATA,  RELOC_SECTION_SDATA  }, | 
 | 	{ _SBSS,   RELOC_SECTION_SBSS   }, | 
 | 	{ _BSS,    RELOC_SECTION_BSS    }, | 
 | 	{ _INIT,   RELOC_SECTION_INIT   }, | 
 | 	{ _LIT8,   RELOC_SECTION_LIT8   }, | 
 | 	{ _LIT4,   RELOC_SECTION_LIT4   }, | 
 | 	{ _XDATA,  RELOC_SECTION_XDATA  }, | 
 | 	{ _PDATA,  RELOC_SECTION_PDATA  }, | 
 | 	{ _FINI,   RELOC_SECTION_FINI   }, | 
 | 	{ _LITA,   RELOC_SECTION_LITA   }, | 
 | 	{ "*ABS*", RELOC_SECTION_ABS    }, | 
 | 	{ _RCONST, RELOC_SECTION_RCONST } | 
 |       }; | 
 |  | 
 |       name = bfd_section_name (section); | 
 |  | 
 |       for (i = 0; i < ARRAY_SIZE (section_symndx); i++) | 
 | 	if (streq (name, section_symndx[i].name)) | 
 | 	  { | 
 | 	    in.r_symndx = section_symndx[i].r_symndx; | 
 | 	    break; | 
 | 	  } | 
 |  | 
 |       if (i == ARRAY_SIZE (section_symndx)) | 
 | 	abort (); | 
 |  | 
 |       in.r_extern = 0; | 
 |     } | 
 |  | 
 |   /* Let the BFD backend adjust the reloc.  */ | 
 |   (*ecoff_backend (output_bfd)->adjust_reloc_out) (output_bfd, &rel, &in); | 
 |  | 
 |   /* Get some memory and swap out the reloc.  */ | 
 |   external_reloc_size = ecoff_backend (output_bfd)->external_reloc_size; | 
 |   rbuf = (bfd_byte *) bfd_malloc (external_reloc_size); | 
 |   if (rbuf == NULL) | 
 |     return false; | 
 |  | 
 |   (*ecoff_backend (output_bfd)->swap_reloc_out) (output_bfd, &in, (void *) rbuf); | 
 |  | 
 |   pos = (output_section->rel_filepos | 
 | 	 + output_section->reloc_count * external_reloc_size); | 
 |   ok = (bfd_seek (output_bfd, pos, SEEK_SET) == 0 | 
 | 	&& (bfd_write (rbuf, external_reloc_size, output_bfd) | 
 | 	    == external_reloc_size)); | 
 |  | 
 |   if (ok) | 
 |     ++output_section->reloc_count; | 
 |  | 
 |   free (rbuf); | 
 |  | 
 |   return ok; | 
 | } | 
 |  | 
 | /* Put out information for an external symbol.  These come only from | 
 |    the hash table.  */ | 
 |  | 
 | static bool | 
 | 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; | 
 |   bool 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); | 
 |  | 
 | 	  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.  */ | 
 |  | 
 | bool | 
 | _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) | 
 |     { | 
 |       bool 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 (bfd_link_relocatable (info)) | 
 |     { | 
 |       /* 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 (bfd_link_relocatable (info)) | 
 |     { | 
 |       /* 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 (bfd_link_relocatable (info)) | 
 | 	{ | 
 | 	  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; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   abfd->symcount = symhdr->iextMax + symhdr->isymMax; | 
 |  | 
 |   ecoff_data (abfd)->linker = true; | 
 |  | 
 |   return true; | 
 | } |