|  | /* ECOFF object file format. | 
|  | Copyright (C) 1993-2022 Free Software Foundation, Inc. | 
|  | Contributed by Cygnus Support. | 
|  | This file was put together by Ian Lance Taylor <ian@cygnus.com>. | 
|  |  | 
|  | This file is part of GAS. | 
|  |  | 
|  | GAS 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, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | GAS 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 GAS; see the file COPYING.  If not, write to the Free | 
|  | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA | 
|  | 02110-1301, USA.  */ | 
|  |  | 
|  | #define OBJ_HEADER "obj-ecoff.h" | 
|  | #include "as.h" | 
|  | #include "coff/internal.h" | 
|  | #include "bfd/libcoff.h" | 
|  | #include "bfd/libecoff.h" | 
|  | #include "bfd/ecoff-bfd.h" | 
|  |  | 
|  | /* Almost all of the ECOFF support is actually in ecoff.c in the main | 
|  | gas directory.  This file mostly just arranges to call that one at | 
|  | the right times.  */ | 
|  |  | 
|  | /* Set section VMAs and GP values before reloc processing.  */ | 
|  |  | 
|  | void | 
|  | ecoff_frob_file_before_fix (void) | 
|  | { | 
|  | bfd_vma addr; | 
|  | asection *sec; | 
|  |  | 
|  | /* Set the section VMA values.  We force the .sdata and .sbss | 
|  | sections to the end to ensure that their VMA addresses are close | 
|  | together so that the GP register can address both of them.  We | 
|  | put the .bss section after the .sbss section. | 
|  |  | 
|  | Also, for the Alpha, we must sort the sections, to make sure they | 
|  | appear in the output file in the correct order.  (Actually, maybe | 
|  | this is a job for BFD.  But the VMAs computed would be out of | 
|  | whack if we computed them given our initial, random ordering. | 
|  | It's possible that that wouldn't break things; I could do some | 
|  | experimenting sometime and find out. | 
|  |  | 
|  | This output ordering of sections is magic, on the Alpha, at | 
|  | least.  The .lita section must come before .lit8 and .lit4, | 
|  | otherwise the OSF/1 linker may silently trash the .lit{4,8} | 
|  | section contents.  Also, .text must precede .rdata.  These differ | 
|  | from the order described in some parts of the DEC OSF/1 Assembly | 
|  | Language Programmer's Guide, but that order doesn't seem to work | 
|  | with their linker. | 
|  |  | 
|  | I don't know if section ordering on the MIPS is important.  */ | 
|  |  | 
|  | static const char *const names[] = | 
|  | { | 
|  | /* text segment */ | 
|  | ".text", ".rdata", ".init", ".fini", | 
|  | /* data segment */ | 
|  | ".data", ".lita", ".lit8", ".lit4", ".sdata", ".got", | 
|  | /* bss segment */ | 
|  | ".sbss", ".bss", | 
|  | }; | 
|  | #define n_names ((int) (sizeof (names) / sizeof (names[0]))) | 
|  |  | 
|  | /* Sections that match names, order to be straightened out later.  */ | 
|  | asection *secs[n_names]; | 
|  | int i; | 
|  |  | 
|  | addr = 0; | 
|  | for (i = 0; i < n_names; i++) | 
|  | secs[i] = NULL; | 
|  |  | 
|  | for (sec = stdoutput->sections; sec != NULL; sec = sec->next) | 
|  | { | 
|  | for (i = 0; i < n_names; i++) | 
|  | if (!strcmp (sec->name, names[i])) | 
|  | { | 
|  | secs[i] = sec; | 
|  | bfd_section_list_remove (stdoutput, sec); | 
|  | break; | 
|  | } | 
|  | if (i == n_names) | 
|  | { | 
|  | bfd_set_section_vma (sec, addr); | 
|  | addr += bfd_section_size (sec); | 
|  | } | 
|  | } | 
|  | for (i = 0; i < n_names; i++) | 
|  | if (secs[i]) | 
|  | { | 
|  | bfd_set_section_vma (secs[i], addr); | 
|  | addr += bfd_section_size (secs[i]); | 
|  | } | 
|  | for (i = n_names - 1; i >= 0; i--) | 
|  | if (secs[i]) | 
|  | bfd_section_list_prepend (stdoutput, secs[i]); | 
|  |  | 
|  | /* Fill in the register masks.  */ | 
|  | { | 
|  | unsigned long gprmask = 0; | 
|  | unsigned long fprmask = 0; | 
|  | unsigned long *cprmask = NULL; | 
|  |  | 
|  | #ifdef TC_MIPS | 
|  | /* Fill in the MIPS register masks.  It's probably not worth | 
|  | setting up a generic interface for this.  */ | 
|  | gprmask = mips_gprmask; | 
|  | cprmask = mips_cprmask; | 
|  | #endif | 
|  |  | 
|  | #ifdef TC_ALPHA | 
|  | alpha_frob_ecoff_data (); | 
|  |  | 
|  | if (! bfd_ecoff_set_gp_value (stdoutput, alpha_gp_value)) | 
|  | as_fatal (_("Can't set GP value")); | 
|  |  | 
|  | gprmask = alpha_gprmask; | 
|  | fprmask = alpha_fprmask; | 
|  | #endif | 
|  |  | 
|  | if (! bfd_ecoff_set_regmasks (stdoutput, gprmask, fprmask, cprmask)) | 
|  | as_fatal (_("Can't set register masks")); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Swap out the symbols and debugging information for BFD.  */ | 
|  |  | 
|  | void | 
|  | ecoff_frob_file (void) | 
|  | { | 
|  | const struct ecoff_debug_swap * const debug_swap | 
|  | = &ecoff_backend (stdoutput)->debug_swap; | 
|  | bfd_vma addr ATTRIBUTE_UNUSED; | 
|  | HDRR *hdr; | 
|  | char *buf; | 
|  | char *set; | 
|  |  | 
|  | /* Build the ECOFF debugging information.  */ | 
|  | gas_assert (ecoff_data (stdoutput) != 0); | 
|  | hdr = &ecoff_data (stdoutput)->debug_info.symbolic_header; | 
|  | ecoff_build_debug (hdr, &buf, debug_swap); | 
|  |  | 
|  | /* Finish up the ecoff_tdata structure.  */ | 
|  | set = buf; | 
|  | #define SET(ptr, count, type, size) \ | 
|  | if (hdr->count == 0) \ | 
|  | ecoff_data (stdoutput)->debug_info.ptr = NULL; \ | 
|  | else \ | 
|  | { \ | 
|  | ecoff_data (stdoutput)->debug_info.ptr = (type) set; \ | 
|  | set += hdr->count * size; \ | 
|  | } | 
|  |  | 
|  | SET (line, cbLine, unsigned char *, sizeof (unsigned char)); | 
|  | SET (external_dnr, idnMax, void *, debug_swap->external_dnr_size); | 
|  | SET (external_pdr, ipdMax, void *, debug_swap->external_pdr_size); | 
|  | SET (external_sym, isymMax, void *, debug_swap->external_sym_size); | 
|  | SET (external_opt, ioptMax, void *, debug_swap->external_opt_size); | 
|  | SET (external_aux, iauxMax, union aux_ext *, sizeof (union aux_ext)); | 
|  | SET (ss, issMax, char *, sizeof (char)); | 
|  | SET (ssext, issExtMax, char *, sizeof (char)); | 
|  | SET (external_rfd, crfd, void *, debug_swap->external_rfd_size); | 
|  | SET (external_fdr, ifdMax, void *, debug_swap->external_fdr_size); | 
|  | SET (external_ext, iextMax, void *, debug_swap->external_ext_size); | 
|  | #undef SET | 
|  | } | 
|  |  | 
|  | /* This is called by the ECOFF code to set the external information | 
|  | for a symbol.  We just pass it on to BFD, which expects the swapped | 
|  | information to be stored in the native field of the symbol.  */ | 
|  |  | 
|  | void | 
|  | obj_ecoff_set_ext (symbolS *sym, EXTR *ext) | 
|  | { | 
|  | const struct ecoff_debug_swap * const debug_swap | 
|  | = &ecoff_backend (stdoutput)->debug_swap; | 
|  | ecoff_symbol_type *esym; | 
|  |  | 
|  | know (bfd_asymbol_flavour (symbol_get_bfdsym (sym)) | 
|  | == bfd_target_ecoff_flavour); | 
|  | esym = ecoffsymbol (symbol_get_bfdsym (sym)); | 
|  | esym->local = false; | 
|  | esym->native = xmalloc (debug_swap->external_ext_size); | 
|  | (*debug_swap->swap_ext_out) (stdoutput, ext, esym->native); | 
|  | } | 
|  |  | 
|  | static int | 
|  | ecoff_sec_sym_ok_for_reloc (asection *sec ATTRIBUTE_UNUSED) | 
|  | { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | obj_ecoff_frob_symbol (symbolS *sym, int *puntp ATTRIBUTE_UNUSED) | 
|  | { | 
|  | ecoff_frob_symbol (sym); | 
|  | } | 
|  |  | 
|  | static void | 
|  | ecoff_pop_insert (void) | 
|  | { | 
|  | pop_insert (obj_pseudo_table); | 
|  | } | 
|  |  | 
|  | static int | 
|  | ecoff_separate_stab_sections (void) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* These are the pseudo-ops we support in this file.  Only those | 
|  | relating to debugging information are supported here. | 
|  |  | 
|  | The following pseudo-ops from the Kane and Heinrich MIPS book | 
|  | should be defined here, but are currently unsupported: .bgnb, | 
|  | .endb, .verstamp, .vreg. | 
|  |  | 
|  | The following pseudo-ops from the Kane and Heinrich MIPS book are | 
|  | MIPS CPU specific, and should be defined by tc-mips.c: .alias, | 
|  | .extern, .galive, .gjaldef, .gjrlive, .livereg, .noalias, .option, | 
|  | .rdata, .sdata, .set. | 
|  |  | 
|  | The following pseudo-ops from the Kane and Heinrich MIPS book are | 
|  | not MIPS CPU specific, but are also not ECOFF specific.  I have | 
|  | only listed the ones which are not already in read.c.  It's not | 
|  | completely clear where these should be defined, but tc-mips.c is | 
|  | probably the most reasonable place: .asciiz, .asm0, .endr, .err, | 
|  | .half, .lab, .repeat, .struct, .weakext.  */ | 
|  |  | 
|  | const pseudo_typeS obj_pseudo_table[] = | 
|  | { | 
|  | /* COFF style debugging information. .ln is not used; .loc is used | 
|  | instead.  */ | 
|  | { "def",	ecoff_directive_def,	0 }, | 
|  | { "dim",	ecoff_directive_dim,	0 }, | 
|  | { "endef",	ecoff_directive_endef,	0 }, | 
|  | { "file",	ecoff_directive_file,	0 }, | 
|  | { "scl",	ecoff_directive_scl,	0 }, | 
|  | { "size",	ecoff_directive_size,	0 }, | 
|  | { "esize",	ecoff_directive_size,	0 }, | 
|  | { "tag",	ecoff_directive_tag,	0 }, | 
|  | { "type",	ecoff_directive_type,	0 }, | 
|  | { "etype",	ecoff_directive_type,	0 }, | 
|  | { "val",	ecoff_directive_val,	0 }, | 
|  |  | 
|  | /* ECOFF specific debugging information.  */ | 
|  | { "aent",	ecoff_directive_ent,	1 }, | 
|  | { "begin",	ecoff_directive_begin,	0 }, | 
|  | { "bend",	ecoff_directive_bend,	0 }, | 
|  | { "end",	ecoff_directive_end,	0 }, | 
|  | { "ent",	ecoff_directive_ent,	0 }, | 
|  | { "fmask",	ecoff_directive_fmask,	0 }, | 
|  | { "frame",	ecoff_directive_frame,	0 }, | 
|  | { "loc",	ecoff_directive_loc,	0 }, | 
|  | { "mask",	ecoff_directive_mask,	0 }, | 
|  |  | 
|  | /* Other ECOFF directives.  */ | 
|  | { "extern",	ecoff_directive_extern,	0 }, | 
|  |  | 
|  | #ifndef TC_MIPS | 
|  | /* For TC_MIPS, tc-mips.c adds this.  */ | 
|  | { "weakext",	ecoff_directive_weakext, 0 }, | 
|  | #endif | 
|  |  | 
|  | /* These are used on Irix.  I don't know how to implement them.  */ | 
|  | { "bgnb",	s_ignore,		0 }, | 
|  | { "endb",	s_ignore,		0 }, | 
|  | { "verstamp",	s_ignore,		0 }, | 
|  |  | 
|  | /* Sentinel.  */ | 
|  | { NULL,	s_ignore,		0 } | 
|  | }; | 
|  |  | 
|  | const struct format_ops ecoff_format_ops = | 
|  | { | 
|  | bfd_target_ecoff_flavour, | 
|  | 0,	/* dfl_leading_underscore.  */ | 
|  |  | 
|  | /* FIXME: A comment why emit_section_symbols is different here (1) from | 
|  | the single-format definition (0) would be in order.  */ | 
|  | 1,	/* emit_section_symbols.  */ | 
|  | 0,	/* begin.  */ | 
|  | ecoff_new_file, | 
|  | obj_ecoff_frob_symbol, | 
|  | ecoff_frob_file, | 
|  | 0,	/* frob_file_before_adjust.  */ | 
|  | ecoff_frob_file_before_fix, | 
|  | 0,	/* frob_file_after_relocs.  */ | 
|  | 0,	/* s_get_size.  */ | 
|  | 0,	/* s_set_size.  */ | 
|  | 0,	/* s_get_align.  */ | 
|  | 0,	/* s_set_align.  */ | 
|  | 0,	/* s_get_other.  */ | 
|  | 0,	/* s_set_other.  */ | 
|  | 0,	/* s_get_desc.  */ | 
|  | 0,	/* s_set_desc.  */ | 
|  | 0,	/* s_get_type.  */ | 
|  | 0,	/* s_set_type.  */ | 
|  | 0,	/* copy_symbol_attributes.  */ | 
|  | ecoff_generate_asm_lineno, | 
|  | ecoff_stab, | 
|  | ecoff_separate_stab_sections, | 
|  | 0,	/* init_stab_section.  */ | 
|  | ecoff_sec_sym_ok_for_reloc, | 
|  | ecoff_pop_insert, | 
|  | ecoff_set_ext, | 
|  | ecoff_read_begin_hook, | 
|  | ecoff_symbol_new_hook, | 
|  | ecoff_symbol_clone_hook, | 
|  | 0	/* adjust_symtab.  */ | 
|  | }; |