From 63b2e4abee666f27c108ced4a9259f84e99a9bb3 Mon Sep 17 00:00:00 2001 From: mfortune Date: Thu, 8 May 2014 16:03:35 +0100 Subject: [PATCH 1/2] Implement O32 FPXX ABI include/ * elf/mips.h (PT_MIPS_ABIFLAGS, SHT_MIPS_ABIFLAGS): Define. (Val_GNU_MIPS_ABI_FP_OLD_64): Rename from Val_GNU_MIPS_ABI_FP_64. (Val_GNU_MIPS_ABI_FP_64): Redefine. (Val_GNU_MIPS_ABI_FP_XX): Define. (Elf_External_ABIFlags_v0, Elf_Internal_ABIFlags_v0): New structures. (AFL_REG_NONE, AFL_REG_32, AFL_REG_64, AFL_REG_128): Define. (AFL_ASE_DSP, AFL_ASE_DSPR2, AFL_ASE_EVA, AFL_ASE_MCU): Likewise. (AFL_ASE_MDMX, AFL_ASE_MIPS3D, AFL_ASE_MT, AFL_ASE_SMARTMIPS): Likewise. (AFL_ASE_VIRT, AFL_ASE_MSA, AFL_ASE_MIPS16): Likewise. (AFL_ASE_MICROMIPS, AFL_ASE_XPA): Likewise. (AFL_EXT_XLR, AFL_EXT_OCTEON2, AFL_EXT_OCTEONP): Likewise. (AFL_EXT_LOONGSON_3A, AFL_EXT_OCTEON, AFL_EXT_5900): Likewise. (AFL_EXT_4650, AFL_EXT_4010, AFL_EXT_4100, AFL_EXT_3900): Likewise. (AFL_EXT_10000, AFL_EXT_SB1, AFL_EXT_4111, AFL_EXT_4120): Likewise. (AFL_EXT_5400, AFL_EXT_5500, AFL_EXT_LOONGSON_2E): Likewise. (AFL_EXT_LOONGSON_2F): Likewise. bfd/ * elfxx-mips.c (ABI_O32_P, MIPS_ELF_ABIFLAGS_SECTION_NAME_P): New macro. (_bfd_mips_elf_additional_program_headers): Account for new PT_MIPS_ABIFLAGS program header. (_bfd_mips_elf_modify_segment_map): Create PT_MIPS_ABIFLAGS segment and associate with .MIPS.abiflags. (mips_elf_merge_obj_attributes, _bfd_mips_fp_abi_string): Error checking for Val_GNU_MIPS_ABI_FP_OLD_64 and Val_GNU_MIPS_ABI_FP_XX. Check EF_MIPS_FP64 flag consistency. (_bfd_mips_elf_merge_private_bfd_data): Restructure and add abiflags checks. (_bfd_mips_elf_print_private_bfd_data): Display abiflags data. (mips_elf_obj_tdata): Add abiflags and abiflags_valid fields. (bfd_mips_elf_swap_abiflags_v0_in): New function. ( bfd_mips_elf_swap_abiflags_v0_out): Likewise. (_bfd_mips_elf_section_from_shdr): Handle SHT_MIPS_ABIFLAGS. ( _bfd_mips_elf_fake_sections): Likewise. (_bfd_mips_elf_always_size_sections): Handle .MIPS.abiflags. (update_mips_abiflags_isa, infer_mips_abiflags): New static function. (bfd_mips_isa_ext, print_mips_ases, print_mips_isa_ext): Likewise. ( print_mips_fp_abi_value, get_mips_reg_size): Likewise. (mips_32bit_flags_p): Moved higher. (_bfd_mips_elf_final_link): Handle .MIPS.abiflags binutils/ * readelf.c (get_mips_segment_type): Display name for PT_MIPS_ABIFLAGS. (get_mips_section_type_name): Display name for SHT_MIPS_ABIFLAGS. (display_mips_gnu_attribute): Abstracted fp abi printing to... (print_mips_fp_abi_value): New static function. Handle Val_GNU_MIPS_ABI_FP_OLD_64 and Val_GNU_MIPS_ABI_FP_XX. (print_mips_ases, print_mips_isa_ext, get_mips_reg_size): New static functions. (process_mips_specific): Display abiflags data. elfcpp/ * elfcpp.h (PT_MIPS_ABIFLAGS): New program header type. gas/ * config/tc-mips.c (file_mips_opts_checked, mips_flags_frag): New static global. (s_module): New static function. (enum options, md_longopts, md_parse_option): Add -mfpxx option. (mips_pseudo_table): Add .module handler. (mips_set_ase): Add opts argument and use instead of mips_opts. (md_begin): Create .MIPS.abiflags section. (md_mips_end): Add consistency checks for command line, .module and .gnu_attribute. Update .gnu_attribute based on command line and .module as applicable. (md_assemble): Use file_mips_check_options. (match_float_constant): Rewrite check regarding FP register width. Add support for generating constants when MXHC1 is present. Handle fp==0 to comply with FPXX ABI. (macro): Update M_LI_DD similarly to match_float_constant. Generate MTHC1 when available. Check that correct code can be generated for FPXX and FP64 ABIs. (mips_set_architecture): Delete function. Moved to... (mips_after_parse_args): Here. All logic now applies to file_mips_opts first and then copies the final state to mips_opts. Move error checking to... (mips_check_options): Here. New static function. Common option checking for command line, .module and .set. Use general terms in error messages instead of refering to command line options. (file_mips_check_options): New static function. A wrapper for mips_check_options with file_mips_opts. Updates BFD arch based on final options. (s_mipsset): Split into s_mipsset and parse_code_option. Settings supported by both .set and .module are moved to parse_code_option. Warnings and errors are kept in s_mipsset because when parse_code_option is used with s_module the warnings are deferred until code is generated. Any setting supporting 'default' value is kept in s_mipsset as it is not applicable to s_module. Inferred settings are also kept in s_mipsset as s_module does not infer any settings. Use mips_check_options. (parse_code_option): New static function derived from s_mipsset. (s_module): New static function that implements .module. Allows file level settings to be changed until code is generated. (s_cpload, s_cpsetup, s_cplocal): Use file_mips_check_options. (s_cprestore, s_cpreturn, s_cpadd): Likewise. (mips_elf_final_processing): Use fpabi == Val_GNU_MIPS_ABI_FP_OLD_64 to determine when to add the EF_MIPS_FP64 flag. Populate the .MIPS.abiflags section. (md_obj_end): Use file_mips_check_options. * doc/c-mips.texi: Document -mfpxx, gnu_attribute values and FP ABIs. ld/ * emulparams/elf32bmip.sh: Add .MIPS.abiflags_valid. * emulparams/elf32bmipn32-defs.sh: Add .MIPS.abiflags_valid. --- bfd/elfxx-mips.c | 661 ++++++++++++++++++++++++++++++--- binutils/readelf.c | 222 ++++++++++-- elfcpp/elfcpp.h | 4 +- gas/config/tc-mips.c | 729 ++++++++++++++++++++++++------------ gas/doc/as.texinfo | 14 + gas/doc/c-mips.texi | 124 ++++++ include/elf/mips.h | 117 ++++++- ld/emulparams/elf32bmip.sh | 3 +- ld/emulparams/elf32bmipn32-defs.sh | 1 + 9 files changed, 1569 insertions(+), 306 deletions(-) diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index 49da874..06d9335 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -547,6 +547,10 @@ struct mips_elf_obj_tdata /* Input BFD providing Tag_GNU_MIPS_ABI_MSA attribute for output. */ bfd *abi_msa_bfd; + /* The abiflags for this object. */ + Elf_Internal_ABIFlags_v0 abiflags; + bfd_boolean abiflags_valid; + /* The GOT requirements of input bfds. */ struct mips_got_info *got; @@ -776,6 +780,10 @@ static bfd *reldyn_sorting_bfd; #define PIC_OBJECT_P(abfd) \ ((elf_elfheader (abfd)->e_flags & EF_MIPS_PIC) != 0) +/* Nonzero if ABFD is using the O32 ABI. */ +#define ABI_O32_P(abfd) \ + ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O32) + /* Nonzero if ABFD is using the N32 ABI. */ #define ABI_N32_P(abfd) \ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0) @@ -808,6 +816,10 @@ static bfd *reldyn_sorting_bfd; #define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \ (strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0) +/* True if NAME is the recognized name of any SHT_MIPS_ABIFLAGS section. */ +#define MIPS_ELF_ABIFLAGS_SECTION_NAME_P(NAME) \ + (strcmp (NAME, ".MIPS.abiflags") == 0) + /* Whether the section is readonly. */ #define MIPS_ELF_READONLY_SECTION(sec) \ ((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) \ @@ -2664,6 +2676,46 @@ bfd_mips_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in, H_PUT_16 (abfd, in->section, ex->section); H_PUT_32 (abfd, in->info, ex->info); } + +/* Swap in an abiflags structure. */ + +void +bfd_mips_elf_swap_abiflags_v0_in (bfd *abfd, + const Elf_External_ABIFlags_v0 *ex, + Elf_Internal_ABIFlags_v0 *in) +{ + in->version = H_GET_16 (abfd, ex->version); + in->isa_level = H_GET_8 (abfd, ex->isa_level); + in->isa_rev = H_GET_8 (abfd, ex->isa_rev); + in->gpr_size = H_GET_8 (abfd, ex->gpr_size); + in->cpr1_size = H_GET_8 (abfd, ex->cpr1_size); + in->cpr2_size = H_GET_8 (abfd, ex->cpr2_size); + in->fp_abi = H_GET_8 (abfd, ex->fp_abi); + in->isa_ext = H_GET_32 (abfd, ex->isa_ext); + in->ases = H_GET_32 (abfd, ex->ases); + in->flags1 = H_GET_32 (abfd, ex->flags1); + in->flags2 = H_GET_32 (abfd, ex->flags2); +} + +/* Swap out an abiflags structure. */ + +void +bfd_mips_elf_swap_abiflags_v0_out (bfd *abfd, + const Elf_Internal_ABIFlags_v0 *in, + Elf_External_ABIFlags_v0 *ex) +{ + H_PUT_16 (abfd, in->version, ex->version); + H_PUT_8 (abfd, in->isa_level, ex->isa_level); + H_PUT_8 (abfd, in->isa_rev, ex->isa_rev); + H_PUT_8 (abfd, in->gpr_size, ex->gpr_size); + H_PUT_8 (abfd, in->cpr1_size, ex->cpr1_size); + H_PUT_8 (abfd, in->cpr2_size, ex->cpr2_size); + H_PUT_8 (abfd, in->fp_abi, ex->fp_abi); + H_PUT_32 (abfd, in->isa_ext, ex->isa_ext); + H_PUT_32 (abfd, in->ases, ex->ases); + H_PUT_32 (abfd, in->flags1, ex->flags1); + H_PUT_32 (abfd, in->flags2, ex->flags2); +} /* This function is called via qsort() to sort the dynamic relocation entries by increasing r_symndx value. */ @@ -6910,6 +6962,11 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd, if (!MIPS_ELF_OPTIONS_SECTION_NAME_P (name)) return FALSE; break; + case SHT_MIPS_ABIFLAGS: + if (!MIPS_ELF_ABIFLAGS_SECTION_NAME_P (name)) + return FALSE; + flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE); + break; case SHT_MIPS_DWARF: if (! CONST_STRNEQ (name, ".debug_") && ! CONST_STRNEQ (name, ".zdebug_")) @@ -6940,6 +6997,20 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd, return FALSE; } + if (hdr->sh_type == SHT_MIPS_ABIFLAGS) + { + Elf_External_ABIFlags_v0 ext; + + if (! bfd_get_section_contents (abfd, hdr->bfd_section, + &ext, 0, sizeof ext)) + return FALSE; + bfd_mips_elf_swap_abiflags_v0_in (abfd, &ext, + &mips_elf_tdata (abfd)->abiflags); + if (mips_elf_tdata (abfd)->abiflags.version != 0) + return FALSE; + mips_elf_tdata (abfd)->abiflags_valid = TRUE; + } + /* FIXME: We should record sh_info for a .gptab section. */ /* For a .reginfo section, set the gp value in the tdata information @@ -7106,6 +7177,11 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec) hdr->sh_entsize = 1; hdr->sh_flags |= SHF_MIPS_NOSTRIP; } + else if (CONST_STRNEQ (name, ".MIPS.abiflags")) + { + hdr->sh_type = SHT_MIPS_ABIFLAGS; + hdr->sh_entsize = sizeof (Elf_External_ABIFlags_v0); + } else if (CONST_STRNEQ (name, ".debug_") || CONST_STRNEQ (name, ".zdebug_")) { @@ -9025,7 +9101,7 @@ bfd_boolean _bfd_mips_elf_always_size_sections (bfd *output_bfd, struct bfd_link_info *info) { - asection *ri; + asection *sect; struct mips_elf_link_hash_table *htab; struct mips_htab_traverse_info hti; @@ -9033,9 +9109,14 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, BFD_ASSERT (htab != NULL); /* The .reginfo section has a fixed size. */ - ri = bfd_get_section_by_name (output_bfd, ".reginfo"); - if (ri != NULL) - bfd_set_section_size (output_bfd, ri, sizeof (Elf32_External_RegInfo)); + sect = bfd_get_section_by_name (output_bfd, ".reginfo"); + if (sect != NULL) + bfd_set_section_size (output_bfd, sect, sizeof (Elf32_External_RegInfo)); + + /* The .MIPS.abiflags section has a fixed size. */ + sect = bfd_get_section_by_name (output_bfd, ".MIPS.abiflags"); + if (sect != NULL) + bfd_set_section_size (output_bfd, sect, sizeof (Elf_External_ABIFlags_v0)); hti.info = info; hti.output_bfd = output_bfd; @@ -11782,6 +11863,10 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd, if (s && (s->flags & SEC_LOAD)) ++ret; + /* See if we need a PT_MIPS_ABIFLAGS segment. */ + if (bfd_get_section_by_name (abfd, ".MIPS.abiflags")) + ++ret; + /* See if we need a PT_MIPS_OPTIONS segment. */ if (IRIX_COMPAT (abfd) == ict_irix6 && bfd_get_section_by_name (abfd, @@ -11844,6 +11929,37 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, } } + /* If there is a .MIPS.abiflags section, we need a PT_MIPS_ABIFLAGS + segment. */ + s = bfd_get_section_by_name (abfd, ".MIPS.abiflags"); + if (s != NULL && (s->flags & SEC_LOAD) != 0) + { + for (m = elf_seg_map (abfd); m != NULL; m = m->next) + if (m->p_type == PT_MIPS_ABIFLAGS) + break; + if (m == NULL) + { + amt = sizeof *m; + m = bfd_zalloc (abfd, amt); + if (m == NULL) + return FALSE; + + m->p_type = PT_MIPS_ABIFLAGS; + m->count = 1; + m->sections[0] = s; + + /* We want to put it after the PHDR and INTERP segments. */ + pm = &elf_seg_map (abfd); + while (*pm != NULL + && ((*pm)->p_type == PT_PHDR + || (*pm)->p_type == PT_INTERP)) + pm = &(*pm)->next; + + m->next = *pm; + *pm = m; + } + } + /* For IRIX 6, we don't have .mdebug sections, nor does anything but .dynamic end up in PT_DYNAMIC. However, we do have to insert a PT_MIPS_OPTIONS segment immediately following the program header @@ -13577,6 +13693,154 @@ _bfd_mips_elf_insn32 (struct bfd_link_info *info, bfd_boolean on) mips_elf_hash_table (info)->insn32 = on; } +/* Return the .MIPS.abiflags value representing each ISA Extension. */ + +unsigned int +bfd_mips_isa_ext (bfd *abfd) +{ + switch (bfd_get_mach (abfd)) + { + case bfd_mach_mips3900: + return AFL_EXT_3900; + case bfd_mach_mips4010: + return AFL_EXT_4010; + case bfd_mach_mips4100: + return AFL_EXT_4100; + case bfd_mach_mips4111: + return AFL_EXT_4111; + case bfd_mach_mips4120: + return AFL_EXT_4120; + case bfd_mach_mips4650: + return AFL_EXT_4650; + case bfd_mach_mips5400: + return AFL_EXT_5400; + case bfd_mach_mips5500: + return AFL_EXT_5500; + case bfd_mach_mips5900: + return AFL_EXT_5900; + case bfd_mach_mips10000: + return AFL_EXT_10000; + case bfd_mach_mips_loongson_2e: + return AFL_EXT_LOONGSON_2E; + case bfd_mach_mips_loongson_2f: + return AFL_EXT_LOONGSON_2F; + case bfd_mach_mips_loongson_3a: + return AFL_EXT_LOONGSON_3A; + case bfd_mach_mips_sb1: + return AFL_EXT_SB1; + case bfd_mach_mips_octeon: + return AFL_EXT_OCTEON; + case bfd_mach_mips_octeonp: + return AFL_EXT_OCTEONP; + case bfd_mach_mips_octeon2: + return AFL_EXT_OCTEON2; + case bfd_mach_mips_xlr: + return AFL_EXT_XLR; + } + return 0; +} + +/* Update the isa_level, isa_rev, isa_ext fields of abiflags. */ + +static void +update_mips_abiflags_isa (bfd *abfd, Elf_Internal_ABIFlags_v0 *abiflags) +{ + abiflags->isa_rev = 0; + switch (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) + { + case E_MIPS_ARCH_1: + abiflags->isa_level = 1; + break; + case E_MIPS_ARCH_2: + abiflags->isa_level = 2; + break; + case E_MIPS_ARCH_3: + abiflags->isa_level = 3; + break; + case E_MIPS_ARCH_4: + abiflags->isa_level = 4; + break; + case E_MIPS_ARCH_5: + abiflags->isa_level = 5; + break; + case E_MIPS_ARCH_32: + abiflags->isa_level = 32; + abiflags->isa_rev = 1; + break; + case E_MIPS_ARCH_32R2: + abiflags->isa_level = 32; + abiflags->isa_rev = 2; + break; + case E_MIPS_ARCH_64: + abiflags->isa_level = 64; + abiflags->isa_rev = 1; + break; + case E_MIPS_ARCH_64R2: + abiflags->isa_level = 64; + abiflags->isa_rev = 2; + break; + default: + (*_bfd_error_handler) + (_("%B: Unknown architecture %s"), + abfd, bfd_printable_name (abfd)); + } + + abiflags->isa_ext = bfd_mips_isa_ext (abfd); +} + +/* Return true if the given ELF header flags describe a 32-bit binary. */ + +static bfd_boolean +mips_32bit_flags_p (flagword flags) +{ + return ((flags & EF_MIPS_32BITMODE) != 0 + || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32 + || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32 + || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1 + || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2 + || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32 + || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2); +} + +/* Infer the content of the ABI flags based on the elf header. */ + +static void +infer_mips_abiflags (bfd *abfd, Elf_Internal_ABIFlags_v0* abiflags) +{ + obj_attribute *in_attr; + + memset (abiflags, 0, sizeof (Elf_Internal_ABIFlags_v0)); + update_mips_abiflags_isa (abfd, abiflags); + + if (mips_32bit_flags_p (elf_elfheader (abfd)->e_flags)) + abiflags->gpr_size = AFL_REG_32; + else + abiflags->gpr_size = AFL_REG_64; + + abiflags->cpr1_size = AFL_REG_NONE; + + in_attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU]; + abiflags->fp_abi = in_attr[Tag_GNU_MIPS_ABI_FP].i; + + if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_SINGLE + || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_XX + || (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE + && abiflags->gpr_size == AFL_REG_32)) + abiflags->cpr1_size = AFL_REG_32; + else if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE + || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_64) + abiflags->cpr1_size = AFL_REG_64; + + abiflags->cpr2_size = AFL_REG_NONE; + + if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX) + abiflags->ases |= AFL_ASE_MDMX; + if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16) + abiflags->ases |= AFL_ASE_MIPS16; + if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) + abiflags->ases |= AFL_ASE_MICROMIPS; +} + /* We need to use a special link routine to handle the .reginfo and the .mdebug sections. We need to merge all instances of these sections together, not write them all out sequentially. */ @@ -13587,7 +13851,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) asection *o; struct bfd_link_order *p; asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec; - asection *rtproc_sec; + asection *rtproc_sec, *abiflags_sec; Elf32_RegInfo reginfo; struct ecoff_debug_info debug; struct mips_htab_traverse_info hti; @@ -13669,12 +13933,46 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) /* Go through the sections and collect the .reginfo and .mdebug information. */ + abiflags_sec = NULL; reginfo_sec = NULL; mdebug_sec = NULL; gptab_data_sec = NULL; gptab_bss_sec = NULL; for (o = abfd->sections; o != NULL; o = o->next) { + if (strcmp (o->name, ".MIPS.abiflags") == 0) + { + /* We have found the .MIPS.abiflags section in the output file. + Look through all the link_orders comprising it and remove them. + The data is merged in _bfd_mips_elf_merge_private_bfd_data. */ + for (p = o->map_head.link_order; p != NULL; p = p->next) + { + asection *input_section; + + if (p->type != bfd_indirect_link_order) + { + if (p->type == bfd_data_link_order) + continue; + abort (); + } + + input_section = p->u.indirect.section; + + /* Hack: reset the SEC_HAS_CONTENTS flag so that + elf_link_input_bfd ignores this section. */ + input_section->flags &= ~SEC_HAS_CONTENTS; + } + + /* Size has been set in _bfd_mips_elf_always_size_sections. */ + BFD_ASSERT(o->size == sizeof (Elf_External_ABIFlags_v0)); + + /* Skip this section later on (I don't think this currently + matters, but someday it might). */ + o->map_head.link_order = NULL; + + abiflags_sec = o; + } + if (strcmp (o->name, ".reginfo") == 0) { memset (®info, 0, sizeof reginfo); @@ -14159,6 +14457,24 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) /* Now write out the computed sections. */ + if (abiflags_sec != NULL) + { + Elf_External_ABIFlags_v0 ext; + Elf_Internal_ABIFlags_v0 *abiflags; + + abiflags = &mips_elf_tdata (abfd)->abiflags; + + /* Set up the abiflags if no valid input sections were found. */ + if (!mips_elf_tdata (abfd)->abiflags_valid) + { + infer_mips_abiflags (abfd, abiflags); + mips_elf_tdata (abfd)->abiflags_valid = TRUE; + } + bfd_mips_elf_swap_abiflags_v0_out (abfd, abiflags, &ext); + if (! bfd_set_section_contents (abfd, abiflags_sec, &ext, 0, sizeof ext)) + return FALSE; + } + if (reginfo_sec != NULL) { Elf32_External_RegInfo ext; @@ -14285,7 +14601,6 @@ static const struct mips_mach_extension mips_mach_extensions[] = { bfd_mach_mips3900, bfd_mach_mips3000 } }; - /* Return true if bfd machine EXTENSION is an extension of machine BASE. */ static bfd_boolean @@ -14316,21 +14631,6 @@ mips_mach_extends_p (unsigned long base, unsigned long extension) } -/* Return true if the given ELF header flags describe a 32-bit binary. */ - -static bfd_boolean -mips_32bit_flags_p (flagword flags) -{ - return ((flags & EF_MIPS_32BITMODE) != 0 - || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32 - || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32 - || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1 - || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2 - || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32 - || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2); -} - - /* Merge object attributes from IBFD into OBFD. Raise an error if there are conflicting attributes. */ static bfd_boolean @@ -14375,7 +14675,17 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd) out_attr[Tag_GNU_MIPS_ABI_FP].type = 1; if (out_fp == Val_GNU_MIPS_ABI_FP_ANY) out_attr[Tag_GNU_MIPS_ABI_FP].i = in_fp; - else if (in_fp != Val_GNU_MIPS_ABI_FP_ANY) + else if (out_fp == Val_GNU_MIPS_ABI_FP_XX + && (in_fp == Val_GNU_MIPS_ABI_FP_DOUBLE + || in_fp == Val_GNU_MIPS_ABI_FP_64)) + { + mips_elf_tdata (obfd)->abi_fp_bfd = ibfd; + out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i; + } + else if (in_fp != Val_GNU_MIPS_ABI_FP_ANY + && (in_fp != Val_GNU_MIPS_ABI_FP_XX + || (out_fp != Val_GNU_MIPS_ABI_FP_64 + && out_fp != Val_GNU_MIPS_ABI_FP_DOUBLE))) { const char *out_string, *in_string; @@ -14492,6 +14802,16 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) return FALSE; } + /* Set up the FP ABI attribute from the abiflags if it is not already + set. */ + if (mips_elf_tdata (ibfd)->abiflags_valid) + { + obj_attribute *in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU]; + if (in_attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY) + in_attr[Tag_GNU_MIPS_ABI_FP].i = + mips_elf_tdata (ibfd)->abiflags.fp_abi; + } + if (!mips_elf_merge_obj_attributes (ibfd, obfd)) return FALSE; @@ -14499,26 +14819,6 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER; old_flags = elf_elfheader (obfd)->e_flags; - if (! elf_flags_init (obfd)) - { - elf_flags_init (obfd) = TRUE; - elf_elfheader (obfd)->e_flags = new_flags; - elf_elfheader (obfd)->e_ident[EI_CLASS] - = elf_elfheader (ibfd)->e_ident[EI_CLASS]; - - if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) - && (bfd_get_arch_info (obfd)->the_default - || mips_mach_extends_p (bfd_get_mach (obfd), - bfd_get_mach (ibfd)))) - { - if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), - bfd_get_mach (ibfd))) - return FALSE; - } - - return TRUE; - } - /* Check flag compatibility. */ new_flags &= ~EF_MIPS_NOREORDER; @@ -14538,9 +14838,6 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) if ((ibfd->flags & DYNAMIC) != 0) new_flags |= EF_MIPS_PIC | EF_MIPS_CPIC; - if (new_flags == old_flags) - return TRUE; - /* Check to see if the input BFD actually contains any sections. If not, its flags may not have been initialised either, but it cannot actually cause any incompatibility. */ @@ -14565,6 +14862,87 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) if (null_input_bfd) return TRUE; + /* Populate abiflags using existing information. */ + if (!mips_elf_tdata (ibfd)->abiflags_valid) + { + infer_mips_abiflags (ibfd, &mips_elf_tdata (ibfd)->abiflags); + mips_elf_tdata (ibfd)->abiflags_valid = TRUE; + } + else + { + Elf_Internal_ABIFlags_v0 abiflags; + Elf_Internal_ABIFlags_v0 *iabiflags; + infer_mips_abiflags (ibfd, &abiflags); + iabiflags = &mips_elf_tdata (ibfd)->abiflags; + + if (iabiflags->isa_level != abiflags.isa_level + || iabiflags->isa_rev != abiflags.isa_rev) + (*_bfd_error_handler) + (_("%B: warning: Inconsistent ISA between e_flags and " + ".MIPS.abiflags"), ibfd); + if (abiflags.fp_abi != Val_GNU_MIPS_ABI_FP_ANY + && iabiflags->fp_abi != abiflags.fp_abi) + (*_bfd_error_handler) + (_("%B: warning: Inconsistent FP ABI between e_flags and " + ".MIPS.abiflags"), ibfd); + if ((iabiflags->ases & abiflags.ases) != abiflags.ases) + (*_bfd_error_handler) + (_("%B: warning: Inconsistent ASEs between e_flags and " + ".MIPS.abiflags"), ibfd); + } + + if (!mips_elf_tdata (obfd)->abiflags_valid) + { + /* Copy input abiflags if output abiflags are not already valid. */ + mips_elf_tdata (obfd)->abiflags = mips_elf_tdata (ibfd)->abiflags; + mips_elf_tdata (obfd)->abiflags_valid = TRUE; + } + + if (! elf_flags_init (obfd)) + { + elf_flags_init (obfd) = TRUE; + elf_elfheader (obfd)->e_flags = new_flags; + elf_elfheader (obfd)->e_ident[EI_CLASS] + = elf_elfheader (ibfd)->e_ident[EI_CLASS]; + + if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) + && (bfd_get_arch_info (obfd)->the_default + || mips_mach_extends_p (bfd_get_mach (obfd), + bfd_get_mach (ibfd)))) + { + if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), + bfd_get_mach (ibfd))) + return FALSE; + + /* Update the ABI flags isa_level, isa_rev and isa_ext fields. */ + update_mips_abiflags_isa (obfd, &mips_elf_tdata (obfd)->abiflags); + } + + return TRUE; + } + + /* Update the output abiflags fp_abi using the computed fp_abi. */ + obj_attribute *out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU]; + mips_elf_tdata (obfd)->abiflags.fp_abi = out_attr[Tag_GNU_MIPS_ABI_FP].i; + +#define max(a,b) ((a) > (b) ? (a) : (b)) + /* Merge abiflags. */ + mips_elf_tdata (obfd)->abiflags.gpr_size + = max (mips_elf_tdata (obfd)->abiflags.gpr_size, + mips_elf_tdata (ibfd)->abiflags.gpr_size); + mips_elf_tdata (obfd)->abiflags.cpr1_size + = max (mips_elf_tdata (obfd)->abiflags.cpr1_size, + mips_elf_tdata (ibfd)->abiflags.cpr1_size); + mips_elf_tdata (obfd)->abiflags.cpr2_size + = max (mips_elf_tdata (obfd)->abiflags.cpr2_size, + mips_elf_tdata (ibfd)->abiflags.cpr2_size); +#undef max + mips_elf_tdata (obfd)->abiflags.ases + |= mips_elf_tdata (ibfd)->abiflags.ases; + + if (new_flags == old_flags) + return TRUE; + ok = TRUE; if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0) @@ -14605,6 +14983,9 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) elf_elfheader (obfd)->e_flags |= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE); + /* Update the ABI flags isa_level, isa_rev, isa_ext fields. */ + update_mips_abiflags_isa (obfd, &mips_elf_tdata (obfd)->abiflags); + /* Copy across the ABI flags if OBFD doesn't use them and if that was what caused us to treat IBFD as 32-bit. */ if ((old_flags & EF_MIPS_ABI) == 0 @@ -14690,6 +15071,20 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) old_flags &= ~EF_MIPS_NAN2008; } + /* Compare FP64 state. */ + if ((new_flags & EF_MIPS_FP64) != (old_flags & EF_MIPS_FP64)) + { + _bfd_error_handler (_("%B: linking %s module with previous %s modules"), + ibfd, + (new_flags & EF_MIPS_FP64 + ? "-mfp64" : "-mfp32"), + (old_flags & EF_MIPS_FP64 + ? "-mfp64" : "-mfp32")); + ok = FALSE; + new_flags &= ~EF_MIPS_FP64; + old_flags &= ~EF_MIPS_FP64; + } + /* Warn about any other mismatches */ if (new_flags != old_flags) { @@ -14840,14 +15235,162 @@ _bfd_mips_fp_abi_string (int fp) case Val_GNU_MIPS_ABI_FP_SOFT: return "-msoft-float"; - case Val_GNU_MIPS_ABI_FP_64: + case Val_GNU_MIPS_ABI_FP_OLD_64: return "-mips32r2 -mfp64"; + case Val_GNU_MIPS_ABI_FP_XX: + return "-mfpxx"; + + case Val_GNU_MIPS_ABI_FP_64: + return "-mfp64"; + default: return 0; } } +static void +print_mips_ases (FILE *file, unsigned int mask) +{ + if (mask & AFL_ASE_DSP) + fputs ("\n\tDSP ASE", file); + if (mask & AFL_ASE_DSPR2) + fputs ("\n\tDSP R2 ASE", file); + if (mask & AFL_ASE_EVA) + fputs ("\n\tEnhanced VA Scheme", file); + if (mask & AFL_ASE_MCU) + fputs ("\n\tMCU (MicroController) ASE", file); + if (mask & AFL_ASE_MDMX) + fputs ("\n\tMDMX ASE", file); + if (mask & AFL_ASE_MIPS3D) + fputs ("\n\tMIPS-3D ASE", file); + if (mask & AFL_ASE_MT) + fputs ("\n\tMT ASE", file); + if (mask & AFL_ASE_SMARTMIPS) + fputs ("\n\tSmartMIPS ASE", file); + if (mask & AFL_ASE_VIRT) + fputs ("\n\tVZ ASE", file); + if (mask & AFL_ASE_MSA) + fputs ("\n\tMSA ASE", file); + if (mask & AFL_ASE_MIPS16) + fputs ("\n\tMIPS16 ASE", file); + if (mask & AFL_ASE_MICROMIPS) + fputs ("\n\tMICROMIPS ASE", file); + if (mask & AFL_ASE_XPA) + fputs ("\n\tXPA ASE", file); + if (mask == 0) + fputs ("\n\tNone", file); +} + +static void +print_mips_isa_ext (FILE *file, unsigned int isa_ext) +{ + switch (isa_ext) + { + case 0: + fputs ("None", file); + break; + case AFL_EXT_XLR: + fputs ("RMI Xlr instruction", file); + break; + case AFL_EXT_OCTEON2: + fputs ("Cavium Networks Octeon2", file); + break; + case AFL_EXT_OCTEONP: + fputs ("Cavium Networks OcteonP", file); + break; + case AFL_EXT_LOONGSON_3A: + fputs ("Loongson 3A", file); + break; + case AFL_EXT_OCTEON: + fputs ("Cavium Networks Octeon", file); + break; + case AFL_EXT_5900: + fputs ("MIPS R5900", file); + break; + case AFL_EXT_4650: + fputs ("MIPS R4650", file); + break; + case AFL_EXT_4010: + fputs ("LSI R4010", file); + break; + case AFL_EXT_4100: + fputs ("NEC VR4100", file); + break; + case AFL_EXT_3900: + fputs ("Toshiba R3900", file); + break; + case AFL_EXT_10000: + fputs ("MIPS R10000", file); + break; + case AFL_EXT_SB1: + fputs ("Broadcom SB-1", file); + break; + case AFL_EXT_4111: + fputs ("NEC VR4111/VR4181", file); + break; + case AFL_EXT_4120: + fputs ("NEC VR4120", file); + break; + case AFL_EXT_5400: + fputs ("NEC VR5400", file); + break; + case AFL_EXT_5500: + fputs ("NEC VR5500", file); + break; + case AFL_EXT_LOONGSON_2E: + fputs ("ST Microelectronics Loongson 2E", file); + break; + case AFL_EXT_LOONGSON_2F: + fputs ("ST Microelectronics Loongson 2F", file); + break; + default: + fputs ("Unknown", file); + } +} + +static void +print_mips_fp_abi_value (FILE *file, int val) +{ + switch (val) + { + case Val_GNU_MIPS_ABI_FP_ANY: + fprintf (file, _("Hard or soft float\n")); + break; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + fprintf (file, _("Hard float (double precision)\n")); + break; + case Val_GNU_MIPS_ABI_FP_SINGLE: + fprintf (file, _("Hard float (single precision)\n")); + break; + case Val_GNU_MIPS_ABI_FP_SOFT: + fprintf (file, _("Soft float\n")); + break; + case Val_GNU_MIPS_ABI_FP_OLD_64: + fprintf (file, _("Hard float (MIPS32r2 64-bit FPU 12 callee-saved)\n")); + break; + case Val_GNU_MIPS_ABI_FP_XX: + fprintf (file, _("Hard float (32-bit CPU, Any FPU)\n")); + break; + case Val_GNU_MIPS_ABI_FP_64: + fprintf (file, _("Hard float (32-bit CPU, 64-bit FPU)\n")); + break; + default: + fprintf (file, "??? (%d)\n", val); + break; + } +} + +static int +get_mips_reg_size (int reg_size) +{ + return (reg_size == AFL_REG_NONE) ? 0 + : (reg_size == AFL_REG_32) ? 32 + : (reg_size == AFL_REG_64) ? 64 + : (reg_size == AFL_REG_128) ? 128 + : -1; +} + bfd_boolean _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr) { @@ -14912,7 +15455,7 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr) fprintf (file, " [nan2008]"); if (elf_elfheader (abfd)->e_flags & EF_MIPS_FP64) - fprintf (file, " [fp64]"); + fprintf (file, " [old fp64]"); if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE) fprintf (file, " [32bitmode]"); @@ -14936,6 +15479,30 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr) fputc ('\n', file); + if (mips_elf_tdata (abfd)->abiflags_valid) + { + Elf_Internal_ABIFlags_v0 *abiflags = &mips_elf_tdata (abfd)->abiflags; + fprintf (file, "\nMIPS ABI Flags Version: %d\n", abiflags->version); + fprintf (file, "\nISA: MIPS%d", abiflags->isa_level); + if (abiflags->isa_rev != 0) + fprintf (file, "r%d", abiflags->isa_rev); + fprintf (file, "\nGPR size: %d", + get_mips_reg_size (abiflags->gpr_size)); + fprintf (file, "\nCPR1 size: %d", + get_mips_reg_size (abiflags->cpr1_size)); + fprintf (file, "\nCPR2 size: %d", + get_mips_reg_size (abiflags->cpr2_size)); + fputs ("\nFP ABI: ", file); + print_mips_fp_abi_value (file, abiflags->fp_abi); + fputs ("ISA Extension: ", file); + print_mips_isa_ext (file, abiflags->isa_ext); + fputs ("\nASEs:", file); + print_mips_ases (file, abiflags->ases); + fprintf (file, "\nFLAGS 1: %8.8lx", abiflags->flags1); + fprintf (file, "\nFLAGS 2: %8.8lx", abiflags->flags2); + fputc ('\n', file); + } + return TRUE; } diff --git a/binutils/readelf.c b/binutils/readelf.c index 9cafd7c..8812cac 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -3165,6 +3165,8 @@ get_mips_segment_type (unsigned long type) return "RTPROC"; case PT_MIPS_OPTIONS: return "OPTIONS"; + case PT_MIPS_ABIFLAGS: + return "ABIFLAGS"; default: break; } @@ -3364,6 +3366,7 @@ get_mips_section_type_name (unsigned int sh_type) case SHT_MIPS_EH_REGION: return "MIPS_EH_REGION"; case SHT_MIPS_XLATE_OLD: return "MIPS_XLATE_OLD"; case SHT_MIPS_PDR_EXCEPTION: return "MIPS_PDR_EXCEPTION"; + case SHT_MIPS_ABIFLAGS: return "MIPS_ABIFLAGS"; default: break; } @@ -11993,6 +11996,38 @@ display_sparc_gnu_attribute (unsigned char * p, return display_tag_value (tag, p, end); } +static void +print_mips_fp_abi_value (int val) +{ + switch (val) + { + case Val_GNU_MIPS_ABI_FP_ANY: + printf (_("Hard or soft float\n")); + break; + case Val_GNU_MIPS_ABI_FP_DOUBLE: + printf (_("Hard float (double precision)\n")); + break; + case Val_GNU_MIPS_ABI_FP_SINGLE: + printf (_("Hard float (single precision)\n")); + break; + case Val_GNU_MIPS_ABI_FP_SOFT: + printf (_("Soft float\n")); + break; + case Val_GNU_MIPS_ABI_FP_OLD_64: + printf (_("Hard float (MIPS32r2 64-bit FPU 12 callee-saved)\n")); + break; + case Val_GNU_MIPS_ABI_FP_XX: + printf (_("Hard float (32-bit CPU, Any FPU)\n")); + break; + case Val_GNU_MIPS_ABI_FP_64: + printf (_("Hard float (32-bit CPU, 64-bit FPU)\n")); + break; + default: + printf ("??? (%d)\n", val); + break; + } +} + static unsigned char * display_mips_gnu_attribute (unsigned char * p, int tag, @@ -12007,27 +12042,8 @@ display_mips_gnu_attribute (unsigned char * p, p += len; printf (" Tag_GNU_MIPS_ABI_FP: "); - switch (val) - { - case Val_GNU_MIPS_ABI_FP_ANY: - printf (_("Hard or soft float\n")); - break; - case Val_GNU_MIPS_ABI_FP_DOUBLE: - printf (_("Hard float (double precision)\n")); - break; - case Val_GNU_MIPS_ABI_FP_SINGLE: - printf (_("Hard float (single precision)\n")); - break; - case Val_GNU_MIPS_ABI_FP_SOFT: - printf (_("Soft float\n")); - break; - case Val_GNU_MIPS_ABI_FP_64: - printf (_("Hard float (MIPS32r2 64-bit FPU)\n")); - break; - default: - printf ("??? (%d)\n", val); - break; - } + print_mips_fp_abi_value (val); + return p; } @@ -12630,10 +12646,121 @@ print_mips_pltgot_entry (unsigned char * data, bfd_vma pltgot, bfd_vma addr) return addr + (is_32bit_elf ? 4 : 8); } +static void +print_mips_ases (unsigned int mask) +{ + if (mask & AFL_ASE_DSP) + fputs ("\n\tDSP ASE", stdout); + if (mask & AFL_ASE_DSPR2) + fputs ("\n\tDSP R2 ASE", stdout); + if (mask & AFL_ASE_EVA) + fputs ("\n\tEnhanced VA Scheme", stdout); + if (mask & AFL_ASE_MCU) + fputs ("\n\tMCU (MicroController) ASE", stdout); + if (mask & AFL_ASE_MDMX) + fputs ("\n\tMDMX ASE", stdout); + if (mask & AFL_ASE_MIPS3D) + fputs ("\n\tMIPS-3D ASE", stdout); + if (mask & AFL_ASE_MT) + fputs ("\n\tMT ASE", stdout); + if (mask & AFL_ASE_SMARTMIPS) + fputs ("\n\tSmartMIPS ASE", stdout); + if (mask & AFL_ASE_VIRT) + fputs ("\n\tVZ ASE", stdout); + if (mask & AFL_ASE_MSA) + fputs ("\n\tMSA ASE", stdout); + if (mask & AFL_ASE_MIPS16) + fputs ("\n\tMIPS16 ASE", stdout); + if (mask & AFL_ASE_MICROMIPS) + fputs ("\n\tMICROMIPS ASE", stdout); + if (mask & AFL_ASE_XPA) + fputs ("\n\tXPA ASE", stdout); + if (mask == 0) + fputs ("\n\tNone", stdout); +} + +static void +print_mips_isa_ext (unsigned int isa_ext) +{ + switch (isa_ext) + { + case 0: + fputs ("None", stdout); + break; + case AFL_EXT_XLR: + fputs ("RMI Xlr instruction", stdout); + break; + case AFL_EXT_OCTEON2: + fputs ("Cavium Networks Octeon2", stdout); + break; + case AFL_EXT_OCTEONP: + fputs ("Cavium Networks OcteonP", stdout); + break; + case AFL_EXT_LOONGSON_3A: + fputs ("Loongson 3A", stdout); + break; + case AFL_EXT_OCTEON: + fputs ("Cavium Networks Octeon", stdout); + break; + case AFL_EXT_5900: + fputs ("MIPS R5900", stdout); + break; + case AFL_EXT_4650: + fputs ("MIPS R4650", stdout); + break; + case AFL_EXT_4010: + fputs ("LSI R4010", stdout); + break; + case AFL_EXT_4100: + fputs ("NEC VR4100", stdout); + break; + case AFL_EXT_3900: + fputs ("Toshiba R3900", stdout); + break; + case AFL_EXT_10000: + fputs ("MIPS R10000", stdout); + break; + case AFL_EXT_SB1: + fputs ("Broadcom SB-1", stdout); + break; + case AFL_EXT_4111: + fputs ("NEC VR4111/VR4181", stdout); + break; + case AFL_EXT_4120: + fputs ("NEC VR4120", stdout); + break; + case AFL_EXT_5400: + fputs ("NEC VR5400", stdout); + break; + case AFL_EXT_5500: + fputs ("NEC VR5500", stdout); + break; + case AFL_EXT_LOONGSON_2E: + fputs ("ST Microelectronics Loongson 2E", stdout); + break; + case AFL_EXT_LOONGSON_2F: + fputs ("ST Microelectronics Loongson 2F", stdout); + break; + default: + fputs ("Unknown", stdout); + } +} + +static int +get_mips_reg_size (int reg_size) +{ + return (reg_size == AFL_REG_NONE) ? 0 + : (reg_size == AFL_REG_32) ? 32 + : (reg_size == AFL_REG_64) ? 64 + : (reg_size == AFL_REG_128) ? 128 + : -1; +} + static int process_mips_specific (FILE * file) { Elf_Internal_Dyn * entry; + Elf_Internal_Shdr *sect = NULL; size_t liblist_offset = 0; size_t liblistno = 0; size_t conflictsno = 0; @@ -12651,6 +12778,57 @@ process_mips_specific (FILE * file) process_attributes (file, NULL, SHT_GNU_ATTRIBUTES, NULL, display_mips_gnu_attribute); + sect = find_section (".MIPS.abiflags"); + + if (sect != NULL) + { + Elf_External_ABIFlags_v0 *abiflags_ext; + Elf_Internal_ABIFlags_v0 abiflags_in; + + if (sizeof (Elf_External_ABIFlags_v0) != sect->sh_size) + fputs ("\nCorrupt ABI Flags section.\n", stdout); + else + { + abiflags_ext = get_data (NULL, file, sect->sh_offset, 1, + sect->sh_size, _("MIPS ABI Flags section")); + if (abiflags_ext) + { + abiflags_in.version = BYTE_GET (abiflags_ext->version); + abiflags_in.isa_level = BYTE_GET (abiflags_ext->isa_level); + abiflags_in.isa_rev = BYTE_GET (abiflags_ext->isa_rev); + abiflags_in.gpr_size = BYTE_GET (abiflags_ext->gpr_size); + abiflags_in.cpr1_size = BYTE_GET (abiflags_ext->cpr1_size); + abiflags_in.cpr2_size = BYTE_GET (abiflags_ext->cpr2_size); + abiflags_in.fp_abi = BYTE_GET (abiflags_ext->fp_abi); + abiflags_in.isa_ext = BYTE_GET (abiflags_ext->isa_ext); + abiflags_in.ases = BYTE_GET (abiflags_ext->ases); + abiflags_in.flags1 = BYTE_GET (abiflags_ext->flags1); + abiflags_in.flags2 = BYTE_GET (abiflags_ext->flags2); + + printf ("\nMIPS ABI Flags Version: %d\n", abiflags_in.version); + printf ("\nISA: MIPS%d", abiflags_in.isa_level); + if (abiflags_in.isa_rev != 0) + printf ("r%d", abiflags_in.isa_rev); + printf ("\nGPR size: %d", + get_mips_reg_size (abiflags_in.gpr_size)); + printf ("\nCPR1 size: %d", + get_mips_reg_size (abiflags_in.cpr1_size)); + printf ("\nCPR2 size: %d", + get_mips_reg_size (abiflags_in.cpr2_size)); + fputs ("\nFP ABI: ", stdout); + print_mips_fp_abi_value (abiflags_in.fp_abi); + fputs ("ISA Extension: ", stdout); + print_mips_isa_ext (abiflags_in.isa_ext); + fputs ("\nASEs:", stdout); + print_mips_ases (abiflags_in.ases); + printf ("\nFLAGS 1: %8.8lx", abiflags_in.flags1); + printf ("\nFLAGS 2: %8.8lx", abiflags_in.flags2); + fputc ('\n', stdout); + free (abiflags_ext); + } + } + } + /* We have a lot of special sections. Thanks SGI! */ if (dynamic_section == NULL) /* No information available. */ @@ -12790,11 +12968,11 @@ process_mips_specific (FILE * file) if (options_offset != 0) { Elf_External_Options * eopt; - Elf_Internal_Shdr * sect = section_headers; Elf_Internal_Options * iopt; Elf_Internal_Options * option; size_t offset; int cnt; + sect = section_headers; /* Find the section header so that we get the size. */ while (sect->sh_type != SHT_MIPS_OPTIONS) diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index 561b54a..1c48bd5 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -490,7 +490,9 @@ enum PT // Runtime procedure table. PT_MIPS_RTPROC = 0x70000001, // .MIPS.options section. - PT_MIPS_OPTIONS = 0x70000002 + PT_MIPS_OPTIONS = 0x70000002, + // .MIPS.abiflags section. + PT_MIPS_ABIFLAGS = 0x70000003 }; // The valid bit flags found in the Phdr p_flags field. diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 12e9792..6ce7644 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -89,6 +89,7 @@ int mips_flag_pdr = TRUE; #include "ecoff.h" static char *mips_regmask_frag; +static char *mips_flags_frag; #define ZERO 0 #define ATREG 1 @@ -259,6 +260,9 @@ struct mips_set_options bfd_boolean single_float; }; +/* Specifies whether module level options have been checked yet. */ +static bfd_boolean file_mips_opts_checked = FALSE; + /* True if -mnan=2008, false if -mnan=legacy. */ static bfd_boolean mips_flag_nan2008 = FALSE; @@ -1294,6 +1298,7 @@ static void s_ehword (int); static void s_cpadd (int); static void s_insn (int); static void s_nan (int); +static void s_module (int); static void md_obj_begin (void); static void md_obj_end (void); static void s_mips_ent (int); @@ -1407,6 +1412,7 @@ enum options OPTION_CONSTRUCT_FLOATS, OPTION_NO_CONSTRUCT_FLOATS, OPTION_FP64, + OPTION_FPXX, OPTION_GP64, OPTION_RELAX_BRANCH, OPTION_NO_RELAX_BRANCH, @@ -1526,6 +1532,7 @@ struct option md_longopts[] = {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS}, {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS}, {"mfp64", no_argument, NULL, OPTION_FP64}, + {"mfpxx", no_argument, NULL, OPTION_FPXX}, {"mgp64", no_argument, NULL, OPTION_GP64}, {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH}, {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH}, @@ -1693,6 +1700,7 @@ static const pseudo_typeS mips_pseudo_table[] = {"cpadd", s_cpadd, 0}, {"insn", s_insn, 0}, {"nan", s_nan, 0}, + {"module", s_module, 0}, /* Relatively generic pseudo-ops that happen to be used on MIPS chips. */ @@ -1980,14 +1988,15 @@ mips_check_isa_supports_ases (void) that were affected. */ static unsigned int -mips_set_ase (const struct mips_ase *ase, bfd_boolean enabled_p) +mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts, + bfd_boolean enabled_p) { unsigned int mask; mask = mips_ase_mask (ase->flags); - mips_opts.ase &= ~mask; + opts->ase &= ~mask; if (enabled_p) - mips_opts.ase |= ase->flags; + opts->ase |= ase->flags; return mask; } @@ -3609,6 +3618,11 @@ md_begin (void) } } + sec = subseg_new (".MIPS.abiflags", (subsegT) 0); + bfd_set_section_flags (stdoutput, sec, flags); + bfd_set_section_alignment (stdoutput, sec, 3); + mips_flags_frag = frag_more (sizeof (Elf_External_ABIFlags_v0)); + if (ECOFF_DEBUGGING) { sec = subseg_new (".mdebug", (subsegT) 0); @@ -3641,6 +3655,175 @@ md_mips_end (void) mips_emit_delays (); if (! ECOFF_DEBUGGING) md_obj_end (); + + int fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, + Tag_GNU_MIPS_ABI_FP); + switch (fpabi) + { + case Val_GNU_MIPS_ABI_FP_DOUBLE: + if ((file_mips_opts.gp32 ? 32 : 64) != file_mips_opts.fp + || file_mips_opts.single_float == 1 + || file_mips_opts.soft_float == 1) + as_warn (_("incorrect .gnu_attribute %d,%d found when module is " + "%s"), + Tag_GNU_MIPS_ABI_FP, fpabi, + (file_mips_opts.single_float == 1) ? "`single-float'" + : (file_mips_opts.soft_float == 1) ? "`soft-float'" + : (file_mips_opts.fp == 32) ? "`fp32'" + : (file_mips_opts.fp == 64) ? "`fp64'" : "`fpxx'"); + break; + case Val_GNU_MIPS_ABI_FP_XX: + if (mips_abi != O32_ABI + || file_mips_opts.fp != 0 + || file_mips_opts.single_float == 1 + || file_mips_opts.soft_float == 1) + as_warn (_("incorrect .gnu_attribute %d,%d found when module is " + "%s"), + Tag_GNU_MIPS_ABI_FP, fpabi, + (mips_abi != O32_ABI) ? "not `-mabi=32'" + : (file_mips_opts.single_float == 1) ? "`single-float'" + : (file_mips_opts.soft_float == 1) ? "`soft-float'" + : (file_mips_opts.fp == 32) ? "`fp32'" + : (file_mips_opts.fp == 64) ? "`fp64'" : "`fpxx'"); + break; + case Val_GNU_MIPS_ABI_FP_64: + if (mips_abi != O32_ABI + || file_mips_opts.fp != 64 + || file_mips_opts.single_float == 1 + || file_mips_opts.soft_float == 1) + as_warn (_("incorrect .gnu_attribute %d,%d found when module is " + "%s"), + Tag_GNU_MIPS_ABI_FP, fpabi, + (mips_abi != O32_ABI) ? "not `-mabi=32'" + : (file_mips_opts.single_float == 1) ? "`single-float'" + : (file_mips_opts.soft_float == 1) ? "`soft-float'" + : (file_mips_opts.fp == 32) ? "`fp32'" + : (file_mips_opts.fp == 64) ? "`fp64'" : "`fpxx'"); + break; + case Val_GNU_MIPS_ABI_FP_SINGLE: + if (file_mips_opts.single_float != 1) + as_warn (_("incorrect .gnu_attribute %d,%d found when module is " + "not %s"), + Tag_GNU_MIPS_ABI_FP, fpabi, "`single-float'"); + break; + case Val_GNU_MIPS_ABI_FP_SOFT: + if (file_mips_opts.soft_float != 1) + as_warn (_("incorrect .gnu_attribute %d,%d found when module is " + "not %s"), + Tag_GNU_MIPS_ABI_FP, fpabi, "`soft-float'"); + break; + case Val_GNU_MIPS_ABI_FP_OLD_64: + as_warn (_("incorrect .gnu_attribute %d,%d found. ABI not " + "supported"), + Tag_GNU_MIPS_ABI_FP, fpabi); + break; + default: + if (fpabi != Val_GNU_MIPS_ABI_FP_ANY) + as_warn (_("incompatible module option and .gnu_attribute seen, " + "unexpected Tag_GNU_MIPS_ABI_FP: %d"), + fpabi); + break; + } +} + +/* Perform consistency checks on the current options. */ + +static void +mips_check_options (struct mips_set_options *opts, bfd_boolean abi_checks) +{ + /* Check the size of integer registers agrees with the ABI and ISA. */ + if (opts->gp32 == 0 && !ISA_HAS_64BIT_REGS (opts->isa)) + as_bad (_("`gp64' used with a 32-bit processor")); + else if (abi_checks + && opts->gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi)) + as_bad (_("`gp32' used with a 64-bit ABI")); + else if (abi_checks + && opts->gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi)) + as_bad (_("`gp64' used with a 32-bit ABI")); + + /* Check the size of the float registers agrees with the ABI and ISA. */ + switch (opts->fp) + { + case 0: + if (!CPU_HAS_LDC1_SDC1 (opts->arch)) + as_bad (_("`fpxx' used with a cpu lacking ldc1/sdc1 instructions")); + else if (opts->single_float == 1 + || opts->soft_float == 1) + as_bad (_("`fpxx' cannot be used with `single-float' or `soft-float'")); + break; + case 64: + if (!ISA_HAS_64BIT_FPRS (opts->isa)) + as_bad (_("`fp64' used with a 32-bit fpu")); + else if (opts->single_float == 1 + || opts->soft_float == 1) + as_bad (_("`fp64' cannot be used with `single-float' or `soft-float'")); + else if (abi_checks + && ABI_NEEDS_32BIT_REGS (mips_abi) + && !ISA_HAS_MXHC1 (opts->isa)) + as_warn (_("`fp64' used with a 32-bit ABI")); + break; + case 32: + if (abi_checks + && ABI_NEEDS_64BIT_REGS (mips_abi)) + as_warn (_("`fp32' used with a 64-bit ABI")); + break; + default: + as_bad (_("Unknown size of floating point registers")); + } + + if (opts->micromips == 1 && opts->mips16 == 1) + as_bad (_("`mips16' cannot be used with `micromips'")); +} + +/* Perform consistency checks on the module level options exactly once. + This is a deferred check that happens: + at the first .set directive + or, at the first pseudo op that generates code + or, at the first instruction + or, at the end. */ + +static void +file_mips_check_options (void) +{ + int fpabi = Val_GNU_MIPS_ABI_FP_ANY; + + if (file_mips_opts_checked) + return; + + mips_check_options (&file_mips_opts, TRUE); + file_mips_opts_checked = TRUE; + + if (!bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch)) + as_warn (_("could not set architecture and machine")); + + /* Soft-float gets precedence over single-float, the two options should + not be used together so this should not matter. */ + if (file_mips_opts.soft_float == 1) + fpabi = Val_GNU_MIPS_ABI_FP_SOFT; + /* Single-float gets precedence over all double_float cases. */ + else if (file_mips_opts.single_float == 1) + fpabi = Val_GNU_MIPS_ABI_FP_SINGLE; + else + { + switch (file_mips_opts.fp) + { + case 32: + fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE; + break; + case 0: + fpabi = Val_GNU_MIPS_ABI_FP_XX; + break; + case 64: + if (file_mips_opts.gp32) + fpabi = Val_GNU_MIPS_ABI_FP_64; + else + fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE; + break; + } + } + + bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, + Tag_GNU_MIPS_ABI_FP, fpabi); } void @@ -3650,6 +3833,8 @@ md_assemble (char *str) bfd_reloc_code_real_type unused_reloc[3] = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED}; + file_mips_check_options (); + imm_expr.X_op = O_absent; offset_expr.X_op = O_absent; offset_reloc[0] = BFD_RELOC_UNUSED; @@ -5361,13 +5546,13 @@ match_float_constant (struct mips_arg_info *arg, expressionS *imm, /* Handle 64-bit constants for which an immediate value is best. */ if (length == 8 && !mips_disable_float_construction - /* Constants can only be constructed in GPRs and copied - to FPRs if the GPRs are at least as wide as the FPRs. - Force the constant into memory if we are using 64-bit FPRs - but the GPRs are only 32 bits wide. */ - /* ??? No longer true with the addition of MTHC1, but this - is legacy code... */ - && (using_gprs || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS)) + /* Constants can only be constructed in GPRs and copied to FPRs if the + GPRs are at least as wide as the FPRs or MTHC1 is available. + Force the constant into memory otherwise. */ + && (using_gprs + || HAVE_64BIT_GPRS + || ISA_HAS_MXHC1 (mips_opts.isa) + || (HAVE_32BIT_FPRS && mips_opts.fp != 0)) && ((data[0] == 0 && data[1] == 0) || (data[2] == 0 && data[3] == 0)) && ((data[4] == 0 && data[5] == 0) @@ -11559,14 +11744,18 @@ macro (struct mips_cl_insn *ip, char *str) { used_at = 1; load_register (AT, &imm_expr, HAVE_64BIT_FPRS); - if (HAVE_64BIT_FPRS) - { - gas_assert (HAVE_64BIT_GPRS); - macro_build (NULL, "dmtc1", "t,S", AT, op[0]); - } + if (HAVE_64BIT_FPRS && HAVE_64BIT_GPRS) + macro_build (NULL, "dmtc1", "t,S", AT, op[0]); else { - macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1); + if (ISA_HAS_MXHC1 (mips_opts.isa)) + macro_build (NULL, "mthc1", "t,G", AT, op[0]); + else if (mips_opts.fp != 32) + as_bad (_("Unable to generate `%s' compliant code " + "without mthc1"), + (mips_opts.fp == 64) ? "fp64" : "fpxx"); + else + macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1); if (offset_expr.X_op == O_absent) macro_build (NULL, "mtc1", "t,G", 0, op[0]); else @@ -13495,7 +13684,7 @@ md_parse_option (int c, char *arg) for (i = 0; i < ARRAY_SIZE (mips_ases); i++) if (c == mips_ases[i].option_on || c == mips_ases[i].option_off) { - file_ase_explicit |= mips_set_ase (&mips_ases[i], + file_ase_explicit |= mips_set_ase (&mips_ases[i], &file_mips_opts, c == mips_ases[i].option_on); return 1; } @@ -13637,32 +13826,32 @@ md_parse_option (int c, char *arg) break; case OPTION_MICROMIPS: - if (mips_opts.mips16 == 1) + if (file_mips_opts.mips16 == 1) { as_bad (_("-mmicromips cannot be used with -mips16")); return 0; } - mips_opts.micromips = 1; + file_mips_opts.micromips = 1; mips_no_prev_insn (); break; case OPTION_NO_MICROMIPS: - mips_opts.micromips = 0; + file_mips_opts.micromips = 0; mips_no_prev_insn (); break; case OPTION_MIPS16: - if (mips_opts.micromips == 1) + if (file_mips_opts.micromips == 1) { as_bad (_("-mips16 cannot be used with -micromips")); return 0; } - mips_opts.mips16 = 1; + file_mips_opts.mips16 = 1; mips_no_prev_insn (); break; case OPTION_NO_MIPS16: - mips_opts.mips16 = 0; + file_mips_opts.mips16 = 0; mips_no_prev_insn (); break; @@ -13731,11 +13920,11 @@ md_parse_option (int c, char *arg) break; case OPTION_INSN32: - mips_opts.insn32 = TRUE; + file_mips_opts.insn32 = TRUE; break; case OPTION_NO_INSN32: - mips_opts.insn32 = FALSE; + file_mips_opts.insn32 = FALSE; break; case OPTION_MSHARED: @@ -13747,11 +13936,11 @@ md_parse_option (int c, char *arg) break; case OPTION_MSYM32: - mips_opts.sym32 = TRUE; + file_mips_opts.sym32 = TRUE; break; case OPTION_MNO_SYM32: - mips_opts.sym32 = FALSE; + file_mips_opts.sym32 = FALSE; break; /* When generating ELF code, we permit -KPIC and -call_shared to @@ -13812,6 +14001,10 @@ md_parse_option (int c, char *arg) file_mips_opts.fp = 32; break; + case OPTION_FPXX: + file_mips_opts.fp = 0; + break; + case OPTION_FP64: file_mips_opts.fp = 64; break; @@ -13904,22 +14097,7 @@ md_parse_option (int c, char *arg) return 1; } -/* Set up globals to generate code for the ISA or processor - described by INFO. */ - -static void -mips_set_architecture (const struct mips_cpu_info *info) -{ - if (info != 0) - { - file_mips_opts.arch = info->cpu; - mips_opts.arch = info->cpu; - mips_opts.isa = info->isa; - } -} - - -/* Likewise for tuning. */ +/* Set up globals to tune for the ISA or processor described by INFO. */ static void mips_set_tune (const struct mips_cpu_info *info) @@ -13984,7 +14162,8 @@ mips_after_parse_args (void) as_bad (_("-march=%s is not compatible with the selected ABI"), arch_info->name); - mips_set_architecture (arch_info); + file_mips_opts.arch = arch_info->cpu; + file_mips_opts.isa = arch_info->isa; /* Optimize for file_mips_opts.arch, unless -mtune selects a different processor. */ @@ -13996,30 +14175,17 @@ mips_after_parse_args (void) else mips_set_tune (tune_info); - if (file_mips_opts.gp32 >= 0) - { - /* The user specified the size of the integer registers. Make sure - it agrees with the ABI and ISA. */ - if (file_mips_opts.gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa)) - as_bad (_("-mgp64 used with a 32-bit processor")); - else if (file_mips_opts.gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi)) - as_bad (_("-mgp32 used with a 64-bit ABI")); - else if (file_mips_opts.gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi)) - as_bad (_("-mgp64 used with a 32-bit ABI")); - } - else + if (file_mips_opts.gp32 < 0) { /* Infer the integer register size from the ABI and processor. Restrict ourselves to 32-bit registers if that's all the processor has, or if the ABI cannot handle 64-bit registers. */ file_mips_opts.gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi) - || !ISA_HAS_64BIT_REGS (mips_opts.isa)); + || !ISA_HAS_64BIT_REGS (file_mips_opts.isa)); } - switch (file_mips_opts.fp) + if (file_mips_opts.fp < 0) { - default: - case -1: /* No user specified float register size. ??? GAS treats single-float processors as though they had 64-bit float registers (although it complains when double-precision @@ -14030,49 +14196,34 @@ mips_after_parse_args (void) if (file_mips_opts.gp32 == 0) /* 64-bit integer registers implies 64-bit float registers. */ file_mips_opts.fp = 64; - else if ((mips_opts.ase & FP64_ASES) - && ISA_HAS_64BIT_FPRS (mips_opts.isa)) + else if ((file_mips_opts.ase & FP64_ASES) + && ISA_HAS_64BIT_FPRS (file_mips_opts.isa)) /* -mips3d and -mdmx imply 64-bit float registers, if possible. */ file_mips_opts.fp = 64; else /* 32-bit float registers. */ file_mips_opts.fp = 32; - break; - - /* The user specified the size of the float registers. Check if it - agrees with the ABI and ISA. */ - case 64: - if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) - as_bad (_("-mfp64 used with a 32-bit fpu")); - else if (ABI_NEEDS_32BIT_REGS (mips_abi) - && !ISA_HAS_MXHC1 (mips_opts.isa)) - as_warn (_("-mfp64 used with a 32-bit ABI")); - break; - case 32: - if (ABI_NEEDS_64BIT_REGS (mips_abi)) - as_warn (_("-mfp32 used with a 64-bit ABI")); - break; } /* End of GCC-shared inference code. */ /* This flag is set when we have a 64-bit capable CPU but use only 32-bit wide registers. Note that EABI does not use it. */ - if (ISA_HAS_64BIT_REGS (mips_opts.isa) + if (ISA_HAS_64BIT_REGS (file_mips_opts.isa) && ((mips_abi == NO_ABI && file_mips_opts.gp32 == 1) || mips_abi == O32_ABI)) mips_32bitmode = 1; - if (mips_opts.isa == ISA_MIPS1 && mips_trap) + if (file_mips_opts.isa == ISA_MIPS1 && mips_trap) as_bad (_("trap exception not supported at ISA 1")); /* If the selected architecture includes support for ASEs, enable generation of code for them. */ - if (mips_opts.mips16 == -1) - mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0; - if (mips_opts.micromips == -1) - mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch)) - ? 1 : 0; + if (file_mips_opts.mips16 == -1) + file_mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0; + if (file_mips_opts.micromips == -1) + file_mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch)) + ? 1 : 0; /* MIPS3D and MDMX require 64-bit FPRs, so -mfp32 should stop those ASEs from being selected implicitly. */ @@ -14081,14 +14232,10 @@ mips_after_parse_args (void) /* If the user didn't explicitly select or deselect a particular ASE, use the default setting for the CPU. */ - mips_opts.ase |= (arch_info->ase & ~file_ase_explicit); + file_mips_opts.ase |= (arch_info->ase & ~file_ase_explicit); - file_mips_opts.isa = mips_opts.isa; - file_mips_opts.ase = mips_opts.ase; - mips_opts.gp32 = file_mips_opts.gp32; - mips_opts.fp = file_mips_opts.fp; - mips_opts.soft_float = file_mips_opts.soft_float; - mips_opts.single_float = file_mips_opts.single_float; + /* Set up the current options. These may change throughout assembly. */ + mips_opts = file_mips_opts; mips_check_isa_supports_ases (); @@ -14979,30 +15126,11 @@ struct mips_option_stack static struct mips_option_stack *mips_opts_stack; -/* Handle the .set pseudo-op. */ - -static void -s_mipsset (int x ATTRIBUTE_UNUSED) +static bfd_boolean +parse_code_option (char * name) { - char *name = input_line_pointer, ch; const struct mips_ase *ase; - - while (!is_end_of_line[(unsigned char) *input_line_pointer]) - ++input_line_pointer; - ch = *input_line_pointer; - *input_line_pointer = '\0'; - - if (strcmp (name, "reorder") == 0) - { - if (mips_opts.noreorder) - end_noreorder (); - } - else if (strcmp (name, "noreorder") == 0) - { - if (!mips_opts.noreorder) - start_noreorder (); - } - else if (strncmp (name, "at=", 3) == 0) + if (strncmp (name, "at=", 3) == 0) { char *s = name + 3; @@ -15010,61 +15138,27 @@ s_mipsset (int x ATTRIBUTE_UNUSED) as_bad (_("unrecognized register name `%s'"), s); } else if (strcmp (name, "at") == 0) - { - mips_opts.at = ATREG; - } + mips_opts.at = ATREG; else if (strcmp (name, "noat") == 0) - { - mips_opts.at = ZERO; - } - else if (strcmp (name, "macro") == 0) - { - mips_opts.warn_about_macros = 0; - } - else if (strcmp (name, "nomacro") == 0) - { - if (mips_opts.noreorder == 0) - as_bad (_("`noreorder' must be set before `nomacro'")); - mips_opts.warn_about_macros = 1; - } + mips_opts.at = ZERO; else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0) - { - mips_opts.nomove = 0; - } + mips_opts.nomove = 0; else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0) - { - mips_opts.nomove = 1; - } + mips_opts.nomove = 1; else if (strcmp (name, "bopt") == 0) - { - mips_opts.nobopt = 0; - } + mips_opts.nobopt = 0; else if (strcmp (name, "nobopt") == 0) - { - mips_opts.nobopt = 1; - } - else if (strcmp (name, "gp=default") == 0) - mips_opts.gp32 = file_mips_opts.gp32; + mips_opts.nobopt = 1; else if (strcmp (name, "gp=32") == 0) mips_opts.gp32 = 1; else if (strcmp (name, "gp=64") == 0) - { - if (!ISA_HAS_64BIT_REGS (mips_opts.isa)) - as_warn (_("%s isa does not support 64-bit registers"), - mips_cpu_info_from_isa (mips_opts.isa)->name); - mips_opts.gp32 = 0; - } - else if (strcmp (name, "fp=default") == 0) - mips_opts.fp = file_mips_opts.fp; + mips_opts.gp32 = 0; else if (strcmp (name, "fp=32") == 0) mips_opts.fp = 32; + else if (strcmp (name, "fp=xx") == 0) + mips_opts.fp = 0; else if (strcmp (name, "fp=64") == 0) - { - if (!ISA_HAS_64BIT_FPRS (mips_opts.isa)) - as_warn (_("%s isa does not support 64-bit floating point registers"), - mips_cpu_info_from_isa (mips_opts.isa)->name); - mips_opts.fp = 64; - } + mips_opts.fp = 64; else if (strcmp (name, "softfloat") == 0) mips_opts.soft_float = 1; else if (strcmp (name, "hardfloat") == 0) @@ -15075,45 +15169,29 @@ s_mipsset (int x ATTRIBUTE_UNUSED) mips_opts.single_float = 0; else if (strcmp (name, "mips16") == 0 || strcmp (name, "MIPS-16") == 0) - { - if (mips_opts.micromips == 1) - as_fatal (_("`mips16' cannot be used with `micromips'")); - mips_opts.mips16 = 1; - } + mips_opts.mips16 = 1; else if (strcmp (name, "nomips16") == 0 || strcmp (name, "noMIPS-16") == 0) mips_opts.mips16 = 0; else if (strcmp (name, "micromips") == 0) - { - if (mips_opts.mips16 == 1) - as_fatal (_("`micromips' cannot be used with `mips16'")); - mips_opts.micromips = 1; - } + mips_opts.micromips = 1; else if (strcmp (name, "nomicromips") == 0) mips_opts.micromips = 0; else if (name[0] == 'n' && name[1] == 'o' && (ase = mips_lookup_ase (name + 2))) - mips_set_ase (ase, FALSE); + mips_set_ase (ase, &mips_opts, FALSE); else if ((ase = mips_lookup_ase (name))) - mips_set_ase (ase, TRUE); + mips_set_ase (ase, &mips_opts, TRUE); else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0) { - int reset = 0; - /* Permit the user to change the ISA and architecture on the fly. Needless to say, misuse can cause serious problems. */ - if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0) - { - reset = 1; - mips_opts.isa = file_mips_opts.isa; - mips_opts.arch = file_mips_opts.arch; - } - else if (strncmp (name, "arch=", 5) == 0) + if (strncmp (name, "arch=", 5) == 0) { const struct mips_cpu_info *p; - p = mips_parse_cpu("internal use", name + 5); + p = mips_parse_cpu ("internal use", name + 5); if (!p) as_bad (_("unknown architecture %s"), name + 5); else @@ -15126,7 +15204,7 @@ s_mipsset (int x ATTRIBUTE_UNUSED) { const struct mips_cpu_info *p; - p = mips_parse_cpu("internal use", name); + p = mips_parse_cpu ("internal use", name); if (!p) as_bad (_("unknown ISA level %s"), name + 4); else @@ -15137,46 +15215,6 @@ s_mipsset (int x ATTRIBUTE_UNUSED) } else as_bad (_("unknown ISA or architecture %s"), name); - - switch (mips_opts.isa) - { - case 0: - break; - case ISA_MIPS1: - case ISA_MIPS2: - case ISA_MIPS32: - case ISA_MIPS32R2: - case ISA_MIPS32R3: - case ISA_MIPS32R5: - mips_opts.gp32 = 1; - mips_opts.fp = 32; - break; - case ISA_MIPS3: - case ISA_MIPS4: - case ISA_MIPS5: - case ISA_MIPS64: - case ISA_MIPS64R2: - case ISA_MIPS64R3: - case ISA_MIPS64R5: - mips_opts.gp32 = 0; - if (mips_opts.arch == CPU_R5900) - { - mips_opts.fp = 32; - } - else - { - mips_opts.fp = 64; - } - break; - default: - as_bad (_("unknown ISA level %s"), name + 4); - break; - } - if (reset) - { - mips_opts.gp32 = file_mips_opts.gp32; - mips_opts.fp = file_mips_opts.fp; - } } else if (strcmp (name, "autoextend") == 0) mips_opts.noautoextend = 0; @@ -15186,6 +15224,68 @@ s_mipsset (int x ATTRIBUTE_UNUSED) mips_opts.insn32 = TRUE; else if (strcmp (name, "noinsn32") == 0) mips_opts.insn32 = FALSE; + else if (strcmp (name, "sym32") == 0) + mips_opts.sym32 = TRUE; + else if (strcmp (name, "nosym32") == 0) + mips_opts.sym32 = FALSE; + else + return FALSE; + return TRUE; +} + +/* Handle the .set pseudo-op. */ + +static void +s_mipsset (int x ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer, ch; + int prev_isa = mips_opts.isa; + + file_mips_check_options (); + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (strchr (name, ',')) + { + /* Generic ".set" directive; use the generic handler. */ + *input_line_pointer = ch; + input_line_pointer = name; + s_set (0); + return; + } + + if (strcmp (name, "reorder") == 0) + { + if (mips_opts.noreorder) + end_noreorder (); + } + else if (strcmp (name, "noreorder") == 0) + { + if (!mips_opts.noreorder) + start_noreorder (); + } + else if (strcmp (name, "macro") == 0) + mips_opts.warn_about_macros = 0; + else if (strcmp (name, "nomacro") == 0) + { + if (mips_opts.noreorder == 0) + as_bad (_("`noreorder' must be set before `nomacro'")); + mips_opts.warn_about_macros = 1; + } + else if (strcmp (name, "gp=default") == 0) + mips_opts.gp32 = file_mips_opts.gp32; + else if (strcmp (name, "fp=default") == 0) + mips_opts.fp = file_mips_opts.fp; + else if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0) + { + mips_opts.isa = file_mips_opts.isa; + mips_opts.arch = file_mips_opts.arch; + mips_opts.gp32 = file_mips_opts.gp32; + mips_opts.fp = file_mips_opts.fp; + } else if (strcmp (name, "push") == 0) { struct mips_option_stack *s; @@ -15216,23 +15316,75 @@ s_mipsset (int x ATTRIBUTE_UNUSED) free (s); } } - else if (strcmp (name, "sym32") == 0) - mips_opts.sym32 = TRUE; - else if (strcmp (name, "nosym32") == 0) - mips_opts.sym32 = FALSE; - else if (strchr (name, ',')) + else if (!parse_code_option (name)) + as_warn (_("tried to set unrecognized symbol: %s\n"), name); + + /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp + registers based on what is supported by the arch/cpu. */ + if (mips_opts.isa != prev_isa) { - /* Generic ".set" directive; use the generic handler. */ - *input_line_pointer = ch; - input_line_pointer = name; - s_set (0); - return; + switch (mips_opts.isa) + { + case 0: + break; + case ISA_MIPS1: + case ISA_MIPS2: + case ISA_MIPS32: + case ISA_MIPS32R2: + mips_opts.gp32 = 1; + if (mips_opts.fp != 0) + mips_opts.fp = 32; + break; + case ISA_MIPS3: + case ISA_MIPS4: + case ISA_MIPS5: + case ISA_MIPS64: + case ISA_MIPS64R2: + mips_opts.gp32 = 0; + if (mips_opts.fp != 0) + { + if (mips_opts.arch == CPU_R5900) + mips_opts.fp = 32; + else + mips_opts.fp = 64; + } + break; + default: + as_bad (_("unknown ISA level %s"), name + 4); + break; + } } - else + + mips_check_options (&mips_opts, FALSE); + + mips_check_isa_supports_ases (); + *input_line_pointer = ch; + demand_empty_rest_of_line (); +} + +/* Handle the .module pseudo-op. */ + +static void +s_module (int ignore ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer, ch; + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + ch = *input_line_pointer; + *input_line_pointer = '\0'; + + if (!file_mips_opts_checked) { - as_warn (_("tried to set unrecognized symbol: %s\n"), name); + if (!parse_code_option (name)) + as_warn (_(".module used with unrecognized symbol: %s\n"), name); + + /* Update module level settings from mips_opts. */ + file_mips_opts = mips_opts; } - mips_check_isa_supports_ases (); + else + as_bad (_(".module is not permitted after generating code")); + *input_line_pointer = ch; demand_empty_rest_of_line (); } @@ -15279,6 +15431,8 @@ s_cpload (int ignore ATTRIBUTE_UNUSED) int reg; int in_shared; + file_mips_check_options (); + /* If we are not generating SVR4 PIC code, or if this is NewABI code, .cpload is ignored. */ if (mips_pic != SVR4_PIC || HAVE_NEWABI) @@ -15356,6 +15510,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED) expressionS ex_sym; int reg1; + file_mips_check_options (); + /* If we are not generating SVR4 PIC code, .cpsetup is ignored. We also need NewABI support. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) @@ -15459,6 +15615,8 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED) static void s_cplocal (int ignore ATTRIBUTE_UNUSED) { + file_mips_check_options (); + /* If we are not generating SVR4 PIC code, or if this is not NewABI code, .cplocal is ignored. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) @@ -15487,6 +15645,8 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED) { expressionS ex; + file_mips_check_options (); + /* If we are not generating SVR4 PIC code, or if this is NewABI code, .cprestore is ignored. */ if (mips_pic != SVR4_PIC || HAVE_NEWABI) @@ -15534,6 +15694,8 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED) { expressionS ex; + file_mips_check_options (); + /* If we are not generating SVR4 PIC code, .cpreturn is ignored. We also need NewABI support. */ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI) @@ -15768,6 +15930,8 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED) { int reg; + file_mips_check_options (); + /* This is ignored when not generating SVR4 PIC code. */ if (mips_pic != SVR4_PIC) { @@ -17333,11 +17497,103 @@ mips_add_dot_label (symbolS *sym) mips_compressed_mark_label (sym); } +/* Converting ASE flags from internal to .MIPS.abiflags values. */ +static unsigned int +mips_convert_ase_flags (int ase) +{ + unsigned int ext_ases = 0; + + if (ase & ASE_DSP) + ext_ases |= AFL_ASE_DSP; + if (ase & ASE_DSPR2) + ext_ases |= AFL_ASE_DSPR2; + if (ase & ASE_EVA) + ext_ases |= AFL_ASE_EVA; + if (ase & ASE_MCU) + ext_ases |= AFL_ASE_MCU; + if (ase & ASE_MDMX) + ext_ases |= AFL_ASE_MDMX; + if (ase & ASE_MIPS3D) + ext_ases |= AFL_ASE_MIPS3D; + if (ase & ASE_MT) + ext_ases |= AFL_ASE_MT; + if (ase & ASE_SMARTMIPS) + ext_ases |= AFL_ASE_SMARTMIPS; + if (ase & ASE_VIRT) + ext_ases |= AFL_ASE_VIRT; + if (ase & ASE_MSA) + ext_ases |= AFL_ASE_MSA; + if (ase & ASE_XPA) + ext_ases |= AFL_ASE_XPA; + + return ext_ases; +} /* Some special processing for a MIPS ELF file. */ void mips_elf_final_processing (void) { + int fpabi; + Elf_Internal_ABIFlags_v0 flags; + + flags.version = 0; + flags.isa_rev = 0; + switch (file_mips_opts.isa) + { + case INSN_ISA1: + flags.isa_level = 1; + break; + case INSN_ISA2: + flags.isa_level = 2; + break; + case INSN_ISA3: + flags.isa_level = 3; + break; + case INSN_ISA4: + flags.isa_level = 4; + break; + case INSN_ISA5: + flags.isa_level = 5; + break; + case INSN_ISA32: + flags.isa_level = 32; + flags.isa_rev = 1; + break; + case INSN_ISA32R2: + flags.isa_level = 32; + flags.isa_rev = 2; + break; + case INSN_ISA64: + flags.isa_level = 64; + flags.isa_rev = 1; + break; + case INSN_ISA64R2: + flags.isa_level = 64; + flags.isa_rev = 2; + break; + } + + flags.gpr_size = file_mips_opts.gp32 ? AFL_REG_32 : AFL_REG_64; + flags.cpr1_size = file_mips_opts.soft_float ? AFL_REG_NONE + : (file_mips_opts.ase & AFL_ASE_MSA) ? AFL_REG_128 + : (file_mips_opts.fp == 64) ? AFL_REG_64 + : AFL_REG_32; + flags.cpr2_size = AFL_REG_NONE; + flags.fp_abi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, + Tag_GNU_MIPS_ABI_FP); + flags.isa_ext = bfd_mips_isa_ext (stdoutput); + flags.ases = mips_convert_ase_flags (file_mips_opts.ase); + if (file_ase_mips16) + flags.ases |= AFL_ASE_MIPS16; + if (file_ase_micromips) + flags.ases |= AFL_ASE_MICROMIPS; + flags.flags1 = 0; + flags.flags2 = 0; + + bfd_mips_elf_swap_abiflags_v0_out (stdoutput, &flags, + ((Elf_External_ABIFlags_v0 *) + mips_flags_frag)); + /* Write out the register information. */ if (mips_abi != N64_ABI) { @@ -17416,7 +17672,9 @@ mips_elf_final_processing (void) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008; /* 32 bit code with 64 bit FP registers. */ - if (file_mips_opts.fp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi)) + fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, + Tag_GNU_MIPS_ABI_FP); + if (fpabi == Val_GNU_MIPS_ABI_FP_OLD_64) elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64; } @@ -17531,6 +17789,9 @@ md_obj_end (void) /* Check for premature end, nesting errors, etc. */ if (cur_proc_ptr) as_warn (_("missing .end at end of assembly")); + + /* Just in case no code was emitted, do the consistency check. */ + file_mips_check_options (); } static long diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index 5759e9b..0062bc8 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -399,6 +399,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}. [@b{-g}[@var{debug level}]] [@b{-G} @var{num}] [@b{-KPIC}] [@b{-call_shared}] [@b{-non_shared}] [@b{-xgot} [@b{-mvxworks-pic}] [@b{-mabi}=@var{ABI}] [@b{-32}] [@b{-n32}] [@b{-64}] [@b{-mfp32}] [@b{-mgp32}] + [@b{-mfp64}] [@b{-mgp64}] [@b{-mfpxx}] [@b{-march}=@var{CPU}] [@b{-mtune}=@var{CPU}] [@b{-mips1}] [@b{-mips2}] [@b{-mips3}] [@b{-mips4}] [@b{-mips5}] [@b{-mips32}] [@b{-mips32r2}] [@b{-mips32r3}] [@b{-mips32r5}] [@b{-mips64}] [@b{-mips64r2}] @@ -1315,6 +1316,19 @@ flags force a certain group of registers to be treated as 32 bits wide at all times. @samp{-mgp32} controls the size of general-purpose registers and @samp{-mfp32} controls the size of floating-point registers. +@item -mgp64 +@itemx -mfp64 +The register sizes are normally inferred from the ISA and ABI, but these +flags force a certain group of registers to be treated as 64 bits wide at +all times. @samp{-mgp64} controls the size of general-purpose registers +and @samp{-mfp64} controls the size of floating-point registers. + +@item -mfpxx +The register sizes are normally inferred from the ISA and ABI, but using +this flag in combination with @samp{-mabi=32} enables an ABI variant +which will operate correctly with floating-point registers which are +32 or 64 bits wide. + @item -mips16 @itemx -no-mips16 Generate code for the MIPS 16 processor. This is equivalent to putting diff --git a/gas/doc/c-mips.texi b/gas/doc/c-mips.texi index 2165c20..7c99b2d 100644 --- a/gas/doc/c-mips.texi +++ b/gas/doc/c-mips.texi @@ -28,6 +28,7 @@ Assembly Language Programming'' in the same work. * MIPS assembly options:: Directives to control code generation * MIPS autoextend:: Directives for extending MIPS 16 bit instructions * MIPS insn:: Directive to mark data as an instruction +* MIPS FP ABIs:: Marking which FP ABI is in use * MIPS NaN Encodings:: Directives to record which NaN encoding is being used * MIPS Option Stack:: Directives to save and restore options * MIPS ASE Instruction Generation Overrides:: Directives to control @@ -125,6 +126,15 @@ The @code{.set gp=64} and @code{.set fp=64} directives allow the size of registers to be changed for parts of an object. The default value is restored by @code{.set gp=default} and @code{.set fp=default}. +@item -mfpxx +Make no assumptions about whether 32-bit or 64-bit floating-point +registers are available. This is provided to support having modules +compatible with either @samp{-mfp32} or @samp{-mfp64}. This option can +only be used with MIPS II and above. + +The @code{.set fp=xx} directive allows a part of an object to be marked +as not making assumptions about 32-bit or 64-bita FP registers. The +default value is restored by @code{.set fp=default}. @item -mips16 @itemx -no-mips16 Generate code for the MIPS 16 processor. This is equivalent to putting @@ -693,6 +703,20 @@ Traditional MIPS assemblers do not support this directive. @node MIPS assembly options @section Directives to control code generation +@cindex MIPS directives to override command line options +@kindex @code{.module} +The @code{.module} directive allows command line options to be set directly +from assembly. The format of the directive matches the @code{.set} +directive but only those options which are relevant to a whole module are +supported. The effect of a @code{.module} directive is the same as the +corresponding command line option. Where @code{.set} directives support +returning to a default then the @code{.module} directives do not as they +define the defaults. + +These module-level directives must appear first in assembly. + +Traditional MIPS assemblers do not support this directive. + @cindex MIPS 32-bit microMIPS instruction generation override @kindex @code{.set insn32} @kindex @code{.set noinsn32} @@ -755,6 +779,106 @@ baz: @end example +@node MIPS FP ABIs +@section Directives to control the FP ABI +@menu +* MIPS FP ABI History:: History of FP ABIs +* MIPS FP ABI Variants:: Supported FP ABIs +* MIPS FP ABI Selection:: Automatic selection of FP ABI +* MIPS FP ABI Compatibility:: Linking different FP ABI variants +@end menu + +@node MIPS FP ABI History +@subsection History of FP ABIs +@cindex @code{.gnu_attribute 4, @var{n}} directive, MIPS +@cindex @code{.gnu_attribute Tag_GNU_MIPS_ABI_FP, @var{n}} directive, MIPS +The MIPS ABIs support a variety of different floating-point extensions +where calling-convention and register sizes vary for floating-point data. +The extensions exist to support a wide variety of optional architecture +features. The resulting ABI variants are generally incompatible with each +other and must be tracked carefully. + +Traditionally the use of an explicit @code{.gnu_attribute 4, @var{n}} +directive is used to indicate which ABI is in use by a specific module. +It was then left to the user to ensure that command line options and the +selected ABI were compatible with some potential for inconsistencies. + +@node MIPS FP ABI Variants +@subsection Supported FP ABIs +The supported floating-point ABI variants are: + +@table @code +@item 0 - No floating-point +This variant is used to indicate that floating-point is not used within +the module at all and therefore has no impact on the ABI. This is the +default. + +@item 1 - Double-precision +This variant indicates that double-precision support is used. For 64-bit +ABIs this means that 64-bit wide floating-point registers are required. +For 32-bit ABIs this means that 32-bit wide floating-point registers are +required and double-precision operations use pairs of registers. + +@item 2 - Single-precision +This variant indicates that single-precision support is used. Double +precision operations will be supported via soft-float routines. + +@item 3 - Soft-float +This variant indicates that although floating-point support is used all +operations are emulated in software. This means the ABI is modified to +pass all floating-point data in general-purpose registers. + +@item 4 - Deprecated +This variant existed as an initial attempt at supporting 64-bit wide +floating-point registers for O32 ABI on a MIPS32r2 cpu. This has been +superceded by @value{5} and @value{6}. + +@item 5 - Double-precision 32-bit CPU, 32-bit or 64-bit FPU +This variant is used by 32-bit ABIs to indicate that the floating-point +code in the module has been designed to operate correctly with either +32-bit wide or 64-bit wide floating-point registers. Double-precision +support is used. Only O32 currently supports this variant and requires +a minimum architecture of MIPS II. + +@item 6 - Double-precision 32-bit FPU, 64-bit FPU +This variant is used by 32-bit ABIs to indicate that the floating-point +code in the module requires 64-bit wide floating-point registers. +Double-precision support is used. Only O32 currently supports this +variant and requires a minimum architecture of MIPS32r2. +@end table + +@node MIPS FP ABI Selection +@subsection Automatic selection of FP ABI +@cindex @code{.module fp=@var{nn}} directive, MIPS +In order to simplify and add safety to the process of selecting the +correct floating-point ABI, the assembler will automatically infer the +correct @code{.gnu_attribute 4, @var{n}} directive based on command line +options and @code{.module} overrides. Where an explicit +@code{.gnu_attribute 4, @var{n}} directive has been seen then a warning +will be raised if it does not match an inferred setting. + +The floating-point ABI is inferred as follows. If @samp{-msoft-float} +has been used the module will be marked as soft-float. The hard-float +ABIs are then only inferred if a floating point instruction is seen. +Firstly, if @samp{-msingle-float} has been used then the module will +be marked as single-precision. The remaining ABIs are selected based +on the FP register width. Double-precision is selected if the width +of GP and FP registers match and the special double-precision variants +for 32-bit ABIs are then selected depending on @samp{-mfpxx} and +@samp{-mfp64}. + +@node MIPS FP ABI Compatibility +@subsection Linking different FP ABI variants +Modules using the default FP ABI (no floating-point) can be linked with +any other (singular) FP ABI variant. + +Special compatibility support exists for O32 with the three +double-precision FP ABI variants. The @samp{-mfpxx} FP ABI is explicitly +designed to be compatible with both the standard double-precision ABI and +the @samp{-mfp64} FP ABI. This makes it desirable for O32 modules to be +built as @samp{-mfpxx} to ensure the maximum compatibility with other +modules produced for more specific needs. + @node MIPS NaN Encodings @section Directives to record which NaN encoding is being used diff --git a/include/elf/mips.h b/include/elf/mips.h index 2949629..2553e6d 100644 --- a/include/elf/mips.h +++ b/include/elf/mips.h @@ -428,6 +428,8 @@ END_RELOC_NUMBERS (R_MIPS_maxext) /* Runtime procedure descriptor table exception information (ucode) ??? */ #define SHT_MIPS_PDR_EXCEPTION 0x70000029 +/* ABI related flags section. */ +#define SHT_MIPS_ABIFLAGS 0x7000002a /* A section of type SHT_MIPS_LIBLIST contains an array of the following structure. The sh_link field is the section index of the @@ -593,6 +595,9 @@ extern void bfd_mips_elf32_swap_reginfo_out /* .MIPS.options section. */ #define PT_MIPS_OPTIONS 0x70000002 + +/* Records ABI related flags. */ +#define PT_MIPS_ABIFLAGS 0x70000003 /* Processor specific dynamic array tags. */ @@ -1048,6 +1053,58 @@ typedef struct bfd_vma ri_gp_value; } Elf64_Internal_RegInfo; +/* ABI Flags structure version 0. */ + +typedef struct +{ + /* Version of flags structure. */ + unsigned char version[2]; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level[1]; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev[1]; + /* The size of general purpose registers. */ + unsigned char gpr_size[1]; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size[1]; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size[1]; + /* The floating-point ABI. */ + unsigned char fp_abi[1]; + /* Mask of processor-specific extensions. */ + unsigned char isa_ext[4]; + /* Mask of ASEs used. */ + unsigned char ases[4]; + /* Mask of general flags. */ + unsigned char flags1[4]; + unsigned char flags2[4]; +} Elf_External_ABIFlags_v0; + +typedef struct +{ + /* Version of flags structure. */ + unsigned short version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Mask of processor-specific extensions. */ + unsigned long isa_ext; + /* Mask of ASEs used. */ + unsigned long ases; + /* Mask of general flags. */ + unsigned long flags1; + unsigned long flags2; +} Elf_Internal_ABIFlags_v0; + typedef struct { /* The hash value computed from the name of the corresponding @@ -1088,6 +1145,12 @@ extern void bfd_mips_elf64_swap_reginfo_in extern void bfd_mips_elf64_swap_reginfo_out (bfd *, const Elf64_Internal_RegInfo *, Elf64_External_RegInfo *); +/* MIPS ELF flags swapping routines. */ +extern void bfd_mips_elf_swap_abiflags_v0_in + (bfd *, const Elf_External_ABIFlags_v0 *, Elf_Internal_ABIFlags_v0 *); +extern void bfd_mips_elf_swap_abiflags_v0_out + (bfd *, const Elf_Internal_ABIFlags_v0 *, Elf_External_ABIFlags_v0 *); + /* Masks for the info work of an ODK_EXCEPTIONS descriptor. */ #define OEX_FPU_MIN 0x1f /* FPEs which must be enabled. */ #define OEX_FPU_MAX 0x1f00 /* FPEs which may be enabled. */ @@ -1125,6 +1188,52 @@ extern void bfd_mips_elf64_swap_reginfo_out /* Masks for the info word of an ODK_HWAND/ODK_HWOR descriptor. */ #define OHWA0_R4KEOP_CHECKED 0x00000001 #define OHWA0_R4KEOP_CLEAN 0x00000002 + +/* Values for the xxx_size bytes of an ABI flags structure. */ + +#define AFL_REG_NONE 0x00 /* No registers. */ +#define AFL_REG_32 0x01 /* 32-bit registers. */ +#define AFL_REG_64 0x02 /* 64-bit registers. */ +#define AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define AFL_ASE_XPA 0x00001000 /* XPA ASE. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +extern unsigned int bfd_mips_isa_ext (bfd *); /* Object attribute tags. */ @@ -1157,7 +1266,13 @@ enum Val_GNU_MIPS_ABI_FP_SOFT = 3, /* Using -mips32r2 -mfp64. */ - Val_GNU_MIPS_ABI_FP_64 = 4, + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + + /* Using -mfpxx */ + Val_GNU_MIPS_ABI_FP_XX = 5, + + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, /* Values defined for Tag_GNU_MIPS_ABI_MSA. */ diff --git a/ld/emulparams/elf32bmip.sh b/ld/emulparams/elf32bmip.sh index 118d57a..8da0f8f 100644 --- a/ld/emulparams/elf32bmip.sh +++ b/ld/emulparams/elf32bmip.sh @@ -17,7 +17,8 @@ if test -z "${CREATE_SHLIB}"; then INITIAL_READONLY_SECTIONS=".interp ${RELOCATING-0} : { *(.interp) }" fi INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS} - .reginfo ${RELOCATING-0} : { *(.reginfo) } + .MIPS.abiflags ${RELOCATING-0} : { *(.MIPS.abiflags) } + .reginfo ${RELOCATING-0} : { *(.reginfo) } " OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' # Unlike most targets, the MIPS backend puts all dynamic relocations diff --git a/ld/emulparams/elf32bmipn32-defs.sh b/ld/emulparams/elf32bmipn32-defs.sh index 514990b..723eac8 100644 --- a/ld/emulparams/elf32bmipn32-defs.sh +++ b/ld/emulparams/elf32bmipn32-defs.sh @@ -88,6 +88,7 @@ if test -z "${CREATE_SHLIB}"; then INITIAL_READONLY_SECTIONS=".interp ${RELOCATING-0} : { *(.interp) }" fi INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS} + .MIPS.abiflags ${RELOCATING-0} : { *(.MIPS.abiflags) } .reginfo ${RELOCATING-0} : { *(.reginfo) }" # Discard any .MIPS.content* or .MIPS.events* sections. The linker # doesn't know how to adjust them. -- 1.7.1