| /* Generic ECOFF (Extended-COFF) routines. | |
| Copyright 1990-2013 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 "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) | |
| #define strneq(a, b, n) (strncmp ((a), (b), (n)) == 0) | |
| /* This stuff is somewhat copied from coffcode.h. */ | |
| static asection bfd_debug_section = | |
| { | |
| /* name, id, index, next, prev, flags, user_set_vma, */ | |
| "*DEBUG*", 0, 0, NULL, NULL, 0, 0, | |
| /* linker_mark, linker_has_input, gc_mark, compress_status, */ | |
| 0, 0, 1, 0, | |
| /* segment_mark, sec_info_type, use_rela_p, */ | |
| 0, 0, 0, | |
| /* sec_flg0, sec_flg1, sec_flg2, sec_flg3, sec_flg4, sec_flg5, */ | |
| 0, 0, 0, 0, 0, 0, | |
| /* vma, lma, size, rawsize, compressed_size, relax, relax_count, */ | |
| 0, 0, 0, 0, 0, 0, 0, | |
| /* output_offset, output_section, alignment_power, */ | |
| 0, NULL, 0, | |
| /* relocation, orelocation, reloc_count, filepos, rel_filepos, */ | |
| NULL, NULL, 0, 0, 0, | |
| /* line_filepos, userdata, contents, lineno, lineno_count, */ | |
| 0, NULL, NULL, NULL, 0, | |
| /* entsize, kept_section, moving_line_filepos, */ | |
| 0, NULL, 0, | |
| /* target_index, used_by_bfd, constructor_chain, owner, */ | |
| 0, NULL, NULL, NULL, | |
| /* symbol, */ | |
| NULL, | |
| /* symbol_ptr_ptr, */ | |
| NULL, | |
| /* map_head, map_tail */ | |
| { NULL }, { NULL } | |
| }; | |
| /* Create an ECOFF object. */ | |
| bfd_boolean | |
| _bfd_ecoff_mkobject (bfd *abfd) | |
| { | |
| bfd_size_type 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; | |
| } | |
| /* Initialize a new section. */ | |
| bfd_boolean | |
| _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 }, | |
| { _RDATA, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | |
| { _LIT8, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | |
| { _LIT4, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | |
| { _RCONST, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | |
| { _PDATA, SEC_ALLOC | SEC_DATA | SEC_LOAD | SEC_READONLY}, | |
| { _BSS, SEC_ALLOC}, | |
| { _SBSS, SEC_ALLOC}, | |
| /* 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); | |
| } | |
| /* 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. */ | |
| bfd_boolean | |
| _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); | |
| } | |
| bfd_boolean | |
| _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. */ | |
| bfd_boolean | |
| _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; | |
| } | |
| else if ((styp_flags & STYP_BSS) | |
| || (styp_flags & STYP_SBSS)) | |
| 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_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 bfd_boolean | |
| 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) | |
| { | |
| bfd_get_symcount (abfd) = 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. */ | |
| raw = bfd_malloc (external_hdr_size); | |
| if (raw == NULL) | |
| goto error_return; | |
| if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) != 0 | |
| || bfd_bread (raw, external_hdr_size, abfd) != external_hdr_size) | |
| 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; | |
| } | |
| /* Now we can get the correct number of symbols. */ | |
| bfd_get_symcount (abfd) = (internal_symhdr->isymMax | |
| + internal_symhdr->iextMax); | |
| if (raw != NULL) | |
| free (raw); | |
| return TRUE; | |
| error_return: | |
| if (raw != NULL) | |
| 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. */ | |
| bfd_boolean | |
| _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; | |
| bfd_size_type amt; | |
| file_ptr pos; | |
| BFD_ASSERT (debug == &ecoff_data (abfd)->debug_info); | |
| /* Check whether we've already gotten it, and whether there's any to | |
| get. */ | |
| if (ecoff_data (abfd)->raw_syments != NULL) | |
| return TRUE; | |
| if (ecoff_data (abfd)->sym_filepos == 0) | |
| { | |
| bfd_get_symcount (abfd) = 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 = 0; | |
| #define UPDATE_RAW_END(start, count, size) \ | |
| cb_end = internal_symhdr->start + internal_symhdr->count * (size); \ | |
| if (cb_end > raw_end) \ | |
| raw_end = cb_end | |
| 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; | |
| } | |
| raw = bfd_alloc (abfd, raw_size); | |
| if (raw == NULL) | |
| return FALSE; | |
| pos = ecoff_data (abfd)->sym_filepos; | |
| pos += backend->debug_swap.external_hdr_size; | |
| if (bfd_seek (abfd, pos, SEEK_SET) != 0 | |
| || bfd_bread (raw, raw_size, abfd) != raw_size) | |
| { | |
| bfd_release (abfd, raw); | |
| return FALSE; | |
| } | |
| ecoff_data (abfd)->raw_syments = raw; | |
| /* Get pointers for the numeric offsets in the HDRR structure. */ | |
| #define FIX(off1, off2, type) \ | |
| if (internal_symhdr->off1 == 0) \ | |
| debug->off2 = NULL; \ | |
| else \ | |
| debug->off2 = (type) ((char *) raw \ | |
| + (internal_symhdr->off1 \ | |
| - raw_base)) | |
| FIX (cbLineOffset, line, unsigned char *); | |
| FIX (cbDnOffset, external_dnr, void *); | |
| FIX (cbPdOffset, external_pdr, void *); | |
| FIX (cbSymOffset, external_sym, void *); | |
| FIX (cbOptOffset, external_opt, void *); | |
| FIX (cbAuxOffset, external_aux, union aux_ext *); | |
| FIX (cbSsOffset, ss, char *); | |
| FIX (cbSsExtOffset, ssext, char *); | |
| FIX (cbFdOffset, external_fdr, void *); | |
| FIX (cbRfdOffset, external_rfd, void *); | |
| FIX (cbExtOffset, external_ext, void *); | |
| #undef FIX | |
| /* 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. */ | |
| amt = internal_symhdr->ifdMax; | |
| amt *= sizeof (struct fdr); | |
| 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; | |
| 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 asymbol ecoff_scom_symbol; | |
| static asymbol *ecoff_scom_symbol_ptr; | |
| /* Create an empty symbol. */ | |
| asymbol * | |
| _bfd_ecoff_make_empty_symbol (bfd *abfd) | |
| { | |
| ecoff_symbol_type *new_symbol; | |
| bfd_size_type 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 bfd_boolean | |
| 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: | |
| if (ecoff_scom_section.name == NULL) | |
| { | |
| /* Initialize the small common section. */ | |
| ecoff_scom_section.name = SCOMMON; | |
| ecoff_scom_section.flags = SEC_IS_COMMON; | |
| ecoff_scom_section.output_section = &ecoff_scom_section; | |
| ecoff_scom_section.symbol = &ecoff_scom_symbol; | |
| ecoff_scom_section.symbol_ptr_ptr = &ecoff_scom_symbol_ptr; | |
| ecoff_scom_symbol.name = SCOMMON; | |
| ecoff_scom_symbol.flags = BSF_SECTION_SYM; | |
| ecoff_scom_symbol.section = &ecoff_scom_section; | |
| ecoff_scom_symbol_ptr = &ecoff_scom_symbol; | |
| } | |
| 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. */ | |
| bfd_boolean | |
| _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; | |
| bfd_size_type internal_size; | |
| ecoff_symbol_type *internal; | |
| ecoff_symbol_type *internal_ptr; | |
| char *eraw_src; | |
| char *eraw_end; | |
| FDR *fdr_ptr; | |
| FDR *fdr_end; | |
| /* 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; | |
| internal_size = bfd_get_symcount (abfd); | |
| internal_size *= sizeof (ecoff_symbol_type); | |
| internal = (ecoff_symbol_type *) bfd_alloc (abfd, internal_size); | |
| 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); | |
| 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. */ | |
| if (internal_esym.ifd >= 0) | |
| internal_ptr->fdr = (ecoff_data (abfd)->debug_info.fdr | |
| + internal_esym.ifd); | |
| else | |
| internal_ptr->fdr = NULL; | |
| 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; | |
| 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); | |
| 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; | |
| } | |
| } | |
| 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) | |
| { | |
| 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]; | |
| static char buffer2[1024]; | |
| char *p1 = buffer1; | |
| char *p2 = buffer2; | |
| 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 (buffer1); | |
| /* 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); | |
| p1 += strlen (buffer1); | |
| } | |
| /* 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 buffer2; | |
| } | |
| /* 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. */ | |
| bfd_boolean | |
| _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 "); | |
| fprintf_vma (file, (bfd_vma) 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 "); | |
| fprintf_vma (file, (bfd_vma) 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); | |
| fprintf_vma (file, (bfd_vma) 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) | |
| 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)); | |
| 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)) | |
| fprintf (file, _("\n Type: %s"), | |
| ecoff_type_to_string (abfd, fdr, indx)); | |
| break; | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| /* Read in the relocs for a section. */ | |
| static bfd_boolean | |
| 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; | |
| char *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; | |
| amt = section->reloc_count; | |
| amt *= sizeof (arelent); | |
| internal_relocs = (arelent *) bfd_alloc (abfd, amt); | |
| external_reloc_size = backend->external_reloc_size; | |
| amt = external_reloc_size * section->reloc_count; | |
| external_relocs = (char *) bfd_alloc (abfd, amt); | |
| if (internal_relocs == NULL || external_relocs == NULL) | |
| return FALSE; | |
| if (bfd_seek (abfd, section->rel_filepos, SEEK_SET) != 0) | |
| return FALSE; | |
| if (bfd_bread (external_relocs, amt, abfd) != amt) | |
| 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); | |
| if (intern.r_extern) | |
| { | |
| /* r_symndx is an index into the external symbols. */ | |
| BFD_ASSERT (intern.r_symndx >= 0 | |
| && (intern.r_symndx | |
| < (ecoff_data (abfd) | |
| ->debug_info.symbolic_header.iextMax))); | |
| rptr->sym_ptr_ptr = symbols + intern.r_symndx; | |
| rptr->addend = 0; | |
| } | |
| else if (intern.r_symndx == RELOC_SECTION_NONE | |
| || intern.r_symndx == RELOC_SECTION_ABS) | |
| { | |
| rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; | |
| rptr->addend = 0; | |
| } | |
| 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: abort (); | |
| } | |
| sec = bfd_get_section_by_name (abfd, sec_name); | |
| if (sec == NULL) | |
| abort (); | |
| rptr->sym_ptr_ptr = sec->symbol_ptr_ptr; | |
| rptr->addend = - bfd_get_section_vma (abfd, sec); | |
| } | |
| rptr->address = intern.r_vaddr - bfd_get_section_vma (abfd, section); | |
| /* Let the backend select the howto field and do any other | |
| required processing. */ | |
| (*backend->adjust_reloc_in) (abfd, &intern, rptr); | |
| } | |
| bfd_release (abfd, 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. */ | |
| bfd_boolean | |
| _bfd_ecoff_find_nearest_line (bfd *abfd, | |
| asection *section, | |
| asymbol **ignore_symbols ATTRIBUTE_UNUSED, | |
| bfd_vma offset, | |
| const char **filename_ptr, | |
| const char **functionname_ptr, | |
| unsigned int *retline_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) | |
| { | |
| bfd_size_type 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; | |
| } | |
| 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. */ | |
| bfd_boolean | |
| _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; | |
| bfd_boolean 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; | |
| } | |
| 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. */ | |
| bfd_boolean | |
| _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. */ | |
| bfd_boolean | |
| _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 bfd_boolean | |
| ecoff_compute_section_file_positions (bfd *abfd) | |
| { | |
| file_ptr sofar, file_sofar; | |
| asection **sorted_hdrs; | |
| asection *current; | |
| unsigned int i; | |
| file_ptr old_sofar; | |
| bfd_boolean rdata_in_text; | |
| bfd_boolean 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. */ | |
| bfd_boolean | |
| _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_bwrite (location, count, abfd) != count) | |
| return FALSE; | |
| return TRUE; | |
| } | |
| /* Get the GP value for an ECOFF file. This is a hook used by | |
| nlmconv. */ | |
| bfd_vma | |
| bfd_ecoff_get_gp_value (bfd *abfd) | |
| { | |
| if (bfd_get_flavour (abfd) != bfd_target_ecoff_flavour | |
| || bfd_get_format (abfd) != bfd_object) | |
| { | |
| bfd_set_error (bfd_error_invalid_operation); | |
| return 0; | |
| } | |
| return ecoff_data (abfd)->gp; | |
| } | |
| /* Set the GP value for an ECOFF file. This is a hook used by the | |
| assembler. */ | |
| bfd_boolean | |
| 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. */ | |
| bfd_boolean | |
| 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 bfd_boolean | |
| 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_get_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. */ | |
| bfd_boolean | |
| _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; | |
| bfd_boolean set_text_start; | |
| bfd_size_type data_size; | |
| bfd_vma data_start; | |
| bfd_boolean 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, (file_ptr) (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_get_section_vma (abfd, 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_bwrite (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, (file_ptr) 0, SEEK_SET) != 0) | |
| goto error_return; | |
| bfd_coff_swap_filehdr_out (abfd, (void *) &internal_f, buff); | |
| if (bfd_bwrite (buff, filhsz, abfd) != filhsz) | |
| goto error_return; | |
| bfd_coff_swap_aouthdr_out (abfd, (void *) &internal_a, buff); | |
| if (bfd_bwrite (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_alloc (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_get_section_vma (abfd, 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_get_section_name (abfd, bfd_get_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_bwrite (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, (file_ptr) ecoff_data (abfd)->sym_filepos - 1, | |
| SEEK_SET) != 0) | |
| goto error_return; | |
| if (bfd_bread (&c, (bfd_size_type) 1, abfd) == 0) | |
| c = 0; | |
| if (bfd_seek (abfd, (file_ptr) ecoff_data (abfd)->sym_filepos - 1, | |
| SEEK_SET) != 0) | |
| goto error_return; | |
| if (bfd_bwrite (&c, (bfd_size_type) 1, abfd) != 1) | |
| goto error_return; | |
| } | |
| if (reloc_buff != NULL) | |
| bfd_release (abfd, reloc_buff); | |
| if (buff != NULL) | |
| free (buff); | |
| return TRUE; | |
| error_return: | |
| if (reloc_buff != NULL) | |
| bfd_release (abfd, reloc_buff); | |
| if (buff != NULL) | |
| 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. */ | |
| bfd_boolean | |
| _bfd_ecoff_slurp_armap (bfd *abfd) | |
| { | |
| char nextname[17]; | |
| unsigned int i; | |
| struct areltdata *mapdata; | |
| bfd_size_type parsed_size; | |
| 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_bread ((void *) nextname, (bfd_size_type) 16, abfd); | |
| if (i == 0) | |
| return TRUE; | |
| if (i != 16) | |
| return FALSE; | |
| if (bfd_seek (abfd, (file_ptr) -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 (CONST_STRNEQ (nextname, "/ ")) | |
| return bfd_slurp_armap (abfd); | |
| /* See if the first element is an armap. */ | |
| if (! strneq (nextname, ecoff_backend (abfd)->armap_start, ARMAP_START_LENGTH) | |
| || 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) | |
| || ! strneq (nextname + ARMAP_END_INDEX, ARMAP_END, sizeof ARMAP_END - 1)) | |
| { | |
| bfd_has_map (abfd) = 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); | |
| raw_armap = (char *) bfd_alloc (abfd, parsed_size); | |
| if (raw_armap == NULL) | |
| return FALSE; | |
| if (bfd_bread ((void *) raw_armap, parsed_size, abfd) != parsed_size) | |
| { | |
| if (bfd_get_error () != bfd_error_system_call) | |
| bfd_set_error (bfd_error_malformed_archive); | |
| bfd_release (abfd, (void *) raw_armap); | |
| return FALSE; | |
| } | |
| ardata->tdata = (void *) raw_armap; | |
| count = H_GET_32 (abfd, raw_armap); | |
| 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; | |
| #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) | |
| return FALSE; | |
| 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); | |
| 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; | |
| bfd_has_map (abfd) = TRUE; | |
| return TRUE; | |
| } | |
| /* Write out an armap. */ | |
| bfd_boolean | |
| _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 (abfd->filename, &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_bwrite ((void *) &hdr, (bfd_size_type) sizeof (struct ar_hdr), abfd) | |
| != sizeof (struct ar_hdr)) | |
| return FALSE; | |
| H_PUT_32 (abfd, hashsize, temp); | |
| if (bfd_bwrite ((void *) temp, (bfd_size_type) 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_bwrite ((void *) hashtable, symdefsize, abfd) != symdefsize) | |
| return FALSE; | |
| bfd_release (abfd, hashtable); | |
| /* Now write the strings. */ | |
| H_PUT_32 (abfd, stringsize, temp); | |
| if (bfd_bwrite ((void *) temp, (bfd_size_type) 4, abfd) != 4) | |
| return FALSE; | |
| for (i = 0; i < orl_count; i++) | |
| { | |
| bfd_size_type len; | |
| len = strlen (*map[i].name) + 1; | |
| if (bfd_bwrite ((void *) (*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_bwrite ("", (bfd_size_type) 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; | |
| bfd_size_type 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 bfd_boolean | |
| 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; | |
| bfd_boolean 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: | |
| if (ecoff_scom_section.name == NULL) | |
| { | |
| /* Initialize the small common section. */ | |
| ecoff_scom_section.name = SCOMMON; | |
| ecoff_scom_section.flags = SEC_IS_COMMON; | |
| ecoff_scom_section.output_section = &ecoff_scom_section; | |
| ecoff_scom_section.symbol = &ecoff_scom_symbol; | |
| ecoff_scom_section.symbol_ptr_ptr = &ecoff_scom_symbol_ptr; | |
| ecoff_scom_symbol.name = SCOMMON; | |
| ecoff_scom_symbol.flags = BSF_SECTION_SYM; | |
| ecoff_scom_symbol.section = &ecoff_scom_section; | |
| ecoff_scom_symbol_ptr = &ecoff_scom_symbol; | |
| } | |
| section = &ecoff_scom_section; | |
| break; | |
| case scSUndefined: | |
| section = bfd_und_section_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 bfd_boolean | |
| 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; | |
| bfd_boolean 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. */ | |
| external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size; | |
| esize = symhdr->iextMax * external_ext_size; | |
| external_ext = bfd_malloc (esize); | |
| if (external_ext == NULL && esize != 0) | |
| goto error_return; | |
| if (bfd_seek (abfd, (file_ptr) symhdr->cbExtOffset, SEEK_SET) != 0 | |
| || bfd_bread (external_ext, esize, abfd) != esize) | |
| goto error_return; | |
| ssext = (char *) bfd_malloc ((bfd_size_type) symhdr->issExtMax); | |
| if (ssext == NULL && symhdr->issExtMax != 0) | |
| goto error_return; | |
| if (bfd_seek (abfd, (file_ptr) symhdr->cbSsExtOffset, SEEK_SET) != 0 | |
| || (bfd_bread (ssext, (bfd_size_type) symhdr->issExtMax, abfd) | |
| != (bfd_size_type) symhdr->issExtMax)) | |
| goto error_return; | |
| result = ecoff_link_add_externals (abfd, info, external_ext, ssext); | |
| if (ssext != NULL) | |
| free (ssext); | |
| if (external_ext != NULL) | |
| free (external_ext); | |
| return result; | |
| error_return: | |
| if (ssext != NULL) | |
| free (ssext); | |
| if (external_ext != NULL) | |
| free (external_ext); | |
| return FALSE; | |
| } | |
| /* Factored out from ecoff_link_check_archive_element. */ | |
| static bfd_boolean | |
| read_ext_syms_and_strs (HDRR **symhdr, bfd_size_type *external_ext_size, | |
| bfd_size_type *esize, void **external_ext, char **ssext, bfd *abfd, | |
| const struct ecoff_backend_data * const backend) | |
| { | |
| 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; | |
| *external_ext_size = backend->debug_swap.external_ext_size; | |
| *esize = (*symhdr)->iextMax * *external_ext_size; | |
| *external_ext = bfd_malloc (*esize); | |
| if (*external_ext == NULL && *esize != 0) | |
| return FALSE; | |
| if (bfd_seek (abfd, (file_ptr) (*symhdr)->cbExtOffset, SEEK_SET) != 0 | |
| || bfd_bread (*external_ext, *esize, abfd) != *esize) | |
| return FALSE; | |
| *ssext = (char *) bfd_malloc ((bfd_size_type) (*symhdr)->issExtMax); | |
| if (*ssext == NULL && (*symhdr)->issExtMax != 0) | |
| return FALSE; | |
| if (bfd_seek (abfd, (file_ptr) (*symhdr)->cbSsExtOffset, SEEK_SET) != 0 | |
| || (bfd_bread (*ssext, (bfd_size_type) (*symhdr)->issExtMax, abfd) | |
| != (bfd_size_type) (*symhdr)->issExtMax)) | |
| return FALSE; | |
| return TRUE; | |
| } | |
| static bfd_boolean | |
| reread_ext_syms_and_strs (HDRR **symhdr, bfd_size_type *external_ext_size, | |
| bfd_size_type *esize, void **external_ext, char **ssext, bfd *abfd, | |
| const struct ecoff_backend_data * const backend) | |
| { | |
| if (*external_ext != NULL) | |
| free (*external_ext); | |
| *external_ext = NULL; | |
| if (*ssext != NULL) | |
| free (*ssext); | |
| *ssext = NULL; | |
| return read_ext_syms_and_strs (symhdr, external_ext_size, esize, | |
| external_ext, ssext, abfd, backend); | |
| } | |
| /* This is called if we used _bfd_generic_link_add_archive_symbols | |
| because we were not dealing with an ECOFF archive. */ | |
| static bfd_boolean | |
| ecoff_link_check_archive_element (bfd *abfd, | |
| struct bfd_link_info *info, | |
| bfd_boolean *pneeded) | |
| { | |
| const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | |
| void (* const swap_ext_in) (bfd *, void *, EXTR *) | |
| = backend->debug_swap.swap_ext_in; | |
| HDRR *symhdr; | |
| bfd_size_type external_ext_size = 0; | |
| void * external_ext = NULL; | |
| bfd_size_type esize = 0; | |
| char *ssext = NULL; | |
| char *ext_ptr; | |
| char *ext_end; | |
| *pneeded = FALSE; | |
| /* Read in the external symbols and external strings. */ | |
| if (!read_ext_syms_and_strs (&symhdr, &external_ext_size, &esize, | |
| &external_ext, &ssext, abfd, backend)) | |
| goto error_return; | |
| /* If there are no symbols, we don't want it. */ | |
| if (bfd_get_symcount (abfd) == 0) | |
| goto successful_return; | |
| /* Look through the external symbols to see if they define some | |
| symbol that is currently undefined. */ | |
| ext_ptr = (char *) external_ext; | |
| ext_end = ext_ptr + esize; | |
| for (; ext_ptr < ext_end; ext_ptr += external_ext_size) | |
| { | |
| EXTR esym; | |
| bfd_boolean def; | |
| const char *name; | |
| bfd *oldbfd; | |
| struct bfd_link_hash_entry *h; | |
| (*swap_ext_in) (abfd, (void *) ext_ptr, &esym); | |
| /* See if this symbol defines something. */ | |
| if (esym.asym.st != stGlobal | |
| && esym.asym.st != stLabel | |
| && esym.asym.st != stProc) | |
| continue; | |
| switch (esym.asym.sc) | |
| { | |
| case scText: | |
| case scData: | |
| case scBss: | |
| case scAbs: | |
| case scSData: | |
| case scSBss: | |
| case scRData: | |
| case scCommon: | |
| case scSCommon: | |
| case scInit: | |
| case scFini: | |
| case scRConst: | |
| def = TRUE; | |
| break; | |
| default: | |
| def = FALSE; | |
| break; | |
| } | |
| if (! def) | |
| continue; | |
| name = ssext + esym.asym.iss; | |
| h = bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, TRUE); | |
| /* Unlike the generic linker, we do not pull in elements because | |
| of common symbols. */ | |
| if (h == NULL | |
| || h->type != bfd_link_hash_undefined) | |
| continue; | |
| /* Include this element. */ | |
| oldbfd = abfd; | |
| if (!(*info->callbacks | |
| ->add_archive_element) (info, abfd, name, &abfd)) | |
| goto error_return; | |
| /* Potentially, the add_archive_element hook may have set a | |
| substitute BFD for us. */ | |
| if (abfd != oldbfd | |
| && !reread_ext_syms_and_strs (&symhdr, &external_ext_size, &esize, | |
| &external_ext, &ssext, abfd, backend)) | |
| goto error_return; | |
| if (! ecoff_link_add_externals (abfd, info, external_ext, ssext)) | |
| goto error_return; | |
| *pneeded = TRUE; | |
| goto successful_return; | |
| } | |
| successful_return: | |
| if (external_ext != NULL) | |
| free (external_ext); | |
| if (ssext != NULL) | |
| free (ssext); | |
| return TRUE; | |
| error_return: | |
| if (external_ext != NULL) | |
| free (external_ext); | |
| if (ssext != NULL) | |
| free (ssext); | |
| return FALSE; | |
| } | |
| /* Add the symbols from an archive file to the global hash table. | |
| This looks through the undefined symbols, looks each one up in the | |
| archive hash table, and adds any associated object file. We do not | |
| use _bfd_generic_link_add_archive_symbols because ECOFF archives | |
| already have a hash table, so there is no reason to construct | |
| another one. */ | |
| static bfd_boolean | |
| 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; | |
| bfd_boolean found; | |
| /* That was the wrong symbol. Try rehashing. */ | |
| found = FALSE; | |
| for (srch = (hash + rehash) & (armap_count - 1); | |
| srch != hash; | |
| srch = (srch + rehash) & (armap_count - 1)) | |
| { | |
| file_offset = H_GET_32 (abfd, hashtable + (srch * 8) + 4); | |
| if (file_offset == 0) | |
| break; | |
| name = stringbase + H_GET_32 (abfd, hashtable + (srch * 8)); | |
| if (name[0] == h->root.string[0] | |
| && streq (name, h->root.string)) | |
| { | |
| found = TRUE; | |
| break; | |
| } | |
| } | |
| if (! found) | |
| { | |
| pundef = &(*pundef)->u.undef.next; | |
| continue; | |
| } | |
| hash = srch; | |
| } | |
| element = (*backend->get_elt_at_filepos) (abfd, (file_ptr) file_offset); | |
| if (element == NULL) | |
| return FALSE; | |
| if (! bfd_check_format (element, bfd_object)) | |
| return FALSE; | |
| /* Unlike the generic linker, we know that this element provides | |
| a definition for an undefined symbol and we know that we want | |
| to include it. We don't need to check anything. */ | |
| if (!(*info->callbacks | |
| ->add_archive_element) (info, element, name, &element)) | |
| return FALSE; | |
| if (! ecoff_link_add_object_symbols (element, info)) | |
| return FALSE; | |
| pundef = &(*pundef)->u.undef.next; | |
| } | |
| return TRUE; | |
| } | |
| /* Given an ECOFF BFD, add symbols to the global hash table as | |
| appropriate. */ | |
| bfd_boolean | |
| _bfd_ecoff_bfd_link_add_symbols (bfd *abfd, struct bfd_link_info *info) | |
| { | |
| switch (bfd_get_format (abfd)) | |
| { | |
| case bfd_object: | |
| return ecoff_link_add_object_symbols (abfd, info); | |
| case bfd_archive: | |
| return ecoff_link_add_archive_symbols (abfd, info); | |
| default: | |
| bfd_set_error (bfd_error_wrong_format); | |
| return FALSE; | |
| } | |
| } | |
| /* ECOFF final link routines. */ | |
| /* Structure used to pass information to ecoff_link_write_external. */ | |
| struct extsym_info | |
| { | |
| bfd *abfd; | |
| struct bfd_link_info *info; | |
| }; | |
| /* Accumulate the debugging information for an input BFD into the | |
| output BFD. This must read in the symbolic information of the | |
| input BFD. */ | |
| static bfd_boolean | |
| ecoff_final_link_debug_accumulate (bfd *output_bfd, | |
| bfd *input_bfd, | |
| struct bfd_link_info *info, | |
| void * handle) | |
| { | |
| struct ecoff_debug_info * const debug = &ecoff_data (input_bfd)->debug_info; | |
| const struct ecoff_debug_swap * const swap = | |
| &ecoff_backend (input_bfd)->debug_swap; | |
| HDRR *symhdr = &debug->symbolic_header; | |
| bfd_boolean ret; | |
| #define READ(ptr, offset, count, size, type) \ | |
| if (symhdr->count == 0) \ | |
| debug->ptr = NULL; \ | |
| else \ | |
| { \ | |
| bfd_size_type amt = (bfd_size_type) size * symhdr->count; \ | |
| debug->ptr = (type) bfd_malloc (amt); \ | |
| if (debug->ptr == NULL) \ | |
| { \ | |
| ret = FALSE; \ | |
| goto return_something; \ | |
| } \ | |
| if (bfd_seek (input_bfd, (file_ptr) symhdr->offset, SEEK_SET) != 0 \ | |
| || bfd_bread (debug->ptr, amt, input_bfd) != amt) \ | |
| { \ | |
| ret = FALSE; \ | |
| goto return_something; \ | |
| } \ | |
| } | |
| /* If raw_syments is not NULL, then the data was already by read by | |
| _bfd_ecoff_slurp_symbolic_info. */ | |
| if (ecoff_data (input_bfd)->raw_syments == NULL) | |
| { | |
| READ (line, cbLineOffset, cbLine, sizeof (unsigned char), | |
| unsigned char *); | |
| READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, void *); | |
| READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, void *); | |
| READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, void *); | |
| READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, void *); | |
| READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext), | |
| union aux_ext *); | |
| READ (ss, cbSsOffset, issMax, sizeof (char), char *); | |
| READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, void *); | |
| READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, void *); | |
| } | |
| #undef READ | |
| /* We do not read the external strings or the external symbols. */ | |
| ret = (bfd_ecoff_debug_accumulate | |
| (handle, output_bfd, &ecoff_data (output_bfd)->debug_info, | |
| &ecoff_backend (output_bfd)->debug_swap, | |
| input_bfd, debug, swap, info)); | |
| return_something: | |
| if (ecoff_data (input_bfd)->raw_syments == NULL) | |
| { | |
| if (debug->line != NULL) | |
| free (debug->line); | |
| if (debug->external_dnr != NULL) | |
| free (debug->external_dnr); | |
| if (debug->external_pdr != NULL) | |
| free (debug->external_pdr); | |
| if (debug->external_sym != NULL) | |
| free (debug->external_sym); | |
| if (debug->external_opt != NULL) | |
| free (debug->external_opt); | |
| if (debug->external_aux != NULL) | |
| free (debug->external_aux); | |
| if (debug->ss != NULL) | |
| free (debug->ss); | |
| if (debug->external_fdr != NULL) | |
| free (debug->external_fdr); | |
| if (debug->external_rfd != NULL) | |
| free (debug->external_rfd); | |
| /* Make sure we don't accidentally follow one of these pointers | |
| into freed memory. */ | |
| debug->line = NULL; | |
| debug->external_dnr = NULL; | |
| debug->external_pdr = NULL; | |
| debug->external_sym = NULL; | |
| debug->external_opt = NULL; | |
| debug->external_aux = NULL; | |
| debug->ss = NULL; | |
| debug->external_fdr = NULL; | |
| debug->external_rfd = NULL; | |
| } | |
| return ret; | |
| } | |
| /* Relocate and write an ECOFF section into an ECOFF output file. */ | |
| static bfd_boolean | |
| ecoff_indirect_link_order (bfd *output_bfd, | |
| struct bfd_link_info *info, | |
| asection *output_section, | |
| struct bfd_link_order *link_order) | |
| { | |
| asection *input_section; | |
| bfd *input_bfd; | |
| bfd_byte *contents = NULL; | |
| bfd_size_type external_reloc_size; | |
| bfd_size_type external_relocs_size; | |
| void * external_relocs = NULL; | |
| BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0); | |
| input_section = link_order->u.indirect.section; | |
| input_bfd = input_section->owner; | |
| if (input_section->size == 0) | |
| return TRUE; | |
| BFD_ASSERT (input_section->output_section == output_section); | |
| BFD_ASSERT (input_section->output_offset == link_order->offset); | |
| BFD_ASSERT (input_section->size == link_order->size); | |
| /* Get the section contents. */ | |
| if (!bfd_malloc_and_get_section (input_bfd, input_section, &contents)) | |
| goto error_return; | |
| /* Get the relocs. If we are relaxing MIPS code, they will already | |
| have been read in. Otherwise, we read them in now. */ | |
| external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size; | |
| external_relocs_size = external_reloc_size * input_section->reloc_count; | |
| external_relocs = bfd_malloc (external_relocs_size); | |
| if (external_relocs == NULL && external_relocs_size != 0) | |
| goto error_return; | |
| if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0 | |
| || (bfd_bread (external_relocs, external_relocs_size, input_bfd) | |
| != external_relocs_size)) | |
| goto error_return; | |
| /* Relocate the section contents. */ | |
| if (! ((*ecoff_backend (input_bfd)->relocate_section) | |
| (output_bfd, info, input_bfd, input_section, contents, | |
| external_relocs))) | |
| goto error_return; | |
| /* Write out the relocated section. */ | |
| if (! bfd_set_section_contents (output_bfd, | |
| output_section, | |
| contents, | |
| input_section->output_offset, | |
| input_section->size)) | |
| goto error_return; | |
| /* If we are producing relocatable output, the relocs were | |
| modified, and we write them out now. We use the reloc_count | |
| field of output_section to keep track of the number of relocs we | |
| have output so far. */ | |
| if (info->relocatable) | |
| { | |
| file_ptr pos = (output_section->rel_filepos | |
| + output_section->reloc_count * external_reloc_size); | |
| if (bfd_seek (output_bfd, pos, SEEK_SET) != 0 | |
| || (bfd_bwrite (external_relocs, external_relocs_size, output_bfd) | |
| != external_relocs_size)) | |
| goto error_return; | |
| output_section->reloc_count += input_section->reloc_count; | |
| } | |
| if (contents != NULL) | |
| free (contents); | |
| if (external_relocs != NULL) | |
| free (external_relocs); | |
| return TRUE; | |
| error_return: | |
| if (contents != NULL) | |
| free (contents); | |
| if (external_relocs != NULL) | |
| free (external_relocs); | |
| return FALSE; | |
| } | |
| /* Generate a reloc when linking an ECOFF file. This is a reloc | |
| requested by the linker, and does come from any input file. This | |
| is used to build constructor and destructor tables when linking | |
| with -Ur. */ | |
| static bfd_boolean | |
| ecoff_reloc_link_order (bfd *output_bfd, | |
| struct bfd_link_info *info, | |
| asection *output_section, | |
| struct bfd_link_order *link_order) | |
| { | |
| enum bfd_link_order_type type; | |
| asection *section; | |
| bfd_vma addend; | |
| arelent rel; | |
| struct internal_reloc in; | |
| bfd_size_type external_reloc_size; | |
| bfd_byte *rbuf; | |
| bfd_boolean ok; | |
| file_ptr pos; | |
| type = link_order->type; | |
| section = NULL; | |
| addend = link_order->u.reloc.p->addend; | |
| /* We set up an arelent to pass to the backend adjust_reloc_out | |
| routine. */ | |
| rel.address = link_order->offset; | |
| rel.howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc); | |
| if (rel.howto == 0) | |
| { | |
| bfd_set_error (bfd_error_bad_value); | |
| return FALSE; | |
| } | |
| if (type == bfd_section_reloc_link_order) | |
| { | |
| section = link_order->u.reloc.p->u.section; | |
| rel.sym_ptr_ptr = section->symbol_ptr_ptr; | |
| } | |
| else | |
| { | |
| struct bfd_link_hash_entry *h; | |
| /* Treat a reloc against a defined symbol as though it were | |
| actually against the section. */ | |
| h = bfd_wrapped_link_hash_lookup (output_bfd, info, | |
| link_order->u.reloc.p->u.name, | |
| FALSE, FALSE, FALSE); | |
| if (h != NULL | |
| && (h->type == bfd_link_hash_defined | |
| || h->type == bfd_link_hash_defweak)) | |
| { | |
| 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) | |
| 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: | |
| if (! ((*info->callbacks->reloc_overflow) | |
| (info, NULL, | |
| (link_order->type == bfd_section_reloc_link_order | |
| ? bfd_section_name (output_bfd, section) | |
| : link_order->u.reloc.p->u.name), | |
| rel.howto->name, addend, NULL, | |
| NULL, (bfd_vma) 0))) | |
| { | |
| free (buf); | |
| return FALSE; | |
| } | |
| 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_get_section_vma (output_bfd, 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 | |
| { | |
| if (! ((*info->callbacks->unattached_reloc) | |
| (info, link_order->u.reloc.p->u.name, NULL, | |
| NULL, (bfd_vma) 0))) | |
| return FALSE; | |
| 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_get_section_name (output_bfd, 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_bwrite ((void *) 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 bfd_boolean | |
| ecoff_link_write_external (struct bfd_hash_entry *bh, void * data) | |
| { | |
| struct ecoff_link_hash_entry *h = (struct ecoff_link_hash_entry *) bh; | |
| struct extsym_info *einfo = (struct extsym_info *) data; | |
| bfd *output_bfd = einfo->abfd; | |
| bfd_boolean strip; | |
| if (h->root.type == bfd_link_hash_warning) | |
| { | |
| h = (struct ecoff_link_hash_entry *) h->root.u.i.link; | |
| if (h->root.type == bfd_link_hash_new) | |
| return TRUE; | |
| } | |
| /* We need to check if this symbol is being stripped. */ | |
| if (h->root.type == bfd_link_hash_undefined | |
| || h->root.type == bfd_link_hash_undefweak) | |
| strip = FALSE; | |
| else if (einfo->info->strip == strip_all | |
| || (einfo->info->strip == strip_some | |
| && bfd_hash_lookup (einfo->info->keep_hash, | |
| h->root.root.string, | |
| FALSE, FALSE) == NULL)) | |
| strip = TRUE; | |
| else | |
| strip = FALSE; | |
| if (strip || h->written) | |
| return TRUE; | |
| if (h->abfd == NULL) | |
| { | |
| h->esym.jmptbl = 0; | |
| h->esym.cobol_main = 0; | |
| h->esym.weakext = 0; | |
| h->esym.reserved = 0; | |
| h->esym.ifd = ifdNil; | |
| h->esym.asym.value = 0; | |
| h->esym.asym.st = stGlobal; | |
| if (h->root.type != bfd_link_hash_defined | |
| && h->root.type != bfd_link_hash_defweak) | |
| h->esym.asym.sc = scAbs; | |
| else | |
| { | |
| asection *output_section; | |
| const char *name; | |
| unsigned int i; | |
| static struct | |
| { | |
| const char * name; | |
| int sc; | |
| } | |
| section_storage_classes [] = | |
| { | |
| { _TEXT, scText }, | |
| { _DATA, scData }, | |
| { _SDATA, scSData }, | |
| { _RDATA, scRData }, | |
| { _BSS, scBss }, | |
| { _SBSS, scSBss }, | |
| { _INIT, scInit }, | |
| { _FINI, scFini }, | |
| { _PDATA, scPData }, | |
| { _XDATA, scXData }, | |
| { _RCONST, scRConst } | |
| }; | |
| output_section = h->root.u.def.section->output_section; | |
| name = bfd_section_name (output_section->owner, output_section); | |
| for (i = 0; i < ARRAY_SIZE (section_storage_classes); i++) | |
| if (streq (name, section_storage_classes[i].name)) | |
| { | |
| h->esym.asym.sc = section_storage_classes[i].sc; | |
| break; | |
| } | |
| if (i == ARRAY_SIZE (section_storage_classes)) | |
| h->esym.asym.sc = scAbs; | |
| } | |
| h->esym.asym.reserved = 0; | |
| h->esym.asym.index = indexNil; | |
| } | |
| else if (h->esym.ifd != -1) | |
| { | |
| struct ecoff_debug_info *debug; | |
| /* Adjust the FDR index for the symbol by that used for the | |
| input BFD. */ | |
| debug = &ecoff_data (h->abfd)->debug_info; | |
| BFD_ASSERT (h->esym.ifd >= 0 | |
| && h->esym.ifd < debug->symbolic_header.ifdMax); | |
| h->esym.ifd = debug->ifdmap[h->esym.ifd]; | |
| } | |
| switch (h->root.type) | |
| { | |
| default: | |
| case bfd_link_hash_warning: | |
| case bfd_link_hash_new: | |
| abort (); | |
| case bfd_link_hash_undefined: | |
| case bfd_link_hash_undefweak: | |
| if (h->esym.asym.sc != scUndefined | |
| && h->esym.asym.sc != scSUndefined) | |
| h->esym.asym.sc = scUndefined; | |
| break; | |
| case bfd_link_hash_defined: | |
| case bfd_link_hash_defweak: | |
| if (h->esym.asym.sc == scUndefined | |
| || h->esym.asym.sc == scSUndefined) | |
| h->esym.asym.sc = scAbs; | |
| else if (h->esym.asym.sc == scCommon) | |
| h->esym.asym.sc = scBss; | |
| else if (h->esym.asym.sc == scSCommon) | |
| h->esym.asym.sc = scSBss; | |
| h->esym.asym.value = (h->root.u.def.value | |
| + h->root.u.def.section->output_section->vma | |
| + h->root.u.def.section->output_offset); | |
| break; | |
| case bfd_link_hash_common: | |
| if (h->esym.asym.sc != scCommon | |
| && h->esym.asym.sc != scSCommon) | |
| h->esym.asym.sc = scCommon; | |
| h->esym.asym.value = h->root.u.c.size; | |
| break; | |
| case bfd_link_hash_indirect: | |
| /* We ignore these symbols, since the indirected symbol is | |
| already in the hash table. */ | |
| return TRUE; | |
| } | |
| /* bfd_ecoff_debug_one_external uses iextMax to keep track of the | |
| symbol number. */ | |
| h->indx = ecoff_data (output_bfd)->debug_info.symbolic_header.iextMax; | |
| h->written = 1; | |
| return (bfd_ecoff_debug_one_external | |
| (output_bfd, &ecoff_data (output_bfd)->debug_info, | |
| &ecoff_backend (output_bfd)->debug_swap, h->root.root.string, | |
| &h->esym)); | |
| } | |
| /* ECOFF final link routine. This looks through all the input BFDs | |
| and gathers together all the debugging information, and then | |
| processes all the link order information. This may cause it to | |
| close and reopen some input BFDs; I'll see how bad this is. */ | |
| bfd_boolean | |
| _bfd_ecoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info) | |
| { | |
| const struct ecoff_backend_data * const backend = ecoff_backend (abfd); | |
| struct ecoff_debug_info * const debug = &ecoff_data (abfd)->debug_info; | |
| HDRR *symhdr; | |
| void * handle; | |
| bfd *input_bfd; | |
| asection *o; | |
| struct bfd_link_order *p; | |
| struct extsym_info einfo; | |
| /* We accumulate the debugging information counts in the symbolic | |
| header. */ | |
| symhdr = &debug->symbolic_header; | |
| symhdr->vstamp = 0; | |
| symhdr->ilineMax = 0; | |
| symhdr->cbLine = 0; | |
| symhdr->idnMax = 0; | |
| symhdr->ipdMax = 0; | |
| symhdr->isymMax = 0; | |
| symhdr->ioptMax = 0; | |
| symhdr->iauxMax = 0; | |
| symhdr->issMax = 0; | |
| symhdr->issExtMax = 0; | |
| symhdr->ifdMax = 0; | |
| symhdr->crfd = 0; | |
| symhdr->iextMax = 0; | |
| /* We accumulate the debugging information itself in the debug_info | |
| structure. */ | |
| debug->line = NULL; | |
| debug->external_dnr = NULL; | |
| debug->external_pdr = NULL; | |
| debug->external_sym = NULL; | |
| debug->external_opt = NULL; | |
| debug->external_aux = NULL; | |
| debug->ss = NULL; | |
| debug->ssext = debug->ssext_end = NULL; | |
| debug->external_fdr = NULL; | |
| debug->external_rfd = NULL; | |
| debug->external_ext = debug->external_ext_end = NULL; | |
| handle = bfd_ecoff_debug_init (abfd, debug, &backend->debug_swap, info); | |
| if (handle == NULL) | |
| return FALSE; | |
| /* Accumulate the debugging symbols from each input BFD. */ | |
| for (input_bfd = info->input_bfds; | |
| input_bfd != NULL; | |
| input_bfd = input_bfd->link_next) | |
| { | |
| bfd_boolean ret; | |
| if (bfd_get_flavour (input_bfd) == bfd_target_ecoff_flavour) | |
| { | |
| /* Arbitrarily set the symbolic header vstamp to the vstamp | |
| of the first object file in the link. */ | |
| if (symhdr->vstamp == 0) | |
| symhdr->vstamp | |
| = ecoff_data (input_bfd)->debug_info.symbolic_header.vstamp; | |
| ret = ecoff_final_link_debug_accumulate (abfd, input_bfd, info, | |
| handle); | |
| } | |
| else | |
| ret = bfd_ecoff_debug_accumulate_other (handle, abfd, | |
| debug, &backend->debug_swap, | |
| input_bfd, info); | |
| if (! ret) | |
| return FALSE; | |
| /* Combine the register masks. */ | |
| ecoff_data (abfd)->gprmask |= ecoff_data (input_bfd)->gprmask; | |
| ecoff_data (abfd)->fprmask |= ecoff_data (input_bfd)->fprmask; | |
| ecoff_data (abfd)->cprmask[0] |= ecoff_data (input_bfd)->cprmask[0]; | |
| ecoff_data (abfd)->cprmask[1] |= ecoff_data (input_bfd)->cprmask[1]; | |
| ecoff_data (abfd)->cprmask[2] |= ecoff_data (input_bfd)->cprmask[2]; | |
| ecoff_data (abfd)->cprmask[3] |= ecoff_data (input_bfd)->cprmask[3]; | |
| } | |
| /* Write out the external symbols. */ | |
| einfo.abfd = abfd; | |
| einfo.info = info; | |
| bfd_hash_traverse (&info->hash->table, ecoff_link_write_external, &einfo); | |
| if (info->relocatable) | |
| { | |
| /* We need to make a pass over the link_orders to count up the | |
| number of relocations we will need to output, so that we know | |
| how much space they will take up. */ | |
| for (o = abfd->sections; o != NULL; o = o->next) | |
| { | |
| o->reloc_count = 0; | |
| for (p = o->map_head.link_order; | |
| p != NULL; | |
| p = p->next) | |
| if (p->type == bfd_indirect_link_order) | |
| o->reloc_count += p->u.indirect.section->reloc_count; | |
| else if (p->type == bfd_section_reloc_link_order | |
| || p->type == bfd_symbol_reloc_link_order) | |
| ++o->reloc_count; | |
| } | |
| } | |
| /* Compute the reloc and symbol file positions. */ | |
| ecoff_compute_reloc_file_positions (abfd); | |
| /* Write out the debugging information. */ | |
| if (! bfd_ecoff_write_accumulated_debug (handle, abfd, debug, | |
| &backend->debug_swap, info, | |
| ecoff_data (abfd)->sym_filepos)) | |
| return FALSE; | |
| bfd_ecoff_debug_free (handle, abfd, debug, &backend->debug_swap, info); | |
| if (info->relocatable) | |
| { | |
| /* Now reset the reloc_count field of the sections in the output | |
| BFD to 0, so that we can use them to keep track of how many | |
| relocs we have output thus far. */ | |
| for (o = abfd->sections; o != NULL; o = o->next) | |
| o->reloc_count = 0; | |
| } | |
| /* Get a value for the GP register. */ | |
| if (ecoff_data (abfd)->gp == 0) | |
| { | |
| struct bfd_link_hash_entry *h; | |
| h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE); | |
| if (h != NULL | |
| && h->type == bfd_link_hash_defined) | |
| ecoff_data (abfd)->gp = (h->u.def.value | |
| + h->u.def.section->output_section->vma | |
| + h->u.def.section->output_offset); | |
| else if (info->relocatable) | |
| { | |
| bfd_vma lo; | |
| /* Make up a value. */ | |
| lo = (bfd_vma) -1; | |
| for (o = abfd->sections; o != NULL; o = o->next) | |
| { | |
| if (o->vma < lo | |
| && (streq (o->name, _SBSS) | |
| || streq (o->name, _SDATA) | |
| || streq (o->name, _LIT4) | |
| || streq (o->name, _LIT8) | |
| || streq (o->name, _LITA))) | |
| lo = o->vma; | |
| } | |
| ecoff_data (abfd)->gp = lo + 0x8000; | |
| } | |
| else | |
| { | |
| /* If the relocate_section function needs to do a reloc | |
| involving the GP value, it should make a reloc_dangerous | |
| callback to warn that GP is not defined. */ | |
| } | |
| } | |
| for (o = abfd->sections; o != NULL; o = o->next) | |
| { | |
| for (p = o->map_head.link_order; | |
| p != NULL; | |
| p = p->next) | |
| { | |
| if (p->type == bfd_indirect_link_order | |
| && (bfd_get_flavour (p->u.indirect.section->owner) | |
| == bfd_target_ecoff_flavour)) | |
| { | |
| if (! ecoff_indirect_link_order (abfd, info, o, p)) | |
| return FALSE; | |
| } | |
| else if (p->type == bfd_section_reloc_link_order | |
| || p->type == bfd_symbol_reloc_link_order) | |
| { | |
| if (! ecoff_reloc_link_order (abfd, info, o, p)) | |
| return FALSE; | |
| } | |
| else | |
| { | |
| if (! _bfd_default_link_order (abfd, info, o, p)) | |
| return FALSE; | |
| } | |
| } | |
| } | |
| bfd_get_symcount (abfd) = symhdr->iextMax + symhdr->isymMax; | |
| ecoff_data (abfd)->linker = TRUE; | |
| return TRUE; | |
| } |