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