diff options
author | obrien <obrien@FreeBSD.org> | 2000-04-05 04:09:32 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2000-04-05 04:09:32 +0000 |
commit | 50b710fb897d50fa8ec10465523c70ec6b01cd7c (patch) | |
tree | d9fb6cde3142de6ba78633da139e0715545bde80 /contrib | |
parent | ef7325f7c927b1764908c855732ed97cc1641e2b (diff) | |
download | FreeBSD-src-50b710fb897d50fa8ec10465523c70ec6b01cd7c.zip FreeBSD-src-50b710fb897d50fa8ec10465523c70ec6b01cd7c.tar.gz |
Import the Sparc bits of GNU binutils 2.9.1.
Requested by: steve
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/binutils/bfd/cpu-sparc.c | 142 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf32-sparc.c | 1863 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf64-sparc.c | 2263 | ||||
-rw-r--r-- | contrib/binutils/bfd/sunos.c | 2918 | ||||
-rw-r--r-- | contrib/binutils/gas/config/tc-sparc.c | 3364 | ||||
-rw-r--r-- | contrib/binutils/gas/config/tc-sparc.h | 149 | ||||
-rw-r--r-- | contrib/binutils/include/aout/sun4.h | 219 | ||||
-rw-r--r-- | contrib/binutils/include/elf/sparc.h | 117 | ||||
-rw-r--r-- | contrib/binutils/include/opcode/sparc.h | 240 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf32_sparc.sh | 10 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf64_sparc.sh | 12 | ||||
-rw-r--r-- | contrib/binutils/opcodes/sparc-dis.c | 961 | ||||
-rw-r--r-- | contrib/binutils/opcodes/sparc-opc.c | 1920 |
13 files changed, 14178 insertions, 0 deletions
diff --git a/contrib/binutils/bfd/cpu-sparc.c b/contrib/binutils/bfd/cpu-sparc.c new file mode 100644 index 0000000..74df2db --- /dev/null +++ b/contrib/binutils/bfd/cpu-sparc.c @@ -0,0 +1,142 @@ +/* BFD support for the SPARC architecture. + Copyright (C) 1992, 94, 95, 96, 1997 Free Software Foundation, Inc. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" + +/* Don't mix 32 bit and 64 bit files. */ + +static const bfd_arch_info_type *sparc_compatible + PARAMS ((const bfd_arch_info_type *, const bfd_arch_info_type *)); + +static const bfd_arch_info_type * +sparc_compatible (a, b) + const bfd_arch_info_type *a; + const bfd_arch_info_type *b; +{ + if (a->bits_per_word != b->bits_per_word) + return NULL; + + return bfd_default_compatible (a, b); +} + +static const bfd_arch_info_type arch_info_struct[] = +{ + { + 32, /* bits in a word */ + 32, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_sparclet, + "sparc", + "sparc:sparclet", + 3, + false, + sparc_compatible, + bfd_default_scan, + &arch_info_struct[1], + }, + { + 32, /* bits in a word */ + 32, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_sparclite, + "sparc", + "sparc:sparclite", + 3, + false, + sparc_compatible, + bfd_default_scan, + &arch_info_struct[2], + }, + { + 32, /* bits in a word */ + 32, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_v8plus, + "sparc", + "sparc:v8plus", + 3, + false, + sparc_compatible, + bfd_default_scan, + &arch_info_struct[3], + }, + { + 32, /* bits in a word */ + 32, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_v8plusa, + "sparc", + "sparc:v8plusa", + 3, + false, + sparc_compatible, + bfd_default_scan, + &arch_info_struct[4], + }, + { + 64, /* bits in a word */ + 64, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_v9, + "sparc", + "sparc:v9", + 3, + false, + sparc_compatible, + bfd_default_scan, + &arch_info_struct[5], + }, + { + 64, /* bits in a word */ + 64, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc_v9a, + "sparc", + "sparc:v9a", + 3, + false, + sparc_compatible, + bfd_default_scan, + 0, + } +}; + +const bfd_arch_info_type bfd_sparc_arch = + { + 32, /* bits in a word */ + 32, /* bits in an address */ + 8, /* bits in a byte */ + bfd_arch_sparc, + bfd_mach_sparc, + "sparc", + "sparc", + 3, + true, /* the default */ + sparc_compatible, + bfd_default_scan, + &arch_info_struct[0], + }; diff --git a/contrib/binutils/bfd/elf32-sparc.c b/contrib/binutils/bfd/elf32-sparc.c new file mode 100644 index 0000000..736438b --- /dev/null +++ b/contrib/binutils/bfd/elf32-sparc.c @@ -0,0 +1,1863 @@ +/* SPARC-specific support for 32-bit ELF + Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "elf-bfd.h" +#include "elf/sparc.h" + +static reloc_howto_type *elf32_sparc_reloc_type_lookup + PARAMS ((bfd *, bfd_reloc_code_real_type)); +static void elf32_sparc_info_to_howto + PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); +static boolean elf32_sparc_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); +static boolean elf32_sparc_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static boolean elf32_sparc_adjust_dynindx + PARAMS ((struct elf_link_hash_entry *, PTR)); +static boolean elf32_sparc_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf32_sparc_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); +static boolean elf32_sparc_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *)); +static boolean elf32_sparc_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf32_sparc_merge_private_bfd_data PARAMS ((bfd *, bfd *)); +static boolean elf32_sparc_object_p + PARAMS ((bfd *)); +static void elf32_sparc_final_write_processing + PARAMS ((bfd *, boolean)); + +/* The relocation "howto" table. */ + +static bfd_reloc_status_type sparc_elf_notsupported_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type sparc_elf_wdisp16_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); + +reloc_howto_type _bfd_sparc_elf_howto_table[] = +{ + HOWTO(R_SPARC_NONE, 0,0, 0,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_NONE", false,0,0x00000000,true), + HOWTO(R_SPARC_8, 0,0, 8,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_8", false,0,0x000000ff,true), + HOWTO(R_SPARC_16, 0,1,16,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_16", false,0,0x0000ffff,true), + HOWTO(R_SPARC_32, 0,2,32,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_32", false,0,0xffffffff,true), + HOWTO(R_SPARC_DISP8, 0,0, 8,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP8", false,0,0x000000ff,true), + HOWTO(R_SPARC_DISP16, 0,1,16,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP16", false,0,0x0000ffff,true), + HOWTO(R_SPARC_DISP32, 0,2,32,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP32", false,0,0x00ffffff,true), + HOWTO(R_SPARC_WDISP30, 2,2,30,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP30", false,0,0x3fffffff,true), + HOWTO(R_SPARC_WDISP22, 2,2,22,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP22", false,0,0x003fffff,true), + HOWTO(R_SPARC_HI22, 10,2,22,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_HI22", false,0,0x003fffff,true), + HOWTO(R_SPARC_22, 0,2,22,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_22", false,0,0x003fffff,true), + HOWTO(R_SPARC_13, 0,2,13,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_13", false,0,0x00001fff,true), + HOWTO(R_SPARC_LO10, 0,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_LO10", false,0,0x000003ff,true), + HOWTO(R_SPARC_GOT10, 0,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOT10", false,0,0x000003ff,true), + HOWTO(R_SPARC_GOT13, 0,2,13,false,0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_GOT13", false,0,0x00001fff,true), + HOWTO(R_SPARC_GOT22, 10,2,22,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOT22", false,0,0x003fffff,true), + HOWTO(R_SPARC_PC10, 0,2,10,true, 0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_PC10", false,0,0x000003ff,true), + HOWTO(R_SPARC_PC22, 10,2,22,true, 0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_PC22", false,0,0x003fffff,true), + HOWTO(R_SPARC_WPLT30, 2,2,30,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WPLT30", false,0,0x3fffffff,true), + HOWTO(R_SPARC_COPY, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_COPY", false,0,0x00000000,true), + HOWTO(R_SPARC_GLOB_DAT, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GLOB_DAT",false,0,0x00000000,true), + HOWTO(R_SPARC_JMP_SLOT, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_JMP_SLOT",false,0,0x00000000,true), + HOWTO(R_SPARC_RELATIVE, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_RELATIVE",false,0,0x00000000,true), + HOWTO(R_SPARC_UA32, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_UA32", false,0,0x00000000,true), + HOWTO(R_SPARC_PLT32, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PLT32", false,0,0x00000000,true), + HOWTO(R_SPARC_HIPLT22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_HIPLT22", false,0,0x00000000,true), + HOWTO(R_SPARC_LOPLT10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_LOPLT10", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT32, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PCPLT32", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PCPLT22", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PCPLT10", false,0,0x00000000,true), + HOWTO(R_SPARC_10, 0,2,10,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_10", false,0,0x000003ff,true), + HOWTO(R_SPARC_11, 0,2,11,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_11", false,0,0x000007ff,true), + /* These are for sparc64 in a 64 bit environment. + Values need to be here because the table is indexed by reloc number. */ + HOWTO(R_SPARC_64, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_64", false,0,0x00000000,true), + HOWTO(R_SPARC_OLO10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_OLO10", false,0,0x00000000,true), + HOWTO(R_SPARC_HH22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_HH22", false,0,0x00000000,true), + HOWTO(R_SPARC_HM10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_HM10", false,0,0x00000000,true), + HOWTO(R_SPARC_LM22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_LM22", false,0,0x00000000,true), + HOWTO(R_SPARC_PC_HH22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PC_HH22", false,0,0x00000000,true), + HOWTO(R_SPARC_PC_HM10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PC_HM10", false,0,0x00000000,true), + HOWTO(R_SPARC_PC_LM22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsupported_reloc, "R_SPARC_PC_LM22", false,0,0x00000000,true), + /* End sparc64 in 64 bit environment values. + The following are for sparc64 in a 32 bit environment. */ + HOWTO(R_SPARC_WDISP16, 2,2,16,true, 0,complain_overflow_signed, sparc_elf_wdisp16_reloc,"R_SPARC_WDISP16", false,0,0x00000000,true), + HOWTO(R_SPARC_WDISP19, 2,2,19,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP19", false,0,0x0007ffff,true), + HOWTO(R_SPARC_UNUSED_42, 0,0, 0,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_UNUSED_42",false,0,0x00000000,true), + HOWTO(R_SPARC_7, 0,2, 7,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_7", false,0,0x0000007f,true), + HOWTO(R_SPARC_5, 0,2, 5,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_5", false,0,0x0000001f,true), + HOWTO(R_SPARC_6, 0,2, 6,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_6", false,0,0x0000003f,true) +}; + +struct elf_reloc_map { + unsigned char bfd_reloc_val; + unsigned char elf_reloc_val; +}; + +static CONST struct elf_reloc_map sparc_reloc_map[] = +{ + { BFD_RELOC_NONE, R_SPARC_NONE, }, + { BFD_RELOC_16, R_SPARC_16, }, + { BFD_RELOC_8, R_SPARC_8 }, + { BFD_RELOC_8_PCREL, R_SPARC_DISP8 }, + { BFD_RELOC_CTOR, R_SPARC_32 }, + { BFD_RELOC_32, R_SPARC_32 }, + { BFD_RELOC_32_PCREL, R_SPARC_DISP32 }, + { BFD_RELOC_HI22, R_SPARC_HI22 }, + { BFD_RELOC_LO10, R_SPARC_LO10, }, + { BFD_RELOC_32_PCREL_S2, R_SPARC_WDISP30 }, + { BFD_RELOC_SPARC22, R_SPARC_22 }, + { BFD_RELOC_SPARC13, R_SPARC_13 }, + { BFD_RELOC_SPARC_GOT10, R_SPARC_GOT10 }, + { BFD_RELOC_SPARC_GOT13, R_SPARC_GOT13 }, + { BFD_RELOC_SPARC_GOT22, R_SPARC_GOT22 }, + { BFD_RELOC_SPARC_PC10, R_SPARC_PC10 }, + { BFD_RELOC_SPARC_PC22, R_SPARC_PC22 }, + { BFD_RELOC_SPARC_WPLT30, R_SPARC_WPLT30 }, + { BFD_RELOC_SPARC_COPY, R_SPARC_COPY }, + { BFD_RELOC_SPARC_GLOB_DAT, R_SPARC_GLOB_DAT }, + { BFD_RELOC_SPARC_JMP_SLOT, R_SPARC_JMP_SLOT }, + { BFD_RELOC_SPARC_RELATIVE, R_SPARC_RELATIVE }, + { BFD_RELOC_SPARC_WDISP22, R_SPARC_WDISP22 }, + /* ??? Doesn't dwarf use this? */ +/*{ BFD_RELOC_SPARC_UA32, R_SPARC_UA32 }, not used?? */ + {BFD_RELOC_SPARC_10, R_SPARC_10}, + {BFD_RELOC_SPARC_11, R_SPARC_11}, + {BFD_RELOC_SPARC_64, R_SPARC_64}, + {BFD_RELOC_SPARC_OLO10, R_SPARC_OLO10}, + {BFD_RELOC_SPARC_HH22, R_SPARC_HH22}, + {BFD_RELOC_SPARC_HM10, R_SPARC_HM10}, + {BFD_RELOC_SPARC_LM22, R_SPARC_LM22}, + {BFD_RELOC_SPARC_PC_HH22, R_SPARC_PC_HH22}, + {BFD_RELOC_SPARC_PC_HM10, R_SPARC_PC_HM10}, + {BFD_RELOC_SPARC_PC_LM22, R_SPARC_PC_LM22}, + {BFD_RELOC_SPARC_WDISP16, R_SPARC_WDISP16}, + {BFD_RELOC_SPARC_WDISP19, R_SPARC_WDISP19}, + {BFD_RELOC_SPARC_7, R_SPARC_7}, + {BFD_RELOC_SPARC_5, R_SPARC_5}, + {BFD_RELOC_SPARC_6, R_SPARC_6} +}; + +static reloc_howto_type * +elf32_sparc_reloc_type_lookup (abfd, code) + bfd *abfd; + bfd_reloc_code_real_type code; +{ + unsigned int i; + for (i = 0; i < sizeof (sparc_reloc_map) / sizeof (struct elf_reloc_map); i++) + { + if (sparc_reloc_map[i].bfd_reloc_val == code) + return &_bfd_sparc_elf_howto_table[(int) sparc_reloc_map[i].elf_reloc_val]; + } + return 0; +} + +/* We need to use ELF32_R_TYPE so we have our own copy of this function, + and elf64-sparc.c has its own copy. */ + +static void +elf32_sparc_info_to_howto (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf_Internal_Rela *dst; +{ + BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_SPARC_max); + cache_ptr->howto = &_bfd_sparc_elf_howto_table[ELF32_R_TYPE(dst->r_info)]; +} + +/* For unsupported relocs. */ + +static bfd_reloc_status_type +sparc_elf_notsupported_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + return bfd_reloc_notsupported; +} + +/* Handle the WDISP16 reloc. */ + +static bfd_reloc_status_type +sparc_elf_wdisp16_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma relocation; + bfd_vma x; + + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && (! reloc_entry->howto->partial_inplace + || reloc_entry->addend == 0)) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (output_bfd != NULL) + return bfd_reloc_continue; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + relocation = (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset); + relocation += reloc_entry->addend; + relocation -= (input_section->output_section->vma + + input_section->output_offset); + relocation -= reloc_entry->address; + + x = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + x |= ((((relocation >> 2) & 0xc000) << 6) + | ((relocation >> 2) & 0x3fff)); + bfd_put_32 (abfd, x, (bfd_byte *) data + reloc_entry->address); + + if ((bfd_signed_vma) relocation < - 0x40000 + || (bfd_signed_vma) relocation > 0x3ffff) + return bfd_reloc_overflow; + else + return bfd_reloc_ok; +} + +/* Functions for the SPARC ELF linker. */ + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ + +#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" + +/* The nop opcode we use. */ + +#define SPARC_NOP 0x01000000 + +/* The size in bytes of an entry in the procedure linkage table. */ + +#define PLT_ENTRY_SIZE 12 + +/* The first four entries in a procedure linkage table are reserved, + and the initial contents are unimportant (we zero them out). + Subsequent entries look like this. See the SVR4 ABI SPARC + supplement to see how this works. */ + +/* sethi %hi(.-.plt0),%g1. We fill in the address later. */ +#define PLT_ENTRY_WORD0 0x03000000 +/* b,a .plt0. We fill in the offset later. */ +#define PLT_ENTRY_WORD1 0x30800000 +/* nop. */ +#define PLT_ENTRY_WORD2 SPARC_NOP + +/* Look through the relocs for a section during the first phase, and + allocate space in the global offset table or procedure linkage + table. */ + +static boolean +elf32_sparc_check_relocs (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_vma *local_got_offsets; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + asection *sgot; + asection *srelgot; + asection *sreloc; + + if (info->relocateable) + return true; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_offsets = elf_local_got_offsets (abfd); + + sgot = NULL; + srelgot = NULL; + sreloc = NULL; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + /* This symbol requires a global offset table entry. */ + + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = abfd; + if (! _bfd_elf_create_got_section (dynobj, info)) + return false; + } + + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } + + if (srelgot == NULL + && (h != NULL || info->shared)) + { + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (srelgot == NULL) + { + srelgot = bfd_make_section (dynobj, ".rela.got"); + if (srelgot == NULL + || ! bfd_set_section_flags (dynobj, srelgot, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, srelgot, 2)) + return false; + } + } + + if (h != NULL) + { + if (h->got_offset != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + h->got_offset = sgot->_raw_size; + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + srelgot->_raw_size += sizeof (Elf32_External_Rela); + } + else + { + /* This is a global offset table entry for a local + symbol. */ + if (local_got_offsets == NULL) + { + size_t size; + register unsigned int i; + + size = symtab_hdr->sh_info * sizeof (bfd_vma); + local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size); + if (local_got_offsets == NULL) + return false; + elf_local_got_offsets (abfd) = local_got_offsets; + for (i = 0; i < symtab_hdr->sh_info; i++) + local_got_offsets[i] = (bfd_vma) -1; + } + if (local_got_offsets[r_symndx] != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + local_got_offsets[r_symndx] = sgot->_raw_size; + + if (info->shared) + { + /* If we are generating a shared object, we need to + output a R_SPARC_RELATIVE reloc so that the + dynamic linker can adjust this GOT entry. */ + srelgot->_raw_size += sizeof (Elf32_External_Rela); + } + } + + sgot->_raw_size += 4; + + /* If the .got section is more than 0x1000 bytes, we add + 0x1000 to the value of _GLOBAL_OFFSET_TABLE_, so that 13 + bit relocations have a greater chance of working. */ + if (sgot->_raw_size >= 0x1000 + && elf_hash_table (info)->hgot->root.u.def.value == 0) + elf_hash_table (info)->hgot->root.u.def.value = 0x1000; + + break; + + case R_SPARC_WPLT30: + /* This symbol requires a procedure linkage table entry. We + actually build the entry in adjust_dynamic_symbol, + because this might be a case of linking PIC code without + linking in any dynamic objects, in which case we don't + need to generate a procedure linkage table after all. */ + + if (h == NULL) + { + /* It does not make sense to have a procedure linkage + table entry for a local symbol. */ + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + + break; + + case R_SPARC_PC10: + case R_SPARC_PC22: + if (h != NULL + && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + break; + /* Fall through. */ + case R_SPARC_DISP8: + case R_SPARC_DISP16: + case R_SPARC_DISP32: + case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_WDISP19: + case R_SPARC_WDISP16: + /* If we are linking with -Bsymbolic, we do not need to copy + a PC relative reloc against a global symbol which is + defined in an object we are including in the link (i.e., + DEF_REGULAR is set). FIXME: At this point we have not + seen all the input files, so it is possible that + DEF_REGULAR is not set now but will be set later (it is + never cleared). This needs to be handled as in + elf32-i386.c. */ + if (h == NULL + || (info->symbolic + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) != 0)) + break; + /* Fall through. */ + case R_SPARC_8: + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_HI22: + case R_SPARC_22: + case R_SPARC_13: + case R_SPARC_LO10: + case R_SPARC_UA32: + if (info->shared) + { + /* When creating a shared object, we must copy these + relocs into the output file. We create a reloc + section in dynobj and make room for the reloc. */ + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (abfd, + elf_elfheader (abfd)->e_shstrndx, + elf_section_data (sec)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (abfd, sec), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + if (sreloc == NULL) + { + flagword flags; + + sreloc = bfd_make_section (dynobj, name); + flags = (SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if ((sec->flags & SEC_ALLOC) != 0) + flags |= SEC_ALLOC | SEC_LOAD; + if (sreloc == NULL + || ! bfd_set_section_flags (dynobj, sreloc, flags) + || ! bfd_set_section_alignment (dynobj, sreloc, 2)) + return false; + } + } + + sreloc->_raw_size += sizeof (Elf32_External_Rela); + } + + break; + + default: + break; + } + } + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +elf32_sparc_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + bfd *dynobj; + asection *s; + unsigned int power_of_two; + + dynobj = elf_hash_table (info)->dynobj; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) + || h->weakdef != NULL + || ((h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_REF_REGULAR) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0))); + + /* If this is a function, put it in the procedure linkage table. We + will fill in the contents of the procedure linkage table later + (although we could actually do it here). The STT_NOTYPE + condition is a hack specifically for the Oracle libraries + delivered for Solaris; for some inexplicable reason, they define + some of their functions as STT_NOTYPE when they really should be + STT_FUNC. */ + if (h->type == STT_FUNC + || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0 + || (h->type == STT_NOTYPE + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section->flags & SEC_CODE) != 0)) + { + if (! elf_hash_table (info)->dynamic_sections_created + || ((!info->shared || info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) != 0)) + { + /* This case can occur if we saw a WPLT30 reloc in an input + file, but none of the input files were dynamic objects. + Or, when linking the main application or a -Bsymbolic + shared library against PIC code. Or when a global symbol + has been made private, e.g. via versioning. + + In these cases we know what value the symbol will resolve + to, so we don't actually need to build a procedure linkage + table, and we can just do a WDISP30 reloc instead. */ + + h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; + return true; + } + + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + + /* The first four entries in .plt are reserved. */ + if (s->_raw_size == 0) + s->_raw_size = 4 * PLT_ENTRY_SIZE; + + /* The procedure linkage table has a maximum size. */ + if (s->_raw_size >= 0x400000) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* If this symbol is not defined in a regular file, and we are + not generating a shared library, then set the symbol to this + location in the .plt. This is required to make function + pointers compare as equal between the normal executable and + the shared library. */ + if (! info->shared + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + } + + h->plt_offset = s->_raw_size; + + /* Make room for this entry. */ + s->_raw_size += PLT_ENTRY_SIZE; + + /* We also need to make an entry in the .rela.plt section. */ + + s = bfd_get_section_by_name (dynobj, ".rela.plt"); + BFD_ASSERT (s != NULL); + s->_raw_size += sizeof (Elf32_External_Rela); + + return true; + } + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined + || h->weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->weakdef->root.u.def.value; + return true; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + /* If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (info->shared) + return true; + + /* We must allocate the symbol in our .dynbss section, which will + become part of the .bss section of the executable. There will be + an entry for this symbol in the .dynsym section. The dynamic + object will contain position independent code, so all references + from the dynamic object to this symbol will go through the global + offset table. The dynamic linker will use the .dynsym entry to + determine the address it must put in the global offset table, so + both the dynamic object and the regular object will refer to the + same memory location for the variable. */ + + s = bfd_get_section_by_name (dynobj, ".dynbss"); + BFD_ASSERT (s != NULL); + + /* We must generate a R_SPARC_COPY reloc to tell the dynamic linker + to copy the initial value out of the dynamic object and into the + runtime process image. We need to remember the offset into the + .rel.bss section we are going to use. */ + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + asection *srel; + + srel = bfd_get_section_by_name (dynobj, ".rela.bss"); + BFD_ASSERT (srel != NULL); + srel->_raw_size += sizeof (Elf32_External_Rela); + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY; + } + + /* We need to figure out the alignment required for this symbol. I + have no idea how ELF linkers handle this. */ + power_of_two = bfd_log2 (h->size); + if (power_of_two > 3) + power_of_two = 3; + + /* Apply the required alignment. */ + s->_raw_size = BFD_ALIGN (s->_raw_size, + (bfd_size_type) (1 << power_of_two)); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (! bfd_set_section_alignment (dynobj, s, power_of_two)) + return false; + } + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* Increment the section size to make room for the symbol. */ + s->_raw_size += h->size; + + return true; +} + +/* Set the sizes of the dynamic sections. */ + +static boolean +elf32_sparc_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s; + boolean reltext; + boolean relplt; + + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + + /* Make space for the trailing nop in .plt. */ + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + if (s->_raw_size > 0) + s->_raw_size += 4; + } + else + { + /* We may have created entries in the .rela.got section. + However, if we are not creating the dynamic sections, we will + not actually use these entries. Reset the size of .rela.got, + which will cause it to get stripped from the output file + below. */ + s = bfd_get_section_by_name (dynobj, ".rela.got"); + if (s != NULL) + s->_raw_size = 0; + } + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + reltext = false; + relplt = false; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + boolean strip; + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + strip = false; + + if (strncmp (name, ".rela", 5) == 0) + { + if (s->_raw_size == 0) + { + /* If we don't need this section, strip it from the + output file. This is to handle .rela.bss and + .rel.plt. We must create it in + create_dynamic_sections, because it must be created + before the linker maps input sections to output + sections. The linker does that before + adjust_dynamic_symbol is called, and it is that + function which decides whether anything needs to go + into these sections. */ + strip = true; + } + else + { + const char *outname; + asection *target; + + /* If this relocation section applies to a read only + section, then we probably need a DT_TEXTREL entry. */ + outname = bfd_get_section_name (output_bfd, + s->output_section); + target = bfd_get_section_by_name (output_bfd, outname + 5); + if (target != NULL + && (target->flags & SEC_READONLY) != 0 + && (target->flags & SEC_ALLOC) != 0) + reltext = true; + + if (strcmp (name, ".rela.plt") == 0) + relplt = true; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + s->reloc_count = 0; + } + } + else if (strcmp (name, ".plt") != 0 + && strcmp (name, ".got") != 0) + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (strip) + { + asection **spp; + + for (spp = &s->output_section->owner->sections; + *spp != s->output_section; + spp = &(*spp)->next) + ; + *spp = s->output_section->next; + --s->output_section->owner->section_count; + + continue; + } + + /* Allocate memory for the section contents. */ + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + return false; + } + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in elf32_sparc_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ + if (! info->shared) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0)) + return false; + } + + if (relplt) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_PLTRELSZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_PLTREL, DT_RELA) + || ! bfd_elf32_add_dynamic_entry (info, DT_JMPREL, 0)) + return false; + } + + if (! bfd_elf32_add_dynamic_entry (info, DT_RELA, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELASZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELAENT, + sizeof (Elf32_External_Rela))) + return false; + + if (reltext) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_TEXTREL, 0)) + return false; + } + } + + /* If we are generating a shared library, we generate a section + symbol for each output section for which we might need to copy + relocs. These are local symbols, which means that they must come + first in the dynamic symbol table. That means we must increment + the dynamic symbol index of every other dynamic symbol. */ + if (info->shared) + { + int c; + + c = 0; + for (s = output_bfd->sections; s != NULL; s = s->next) + { + if ((s->flags & SEC_LINKER_CREATED) != 0 + || (s->flags & SEC_ALLOC) == 0) + continue; + + elf_section_data (s)->dynindx = c + 1; + + /* These symbols will have no names, so we don't need to + fiddle with dynstr_index. */ + + ++c; + } + + elf_link_hash_traverse (elf_hash_table (info), + elf32_sparc_adjust_dynindx, + (PTR) &c); + elf_hash_table (info)->dynsymcount += c; + } + + return true; +} + +/* Increment the index of a dynamic symbol by a given amount. Called + via elf_link_hash_traverse. */ + +static boolean +elf32_sparc_adjust_dynindx (h, cparg) + struct elf_link_hash_entry *h; + PTR cparg; +{ + int *cp = (int *) cparg; + + if (h->dynindx != -1) + h->dynindx += *cp; + return true; +} + +/* Relocate a SPARC ELF section. */ + +static boolean +elf32_sparc_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_vma *local_got_offsets; + bfd_vma got_base; + asection *sgot; + asection *splt; + asection *sreloc; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + local_got_offsets = elf_local_got_offsets (input_bfd); + + if (elf_hash_table (info)->hgot == NULL) + got_base = 0; + else + got_base = elf_hash_table (info)->hgot->root.u.def.value; + + sgot = NULL; + splt = NULL; + sreloc = NULL; + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sec; + bfd_vma relocation; + bfd_reloc_status_type r; + + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type < 0 || r_type >= (int) R_SPARC_max) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + howto = _bfd_sparc_elf_howto_table + r_type; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + rel->r_addend += sec->output_offset + sym->st_value; + } + } + + continue; + } + + /* This is a final link. */ + h = NULL; + sym = NULL; + sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = (sec->output_section->vma + + sec->output_offset + + sym->st_value); + } + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sec = h->root.u.def.section; + if ((r_type == R_SPARC_WPLT30 + && h->plt_offset != (bfd_vma) -1) + || ((r_type == R_SPARC_GOT10 + || r_type == R_SPARC_GOT13 + || r_type == R_SPARC_GOT22) + && elf_hash_table (info)->dynamic_sections_created + && (! info->shared + || (! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + || (info->shared + && ((! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0) + && (r_type == R_SPARC_8 + || r_type == R_SPARC_16 + || r_type == R_SPARC_32 + || r_type == R_SPARC_DISP8 + || r_type == R_SPARC_DISP16 + || r_type == R_SPARC_DISP32 + || r_type == R_SPARC_WDISP30 + || r_type == R_SPARC_WDISP22 + || r_type == R_SPARC_WDISP19 + || r_type == R_SPARC_WDISP16 + || r_type == R_SPARC_HI22 + || r_type == R_SPARC_22 + || r_type == R_SPARC_13 + || r_type == R_SPARC_LO10 + || r_type == R_SPARC_UA32 + || ((r_type == R_SPARC_PC10 + || r_type == R_SPARC_PC22) + && strcmp (h->root.root.string, + "_GLOBAL_OFFSET_TABLE_") != 0)))) + { + /* In these cases, we don't need the relocation + value. We check specially because in some + obscure cases sec->output_section will be NULL. */ + relocation = 0; + } + else + relocation = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else if (h->root.type == bfd_link_hash_undefweak) + relocation = 0; + else if (info->shared && !info->symbolic) + relocation = 0; + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset))) + return false; + relocation = 0; + } + } + + switch (r_type) + { + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + /* Relocation is to the entry for this symbol in the global + offset table. */ + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } + + if (h != NULL) + { + bfd_vma off; + + off = h->got_offset; + BFD_ASSERT (off != (bfd_vma) -1); + + if (! elf_hash_table (info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))) + { + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file. We must initialize + this entry in the global offset table. Since the + offset must always be a multiple of 4, we use the + least significant bit to record whether we have + initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + sgot->contents + off); + h->got_offset |= 1; + } + } + + relocation = sgot->output_offset + off - got_base; + } + else + { + bfd_vma off; + + BFD_ASSERT (local_got_offsets != NULL + && local_got_offsets[r_symndx] != (bfd_vma) -1); + + off = local_got_offsets[r_symndx]; + + /* The offset must always be a multiple of 4. We use + the least significant bit to record whether we have + already processed this entry. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, sgot->contents + off); + + if (info->shared) + { + asection *srelgot; + Elf_Internal_Rela outrel; + + /* We need to generate a R_SPARC_RELATIVE reloc + for the dynamic linker. */ + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + off); + outrel.r_info = ELF32_R_INFO (0, R_SPARC_RELATIVE); + outrel.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + (((Elf32_External_Rela *) + srelgot->contents) + + srelgot->reloc_count)); + ++srelgot->reloc_count; + } + + local_got_offsets[r_symndx] |= 1; + } + + relocation = sgot->output_offset + off - got_base; + } + + break; + + case R_SPARC_WPLT30: + /* Relocation is to the entry for this symbol in the + procedure linkage table. */ + BFD_ASSERT (h != NULL); + + if (h->plt_offset == (bfd_vma) -1) + { + /* We didn't make a PLT entry for this symbol. This + happens when statically linking PIC code, or when + using -Bsymbolic. */ + break; + } + + if (splt == NULL) + { + splt = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (splt != NULL); + } + + relocation = (splt->output_section->vma + + splt->output_offset + + h->plt_offset); + break; + + case R_SPARC_PC10: + case R_SPARC_PC22: + if (h != NULL + && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + break; + /* Fall through. */ + case R_SPARC_DISP8: + case R_SPARC_DISP16: + case R_SPARC_DISP32: + case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_WDISP19: + case R_SPARC_WDISP16: + if (h == NULL + || (info->symbolic + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) != 0)) + break; + /* Fall through. */ + case R_SPARC_8: + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_HI22: + case R_SPARC_22: + case R_SPARC_13: + case R_SPARC_LO10: + case R_SPARC_UA32: + if (info->shared) + { + Elf_Internal_Rela outrel; + boolean skip; + + /* When generating a shared object, these relocations + are copied into the output file to be resolved at run + time. */ + + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + skip = false; + + if (elf_section_data (input_section)->stab_info == NULL) + outrel.r_offset = rel->r_offset; + else + { + bfd_vma off; + + off = (_bfd_stab_section_offset + (output_bfd, &elf_hash_table (info)->stab_info, + input_section, + &elf_section_data (input_section)->stab_info, + rel->r_offset)); + if (off == (bfd_vma) -1) + skip = true; + outrel.r_offset = off; + } + + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + if (skip) + memset (&outrel, 0, sizeof outrel); + /* h->dynindx may be -1 if the symbol was marked to + become local. */ + else if (h != NULL + && ((! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + { + BFD_ASSERT (h->dynindx != -1); + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + else + { + if (r_type == R_SPARC_32) + { + outrel.r_info = ELF32_R_INFO (0, R_SPARC_RELATIVE); + outrel.r_addend = relocation + rel->r_addend; + } + else + { + long indx; + + if (h == NULL) + sec = local_sections[r_symndx]; + else + { + BFD_ASSERT (h->root.type == bfd_link_hash_defined + || (h->root.type + == bfd_link_hash_defweak)); + sec = h->root.u.def.section; + } + if (sec != NULL && bfd_is_abs_section (sec)) + indx = 0; + else if (sec == NULL || sec->owner == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + else + { + asection *osec; + + osec = sec->output_section; + indx = elf_section_data (osec)->dynindx; + + /* FIXME: we really should be able to link non-pic + shared libraries. */ + if (indx == 0) + { + BFD_FAIL (); + (*_bfd_error_handler) + ("%s: probably compiled without -fPIC?", + bfd_get_filename (input_bfd)); + bfd_set_error (bfd_error_bad_value); + return false; + } + } + + outrel.r_info = ELF32_R_INFO (indx, r_type); + outrel.r_addend = relocation + rel->r_addend; + } + } + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + (((Elf32_External_Rela *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + /* This reloc will be computed at runtime, so there's no + need to do anything now, unless this is a RELATIVE + reloc in an unallocated section. */ + if (skip + || (input_section->flags & SEC_ALLOC) != 0 + || ELF32_R_TYPE (outrel.r_info) != R_SPARC_RELATIVE) + continue; + } + break; + + default: + break; + } + + if (r_type != R_SPARC_WDISP16) + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + else + { + bfd_vma x; + + relocation += rel->r_addend; + relocation -= (input_section->output_section->vma + + input_section->output_offset); + relocation -= rel->r_offset; + + x = bfd_get_32 (input_bfd, contents + rel->r_offset); + x |= ((((relocation >> 2) & 0xc000) << 6) + | ((relocation >> 2) & 0x3fff)); + bfd_put_32 (input_bfd, x, contents + rel->r_offset); + + if ((bfd_signed_vma) relocation < - 0x40000 + || (bfd_signed_vma) relocation > 0x3ffff) + r = bfd_reloc_overflow; + else + r = bfd_reloc_ok; + } + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + { + const char *name; + + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (name == NULL) + return false; + if (*name == '\0') + name = bfd_section_name (input_bfd, sec); + } + if (! ((*info->callbacks->reloc_overflow) + (info, name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + break; + } + } + } + + return true; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static boolean +elf32_sparc_finish_dynamic_symbol (output_bfd, info, h, sym) + bfd *output_bfd; + struct bfd_link_info *info; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + bfd *dynobj; + + dynobj = elf_hash_table (info)->dynobj; + + if (h->plt_offset != (bfd_vma) -1) + { + asection *splt; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the procedure linkage table. Set + it up. */ + + BFD_ASSERT (h->dynindx != -1); + + splt = bfd_get_section_by_name (dynobj, ".plt"); + srela = bfd_get_section_by_name (dynobj, ".rela.plt"); + BFD_ASSERT (splt != NULL && srela != NULL); + + /* Fill in the entry in the procedure linkage table. */ + bfd_put_32 (output_bfd, + PLT_ENTRY_WORD0 + h->plt_offset, + splt->contents + h->plt_offset); + bfd_put_32 (output_bfd, + (PLT_ENTRY_WORD1 + + (((- (h->plt_offset + 4)) >> 2) & 0x3fffff)), + splt->contents + h->plt_offset + 4); + bfd_put_32 (output_bfd, PLT_ENTRY_WORD2, + splt->contents + h->plt_offset + 8); + + /* Fill in the entry in the .rela.plt section. */ + rela.r_offset = (splt->output_section->vma + + splt->output_offset + + h->plt_offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_SPARC_JMP_SLOT); + rela.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rela, + ((Elf32_External_Rela *) srela->contents + + h->plt_offset / PLT_ENTRY_SIZE - 4)); + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + } + } + + if (h->got_offset != (bfd_vma) -1) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the global offset table. Set it + up. */ + + sgot = bfd_get_section_by_name (dynobj, ".got"); + srela = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (sgot != NULL && srela != NULL); + + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + + (h->got_offset &~ 1)); + + /* If this is a -Bsymbolic link, and the symbol is defined + locally, we just want to emit a RELATIVE reloc. Likewise if + the symbol was forced to be local because of a version file. + The entry in the global offset table will already have been + initialized in the relocate_section function. */ + if (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)) + rela.r_info = ELF32_R_INFO (0, R_SPARC_RELATIVE); + else + { + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got_offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_SPARC_GLOB_DAT); + } + + rela.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rela, + ((Elf32_External_Rela *) srela->contents + + srela->reloc_count)); + ++srela->reloc_count; + } + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0) + { + asection *s; + Elf_Internal_Rela rela; + + /* This symbols needs a copy reloc. Set it up. */ + + BFD_ASSERT (h->dynindx != -1); + + s = bfd_get_section_by_name (h->root.u.def.section->owner, + ".rela.bss"); + BFD_ASSERT (s != NULL); + + rela.r_offset = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_SPARC_COPY); + rela.r_addend = 0; + bfd_elf32_swap_reloca_out (output_bfd, &rela, + ((Elf32_External_Rela *) s->contents + + s->reloc_count)); + ++s->reloc_count; + } + + /* Mark some specially defined symbols as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0 + || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + + return true; +} + +/* Finish up the dynamic sections. */ + +static boolean +elf32_sparc_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *sdyn; + asection *sgot; + + dynobj = elf_hash_table (info)->dynobj; + + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + + if (elf_hash_table (info)->dynamic_sections_created) + { + asection *splt; + Elf32_External_Dyn *dyncon, *dynconend; + + splt = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (splt != NULL && sdyn != NULL); + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + boolean size; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + case DT_PLTGOT: name = ".plt"; size = false; break; + case DT_PLTRELSZ: name = ".rela.plt"; size = true; break; + case DT_JMPREL: name = ".rela.plt"; size = false; break; + default: name = NULL; size = false; break; + } + + if (name != NULL) + { + asection *s; + + s = bfd_get_section_by_name (output_bfd, name); + if (s == NULL) + dyn.d_un.d_val = 0; + else + { + if (! size) + dyn.d_un.d_ptr = s->vma; + else + { + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size; + else + dyn.d_un.d_val = s->_raw_size; + } + } + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + } + } + + /* Clear the first four entries in the procedure linkage table, + and put a nop in the last four bytes. */ + if (splt->_raw_size > 0) + { + memset (splt->contents, 0, 4 * PLT_ENTRY_SIZE); + bfd_put_32 (output_bfd, SPARC_NOP, + splt->contents + splt->_raw_size - 4); + } + + elf_section_data (splt->output_section)->this_hdr.sh_entsize = + PLT_ENTRY_SIZE; + } + + /* Set the first entry in the global offset table to the address of + the dynamic section. */ + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + if (sgot->_raw_size > 0) + { + if (sdyn == NULL) + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); + else + bfd_put_32 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgot->contents); + } + + elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; + + if (info->shared) + { + asection *sdynsym; + asection *s; + Elf_Internal_Sym sym; + int c; + + /* Set up the section symbols for the output sections. */ + + sdynsym = bfd_get_section_by_name (dynobj, ".dynsym"); + BFD_ASSERT (sdynsym != NULL); + + sym.st_size = 0; + sym.st_name = 0; + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); + sym.st_other = 0; + + c = 0; + for (s = output_bfd->sections; s != NULL; s = s->next) + { + int indx; + + if (elf_section_data (s)->dynindx == 0) + continue; + + sym.st_value = s->vma; + + indx = elf_section_data (s)->this_idx; + BFD_ASSERT (indx > 0); + sym.st_shndx = indx; + + bfd_elf32_swap_symbol_out (output_bfd, &sym, + (PTR) (((Elf32_External_Sym *) + sdynsym->contents) + + elf_section_data (s)->dynindx)); + + ++c; + } + + /* Set the sh_info field of the output .dynsym section to the + index of the first global symbol. */ + elf_section_data (sdynsym->output_section)->this_hdr.sh_info = c + 1; + } + + return true; +} + +/* Functions for dealing with the e_flags field. + + We don't define set_private_flags or copy_private_bfd_data because + the only currently defined values are based on the bfd mach number, + so we use the latter instead and defer setting e_flags until the + file is written out. */ + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +static boolean +elf32_sparc_merge_private_bfd_data (ibfd, obfd) + bfd *ibfd; + bfd *obfd; +{ + boolean error; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return true; + + error = false; + +#if 0 + /* ??? The native linker doesn't do this so we can't (otherwise gcc would + have to know which linker is being used). Instead, the native linker + bumps up the architecture level when it has to. However, I still think + warnings like these are good, so it would be nice to have them turned on + by some option. */ + + /* If the output machine is normal sparc, we can't allow v9 input files. */ + if (bfd_get_mach (obfd) == bfd_mach_sparc + && (bfd_get_mach (ibfd) == bfd_mach_sparc_v8plus + || bfd_get_mach (ibfd) == bfd_mach_sparc_v8plusa)) + { + error = true; + (*_bfd_error_handler) + ("%s: compiled for a v8plus system and target is v8", + bfd_get_filename (ibfd)); + } + /* If the output machine is v9, we can't allow v9+vis input files. */ + if (bfd_get_mach (obfd) == bfd_mach_sparc_v8plus + && bfd_get_mach (ibfd) == bfd_mach_sparc_v8plusa) + { + error = true; + (*_bfd_error_handler) + ("%s: compiled for a v8plusa system and target is v8plus", + bfd_get_filename (ibfd)); + } +#else + if (bfd_get_mach (ibfd) >= bfd_mach_sparc_v9) + { + error = true; + (*_bfd_error_handler) + ("%s: compiled for a 64 bit system and target is 32 bit", + bfd_get_filename (ibfd)); + } + else if (bfd_get_mach (obfd) < bfd_get_mach (ibfd)) + bfd_set_arch_mach (obfd, bfd_arch_sparc, bfd_get_mach (ibfd)); +#endif + + if (error) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + return true; +} + +/* Set the right machine number. */ + +static boolean +elf32_sparc_object_p (abfd) + bfd *abfd; +{ + if (elf_elfheader (abfd)->e_machine == EM_SPARC32PLUS) + { + if (elf_elfheader (abfd)->e_flags & EF_SPARC_SUN_US1) + return bfd_default_set_arch_mach (abfd, bfd_arch_sparc, + bfd_mach_sparc_v8plusa); + else if (elf_elfheader (abfd)->e_flags & EF_SPARC_32PLUS) + return bfd_default_set_arch_mach (abfd, bfd_arch_sparc, + bfd_mach_sparc_v8plus); + else + return false; + } + else + return bfd_default_set_arch_mach (abfd, bfd_arch_sparc, bfd_mach_sparc); +} + +/* The final processing done just before writing out the object file. + We need to set the e_machine field appropriately. */ + +static void +elf32_sparc_final_write_processing (abfd, linker) + bfd *abfd; + boolean linker; +{ + switch (bfd_get_mach (abfd)) + { + case bfd_mach_sparc : + break; /* nothing to do */ + case bfd_mach_sparc_v8plus : + elf_elfheader (abfd)->e_machine = EM_SPARC32PLUS; + elf_elfheader (abfd)->e_flags &=~ EF_SPARC_32PLUS_MASK; + elf_elfheader (abfd)->e_flags |= EF_SPARC_32PLUS; + break; + case bfd_mach_sparc_v8plusa : + elf_elfheader (abfd)->e_machine = EM_SPARC32PLUS; + elf_elfheader (abfd)->e_flags &=~ EF_SPARC_32PLUS_MASK; + elf_elfheader (abfd)->e_flags |= EF_SPARC_32PLUS | EF_SPARC_SUN_US1; + break; + default : + abort (); + } +} + +#define TARGET_BIG_SYM bfd_elf32_sparc_vec +#define TARGET_BIG_NAME "elf32-sparc" +#define ELF_ARCH bfd_arch_sparc +#define ELF_MACHINE_CODE EM_SPARC +#define ELF_MACHINE_ALT1 EM_SPARC32PLUS +#define ELF_MAXPAGESIZE 0x10000 + +#define bfd_elf32_bfd_reloc_type_lookup elf32_sparc_reloc_type_lookup +#define elf_info_to_howto elf32_sparc_info_to_howto +#define elf_backend_create_dynamic_sections \ + _bfd_elf_create_dynamic_sections +#define elf_backend_check_relocs elf32_sparc_check_relocs +#define elf_backend_adjust_dynamic_symbol \ + elf32_sparc_adjust_dynamic_symbol +#define elf_backend_size_dynamic_sections \ + elf32_sparc_size_dynamic_sections +#define elf_backend_relocate_section elf32_sparc_relocate_section +#define elf_backend_finish_dynamic_symbol \ + elf32_sparc_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + elf32_sparc_finish_dynamic_sections +#define bfd_elf32_bfd_merge_private_bfd_data \ + elf32_sparc_merge_private_bfd_data +#define elf_backend_object_p elf32_sparc_object_p +#define elf_backend_final_write_processing \ + elf32_sparc_final_write_processing +#define elf_backend_want_got_plt 0 +#define elf_backend_plt_readonly 0 +#define elf_backend_want_plt_sym 1 + +#include "elf32-target.h" diff --git a/contrib/binutils/bfd/elf64-sparc.c b/contrib/binutils/bfd/elf64-sparc.c new file mode 100644 index 0000000..85dabaa --- /dev/null +++ b/contrib/binutils/bfd/elf64-sparc.c @@ -0,0 +1,2263 @@ +/* SPARC-specific support for 64-bit ELF + Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "elf-bfd.h" + +/* This is defined if one wants to build upward compatible binaries + with the original sparc64-elf toolchain. The support is kept in for + now but is turned off by default. dje 970930 */ +/*#define SPARC64_OLD_RELOCS*/ + +#include "elf/sparc.h" + +/* In case we're on a 32-bit machine, construct a 64-bit "-1" value. */ +#define MINUS_ONE (~ (bfd_vma) 0) + +static reloc_howto_type *sparc64_elf_reloc_type_lookup + PARAMS ((bfd *, bfd_reloc_code_real_type)); +static void sparc64_elf_info_to_howto + PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); + +static void sparc64_elf_build_plt + PARAMS((bfd *, unsigned char *, int)); +static bfd_vma sparc64_elf_plt_entry_offset + PARAMS((int)); +static bfd_vma sparc64_elf_plt_ptr_offset + PARAMS((int, int)); + +static boolean sparc64_elf_check_relocs + PARAMS((bfd *, struct bfd_link_info *, asection *sec, + const Elf_Internal_Rela *)); +static boolean sparc64_elf_adjust_dynamic_symbol + PARAMS((struct bfd_link_info *, struct elf_link_hash_entry *)); +static boolean sparc64_elf_size_dynamic_sections + PARAMS((bfd *, struct bfd_link_info *)); +static boolean sparc64_elf_adjust_dynindx + PARAMS((struct elf_link_hash_entry *, PTR)); + +static boolean sparc64_elf_merge_private_bfd_data + PARAMS ((bfd *, bfd *)); + +static boolean sparc64_elf_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); +static boolean sparc64_elf_object_p PARAMS ((bfd *)); + +/* The relocation "howto" table. */ + +static bfd_reloc_status_type sparc_elf_notsup_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type sparc_elf_wdisp16_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type sparc_elf_hix22_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type sparc_elf_lox10_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); + +static reloc_howto_type sparc64_elf_howto_table[] = +{ + HOWTO(R_SPARC_NONE, 0,0, 0,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_NONE", false,0,0x00000000,true), + HOWTO(R_SPARC_8, 0,0, 8,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_8", false,0,0x000000ff,true), + HOWTO(R_SPARC_16, 0,1,16,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_16", false,0,0x0000ffff,true), + HOWTO(R_SPARC_32, 0,2,32,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_32", false,0,0xffffffff,true), + HOWTO(R_SPARC_DISP8, 0,0, 8,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP8", false,0,0x000000ff,true), + HOWTO(R_SPARC_DISP16, 0,1,16,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP16", false,0,0x0000ffff,true), + HOWTO(R_SPARC_DISP32, 0,2,32,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP32", false,0,0x00ffffff,true), + HOWTO(R_SPARC_WDISP30, 2,2,30,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP30", false,0,0x3fffffff,true), + HOWTO(R_SPARC_WDISP22, 2,2,22,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP22", false,0,0x003fffff,true), + HOWTO(R_SPARC_HI22, 10,2,22,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_HI22", false,0,0x003fffff,true), + HOWTO(R_SPARC_22, 0,2,22,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_22", false,0,0x003fffff,true), + HOWTO(R_SPARC_13, 0,2,13,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_13", false,0,0x00001fff,true), + HOWTO(R_SPARC_LO10, 0,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_LO10", false,0,0x000003ff,true), + HOWTO(R_SPARC_GOT10, 0,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOT10", false,0,0x000003ff,true), + HOWTO(R_SPARC_GOT13, 0,2,13,false,0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_GOT13", false,0,0x00001fff,true), + HOWTO(R_SPARC_GOT22, 10,2,22,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOT22", false,0,0x003fffff,true), + HOWTO(R_SPARC_PC10, 0,2,10,true, 0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_PC10", false,0,0x000003ff,true), + HOWTO(R_SPARC_PC22, 10,2,22,true, 0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_PC22", false,0,0x003fffff,true), + HOWTO(R_SPARC_WPLT30, 2,2,30,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WPLT30", false,0,0x3fffffff,true), + HOWTO(R_SPARC_COPY, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_COPY", false,0,0x00000000,true), + HOWTO(R_SPARC_GLOB_DAT, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GLOB_DAT",false,0,0x00000000,true), + HOWTO(R_SPARC_JMP_SLOT, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_JMP_SLOT",false,0,0x00000000,true), + HOWTO(R_SPARC_RELATIVE, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_RELATIVE",false,0,0x00000000,true), + HOWTO(R_SPARC_UA32, 0,0,00,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_UA32", false,0,0x00000000,true), +#ifndef SPARC64_OLD_RELOCS + /* These aren't implemented yet. */ + HOWTO(R_SPARC_PLT32, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_PLT32", false,0,0x00000000,true), + HOWTO(R_SPARC_HIPLT22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_HIPLT22", false,0,0x00000000,true), + HOWTO(R_SPARC_LOPLT10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_LOPLT10", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT32, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_PCPLT32", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT22, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_PCPLT22", false,0,0x00000000,true), + HOWTO(R_SPARC_PCPLT10, 0,0,00,false,0,complain_overflow_dont, sparc_elf_notsup_reloc, "R_SPARC_PCPLT10", false,0,0x00000000,true), +#endif + HOWTO(R_SPARC_10, 0,2,10,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_10", false,0,0x000003ff,true), + HOWTO(R_SPARC_11, 0,2,11,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_11", false,0,0x000007ff,true), + HOWTO(R_SPARC_64, 0,4,64,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_64", false,0,MINUS_ONE, true), + HOWTO(R_SPARC_OLO10, 0,2,13,false,0,complain_overflow_signed, sparc_elf_notsup_reloc, "R_SPARC_OLO10", false,0,0x00001fff,true), + HOWTO(R_SPARC_HH22, 42,2,22,false,0,complain_overflow_unsigned,bfd_elf_generic_reloc, "R_SPARC_HH22", false,0,0x003fffff,true), + HOWTO(R_SPARC_HM10, 32,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_HM10", false,0,0x000003ff,true), + HOWTO(R_SPARC_LM22, 10,2,22,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_LM22", false,0,0x003fffff,true), + HOWTO(R_SPARC_PC_HH22, 42,2,22,true, 0,complain_overflow_unsigned,bfd_elf_generic_reloc, "R_SPARC_PC_HH22", false,0,0x003fffff,true), + HOWTO(R_SPARC_PC_HM10, 32,2,10,true, 0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_PC_HM10", false,0,0x000003ff,true), + HOWTO(R_SPARC_PC_LM22, 10,2,22,true, 0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_PC_LM22", false,0,0x003fffff,true), + HOWTO(R_SPARC_WDISP16, 2,2,16,true, 0,complain_overflow_signed, sparc_elf_wdisp16_reloc,"R_SPARC_WDISP16", false,0,0x00000000,true), + HOWTO(R_SPARC_WDISP19, 2,2,19,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_WDISP19", false,0,0x0007ffff,true), + HOWTO(R_SPARC_UNUSED_42, 0,0, 0,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_UNUSED_42",false,0,0x00000000,true), + HOWTO(R_SPARC_7, 0,2, 7,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_7", false,0,0x0000007f,true), + HOWTO(R_SPARC_5, 0,2, 5,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_5", false,0,0x0000001f,true), + HOWTO(R_SPARC_6, 0,2, 6,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_6", false,0,0x0000003f,true), + HOWTO(R_SPARC_DISP64, 0,4,64,true, 0,complain_overflow_signed, bfd_elf_generic_reloc, "R_SPARC_DISP64", false,0,MINUS_ONE, true), + HOWTO(R_SPARC_PLT64, 0,4,64,false,0,complain_overflow_bitfield,sparc_elf_notsup_reloc, "R_SPARC_PLT64", false,0,MINUS_ONE, false), + HOWTO(R_SPARC_HIX22, 0,4, 0,false,0,complain_overflow_bitfield,sparc_elf_hix22_reloc, "R_SPARC_HIX22", false,0,MINUS_ONE, false), + HOWTO(R_SPARC_LOX10, 0,4, 0,false,0,complain_overflow_dont, sparc_elf_lox10_reloc, "R_SPARC_LOX10", false,0,MINUS_ONE, false), + HOWTO(R_SPARC_H44, 22,2,22,false,0,complain_overflow_unsigned,bfd_elf_generic_reloc, "R_SPARC_H44", false,0,0x003fffff,false), + HOWTO(R_SPARC_M44, 12,2,10,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_M44", false,0,0x000003ff,false), + HOWTO(R_SPARC_L44, 0,2,13,false,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_L44", false,0,0x00000fff,false), + HOWTO(R_SPARC_REGISTER, 0,4, 0,false,0,complain_overflow_bitfield,sparc_elf_notsup_reloc, "R_SPARC_REGISTER",false,0,MINUS_ONE, false), + HOWTO(R_SPARC_UA64, 0,4,64,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_UA64", false,0,MINUS_ONE, true), + HOWTO(R_SPARC_UA16, 0,1,16,false,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_UA16", false,0,0x0000ffff,true) +}; + +struct elf_reloc_map { + unsigned char bfd_reloc_val; + unsigned char elf_reloc_val; +}; + +static CONST struct elf_reloc_map sparc_reloc_map[] = +{ + { BFD_RELOC_NONE, R_SPARC_NONE, }, + { BFD_RELOC_16, R_SPARC_16, }, + { BFD_RELOC_8, R_SPARC_8 }, + { BFD_RELOC_8_PCREL, R_SPARC_DISP8 }, + { BFD_RELOC_CTOR, R_SPARC_64 }, + { BFD_RELOC_32, R_SPARC_32 }, + { BFD_RELOC_32_PCREL, R_SPARC_DISP32 }, + { BFD_RELOC_HI22, R_SPARC_HI22 }, + { BFD_RELOC_LO10, R_SPARC_LO10, }, + { BFD_RELOC_32_PCREL_S2, R_SPARC_WDISP30 }, + { BFD_RELOC_SPARC22, R_SPARC_22 }, + { BFD_RELOC_SPARC13, R_SPARC_13 }, + { BFD_RELOC_SPARC_GOT10, R_SPARC_GOT10 }, + { BFD_RELOC_SPARC_GOT13, R_SPARC_GOT13 }, + { BFD_RELOC_SPARC_GOT22, R_SPARC_GOT22 }, + { BFD_RELOC_SPARC_PC10, R_SPARC_PC10 }, + { BFD_RELOC_SPARC_PC22, R_SPARC_PC22 }, + { BFD_RELOC_SPARC_WPLT30, R_SPARC_WPLT30 }, + { BFD_RELOC_SPARC_COPY, R_SPARC_COPY }, + { BFD_RELOC_SPARC_GLOB_DAT, R_SPARC_GLOB_DAT }, + { BFD_RELOC_SPARC_JMP_SLOT, R_SPARC_JMP_SLOT }, + { BFD_RELOC_SPARC_RELATIVE, R_SPARC_RELATIVE }, + { BFD_RELOC_SPARC_WDISP22, R_SPARC_WDISP22 }, + /* ??? Doesn't dwarf use this? */ +/*{ BFD_RELOC_SPARC_UA32, R_SPARC_UA32 }, not used?? */ + {BFD_RELOC_SPARC_10, R_SPARC_10}, + {BFD_RELOC_SPARC_11, R_SPARC_11}, + {BFD_RELOC_SPARC_64, R_SPARC_64}, + {BFD_RELOC_SPARC_OLO10, R_SPARC_OLO10}, + {BFD_RELOC_SPARC_HH22, R_SPARC_HH22}, + {BFD_RELOC_SPARC_HM10, R_SPARC_HM10}, + {BFD_RELOC_SPARC_LM22, R_SPARC_LM22}, + {BFD_RELOC_SPARC_PC_HH22, R_SPARC_PC_HH22}, + {BFD_RELOC_SPARC_PC_HM10, R_SPARC_PC_HM10}, + {BFD_RELOC_SPARC_PC_LM22, R_SPARC_PC_LM22}, + {BFD_RELOC_SPARC_WDISP16, R_SPARC_WDISP16}, + {BFD_RELOC_SPARC_WDISP19, R_SPARC_WDISP19}, + {BFD_RELOC_SPARC_7, R_SPARC_7}, + {BFD_RELOC_SPARC_5, R_SPARC_5}, + {BFD_RELOC_SPARC_6, R_SPARC_6}, + {BFD_RELOC_SPARC_DISP64, R_SPARC_DISP64}, + {BFD_RELOC_SPARC_PLT64, R_SPARC_PLT64}, + {BFD_RELOC_SPARC_HIX22, R_SPARC_HIX22}, + {BFD_RELOC_SPARC_LOX10, R_SPARC_LOX10}, + {BFD_RELOC_SPARC_H44, R_SPARC_H44}, + {BFD_RELOC_SPARC_M44, R_SPARC_M44}, + {BFD_RELOC_SPARC_L44, R_SPARC_L44}, + {BFD_RELOC_SPARC_REGISTER, R_SPARC_REGISTER} +}; + +static reloc_howto_type * +sparc64_elf_reloc_type_lookup (abfd, code) + bfd *abfd; + bfd_reloc_code_real_type code; +{ + unsigned int i; + for (i = 0; i < sizeof (sparc_reloc_map) / sizeof (struct elf_reloc_map); i++) + { + if (sparc_reloc_map[i].bfd_reloc_val == code) + return &sparc64_elf_howto_table[(int) sparc_reloc_map[i].elf_reloc_val]; + } + return 0; +} + +static void +sparc64_elf_info_to_howto (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf64_Internal_Rela *dst; +{ + BFD_ASSERT (ELF64_R_TYPE (dst->r_info) < (unsigned int) R_SPARC_max); + cache_ptr->howto = &sparc64_elf_howto_table[ELF64_R_TYPE (dst->r_info)]; +} + +/* Utility for performing the standard initial work of an instruction + relocation. + *PRELOCATION will contain the relocated item. + *PINSN will contain the instruction from the input stream. + If the result is `bfd_reloc_other' the caller can continue with + performing the relocation. Otherwise it must stop and return the + value to its caller. */ + +static bfd_reloc_status_type +init_insn_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + prelocation, + pinsn) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + bfd_vma *prelocation; + bfd_vma *pinsn; +{ + bfd_vma relocation; + reloc_howto_type *howto = reloc_entry->howto; + + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && (! howto->partial_inplace + || reloc_entry->addend == 0)) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + /* This works because partial_inplace == false. */ + if (output_bfd != NULL) + return bfd_reloc_continue; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + relocation = (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset); + relocation += reloc_entry->addend; + if (howto->pc_relative) + { + relocation -= (input_section->output_section->vma + + input_section->output_offset); + relocation -= reloc_entry->address; + } + + *prelocation = relocation; + *pinsn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + return bfd_reloc_other; +} + +/* For unsupported relocs. */ + +static bfd_reloc_status_type +sparc_elf_notsup_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + return bfd_reloc_notsupported; +} + +/* Handle the WDISP16 reloc. */ + +static bfd_reloc_status_type +sparc_elf_wdisp16_reloc (abfd, reloc_entry, symbol, data, input_section, + output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma relocation; + bfd_vma insn; + bfd_reloc_status_type status; + + status = init_insn_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, &relocation, &insn); + if (status != bfd_reloc_other) + return status; + + insn = (insn & ~0x303fff) | ((((relocation >> 2) & 0xc000) << 6) + | ((relocation >> 2) & 0x3fff)); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address); + + if ((bfd_signed_vma) relocation < - 0x40000 + || (bfd_signed_vma) relocation > 0x3ffff) + return bfd_reloc_overflow; + else + return bfd_reloc_ok; +} + +/* Handle the HIX22 reloc. */ + +static bfd_reloc_status_type +sparc_elf_hix22_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma relocation; + bfd_vma insn; + bfd_reloc_status_type status; + + status = init_insn_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, &relocation, &insn); + if (status != bfd_reloc_other) + return status; + + relocation ^= MINUS_ONE; + insn = (insn & ~0x3fffff) | ((relocation >> 10) & 0x3fffff); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address); + + if ((relocation & ~ (bfd_vma) 0xffffffff) != 0) + return bfd_reloc_overflow; + else + return bfd_reloc_ok; +} + +/* Handle the LOX10 reloc. */ + +static bfd_reloc_status_type +sparc_elf_lox10_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma relocation; + bfd_vma insn; + bfd_reloc_status_type status; + + status = init_insn_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, &relocation, &insn); + if (status != bfd_reloc_other) + return status; + + insn = (insn & ~0x1fff) | 0x1c00 | (relocation & 0x3ff); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address); + + return bfd_reloc_ok; +} + +/* PLT/GOT stuff */ + +/* Both the headers and the entries are icache aligned. */ +#define PLT_ENTRY_SIZE 32 +#define PLT_HEADER_SIZE (4 * PLT_ENTRY_SIZE) +#define LARGE_PLT_THRESHOLD 32768 +#define GOT_RESERVED_ENTRIES 1 + +#define ELF_DYNAMIC_INTERPRETER "/usr/lib/sparcv9/ld.so.1" + + +/* Fill in the .plt section. */ + +static void +sparc64_elf_build_plt (output_bfd, contents, nentries) + bfd *output_bfd; + unsigned char *contents; + int nentries; +{ + const unsigned int nop = 0x01000000; + int i, j; + + /* The first four entries are reserved, and are initially undefined. + We fill them with `illtrap 0' to force ld.so to do something. */ + + for (i = 0; i < PLT_HEADER_SIZE/4; ++i) + bfd_put_32 (output_bfd, 0, contents+i*4); + + /* The first 32768 entries are close enough to plt1 to get there via + a straight branch. */ + + for (i = 4; i < LARGE_PLT_THRESHOLD && i < nentries; ++i) + { + unsigned char *entry = contents + i * PLT_ENTRY_SIZE; + unsigned int sethi, ba; + + /* sethi (. - plt0), %g1 */ + sethi = 0x03000000 | (i * PLT_ENTRY_SIZE); + + /* ba,a,pt %icc, plt1 */ + ba = 0x30480000 | (((contents+PLT_ENTRY_SIZE) - (entry+4)) / 4 & 0x7ffff); + + bfd_put_32 (output_bfd, sethi, entry); + bfd_put_32 (output_bfd, ba, entry+4); + bfd_put_32 (output_bfd, nop, entry+8); + bfd_put_32 (output_bfd, nop, entry+12); + bfd_put_32 (output_bfd, nop, entry+16); + bfd_put_32 (output_bfd, nop, entry+20); + bfd_put_32 (output_bfd, nop, entry+24); + bfd_put_32 (output_bfd, nop, entry+28); + } + + /* Now the tricky bit. Entries 32768 and higher are grouped in blocks of + 160: 160 entries and 160 pointers. This is to separate code from data, + which is much friendlier on the cache. */ + + for (; i < nentries; i += 160) + { + int block = (i + 160 <= nentries ? 160 : nentries - i); + for (j = 0; j < block; ++j) + { + unsigned char *entry, *ptr; + unsigned int ldx; + + entry = contents + i*PLT_ENTRY_SIZE + j*4*6; + ptr = contents + i*PLT_ENTRY_SIZE + block*4*6 + j*8; + + /* ldx [%o7 + ptr - entry+4], %g1 */ + ldx = 0xc25be000 | ((ptr - entry+4) & 0x1fff); + + bfd_put_32 (output_bfd, 0x8a10000f, entry); /* mov %o7,%g5 */ + bfd_put_32 (output_bfd, 0x40000002, entry+4); /* call .+8 */ + bfd_put_32 (output_bfd, nop, entry+8); /* nop */ + bfd_put_32 (output_bfd, ldx, entry+12); /* ldx [%o7+P],%g1 */ + bfd_put_32 (output_bfd, 0x83c3c001, entry+16); /* jmpl %o7+%g1,%g1 */ + bfd_put_32 (output_bfd, 0x9e100005, entry+20); /* mov %g5,%o7 */ + + bfd_put_64 (output_bfd, contents - entry+4, ptr); + } + } +} + +/* Return the offset of a particular plt entry within the .plt section. */ + +static bfd_vma +sparc64_elf_plt_entry_offset (index) + int index; +{ + int block, ofs; + + if (index < LARGE_PLT_THRESHOLD) + return index * PLT_ENTRY_SIZE; + + /* See above for details. */ + + block = (index - LARGE_PLT_THRESHOLD) / 160; + ofs = (index - LARGE_PLT_THRESHOLD) % 160; + + return ((bfd_vma)(LARGE_PLT_THRESHOLD + block*160) * PLT_ENTRY_SIZE + + ofs * 6*4); +} + +static bfd_vma +sparc64_elf_plt_ptr_offset (index, max) + int index, max; +{ + int block, ofs, last; + + BFD_ASSERT(index >= LARGE_PLT_THRESHOLD); + + /* See above for details. */ + + block = (index - LARGE_PLT_THRESHOLD) / 160; + ofs = (index - LARGE_PLT_THRESHOLD) % 160; + last = (max - LARGE_PLT_THRESHOLD) % 160; + + return ((LARGE_PLT_THRESHOLD + block*160) * PLT_ENTRY_SIZE + + last * 6*4 + + ofs * 8); +} + + + +/* Look through the relocs for a section during the first phase, and + allocate space in the global offset table or procedure linkage + table. */ + +static boolean +sparc64_elf_check_relocs (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_vma *local_got_offsets; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + asection *sgot; + asection *srelgot; + asection *sreloc; + + if (info->relocateable || !(sec->flags & SEC_ALLOC)) + return true; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_offsets = elf_local_got_offsets (abfd); + + sgot = NULL; + srelgot = NULL; + sreloc = NULL; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h; + + r_symndx = ELF64_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + switch (ELF64_R_TYPE (rel->r_info)) + { + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + /* This symbol requires a global offset table entry. */ + + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = abfd; + if (! _bfd_elf_create_got_section (dynobj, info)) + return false; + } + + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } + + if (srelgot == NULL && (h != NULL || info->shared)) + { + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (srelgot == NULL) + { + srelgot = bfd_make_section (dynobj, ".rela.got"); + if (srelgot == NULL + || ! bfd_set_section_flags (dynobj, srelgot, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, srelgot, 3)) + return false; + } + } + + if (h != NULL) + { + if (h->got_offset != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + h->got_offset = sgot->_raw_size; + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf64_link_record_dynamic_symbol (info, h)) + return false; + } + + srelgot->_raw_size += sizeof (Elf64_External_Rela); + } + else + { + /* This is a global offset table entry for a local + symbol. */ + if (local_got_offsets == NULL) + { + size_t size; + register unsigned int i; + + size = symtab_hdr->sh_info * sizeof (bfd_vma); + local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size); + if (local_got_offsets == NULL) + return false; + elf_local_got_offsets (abfd) = local_got_offsets; + for (i = 0; i < symtab_hdr->sh_info; i++) + local_got_offsets[i] = (bfd_vma) -1; + } + if (local_got_offsets[r_symndx] != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + local_got_offsets[r_symndx] = sgot->_raw_size; + + if (info->shared) + { + /* If we are generating a shared object, we need to + output a R_SPARC_RELATIVE reloc so that the + dynamic linker can adjust this GOT entry. */ + srelgot->_raw_size += sizeof (Elf64_External_Rela); + } + } + + sgot->_raw_size += 8; + +#if 0 + /* Doesn't work for 64-bit -fPIC, since sethi/or builds + unsigned numbers. If we permit ourselves to modify + code so we get sethi/xor, this could work. + Question: do we consider conditionally re-enabling + this for -fpic, once we know about object code models? */ + /* If the .got section is more than 0x1000 bytes, we add + 0x1000 to the value of _GLOBAL_OFFSET_TABLE_, so that 13 + bit relocations have a greater chance of working. */ + if (sgot->_raw_size >= 0x1000 + && elf_hash_table (info)->hgot->root.u.def.value == 0) + elf_hash_table (info)->hgot->root.u.def.value = 0x1000; +#endif + + break; + + case R_SPARC_WPLT30: + case R_SPARC_PLT32: + case R_SPARC_HIPLT22: + case R_SPARC_LOPLT10: + case R_SPARC_PCPLT32: + case R_SPARC_PCPLT22: + case R_SPARC_PCPLT10: + case R_SPARC_PLT64: + /* This symbol requires a procedure linkage table entry. We + actually build the entry in adjust_dynamic_symbol, + because this might be a case of linking PIC code without + linking in any dynamic objects, in which case we don't + need to generate a procedure linkage table after all. */ + + if (h == NULL) + { + /* It does not make sense to have a procedure linkage + table entry for a local symbol. */ + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf64_link_record_dynamic_symbol (info, h)) + return false; + } + + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + break; + + case R_SPARC_PC10: + case R_SPARC_PC22: + case R_SPARC_PC_HH22: + case R_SPARC_PC_HM10: + case R_SPARC_PC_LM22: + if (h != NULL + && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + break; + /* Fall through. */ + case R_SPARC_DISP8: + case R_SPARC_DISP16: + case R_SPARC_DISP32: + case R_SPARC_DISP64: + case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_WDISP19: + case R_SPARC_WDISP16: + if (h == NULL) + break; + /* Fall through. */ + case R_SPARC_8: + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_HI22: + case R_SPARC_22: + case R_SPARC_13: + case R_SPARC_LO10: + case R_SPARC_UA32: + case R_SPARC_10: + case R_SPARC_11: + case R_SPARC_64: + case R_SPARC_OLO10: + case R_SPARC_HH22: + case R_SPARC_HM10: + case R_SPARC_LM22: + case R_SPARC_7: + case R_SPARC_5: + case R_SPARC_6: + case R_SPARC_HIX22: + case R_SPARC_LOX10: + case R_SPARC_H44: + case R_SPARC_M44: + case R_SPARC_L44: + case R_SPARC_UA64: + case R_SPARC_UA16: + /* When creating a shared object, we must copy these relocs + into the output file. We create a reloc section in + dynobj and make room for the reloc. + + But don't do this for debugging sections -- this shows up + with DWARF2 -- first because they are not loaded, and + second because DWARF sez the debug info is not to be + biased by the load address. */ + if (info->shared && (sec->flags & SEC_ALLOC)) + { + if (sreloc == NULL) + { + const char *name; + + name = (bfd_elf_string_from_elf_section + (abfd, + elf_elfheader (abfd)->e_shstrndx, + elf_section_data (sec)->rel_hdr.sh_name)); + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (abfd, sec), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + if (sreloc == NULL) + { + flagword flags; + + sreloc = bfd_make_section (dynobj, name); + flags = (SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if ((sec->flags & SEC_ALLOC) != 0) + flags |= SEC_ALLOC | SEC_LOAD; + if (sreloc == NULL + || ! bfd_set_section_flags (dynobj, sreloc, flags) + || ! bfd_set_section_alignment (dynobj, sreloc, 3)) + return false; + } + } + + sreloc->_raw_size += sizeof (Elf64_External_Rela); + } + break; + + case R_SPARC_REGISTER: + /* Nothing to do. */ + break; + + default: + (*_bfd_error_handler)("%s: check_relocs: unhandled reloc type %d", + bfd_get_filename(abfd), + ELF64_R_TYPE (rel->r_info)); + return false; + } + } + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +sparc64_elf_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + bfd *dynobj; + asection *s; + unsigned int power_of_two; + + dynobj = elf_hash_table (info)->dynobj; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) + || h->weakdef != NULL + || ((h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_REF_REGULAR) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0))); + + /* If this is a function, put it in the procedure linkage table. We + will fill in the contents of the procedure linkage table later + (although we could actually do it here). The STT_NOTYPE + condition is a hack specifically for the Oracle libraries + delivered for Solaris; for some inexplicable reason, they define + some of their functions as STT_NOTYPE when they really should be + STT_FUNC. */ + if (h->type == STT_FUNC + || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0 + || (h->type == STT_NOTYPE + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section->flags & SEC_CODE) != 0)) + { + if (! elf_hash_table (info)->dynamic_sections_created) + { + /* This case can occur if we saw a WPLT30 reloc in an input + file, but none of the input files were dynamic objects. + In such a case, we don't actually need to build a + procedure linkage table, and we can just do a WDISP30 + reloc instead. */ + BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0); + return true; + } + + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + + /* The first four bit in .plt is reserved. */ + if (s->_raw_size == 0) + s->_raw_size = PLT_HEADER_SIZE; + + /* If this symbol is not defined in a regular file, and we are + not generating a shared library, then set the symbol to this + location in the .plt. This is required to make function + pointers compare as equal between the normal executable and + the shared library. */ + if (! info->shared + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + } + + /* To simplify matters later, just store the plt index here. */ + h->plt_offset = s->_raw_size / PLT_ENTRY_SIZE; + + /* Make room for this entry. */ + s->_raw_size += PLT_ENTRY_SIZE; + + /* We also need to make an entry in the .rela.plt section. */ + + s = bfd_get_section_by_name (dynobj, ".rela.plt"); + BFD_ASSERT (s != NULL); + + /* The first plt entries are reserved, and the relocations must + pair up exactly. */ + if (s->_raw_size == 0) + s->_raw_size += (PLT_HEADER_SIZE/PLT_ENTRY_SIZE + * sizeof (Elf64_External_Rela)); + + s->_raw_size += sizeof (Elf64_External_Rela); + + /* The procedure linkage table size is bounded by the magnitude + of the offset we can describe in the entry. */ + if (s->_raw_size >= (bfd_vma)1 << 32) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + return true; + } + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined + || h->weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->weakdef->root.u.def.value; + return true; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + /* If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (info->shared) + return true; + + /* We must allocate the symbol in our .dynbss section, which will + become part of the .bss section of the executable. There will be + an entry for this symbol in the .dynsym section. The dynamic + object will contain position independent code, so all references + from the dynamic object to this symbol will go through the global + offset table. The dynamic linker will use the .dynsym entry to + determine the address it must put in the global offset table, so + both the dynamic object and the regular object will refer to the + same memory location for the variable. */ + + s = bfd_get_section_by_name (dynobj, ".dynbss"); + BFD_ASSERT (s != NULL); + + /* We must generate a R_SPARC_COPY reloc to tell the dynamic linker + to copy the initial value out of the dynamic object and into the + runtime process image. We need to remember the offset into the + .rel.bss section we are going to use. */ + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + asection *srel; + + srel = bfd_get_section_by_name (dynobj, ".rela.bss"); + BFD_ASSERT (srel != NULL); + srel->_raw_size += sizeof (Elf64_External_Rela); + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY; + } + + /* We need to figure out the alignment required for this symbol. I + have no idea how ELF linkers handle this. 16-bytes is the size + of the largest type that requires hard alignment -- long double. */ + power_of_two = bfd_log2 (h->size); + if (power_of_two > 4) + power_of_two = 4; + + /* Apply the required alignment. */ + s->_raw_size = BFD_ALIGN (s->_raw_size, + (bfd_size_type) (1 << power_of_two)); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (! bfd_set_section_alignment (dynobj, s, power_of_two)) + return false; + } + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* Increment the section size to make room for the symbol. */ + s->_raw_size += h->size; + + return true; +} + +/* Set the sizes of the dynamic sections. */ + +static boolean +sparc64_elf_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s; + boolean reltext; + boolean relplt; + + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + } + else + { + /* We may have created entries in the .rela.got section. + However, if we are not creating the dynamic sections, we will + not actually use these entries. Reset the size of .rela.got, + which will cause it to get stripped from the output file + below. */ + s = bfd_get_section_by_name (dynobj, ".rela.got"); + if (s != NULL) + s->_raw_size = 0; + } + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + reltext = false; + relplt = false; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + boolean strip; + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + strip = false; + + if (strncmp (name, ".rela", 5) == 0) + { + if (s->_raw_size == 0) + { + /* If we don't need this section, strip it from the + output file. This is to handle .rela.bss and + .rel.plt. We must create it in + create_dynamic_sections, because it must be created + before the linker maps input sections to output + sections. The linker does that before + adjust_dynamic_symbol is called, and it is that + function which decides whether anything needs to go + into these sections. */ + strip = true; + } + else + { + const char *outname; + asection *target; + + /* If this relocation section applies to a read only + section, then we probably need a DT_TEXTREL entry. */ + outname = bfd_get_section_name (output_bfd, + s->output_section); + target = bfd_get_section_by_name (output_bfd, outname + 5); + if (target != NULL + && (target->flags & SEC_READONLY) != 0) + reltext = true; + + if (strcmp (name, ".rela.plt") == 0) + relplt = true; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + s->reloc_count = 0; + } + } + else if (strcmp (name, ".plt") != 0 + && strncmp (name, ".got", 4) != 0) + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (strip) + { + asection **spp; + + for (spp = &s->output_section->owner->sections; + *spp != s->output_section; + spp = &(*spp)->next) + ; + *spp = s->output_section->next; + --s->output_section->owner->section_count; + + continue; + } + + /* Allocate memory for the section contents. Zero the memory + for the benefit of .rela.plt, which has 4 unused entries + at the beginning, and we don't want garbage. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + return false; + } + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in sparc64_elf_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ + if (! info->shared) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0)) + return false; + } + + if (relplt) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_PLTGOT, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_PLTRELSZ, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_PLTREL, DT_RELA) + || ! bfd_elf64_add_dynamic_entry (info, DT_JMPREL, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_SPARC_PLTFMT, + (info->shared != 0) + 1)) + return false; + } + + if (! bfd_elf64_add_dynamic_entry (info, DT_RELA, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_RELASZ, 0) + || ! bfd_elf64_add_dynamic_entry (info, DT_RELAENT, + sizeof (Elf64_External_Rela))) + return false; + + if (reltext) + { + if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0)) + return false; + } + } + + /* If we are generating a shared library, we generate a section + symbol for each output section for which we might need to copy + relocs. These are local symbols, which means that they must come + first in the dynamic symbol table. That means we must increment + the dynamic symbol index of every other dynamic symbol. */ + if (info->shared) + { + int c; + + c = 0; + for (s = output_bfd->sections; s != NULL; s = s->next) + { + if ((s->flags & SEC_LINKER_CREATED) != 0 + || (s->flags & SEC_ALLOC) == 0) + continue; + + elf_section_data (s)->dynindx = c + 1; + + /* These symbols will have no names, so we don't need to + fiddle with dynstr_index. */ + + ++c; + } + + elf_link_hash_traverse (elf_hash_table (info), + sparc64_elf_adjust_dynindx, + (PTR) &c); + elf_hash_table (info)->dynsymcount += c; + } + + return true; +} + +/* Increment the index of a dynamic symbol by a given amount. Called + via elf_link_hash_traverse. */ + +static boolean +sparc64_elf_adjust_dynindx (h, cparg) + struct elf_link_hash_entry *h; + PTR cparg; +{ + int *cp = (int *) cparg; + + if (h->dynindx != -1) + h->dynindx += *cp; + return true; +} + + +/* Relocate a SPARC64 ELF section. */ + +static boolean +sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_vma *local_got_offsets; + bfd_vma got_base; + asection *sgot; + asection *splt; + asection *sreloc; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + local_got_offsets = elf_local_got_offsets (input_bfd); + + if (elf_hash_table(info)->hgot == NULL) + got_base = 0; + else + got_base = elf_hash_table (info)->hgot->root.u.def.value; + + sgot = splt = sreloc = NULL; + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + reloc_howto_type *howto; + long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sec; + bfd_vma relocation; + bfd_reloc_status_type r; + + r_type = ELF64_R_TYPE (rel->r_info); + if (r_type < 0 || r_type >= (int) R_SPARC_max) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + howto = sparc64_elf_howto_table + r_type; + + r_symndx = ELF64_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + rel->r_addend += sec->output_offset + sym->st_value; + } + } + + continue; + } + + /* This is a final link. */ + h = NULL; + sym = NULL; + sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = (sec->output_section->vma + + sec->output_offset + + sym->st_value); + } + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + boolean skip_it = false; + sec = h->root.u.def.section; + + switch (r_type) + { + case R_SPARC_WPLT30: + case R_SPARC_PLT32: + case R_SPARC_HIPLT22: + case R_SPARC_LOPLT10: + case R_SPARC_PCPLT32: + case R_SPARC_PCPLT22: + case R_SPARC_PCPLT10: + case R_SPARC_PLT64: + if (h->plt_offset != (bfd_vma) -1) + skip_it = true; + break; + + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + if (elf_hash_table(info)->dynamic_sections_created + && (!info->shared + || (!info->symbolic && h->dynindx != -1) + || !(h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR))) + skip_it = true; + break; + + case R_SPARC_PC10: + case R_SPARC_PC22: + case R_SPARC_PC_HH22: + case R_SPARC_PC_HM10: + case R_SPARC_PC_LM22: + if (!strcmp(h->root.root.string, "_GLOBAL_OFFSET_TABLE_")) + break; + /* FALLTHRU */ + + case R_SPARC_8: + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_DISP8: + case R_SPARC_DISP16: + case R_SPARC_DISP32: + case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_HI22: + case R_SPARC_22: + case R_SPARC_13: + case R_SPARC_LO10: + case R_SPARC_UA32: + case R_SPARC_10: + case R_SPARC_11: + case R_SPARC_64: + case R_SPARC_OLO10: + case R_SPARC_HH22: + case R_SPARC_HM10: + case R_SPARC_LM22: + case R_SPARC_WDISP19: + case R_SPARC_WDISP16: + case R_SPARC_7: + case R_SPARC_5: + case R_SPARC_6: + case R_SPARC_DISP64: + case R_SPARC_HIX22: + case R_SPARC_LOX10: + case R_SPARC_H44: + case R_SPARC_M44: + case R_SPARC_L44: + case R_SPARC_UA64: + case R_SPARC_UA16: + if (info->shared + && ((!info->symbolic && h->dynindx != -1) + || !(h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR))) + skip_it = true; + break; + } + + if (skip_it) + { + /* In these cases, we don't need the relocation + value. We check specially because in some + obscure cases sec->output_section will be NULL. */ + relocation = 0; + } + else + { + relocation = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + } + else if (h->root.type == bfd_link_hash_undefweak) + relocation = 0; + else if (info->shared && !info->symbolic) + relocation = 0; + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset))) + return false; + relocation = 0; + } + } + + /* When generating a shared object, these relocations are copied + into the output file to be resolved at run time. */ + if (info->shared && (input_section->flags & SEC_ALLOC)) + { + switch (r_type) + { + case R_SPARC_PC10: + case R_SPARC_PC22: + case R_SPARC_PC_HH22: + case R_SPARC_PC_HM10: + case R_SPARC_PC_LM22: + if (h != NULL + && !strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_")) + break; + /* Fall through. */ + case R_SPARC_DISP8: + case R_SPARC_DISP16: + case R_SPARC_DISP32: + case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_WDISP19: + case R_SPARC_WDISP16: + case R_SPARC_DISP64: + if (h == NULL) + break; + /* Fall through. */ + case R_SPARC_8: + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_HI22: + case R_SPARC_22: + case R_SPARC_13: + case R_SPARC_LO10: + case R_SPARC_UA32: + case R_SPARC_10: + case R_SPARC_11: + case R_SPARC_64: + case R_SPARC_OLO10: + case R_SPARC_HH22: + case R_SPARC_HM10: + case R_SPARC_LM22: + case R_SPARC_7: + case R_SPARC_5: + case R_SPARC_6: + case R_SPARC_HIX22: + case R_SPARC_LOX10: + case R_SPARC_H44: + case R_SPARC_M44: + case R_SPARC_L44: + case R_SPARC_UA64: + case R_SPARC_UA16: + { + Elf_Internal_Rela outrel; + boolean skip; + + if (sreloc == NULL) + { + const char *name = + (bfd_elf_string_from_elf_section + (input_bfd, + elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rel_hdr.sh_name)); + + if (name == NULL) + return false; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name(input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + skip = false; + + if (elf_section_data (input_section)->stab_info == NULL) + outrel.r_offset = rel->r_offset; + else + { + bfd_vma off; + + off = (_bfd_stab_section_offset + (output_bfd, &elf_hash_table (info)->stab_info, + input_section, + &elf_section_data (input_section)->stab_info, + rel->r_offset)); + if (off == MINUS_ONE) + skip = true; + outrel.r_offset = off; + } + + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + /* Optimize unaligned reloc usage now that we know where + it finally resides. */ + switch (r_type) + { + case R_SPARC_16: + if (outrel.r_offset & 1) r_type = R_SPARC_UA16; + break; + case R_SPARC_UA16: + if (!(outrel.r_offset & 1)) r_type = R_SPARC_16; + break; + case R_SPARC_32: + if (outrel.r_offset & 3) r_type = R_SPARC_UA32; + break; + case R_SPARC_UA32: + if (!(outrel.r_offset & 3)) r_type = R_SPARC_32; + break; + case R_SPARC_64: + if (outrel.r_offset & 7) r_type = R_SPARC_UA64; + break; + case R_SPARC_UA64: + if (!(outrel.r_offset & 7)) r_type = R_SPARC_64; + break; + } + + if (skip) + memset (&outrel, 0, sizeof outrel); + /* h->dynindx may be -1 if the symbol was marked to + become local. */ + else if (h != NULL + && ((! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + { + BFD_ASSERT (h->dynindx != -1); + outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + else + { + if (r_type == R_SPARC_64) + { + outrel.r_info = ELF64_R_INFO (0, R_SPARC_RELATIVE); + outrel.r_addend = relocation + rel->r_addend; + } + else + { + long indx; + + if (h == NULL) + sec = local_sections[r_symndx]; + else + { + BFD_ASSERT (h->root.type == bfd_link_hash_defined + || (h->root.type + == bfd_link_hash_defweak)); + sec = h->root.u.def.section; + } + if (sec != NULL && bfd_is_abs_section (sec)) + indx = 0; + else if (sec == NULL || sec->owner == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + else + { + asection *osec; + + osec = sec->output_section; + indx = elf_section_data (osec)->dynindx; + + /* FIXME: we really should be able to link non-pic + shared libraries. */ + if (indx == 0) + { + BFD_FAIL (); + (*_bfd_error_handler) + ("%s: probably compiled without -fPIC?", + bfd_get_filename (input_bfd)); + bfd_set_error (bfd_error_bad_value); + return false; + } + } + + outrel.r_info = ELF64_R_INFO (indx, r_type); + outrel.r_addend = relocation + rel->r_addend; + } + } + + bfd_elf64_swap_reloca_out (output_bfd, &outrel, + (((Elf64_External_Rela *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + /* This reloc will be computed at runtime, so there's no + need to do anything now, unless this is a RELATIVE + reloc in an unallocated section. */ + if (skip + || (input_section->flags & SEC_ALLOC) != 0 + || ELF64_R_TYPE (outrel.r_info) != R_SPARC_RELATIVE) + continue; + } + break; + } + } + + switch (r_type) + { + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + /* Relocation is to the entry for this symbol in the global + offset table. */ + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } + + if (h != NULL) + { + bfd_vma off = h->got_offset; + BFD_ASSERT (off != (bfd_vma) -1); + + if (! elf_hash_table (info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR))) + { + /* This is actually a static link, or it is a -Bsymbolic + link and the symbol is defined locally, or the symbol + was forced to be local because of a version file. We + must initialize this entry in the global offset table. + Since the offset must always be a multiple of 8, we + use the least significant bit to record whether we + have initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This is + done in the finish_dynamic_symbol routine. */ + + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_64 (output_bfd, relocation, + sgot->contents + off); + h->got_offset |= 1; + } + } + relocation = sgot->output_offset + off - got_base; + } + else + { + bfd_vma off; + + BFD_ASSERT (local_got_offsets != NULL); + off = local_got_offsets[r_symndx]; + BFD_ASSERT (off != (bfd_vma) -1); + + /* The offset must always be a multiple of 8. We use + the least significant bit to record whether we have + already processed this entry. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_64 (output_bfd, relocation, sgot->contents + off); + local_got_offsets[r_symndx] |= 1; + + if (info->shared) + { + asection *srelgot; + Elf_Internal_Rela outrel; + + /* We need to generate a R_SPARC_RELATIVE reloc + for the dynamic linker. */ + srelgot = bfd_get_section_by_name(dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + off); + outrel.r_info = ELF64_R_INFO (0, R_SPARC_RELATIVE); + outrel.r_addend = relocation; + bfd_elf64_swap_reloca_out (output_bfd, &outrel, + (((Elf64_External_Rela *) + srelgot->contents) + + srelgot->reloc_count)); + ++srelgot->reloc_count; + } + } + relocation = sgot->output_offset + off - got_base; + } + goto do_default; + + case R_SPARC_WPLT30: + case R_SPARC_PLT32: + case R_SPARC_HIPLT22: + case R_SPARC_LOPLT10: + case R_SPARC_PCPLT32: + case R_SPARC_PCPLT22: + case R_SPARC_PCPLT10: + case R_SPARC_PLT64: + /* Relocation is to the entry for this symbol in the + procedure linkage table. */ + BFD_ASSERT (h != NULL); + + if (h->plt_offset == (bfd_vma) -1) + { + /* We didn't make a PLT entry for this symbol. This + happens when statically linking PIC code, or when + using -Bsymbolic. */ + goto do_default; + } + + if (splt == NULL) + { + splt = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (splt != NULL); + } + + relocation = (splt->output_section->vma + + splt->output_offset + + sparc64_elf_plt_entry_offset (h->plt_offset)); + goto do_default; + + case R_SPARC_OLO10: + { + bfd_vma x; + + relocation += rel->r_addend; + relocation = (relocation & 0x3ff) + ELF64_R_TYPE_DATA (rel->r_info); + + x = bfd_get_32 (input_bfd, contents + rel->r_offset); + x = (x & ~0x1fff) | (relocation & 0x1fff); + bfd_put_32 (input_bfd, x, contents + rel->r_offset); + + r = bfd_check_overflow (howto->complain_on_overflow, + howto->bitsize, howto->rightshift, + relocation); + } + break; + + case R_SPARC_WDISP16: + { + bfd_vma x; + + relocation += rel->r_addend; + /* Adjust for pc-relative-ness. */ + relocation -= (input_section->output_section->vma + + input_section->output_offset); + relocation -= rel->r_offset; + + x = bfd_get_32 (input_bfd, contents + rel->r_offset); + x = (x & ~0x303fff) | ((((relocation >> 2) & 0xc000) << 6) + | ((relocation >> 2) & 0x3fff)); + bfd_put_32 (input_bfd, x, contents + rel->r_offset); + + r = bfd_check_overflow (howto->complain_on_overflow, + howto->bitsize, howto->rightshift, + relocation); + } + break; + + case R_SPARC_HIX22: + { + bfd_vma x; + + relocation += rel->r_addend; + relocation = relocation ^ MINUS_ONE; + + x = bfd_get_32 (input_bfd, contents + rel->r_offset); + x = (x & ~0x3fffff) | ((relocation >> 10) & 0x3fffff); + bfd_put_32 (input_bfd, x, contents + rel->r_offset); + + r = bfd_check_overflow (howto->complain_on_overflow, + howto->bitsize, howto->rightshift, + relocation); + } + break; + + case R_SPARC_LOX10: + { + bfd_vma x; + + relocation += rel->r_addend; + relocation = (relocation & 0x3ff) | 0x1c00; + + x = bfd_get_32 (input_bfd, contents + rel->r_offset); + x = (x & ~0x1fff) | relocation; + bfd_put_32 (input_bfd, x, contents + rel->r_offset); + + r = bfd_reloc_ok; + } + break; + + default: + do_default: + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + } + + switch (r) + { + case bfd_reloc_ok: + break; + + default: + case bfd_reloc_outofrange: + abort (); + + case bfd_reloc_overflow: + { + const char *name; + + if (h != NULL) + { + if (h->root.type == bfd_link_hash_undefweak + && howto->pc_relative) + { + /* Assume this is a call protected by other code that + detect the symbol is undefined. If this is the case, + we can safely ignore the overflow. If not, the + program is hosed anyway, and a little warning isn't + going to help. */ + break; + } + + name = h->root.root.string; + } + else + { + name = (bfd_elf_string_from_elf_section + (input_bfd, + symtab_hdr->sh_link, + sym->st_name)); + if (name == NULL) + return false; + if (*name == '\0') + name = bfd_section_name (input_bfd, sec); + } + if (! ((*info->callbacks->reloc_overflow) + (info, name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + break; + } + } + + return true; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static boolean +sparc64_elf_finish_dynamic_symbol (output_bfd, info, h, sym) + bfd *output_bfd; + struct bfd_link_info *info; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + bfd *dynobj; + + dynobj = elf_hash_table (info)->dynobj; + + if (h->plt_offset != (bfd_vma) -1) + { + asection *splt; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the PLT. Set it up. */ + + BFD_ASSERT (h->dynindx != -1); + + splt = bfd_get_section_by_name (dynobj, ".plt"); + srela = bfd_get_section_by_name (dynobj, ".rela.plt"); + BFD_ASSERT (splt != NULL && srela != NULL); + + /* Fill in the entry in the .rela.plt section. */ + + if (h->plt_offset < LARGE_PLT_THRESHOLD) + { + rela.r_offset = sparc64_elf_plt_entry_offset (h->plt_offset); + rela.r_addend = 0; + } + else + { + int max = splt->_raw_size / PLT_ENTRY_SIZE; + rela.r_offset = sparc64_elf_plt_ptr_offset (h->plt_offset, max); + rela.r_addend = -(sparc64_elf_plt_entry_offset (h->plt_offset) + 4); + } + rela.r_info = ELF64_R_INFO (h->dynindx, R_SPARC_JMP_SLOT); + + bfd_elf64_swap_reloca_out (output_bfd, &rela, + ((Elf64_External_Rela *) srela->contents + + h->plt_offset)); + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + } + } + + if (h->got_offset != (bfd_vma) -1) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the GOT. Set it up. */ + + sgot = bfd_get_section_by_name (dynobj, ".got"); + srela = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (sgot != NULL && srela != NULL); + + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + + (h->got_offset &~ 1)); + + /* If this is a -Bsymbolic link, and the symbol is defined + locally, we just want to emit a RELATIVE reloc. Likewise if + the symbol was forced to be local because of a version file. + The entry in the global offset table will already have been + initialized in the relocate_section function. */ + if (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)) + { + asection *sec = h->root.u.def.section; + rela.r_info = ELF64_R_INFO (0, R_SPARC_RELATIVE); + rela.r_addend = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else + { + bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + h->got_offset); + rela.r_info = ELF64_R_INFO (h->dynindx, R_SPARC_GLOB_DAT); + rela.r_addend = 0; + } + + bfd_elf64_swap_reloca_out (output_bfd, &rela, + ((Elf64_External_Rela *) srela->contents + + srela->reloc_count)); + ++srela->reloc_count; + } + + if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0) + { + asection *s; + Elf_Internal_Rela rela; + + /* This symbols needs a copy reloc. Set it up. */ + + BFD_ASSERT (h->dynindx != -1); + + s = bfd_get_section_by_name (h->root.u.def.section->owner, + ".rela.bss"); + BFD_ASSERT (s != NULL); + + rela.r_offset = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + rela.r_info = ELF64_R_INFO (h->dynindx, R_SPARC_COPY); + rela.r_addend = 0; + bfd_elf64_swap_reloca_out (output_bfd, &rela, + ((Elf64_External_Rela *) s->contents + + s->reloc_count)); + ++s->reloc_count; + } + + /* Mark some specially defined symbols as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0 + || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + + return true; +} + +/* Finish up the dynamic sections. */ + +static boolean +sparc64_elf_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *sdyn; + asection *sgot; + + dynobj = elf_hash_table (info)->dynobj; + + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + + if (elf_hash_table (info)->dynamic_sections_created) + { + asection *splt; + Elf64_External_Dyn *dyncon, *dynconend; + + splt = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (splt != NULL && sdyn != NULL); + + dyncon = (Elf64_External_Dyn *) sdyn->contents; + dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + boolean size; + + bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + case DT_PLTGOT: name = ".plt"; size = false; break; + case DT_PLTRELSZ: name = ".rela.plt"; size = true; break; + case DT_JMPREL: name = ".rela.plt"; size = false; break; + default: name = NULL; size = false; break; + } + + if (name != NULL) + { + asection *s; + + s = bfd_get_section_by_name (output_bfd, name); + if (s == NULL) + dyn.d_un.d_val = 0; + else + { + if (! size) + dyn.d_un.d_ptr = s->vma; + else + { + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size; + else + dyn.d_un.d_val = s->_raw_size; + } + } + bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); + } + } + + /* Initialize the contents of the .plt section. */ + if (splt->_raw_size > 0) + { + sparc64_elf_build_plt(output_bfd, splt->contents, + splt->_raw_size / PLT_ENTRY_SIZE); + } + + elf_section_data (splt->output_section)->this_hdr.sh_entsize = + PLT_ENTRY_SIZE; + } + + /* Set the first entry in the global offset table to the address of + the dynamic section. */ + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + if (sgot->_raw_size > 0) + { + if (sdyn == NULL) + bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents); + else + bfd_put_64 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgot->contents); + } + + elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 8; + + if (info->shared) + { + asection *sdynsym; + asection *s; + Elf_Internal_Sym sym; + int c; + + /* Set up the section symbols for the output sections. */ + + sdynsym = bfd_get_section_by_name (dynobj, ".dynsym"); + BFD_ASSERT (sdynsym != NULL); + + sym.st_size = 0; + sym.st_name = 0; + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); + sym.st_other = 0; + + c = 0; + for (s = output_bfd->sections; s != NULL; s = s->next) + { + int indx; + + if (elf_section_data (s)->dynindx == 0) + continue; + + sym.st_value = s->vma; + + indx = elf_section_data (s)->this_idx; + BFD_ASSERT (indx > 0); + sym.st_shndx = indx; + + bfd_elf64_swap_symbol_out (output_bfd, &sym, + (PTR) (((Elf64_External_Sym *) + sdynsym->contents) + + elf_section_data (s)->dynindx)); + + ++c; + } + + /* Set the sh_info field of the output .dynsym section to the + index of the first global symbol. */ + elf_section_data (sdynsym->output_section)->this_hdr.sh_info = c + 1; + } + + return true; +} + +/* Functions for dealing with the e_flags field. */ + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +static boolean +sparc64_elf_merge_private_bfd_data (ibfd, obfd) + bfd *ibfd; + bfd *obfd; +{ + boolean error; + flagword new_flags, old_flags; + int new_mm, old_mm; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return true; + + new_flags = elf_elfheader (ibfd)->e_flags; + old_flags = elf_elfheader (obfd)->e_flags; + + if (!elf_flags_init (obfd)) /* First call, no flags set */ + { + elf_flags_init (obfd) = true; + elf_elfheader (obfd)->e_flags = new_flags; + } + + else if (new_flags == old_flags) /* Compatible flags are ok */ + ; + + else /* Incompatible flags */ + { + error = false; + + old_flags |= (new_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1)); + new_flags |= (old_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1)); + if ((old_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1)) == + (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1)) + { + error = true; + (*_bfd_error_handler) + ("%s: linking UltraSPARC specific with HAL specific code", + bfd_get_filename (ibfd)); + } + + /* Choose the most restrictive memory ordering */ + old_mm = (old_flags & EF_SPARCV9_MM); + new_mm = (new_flags & EF_SPARCV9_MM); + old_flags &= ~EF_SPARCV9_MM; + new_flags &= ~EF_SPARCV9_MM; + if (new_mm < old_mm) old_mm = new_mm; + old_flags |= old_mm; + new_flags |= old_mm; + + /* Warn about any other mismatches */ + if (new_flags != old_flags) + { + error = true; + (*_bfd_error_handler) + ("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)", + bfd_get_filename (ibfd), (long)new_flags, (long)old_flags); + } + + elf_elfheader (obfd)->e_flags = old_flags; + + if (error) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + } + return true; +} + + +/* Set the right machine number for a SPARC64 ELF file. */ + +static boolean +sparc64_elf_object_p (abfd) + bfd *abfd; +{ + unsigned long mach = bfd_mach_sparc_v9; + + if (elf_elfheader (abfd)->e_flags & EF_SPARC_SUN_US1) + mach = bfd_mach_sparc_v9a; + return bfd_default_set_arch_mach (abfd, bfd_arch_sparc, mach); +} + +#define TARGET_BIG_SYM bfd_elf64_sparc_vec +#define TARGET_BIG_NAME "elf64-sparc" +#define ELF_ARCH bfd_arch_sparc +#define ELF_MACHINE_CODE EM_SPARC64 +#define ELF_MAXPAGESIZE 0x100000 + +#define elf_info_to_howto \ + sparc64_elf_info_to_howto +#define bfd_elf64_bfd_reloc_type_lookup \ + sparc64_elf_reloc_type_lookup + +#define elf_backend_create_dynamic_sections \ + _bfd_elf_create_dynamic_sections +#define elf_backend_check_relocs \ + sparc64_elf_check_relocs +#define elf_backend_adjust_dynamic_symbol \ + sparc64_elf_adjust_dynamic_symbol +#define elf_backend_size_dynamic_sections \ + sparc64_elf_size_dynamic_sections +#define elf_backend_relocate_section \ + sparc64_elf_relocate_section +#define elf_backend_finish_dynamic_symbol \ + sparc64_elf_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + sparc64_elf_finish_dynamic_sections + +#define bfd_elf64_bfd_merge_private_bfd_data \ + sparc64_elf_merge_private_bfd_data + +#define elf_backend_object_p \ + sparc64_elf_object_p + +#define elf_backend_want_got_plt 0 +#define elf_backend_plt_readonly 0 +#define elf_backend_want_plt_sym 1 + +/* Section 5.2.4 of the ABI specifies a 256-byte boundary for the table. */ +#define elf_backend_plt_alignment 8 + +#include "elf64-target.h" diff --git a/contrib/binutils/bfd/sunos.c b/contrib/binutils/bfd/sunos.c new file mode 100644 index 0000000..45e0cba --- /dev/null +++ b/contrib/binutils/bfd/sunos.c @@ -0,0 +1,2918 @@ +/* BFD backend for SunOS binaries. + Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 97, 1998 + Free Software Foundation, Inc. + Written by Cygnus Support. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGETNAME "a.out-sunos-big" +#define MY(OP) CAT(sunos_big_,OP) + +#include "bfd.h" +#include "bfdlink.h" +#include "libaout.h" + +/* Static routines defined in this file. */ + +static boolean sunos_read_dynamic_info PARAMS ((bfd *)); +static long sunos_get_dynamic_symtab_upper_bound PARAMS ((bfd *)); +static boolean sunos_slurp_dynamic_symtab PARAMS ((bfd *)); +static long sunos_canonicalize_dynamic_symtab PARAMS ((bfd *, asymbol **)); +static long sunos_get_dynamic_reloc_upper_bound PARAMS ((bfd *)); +static long sunos_canonicalize_dynamic_reloc + PARAMS ((bfd *, arelent **, asymbol **)); +static struct bfd_hash_entry *sunos_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static struct bfd_link_hash_table *sunos_link_hash_table_create + PARAMS ((bfd *)); +static boolean sunos_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *, boolean)); +static boolean sunos_add_dynamic_symbols + PARAMS ((bfd *, struct bfd_link_info *, struct external_nlist **, + bfd_size_type *, char **)); +static boolean sunos_add_one_symbol + PARAMS ((struct bfd_link_info *, bfd *, const char *, flagword, asection *, + bfd_vma, const char *, boolean, boolean, + struct bfd_link_hash_entry **)); +static boolean sunos_scan_relocs + PARAMS ((struct bfd_link_info *, bfd *, asection *, bfd_size_type)); +static boolean sunos_scan_std_relocs + PARAMS ((struct bfd_link_info *, bfd *, asection *, + const struct reloc_std_external *, bfd_size_type)); +static boolean sunos_scan_ext_relocs + PARAMS ((struct bfd_link_info *, bfd *, asection *, + const struct reloc_ext_external *, bfd_size_type)); +static boolean sunos_link_dynamic_object + PARAMS ((struct bfd_link_info *, bfd *)); +static boolean sunos_write_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct aout_link_hash_entry *)); +static boolean sunos_check_dynamic_reloc + PARAMS ((struct bfd_link_info *, bfd *, asection *, + struct aout_link_hash_entry *, PTR, bfd_byte *, boolean *, + bfd_vma *)); +static boolean sunos_finish_dynamic_link + PARAMS ((bfd *, struct bfd_link_info *)); + +#define MY_get_dynamic_symtab_upper_bound sunos_get_dynamic_symtab_upper_bound +#define MY_canonicalize_dynamic_symtab sunos_canonicalize_dynamic_symtab +#define MY_get_dynamic_reloc_upper_bound sunos_get_dynamic_reloc_upper_bound +#define MY_canonicalize_dynamic_reloc sunos_canonicalize_dynamic_reloc +#define MY_bfd_link_hash_table_create sunos_link_hash_table_create +#define MY_add_dynamic_symbols sunos_add_dynamic_symbols +#define MY_add_one_symbol sunos_add_one_symbol +#define MY_link_dynamic_object sunos_link_dynamic_object +#define MY_write_dynamic_symbol sunos_write_dynamic_symbol +#define MY_check_dynamic_reloc sunos_check_dynamic_reloc +#define MY_finish_dynamic_link sunos_finish_dynamic_link + +/* ??? Where should this go? */ +#define MACHTYPE_OK(mtype) \ + (((mtype) == M_SPARC && bfd_lookup_arch (bfd_arch_sparc, 0) != NULL) \ + || ((mtype) == M_SPARCLET \ + && bfd_lookup_arch (bfd_arch_sparc, bfd_mach_sparc_sparclet) != NULL) \ + || (((mtype) == M_UNKNOWN || (mtype) == M_68010 || (mtype) == M_68020) \ + && bfd_lookup_arch (bfd_arch_m68k, 0) != NULL)) + +/* Include the usual a.out support. */ +#include "aoutf1.h" + +/* SunOS shared library support. We store a pointer to this structure + in obj_aout_dynamic_info (abfd). */ + +struct sunos_dynamic_info +{ + /* Whether we found any dynamic information. */ + boolean valid; + /* Dynamic information. */ + struct internal_sun4_dynamic_link dyninfo; + /* Number of dynamic symbols. */ + unsigned long dynsym_count; + /* Read in nlists for dynamic symbols. */ + struct external_nlist *dynsym; + /* asymbol structures for dynamic symbols. */ + aout_symbol_type *canonical_dynsym; + /* Read in dynamic string table. */ + char *dynstr; + /* Number of dynamic relocs. */ + unsigned long dynrel_count; + /* Read in dynamic relocs. This may be reloc_std_external or + reloc_ext_external. */ + PTR dynrel; + /* arelent structures for dynamic relocs. */ + arelent *canonical_dynrel; +}; + +/* The hash table of dynamic symbols is composed of two word entries. + See include/aout/sun4.h for details. */ + +#define HASH_ENTRY_SIZE (2 * BYTES_IN_WORD) + +/* Read in the basic dynamic information. This locates the __DYNAMIC + structure and uses it to find the dynamic_link structure. It + creates and saves a sunos_dynamic_info structure. If it can't find + __DYNAMIC, it sets the valid field of the sunos_dynamic_info + structure to false to avoid doing this work again. */ + +static boolean +sunos_read_dynamic_info (abfd) + bfd *abfd; +{ + struct sunos_dynamic_info *info; + asection *dynsec; + bfd_vma dynoff; + struct external_sun4_dynamic dyninfo; + unsigned long dynver; + struct external_sun4_dynamic_link linkinfo; + + if (obj_aout_dynamic_info (abfd) != (PTR) NULL) + return true; + + if ((abfd->flags & DYNAMIC) == 0) + { + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + info = ((struct sunos_dynamic_info *) + bfd_zalloc (abfd, sizeof (struct sunos_dynamic_info))); + if (!info) + return false; + info->valid = false; + info->dynsym = NULL; + info->dynstr = NULL; + info->canonical_dynsym = NULL; + info->dynrel = NULL; + info->canonical_dynrel = NULL; + obj_aout_dynamic_info (abfd) = (PTR) info; + + /* This code used to look for the __DYNAMIC symbol to locate the dynamic + linking information. + However this inhibits recovering the dynamic symbols from a + stripped object file, so blindly assume that the dynamic linking + information is located at the start of the data section. + We could verify this assumption later by looking through the dynamic + symbols for the __DYNAMIC symbol. */ + if ((abfd->flags & DYNAMIC) == 0) + return true; + if (! bfd_get_section_contents (abfd, obj_datasec (abfd), (PTR) &dyninfo, + (file_ptr) 0, sizeof dyninfo)) + return true; + + dynver = GET_WORD (abfd, dyninfo.ld_version); + if (dynver != 2 && dynver != 3) + return true; + + dynoff = GET_WORD (abfd, dyninfo.ld); + + /* dynoff is a virtual address. It is probably always in the .data + section, but this code should work even if it moves. */ + if (dynoff < bfd_get_section_vma (abfd, obj_datasec (abfd))) + dynsec = obj_textsec (abfd); + else + dynsec = obj_datasec (abfd); + dynoff -= bfd_get_section_vma (abfd, dynsec); + if (dynoff > bfd_section_size (abfd, dynsec)) + return true; + + /* This executable appears to be dynamically linked in a way that we + can understand. */ + if (! bfd_get_section_contents (abfd, dynsec, (PTR) &linkinfo, dynoff, + (bfd_size_type) sizeof linkinfo)) + return true; + + /* Swap in the dynamic link information. */ + info->dyninfo.ld_loaded = GET_WORD (abfd, linkinfo.ld_loaded); + info->dyninfo.ld_need = GET_WORD (abfd, linkinfo.ld_need); + info->dyninfo.ld_rules = GET_WORD (abfd, linkinfo.ld_rules); + info->dyninfo.ld_got = GET_WORD (abfd, linkinfo.ld_got); + info->dyninfo.ld_plt = GET_WORD (abfd, linkinfo.ld_plt); + info->dyninfo.ld_rel = GET_WORD (abfd, linkinfo.ld_rel); + info->dyninfo.ld_hash = GET_WORD (abfd, linkinfo.ld_hash); + info->dyninfo.ld_stab = GET_WORD (abfd, linkinfo.ld_stab); + info->dyninfo.ld_stab_hash = GET_WORD (abfd, linkinfo.ld_stab_hash); + info->dyninfo.ld_buckets = GET_WORD (abfd, linkinfo.ld_buckets); + info->dyninfo.ld_symbols = GET_WORD (abfd, linkinfo.ld_symbols); + info->dyninfo.ld_symb_size = GET_WORD (abfd, linkinfo.ld_symb_size); + info->dyninfo.ld_text = GET_WORD (abfd, linkinfo.ld_text); + info->dyninfo.ld_plt_sz = GET_WORD (abfd, linkinfo.ld_plt_sz); + + /* Reportedly the addresses need to be offset by the size of the + exec header in an NMAGIC file. */ + if (adata (abfd).magic == n_magic) + { + unsigned long exec_bytes_size = adata (abfd).exec_bytes_size; + + info->dyninfo.ld_need += exec_bytes_size; + info->dyninfo.ld_rules += exec_bytes_size; + info->dyninfo.ld_rel += exec_bytes_size; + info->dyninfo.ld_hash += exec_bytes_size; + info->dyninfo.ld_stab += exec_bytes_size; + info->dyninfo.ld_symbols += exec_bytes_size; + } + + /* The only way to get the size of the symbol information appears to + be to determine the distance between it and the string table. */ + info->dynsym_count = ((info->dyninfo.ld_symbols - info->dyninfo.ld_stab) + / EXTERNAL_NLIST_SIZE); + BFD_ASSERT (info->dynsym_count * EXTERNAL_NLIST_SIZE + == (unsigned long) (info->dyninfo.ld_symbols + - info->dyninfo.ld_stab)); + + /* Similarly, the relocs end at the hash table. */ + info->dynrel_count = ((info->dyninfo.ld_hash - info->dyninfo.ld_rel) + / obj_reloc_entry_size (abfd)); + BFD_ASSERT (info->dynrel_count * obj_reloc_entry_size (abfd) + == (unsigned long) (info->dyninfo.ld_hash + - info->dyninfo.ld_rel)); + + info->valid = true; + + return true; +} + +/* Return the amount of memory required for the dynamic symbols. */ + +static long +sunos_get_dynamic_symtab_upper_bound (abfd) + bfd *abfd; +{ + struct sunos_dynamic_info *info; + + if (! sunos_read_dynamic_info (abfd)) + return -1; + + info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + if (! info->valid) + { + bfd_set_error (bfd_error_no_symbols); + return -1; + } + + return (info->dynsym_count + 1) * sizeof (asymbol *); +} + +/* Read the external dynamic symbols. */ + +static boolean +sunos_slurp_dynamic_symtab (abfd) + bfd *abfd; +{ + struct sunos_dynamic_info *info; + + /* Get the general dynamic information. */ + if (obj_aout_dynamic_info (abfd) == NULL) + { + if (! sunos_read_dynamic_info (abfd)) + return false; + } + + info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + if (! info->valid) + { + bfd_set_error (bfd_error_no_symbols); + return false; + } + + /* Get the dynamic nlist structures. */ + if (info->dynsym == (struct external_nlist *) NULL) + { + info->dynsym = ((struct external_nlist *) + bfd_alloc (abfd, + (info->dynsym_count + * EXTERNAL_NLIST_SIZE))); + if (info->dynsym == NULL && info->dynsym_count != 0) + return false; + if (bfd_seek (abfd, info->dyninfo.ld_stab, SEEK_SET) != 0 + || (bfd_read ((PTR) info->dynsym, info->dynsym_count, + EXTERNAL_NLIST_SIZE, abfd) + != info->dynsym_count * EXTERNAL_NLIST_SIZE)) + { + if (info->dynsym != NULL) + { + bfd_release (abfd, info->dynsym); + info->dynsym = NULL; + } + return false; + } + } + + /* Get the dynamic strings. */ + if (info->dynstr == (char *) NULL) + { + info->dynstr = (char *) bfd_alloc (abfd, info->dyninfo.ld_symb_size); + if (info->dynstr == NULL && info->dyninfo.ld_symb_size != 0) + return false; + if (bfd_seek (abfd, info->dyninfo.ld_symbols, SEEK_SET) != 0 + || (bfd_read ((PTR) info->dynstr, 1, info->dyninfo.ld_symb_size, + abfd) + != info->dyninfo.ld_symb_size)) + { + if (info->dynstr != NULL) + { + bfd_release (abfd, info->dynstr); + info->dynstr = NULL; + } + return false; + } + } + + return true; +} + +/* Read in the dynamic symbols. */ + +static long +sunos_canonicalize_dynamic_symtab (abfd, storage) + bfd *abfd; + asymbol **storage; +{ + struct sunos_dynamic_info *info; + unsigned long i; + + if (! sunos_slurp_dynamic_symtab (abfd)) + return -1; + + info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + +#ifdef CHECK_DYNAMIC_HASH + /* Check my understanding of the dynamic hash table by making sure + that each symbol can be located in the hash table. */ + { + bfd_size_type table_size; + bfd_byte *table; + bfd_size_type i; + + if (info->dyninfo.ld_buckets > info->dynsym_count) + abort (); + table_size = info->dyninfo.ld_stab - info->dyninfo.ld_hash; + table = (bfd_byte *) bfd_malloc (table_size); + if (table == NULL && table_size != 0) + abort (); + if (bfd_seek (abfd, info->dyninfo.ld_hash, SEEK_SET) != 0 + || bfd_read ((PTR) table, 1, table_size, abfd) != table_size) + abort (); + for (i = 0; i < info->dynsym_count; i++) + { + unsigned char *name; + unsigned long hash; + + name = ((unsigned char *) info->dynstr + + GET_WORD (abfd, info->dynsym[i].e_strx)); + hash = 0; + while (*name != '\0') + hash = (hash << 1) + *name++; + hash &= 0x7fffffff; + hash %= info->dyninfo.ld_buckets; + while (GET_WORD (abfd, table + hash * HASH_ENTRY_SIZE) != i) + { + hash = GET_WORD (abfd, + table + hash * HASH_ENTRY_SIZE + BYTES_IN_WORD); + if (hash == 0 || hash >= table_size / HASH_ENTRY_SIZE) + abort (); + } + } + free (table); + } +#endif /* CHECK_DYNAMIC_HASH */ + + /* Get the asymbol structures corresponding to the dynamic nlist + structures. */ + if (info->canonical_dynsym == (aout_symbol_type *) NULL) + { + info->canonical_dynsym = ((aout_symbol_type *) + bfd_alloc (abfd, + (info->dynsym_count + * sizeof (aout_symbol_type)))); + if (info->canonical_dynsym == NULL && info->dynsym_count != 0) + return -1; + + if (! aout_32_translate_symbol_table (abfd, info->canonical_dynsym, + info->dynsym, info->dynsym_count, + info->dynstr, + info->dyninfo.ld_symb_size, + true)) + { + if (info->canonical_dynsym != NULL) + { + bfd_release (abfd, info->canonical_dynsym); + info->canonical_dynsym = NULL; + } + return -1; + } + } + + /* Return pointers to the dynamic asymbol structures. */ + for (i = 0; i < info->dynsym_count; i++) + *storage++ = (asymbol *) (info->canonical_dynsym + i); + *storage = NULL; + + return info->dynsym_count; +} + +/* Return the amount of memory required for the dynamic relocs. */ + +static long +sunos_get_dynamic_reloc_upper_bound (abfd) + bfd *abfd; +{ + struct sunos_dynamic_info *info; + + if (! sunos_read_dynamic_info (abfd)) + return -1; + + info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + if (! info->valid) + { + bfd_set_error (bfd_error_no_symbols); + return -1; + } + + return (info->dynrel_count + 1) * sizeof (arelent *); +} + +/* Read in the dynamic relocs. */ + +static long +sunos_canonicalize_dynamic_reloc (abfd, storage, syms) + bfd *abfd; + arelent **storage; + asymbol **syms; +{ + struct sunos_dynamic_info *info; + unsigned long i; + + /* Get the general dynamic information. */ + if (obj_aout_dynamic_info (abfd) == (PTR) NULL) + { + if (! sunos_read_dynamic_info (abfd)) + return -1; + } + + info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + if (! info->valid) + { + bfd_set_error (bfd_error_no_symbols); + return -1; + } + + /* Get the dynamic reloc information. */ + if (info->dynrel == NULL) + { + info->dynrel = (PTR) bfd_alloc (abfd, + (info->dynrel_count + * obj_reloc_entry_size (abfd))); + if (info->dynrel == NULL && info->dynrel_count != 0) + return -1; + if (bfd_seek (abfd, info->dyninfo.ld_rel, SEEK_SET) != 0 + || (bfd_read ((PTR) info->dynrel, info->dynrel_count, + obj_reloc_entry_size (abfd), abfd) + != info->dynrel_count * obj_reloc_entry_size (abfd))) + { + if (info->dynrel != NULL) + { + bfd_release (abfd, info->dynrel); + info->dynrel = NULL; + } + return -1; + } + } + + /* Get the arelent structures corresponding to the dynamic reloc + information. */ + if (info->canonical_dynrel == (arelent *) NULL) + { + arelent *to; + + info->canonical_dynrel = ((arelent *) + bfd_alloc (abfd, + (info->dynrel_count + * sizeof (arelent)))); + if (info->canonical_dynrel == NULL && info->dynrel_count != 0) + return -1; + + to = info->canonical_dynrel; + + if (obj_reloc_entry_size (abfd) == RELOC_EXT_SIZE) + { + register struct reloc_ext_external *p; + struct reloc_ext_external *pend; + + p = (struct reloc_ext_external *) info->dynrel; + pend = p + info->dynrel_count; + for (; p < pend; p++, to++) + NAME(aout,swap_ext_reloc_in) (abfd, p, to, syms, + info->dynsym_count); + } + else + { + register struct reloc_std_external *p; + struct reloc_std_external *pend; + + p = (struct reloc_std_external *) info->dynrel; + pend = p + info->dynrel_count; + for (; p < pend; p++, to++) + NAME(aout,swap_std_reloc_in) (abfd, p, to, syms, + info->dynsym_count); + } + } + + /* Return pointers to the dynamic arelent structures. */ + for (i = 0; i < info->dynrel_count; i++) + *storage++ = info->canonical_dynrel + i; + *storage = NULL; + + return info->dynrel_count; +} + +/* Code to handle linking of SunOS shared libraries. */ + +/* A SPARC procedure linkage table entry is 12 bytes. The first entry + in the table is a jump which is filled in by the runtime linker. + The remaining entries are branches back to the first entry, + followed by an index into the relocation table encoded to look like + a sethi of %g0. */ + +#define SPARC_PLT_ENTRY_SIZE (12) + +static const bfd_byte sparc_plt_first_entry[SPARC_PLT_ENTRY_SIZE] = +{ + /* sethi %hi(0),%g1; address filled in by runtime linker. */ + 0x3, 0, 0, 0, + /* jmp %g1; offset filled in by runtime linker. */ + 0x81, 0xc0, 0x60, 0, + /* nop */ + 0x1, 0, 0, 0 +}; + +/* save %sp, -96, %sp */ +#define SPARC_PLT_ENTRY_WORD0 0x9de3bfa0 +/* call; address filled in later. */ +#define SPARC_PLT_ENTRY_WORD1 0x40000000 +/* sethi; reloc index filled in later. */ +#define SPARC_PLT_ENTRY_WORD2 0x01000000 + +/* This sequence is used when for the jump table entry to a defined + symbol in a complete executable. It is used when linking PIC + compiled code which is not being put into a shared library. */ +/* sethi <address to be filled in later>, %g1 */ +#define SPARC_PLT_PIC_WORD0 0x03000000 +/* jmp %g1 + <address to be filled in later> */ +#define SPARC_PLT_PIC_WORD1 0x81c06000 +/* nop */ +#define SPARC_PLT_PIC_WORD2 0x01000000 + +/* An m68k procedure linkage table entry is 8 bytes. The first entry + in the table is a jump which is filled in the by the runtime + linker. The remaining entries are branches back to the first + entry, followed by a two byte index into the relocation table. */ + +#define M68K_PLT_ENTRY_SIZE (8) + +static const bfd_byte m68k_plt_first_entry[M68K_PLT_ENTRY_SIZE] = +{ + /* jmps @# */ + 0x4e, 0xf9, + /* Filled in by runtime linker with a magic address. */ + 0, 0, 0, 0, + /* Not used? */ + 0, 0 +}; + +/* bsrl */ +#define M68K_PLT_ENTRY_WORD0 (0x61ff) +/* Remaining words filled in later. */ + +/* An entry in the SunOS linker hash table. */ + +struct sunos_link_hash_entry +{ + struct aout_link_hash_entry root; + + /* If this is a dynamic symbol, this is its index into the dynamic + symbol table. This is initialized to -1. As the linker looks at + the input files, it changes this to -2 if it will be added to the + dynamic symbol table. After all the input files have been seen, + the linker will know whether to build a dynamic symbol table; if + it does build one, this becomes the index into the table. */ + long dynindx; + + /* If this is a dynamic symbol, this is the index of the name in the + dynamic symbol string table. */ + long dynstr_index; + + /* The offset into the global offset table used for this symbol. If + the symbol does not require a GOT entry, this is 0. */ + bfd_vma got_offset; + + /* The offset into the procedure linkage table used for this symbol. + If the symbol does not require a PLT entry, this is 0. */ + bfd_vma plt_offset; + + /* Some linker flags. */ + unsigned char flags; + /* Symbol is referenced by a regular object. */ +#define SUNOS_REF_REGULAR 01 + /* Symbol is defined by a regular object. */ +#define SUNOS_DEF_REGULAR 02 + /* Symbol is referenced by a dynamic object. */ +#define SUNOS_REF_DYNAMIC 04 + /* Symbol is defined by a dynamic object. */ +#define SUNOS_DEF_DYNAMIC 010 + /* Symbol is a constructor symbol in a regular object. */ +#define SUNOS_CONSTRUCTOR 020 +}; + +/* The SunOS linker hash table. */ + +struct sunos_link_hash_table +{ + struct aout_link_hash_table root; + + /* The object which holds the dynamic sections. */ + bfd *dynobj; + + /* Whether we have created the dynamic sections. */ + boolean dynamic_sections_created; + + /* Whether we need the dynamic sections. */ + boolean dynamic_sections_needed; + + /* Whether we need the .got table. */ + boolean got_needed; + + /* The number of dynamic symbols. */ + size_t dynsymcount; + + /* The number of buckets in the hash table. */ + size_t bucketcount; + + /* The list of dynamic objects needed by dynamic objects included in + the link. */ + struct bfd_link_needed_list *needed; + + /* The offset of __GLOBAL_OFFSET_TABLE_ into the .got section. */ + bfd_vma got_base; +}; + +/* Routine to create an entry in an SunOS link hash table. */ + +static struct bfd_hash_entry * +sunos_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct sunos_link_hash_entry *ret = (struct sunos_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct sunos_link_hash_entry *) NULL) + ret = ((struct sunos_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct sunos_link_hash_entry))); + if (ret == (struct sunos_link_hash_entry *) NULL) + return (struct bfd_hash_entry *) ret; + + /* Call the allocation method of the superclass. */ + ret = ((struct sunos_link_hash_entry *) + NAME(aout,link_hash_newfunc) ((struct bfd_hash_entry *) ret, + table, string)); + if (ret != NULL) + { + /* Set local fields. */ + ret->dynindx = -1; + ret->dynstr_index = -1; + ret->got_offset = 0; + ret->plt_offset = 0; + ret->flags = 0; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Create a SunOS link hash table. */ + +static struct bfd_link_hash_table * +sunos_link_hash_table_create (abfd) + bfd *abfd; +{ + struct sunos_link_hash_table *ret; + + ret = ((struct sunos_link_hash_table *) + bfd_alloc (abfd, sizeof (struct sunos_link_hash_table))); + if (ret == (struct sunos_link_hash_table *) NULL) + return (struct bfd_link_hash_table *) NULL; + if (! NAME(aout,link_hash_table_init) (&ret->root, abfd, + sunos_link_hash_newfunc)) + { + bfd_release (abfd, ret); + return (struct bfd_link_hash_table *) NULL; + } + + ret->dynobj = NULL; + ret->dynamic_sections_created = false; + ret->dynamic_sections_needed = false; + ret->got_needed = false; + ret->dynsymcount = 0; + ret->bucketcount = 0; + ret->needed = NULL; + ret->got_base = 0; + + return &ret->root.root; +} + +/* Look up an entry in an SunOS link hash table. */ + +#define sunos_link_hash_lookup(table, string, create, copy, follow) \ + ((struct sunos_link_hash_entry *) \ + aout_link_hash_lookup (&(table)->root, (string), (create), (copy),\ + (follow))) + +/* Traverse a SunOS link hash table. */ + +#define sunos_link_hash_traverse(table, func, info) \ + (aout_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct aout_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the SunOS link hash table from the info structure. This is + just a cast. */ + +#define sunos_hash_table(p) ((struct sunos_link_hash_table *) ((p)->hash)) + +static boolean sunos_scan_dynamic_symbol + PARAMS ((struct sunos_link_hash_entry *, PTR)); + +/* Create the dynamic sections needed if we are linking against a + dynamic object, or if we are linking PIC compiled code. ABFD is a + bfd we can attach the dynamic sections to. The linker script will + look for these special sections names and put them in the right + place in the output file. See include/aout/sun4.h for more details + of the dynamic linking information. */ + +static boolean +sunos_create_dynamic_sections (abfd, info, needed) + bfd *abfd; + struct bfd_link_info *info; + boolean needed; +{ + asection *s; + + if (! sunos_hash_table (info)->dynamic_sections_created) + { + flagword flags; + + sunos_hash_table (info)->dynobj = abfd; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED); + + /* The .dynamic section holds the basic dynamic information: the + sun4_dynamic structure, the dynamic debugger information, and + the sun4_dynamic_link structure. */ + s = bfd_make_section (abfd, ".dynamic"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .got section holds the global offset table. The address + is put in the ld_got field. */ + s = bfd_make_section (abfd, ".got"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .plt section holds the procedure linkage table. The + address is put in the ld_plt field. */ + s = bfd_make_section (abfd, ".plt"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_CODE) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .dynrel section holds the dynamic relocs. The address is + put in the ld_rel field. */ + s = bfd_make_section (abfd, ".dynrel"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .hash section holds the dynamic hash table. The address + is put in the ld_hash field. */ + s = bfd_make_section (abfd, ".hash"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .dynsym section holds the dynamic symbols. The address + is put in the ld_stab field. */ + s = bfd_make_section (abfd, ".dynsym"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* The .dynstr section holds the dynamic symbol string table. + The address is put in the ld_symbols field. */ + s = bfd_make_section (abfd, ".dynstr"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + sunos_hash_table (info)->dynamic_sections_created = true; + } + + if ((needed && ! sunos_hash_table (info)->dynamic_sections_needed) + || info->shared) + { + bfd *dynobj; + + dynobj = sunos_hash_table (info)->dynobj; + + s = bfd_get_section_by_name (dynobj, ".got"); + if (s->_raw_size == 0) + s->_raw_size = BYTES_IN_WORD; + + sunos_hash_table (info)->dynamic_sections_needed = true; + sunos_hash_table (info)->got_needed = true; + } + + return true; +} + +/* Add dynamic symbols during a link. This is called by the a.out + backend linker for each object it encounters. */ + +static boolean +sunos_add_dynamic_symbols (abfd, info, symsp, sym_countp, stringsp) + bfd *abfd; + struct bfd_link_info *info; + struct external_nlist **symsp; + bfd_size_type *sym_countp; + char **stringsp; +{ + asection *s; + bfd *dynobj; + struct sunos_dynamic_info *dinfo; + unsigned long need; + + /* Make sure we have all the required sections. */ + if (info->hash->creator == abfd->xvec) + { + if (! sunos_create_dynamic_sections (abfd, info, + (((abfd->flags & DYNAMIC) != 0 + && ! info->relocateable) + ? true + : false))) + return false; + } + + /* There is nothing else to do for a normal object. */ + if ((abfd->flags & DYNAMIC) == 0) + return true; + + dynobj = sunos_hash_table (info)->dynobj; + + /* We do not want to include the sections in a dynamic object in the + output file. We hack by simply clobbering the list of sections + in the BFD. This could be handled more cleanly by, say, a new + section flag; the existing SEC_NEVER_LOAD flag is not the one we + want, because that one still implies that the section takes up + space in the output file. If this is the first object we have + seen, we must preserve the dynamic sections we just created. */ + if (abfd != dynobj) + abfd->sections = NULL; + else + { + asection *s; + + for (s = abfd->sections; + (s->flags & SEC_LINKER_CREATED) == 0; + s = s->next) + ; + abfd->sections = s; + } + + /* The native linker seems to just ignore dynamic objects when -r is + used. */ + if (info->relocateable) + return true; + + /* There's no hope of using a dynamic object which does not exactly + match the format of the output file. */ + if (info->hash->creator != abfd->xvec) + { + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + /* Make sure we have a .need and a .rules sections. These are only + needed if there really is a dynamic object in the link, so they + are not added by sunos_create_dynamic_sections. */ + if (bfd_get_section_by_name (dynobj, ".need") == NULL) + { + /* The .need section holds the list of names of shared objets + which must be included at runtime. The address of this + section is put in the ld_need field. */ + s = bfd_make_section (dynobj, ".need"); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return false; + } + + if (bfd_get_section_by_name (dynobj, ".rules") == NULL) + { + /* The .rules section holds the path to search for shared + objects. The address of this section is put in the ld_rules + field. */ + s = bfd_make_section (dynobj, ".rules"); + if (s == NULL + || ! bfd_set_section_flags (dynobj, s, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, s, 2)) + return false; + } + + /* Pick up the dynamic symbols and return them to the caller. */ + if (! sunos_slurp_dynamic_symtab (abfd)) + return false; + + dinfo = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); + *symsp = dinfo->dynsym; + *sym_countp = dinfo->dynsym_count; + *stringsp = dinfo->dynstr; + + /* Record information about any other objects needed by this one. */ + need = dinfo->dyninfo.ld_need; + while (need != 0) + { + bfd_byte buf[16]; + unsigned long name, flags; + unsigned short major_vno, minor_vno; + struct bfd_link_needed_list *needed, **pp; + char *namebuf, *p; + size_t alc; + bfd_byte b; + char *namecopy; + + if (bfd_seek (abfd, need, SEEK_SET) != 0 + || bfd_read (buf, 1, 16, abfd) != 16) + return false; + + /* For the format of an ld_need entry, see aout/sun4.h. We + should probably define structs for this manipulation. */ + + name = bfd_get_32 (abfd, buf); + flags = bfd_get_32 (abfd, buf + 4); + major_vno = (unsigned short)bfd_get_16 (abfd, buf + 8); + minor_vno = (unsigned short)bfd_get_16 (abfd, buf + 10); + need = bfd_get_32 (abfd, buf + 12); + + needed = ((struct bfd_link_needed_list *) + bfd_alloc (abfd, sizeof (struct bfd_link_needed_list))); + if (needed == NULL) + return false; + needed->by = abfd; + + /* We return the name as [-l]name[.maj][.min]. */ + alc = 30; + namebuf = (char *) bfd_malloc (alc + 1); + if (namebuf == NULL) + return false; + p = namebuf; + + if ((flags & 0x80000000) != 0) + { + *p++ = '-'; + *p++ = 'l'; + } + if (bfd_seek (abfd, name, SEEK_SET) != 0) + { + free (namebuf); + return false; + } + + do + { + if (bfd_read (&b, 1, 1, abfd) != 1) + { + free (namebuf); + return false; + } + + if ((size_t) (p - namebuf) >= alc) + { + char *n; + + alc *= 2; + n = (char *) bfd_realloc (namebuf, alc + 1); + if (n == NULL) + { + free (namebuf); + return false; + } + p = n + (p - namebuf); + namebuf = n; + } + + *p++ = b; + } + while (b != '\0'); + + if (major_vno == 0) + *p = '\0'; + else + { + char majbuf[30]; + char minbuf[30]; + + sprintf (majbuf, ".%d", major_vno); + if (minor_vno == 0) + minbuf[0] = '\0'; + else + sprintf (minbuf, ".%d", minor_vno); + + if ((p - namebuf) + strlen (majbuf) + strlen (minbuf) >= alc) + { + char *n; + + alc = (p - namebuf) + strlen (majbuf) + strlen (minbuf); + n = (char *) bfd_realloc (namebuf, alc + 1); + if (n == NULL) + { + free (namebuf); + return false; + } + p = n + (p - namebuf); + namebuf = n; + } + + strcpy (p, majbuf); + strcat (p, minbuf); + } + + namecopy = bfd_alloc (abfd, strlen (namebuf) + 1); + if (namecopy == NULL) + { + free (namebuf); + return false; + } + strcpy (namecopy, namebuf); + free (namebuf); + needed->name = namecopy; + + needed->next = NULL; + + for (pp = &sunos_hash_table (info)->needed; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = needed; + } + + return true; +} + +/* Function to add a single symbol to the linker hash table. This is + a wrapper around _bfd_generic_link_add_one_symbol which handles the + tweaking needed for dynamic linking support. */ + +static boolean +sunos_add_one_symbol (info, abfd, name, flags, section, value, string, + copy, collect, hashp) + struct bfd_link_info *info; + bfd *abfd; + const char *name; + flagword flags; + asection *section; + bfd_vma value; + const char *string; + boolean copy; + boolean collect; + struct bfd_link_hash_entry **hashp; +{ + struct sunos_link_hash_entry *h; + int new_flag; + + if ((flags & (BSF_INDIRECT | BSF_WARNING | BSF_CONSTRUCTOR)) != 0 + || ! bfd_is_und_section (section)) + h = sunos_link_hash_lookup (sunos_hash_table (info), name, true, copy, + false); + else + h = ((struct sunos_link_hash_entry *) + bfd_wrapped_link_hash_lookup (abfd, info, name, true, copy, false)); + if (h == NULL) + return false; + + if (hashp != NULL) + *hashp = (struct bfd_link_hash_entry *) h; + + /* Treat a common symbol in a dynamic object as defined in the .bss + section of the dynamic object. We don't want to allocate space + for it in our process image. */ + if ((abfd->flags & DYNAMIC) != 0 + && bfd_is_com_section (section)) + section = obj_bsssec (abfd); + + if (! bfd_is_und_section (section) + && h->root.root.type != bfd_link_hash_new + && h->root.root.type != bfd_link_hash_undefined + && h->root.root.type != bfd_link_hash_defweak) + { + /* We are defining the symbol, and it is already defined. This + is a potential multiple definition error. */ + if ((abfd->flags & DYNAMIC) != 0) + { + /* The definition we are adding is from a dynamic object. + We do not want this new definition to override the + existing definition, so we pretend it is just a + reference. */ + section = bfd_und_section_ptr; + } + else if (h->root.root.type == bfd_link_hash_defined + && h->root.root.u.def.section->owner != NULL + && (h->root.root.u.def.section->owner->flags & DYNAMIC) != 0) + { + /* The existing definition is from a dynamic object. We + want to override it with the definition we just found. + Clobber the existing definition. */ + h->root.root.type = bfd_link_hash_undefined; + h->root.root.u.undef.abfd = h->root.root.u.def.section->owner; + } + else if (h->root.root.type == bfd_link_hash_common + && (h->root.root.u.c.p->section->owner->flags & DYNAMIC) != 0) + { + /* The existing definition is from a dynamic object. We + want to override it with the definition we just found. + Clobber the existing definition. We can't set it to new, + because it is on the undefined list. */ + h->root.root.type = bfd_link_hash_undefined; + h->root.root.u.undef.abfd = h->root.root.u.c.p->section->owner; + } + } + + if ((abfd->flags & DYNAMIC) != 0 + && abfd->xvec == info->hash->creator + && (h->flags & SUNOS_CONSTRUCTOR) != 0) + { + /* The existing symbol is a constructor symbol, and this symbol + is from a dynamic object. A constructor symbol is actually a + definition, although the type will be bfd_link_hash_undefined + at this point. We want to ignore the definition from the + dynamic object. */ + section = bfd_und_section_ptr; + } + else if ((flags & BSF_CONSTRUCTOR) != 0 + && (abfd->flags & DYNAMIC) == 0 + && h->root.root.type == bfd_link_hash_defined + && h->root.root.u.def.section->owner != NULL + && (h->root.root.u.def.section->owner->flags & DYNAMIC) != 0) + { + /* The existing symbol is defined by a dynamic object, and this + is a constructor symbol. As above, we want to force the use + of the constructor symbol from the regular object. */ + h->root.root.type = bfd_link_hash_new; + } + + /* Do the usual procedure for adding a symbol. */ + if (! _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, + value, string, copy, collect, + hashp)) + return false; + + if (abfd->xvec == info->hash->creator) + { + /* Set a flag in the hash table entry indicating the type of + reference or definition we just found. Keep a count of the + number of dynamic symbols we find. A dynamic symbol is one + which is referenced or defined by both a regular object and a + shared object. */ + if ((abfd->flags & DYNAMIC) == 0) + { + if (bfd_is_und_section (section)) + new_flag = SUNOS_REF_REGULAR; + else + new_flag = SUNOS_DEF_REGULAR; + } + else + { + if (bfd_is_und_section (section)) + new_flag = SUNOS_REF_DYNAMIC; + else + new_flag = SUNOS_DEF_DYNAMIC; + } + h->flags |= new_flag; + + if (h->dynindx == -1 + && (h->flags & (SUNOS_DEF_REGULAR | SUNOS_REF_REGULAR)) != 0) + { + ++sunos_hash_table (info)->dynsymcount; + h->dynindx = -2; + } + + if ((flags & BSF_CONSTRUCTOR) != 0 + && (abfd->flags & DYNAMIC) == 0) + h->flags |= SUNOS_CONSTRUCTOR; + } + + return true; +} + +/* Return the list of objects needed by BFD. */ + +/*ARGSUSED*/ +struct bfd_link_needed_list * +bfd_sunos_get_needed_list (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + if (info->hash->creator != &MY(vec)) + return NULL; + return sunos_hash_table (info)->needed; +} + +/* Record an assignment made to a symbol by a linker script. We need + this in case some dynamic object refers to this symbol. */ + +boolean +bfd_sunos_record_link_assignment (output_bfd, info, name) + bfd *output_bfd; + struct bfd_link_info *info; + const char *name; +{ + struct sunos_link_hash_entry *h; + + if (output_bfd->xvec != &MY(vec)) + return true; + + /* This is called after we have examined all the input objects. If + the symbol does not exist, it merely means that no object refers + to it, and we can just ignore it at this point. */ + h = sunos_link_hash_lookup (sunos_hash_table (info), name, + false, false, false); + if (h == NULL) + return true; + + /* In a shared library, the __DYNAMIC symbol does not appear in the + dynamic symbol table. */ + if (! info->shared || strcmp (name, "__DYNAMIC") != 0) + { + h->flags |= SUNOS_DEF_REGULAR; + + if (h->dynindx == -1) + { + ++sunos_hash_table (info)->dynsymcount; + h->dynindx = -2; + } + } + + return true; +} + +/* Set up the sizes and contents of the dynamic sections created in + sunos_add_dynamic_symbols. This is called by the SunOS linker + emulation before_allocation routine. We must set the sizes of the + sections before the linker sets the addresses of the various + sections. This unfortunately requires reading all the relocs so + that we can work out which ones need to become dynamic relocs. If + info->keep_memory is true, we keep the relocs in memory; otherwise, + we discard them, and will read them again later. */ + +boolean +bfd_sunos_size_dynamic_sections (output_bfd, info, sdynptr, sneedptr, + srulesptr) + bfd *output_bfd; + struct bfd_link_info *info; + asection **sdynptr; + asection **sneedptr; + asection **srulesptr; +{ + bfd *dynobj; + size_t dynsymcount; + struct sunos_link_hash_entry *h; + asection *s; + size_t bucketcount; + size_t hashalloc; + size_t i; + bfd *sub; + + *sdynptr = NULL; + *sneedptr = NULL; + *srulesptr = NULL; + + if (info->relocateable) + return true; + + if (output_bfd->xvec != &MY(vec)) + return true; + + /* Look through all the input BFD's and read their relocs. It would + be better if we didn't have to do this, but there is no other way + to determine the number of dynamic relocs we need, and, more + importantly, there is no other way to know which symbols should + get an entry in the procedure linkage table. */ + for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) + { + if ((sub->flags & DYNAMIC) == 0 + && sub->xvec == output_bfd->xvec) + { + if (! sunos_scan_relocs (info, sub, obj_textsec (sub), + exec_hdr (sub)->a_trsize) + || ! sunos_scan_relocs (info, sub, obj_datasec (sub), + exec_hdr (sub)->a_drsize)) + return false; + } + } + + dynobj = sunos_hash_table (info)->dynobj; + dynsymcount = sunos_hash_table (info)->dynsymcount; + + /* If there were no dynamic objects in the link, and we don't need + to build a global offset table, there is nothing to do here. */ + if (! sunos_hash_table (info)->dynamic_sections_needed + && ! sunos_hash_table (info)->got_needed) + return true; + + /* If __GLOBAL_OFFSET_TABLE_ was mentioned, define it. */ + h = sunos_link_hash_lookup (sunos_hash_table (info), + "__GLOBAL_OFFSET_TABLE_", false, false, false); + if (h != NULL && (h->flags & SUNOS_REF_REGULAR) != 0) + { + h->flags |= SUNOS_DEF_REGULAR; + if (h->dynindx == -1) + { + ++sunos_hash_table (info)->dynsymcount; + h->dynindx = -2; + } + h->root.root.type = bfd_link_hash_defined; + h->root.root.u.def.section = bfd_get_section_by_name (dynobj, ".got"); + + /* If the .got section is more than 0x1000 bytes, we set + __GLOBAL_OFFSET_TABLE_ to be 0x1000 bytes into the section, + so that 13 bit relocations have a greater chance of working. */ + s = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (s != NULL); + if (s->_raw_size >= 0x1000) + h->root.root.u.def.value = 0x1000; + else + h->root.root.u.def.value = 0; + + sunos_hash_table (info)->got_base = h->root.root.u.def.value; + } + + /* If there are any shared objects in the link, then we need to set + up the dynamic linking information. */ + if (sunos_hash_table (info)->dynamic_sections_needed) + { + *sdynptr = bfd_get_section_by_name (dynobj, ".dynamic"); + + /* The .dynamic section is always the same size. */ + s = *sdynptr; + BFD_ASSERT (s != NULL); + s->_raw_size = (sizeof (struct external_sun4_dynamic) + + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE + + sizeof (struct external_sun4_dynamic_link)); + + /* Set the size of the .dynsym and .hash sections. We counted + the number of dynamic symbols as we read the input files. We + will build the dynamic symbol table (.dynsym) and the hash + table (.hash) when we build the final symbol table, because + until then we do not know the correct value to give the + symbols. We build the dynamic symbol string table (.dynstr) + in a traversal of the symbol table using + sunos_scan_dynamic_symbol. */ + s = bfd_get_section_by_name (dynobj, ".dynsym"); + BFD_ASSERT (s != NULL); + s->_raw_size = dynsymcount * sizeof (struct external_nlist); + s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + return false; + + /* The number of buckets is just the number of symbols divided + by four. To compute the final size of the hash table, we + must actually compute the hash table. Normally we need + exactly as many entries in the hash table as there are + dynamic symbols, but if some of the buckets are not used we + will need additional entries. In the worst case, every + symbol will hash to the same bucket, and we will need + BUCKETCOUNT - 1 extra entries. */ + if (dynsymcount >= 4) + bucketcount = dynsymcount / 4; + else if (dynsymcount > 0) + bucketcount = dynsymcount; + else + bucketcount = 1; + s = bfd_get_section_by_name (dynobj, ".hash"); + BFD_ASSERT (s != NULL); + hashalloc = (dynsymcount + bucketcount - 1) * HASH_ENTRY_SIZE; + s->contents = (bfd_byte *) bfd_alloc (dynobj, hashalloc); + if (s->contents == NULL && dynsymcount > 0) + return false; + memset (s->contents, 0, hashalloc); + for (i = 0; i < bucketcount; i++) + PUT_WORD (output_bfd, (bfd_vma) -1, s->contents + i * HASH_ENTRY_SIZE); + s->_raw_size = bucketcount * HASH_ENTRY_SIZE; + + sunos_hash_table (info)->bucketcount = bucketcount; + + /* Scan all the symbols, place them in the dynamic symbol table, + and build the dynamic hash table. We reuse dynsymcount as a + counter for the number of symbols we have added so far. */ + sunos_hash_table (info)->dynsymcount = 0; + sunos_link_hash_traverse (sunos_hash_table (info), + sunos_scan_dynamic_symbol, + (PTR) info); + BFD_ASSERT (sunos_hash_table (info)->dynsymcount == dynsymcount); + + /* The SunOS native linker seems to align the total size of the + symbol strings to a multiple of 8. I don't know if this is + important, but it can't hurt much. */ + s = bfd_get_section_by_name (dynobj, ".dynstr"); + BFD_ASSERT (s != NULL); + if ((s->_raw_size & 7) != 0) + { + bfd_size_type add; + bfd_byte *contents; + + add = 8 - (s->_raw_size & 7); + contents = (bfd_byte *) bfd_realloc (s->contents, + (size_t) (s->_raw_size + add)); + if (contents == NULL) + return false; + memset (contents + s->_raw_size, 0, (size_t) add); + s->contents = contents; + s->_raw_size += add; + } + } + + /* Now that we have worked out the sizes of the procedure linkage + table and the dynamic relocs, allocate storage for them. */ + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + if (s->_raw_size != 0) + { + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL) + return false; + + /* Fill in the first entry in the table. */ + switch (bfd_get_arch (dynobj)) + { + case bfd_arch_sparc: + memcpy (s->contents, sparc_plt_first_entry, SPARC_PLT_ENTRY_SIZE); + break; + + case bfd_arch_m68k: + memcpy (s->contents, m68k_plt_first_entry, M68K_PLT_ENTRY_SIZE); + break; + + default: + abort (); + } + } + + s = bfd_get_section_by_name (dynobj, ".dynrel"); + if (s->_raw_size != 0) + { + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL) + return false; + } + /* We use the reloc_count field to keep track of how many of the + relocs we have output so far. */ + s->reloc_count = 0; + + /* Make space for the global offset table. */ + s = bfd_get_section_by_name (dynobj, ".got"); + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL) + return false; + + *sneedptr = bfd_get_section_by_name (dynobj, ".need"); + *srulesptr = bfd_get_section_by_name (dynobj, ".rules"); + + return true; +} + +/* Scan the relocs for an input section. */ + +static boolean +sunos_scan_relocs (info, abfd, sec, rel_size) + struct bfd_link_info *info; + bfd *abfd; + asection *sec; + bfd_size_type rel_size; +{ + PTR relocs; + PTR free_relocs = NULL; + + if (rel_size == 0) + return true; + + if (! info->keep_memory) + relocs = free_relocs = bfd_malloc ((size_t) rel_size); + else + { + struct aout_section_data_struct *n; + + n = ((struct aout_section_data_struct *) + bfd_alloc (abfd, sizeof (struct aout_section_data_struct))); + if (n == NULL) + relocs = NULL; + else + { + set_aout_section_data (sec, n); + relocs = bfd_malloc ((size_t) rel_size); + aout_section_data (sec)->relocs = relocs; + } + } + if (relocs == NULL) + return false; + + if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0 + || bfd_read (relocs, 1, rel_size, abfd) != rel_size) + goto error_return; + + if (obj_reloc_entry_size (abfd) == RELOC_STD_SIZE) + { + if (! sunos_scan_std_relocs (info, abfd, sec, + (struct reloc_std_external *) relocs, + rel_size)) + goto error_return; + } + else + { + if (! sunos_scan_ext_relocs (info, abfd, sec, + (struct reloc_ext_external *) relocs, + rel_size)) + goto error_return; + } + + if (free_relocs != NULL) + free (free_relocs); + + return true; + + error_return: + if (free_relocs != NULL) + free (free_relocs); + return false; +} + +/* Scan the relocs for an input section using standard relocs. We + need to figure out what to do for each reloc against a dynamic + symbol. If the symbol is in the .text section, an entry is made in + the procedure linkage table. Note that this will do the wrong + thing if the symbol is actually data; I don't think the Sun 3 + native linker handles this case correctly either. If the symbol is + not in the .text section, we must preserve the reloc as a dynamic + reloc. FIXME: We should also handle the PIC relocs here by + building global offset table entries. */ + +static boolean +sunos_scan_std_relocs (info, abfd, sec, relocs, rel_size) + struct bfd_link_info *info; + bfd *abfd; + asection *sec; + const struct reloc_std_external *relocs; + bfd_size_type rel_size; +{ + bfd *dynobj; + asection *splt = NULL; + asection *srel = NULL; + struct sunos_link_hash_entry **sym_hashes; + const struct reloc_std_external *rel, *relend; + + /* We only know how to handle m68k plt entries. */ + if (bfd_get_arch (abfd) != bfd_arch_m68k) + { + bfd_set_error (bfd_error_invalid_target); + return false; + } + + dynobj = NULL; + + sym_hashes = (struct sunos_link_hash_entry **) obj_aout_sym_hashes (abfd); + + relend = relocs + rel_size / RELOC_STD_SIZE; + for (rel = relocs; rel < relend; rel++) + { + int r_index; + struct sunos_link_hash_entry *h; + + /* We only want relocs against external symbols. */ + if (bfd_header_big_endian (abfd)) + { + if ((rel->r_type[0] & RELOC_STD_BITS_EXTERN_BIG) == 0) + continue; + } + else + { + if ((rel->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE) == 0) + continue; + } + + /* Get the symbol index. */ + if (bfd_header_big_endian (abfd)) + r_index = ((rel->r_index[0] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[2]); + else + r_index = ((rel->r_index[2] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[0]); + + /* Get the hash table entry. */ + h = sym_hashes[r_index]; + if (h == NULL) + { + /* This should not normally happen, but it will in any case + be caught in the relocation phase. */ + continue; + } + + /* At this point common symbols have already been allocated, so + we don't have to worry about them. We need to consider that + we may have already seen this symbol and marked it undefined; + if the symbol is really undefined, then SUNOS_DEF_DYNAMIC + will be zero. */ + if (h->root.root.type != bfd_link_hash_defined + && h->root.root.type != bfd_link_hash_defweak + && h->root.root.type != bfd_link_hash_undefined) + continue; + + if ((h->flags & SUNOS_DEF_DYNAMIC) == 0 + || (h->flags & SUNOS_DEF_REGULAR) != 0) + continue; + + if (dynobj == NULL) + { + asection *sgot; + + if (! sunos_create_dynamic_sections (abfd, info, false)) + return false; + dynobj = sunos_hash_table (info)->dynobj; + splt = bfd_get_section_by_name (dynobj, ".plt"); + srel = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (splt != NULL && srel != NULL); + + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + if (sgot->_raw_size == 0) + sgot->_raw_size = BYTES_IN_WORD; + sunos_hash_table (info)->got_needed = true; + } + + BFD_ASSERT ((h->flags & SUNOS_REF_REGULAR) != 0); + BFD_ASSERT (h->plt_offset != 0 + || ((h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + ? (h->root.root.u.def.section->owner->flags + & DYNAMIC) != 0 + : (h->root.root.u.undef.abfd->flags & DYNAMIC) != 0)); + + /* This reloc is against a symbol defined only by a dynamic + object. */ + + if (h->root.root.type == bfd_link_hash_undefined) + { + /* Presumably this symbol was marked as being undefined by + an earlier reloc. */ + srel->_raw_size += RELOC_STD_SIZE; + } + else if ((h->root.root.u.def.section->flags & SEC_CODE) == 0) + { + bfd *sub; + + /* This reloc is not in the .text section. It must be + copied into the dynamic relocs. We mark the symbol as + being undefined. */ + srel->_raw_size += RELOC_STD_SIZE; + sub = h->root.root.u.def.section->owner; + h->root.root.type = bfd_link_hash_undefined; + h->root.root.u.undef.abfd = sub; + } + else + { + /* This symbol is in the .text section. We must give it an + entry in the procedure linkage table, if we have not + already done so. We change the definition of the symbol + to the .plt section; this will cause relocs against it to + be handled correctly. */ + if (h->plt_offset == 0) + { + if (splt->_raw_size == 0) + splt->_raw_size = M68K_PLT_ENTRY_SIZE; + h->plt_offset = splt->_raw_size; + + if ((h->flags & SUNOS_DEF_REGULAR) == 0) + { + h->root.root.u.def.section = splt; + h->root.root.u.def.value = splt->_raw_size; + } + + splt->_raw_size += M68K_PLT_ENTRY_SIZE; + + /* We may also need a dynamic reloc entry. */ + if ((h->flags & SUNOS_DEF_REGULAR) == 0) + srel->_raw_size += RELOC_STD_SIZE; + } + } + } + + return true; +} + +/* Scan the relocs for an input section using extended relocs. We + need to figure out what to do for each reloc against a dynamic + symbol. If the reloc is a WDISP30, and the symbol is in the .text + section, an entry is made in the procedure linkage table. + Otherwise, we must preserve the reloc as a dynamic reloc. */ + +static boolean +sunos_scan_ext_relocs (info, abfd, sec, relocs, rel_size) + struct bfd_link_info *info; + bfd *abfd; + asection *sec; + const struct reloc_ext_external *relocs; + bfd_size_type rel_size; +{ + bfd *dynobj; + struct sunos_link_hash_entry **sym_hashes; + const struct reloc_ext_external *rel, *relend; + asection *splt = NULL; + asection *sgot = NULL; + asection *srel = NULL; + + /* We only know how to handle SPARC plt entries. */ + if (bfd_get_arch (abfd) != bfd_arch_sparc) + { + bfd_set_error (bfd_error_invalid_target); + return false; + } + + dynobj = NULL; + + sym_hashes = (struct sunos_link_hash_entry **) obj_aout_sym_hashes (abfd); + + relend = relocs + rel_size / RELOC_EXT_SIZE; + for (rel = relocs; rel < relend; rel++) + { + unsigned int r_index; + int r_extern; + int r_type; + struct sunos_link_hash_entry *h = NULL; + + /* Swap in the reloc information. */ + if (bfd_header_big_endian (abfd)) + { + r_index = ((rel->r_index[0] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[2]); + r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_BIG)); + r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_BIG) + >> RELOC_EXT_BITS_TYPE_SH_BIG); + } + else + { + r_index = ((rel->r_index[2] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[0]); + r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_LITTLE)); + r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE) + >> RELOC_EXT_BITS_TYPE_SH_LITTLE); + } + + if (r_extern) + { + h = sym_hashes[r_index]; + if (h == NULL) + { + /* This should not normally happen, but it will in any + case be caught in the relocation phase. */ + continue; + } + } + + /* If this is a base relative reloc, we need to make an entry in + the .got section. */ + if (r_type == RELOC_BASE10 + || r_type == RELOC_BASE13 + || r_type == RELOC_BASE22) + { + if (dynobj == NULL) + { + if (! sunos_create_dynamic_sections (abfd, info, false)) + return false; + dynobj = sunos_hash_table (info)->dynobj; + splt = bfd_get_section_by_name (dynobj, ".plt"); + sgot = bfd_get_section_by_name (dynobj, ".got"); + srel = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL); + + /* Make sure we have an initial entry in the .got table. */ + if (sgot->_raw_size == 0) + sgot->_raw_size = BYTES_IN_WORD; + sunos_hash_table (info)->got_needed = true; + } + + if (r_extern) + { + if (h->got_offset != 0) + continue; + + h->got_offset = sgot->_raw_size; + } + else + { + if (r_index >= bfd_get_symcount (abfd)) + { + /* This is abnormal, but should be caught in the + relocation phase. */ + continue; + } + + if (adata (abfd).local_got_offsets == NULL) + { + adata (abfd).local_got_offsets = + (bfd_vma *) bfd_zalloc (abfd, + (bfd_get_symcount (abfd) + * sizeof (bfd_vma))); + if (adata (abfd).local_got_offsets == NULL) + return false; + } + + if (adata (abfd).local_got_offsets[r_index] != 0) + continue; + + adata (abfd).local_got_offsets[r_index] = sgot->_raw_size; + } + + sgot->_raw_size += BYTES_IN_WORD; + + /* If we are making a shared library, or if the symbol is + defined by a dynamic object, we will need a dynamic reloc + entry. */ + if (info->shared + || (h != NULL + && (h->flags & SUNOS_DEF_DYNAMIC) != 0 + && (h->flags & SUNOS_DEF_REGULAR) == 0)) + srel->_raw_size += RELOC_EXT_SIZE; + + continue; + } + + /* Otherwise, we are only interested in relocs against symbols + defined in dynamic objects but not in regular objects. We + only need to consider relocs against external symbols. */ + if (! r_extern) + { + /* But, if we are creating a shared library, we need to + generate an absolute reloc. */ + if (info->shared) + { + if (dynobj == NULL) + { + if (! sunos_create_dynamic_sections (abfd, info, true)) + return false; + dynobj = sunos_hash_table (info)->dynobj; + splt = bfd_get_section_by_name (dynobj, ".plt"); + sgot = bfd_get_section_by_name (dynobj, ".got"); + srel = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL); + } + + srel->_raw_size += RELOC_EXT_SIZE; + } + + continue; + } + + /* At this point common symbols have already been allocated, so + we don't have to worry about them. We need to consider that + we may have already seen this symbol and marked it undefined; + if the symbol is really undefined, then SUNOS_DEF_DYNAMIC + will be zero. */ + if (h->root.root.type != bfd_link_hash_defined + && h->root.root.type != bfd_link_hash_defweak + && h->root.root.type != bfd_link_hash_undefined) + continue; + + if (r_type != RELOC_JMP_TBL + && ! info->shared + && ((h->flags & SUNOS_DEF_DYNAMIC) == 0 + || (h->flags & SUNOS_DEF_REGULAR) != 0)) + continue; + + if (r_type == RELOC_JMP_TBL + && ! info->shared + && (h->flags & SUNOS_DEF_DYNAMIC) == 0 + && (h->flags & SUNOS_DEF_REGULAR) == 0) + { + /* This symbol is apparently undefined. Don't do anything + here; just let the relocation routine report an undefined + symbol. */ + continue; + } + + if (strcmp (h->root.root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) + continue; + + if (dynobj == NULL) + { + if (! sunos_create_dynamic_sections (abfd, info, false)) + return false; + dynobj = sunos_hash_table (info)->dynobj; + splt = bfd_get_section_by_name (dynobj, ".plt"); + sgot = bfd_get_section_by_name (dynobj, ".got"); + srel = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL); + + /* Make sure we have an initial entry in the .got table. */ + if (sgot->_raw_size == 0) + sgot->_raw_size = BYTES_IN_WORD; + sunos_hash_table (info)->got_needed = true; + } + + BFD_ASSERT (r_type == RELOC_JMP_TBL + || info->shared + || (h->flags & SUNOS_REF_REGULAR) != 0); + BFD_ASSERT (r_type == RELOC_JMP_TBL + || info->shared + || h->plt_offset != 0 + || ((h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + ? (h->root.root.u.def.section->owner->flags + & DYNAMIC) != 0 + : (h->root.root.u.undef.abfd->flags & DYNAMIC) != 0)); + + /* This reloc is against a symbol defined only by a dynamic + object, or it is a jump table reloc from PIC compiled code. */ + + if (r_type != RELOC_JMP_TBL + && h->root.root.type == bfd_link_hash_undefined) + { + /* Presumably this symbol was marked as being undefined by + an earlier reloc. */ + srel->_raw_size += RELOC_EXT_SIZE; + } + else if (r_type != RELOC_JMP_TBL + && (h->root.root.u.def.section->flags & SEC_CODE) == 0) + { + bfd *sub; + + /* This reloc is not in the .text section. It must be + copied into the dynamic relocs. We mark the symbol as + being undefined. */ + srel->_raw_size += RELOC_EXT_SIZE; + if ((h->flags & SUNOS_DEF_REGULAR) == 0) + { + sub = h->root.root.u.def.section->owner; + h->root.root.type = bfd_link_hash_undefined; + h->root.root.u.undef.abfd = sub; + } + } + else + { + /* This symbol is in the .text section. We must give it an + entry in the procedure linkage table, if we have not + already done so. We change the definition of the symbol + to the .plt section; this will cause relocs against it to + be handled correctly. */ + if (h->plt_offset == 0) + { + if (splt->_raw_size == 0) + splt->_raw_size = SPARC_PLT_ENTRY_SIZE; + h->plt_offset = splt->_raw_size; + + if ((h->flags & SUNOS_DEF_REGULAR) == 0) + { + if (h->root.root.type == bfd_link_hash_undefined) + h->root.root.type = bfd_link_hash_defined; + h->root.root.u.def.section = splt; + h->root.root.u.def.value = splt->_raw_size; + } + + splt->_raw_size += SPARC_PLT_ENTRY_SIZE; + + /* We will also need a dynamic reloc entry, unless this + is a JMP_TBL reloc produced by linking PIC compiled + code, and we are not making a shared library. */ + if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0) + srel->_raw_size += RELOC_EXT_SIZE; + } + + /* If we are creating a shared library, we need to copy over + any reloc other than a jump table reloc. */ + if (info->shared && r_type != RELOC_JMP_TBL) + srel->_raw_size += RELOC_EXT_SIZE; + } + } + + return true; +} + +/* Build the hash table of dynamic symbols, and to mark as written all + symbols from dynamic objects which we do not plan to write out. */ + +static boolean +sunos_scan_dynamic_symbol (h, data) + struct sunos_link_hash_entry *h; + PTR data; +{ + struct bfd_link_info *info = (struct bfd_link_info *) data; + + /* Set the written flag for symbols we do not want to write out as + part of the regular symbol table. This is all symbols which are + not defined in a regular object file. For some reason symbols + which are referenced by a regular object and defined by a dynamic + object do not seem to show up in the regular symbol table. It is + possible for a symbol to have only SUNOS_REF_REGULAR set here, it + is an undefined symbol which was turned into a common symbol + because it was found in an archive object which was not included + in the link. */ + if ((h->flags & SUNOS_DEF_REGULAR) == 0 + && (h->flags & SUNOS_DEF_DYNAMIC) != 0 + && strcmp (h->root.root.root.string, "__DYNAMIC") != 0) + h->root.written = true; + + /* If this symbol is defined by a dynamic object and referenced by a + regular object, see whether we gave it a reasonable value while + scanning the relocs. */ + + if ((h->flags & SUNOS_DEF_REGULAR) == 0 + && (h->flags & SUNOS_DEF_DYNAMIC) != 0 + && (h->flags & SUNOS_REF_REGULAR) != 0) + { + if ((h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + && ((h->root.root.u.def.section->owner->flags & DYNAMIC) != 0) + && h->root.root.u.def.section->output_section == NULL) + { + bfd *sub; + + /* This symbol is currently defined in a dynamic section + which is not being put into the output file. This + implies that there is no reloc against the symbol. I'm + not sure why this case would ever occur. In any case, we + change the symbol to be undefined. */ + sub = h->root.root.u.def.section->owner; + h->root.root.type = bfd_link_hash_undefined; + h->root.root.u.undef.abfd = sub; + } + } + + /* If this symbol is defined or referenced by a regular file, add it + to the dynamic symbols. */ + if ((h->flags & (SUNOS_DEF_REGULAR | SUNOS_REF_REGULAR)) != 0) + { + asection *s; + size_t len; + bfd_byte *contents; + unsigned char *name; + unsigned long hash; + bfd *dynobj; + + BFD_ASSERT (h->dynindx == -2); + + dynobj = sunos_hash_table (info)->dynobj; + + h->dynindx = sunos_hash_table (info)->dynsymcount; + ++sunos_hash_table (info)->dynsymcount; + + len = strlen (h->root.root.root.string); + + /* We don't bother to construct a BFD hash table for the strings + which are the names of the dynamic symbols. Using a hash + table for the regular symbols is beneficial, because the + regular symbols includes the debugging symbols, which have + long names and are often duplicated in several object files. + There are no debugging symbols in the dynamic symbols. */ + s = bfd_get_section_by_name (dynobj, ".dynstr"); + BFD_ASSERT (s != NULL); + contents = (bfd_byte *) bfd_realloc (s->contents, + s->_raw_size + len + 1); + if (contents == NULL) + return false; + s->contents = contents; + + h->dynstr_index = s->_raw_size; + strcpy ((char *) contents + s->_raw_size, h->root.root.root.string); + s->_raw_size += len + 1; + + /* Add it to the dynamic hash table. */ + name = (unsigned char *) h->root.root.root.string; + hash = 0; + while (*name != '\0') + hash = (hash << 1) + *name++; + hash &= 0x7fffffff; + hash %= sunos_hash_table (info)->bucketcount; + + s = bfd_get_section_by_name (dynobj, ".hash"); + BFD_ASSERT (s != NULL); + + if (GET_SWORD (dynobj, s->contents + hash * HASH_ENTRY_SIZE) == -1) + PUT_WORD (dynobj, h->dynindx, s->contents + hash * HASH_ENTRY_SIZE); + else + { + bfd_vma next; + + next = GET_WORD (dynobj, + (s->contents + + hash * HASH_ENTRY_SIZE + + BYTES_IN_WORD)); + PUT_WORD (dynobj, s->_raw_size / HASH_ENTRY_SIZE, + s->contents + hash * HASH_ENTRY_SIZE + BYTES_IN_WORD); + PUT_WORD (dynobj, h->dynindx, s->contents + s->_raw_size); + PUT_WORD (dynobj, next, s->contents + s->_raw_size + BYTES_IN_WORD); + s->_raw_size += HASH_ENTRY_SIZE; + } + } + + return true; +} + +/* Link a dynamic object. We actually don't have anything to do at + this point. This entry point exists to prevent the regular linker + code from doing anything with the object. */ + +/*ARGSUSED*/ +static boolean +sunos_link_dynamic_object (info, abfd) + struct bfd_link_info *info; + bfd *abfd; +{ + return true; +} + +/* Write out a dynamic symbol. This is called by the final traversal + over the symbol table. */ + +static boolean +sunos_write_dynamic_symbol (output_bfd, info, harg) + bfd *output_bfd; + struct bfd_link_info *info; + struct aout_link_hash_entry *harg; +{ + struct sunos_link_hash_entry *h = (struct sunos_link_hash_entry *) harg; + int type; + bfd_vma val; + asection *s; + struct external_nlist *outsym; + + /* If this symbol is in the procedure linkage table, fill in the + table entry. */ + if (h->plt_offset != 0) + { + bfd *dynobj; + asection *splt; + bfd_byte *p; + asection *s; + bfd_vma r_address; + + dynobj = sunos_hash_table (info)->dynobj; + splt = bfd_get_section_by_name (dynobj, ".plt"); + p = splt->contents + h->plt_offset; + + s = bfd_get_section_by_name (dynobj, ".dynrel"); + + r_address = (splt->output_section->vma + + splt->output_offset + + h->plt_offset); + + switch (bfd_get_arch (output_bfd)) + { + case bfd_arch_sparc: + if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0) + { + bfd_put_32 (output_bfd, SPARC_PLT_ENTRY_WORD0, p); + bfd_put_32 (output_bfd, + (SPARC_PLT_ENTRY_WORD1 + + (((- (h->plt_offset + 4) >> 2) + & 0x3fffffff))), + p + 4); + bfd_put_32 (output_bfd, SPARC_PLT_ENTRY_WORD2 + s->reloc_count, + p + 8); + } + else + { + bfd_vma val; + + val = (h->root.root.u.def.section->output_section->vma + + h->root.root.u.def.section->output_offset + + h->root.root.u.def.value); + bfd_put_32 (output_bfd, + SPARC_PLT_PIC_WORD0 + ((val >> 10) & 0x3fffff), + p); + bfd_put_32 (output_bfd, + SPARC_PLT_PIC_WORD1 + (val & 0x3ff), + p + 4); + bfd_put_32 (output_bfd, SPARC_PLT_PIC_WORD2, p + 8); + } + break; + + case bfd_arch_m68k: + if (! info->shared && (h->flags & SUNOS_DEF_REGULAR) != 0) + abort (); + bfd_put_16 (output_bfd, M68K_PLT_ENTRY_WORD0, p); + bfd_put_32 (output_bfd, (- (h->plt_offset + 2)), p + 2); + bfd_put_16 (output_bfd, s->reloc_count, p + 6); + r_address += 2; + break; + + default: + abort (); + } + + /* We also need to add a jump table reloc, unless this is the + result of a JMP_TBL reloc from PIC compiled code. */ + if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0) + { + BFD_ASSERT (h->dynindx >= 0); + BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) + < s->_raw_size); + p = s->contents + s->reloc_count * obj_reloc_entry_size (output_bfd); + if (obj_reloc_entry_size (output_bfd) == RELOC_STD_SIZE) + { + struct reloc_std_external *srel; + + srel = (struct reloc_std_external *) p; + PUT_WORD (output_bfd, r_address, srel->r_address); + if (bfd_header_big_endian (output_bfd)) + { + srel->r_index[0] = (bfd_byte)(h->dynindx >> 16); + srel->r_index[1] = (bfd_byte)(h->dynindx >> 8); + srel->r_index[2] = (bfd_byte)(h->dynindx); + srel->r_type[0] = (RELOC_STD_BITS_EXTERN_BIG + | RELOC_STD_BITS_JMPTABLE_BIG); + } + else + { + srel->r_index[2] = (bfd_byte)(h->dynindx >> 16); + srel->r_index[1] = (bfd_byte)(h->dynindx >> 8); + srel->r_index[0] = (bfd_byte)h->dynindx; + srel->r_type[0] = (RELOC_STD_BITS_EXTERN_LITTLE + | RELOC_STD_BITS_JMPTABLE_LITTLE); + } + } + else + { + struct reloc_ext_external *erel; + + erel = (struct reloc_ext_external *) p; + PUT_WORD (output_bfd, r_address, erel->r_address); + if (bfd_header_big_endian (output_bfd)) + { + erel->r_index[0] = (bfd_byte)(h->dynindx >> 16); + erel->r_index[1] = (bfd_byte)(h->dynindx >> 8); + erel->r_index[2] = (bfd_byte)h->dynindx; + erel->r_type[0] = + (RELOC_EXT_BITS_EXTERN_BIG + | (RELOC_JMP_SLOT << RELOC_EXT_BITS_TYPE_SH_BIG)); + } + else + { + erel->r_index[2] = (bfd_byte)(h->dynindx >> 16); + erel->r_index[1] = (bfd_byte)(h->dynindx >> 8); + erel->r_index[0] = (bfd_byte)h->dynindx; + erel->r_type[0] = + (RELOC_EXT_BITS_EXTERN_LITTLE + | (RELOC_JMP_SLOT << RELOC_EXT_BITS_TYPE_SH_LITTLE)); + } + PUT_WORD (output_bfd, (bfd_vma) 0, erel->r_addend); + } + + ++s->reloc_count; + } + } + + /* If this is not a dynamic symbol, we don't have to do anything + else. We only check this after handling the PLT entry, because + we can have a PLT entry for a nondynamic symbol when linking PIC + compiled code from a regular object. */ + if (h->dynindx < 0) + return true; + + switch (h->root.root.type) + { + default: + case bfd_link_hash_new: + abort (); + /* Avoid variable not initialized warnings. */ + return true; + case bfd_link_hash_undefined: + type = N_UNDF | N_EXT; + val = 0; + break; + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + { + asection *sec; + asection *output_section; + + sec = h->root.root.u.def.section; + output_section = sec->output_section; + BFD_ASSERT (bfd_is_abs_section (output_section) + || output_section->owner == output_bfd); + if (h->plt_offset != 0 + && (h->flags & SUNOS_DEF_REGULAR) == 0) + { + type = N_UNDF | N_EXT; + val = 0; + } + else + { + if (output_section == obj_textsec (output_bfd)) + type = (h->root.root.type == bfd_link_hash_defined + ? N_TEXT + : N_WEAKT); + else if (output_section == obj_datasec (output_bfd)) + type = (h->root.root.type == bfd_link_hash_defined + ? N_DATA + : N_WEAKD); + else if (output_section == obj_bsssec (output_bfd)) + type = (h->root.root.type == bfd_link_hash_defined + ? N_BSS + : N_WEAKB); + else + type = (h->root.root.type == bfd_link_hash_defined + ? N_ABS + : N_WEAKA); + type |= N_EXT; + val = (h->root.root.u.def.value + + output_section->vma + + sec->output_offset); + } + } + break; + case bfd_link_hash_common: + type = N_UNDF | N_EXT; + val = h->root.root.u.c.size; + break; + case bfd_link_hash_undefweak: + type = N_WEAKU; + val = 0; + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + /* FIXME: Ignore these for now. The circumstances under which + they should be written out are not clear to me. */ + return true; + } + + s = bfd_get_section_by_name (sunos_hash_table (info)->dynobj, ".dynsym"); + BFD_ASSERT (s != NULL); + outsym = ((struct external_nlist *) + (s->contents + h->dynindx * EXTERNAL_NLIST_SIZE)); + + bfd_h_put_8 (output_bfd, type, outsym->e_type); + bfd_h_put_8 (output_bfd, 0, outsym->e_other); + + /* FIXME: The native linker doesn't use 0 for desc. It seems to use + one less than the desc value in the shared library, although that + seems unlikely. */ + bfd_h_put_16 (output_bfd, 0, outsym->e_desc); + + PUT_WORD (output_bfd, h->dynstr_index, outsym->e_strx); + PUT_WORD (output_bfd, val, outsym->e_value); + + return true; +} + +/* This is called for each reloc against an external symbol. If this + is a reloc which are are going to copy as a dynamic reloc, then + copy it over, and tell the caller to not bother processing this + reloc. */ + +/*ARGSUSED*/ +static boolean +sunos_check_dynamic_reloc (info, input_bfd, input_section, harg, reloc, + contents, skip, relocationp) + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + struct aout_link_hash_entry *harg; + PTR reloc; + bfd_byte *contents; + boolean *skip; + bfd_vma *relocationp; +{ + struct sunos_link_hash_entry *h = (struct sunos_link_hash_entry *) harg; + bfd *dynobj; + boolean baserel; + boolean jmptbl; + asection *s; + bfd_byte *p; + long indx; + + *skip = false; + + dynobj = sunos_hash_table (info)->dynobj; + + if (h != NULL && h->plt_offset != 0) + { + asection *splt; + + /* Redirect the relocation to the PLT entry. */ + splt = bfd_get_section_by_name (dynobj, ".plt"); + *relocationp = (splt->output_section->vma + + splt->output_offset + + h->plt_offset); + } + + if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE) + { + struct reloc_std_external *srel; + + srel = (struct reloc_std_external *) reloc; + if (bfd_header_big_endian (input_bfd)) + { + baserel = (0 != (srel->r_type[0] & RELOC_STD_BITS_BASEREL_BIG)); + jmptbl = (0 != (srel->r_type[0] & RELOC_STD_BITS_JMPTABLE_BIG)); + } + else + { + baserel = (0 != (srel->r_type[0] & RELOC_STD_BITS_BASEREL_LITTLE)); + jmptbl = (0 != (srel->r_type[0] & RELOC_STD_BITS_JMPTABLE_LITTLE)); + } + } + else + { + struct reloc_ext_external *erel; + int r_type; + + erel = (struct reloc_ext_external *) reloc; + if (bfd_header_big_endian (input_bfd)) + r_type = ((erel->r_type[0] & RELOC_EXT_BITS_TYPE_BIG) + >> RELOC_EXT_BITS_TYPE_SH_BIG); + else + r_type = ((erel->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE) + >> RELOC_EXT_BITS_TYPE_SH_LITTLE); + baserel = (r_type == RELOC_BASE10 + || r_type == RELOC_BASE13 + || r_type == RELOC_BASE22); + jmptbl = r_type == RELOC_JMP_TBL; + } + + if (baserel) + { + bfd_vma *got_offsetp; + asection *sgot; + + if (h != NULL) + got_offsetp = &h->got_offset; + else if (adata (input_bfd).local_got_offsets == NULL) + got_offsetp = NULL; + else + { + struct reloc_std_external *srel; + int r_index; + + srel = (struct reloc_std_external *) reloc; + if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE) + { + if (bfd_header_big_endian (input_bfd)) + r_index = ((srel->r_index[0] << 16) + | (srel->r_index[1] << 8) + | srel->r_index[2]); + else + r_index = ((srel->r_index[2] << 16) + | (srel->r_index[1] << 8) + | srel->r_index[0]); + } + else + { + struct reloc_ext_external *erel; + + erel = (struct reloc_ext_external *) reloc; + if (bfd_header_big_endian (input_bfd)) + r_index = ((erel->r_index[0] << 16) + | (erel->r_index[1] << 8) + | erel->r_index[2]); + else + r_index = ((erel->r_index[2] << 16) + | (erel->r_index[1] << 8) + | erel->r_index[0]); + } + + got_offsetp = adata (input_bfd).local_got_offsets + r_index; + } + + BFD_ASSERT (got_offsetp != NULL && *got_offsetp != 0); + + sgot = bfd_get_section_by_name (dynobj, ".got"); + + /* We set the least significant bit to indicate whether we have + already initialized the GOT entry. */ + if ((*got_offsetp & 1) == 0) + { + if (h == NULL + || (! info->shared + && ((h->flags & SUNOS_DEF_DYNAMIC) == 0 + || (h->flags & SUNOS_DEF_REGULAR) != 0))) + PUT_WORD (dynobj, *relocationp, sgot->contents + *got_offsetp); + else + PUT_WORD (dynobj, 0, sgot->contents + *got_offsetp); + + if (info->shared + || (h != NULL + && (h->flags & SUNOS_DEF_DYNAMIC) != 0 + && (h->flags & SUNOS_DEF_REGULAR) == 0)) + { + /* We need to create a GLOB_DAT or 32 reloc to tell the + dynamic linker to fill in this entry in the table. */ + + s = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (s != NULL); + BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) + < s->_raw_size); + + p = (s->contents + + s->reloc_count * obj_reloc_entry_size (dynobj)); + + if (h != NULL) + indx = h->dynindx; + else + indx = 0; + + if (obj_reloc_entry_size (dynobj) == RELOC_STD_SIZE) + { + struct reloc_std_external *srel; + + srel = (struct reloc_std_external *) p; + PUT_WORD (dynobj, + (*got_offsetp + + sgot->output_section->vma + + sgot->output_offset), + srel->r_address); + if (bfd_header_big_endian (dynobj)) + { + srel->r_index[0] = (bfd_byte)(indx >> 16); + srel->r_index[1] = (bfd_byte)(indx >> 8); + srel->r_index[2] = (bfd_byte)indx; + if (h == NULL) + srel->r_type[0] = 2 << RELOC_STD_BITS_LENGTH_SH_BIG; + else + srel->r_type[0] = + (RELOC_STD_BITS_EXTERN_BIG + | RELOC_STD_BITS_BASEREL_BIG + | RELOC_STD_BITS_RELATIVE_BIG + | (2 << RELOC_STD_BITS_LENGTH_SH_BIG)); + } + else + { + srel->r_index[2] = (bfd_byte)(indx >> 16); + srel->r_index[1] = (bfd_byte)(indx >> 8); + srel->r_index[0] = (bfd_byte)indx; + if (h == NULL) + srel->r_type[0] = 2 << RELOC_STD_BITS_LENGTH_SH_LITTLE; + else + srel->r_type[0] = + (RELOC_STD_BITS_EXTERN_LITTLE + | RELOC_STD_BITS_BASEREL_LITTLE + | RELOC_STD_BITS_RELATIVE_LITTLE + | (2 << RELOC_STD_BITS_LENGTH_SH_LITTLE)); + } + } + else + { + struct reloc_ext_external *erel; + + erel = (struct reloc_ext_external *) p; + PUT_WORD (dynobj, + (*got_offsetp + + sgot->output_section->vma + + sgot->output_offset), + erel->r_address); + if (bfd_header_big_endian (dynobj)) + { + erel->r_index[0] = (bfd_byte)(indx >> 16); + erel->r_index[1] = (bfd_byte)(indx >> 8); + erel->r_index[2] = (bfd_byte)indx; + if (h == NULL) + erel->r_type[0] = + RELOC_32 << RELOC_EXT_BITS_TYPE_SH_BIG; + else + erel->r_type[0] = + (RELOC_EXT_BITS_EXTERN_BIG + | (RELOC_GLOB_DAT << RELOC_EXT_BITS_TYPE_SH_BIG)); + } + else + { + erel->r_index[2] = (bfd_byte)(indx >> 16); + erel->r_index[1] = (bfd_byte)(indx >> 8); + erel->r_index[0] = (bfd_byte)indx; + if (h == NULL) + erel->r_type[0] = + RELOC_32 << RELOC_EXT_BITS_TYPE_SH_LITTLE; + else + erel->r_type[0] = + (RELOC_EXT_BITS_EXTERN_LITTLE + | (RELOC_GLOB_DAT + << RELOC_EXT_BITS_TYPE_SH_LITTLE)); + } + PUT_WORD (dynobj, 0, erel->r_addend); + } + + ++s->reloc_count; + } + + *got_offsetp |= 1; + } + + *relocationp = (sgot->vma + + (*got_offsetp &~ 1) + - sunos_hash_table (info)->got_base); + + /* There is nothing else to do for a base relative reloc. */ + return true; + } + + if (! sunos_hash_table (info)->dynamic_sections_needed) + return true; + if (! info->shared) + { + if (h == NULL + || h->dynindx == -1 + || h->root.root.type != bfd_link_hash_undefined + || (h->flags & SUNOS_DEF_REGULAR) != 0 + || (h->flags & SUNOS_DEF_DYNAMIC) == 0 + || (h->root.root.u.undef.abfd->flags & DYNAMIC) == 0) + return true; + } + else + { + if (h != NULL + && (h->dynindx == -1 + || jmptbl + || strcmp (h->root.root.root.string, + "__GLOBAL_OFFSET_TABLE_") == 0)) + return true; + } + + /* It looks like this is a reloc we are supposed to copy. */ + + s = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (s != NULL); + BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) < s->_raw_size); + + p = s->contents + s->reloc_count * obj_reloc_entry_size (dynobj); + + /* Copy the reloc over. */ + memcpy (p, reloc, obj_reloc_entry_size (dynobj)); + + if (h != NULL) + indx = h->dynindx; + else + indx = 0; + + /* Adjust the address and symbol index. */ + if (obj_reloc_entry_size (dynobj) == RELOC_STD_SIZE) + { + struct reloc_std_external *srel; + + srel = (struct reloc_std_external *) p; + PUT_WORD (dynobj, + (GET_WORD (dynobj, srel->r_address) + + input_section->output_section->vma + + input_section->output_offset), + srel->r_address); + if (bfd_header_big_endian (dynobj)) + { + srel->r_index[0] = (bfd_byte)(indx >> 16); + srel->r_index[1] = (bfd_byte)(indx >> 8); + srel->r_index[2] = (bfd_byte)indx; + } + else + { + srel->r_index[2] = (bfd_byte)(indx >> 16); + srel->r_index[1] = (bfd_byte)(indx >> 8); + srel->r_index[0] = (bfd_byte)indx; + } + } + else + { + struct reloc_ext_external *erel; + + erel = (struct reloc_ext_external *) p; + PUT_WORD (dynobj, + (GET_WORD (dynobj, erel->r_address) + + input_section->output_section->vma + + input_section->output_offset), + erel->r_address); + if (bfd_header_big_endian (dynobj)) + { + erel->r_index[0] = (bfd_byte)(indx >> 16); + erel->r_index[1] = (bfd_byte)(indx >> 8); + erel->r_index[2] = (bfd_byte)indx; + } + else + { + erel->r_index[2] = (bfd_byte)(indx >> 16); + erel->r_index[1] = (bfd_byte)(indx >> 8); + erel->r_index[0] = (bfd_byte)indx; + } + } + + ++s->reloc_count; + + if (h != NULL) + *skip = true; + + return true; +} + +/* Finish up the dynamic linking information. */ + +static boolean +sunos_finish_dynamic_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *o; + asection *s; + asection *sdyn; + + if (! sunos_hash_table (info)->dynamic_sections_needed + && ! sunos_hash_table (info)->got_needed) + return true; + + dynobj = sunos_hash_table (info)->dynobj; + + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + BFD_ASSERT (sdyn != NULL); + + /* Finish up the .need section. The linker emulation code filled it + in, but with offsets from the start of the section instead of + real addresses. Now that we know the section location, we can + fill in the final values. */ + s = bfd_get_section_by_name (dynobj, ".need"); + if (s != NULL && s->_raw_size != 0) + { + file_ptr filepos; + bfd_byte *p; + + filepos = s->output_section->filepos + s->output_offset; + p = s->contents; + while (1) + { + bfd_vma val; + + PUT_WORD (dynobj, GET_WORD (dynobj, p) + filepos, p); + val = GET_WORD (dynobj, p + 12); + if (val == 0) + break; + PUT_WORD (dynobj, val + filepos, p + 12); + p += 16; + } + } + + /* The first entry in the .got section is the address of the + dynamic information, unless this is a shared library. */ + s = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (s != NULL); + if (info->shared || sdyn->_raw_size == 0) + PUT_WORD (dynobj, 0, s->contents); + else + PUT_WORD (dynobj, sdyn->output_section->vma + sdyn->output_offset, + s->contents); + + for (o = dynobj->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_HAS_CONTENTS) != 0 + && o->contents != NULL) + { + BFD_ASSERT (o->output_section != NULL + && o->output_section->owner == abfd); + if (! bfd_set_section_contents (abfd, o->output_section, + o->contents, o->output_offset, + o->_raw_size)) + return false; + } + } + + if (sdyn->_raw_size > 0) + { + struct external_sun4_dynamic esd; + struct external_sun4_dynamic_link esdl; + + /* Finish up the dynamic link information. */ + PUT_WORD (dynobj, (bfd_vma) 3, esd.ld_version); + PUT_WORD (dynobj, + sdyn->output_section->vma + sdyn->output_offset + sizeof esd, + esd.ldd); + PUT_WORD (dynobj, + (sdyn->output_section->vma + + sdyn->output_offset + + sizeof esd + + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE), + esd.ld); + + if (! bfd_set_section_contents (abfd, sdyn->output_section, &esd, + sdyn->output_offset, sizeof esd)) + return false; + + PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_loaded); + + s = bfd_get_section_by_name (dynobj, ".need"); + if (s == NULL || s->_raw_size == 0) + PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_need); + else + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_need); + + s = bfd_get_section_by_name (dynobj, ".rules"); + if (s == NULL || s->_raw_size == 0) + PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_rules); + else + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_rules); + + s = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (s != NULL); + PUT_WORD (dynobj, s->output_section->vma + s->output_offset, + esdl.ld_got); + + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + PUT_WORD (dynobj, s->output_section->vma + s->output_offset, + esdl.ld_plt); + PUT_WORD (dynobj, s->_raw_size, esdl.ld_plt_sz); + + s = bfd_get_section_by_name (dynobj, ".dynrel"); + BFD_ASSERT (s != NULL); + BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) + == s->_raw_size); + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_rel); + + s = bfd_get_section_by_name (dynobj, ".hash"); + BFD_ASSERT (s != NULL); + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_hash); + + s = bfd_get_section_by_name (dynobj, ".dynsym"); + BFD_ASSERT (s != NULL); + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_stab); + + PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_stab_hash); + + PUT_WORD (dynobj, (bfd_vma) sunos_hash_table (info)->bucketcount, + esdl.ld_buckets); + + s = bfd_get_section_by_name (dynobj, ".dynstr"); + BFD_ASSERT (s != NULL); + PUT_WORD (dynobj, s->output_section->filepos + s->output_offset, + esdl.ld_symbols); + PUT_WORD (dynobj, s->_raw_size, esdl.ld_symb_size); + + /* The size of the text area is the size of the .text section + rounded up to a page boundary. FIXME: Should the page size be + conditional on something? */ + PUT_WORD (dynobj, + BFD_ALIGN (obj_textsec (abfd)->_raw_size, 0x2000), + esdl.ld_text); + + if (! bfd_set_section_contents (abfd, sdyn->output_section, &esdl, + (sdyn->output_offset + + sizeof esd + + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE), + sizeof esdl)) + return false; + + abfd->flags |= DYNAMIC; + } + + return true; +} diff --git a/contrib/binutils/gas/config/tc-sparc.c b/contrib/binutils/gas/config/tc-sparc.c new file mode 100644 index 0000000..18d6572 --- /dev/null +++ b/contrib/binutils/gas/config/tc-sparc.c @@ -0,0 +1,3364 @@ +/* tc-sparc.c -- Assemble for the SPARC + Copyright (C) 1989, 90-96, 97, 1998 Free Software Foundation, Inc. + This file is part of GAS, the GNU Assembler. + + 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 2, 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, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include <ctype.h> + +#include "as.h" +#include "subsegs.h" + +#include "opcode/sparc.h" + +#ifdef OBJ_ELF +#include "elf/sparc.h" +#endif + +static struct sparc_arch *lookup_arch PARAMS ((char *)); +static void init_default_arch PARAMS ((void)); +static void sparc_ip PARAMS ((char *, const struct sparc_opcode **)); +static int in_signed_range PARAMS ((bfd_signed_vma, bfd_signed_vma)); +static int in_unsigned_range PARAMS ((bfd_vma, bfd_vma)); +static int in_bitfield_range PARAMS ((bfd_signed_vma, bfd_signed_vma)); +static int sparc_ffs PARAMS ((unsigned int)); +static bfd_vma BSR PARAMS ((bfd_vma, int)); +static int cmp_reg_entry PARAMS ((const PTR, const PTR)); +static int parse_keyword_arg PARAMS ((int (*) (const char *), char **, int *)); +static int parse_const_expr_arg PARAMS ((char **, int *)); +static int get_expression PARAMS ((char *str)); + +/* Default architecture. */ +/* ??? The default value should be V8, but sparclite support was added + by making it the default. GCC now passes -Asparclite, so maybe sometime in + the future we can set this to V8. */ +#ifndef DEFAULT_ARCH +#define DEFAULT_ARCH "sparclite" +#endif +static char *default_arch = DEFAULT_ARCH; + +/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size' + have been set. */ +static int default_init_p; + +/* Current architecture. We don't bump up unless necessary. */ +static enum sparc_opcode_arch_val current_architecture = SPARC_OPCODE_ARCH_V6; + +/* The maximum architecture level we can bump up to. + In a 32 bit environment, don't allow bumping up to v9 by default. + The native assembler works this way. The user is required to pass + an explicit argument before we'll create v9 object files. However, if + we don't see any v9 insns, a v8plus object file is not created. */ +static enum sparc_opcode_arch_val max_architecture; + +/* Either 32 or 64, selects file format. */ +static int sparc_arch_size; +/* Initial (default) value, recorded separately in case a user option + changes the value before md_show_usage is called. */ +static int default_arch_size; + +#ifdef OBJ_ELF +/* The currently selected v9 memory model. Currently only used for + ELF. */ +static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO; +#endif + +static int architecture_requested; +static int warn_on_bump; + +/* If warn_on_bump and the needed architecture is higher than this + architecture, issue a warning. */ +static enum sparc_opcode_arch_val warn_after_architecture; + +/* Non-zero if we are generating PIC code. */ +int sparc_pic_code; + +/* Non-zero if we should give an error when misaligned data is seen. */ +static int enforce_aligned_data; + +extern int target_big_endian; + +/* V9 has big and little endian data, but instructions are always big endian. + The sparclet has bi-endian support but both data and insns have the same + endianness. Global `target_big_endian' is used for data. The following + macro is used for instructions. */ +#define INSN_BIG_ENDIAN (target_big_endian \ + || SPARC_OPCODE_ARCH_V9_P (max_architecture)) + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash; + +static void s_data1 PARAMS ((void)); +static void s_seg PARAMS ((int)); +static void s_proc PARAMS ((int)); +static void s_reserve PARAMS ((int)); +static void s_common PARAMS ((int)); +static void s_empty PARAMS ((int)); +static void s_uacons PARAMS ((int)); + +const pseudo_typeS md_pseudo_table[] = +{ + {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */ + {"common", s_common, 0}, + {"empty", s_empty, 0}, + {"global", s_globl, 0}, + {"half", cons, 2}, + {"optim", s_ignore, 0}, + {"proc", s_proc, 0}, + {"reserve", s_reserve, 0}, + {"seg", s_seg, 0}, + {"skip", s_space, 0}, + {"word", cons, 4}, + {"xword", cons, 8}, + {"uahalf", s_uacons, 2}, + {"uaword", s_uacons, 4}, + {"uaxword", s_uacons, 8}, +#ifdef OBJ_ELF + /* these are specific to sparc/svr4 */ + {"pushsection", obj_elf_section, 0}, + {"popsection", obj_elf_previous, 0}, + {"2byte", s_uacons, 2}, + {"4byte", s_uacons, 4}, + {"8byte", s_uacons, 8}, +#endif + {NULL, 0, 0}, +}; + +const int md_reloc_size = 12; /* Size of relocation record */ + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +const char comment_chars[] = "!"; /* JF removed '|' from comment_chars */ + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments started like this one will always + work if '/' isn't otherwise defined. */ +const char line_comment_chars[] = "#"; + +const char line_separator_chars[] = ""; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c. Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. */ + +static unsigned char octal[256]; +#define isoctal(c) octal[(unsigned char) (c)] +static unsigned char toHex[256]; + +struct sparc_it + { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + bfd_reloc_code_real_type reloc; + }; + +struct sparc_it the_insn, set_insn; + +static void output_insn + PARAMS ((const struct sparc_opcode *, struct sparc_it *)); + +/* Table of arguments to -A. + The sparc_opcode_arch table in sparc-opc.c is insufficient and incorrect + for this use. That table is for opcodes only. This table is for opcodes + and file formats. */ + +static struct sparc_arch { + char *name; + char *opcode_arch; + /* Default word size, as specified during configuration. + A value of zero means can't be used to specify default architecture. */ + int default_arch_size; + /* Allowable arg to -A? */ + int user_option_p; +} sparc_arch_table[] = { + { "v6", "v6", 0, 1 }, + { "v7", "v7", 0, 1 }, + { "v8", "v8", 32, 1 }, + { "sparclet", "sparclet", 32, 1 }, + { "sparclite", "sparclite", 32, 1 }, + { "v8plus", "v9", 0, 1 }, + { "v8plusa", "v9a", 0, 1 }, + { "v9", "v9", 0, 1 }, + { "v9a", "v9a", 0, 1 }, + /* This exists to allow configure.in/Makefile.in to pass one + value to specify both the default machine and default word size. */ + { "v9-64", "v9", 64, 0 }, + { NULL, NULL, 0 } +}; + +static struct sparc_arch * +lookup_arch (name) + char *name; +{ + struct sparc_arch *sa; + + for (sa = &sparc_arch_table[0]; sa->name != NULL; sa++) + if (strcmp (sa->name, name) == 0) + break; + if (sa->name == NULL) + return NULL; + return sa; +} + +/* Initialize the default opcode arch and word size from the default + architecture name. */ + +static void +init_default_arch () +{ + struct sparc_arch *sa = lookup_arch (default_arch); + + if (sa == NULL + || sa->default_arch_size == 0) + as_fatal ("Invalid default architecture, broken assembler."); + + max_architecture = sparc_opcode_lookup_arch (sa->opcode_arch); + if (max_architecture == SPARC_OPCODE_ARCH_BAD) + as_fatal ("Bad opcode table, broken assembler."); + default_arch_size = sparc_arch_size = sa->default_arch_size; + default_init_p = 1; +} + +/* Called by TARGET_FORMAT. */ + +const char * +sparc_target_format () +{ + /* We don't get a chance to initialize anything before we're called, + so handle that now. */ + if (! default_init_p) + init_default_arch (); + +#ifdef OBJ_AOUT +#ifdef TE_NetBSD + return "a.out-sparc-netbsd"; +#else +#ifdef TE_SPARCAOUT + return target_big_endian ? "a.out-sunos-big" : "a.out-sparc-little"; +#else + return "a.out-sunos-big"; +#endif +#endif +#endif + +#ifdef OBJ_BOUT + return "b.out.big"; +#endif + +#ifdef OBJ_COFF +#ifdef TE_LYNX + return "coff-sparc-lynx"; +#else + return "coff-sparc"; +#endif +#endif + +#ifdef OBJ_ELF + return sparc_arch_size == 64 ? "elf64-sparc" : "elf32-sparc"; +#endif + + abort (); +} + +/* + * md_parse_option + * Invocation line includes a switch not recognized by the base assembler. + * See if it's a processor-specific option. These are: + * + * -bump + * Warn on architecture bumps. See also -A. + * + * -Av6, -Av7, -Av8, -Asparclite, -Asparclet + * Standard 32 bit architectures. + * -Av8plus, -Av8plusa + * Sparc64 in a 32 bit world. + * -Av9, -Av9a + * Sparc64 in either a 32 or 64 bit world (-32/-64 says which). + * This used to only mean 64 bits, but properly specifying it + * complicated gcc's ASM_SPECs, so now opcode selection is + * specified orthogonally to word size (except when specifying + * the default, but that is an internal implementation detail). + * -xarch=v8plus, -xarch=v8plusa + * Same as -Av8plus{,a}, for compatibility with Sun's assembler. + * + * Select the architecture and possibly the file format. + * Instructions or features not supported by the selected + * architecture cause fatal errors. + * + * The default is to start at v6, and bump the architecture up + * whenever an instruction is seen at a higher level. In 32 bit + * environments, v9 is not bumped up to, the user must pass + * -Av8plus{,a}. + * + * If -bump is specified, a warning is printing when bumping to + * higher levels. + * + * If an architecture is specified, all instructions must match + * that architecture. Any higher level instructions are flagged + * as errors. Note that in the 32 bit environment specifying + * -Av8plus does not automatically create a v8plus object file, a + * v9 insn must be seen. + * + * If both an architecture and -bump are specified, the + * architecture starts at the specified level, but bumps are + * warnings. Note that we can't set `current_architecture' to + * the requested level in this case: in the 32 bit environment, + * we still must avoid creating v8plus object files unless v9 + * insns are seen. + * + * Note: + * Bumping between incompatible architectures is always an + * error. For example, from sparclite to v9. + */ + +#ifdef OBJ_ELF +CONST char *md_shortopts = "A:K:VQ:sq"; +#else +#ifdef OBJ_AOUT +CONST char *md_shortopts = "A:k"; +#else +CONST char *md_shortopts = "A:"; +#endif +#endif +struct option md_longopts[] = { +#define OPTION_BUMP (OPTION_MD_BASE) + {"bump", no_argument, NULL, OPTION_BUMP}, +#define OPTION_SPARC (OPTION_MD_BASE + 1) + {"sparc", no_argument, NULL, OPTION_SPARC}, +#define OPTION_XARCH (OPTION_MD_BASE + 2) + {"xarch", required_argument, NULL, OPTION_XARCH}, +#ifdef OBJ_ELF +#define OPTION_32 (OPTION_MD_BASE + 3) + {"32", no_argument, NULL, OPTION_32}, +#define OPTION_64 (OPTION_MD_BASE + 4) + {"64", no_argument, NULL, OPTION_64}, +#define OPTION_TSO (OPTION_MD_BASE + 5) + {"TSO", no_argument, NULL, OPTION_TSO}, +#define OPTION_PSO (OPTION_MD_BASE + 6) + {"PSO", no_argument, NULL, OPTION_PSO}, +#define OPTION_RMO (OPTION_MD_BASE + 7) + {"RMO", no_argument, NULL, OPTION_RMO}, +#endif +#ifdef SPARC_BIENDIAN +#define OPTION_LITTLE_ENDIAN (OPTION_MD_BASE + 8) + {"EL", no_argument, NULL, OPTION_LITTLE_ENDIAN}, +#define OPTION_BIG_ENDIAN (OPTION_MD_BASE + 9) + {"EB", no_argument, NULL, OPTION_BIG_ENDIAN}, +#endif +#define OPTION_ENFORCE_ALIGNED_DATA (OPTION_MD_BASE + 10) + {"enforce-aligned-data", no_argument, NULL, OPTION_ENFORCE_ALIGNED_DATA}, + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof(md_longopts); + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + /* We don't get a chance to initialize anything before we're called, + so handle that now. */ + if (! default_init_p) + init_default_arch (); + + switch (c) + { + case OPTION_BUMP: + warn_on_bump = 1; + warn_after_architecture = SPARC_OPCODE_ARCH_V6; + break; + + case OPTION_XARCH: + /* This is for compatibility with Sun's assembler. */ + if (strcmp (arg, "v8plus") != 0 + && strcmp (arg, "v8plusa") != 0) + { + as_bad ("invalid architecture -xarch=%s", arg); + return 0; + } + + /* fall through */ + + case 'A': + { + struct sparc_arch *sa; + enum sparc_opcode_arch_val opcode_arch; + + sa = lookup_arch (arg); + if (sa == NULL + || ! sa->user_option_p) + { + as_bad ("invalid architecture -A%s", arg); + return 0; + } + + opcode_arch = sparc_opcode_lookup_arch (sa->opcode_arch); + if (opcode_arch == SPARC_OPCODE_ARCH_BAD) + as_fatal ("Bad opcode table, broken assembler."); + + max_architecture = opcode_arch; + architecture_requested = 1; + } + break; + + case OPTION_SPARC: + /* Ignore -sparc, used by SunOS make default .s.o rule. */ + break; + + case OPTION_ENFORCE_ALIGNED_DATA: + enforce_aligned_data = 1; + break; + +#ifdef SPARC_BIENDIAN + case OPTION_LITTLE_ENDIAN: + target_big_endian = 0; + break; + case OPTION_BIG_ENDIAN: + target_big_endian = 1; + break; +#endif + +#ifdef OBJ_AOUT + case 'k': + sparc_pic_code = 1; + break; +#endif + +#ifdef OBJ_ELF + case OPTION_32: + case OPTION_64: + { + const char **list, **l; + + sparc_arch_size = c == OPTION_32 ? 32 : 64; + list = bfd_target_list (); + for (l = list; *l != NULL; l++) + { + if (sparc_arch_size == 32) + { + if (strcmp (*l, "elf32-sparc") == 0) + break; + } + else + { + if (strcmp (*l, "elf64-sparc") == 0) + break; + } + } + if (*l == NULL) + as_fatal ("No compiled in support for %d bit object file format", + sparc_arch_size); + free (list); + } + break; + + case OPTION_TSO: + sparc_memory_model = MM_TSO; + break; + + case OPTION_PSO: + sparc_memory_model = MM_PSO; + break; + + case OPTION_RMO: + sparc_memory_model = MM_RMO; + break; + + case 'V': + print_version_id (); + break; + + case 'Q': + /* Qy - do emit .comment + Qn - do not emit .comment */ + break; + + case 's': + /* use .stab instead of .stab.excl */ + break; + + case 'q': + /* quick -- native assembler does fewer checks */ + break; + + case 'K': + if (strcmp (arg, "PIC") != 0) + as_warn ("Unrecognized option following -K"); + else + sparc_pic_code = 1; + break; +#endif + + default: + return 0; + } + + return 1; +} + +void +md_show_usage (stream) + FILE *stream; +{ + const struct sparc_arch *arch; + + /* We don't get a chance to initialize anything before we're called, + so handle that now. */ + if (! default_init_p) + init_default_arch (); + + fprintf(stream, "SPARC options:\n"); + for (arch = &sparc_arch_table[0]; arch->name; arch++) + { + if (arch != &sparc_arch_table[0]) + fprintf (stream, " | "); + if (arch->user_option_p) + fprintf (stream, "-A%s", arch->name); + } + fprintf (stream, "\n-xarch=v8plus | -xarch=v8plusa\n"); + fprintf (stream, "\ + specify variant of SPARC architecture\n\ +-bump warn when assembler switches architectures\n\ +-sparc ignored\n\ +--enforce-aligned-data force .long, etc., to be aligned correctly\n"); +#ifdef OBJ_AOUT + fprintf (stream, "\ +-k generate PIC\n"); +#endif +#ifdef OBJ_ELF + fprintf (stream, "\ +-32 create 32 bit object file\n\ +-64 create 64 bit object file\n"); + fprintf (stream, "\ + [default is %d]\n", default_arch_size); + fprintf (stream, "\ +-TSO use Total Store Ordering\n\ +-PSO use Partial Store Ordering\n\ +-RMO use Relaxed Memory Ordering\n"); + fprintf (stream, "\ + [default is %s]\n", (default_arch_size == 64) ? "RMO" : "TSO"); + fprintf (stream, "\ +-KPIC generate PIC\n\ +-V print assembler version number\n\ +-q ignored\n\ +-Qy, -Qn ignored\n\ +-s ignored\n"); +#endif +#ifdef SPARC_BIENDIAN + fprintf (stream, "\ +-EL generate code for a little endian machine\n\ +-EB generate code for a big endian machine\n"); +#endif +} + +/* sparc64 priviledged registers */ + +struct priv_reg_entry + { + char *name; + int regnum; + }; + +struct priv_reg_entry priv_reg_table[] = +{ + {"tpc", 0}, + {"tnpc", 1}, + {"tstate", 2}, + {"tt", 3}, + {"tick", 4}, + {"tba", 5}, + {"pstate", 6}, + {"tl", 7}, + {"pil", 8}, + {"cwp", 9}, + {"cansave", 10}, + {"canrestore", 11}, + {"cleanwin", 12}, + {"otherwin", 13}, + {"wstate", 14}, + {"fq", 15}, + {"ver", 31}, + {"", -1}, /* end marker */ +}; + +/* v9a specific asrs */ + +struct priv_reg_entry v9a_asr_table[] = +{ + {"tick_cmpr", 23}, + {"softint", 22}, + {"set_softint", 20}, + {"pic", 17}, + {"pcr", 16}, + {"gsr", 19}, + {"dcr", 18}, + {"clear_softint", 21}, + {"", -1}, /* end marker */ +}; + +static int +cmp_reg_entry (parg, qarg) + const PTR parg; + const PTR qarg; +{ + const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg; + const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg; + + return strcmp (q->name, p->name); +} + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ + +void +md_begin () +{ + register const char *retval = NULL; + int lose = 0; + register unsigned int i = 0; + + /* We don't get a chance to initialize anything before md_parse_option + is called, and it may not be called, so handle default initialization + now if not already done. */ + if (! default_init_p) + init_default_arch (); + + op_hash = hash_new (); + + while (i < (unsigned int) sparc_num_opcodes) + { + const char *name = sparc_opcodes[i].name; + retval = hash_insert (op_hash, name, (PTR) &sparc_opcodes[i]); + if (retval != NULL) + { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + sparc_opcodes[i].name, retval); + lose = 1; + } + do + { + if (sparc_opcodes[i].match & sparc_opcodes[i].lose) + { + fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n", + sparc_opcodes[i].name, sparc_opcodes[i].args); + lose = 1; + } + ++i; + } + while (i < (unsigned int) sparc_num_opcodes + && !strcmp (sparc_opcodes[i].name, name)); + } + + if (lose) + as_fatal ("Broken assembler. No assembly attempted."); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; + + qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]), + sizeof (priv_reg_table[0]), cmp_reg_entry); + + /* If -bump, record the architecture level at which we start issuing + warnings. The behaviour is different depending upon whether an + architecture was explicitly specified. If it wasn't, we issue warnings + for all upwards bumps. If it was, we don't start issuing warnings until + we need to bump beyond the requested architecture or when we bump between + conflicting architectures. */ + + if (warn_on_bump + && architecture_requested) + { + /* `max_architecture' records the requested architecture. + Issue warnings if we go above it. */ + warn_after_architecture = max_architecture; + + /* Find the highest architecture level that doesn't conflict with + the requested one. */ + for (max_architecture = SPARC_OPCODE_ARCH_MAX; + max_architecture > warn_after_architecture; + --max_architecture) + if (! SPARC_OPCODE_CONFLICT_P (max_architecture, + warn_after_architecture)) + break; + } +} + +/* Called after all assembly has been done. */ + +void +sparc_md_end () +{ + if (sparc_arch_size == 64) + { + if (current_architecture == SPARC_OPCODE_ARCH_V9A) + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9a); + else + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9); + } + else + { + if (current_architecture == SPARC_OPCODE_ARCH_V9) + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plus); + else if (current_architecture == SPARC_OPCODE_ARCH_V9A) + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plusa); + else if (current_architecture == SPARC_OPCODE_ARCH_SPARCLET) + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclet); + else + { + /* The sparclite is treated like a normal sparc. Perhaps it shouldn't + be but for now it is (since that's the way it's always been + treated). */ + bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc); + } + } +} + +/* Return non-zero if VAL is in the range -(MAX+1) to MAX. */ + +static INLINE int +in_signed_range (val, max) + bfd_signed_vma val, max; +{ + if (max <= 0) + abort (); + if (val > max) + return 0; + if (val < ~max) + return 0; + return 1; +} + +/* Return non-zero if VAL is in the range 0 to MAX. */ + +static INLINE int +in_unsigned_range (val, max) + bfd_vma val, max; +{ + if (val > max) + return 0; + return 1; +} + +/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX. + (e.g. -15 to +31). */ + +static INLINE int +in_bitfield_range (val, max) + bfd_signed_vma val, max; +{ + if (max <= 0) + abort (); + if (val > max) + return 0; + if (val < ~(max >> 1)) + return 0; + return 1; +} + +static int +sparc_ffs (mask) + unsigned int mask; +{ + int i; + + if (mask == 0) + return -1; + + for (i = 0; (mask & 1) == 0; ++i) + mask >>= 1; + return i; +} + +/* Implement big shift right. */ +static bfd_vma +BSR (val, amount) + bfd_vma val; + int amount; +{ + if (sizeof (bfd_vma) <= 4 && amount >= 32) + as_fatal ("Support for 64-bit arithmetic not compiled in."); + return val >> amount; +} + +/* For communication between sparc_ip and get_expression. */ +static char *expr_end; + +/* For communication between md_assemble and sparc_ip. */ +static int special_case; + +/* Values for `special_case'. + Instructions that require wierd handling because they're longer than + 4 bytes. */ +#define SPECIAL_CASE_NONE 0 +#define SPECIAL_CASE_SET 1 +#define SPECIAL_CASE_SETSW 2 +#define SPECIAL_CASE_SETX 3 +/* FIXME: sparc-opc.c doesn't have necessary "S" trigger to enable this. */ +#define SPECIAL_CASE_FDIV 4 + +/* Bit masks of various insns. */ +#define NOP_INSN 0x01000000 +#define OR_INSN 0x80100000 +#define FMOVS_INSN 0x81A00020 +#define SETHI_INSN 0x01000000 +#define SLLX_INSN 0x81281000 +#define SRA_INSN 0x81380000 + +/* The last instruction to be assembled. */ +static const struct sparc_opcode *last_insn; +/* The assembled opcode of `last_insn'. */ +static unsigned long last_opcode; + +/* Main entry point to assemble one instruction. */ + +void +md_assemble (str) + char *str; +{ + const struct sparc_opcode *insn; + + know (str); + special_case = SPECIAL_CASE_NONE; + sparc_ip (str, &insn); + + /* We warn about attempts to put a floating point branch in a delay slot, + unless the delay slot has been annulled. */ + if (insn != NULL + && last_insn != NULL + && (insn->flags & F_FBR) != 0 + && (last_insn->flags & F_DELAYED) != 0 + /* ??? This test isn't completely accurate. We assume anything with + F_{UNBR,CONDBR,FBR} set is annullable. */ + && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0 + || (last_opcode & ANNUL) == 0)) + as_warn ("FP branch in delay slot"); + + /* SPARC before v9 requires a nop instruction between a floating + point instruction and a floating point branch. We insert one + automatically, with a warning. */ + if (max_architecture < SPARC_OPCODE_ARCH_V9 + && insn != NULL + && last_insn != NULL + && (insn->flags & F_FBR) != 0 + && (last_insn->flags & F_FLOAT) != 0) + { + struct sparc_it nop_insn; + + nop_insn.opcode = NOP_INSN; + nop_insn.reloc = BFD_RELOC_NONE; + output_insn (insn, &nop_insn); + as_warn ("FP branch preceded by FP instruction; NOP inserted"); + } + + switch (special_case) + { + case SPECIAL_CASE_NONE: + /* normal insn */ + output_insn (insn, &the_insn); + break; + + case SPECIAL_CASE_SET: + { + int need_hi22_p = 0; + + /* "set" is not defined for negative numbers in v9: it doesn't yield + what you expect it to. */ + if (SPARC_OPCODE_ARCH_V9_P (max_architecture) + && the_insn.exp.X_op == O_constant) + { + if (the_insn.exp.X_add_number < 0) + as_warn ("set: used with negative number"); + else if (the_insn.exp.X_add_number > (offsetT) 0xffffffff) + as_warn ("set: number larger than 4294967295"); + } + + /* See if operand is absolute and small; skip sethi if so. */ + if (the_insn.exp.X_op != O_constant + || the_insn.exp.X_add_number >= (1 << 12) + || the_insn.exp.X_add_number < -(1 << 12)) + { + output_insn (insn, &the_insn); + need_hi22_p = 1; + } + /* See if operand has no low-order bits; skip OR if so. */ + if (the_insn.exp.X_op != O_constant + || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0) + || ! need_hi22_p) + { + int rd = (the_insn.opcode & RD (~0)) >> 25; + the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0) + | RD (rd) + | IMMED + | (the_insn.exp.X_add_number + & (need_hi22_p ? 0x3ff : 0x1fff))); + the_insn.reloc = (the_insn.exp.X_op != O_constant + ? BFD_RELOC_LO10 + : BFD_RELOC_NONE); + output_insn (insn, &the_insn); + } + break; + } + + case SPECIAL_CASE_SETSW: + { + /* FIXME: Not finished. */ + break; + } + + case SPECIAL_CASE_SETX: + { +#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000) + int upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32)); + int lower32 = SIGNEXT32 (the_insn.exp.X_add_number); +#undef SIGNEXT32 + int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14; + int dstreg = (the_insn.opcode & RD (~0)) >> 25; + /* Output directly to dst reg if lower 32 bits are all zero. */ + int upper_dstreg = (the_insn.exp.X_op == O_constant + && lower32 == 0) ? dstreg : tmpreg; + int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0; + + /* The tmp reg should not be the dst reg. */ + if (tmpreg == dstreg) + as_warn ("setx: temporary register same as destination register"); + + /* Reset X_add_number, we've extracted it as upper32/lower32. + Otherwise fixup_segment will complain about not being able to + write an 8 byte number in a 4 byte field. */ + the_insn.exp.X_add_number = 0; + + /* ??? Obviously there are other optimizations we can do + (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be + doing some of these. Later. If you do change things, try to + change all of this to be table driven as well. */ + + /* What to output depends on the number if it's constant. + Compute that first, then output what we've decided upon. */ + if (the_insn.exp.X_op != O_constant) + need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1; + else + { + /* Only need hh22 if `or' insn can't handle constant. */ + if (upper32 < -(1 << 12) || upper32 >= (1 << 12)) + need_hh22_p = 1; + + /* Does bottom part (after sethi) have bits? */ + if ((need_hh22_p && (upper32 & 0x3ff) != 0) + /* No hh22, but does upper32 still have bits we can't set + from lower32? */ + || (! need_hh22_p + && upper32 != 0 + && (upper32 != -1 || lower32 >= 0))) + need_hm10_p = 1; + + /* If the lower half is all zero, we build the upper half directly + into the dst reg. */ + if (lower32 != 0 + /* Need lower half if number is zero. */ + || (! need_hh22_p && ! need_hm10_p)) + { + /* No need for sethi if `or' insn can handle constant. */ + if (lower32 < -(1 << 12) || lower32 >= (1 << 12) + /* Note that we can't use a negative constant in the `or' + insn unless the upper 32 bits are all ones. */ + || (lower32 < 0 && upper32 != -1)) + need_hi22_p = 1; + + /* Does bottom part (after sethi) have bits? */ + if ((need_hi22_p && (lower32 & 0x3ff) != 0) + /* No sethi. */ + || (! need_hi22_p && (lower32 & 0x1fff) != 0) + /* Need `or' if we didn't set anything else. */ + || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p)) + need_lo10_p = 1; + } + } + + if (need_hh22_p) + { + the_insn.opcode = (SETHI_INSN | RD (upper_dstreg) + | ((upper32 >> 10) & 0x3fffff)); + the_insn.reloc = (the_insn.exp.X_op != O_constant + ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE); + output_insn (insn, &the_insn); + } + + if (need_hm10_p) + { + the_insn.opcode = (OR_INSN + | (need_hh22_p ? RS1 (upper_dstreg) : 0) + | RD (upper_dstreg) + | IMMED + | (upper32 + & (need_hh22_p ? 0x3ff : 0x1fff))); + the_insn.reloc = (the_insn.exp.X_op != O_constant + ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE); + output_insn (insn, &the_insn); + } + + if (need_hi22_p) + { + the_insn.opcode = (SETHI_INSN | RD (dstreg) + | ((lower32 >> 10) & 0x3fffff)); + the_insn.reloc = BFD_RELOC_HI22; + output_insn (insn, &the_insn); + } + + if (need_lo10_p) + { + /* FIXME: One nice optimization to do here is to OR the low part + with the highpart if hi22 isn't needed and the low part is + positive. */ + the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0) + | RD (dstreg) + | IMMED + | (lower32 + & (need_hi22_p ? 0x3ff : 0x1fff))); + the_insn.reloc = BFD_RELOC_LO10; + output_insn (insn, &the_insn); + } + + /* If we needed to build the upper part, shift it into place. */ + if (need_hh22_p || need_hm10_p) + { + the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg) + | IMMED | 32); + the_insn.reloc = BFD_RELOC_NONE; + output_insn (insn, &the_insn); + } + + /* If we needed to build both upper and lower parts, OR them together. */ + if ((need_hh22_p || need_hm10_p) + && (need_hi22_p || need_lo10_p)) + { + the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg) + | RD (dstreg)); + the_insn.reloc = BFD_RELOC_NONE; + output_insn (insn, &the_insn); + } + /* We didn't need both regs, but we may have to sign extend lower32. */ + else if (need_hi22_p && upper32 == -1) + { + the_insn.opcode = (SRA_INSN | RS1 (dstreg) | RD (dstreg) + | IMMED | 0); + the_insn.reloc = BFD_RELOC_NONE; + output_insn (insn, &the_insn); + } + break; + } + + case SPECIAL_CASE_FDIV: + { + int rd = (the_insn.opcode >> 25) & 0x1f; + + output_insn (insn, &the_insn); + + /* According to information leaked from Sun, the "fdiv" instructions + on early SPARC machines would produce incorrect results sometimes. + The workaround is to add an fmovs of the destination register to + itself just after the instruction. This was true on machines + with Weitek 1165 float chips, such as the Sun-4/260 and /280. */ + assert (the_insn.reloc == BFD_RELOC_NONE); + the_insn.opcode = FMOVS_INSN | rd | RD (rd); + output_insn (insn, &the_insn); + break; + } + + default: + as_fatal ("failed special case insn sanity check"); + } +} + +/* Subroutine of md_assemble to do the actual parsing. */ + +static void +sparc_ip (str, pinsn) + char *str; + const struct sparc_opcode **pinsn; +{ + char *error_message = ""; + char *s; + const char *args; + char c; + const struct sparc_opcode *insn; + char *argsStart; + unsigned long opcode; + unsigned int mask = 0; + int match = 0; + int comma = 0; + int v9_arg_p; + + for (s = str; islower ((unsigned char) *s) || (*s >= '0' && *s <= '3'); ++s) + ; + + switch (*s) + { + case '\0': + break; + + case ',': + comma = 1; + + /*FALLTHROUGH */ + + case ' ': + *s++ = '\0'; + break; + + default: + as_fatal ("Unknown opcode: `%s'", str); + } + insn = (struct sparc_opcode *) hash_find (op_hash, str); + *pinsn = insn; + if (insn == NULL) + { + as_bad ("Unknown opcode: `%s'", str); + return; + } + if (comma) + { + *--s = ','; + } + + argsStart = s; + for (;;) + { + opcode = insn->match; + memset (&the_insn, '\0', sizeof (the_insn)); + the_insn.reloc = BFD_RELOC_NONE; + v9_arg_p = 0; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match + */ + for (args = insn->args;; ++args) + { + switch (*args) + { + case 'K': + { + int kmask = 0; + + /* Parse a series of masks. */ + if (*s == '#') + { + while (*s == '#') + { + int mask; + + if (! parse_keyword_arg (sparc_encode_membar, &s, + &mask)) + { + error_message = ": invalid membar mask name"; + goto error; + } + kmask |= mask; + while (*s == ' ') { ++s; continue; } + if (*s == '|' || *s == '+') + ++s; + while (*s == ' ') { ++s; continue; } + } + } + else + { + if (! parse_const_expr_arg (&s, &kmask)) + { + error_message = ": invalid membar mask expression"; + goto error; + } + if (kmask < 0 || kmask > 127) + { + error_message = ": invalid membar mask number"; + goto error; + } + } + + opcode |= MEMBAR (kmask); + continue; + } + + case '*': + { + int fcn = 0; + + /* Parse a prefetch function. */ + if (*s == '#') + { + if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn)) + { + error_message = ": invalid prefetch function name"; + goto error; + } + } + else + { + if (! parse_const_expr_arg (&s, &fcn)) + { + error_message = ": invalid prefetch function expression"; + goto error; + } + if (fcn < 0 || fcn > 31) + { + error_message = ": invalid prefetch function number"; + goto error; + } + } + opcode |= RD (fcn); + continue; + } + + case '!': + case '?': + /* Parse a sparc64 privileged register. */ + if (*s == '%') + { + struct priv_reg_entry *p = priv_reg_table; + unsigned int len = 9999999; /* init to make gcc happy */ + + s += 1; + while (p->name[0] > s[0]) + p++; + while (p->name[0] == s[0]) + { + len = strlen (p->name); + if (strncmp (p->name, s, len) == 0) + break; + p++; + } + if (p->name[0] != s[0]) + { + error_message = ": unrecognizable privileged register"; + goto error; + } + if (*args == '?') + opcode |= (p->regnum << 14); + else + opcode |= (p->regnum << 25); + s += len; + continue; + } + else + { + error_message = ": unrecognizable privileged register"; + goto error; + } + + case '_': + case '/': + /* Parse a v9a ancillary state register. */ + if (*s == '%') + { + struct priv_reg_entry *p = v9a_asr_table; + unsigned int len = 9999999; /* init to make gcc happy */ + + s += 1; + while (p->name[0] > s[0]) + p++; + while (p->name[0] == s[0]) + { + len = strlen (p->name); + if (strncmp (p->name, s, len) == 0) + break; + p++; + } + if (p->name[0] != s[0]) + { + error_message = ": unrecognizable v9a ancillary state register"; + goto error; + } + if (*args == '/' && (p->regnum == 20 || p->regnum == 21)) + { + error_message = ": rd on write only ancillary state register"; + goto error; + } + if (*args == '/') + opcode |= (p->regnum << 14); + else + opcode |= (p->regnum << 25); + s += len; + continue; + } + else + { + error_message = ": unrecognizable v9a ancillary state register"; + goto error; + } + + case 'M': + case 'm': + if (strncmp (s, "%asr", 4) == 0) + { + s += 4; + + if (isdigit ((unsigned char) *s)) + { + long num = 0; + + while (isdigit ((unsigned char) *s)) + { + num = num * 10 + *s - '0'; + ++s; + } + + if (current_architecture >= SPARC_OPCODE_ARCH_V9) + { + if (num < 16 || 31 < num) + { + error_message = ": asr number must be between 16 and 31"; + goto error; + } + } + else + { + if (num < 0 || 31 < num) + { + error_message = ": asr number must be between 0 and 31"; + goto error; + } + } + + opcode |= (*args == 'M' ? RS1 (num) : RD (num)); + continue; + } + else + { + error_message = ": expecting %asrN"; + goto error; + } + } /* if %asr */ + break; + + case 'I': + the_insn.reloc = BFD_RELOC_SPARC_11; + goto immediate; + + case 'j': + the_insn.reloc = BFD_RELOC_SPARC_10; + goto immediate; + + case 'X': + /* V8 systems don't understand BFD_RELOC_SPARC_5. */ + if (SPARC_OPCODE_ARCH_V9_P (max_architecture)) + the_insn.reloc = BFD_RELOC_SPARC_5; + else + the_insn.reloc = BFD_RELOC_SPARC13; + /* These fields are unsigned, but for upward compatibility, + allow negative values as well. */ + goto immediate; + + case 'Y': + /* V8 systems don't understand BFD_RELOC_SPARC_6. */ + if (SPARC_OPCODE_ARCH_V9_P (max_architecture)) + the_insn.reloc = BFD_RELOC_SPARC_6; + else + the_insn.reloc = BFD_RELOC_SPARC13; + /* These fields are unsigned, but for upward compatibility, + allow negative values as well. */ + goto immediate; + + case 'k': + the_insn.reloc = /* RELOC_WDISP2_14 */ BFD_RELOC_SPARC_WDISP16; + the_insn.pcrel = 1; + goto immediate; + + case 'G': + the_insn.reloc = BFD_RELOC_SPARC_WDISP19; + the_insn.pcrel = 1; + goto immediate; + + case 'N': + if (*s == 'p' && s[1] == 'n') + { + s += 2; + continue; + } + break; + + case 'T': + if (*s == 'p' && s[1] == 't') + { + s += 2; + continue; + } + break; + + case 'z': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%icc", 4) == 0) + { + s += 4; + continue; + } + break; + + case 'Z': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%xcc", 4) == 0) + { + s += 4; + continue; + } + break; + + case '6': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%fcc0", 5) == 0) + { + s += 5; + continue; + } + break; + + case '7': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%fcc1", 5) == 0) + { + s += 5; + continue; + } + break; + + case '8': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%fcc2", 5) == 0) + { + s += 5; + continue; + } + break; + + case '9': + if (*s == ' ') + { + ++s; + } + if (strncmp (s, "%fcc3", 5) == 0) + { + s += 5; + continue; + } + break; + + case 'P': + if (strncmp (s, "%pc", 3) == 0) + { + s += 3; + continue; + } + break; + + case 'W': + if (strncmp (s, "%tick", 5) == 0) + { + s += 5; + continue; + } + break; + + case '\0': /* end of args */ + if (*s == '\0') + { + match = 1; + } + break; + + case '+': + if (*s == '+') + { + ++s; + continue; + } + if (*s == '-') + { + continue; + } + break; + + case '[': /* these must match exactly */ + case ']': + case ',': + case ' ': + if (*s++ == *args) + continue; + break; + + case '#': /* must be at least one digit */ + if (isdigit ((unsigned char) *s++)) + { + while (isdigit ((unsigned char) *s)) + { + ++s; + } + continue; + } + break; + + case 'C': /* coprocessor state register */ + if (strncmp (s, "%csr", 4) == 0) + { + s += 4; + continue; + } + break; + + case 'b': /* next operand is a coprocessor register */ + case 'c': + case 'D': + if (*s++ == '%' && *s++ == 'c' && isdigit ((unsigned char) *s)) + { + mask = *s++; + if (isdigit ((unsigned char) *s)) + { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) + { + break; + } + } + else + { + mask -= '0'; + } + switch (*args) + { + + case 'b': + opcode |= mask << 14; + continue; + + case 'c': + opcode |= mask; + continue; + + case 'D': + opcode |= mask << 25; + continue; + } + } + break; + + case 'r': /* next operand must be a register */ + case 'O': + case '1': + case '2': + case 'd': + if (*s++ == '%') + { + switch (c = *s++) + { + + case 'f': /* frame pointer */ + if (*s++ == 'p') + { + mask = 0x1e; + break; + } + goto error; + + case 'g': /* global register */ + if (isoctal (c = *s++)) + { + mask = c - '0'; + break; + } + goto error; + + case 'i': /* in register */ + if (isoctal (c = *s++)) + { + mask = c - '0' + 24; + break; + } + goto error; + + case 'l': /* local register */ + if (isoctal (c = *s++)) + { + mask = (c - '0' + 16); + break; + } + goto error; + + case 'o': /* out register */ + if (isoctal (c = *s++)) + { + mask = (c - '0' + 8); + break; + } + goto error; + + case 's': /* stack pointer */ + if (*s++ == 'p') + { + mask = 0xe; + break; + } + goto error; + + case 'r': /* any register */ + if (!isdigit ((unsigned char) (c = *s++))) + { + goto error; + } + /* FALLTHROUGH */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (isdigit ((unsigned char) *s)) + { + if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) + { + goto error; + } + } + else + { + c -= '0'; + } + mask = c; + break; + + default: + goto error; + } + + /* Got the register, now figure out where + it goes in the opcode. */ + switch (*args) + { + case '1': + opcode |= mask << 14; + continue; + + case '2': + opcode |= mask; + continue; + + case 'd': + opcode |= mask << 25; + continue; + + case 'r': + opcode |= (mask << 25) | (mask << 14); + continue; + + case 'O': + opcode |= (mask << 25) | (mask << 0); + continue; + } + } + break; + + case 'e': /* next operand is a floating point register */ + case 'v': + case 'V': + + case 'f': + case 'B': + case 'R': + + case 'g': + case 'H': + case 'J': + { + char format; + + if (*s++ == '%' + && ((format = *s) == 'f') + && isdigit ((unsigned char) *++s)) + { + for (mask = 0; isdigit ((unsigned char) *s); ++s) + { + mask = 10 * mask + (*s - '0'); + } /* read the number */ + + if ((*args == 'v' + || *args == 'B' + || *args == 'H') + && (mask & 1)) + { + break; + } /* register must be even numbered */ + + if ((*args == 'V' + || *args == 'R' + || *args == 'J') + && (mask & 3)) + { + break; + } /* register must be multiple of 4 */ + + if (mask >= 64) + { + if (SPARC_OPCODE_ARCH_V9_P (max_architecture)) + error_message = ": There are only 64 f registers; [0-63]"; + else + error_message = ": There are only 32 f registers; [0-31]"; + goto error; + } /* on error */ + else if (mask >= 32) + { + if (SPARC_OPCODE_ARCH_V9_P (max_architecture)) + { + v9_arg_p = 1; + mask -= 31; /* wrap high bit */ + } + else + { + error_message = ": There are only 32 f registers; [0-31]"; + goto error; + } + } + } + else + { + break; + } /* if not an 'f' register. */ + + switch (*args) + { + case 'v': + case 'V': + case 'e': + opcode |= RS1 (mask); + continue; + + + case 'f': + case 'B': + case 'R': + opcode |= RS2 (mask); + continue; + + case 'g': + case 'H': + case 'J': + opcode |= RD (mask); + continue; + } /* pack it in. */ + + know (0); + break; + } /* float arg */ + + case 'F': + if (strncmp (s, "%fsr", 4) == 0) + { + s += 4; + continue; + } + break; + + case '0': /* 64 bit immediate (setx insn) */ + the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */ + goto immediate; + + case 'h': /* high 22 bits */ + the_insn.reloc = BFD_RELOC_HI22; + goto immediate; + + case 'l': /* 22 bit PC relative immediate */ + the_insn.reloc = BFD_RELOC_SPARC_WDISP22; + the_insn.pcrel = 1; + goto immediate; + + case 'L': /* 30 bit immediate */ + the_insn.reloc = BFD_RELOC_32_PCREL_S2; + the_insn.pcrel = 1; + goto immediate; + + case 'n': /* 22 bit immediate */ + the_insn.reloc = BFD_RELOC_SPARC22; + goto immediate; + + case 'i': /* 13 bit immediate */ + the_insn.reloc = BFD_RELOC_SPARC13; + + /* fallthrough */ + + immediate: + if (*s == ' ') + s++; + + /* Check for %hi, etc. */ + if (*s == '%') + { + static struct ops { + /* The name as it appears in assembler. */ + char *name; + /* strlen (name), precomputed for speed */ + int len; + /* The reloc this pseudo-op translates to. */ + int reloc; + /* Non-zero if for v9 only. */ + int v9_p; + /* Non-zero if can be used in pc-relative contexts. */ + int pcrel_p;/*FIXME:wip*/ + } ops[] = { + /* hix/lox must appear before hi/lo so %hix won't be + mistaken for %hi. */ + { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 }, + { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 }, + { "hi", 2, BFD_RELOC_HI22, 0, 1 }, + { "lo", 2, BFD_RELOC_LO10, 0, 1 }, + { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 }, + { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 }, + { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 }, + { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 }, + { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 }, + { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 }, + { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 }, + { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 }, + { NULL } + }; + struct ops *o; + + for (o = ops; o->name; o++) + if (strncmp (s + 1, o->name, o->len) == 0) + break; + if (o->name == NULL) + break; + + the_insn.reloc = o->reloc; + s += o->len + 1; + v9_arg_p = o->v9_p; + } + + /* Note that if the get_expression() fails, we will still + have created U entries in the symbol table for the + 'symbols' in the input string. Try not to create U + symbols for registers, etc. */ + { + /* This stuff checks to see if the expression ends in + +%reg. If it does, it removes the register from + the expression, and re-sets 's' to point to the + right place. */ + + char *s1; + + for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) ; + + if (s1 != s && isdigit ((unsigned char) s1[-1])) + { + if (s1[-2] == '%' && s1[-3] == '+') + { + s1 -= 3; + *s1 = '\0'; + (void) get_expression (s); + *s1 = '+'; + s = s1; + continue; + } + else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+') + { + s1 -= 4; + *s1 = '\0'; + (void) get_expression (s); + *s1 = '+'; + s = s1; + continue; + } + } + } + (void) get_expression (s); + s = expr_end; + + /* Check for constants that don't require emitting a reloc. */ + if (the_insn.exp.X_op == O_constant + && the_insn.exp.X_add_symbol == 0 + && the_insn.exp.X_op_symbol == 0) + { + /* For pc-relative call instructions, we reject + constants to get better code. */ + if (the_insn.pcrel + && the_insn.reloc == BFD_RELOC_32_PCREL_S2 + && in_signed_range (the_insn.exp.X_add_number, 0x3fff)) + { + error_message = ": PC-relative operand can't be a constant"; + goto error; + } + + /* Constants that won't fit are checked in md_apply_fix3 + and bfd_install_relocation. + ??? It would be preferable to install the constants + into the insn here and save having to create a fixS + for each one. There already exists code to handle + all the various cases (e.g. in md_apply_fix3 and + bfd_install_relocation) so duplicating all that code + here isn't right. */ + } + + continue; + + case 'a': + if (*s++ == 'a') + { + opcode |= ANNUL; + continue; + } + break; + + case 'A': + { + int asi = 0; + + /* Parse an asi. */ + if (*s == '#') + { + if (! parse_keyword_arg (sparc_encode_asi, &s, &asi)) + { + error_message = ": invalid ASI name"; + goto error; + } + } + else + { + if (! parse_const_expr_arg (&s, &asi)) + { + error_message = ": invalid ASI expression"; + goto error; + } + if (asi < 0 || asi > 255) + { + error_message = ": invalid ASI number"; + goto error; + } + } + opcode |= ASI (asi); + continue; + } /* alternate space */ + + case 'p': + if (strncmp (s, "%psr", 4) == 0) + { + s += 4; + continue; + } + break; + + case 'q': /* floating point queue */ + if (strncmp (s, "%fq", 3) == 0) + { + s += 3; + continue; + } + break; + + case 'Q': /* coprocessor queue */ + if (strncmp (s, "%cq", 3) == 0) + { + s += 3; + continue; + } + break; + + case 'S': + if (strcmp (str, "set") == 0 + || strcmp (str, "setuw") == 0) + { + special_case = SPECIAL_CASE_SET; + continue; + } + else if (strcmp (str, "setsw") == 0) + { + special_case = SPECIAL_CASE_SETSW; + continue; + } + else if (strcmp (str, "setx") == 0) + { + special_case = SPECIAL_CASE_SETX; + continue; + } + else if (strncmp (str, "fdiv", 4) == 0) + { + special_case = SPECIAL_CASE_FDIV; + continue; + } + break; + + case 'o': + if (strncmp (s, "%asi", 4) != 0) + break; + s += 4; + continue; + + case 's': + if (strncmp (s, "%fprs", 5) != 0) + break; + s += 5; + continue; + + case 'E': + if (strncmp (s, "%ccr", 4) != 0) + break; + s += 4; + continue; + + case 't': + if (strncmp (s, "%tbr", 4) != 0) + break; + s += 4; + continue; + + case 'w': + if (strncmp (s, "%wim", 4) != 0) + break; + s += 4; + continue; + + case 'x': + { + char *push = input_line_pointer; + expressionS e; + + input_line_pointer = s; + expression (&e); + if (e.X_op == O_constant) + { + int n = e.X_add_number; + if (n != e.X_add_number || (n & ~0x1ff) != 0) + as_bad ("OPF immediate operand out of range (0-0x1ff)"); + else + opcode |= e.X_add_number << 5; + } + else + as_bad ("non-immediate OPF operand, ignored"); + s = input_line_pointer; + input_line_pointer = push; + continue; + } + + case 'y': + if (strncmp (s, "%y", 2) != 0) + break; + s += 2; + continue; + + case 'u': + case 'U': + { + /* Parse a sparclet cpreg. */ + int cpreg; + if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg)) + { + error_message = ": invalid cpreg name"; + goto error; + } + opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg)); + continue; + } + + default: + as_fatal ("failed sanity check."); + } /* switch on arg code */ + + /* Break out of for() loop. */ + break; + } /* for each arg that we expect */ + + error: + if (match == 0) + { + /* Args don't match. */ + if (&insn[1] - sparc_opcodes < sparc_num_opcodes + && (insn->name == insn[1].name + || !strcmp (insn->name, insn[1].name))) + { + ++insn; + s = argsStart; + continue; + } + else + { + as_bad ("Illegal operands%s", error_message); + return; + } + } + else + { + /* We have a match. Now see if the architecture is ok. */ + int needed_arch_mask = insn->architecture; + + if (v9_arg_p) + { + needed_arch_mask &= ~ ((1 << SPARC_OPCODE_ARCH_V9) + | (1 << SPARC_OPCODE_ARCH_V9A)); + needed_arch_mask |= (1 << SPARC_OPCODE_ARCH_V9); + } + + if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (current_architecture)) + ; /* ok */ + /* Can we bump up the architecture? */ + else if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (max_architecture)) + { + enum sparc_opcode_arch_val needed_architecture = + sparc_ffs (SPARC_OPCODE_SUPPORTED (max_architecture) + & needed_arch_mask); + + assert (needed_architecture <= SPARC_OPCODE_ARCH_MAX); + if (warn_on_bump + && needed_architecture > warn_after_architecture) + { + as_warn ("architecture bumped from \"%s\" to \"%s\" on \"%s\"", + sparc_opcode_archs[current_architecture].name, + sparc_opcode_archs[needed_architecture].name, + str); + warn_after_architecture = needed_architecture; + } + current_architecture = needed_architecture; + } + /* Conflict. */ + /* ??? This seems to be a bit fragile. What if the next entry in + the opcode table is the one we want and it is supported? + It is possible to arrange the table today so that this can't + happen but what about tomorrow? */ + else + { + int arch,printed_one_p = 0; + char *p; + char required_archs[SPARC_OPCODE_ARCH_MAX * 16]; + + /* Create a list of the architectures that support the insn. */ + needed_arch_mask &= ~ SPARC_OPCODE_SUPPORTED (max_architecture); + p = required_archs; + arch = sparc_ffs (needed_arch_mask); + while ((1 << arch) <= needed_arch_mask) + { + if ((1 << arch) & needed_arch_mask) + { + if (printed_one_p) + *p++ = '|'; + strcpy (p, sparc_opcode_archs[arch].name); + p += strlen (p); + printed_one_p = 1; + } + ++arch; + } + + as_bad ("Architecture mismatch on \"%s\".", str); + as_tsktsk (" (Requires %s; requested architecture is %s.)", + required_archs, + sparc_opcode_archs[max_architecture].name); + return; + } + } /* if no match */ + + break; + } /* forever looking for a match */ + + the_insn.opcode = opcode; +} + +/* Parse an argument that can be expressed as a keyword. + (eg: #StoreStore or %ccfr). + The result is a boolean indicating success. + If successful, INPUT_POINTER is updated. */ + +static int +parse_keyword_arg (lookup_fn, input_pointerP, valueP) + int (*lookup_fn) PARAMS ((const char *)); + char **input_pointerP; + int *valueP; +{ + int value; + char c, *p, *q; + + p = *input_pointerP; + for (q = p + (*p == '#' || *p == '%'); + isalnum ((unsigned char) *q) || *q == '_'; + ++q) + continue; + c = *q; + *q = 0; + value = (*lookup_fn) (p); + *q = c; + if (value == -1) + return 0; + *valueP = value; + *input_pointerP = q; + return 1; +} + +/* Parse an argument that is a constant expression. + The result is a boolean indicating success. */ + +static int +parse_const_expr_arg (input_pointerP, valueP) + char **input_pointerP; + int *valueP; +{ + char *save = input_line_pointer; + expressionS exp; + + input_line_pointer = *input_pointerP; + /* The next expression may be something other than a constant + (say if we're not processing the right variant of the insn). + Don't call expression unless we're sure it will succeed as it will + signal an error (which we want to defer until later). */ + /* FIXME: It might be better to define md_operand and have it recognize + things like %asi, etc. but continuing that route through to the end + is a lot of work. */ + if (*input_line_pointer == '%') + { + input_line_pointer = save; + return 0; + } + expression (&exp); + *input_pointerP = input_line_pointer; + input_line_pointer = save; + if (exp.X_op != O_constant) + return 0; + *valueP = exp.X_add_number; + return 1; +} + +/* Subroutine of sparc_ip to parse an expression. */ + +static int +get_expression (str) + char *str; +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = str; + seg = expression (&the_insn.exp); + if (seg != absolute_section + && seg != text_section + && seg != data_section + && seg != bss_section + && seg != undefined_section) + { + the_insn.error = "bad segment"; + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 1; + } + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 0; +} + +/* Subroutine of md_assemble to output one insn. */ + +static void +output_insn (insn, the_insn) + const struct sparc_opcode *insn; + struct sparc_it *the_insn; +{ + char *toP = frag_more (4); + + /* put out the opcode */ + if (INSN_BIG_ENDIAN) + number_to_chars_bigendian (toP, (valueT) the_insn->opcode, 4); + else + number_to_chars_littleendian (toP, (valueT) the_insn->opcode, 4); + + /* put out the symbol-dependent stuff */ + if (the_insn->reloc != BFD_RELOC_NONE) + { + fixS *fixP = fix_new_exp (frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + &the_insn->exp, + the_insn->pcrel, + the_insn->reloc); + /* Turn off overflow checking in fixup_segment. We'll do our + own overflow checking in md_apply_fix3. This is necessary because + the insn size is 4 and fixup_segment will signal an overflow for + large 8 byte quantities. */ + fixP->fx_no_overflow = 1; + } + + last_insn = insn; + last_opcode = the_insn->opcode; +} + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +char * +md_atof (type, litP, sizeP) + char type; + char *litP; + int *sizeP; +{ + int i,prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + char *t; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP = 0; + return "Bad call to MD_ATOF()"; + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + *sizeP = prec * sizeof (LITTLENUM_TYPE); + + if (target_big_endian) + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + else + { + for (i = prec - 1; i >= 0; i--) + { + md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + + return 0; +} + +/* Write a value out to the object file, using the appropriate + endianness. */ + +void +md_number_to_chars (buf, val, n) + char *buf; + valueT val; + int n; +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +/* Apply a fixS to the frags, now that we know the value it ought to + hold. */ + +int +md_apply_fix3 (fixP, value, segment) + fixS *fixP; + valueT *value; + segT segment; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + offsetT val; + long insn; + + val = *value; + + assert (fixP->fx_r_type < BFD_RELOC_UNUSED); + + fixP->fx_addnumber = val; /* Remember value for emit_reloc */ + +#ifdef OBJ_ELF + /* FIXME: SPARC ELF relocations don't use an addend in the data + field itself. This whole approach should be somehow combined + with the calls to bfd_install_relocation. Also, the value passed + in by fixup_segment includes the value of a defined symbol. We + don't want to include the value of an externally visible symbol. */ + if (fixP->fx_addsy != NULL) + { + if (fixP->fx_addsy->sy_used_in_reloc + && (S_IS_EXTERNAL (fixP->fx_addsy) + || S_IS_WEAK (fixP->fx_addsy) + || (sparc_pic_code && ! fixP->fx_pcrel) + || (S_GET_SEGMENT (fixP->fx_addsy) != segment + && ((bfd_get_section_flags (stdoutput, + S_GET_SEGMENT (fixP->fx_addsy)) + & SEC_LINK_ONCE) != 0 + || strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)), + ".gnu.linkonce", + sizeof ".gnu.linkonce" - 1) == 0))) + && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section + && S_GET_SEGMENT (fixP->fx_addsy) != undefined_section + && ! bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy))) + fixP->fx_addnumber -= S_GET_VALUE (fixP->fx_addsy); + return 1; + } +#endif + + /* This is a hack. There should be a better way to + handle this. Probably in terms of howto fields, once + we can look at these fixups in terms of howtos. */ + if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy) + val += fixP->fx_where + fixP->fx_frag->fr_address; + +#ifdef OBJ_AOUT + /* FIXME: More ridiculous gas reloc hacking. If we are going to + generate a reloc, then we just want to let the reloc addend set + the value. We do not want to also stuff the addend into the + object file. Including the addend in the object file works when + doing a static link, because the linker will ignore the object + file contents. However, the dynamic linker does not ignore the + object file contents. */ + if (fixP->fx_addsy != NULL + && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2) + val = 0; + + /* When generating PIC code, we do not want an addend for a reloc + against a local symbol. We adjust fx_addnumber to cancel out the + value already included in val, and to also cancel out the + adjustment which bfd_install_relocation will create. */ + if (sparc_pic_code + && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2 + && fixP->fx_addsy != NULL + && ! S_IS_COMMON (fixP->fx_addsy) + && (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0) + fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy); +#endif + + /* If this is a data relocation, just output VAL. */ + + if (fixP->fx_r_type == BFD_RELOC_16) + { + md_number_to_chars (buf, val, 2); + } + else if (fixP->fx_r_type == BFD_RELOC_32) + { + md_number_to_chars (buf, val, 4); + } + else if (fixP->fx_r_type == BFD_RELOC_64) + { + md_number_to_chars (buf, val, 8); + } + else + { + /* It's a relocation against an instruction. */ + + if (INSN_BIG_ENDIAN) + insn = bfd_getb32 ((unsigned char *) buf); + else + insn = bfd_getl32 ((unsigned char *) buf); + + switch (fixP->fx_r_type) + { + case BFD_RELOC_32_PCREL_S2: + val = val >> 2; + /* FIXME: This increment-by-one deserves a comment of why it's + being done! */ + if (! sparc_pic_code + || fixP->fx_addsy == NULL + || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0) + ++val; + insn |= val & 0x3fffffff; + break; + + case BFD_RELOC_SPARC_11: + if (! in_signed_range (val, 0x7ff)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x7ff; + break; + + case BFD_RELOC_SPARC_10: + if (! in_signed_range (val, 0x3ff)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x3ff; + break; + + case BFD_RELOC_SPARC_7: + if (! in_bitfield_range (val, 0x7f)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x7f; + break; + + case BFD_RELOC_SPARC_6: + if (! in_bitfield_range (val, 0x3f)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x3f; + break; + + case BFD_RELOC_SPARC_5: + if (! in_bitfield_range (val, 0x1f)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x1f; + break; + + case BFD_RELOC_SPARC_WDISP16: + /* FIXME: simplify */ + if (((val > 0) && (val & ~0x3fffc)) + || ((val < 0) && (~(val - 1) & ~0x3fffc))) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + /* FIXME: The +1 deserves a comment. */ + val = (val >> 2) + 1; + insn |= ((val & 0xc000) << 6) | (val & 0x3fff); + break; + + case BFD_RELOC_SPARC_WDISP19: + /* FIXME: simplify */ + if (((val > 0) && (val & ~0x1ffffc)) + || ((val < 0) && (~(val - 1) & ~0x1ffffc))) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + /* FIXME: The +1 deserves a comment. */ + val = (val >> 2) + 1; + insn |= val & 0x7ffff; + break; + + case BFD_RELOC_SPARC_HH22: + val = BSR (val, 32); + /* intentional fallthrough */ + + case BFD_RELOC_SPARC_LM22: + case BFD_RELOC_HI22: + if (!fixP->fx_addsy) + { + insn |= (val >> 10) & 0x3fffff; + } + else + { + /* FIXME: Need comment explaining why we do this. */ + insn &= ~0xffff; + } + break; + + case BFD_RELOC_SPARC22: + if (val & ~0x003fffff) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= (val & 0x3fffff); + break; + + case BFD_RELOC_SPARC_HM10: + val = BSR (val, 32); + /* intentional fallthrough */ + + case BFD_RELOC_LO10: + if (!fixP->fx_addsy) + { + insn |= val & 0x3ff; + } + else + { + /* FIXME: Need comment explaining why we do this. */ + insn &= ~0xff; + } + break; + + case BFD_RELOC_SPARC13: + if (! in_signed_range (val, 0x1fff)) + as_bad_where (fixP->fx_file, fixP->fx_line, "relocation overflow"); + insn |= val & 0x1fff; + break; + + case BFD_RELOC_SPARC_WDISP22: + val = (val >> 2) + 1; + /* FALLTHROUGH */ + case BFD_RELOC_SPARC_BASE22: + insn |= val & 0x3fffff; + break; + + case BFD_RELOC_SPARC_H44: + if (!fixP->fx_addsy) + { + bfd_vma tval = val; + tval >>= 22; + insn |= tval & 0x3fffff; + } + break; + + case BFD_RELOC_SPARC_M44: + if (!fixP->fx_addsy) + insn |= (val >> 12) & 0x3ff; + break; + + case BFD_RELOC_SPARC_L44: + if (!fixP->fx_addsy) + insn |= val & 0xfff; + break; + + case BFD_RELOC_SPARC_HIX22: + if (!fixP->fx_addsy) + { + val ^= ~ (offsetT) 0; + insn |= (val >> 10) & 0x3fffff; + } + break; + + case BFD_RELOC_SPARC_LOX10: + if (!fixP->fx_addsy) + insn |= 0x1c00 | (val & 0x3ff); + break; + + case BFD_RELOC_NONE: + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + "bad or unhandled relocation type: 0x%02x", + fixP->fx_r_type); + break; + } + + if (INSN_BIG_ENDIAN) + bfd_putb32 (insn, (unsigned char *) buf); + else + bfd_putl32 (insn, (unsigned char *) buf); + } + + /* Are we finished with this relocation now? */ + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) + fixP->fx_done = 1; + + return 1; +} + +/* Translate internal representation of relocation info to BFD target + format. */ +arelent * +tc_gen_reloc (section, fixp) + asection *section; + fixS *fixp; +{ + arelent *reloc; + bfd_reloc_code_real_type code; + + reloc = (arelent *) xmalloc (sizeof (arelent)); + + reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym; + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + switch (fixp->fx_r_type) + { + case BFD_RELOC_16: + case BFD_RELOC_32: + case BFD_RELOC_HI22: + case BFD_RELOC_LO10: + case BFD_RELOC_32_PCREL_S2: + case BFD_RELOC_SPARC13: + case BFD_RELOC_SPARC_BASE13: + case BFD_RELOC_SPARC_WDISP16: + case BFD_RELOC_SPARC_WDISP19: + case BFD_RELOC_SPARC_WDISP22: + case BFD_RELOC_64: + case BFD_RELOC_SPARC_5: + case BFD_RELOC_SPARC_6: + case BFD_RELOC_SPARC_7: + case BFD_RELOC_SPARC_10: + case BFD_RELOC_SPARC_11: + case BFD_RELOC_SPARC_HH22: + case BFD_RELOC_SPARC_HM10: + case BFD_RELOC_SPARC_LM22: + case BFD_RELOC_SPARC_PC_HH22: + case BFD_RELOC_SPARC_PC_HM10: + case BFD_RELOC_SPARC_PC_LM22: + case BFD_RELOC_SPARC_H44: + case BFD_RELOC_SPARC_M44: + case BFD_RELOC_SPARC_L44: + case BFD_RELOC_SPARC_HIX22: + case BFD_RELOC_SPARC_LOX10: + code = fixp->fx_r_type; + break; + default: + abort (); + return NULL; + } + +#if defined (OBJ_ELF) || defined (OBJ_AOUT) + /* If we are generating PIC code, we need to generate a different + set of relocs. */ + +#ifdef OBJ_ELF +#define GOT_NAME "_GLOBAL_OFFSET_TABLE_" +#else +#define GOT_NAME "__GLOBAL_OFFSET_TABLE_" +#endif + + if (sparc_pic_code) + { + switch (code) + { + case BFD_RELOC_32_PCREL_S2: + if (! S_IS_DEFINED (fixp->fx_addsy) + || S_IS_COMMON (fixp->fx_addsy) + || S_IS_EXTERNAL (fixp->fx_addsy) + || S_IS_WEAK (fixp->fx_addsy)) + code = BFD_RELOC_SPARC_WPLT30; + break; + case BFD_RELOC_HI22: + if (fixp->fx_addsy != NULL + && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0) + code = BFD_RELOC_SPARC_PC22; + else + code = BFD_RELOC_SPARC_GOT22; + break; + case BFD_RELOC_LO10: + if (fixp->fx_addsy != NULL + && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0) + code = BFD_RELOC_SPARC_PC10; + else + code = BFD_RELOC_SPARC_GOT10; + break; + case BFD_RELOC_SPARC13: + code = BFD_RELOC_SPARC_GOT13; + break; + default: + break; + } + } +#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */ + + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + if (reloc->howto == 0) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + "internal error: can't export reloc type %d (`%s')", + fixp->fx_r_type, bfd_get_reloc_code_name (code)); + return 0; + } + + /* @@ Why fx_addnumber sometimes and fx_offset other times? */ +#ifdef OBJ_AOUT + + if (reloc->howto->pc_relative == 0 + || code == BFD_RELOC_SPARC_PC10 + || code == BFD_RELOC_SPARC_PC22) + reloc->addend = fixp->fx_addnumber; + else + reloc->addend = fixp->fx_offset - reloc->address; + +#else /* elf or coff */ + + if (reloc->howto->pc_relative == 0 + || code == BFD_RELOC_SPARC_PC10 + || code == BFD_RELOC_SPARC_PC22) + reloc->addend = fixp->fx_addnumber; + else if ((fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0) + reloc->addend = (section->vma + + fixp->fx_addnumber + + md_pcrel_from (fixp)); + else + reloc->addend = fixp->fx_offset; +#endif + + return reloc; +} + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} /* md_undefined_symbol() */ + +/* Round up a section size to the appropriate boundary. */ +valueT +md_section_align (segment, size) + segT segment; + valueT size; +{ +#ifndef OBJ_ELF + /* This is not right for ELF; a.out wants it, and COFF will force + the alignment anyways. */ + valueT align = ((valueT) 1 + << (valueT) bfd_get_section_alignment (stdoutput, segment)); + valueT newsize; + /* turn alignment value into a mask */ + align--; + newsize = (size + align) & ~align; + return newsize; +#else + return size; +#endif +} + +/* Exactly what point is a PC-relative offset relative TO? + On the sparc, they're relative to the address of the offset, plus + its size. This gets us to the following instruction. + (??? Is this right? FIXME-SOON) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + long ret; + + ret = fixP->fx_where + fixP->fx_frag->fr_address; + if (! sparc_pic_code + || fixP->fx_addsy == NULL + || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0) + ret += fixP->fx_size; + return ret; +} + +/* + * sort of like s_lcomm + */ + +#ifndef OBJ_ELF +static int max_alignment = 15; +#endif + +static void +s_reserve (ignore) + int ignore; +{ + char *name; + char *p; + char c; + int align; + int size; + int temp; + symbolS *symbolP; + + name = input_line_pointer; + c = get_symbol_end (); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',') + { + as_bad ("Expected comma after name"); + ignore_rest_of_line (); + return; + } + + ++input_line_pointer; + + if ((size = get_absolute_expression ()) < 0) + { + as_bad ("BSS length (%d.) <0! Ignored.", size); + ignore_rest_of_line (); + return; + } /* bad length */ + + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + + if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0 + && strncmp (input_line_pointer, ",\".bss\"", 7) != 0) + { + as_bad ("bad .reserve segment -- expected BSS segment"); + return; + } + + if (input_line_pointer[2] == '.') + input_line_pointer += 7; + else + input_line_pointer += 6; + SKIP_WHITESPACE (); + + if (*input_line_pointer == ',') + { + ++input_line_pointer; + + SKIP_WHITESPACE (); + if (*input_line_pointer == '\n') + { + as_bad ("Missing alignment"); + return; + } + + align = get_absolute_expression (); +#ifndef OBJ_ELF + if (align > max_alignment) + { + align = max_alignment; + as_warn ("Alignment too large: %d. assumed.", align); + } +#endif + if (align < 0) + { + align = 0; + as_warn ("Alignment negative. 0 assumed."); + } + + record_alignment (bss_section, align); + + /* convert to a power of 2 alignment */ + for (temp = 0; (align & 1) == 0; align >>= 1, ++temp);; + + if (align != 1) + { + as_bad ("Alignment not a power of 2"); + ignore_rest_of_line (); + return; + } /* not a power of two */ + + align = temp; + } /* if has optional alignment */ + else + align = 0; + + if (!S_IS_DEFINED (symbolP) +#ifdef OBJ_AOUT + && S_GET_OTHER (symbolP) == 0 + && S_GET_DESC (symbolP) == 0 +#endif + ) + { + if (! need_pass_2) + { + char *pfrag; + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + + subseg_set (bss_section, 1); /* switch to bss */ + + if (align) + frag_align (align, 0, 0); /* do alignment */ + + /* detach from old frag */ + if (S_GET_SEGMENT(symbolP) == bss_section) + symbolP->sy_frag->fr_symbol = NULL; + + symbolP->sy_frag = frag_now; + pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP, + (offsetT) size, (char *)0); + *pfrag = 0; + + S_SET_SEGMENT (symbolP, bss_section); + + subseg_set (current_seg, current_subseg); + } + } + else + { + as_warn("Ignoring attempt to re-define symbol %s", + S_GET_NAME (symbolP)); + } /* if not redefining */ + + demand_empty_rest_of_line (); +} + +static void +s_common (ignore) + int ignore; +{ + char *name; + char c; + char *p; + int temp, size; + symbolS *symbolP; + + name = input_line_pointer; + c = get_symbol_end (); + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad ("Expected comma after symbol-name"); + ignore_rest_of_line (); + return; + } + input_line_pointer++; /* skip ',' */ + if ((temp = get_absolute_expression ()) < 0) + { + as_bad (".COMMon length (%d.) <0! Ignored.", temp); + ignore_rest_of_line (); + return; + } + size = temp; + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) + { + as_bad ("Ignoring attempt to re-define symbol"); + ignore_rest_of_line (); + return; + } + if (S_GET_VALUE (symbolP) != 0) + { + if (S_GET_VALUE (symbolP) != (valueT) size) + { + as_warn ("Length of .comm \"%s\" is already %ld. Not changed to %d.", + S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size); + } + } + else + { +#ifndef OBJ_ELF + S_SET_VALUE (symbolP, (valueT) size); + S_SET_EXTERNAL (symbolP); +#endif + } + know (symbolP->sy_frag == &zero_address_frag); + if (*input_line_pointer != ',') + { + as_bad ("Expected comma after common length"); + ignore_rest_of_line (); + return; + } + input_line_pointer++; + SKIP_WHITESPACE (); + if (*input_line_pointer != '"') + { + temp = get_absolute_expression (); +#ifndef OBJ_ELF + if (temp > max_alignment) + { + temp = max_alignment; + as_warn ("Common alignment too large: %d. assumed", temp); + } +#endif + if (temp < 0) + { + temp = 0; + as_warn ("Common alignment negative; 0 assumed"); + } +#ifdef OBJ_ELF + if (symbolP->local) + { + segT old_sec; + int old_subsec; + char *p; + int align; + + old_sec = now_seg; + old_subsec = now_subseg; + align = temp; + record_alignment (bss_section, align); + subseg_set (bss_section, 0); + if (align) + frag_align (align, 0, 0); + if (S_GET_SEGMENT (symbolP) == bss_section) + symbolP->sy_frag->fr_symbol = 0; + symbolP->sy_frag = frag_now; + p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, + (offsetT) size, (char *) 0); + *p = 0; + S_SET_SEGMENT (symbolP, bss_section); + S_CLEAR_EXTERNAL (symbolP); + subseg_set (old_sec, old_subsec); + } + else +#endif + { + allocate_common: + S_SET_VALUE (symbolP, (valueT) size); +#ifdef OBJ_ELF + S_SET_ALIGN (symbolP, temp); +#endif + S_SET_EXTERNAL (symbolP); + S_SET_SEGMENT (symbolP, bfd_com_section_ptr); + } + } + else + { + input_line_pointer++; + /* @@ Some use the dot, some don't. Can we get some consistency?? */ + if (*input_line_pointer == '.') + input_line_pointer++; + /* @@ Some say data, some say bss. */ + if (strncmp (input_line_pointer, "bss\"", 4) + && strncmp (input_line_pointer, "data\"", 5)) + { + while (*--input_line_pointer != '"') + ; + input_line_pointer--; + goto bad_common_segment; + } + while (*input_line_pointer++ != '"') + ; + goto allocate_common; + } + +#ifdef BFD_ASSEMBLER + symbolP->bsym->flags |= BSF_OBJECT; +#endif + + demand_empty_rest_of_line (); + return; + + { + bad_common_segment: + p = input_line_pointer; + while (*p && *p != '\n') + p++; + c = *p; + *p = '\0'; + as_bad ("bad .common segment %s", input_line_pointer + 1); + *p = c; + input_line_pointer = p; + ignore_rest_of_line (); + return; + } +} + +/* Handle the .empty pseudo-op. This supresses the warnings about + invalid delay slot usage. */ + +static void +s_empty (ignore) + int ignore; +{ + /* The easy way to implement is to just forget about the last + instruction. */ + last_insn = NULL; +} + +static void +s_seg (ignore) + int ignore; +{ + + if (strncmp (input_line_pointer, "\"text\"", 6) == 0) + { + input_line_pointer += 6; + s_text (0); + return; + } + if (strncmp (input_line_pointer, "\"data\"", 6) == 0) + { + input_line_pointer += 6; + s_data (0); + return; + } + if (strncmp (input_line_pointer, "\"data1\"", 7) == 0) + { + input_line_pointer += 7; + s_data1 (); + return; + } + if (strncmp (input_line_pointer, "\"bss\"", 5) == 0) + { + input_line_pointer += 5; + /* We only support 2 segments -- text and data -- for now, so + things in the "bss segment" will have to go into data for now. + You can still allocate SEG_BSS stuff with .lcomm or .reserve. */ + subseg_set (data_section, 255); /* FIXME-SOMEDAY */ + return; + } + as_bad ("Unknown segment type"); + demand_empty_rest_of_line (); +} + +static void +s_data1 () +{ + subseg_set (data_section, 1); + demand_empty_rest_of_line (); +} + +static void +s_proc (ignore) + int ignore; +{ + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + ++input_line_pointer; + } + ++input_line_pointer; +} + +/* This static variable is set by s_uacons to tell sparc_cons_align + that the expession does not need to be aligned. */ + +static int sparc_no_align_cons = 0; + +/* This handles the unaligned space allocation pseudo-ops, such as + .uaword. .uaword is just like .word, but the value does not need + to be aligned. */ + +static void +s_uacons (bytes) + int bytes; +{ + /* Tell sparc_cons_align not to align this value. */ + sparc_no_align_cons = 1; + cons (bytes); +} + +/* If the --enforce-aligned-data option is used, we require .word, + et. al., to be aligned correctly. We do it by setting up an + rs_align_code frag, and checking in HANDLE_ALIGN to make sure that + no unexpected alignment was introduced. + + The SunOS and Solaris native assemblers enforce aligned data by + default. We don't want to do that, because gcc can deliberately + generate misaligned data if the packed attribute is used. Instead, + we permit misaligned data by default, and permit the user to set an + option to check for it. */ + +void +sparc_cons_align (nbytes) + int nbytes; +{ + int nalign; + char *p; + + /* Only do this if we are enforcing aligned data. */ + if (! enforce_aligned_data) + return; + + if (sparc_no_align_cons) + { + /* This is an unaligned pseudo-op. */ + sparc_no_align_cons = 0; + return; + } + + nalign = 0; + while ((nbytes & 1) == 0) + { + ++nalign; + nbytes >>= 1; + } + + if (nalign == 0) + return; + + if (now_seg == absolute_section) + { + if ((abs_section_offset & ((1 << nalign) - 1)) != 0) + as_bad ("misaligned data"); + return; + } + + p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0, + (symbolS *) NULL, (offsetT) nalign, (char *) NULL); + + record_alignment (now_seg, nalign); +} + +/* This is where we do the unexpected alignment check. + This is called from HANDLE_ALIGN in tc-sparc.h. */ + +void +sparc_handle_align (fragp) + fragS *fragp; +{ + if (fragp->fr_type == rs_align_code && !fragp->fr_subtype + && fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix != 0) + as_bad_where (fragp->fr_file, fragp->fr_line, "misaligned data"); + if (fragp->fr_type == rs_align_code && fragp->fr_subtype == 1024) + { + int count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + + if (count >= 4 && !(count & 3) && count <= 1024 && !((long)(fragp->fr_literal + fragp->fr_fix) & 3)) + { + unsigned *p = (unsigned *)(fragp->fr_literal + fragp->fr_fix); + int i; + + for (i = 0; i < count; i += 4) + *p++ = 0x01000000; /* nop */ + if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8) + *(unsigned *)(fragp->fr_literal + fragp->fr_fix) = + 0x30680000 | (count >> 2); /* ba,a,pt %xcc, 1f */ + fragp->fr_var = count; + } + } +} + +#ifdef OBJ_ELF +/* Some special processing for a Sparc ELF file. */ + +void +sparc_elf_final_processing () +{ + /* Set the Sparc ELF flag bits. FIXME: There should probably be some + sort of BFD interface for this. */ + if (sparc_arch_size == 64) + switch (sparc_memory_model) + { + case MM_RMO: + elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_RMO; + break; + case MM_PSO: + elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_PSO; + break; + } + else if (current_architecture >= SPARC_OPCODE_ARCH_V9) + elf_elfheader (stdoutput)->e_flags |= EF_SPARC_32PLUS; + if (current_architecture == SPARC_OPCODE_ARCH_V9A) + elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1; +} +#endif diff --git a/contrib/binutils/gas/config/tc-sparc.h b/contrib/binutils/gas/config/tc-sparc.h new file mode 100644 index 0000000..bb09bd4 --- /dev/null +++ b/contrib/binutils/gas/config/tc-sparc.h @@ -0,0 +1,149 @@ +/* tc-sparc.h - Macros and type defines for the sparc. + Copyright (C) 1989, 90-96, 97, 1998 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + 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 2, + 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, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef TC_SPARC +#define TC_SPARC 1 + +#ifdef ANSI_PROTOTYPES +struct frag; +#endif + +/* This is used to set the default value for `target_big_endian'. */ +#define TARGET_BYTES_BIG_ENDIAN 1 + +#define LOCAL_LABELS_FB 1 + +#define TARGET_ARCH bfd_arch_sparc + +extern const char *sparc_target_format PARAMS ((void)); +#define TARGET_FORMAT sparc_target_format () + +#ifdef TE_SPARCAOUT +/* Bi-endian support may eventually be unconditional, but until things are + working well it's only provided for targets that need it. */ +#define SPARC_BIENDIAN +#endif + +#define WORKING_DOT_WORD + +#define md_convert_frag(b,s,f) {as_fatal ("sparc convert_frag\n");} +#define md_create_long_jump(p,f,t,fr,s) as_fatal("sparc_create_long_jump") +#define md_create_short_jump(p,f,t,fr,s) as_fatal("sparc_create_short_jump") +#define md_estimate_size_before_relax(f,s) \ + (as_fatal("estimate_size_before_relax called"),1) + +#define LISTING_HEADER "SPARC GAS " + +extern int sparc_pic_code; + +#define md_do_align(n, fill, len, max, around) \ +if ((n) && (n) <= 10 && !need_pass_2 && !(fill) \ + && now_seg != data_section && now_seg != bss_section) \ + { \ + char *p; \ + p = frag_var (rs_align_code, 1024, 1, (relax_substateT) 1024, \ + (symbolS *) 0, (offsetT) (n), (char *) 0); \ + *p = 0x00; \ + goto around; \ + } + +/* We require .word, et. al., to be aligned correctly. */ +#define md_cons_align(nbytes) sparc_cons_align (nbytes) +extern void sparc_cons_align PARAMS ((int)); +#define HANDLE_ALIGN(fragp) sparc_handle_align (fragp) +extern void sparc_handle_align PARAMS ((struct frag *)); + +#if defined (OBJ_ELF) || defined (OBJ_AOUT) + +/* This expression evaluates to false if the relocation is for a local + object for which we still want to do the relocation at runtime. + True if we are willing to perform this relocation while building + the .o file. + + If the reloc is against an externally visible symbol, then the + a.out assembler should not do the relocation if generating PIC, and + the ELF assembler should never do the relocation. */ + +#ifdef OBJ_ELF +#define obj_relocate_extern 0 +#else +#define obj_relocate_extern (! sparc_pic_code) +#endif + +#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \ + (obj_relocate_extern \ + || (FIX)->fx_addsy == NULL \ + || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \ + && ! S_IS_WEAK ((FIX)->fx_addsy) \ + && S_IS_DEFINED ((FIX)->fx_addsy) \ + && ! S_IS_COMMON ((FIX)->fx_addsy))) +#endif + +/* I know that "call 0" fails in sparc-coff if this doesn't return 1. I + don't know about other relocation types, or other formats, yet. */ +#ifdef OBJ_COFF +#define TC_FORCE_RELOCATION(FIXP) \ + ((FIXP)->fx_r_type == BFD_RELOC_32_PCREL_S2 \ + && ((FIXP)->fx_addsy == 0 \ + || S_GET_SEGMENT ((FIXP)->fx_addsy) == absolute_section)) +#define RELOC_REQUIRES_SYMBOL +#endif + +#define MD_APPLY_FIX3 +#define TC_HANDLES_FX_DONE + +#ifdef OBJ_ELF +/* Keep relocations against global symbols. Don't turn them into + relocations against sections. This is required for the dynamic + linker to operate properly. When generating PIC, we need to keep + any non PC relative reloc. */ +#define tc_fix_adjustable(FIX) \ + (! S_IS_EXTERNAL ((FIX)->fx_addsy) \ + && ! S_IS_WEAK ((FIX)->fx_addsy) \ + && (! sparc_pic_code \ + || (FIX)->fx_pcrel \ + || ((FIX)->fx_subsy != NULL \ + && (S_GET_SEGMENT ((FIX)->fx_subsy) \ + == S_GET_SEGMENT ((FIX)->fx_addsy))) \ + || strchr (S_GET_NAME ((FIX)->fx_addsy), '\001') != NULL \ + || strchr (S_GET_NAME ((FIX)->fx_addsy), '\002') != NULL)) +#endif + +#ifdef OBJ_AOUT +/* When generating PIC code, we must not adjust any reloc which will + turn into a reloc against the global offset table. */ +#define tc_fix_adjustable(FIX) \ + (! sparc_pic_code \ + || (FIX)->fx_pcrel \ + || (FIX)->fx_r_type == BFD_RELOC_16 \ + || (FIX)->fx_r_type == BFD_RELOC_32) +#endif + +#define elf_tc_final_processing sparc_elf_final_processing +extern void sparc_elf_final_processing PARAMS ((void)); + +#define md_operand(x) + +extern void sparc_md_end PARAMS ((void)); +#define md_end() sparc_md_end () + +#endif + +/* end of tc-sparc.h */ diff --git a/contrib/binutils/include/aout/sun4.h b/contrib/binutils/include/aout/sun4.h new file mode 100644 index 0000000..f42a0dd --- /dev/null +++ b/contrib/binutils/include/aout/sun4.h @@ -0,0 +1,219 @@ +/* SPARC-specific values for a.out files */ + +/* Some systems, e.g., AIX, may have defined this in header files already + included. */ +#undef TARGET_PAGE_SIZE +#define TARGET_PAGE_SIZE 0x2000 /* 8K. aka NBPG in <sys/param.h> */ +/* Note that some SPARCs have 4K pages, some 8K, some others. */ + +#define SEG_SIZE_SPARC TARGET_PAGE_SIZE +#define SEG_SIZE_SUN3 0x20000 /* Resolution of r/w protection hw */ + +#define TEXT_START_ADDR TARGET_PAGE_SIZE /* Location 0 is not accessible */ +#define N_HEADER_IN_TEXT(x) 1 + +/* Non-default definitions of the accessor macros... */ + +/* Segment size varies on Sun-3 versus Sun-4. */ + +#define N_SEGSIZE(x) (N_MACHTYPE(x) == M_SPARC? SEG_SIZE_SPARC: \ + N_MACHTYPE(x) == M_68020? SEG_SIZE_SUN3: \ + /* Guess? */ TARGET_PAGE_SIZE) + +/* Virtual Address of text segment from the a.out file. For OMAGIC, + (almost always "unlinked .o's" these days), should be zero. + Sun added a kludge so that shared libraries linked ZMAGIC get + an address of zero if a_entry (!!!) is lower than the otherwise + expected text address. These kludges have gotta go! + For linked files, should reflect reality if we know it. */ + +/* This differs from the version in aout64.h (which we override by defining + it here) only for NMAGIC (we return TEXT_START_ADDR+EXEC_BYTES_SIZE; + they return 0). */ + +#define N_TXTADDR(x) \ + (N_MAGIC(x)==OMAGIC? 0 \ + : (N_MAGIC(x) == ZMAGIC && (x).a_entry < TEXT_START_ADDR)? 0 \ + : TEXT_START_ADDR+EXEC_BYTES_SIZE) + +/* When a file is linked against a shared library on SunOS 4, the + dynamic bit in the exec header is set, and the first symbol in the + symbol table is __DYNAMIC. Its value is the address of the + following structure. */ + +struct external_sun4_dynamic +{ + /* The version number of the structure. SunOS 4.1.x creates files + with version number 3, which is what this structure is based on. + According to gdb, version 2 is similar. I believe that version 2 + used a different type of procedure linkage table, and there may + have been other differences. */ + bfd_byte ld_version[4]; + /* The virtual address of a 28 byte structure used in debugging. + The contents are filled in at run time by ld.so. */ + bfd_byte ldd[4]; + /* The virtual address of another structure with information about + how to relocate the executable at run time. */ + bfd_byte ld[4]; +}; + +/* The size of the debugging structure pointed to by the debugger + field of __DYNAMIC. */ +#define EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE (24) + +/* The structure pointed to by the linker field of __DYNAMIC. As far + as I can tell, most of the addresses in this structure are offsets + within the file, but some are actually virtual addresses. */ + +struct internal_sun4_dynamic_link +{ + /* Linked list of loaded objects. This is filled in at runtime by + ld.so and probably by dlopen. */ + unsigned long ld_loaded; + + /* The address of the list of names of shared objects which must be + included at runtime. Each entry in the list is 16 bytes: the 4 + byte address of the string naming the object (e.g., for -lc this + is "c"); 4 bytes of flags--the high bit is whether to search for + the object using the library path; the 2 byte major version + number; the 2 byte minor version number; the 4 byte address of + the next entry in the list (zero if this is the last entry). The + version numbers seem to only be non-zero when doing library + searching. */ + unsigned long ld_need; + + /* The address of the path to search for the shared objects which + must be included. This points to a string in PATH format which + is generated from the -L arguments to the linker. According to + the man page, ld.so implicitly adds ${LD_LIBRARY_PATH} to the + beginning of this string and /lib:/usr/lib:/usr/local/lib to the + end. The string is terminated by a null byte. This field is + zero if there is no additional path. */ + unsigned long ld_rules; + + /* The address of the global offset table. This appears to be a + virtual address, not a file offset. The first entry in the + global offset table seems to be the virtual address of the + sun4_dynamic structure (the same value as the __DYNAMIC symbol). + The global offset table is used for PIC code to hold the + addresses of variables. A dynamically linked file which does not + itself contain PIC code has a four byte global offset table. */ + unsigned long ld_got; + + /* The address of the procedure linkage table. This appears to be a + virtual address, not a file offset. + + On a SPARC, the table is composed of 12 byte entries, each of + which consists of three instructions. The first entry is + sethi %hi(0),%g1 + jmp %g1 + nop + These instructions are changed by ld.so into a jump directly into + ld.so itself. Each subsequent entry is + save %sp, -96, %sp + call <address of first entry in procedure linkage table> + <reloc_number | 0x01000000> + The reloc_number is the number of the reloc to use to resolve + this entry. The reloc will be a JMP_SLOT reloc against some + symbol that is not defined in this object file but should be + defined in a shared object (if it is not, ld.so will report a + runtime error and exit). The constant 0x010000000 turns the + reloc number into a sethi of %g0, which does nothing since %g0 is + hardwired to zero. + + When one of these entries is executed, it winds up calling into + ld.so. ld.so looks at the reloc number, available via the return + address, to determine which entry this is. It then looks at the + reloc and patches up the entry in the table into a sethi and jmp + to the real address followed by a nop. This means that the reloc + lookup only has to happen once, and it also means that the + relocation only needs to be done if the function is actually + called. The relocation is expensive because ld.so must look up + the symbol by name. + + The size of the procedure linkage table is given by the ld_plt_sz + field. */ + unsigned long ld_plt; + + /* The address of the relocs. These are in the same format as + ordinary relocs. Symbol index numbers refer to the symbols + pointed to by ld_stab. I think the only way to determine the + number of relocs is to assume that all the bytes from ld_rel to + ld_hash contain reloc entries. */ + unsigned long ld_rel; + + /* The address of a hash table of symbols. The hash table has + roughly the same number of entries as there are dynamic symbols; + I think the only way to get the exact size is to assume that + every byte from ld_hash to ld_stab is devoted to the hash table. + + Each entry in the hash table is eight bytes. The first four + bytes are a symbol index into the dynamic symbols. The second + four bytes are the index of the next hash table entry in the + bucket. The ld_buckets field gives the number of buckets, say B. + The first B entries in the hash table each start a bucket which + is chained through the second four bytes of each entry. A value + of zero ends the chain. + + The hash function is simply + h = 0; + while (*string != '\0') + h = (h << 1) + *string++; + h &= 0x7fffffff; + + To look up a symbol, compute the hash value of the name. Take + the modulos of hash value and the number of buckets. Start at + that entry in the hash table. See if the symbol (from the first + four bytes of the hash table entry) has the name you are looking + for. If not, use the chain field (the second four bytes of the + hash table entry) to move on to the next entry in this bucket. + If the chain field is zero you have reached the end of the + bucket, and the symbol is not in the hash table. */ + unsigned long ld_hash; + + /* The address of the symbol table. This is a list of + external_nlist structures. The string indices are relative to + the ld_symbols field. I think the only way to determine the + number of symbols is to assume that all the bytes between ld_stab + and ld_symbols are external_nlist structures. */ + unsigned long ld_stab; + + /* I don't know what this is for. It seems to always be zero. */ + unsigned long ld_stab_hash; + + /* The number of buckets in the hash table. */ + unsigned long ld_buckets; + + /* The address of the symbol string table. The first string in this + string table need not be the empty string. */ + unsigned long ld_symbols; + + /* The size in bytes of the symbol string table. */ + unsigned long ld_symb_size; + + /* The size in bytes of the text segment. */ + unsigned long ld_text; + + /* The size in bytes of the procedure linkage table. */ + unsigned long ld_plt_sz; +}; + +/* The external form of the structure. */ + +struct external_sun4_dynamic_link +{ + bfd_byte ld_loaded[4]; + bfd_byte ld_need[4]; + bfd_byte ld_rules[4]; + bfd_byte ld_got[4]; + bfd_byte ld_plt[4]; + bfd_byte ld_rel[4]; + bfd_byte ld_hash[4]; + bfd_byte ld_stab[4]; + bfd_byte ld_stab_hash[4]; + bfd_byte ld_buckets[4]; + bfd_byte ld_symbols[4]; + bfd_byte ld_symb_size[4]; + bfd_byte ld_text[4]; + bfd_byte ld_plt_sz[4]; +}; diff --git a/contrib/binutils/include/elf/sparc.h b/contrib/binutils/include/elf/sparc.h new file mode 100644 index 0000000..d9aed21 --- /dev/null +++ b/contrib/binutils/include/elf/sparc.h @@ -0,0 +1,117 @@ +/* SPARC ELF support for BFD. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + By Doug Evans, Cygnus Support, <dje@cygnus.com>. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _ELF_SPARC_H +#define _ELF_SPARC_H + +/* Processor specific flags for the ELF header e_flags field. */ + +/* These are defined by Sun. */ + +#define EF_SPARC_32PLUS_MASK 0xffff00 /* bits indicating V8+ type */ +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ + +/* This name is used in the V9 ABI. */ +#define EF_SPARC_EXT_MASK 0xffff00 /* reserved for vendor extensions */ + +/* V9 memory models */ +#define EF_SPARCV9_MM 0x3 /* memory model mask */ +#define EF_SPARCV9_TSO 0x0 /* total store ordering */ +#define EF_SPARCV9_PSO 0x1 /* partial store ordering */ +#define EF_SPARCV9_RMO 0x2 /* relaxed store ordering */ + +/* Section indices. */ + +#define SHN_BEFORE 0xff00 /* used with SHF_ORDERED */ +#define SHN_AFTER 0xff01 /* used with SHF_ORDERED */ + +/* Section flags. */ + +#define SHF_EXCLUDE 0x80000000 /* exclude from linking */ +#define SHF_ORDERED 0x40000000 /* treat sh_link,sh_info specially */ + +/* Symbol types. */ + +#define STT_REGISTER 13 /* global reg reserved to app. */ + +/* Relocation types. */ + +enum elf_sparc_reloc_type { + R_SPARC_NONE = 0, + R_SPARC_8, R_SPARC_16, R_SPARC_32, + R_SPARC_DISP8, R_SPARC_DISP16, R_SPARC_DISP32, + R_SPARC_WDISP30, R_SPARC_WDISP22, + R_SPARC_HI22, R_SPARC_22, + R_SPARC_13, R_SPARC_LO10, + R_SPARC_GOT10, R_SPARC_GOT13, R_SPARC_GOT22, + R_SPARC_PC10, R_SPARC_PC22, + R_SPARC_WPLT30, + R_SPARC_COPY, + R_SPARC_GLOB_DAT, R_SPARC_JMP_SLOT, + R_SPARC_RELATIVE, + R_SPARC_UA32, + + /* ??? These 6 relocs are new but not currently used. For binary + compatility in the sparc64-elf toolchain, we leave them out. + A non-binary upward compatible change is expected for sparc64-elf. */ +#ifndef SPARC64_OLD_RELOCS + /* ??? New relocs on the UltraSPARC. Not sure what they're for yet. */ + R_SPARC_PLT32, R_SPARC_HIPLT22, R_SPARC_LOPLT10, + R_SPARC_PCPLT32, R_SPARC_PCPLT22, R_SPARC_PCPLT10, +#endif + + /* v9 relocs */ + R_SPARC_10, R_SPARC_11, R_SPARC_64, + R_SPARC_OLO10, R_SPARC_HH22, R_SPARC_HM10, R_SPARC_LM22, + R_SPARC_PC_HH22, R_SPARC_PC_HM10, R_SPARC_PC_LM22, + R_SPARC_WDISP16, R_SPARC_WDISP19, + R_SPARC_UNUSED_42, + R_SPARC_7, R_SPARC_5, R_SPARC_6, + R_SPARC_DISP64, R_SPARC_PLT64, + R_SPARC_HIX22, R_SPARC_LOX10, + R_SPARC_H44, R_SPARC_M44, R_SPARC_L44, + R_SPARC_REGISTER, + R_SPARC_UA64, R_SPARC_UA16, + + R_SPARC_max +}; + +/* Relocation macros. */ + +#define ELF64_R_TYPE_DATA(info) (((bfd_vma) (info) << 32) >> 40) +#define ELF64_R_TYPE_ID(info) (((bfd_vma) (info) << 56) >> 56) +#define ELF64_R_TYPE_INFO(data, type) (((bfd_vma) (data) << 8) \ + + (bfd_vma) (type)) + +#define DT_SPARC_REGISTER 0x70000001 + +/* + * FIXME: NOT ABI -- GET RID OF THIS + * Defines the format used by the .plt. Currently defined values are + * 0 -- reserved to SI + * 1 -- absolute address in .got.plt + * 2 -- got-relative address in .got.plt + */ + +#define DT_SPARC_PLTFMT 0x70000001 + +#endif /* _ELF_SPARC_H */ diff --git a/contrib/binutils/include/opcode/sparc.h b/contrib/binutils/include/opcode/sparc.h new file mode 100644 index 0000000..4f159bd --- /dev/null +++ b/contrib/binutils/include/opcode/sparc.h @@ -0,0 +1,240 @@ +/* Definitions for opcode table for the sparc. + Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 1997 + Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler, GDB, the GNU debugger, and +the GNU Binutils. + +GAS/GDB 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 2, or (at your option) +any later version. + +GAS/GDB 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 or GDB; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <ansidecl.h> + +/* The SPARC opcode table (and other related data) is defined in + the opcodes library in sparc-opc.c. If you change anything here, make + sure you fix up that file, and vice versa. */ + + /* FIXME-someday: perhaps the ,a's and such should be embedded in the + instruction's name rather than the args. This would make gas faster, pinsn + slower, but would mess up some macros a bit. xoxorich. */ + +/* List of instruction sets variations. + These values are such that each element is either a superset of a + preceding each one or they conflict in which case SPARC_OPCODE_CONFLICT_P + returns non-zero. + The values are indices into `sparc_opcode_archs' defined in sparc-opc.c. + Don't change this without updating sparc-opc.c. */ + +enum sparc_opcode_arch_val { + SPARC_OPCODE_ARCH_V6 = 0, + SPARC_OPCODE_ARCH_V7, + SPARC_OPCODE_ARCH_V8, + SPARC_OPCODE_ARCH_SPARCLET, + SPARC_OPCODE_ARCH_SPARCLITE, + /* v9 variants must appear last */ + SPARC_OPCODE_ARCH_V9, + SPARC_OPCODE_ARCH_V9A, /* v9 with ultrasparc additions */ + SPARC_OPCODE_ARCH_BAD /* error return from sparc_opcode_lookup_arch */ +}; + +/* The highest architecture in the table. */ +#define SPARC_OPCODE_ARCH_MAX (SPARC_OPCODE_ARCH_BAD - 1) + +/* Given an enum sparc_opcode_arch_val, return the bitmask to use in + insn encoding/decoding. */ +#define SPARC_OPCODE_ARCH_MASK(arch) (1 << (arch)) + +/* Given a valid sparc_opcode_arch_val, return non-zero if it's v9. */ +#define SPARC_OPCODE_ARCH_V9_P(arch) ((arch) >= SPARC_OPCODE_ARCH_V9) + +/* Table of cpu variants. */ + +struct sparc_opcode_arch { + const char *name; + /* Mask of sparc_opcode_arch_val's supported. + EG: For v7 this would be + (SPARC_OPCODE_ARCH_MASK (..._V6) | SPARC_OPCODE_ARCH_MASK (..._V7)). + These are short's because sparc_opcode.architecture is. */ + short supported; +}; + +extern const struct sparc_opcode_arch sparc_opcode_archs[]; + +/* Given architecture name, look up it's sparc_opcode_arch_val value. */ +extern enum sparc_opcode_arch_val sparc_opcode_lookup_arch + PARAMS ((const char *)); + +/* Return the bitmask of supported architectures for ARCH. */ +#define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported) + +/* Non-zero if ARCH1 conflicts with ARCH2. + IE: ARCH1 as a supported bit set that ARCH2 doesn't, and vice versa. */ +#define SPARC_OPCODE_CONFLICT_P(ARCH1, ARCH2) \ +(((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \ + != SPARC_OPCODE_SUPPORTED (ARCH1)) \ + && ((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \ + != SPARC_OPCODE_SUPPORTED (ARCH2))) + +/* Structure of an opcode table entry. */ + +struct sparc_opcode { + const char *name; + unsigned long match; /* Bits that must be set. */ + unsigned long lose; /* Bits that must not be set. */ + const char *args; + /* This was called "delayed" in versions before the flags. */ + char flags; + short architecture; /* Bitmask of sparc_opcode_arch_val's. */ +}; + +#define F_DELAYED 1 /* Delayed branch */ +#define F_ALIAS 2 /* Alias for a "real" instruction */ +#define F_UNBR 4 /* Unconditional branch */ +#define F_CONDBR 8 /* Conditional branch */ +#define F_JSR 16 /* Subroutine call */ +#define F_FLOAT 32 /* Floating point instruction (not a branch) */ +#define F_FBR 64 /* Floating point branch */ +/* FIXME: Add F_ANACHRONISTIC flag for v9. */ + +/* + +All sparc opcodes are 32 bits, except for the `set' instruction (really a +macro), which is 64 bits. It is handled as a special case. + +The match component is a mask saying which bits must match a particular +opcode in order for an instruction to be an instance of that opcode. + +The args component is a string containing one character for each operand of the +instruction. + +Kinds of operands: + # Number used by optimizer. It is ignored. + 1 rs1 register. + 2 rs2 register. + d rd register. + e frs1 floating point register. + v frs1 floating point register (double/even). + V frs1 floating point register (quad/multiple of 4). + f frs2 floating point register. + B frs2 floating point register (double/even). + R frs2 floating point register (quad/multiple of 4). + g frsd floating point register. + H frsd floating point register (double/even). + J frsd floating point register (quad/multiple of 4). + b crs1 coprocessor register + c crs2 coprocessor register + D crsd coprocessor register + m alternate space register (asr) in rd + M alternate space register (asr) in rs1 + h 22 high bits. + X 5 bit unsigned immediate + Y 6 bit unsigned immediate + K MEMBAR mask (7 bits). (v9) + j 10 bit Immediate. (v9) + I 11 bit Immediate. (v9) + i 13 bit Immediate. + n 22 bit immediate. + k 2+14 bit PC relative immediate. (v9) + G 19 bit PC relative immediate. (v9) + l 22 bit PC relative immediate. + L 30 bit PC relative immediate. + a Annul. The annul bit is set. + A Alternate address space. Stored as 8 bits. + C Coprocessor state register. + F floating point state register. + p Processor state register. + N Branch predict clear ",pn" (v9) + T Branch predict set ",pt" (v9) + z %icc. (v9) + Z %xcc. (v9) + q Floating point queue. + r Single register that is both rs1 and rd. + O Single register that is both rs2 and rd. + Q Coprocessor queue. + S Special case. + t Trap base register. + w Window invalid mask register. + y Y register. + u sparclet coprocessor registers in rd position + U sparclet coprocessor registers in rs1 position + E %ccr. (v9) + s %fprs. (v9) + P %pc. (v9) + W %tick. (v9) + o %asi. (v9) + 6 %fcc0. (v9) + 7 %fcc1. (v9) + 8 %fcc2. (v9) + 9 %fcc3. (v9) + ! Privileged Register in rd (v9) + ? Privileged Register in rs1 (v9) + * Prefetch function constant. (v9) + x OPF field (v9 impdep). + 0 32/64 bit immediate for set or setx (v9) insns + _ Ancillary state register in rd (v9a) + / Ancillary state register in rs1 (v9a) + +The following chars are unused: (note: ,[] are used as punctuation) +[345] + +*/ + +#define OP2(x) (((x)&0x7) << 22) /* op2 field of format2 insns */ +#define OP3(x) (((x)&0x3f) << 19) /* op3 field of format3 insns */ +#define OP(x) ((unsigned)((x)&0x3) << 30) /* op field of all insns */ +#define OPF(x) (((x)&0x1ff) << 5) /* opf field of float insns */ +#define OPF_LOW5(x) OPF((x)&0x1f) /* v9 */ +#define F3F(x, y, z) (OP(x) | OP3(y) | OPF(z)) /* format3 float insns */ +#define F3I(x) (((x)&0x1) << 13) /* immediate field of format 3 insns */ +#define F2(x, y) (OP(x) | OP2(y)) /* format 2 insns */ +#define F3(x, y, z) (OP(x) | OP3(y) | F3I(z)) /* format3 insns */ +#define F1(x) (OP(x)) +#define DISP30(x) ((x)&0x3fffffff) +#define ASI(x) (((x)&0xff) << 5) /* asi field of format3 insns */ +#define RS2(x) ((x)&0x1f) /* rs2 field */ +#define SIMM13(x) ((x)&0x1fff) /* simm13 field */ +#define RD(x) (((x)&0x1f) << 25) /* destination register field */ +#define RS1(x) (((x)&0x1f) << 14) /* rs1 field */ +#define ASI_RS2(x) (SIMM13(x)) +#define MEMBAR(x) ((x)&0x7f) +#define SLCPOP(x) (((x)&0x7f) << 6) /* sparclet cpop */ + +#define ANNUL (1<<29) +#define BPRED (1<<19) /* v9 */ +#define IMMED F3I(1) +#define RD_G0 RD(~0) +#define RS1_G0 RS1(~0) +#define RS2_G0 RS2(~0) + +extern const struct sparc_opcode sparc_opcodes[]; +extern const int sparc_num_opcodes; + +extern int sparc_encode_asi PARAMS ((const char *)); +extern const char *sparc_decode_asi PARAMS ((int)); +extern int sparc_encode_membar PARAMS ((const char *)); +extern const char *sparc_decode_membar PARAMS ((int)); +extern int sparc_encode_prefetch PARAMS ((const char *)); +extern const char *sparc_decode_prefetch PARAMS ((int)); +extern int sparc_encode_sparclet_cpreg PARAMS ((const char *)); +extern const char *sparc_decode_sparclet_cpreg PARAMS ((int)); + +/* + * Local Variables: + * fill-column: 131 + * comment-column: 0 + * End: + */ + +/* end of sparc.h */ diff --git a/contrib/binutils/ld/emulparams/elf32_sparc.sh b/contrib/binutils/ld/emulparams/elf32_sparc.sh new file mode 100644 index 0000000..3a1a7f2 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32_sparc.sh @@ -0,0 +1,10 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-sparc" +TEXT_START_ADDR=0x10000 +MAXPAGESIZE=0x10000 +NONPAGED_TEXT_START_ADDR=0x10000 +ARCH=sparc +MACHINE= +TEMPLATE_NAME=elf32 +DATA_PLT= +GENERATE_SHLIB_SCRIPT=yes diff --git a/contrib/binutils/ld/emulparams/elf64_sparc.sh b/contrib/binutils/ld/emulparams/elf64_sparc.sh new file mode 100644 index 0000000..d0fbdfd --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf64_sparc.sh @@ -0,0 +1,12 @@ +SCRIPT_NAME=elf +ELFSIZE=64 +TEMPLATE_NAME=elf32 +OUTPUT_FORMAT="elf64-sparc" +TEXT_START_ADDR=0x100000 +MAXPAGESIZE=0x100000 +NONPAGED_TEXT_START_ADDR=0x100000 +ARCH="sparc:v9" +MACHINE= +DATA_PLT= +GENERATE_SHLIB_SCRIPT=yes +NOP=0x01000000 diff --git a/contrib/binutils/opcodes/sparc-dis.c b/contrib/binutils/opcodes/sparc-dis.c new file mode 100644 index 0000000..0f5a091 --- /dev/null +++ b/contrib/binutils/opcodes/sparc-dis.c @@ -0,0 +1,961 @@ +/* Print SPARC instructions. + Copyright (C) 1989, 91-94, 1995, 1996, 1997 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> + +#include "ansidecl.h" +#include "sysdep.h" +#include "opcode/sparc.h" +#include "dis-asm.h" +#include "libiberty.h" + +/* Bitmask of v9 architectures. */ +#define MASK_V9 ((1 << SPARC_OPCODE_ARCH_V9) \ + | (1 << SPARC_OPCODE_ARCH_V9A)) +/* 1 if INSN is for v9 only. */ +#define V9_ONLY_P(insn) (! ((insn)->architecture & ~MASK_V9)) +/* 1 if INSN is for v9. */ +#define V9_P(insn) (((insn)->architecture & MASK_V9) != 0) + +/* The sorted opcode table. */ +static const struct sparc_opcode **sorted_opcodes; + +/* For faster lookup, after insns are sorted they are hashed. */ +/* ??? I think there is room for even more improvement. */ + +#define HASH_SIZE 256 +/* It is important that we only look at insn code bits as that is how the + opcode table is hashed. OPCODE_BITS is a table of valid bits for each + of the main types (0,1,2,3). */ +static int opcode_bits[4] = { 0x01c00000, 0x0, 0x01f80000, 0x01f80000 }; +#define HASH_INSN(INSN) \ + ((((INSN) >> 24) & 0xc0) | (((INSN) & opcode_bits[((INSN) >> 30) & 3]) >> 19)) +struct opcode_hash { + struct opcode_hash *next; + const struct sparc_opcode *opcode; +}; +static struct opcode_hash *opcode_hash_table[HASH_SIZE]; + +static void build_hash_table + PARAMS ((const struct sparc_opcode **, struct opcode_hash **, int)); +static int is_delayed_branch PARAMS ((unsigned long)); +static int compare_opcodes PARAMS ((const PTR, const PTR)); +static int compute_arch_mask PARAMS ((unsigned long)); + +/* Sign-extend a value which is N bits long. */ +#define SEX(value, bits) \ + ((((int)(value)) << ((8 * sizeof (int)) - bits)) \ + >> ((8 * sizeof (int)) - bits) ) + +static char *reg_names[] = +{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39", + "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47", + "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55", + "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63", +/* psr, wim, tbr, fpsr, cpsr are v8 only. */ + "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr" +}; + +#define freg_names (®_names[4 * 8]) + +/* These are ordered according to there register number in + rdpr and wrpr insns. */ +static char *v9_priv_reg_names[] = +{ + "tpc", "tnpc", "tstate", "tt", "tick", "tba", "pstate", "tl", + "pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin", + "wstate", "fq" + /* "ver" - special cased */ +}; + +/* These are ordered according to there register number in + rd and wr insns (-16). */ +static char *v9a_asr_reg_names[] = +{ + "pcr", "pic", "dcr", "gsr", "set_softint", "clear_softint", + "softint", "tick_cmpr" +}; + +/* Macros used to extract instruction fields. Not all fields have + macros defined here, only those which are actually used. */ + +#define X_RD(i) (((i) >> 25) & 0x1f) +#define X_RS1(i) (((i) >> 14) & 0x1f) +#define X_LDST_I(i) (((i) >> 13) & 1) +#define X_ASI(i) (((i) >> 5) & 0xff) +#define X_RS2(i) (((i) >> 0) & 0x1f) +#define X_IMM(i,n) (((i) >> 0) & ((1 << (n)) - 1)) +#define X_SIMM(i,n) SEX (X_IMM ((i), (n)), (n)) +#define X_DISP22(i) (((i) >> 0) & 0x3fffff) +#define X_IMM22(i) X_DISP22 (i) +#define X_DISP30(i) (((i) >> 0) & 0x3fffffff) + +/* These are for v9. */ +#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff)) +#define X_DISP19(i) (((i) >> 0) & 0x7ffff) +#define X_MEMBAR(i) ((i) & 0x7f) + +/* Here is the union which was used to extract instruction fields + before the shift and mask macros were written. + + union sparc_insn + { + unsigned long int code; + struct + { + unsigned int anop:2; + #define op ldst.anop + unsigned int anrd:5; + #define rd ldst.anrd + unsigned int op3:6; + unsigned int anrs1:5; + #define rs1 ldst.anrs1 + unsigned int i:1; + unsigned int anasi:8; + #define asi ldst.anasi + unsigned int anrs2:5; + #define rs2 ldst.anrs2 + #define shcnt rs2 + } ldst; + struct + { + unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1; + unsigned int IMM13:13; + #define imm13 IMM13.IMM13 + } IMM13; + struct + { + unsigned int anop:2; + unsigned int a:1; + unsigned int cond:4; + unsigned int op2:3; + unsigned int DISP22:22; + #define disp22 branch.DISP22 + #define imm22 disp22 + } branch; + struct + { + unsigned int anop:2; + unsigned int a:1; + unsigned int z:1; + unsigned int rcond:3; + unsigned int op2:3; + unsigned int DISP16HI:2; + unsigned int p:1; + unsigned int _rs1:5; + unsigned int DISP16LO:14; + } branch16; + struct + { + unsigned int anop:2; + unsigned int adisp30:30; + #define disp30 call.adisp30 + } call; + }; + + */ + +/* Nonzero if INSN is the opcode for a delayed branch. */ +static int +is_delayed_branch (insn) + unsigned long insn; +{ + struct opcode_hash *op; + + for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next) + { + CONST struct sparc_opcode *opcode = op->opcode; + if ((opcode->match & insn) == opcode->match + && (opcode->lose & insn) == 0) + return (opcode->flags & F_DELAYED); + } + return 0; +} + +/* extern void qsort (); */ + +/* Records current mask of SPARC_OPCODE_ARCH_FOO values, used to pass value + to compare_opcodes. */ +static unsigned int current_arch_mask; + +/* Print one instruction from MEMADDR on INFO->STREAM. + + We suffix the instruction with a comment that gives the absolute + address involved, as well as its symbolic form, if the instruction + is preceded by a findable `sethi' and it either adds an immediate + displacement to that register, or it is an `add' or `or' instruction + on that register. */ + +int +print_insn_sparc (memaddr, info) + bfd_vma memaddr; + disassemble_info *info; +{ + FILE *stream = info->stream; + bfd_byte buffer[4]; + unsigned long insn; + register struct opcode_hash *op; + /* Nonzero of opcode table has been initialized. */ + static int opcodes_initialized = 0; + /* bfd mach number of last call. */ + static unsigned long current_mach = 0; + + if (!opcodes_initialized + || info->mach != current_mach) + { + int i; + + current_arch_mask = compute_arch_mask (info->mach); + + if (!opcodes_initialized) + sorted_opcodes = (const struct sparc_opcode **) + xmalloc (sparc_num_opcodes * sizeof (struct sparc_opcode *)); + /* Reset the sorted table so we can resort it. */ + for (i = 0; i < sparc_num_opcodes; ++i) + sorted_opcodes[i] = &sparc_opcodes[i]; + qsort ((char *) sorted_opcodes, sparc_num_opcodes, + sizeof (sorted_opcodes[0]), compare_opcodes); + + build_hash_table (sorted_opcodes, opcode_hash_table, sparc_num_opcodes); + current_mach = info->mach; + opcodes_initialized = 1; + } + + { + int status = + (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + } + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb32 (buffer); + else + insn = bfd_getl32 (buffer); + + info->insn_info_valid = 1; /* We do return this info */ + info->insn_type = dis_nonbranch; /* Assume non branch insn */ + info->branch_delay_insns = 0; /* Assume no delay */ + info->target = 0; /* Assume no target known */ + + for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next) + { + CONST struct sparc_opcode *opcode = op->opcode; + + /* If the insn isn't supported by the current architecture, skip it. */ + if (! (opcode->architecture & current_arch_mask)) + continue; + + if ((opcode->match & insn) == opcode->match + && (opcode->lose & insn) == 0) + { + /* Nonzero means that we have found an instruction which has + the effect of adding or or'ing the imm13 field to rs1. */ + int imm_added_to_rs1 = 0; + + /* Nonzero means that we have found a plus sign in the args + field of the opcode table. */ + int found_plus = 0; + + /* Nonzero means we have an annulled branch. */ + int is_annulled = 0; + + /* Do we have an `add' or `or' instruction combining an + immediate with rs1? */ + if (opcode->match == 0x80102000 || opcode->match == 0x80002000) + /* (or) (add) */ + imm_added_to_rs1 = 1; + + if (X_RS1 (insn) != X_RD (insn) + && strchr (opcode->args, 'r') != 0) + /* Can't do simple format if source and dest are different. */ + continue; + if (X_RS2 (insn) != X_RD (insn) + && strchr (opcode->args, 'O') != 0) + /* Can't do simple format if source and dest are different. */ + continue; + + (*info->fprintf_func) (stream, opcode->name); + + { + register CONST char *s; + + if (opcode->args[0] != ',') + (*info->fprintf_func) (stream, " "); + for (s = opcode->args; *s != '\0'; ++s) + { + while (*s == ',') + { + (*info->fprintf_func) (stream, ","); + ++s; + switch (*s) { + case 'a': + (*info->fprintf_func) (stream, "a"); + is_annulled = 1; + ++s; + continue; + case 'N': + (*info->fprintf_func) (stream, "pn"); + ++s; + continue; + + case 'T': + (*info->fprintf_func) (stream, "pt"); + ++s; + continue; + + default: + break; + } /* switch on arg */ + } /* while there are comma started args */ + + (*info->fprintf_func) (stream, " "); + + switch (*s) + { + case '+': + found_plus = 1; + + /* note fall-through */ + default: + (*info->fprintf_func) (stream, "%c", *s); + break; + + case '#': + (*info->fprintf_func) (stream, "0"); + break; + +#define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n]) + case '1': + case 'r': + reg (X_RS1 (insn)); + break; + + case '2': + case 'O': + reg (X_RS2 (insn)); + break; + + case 'd': + reg (X_RD (insn)); + break; +#undef reg + +#define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n]) +#define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)]) + case 'e': + freg (X_RS1 (insn)); + break; + case 'v': /* double/even */ + case 'V': /* quad/multiple of 4 */ + fregx (X_RS1 (insn)); + break; + + case 'f': + freg (X_RS2 (insn)); + break; + case 'B': /* double/even */ + case 'R': /* quad/multiple of 4 */ + fregx (X_RS2 (insn)); + break; + + case 'g': + freg (X_RD (insn)); + break; + case 'H': /* double/even */ + case 'J': /* quad/multiple of 4 */ + fregx (X_RD (insn)); + break; +#undef freg +#undef fregx + +#define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n)) + case 'b': + creg (X_RS1 (insn)); + break; + + case 'c': + creg (X_RS2 (insn)); + break; + + case 'D': + creg (X_RD (insn)); + break; +#undef creg + + case 'h': + (*info->fprintf_func) (stream, "%%hi(%#x)", + (0xFFFFFFFF + & ((int) X_IMM22 (insn) << 10))); + break; + + case 'i': /* 13 bit immediate */ + case 'I': /* 11 bit immediate */ + case 'j': /* 10 bit immediate */ + { + int imm; + + if (*s == 'i') + imm = X_SIMM (insn, 13); + else if (*s == 'I') + imm = X_SIMM (insn, 11); + else + imm = X_SIMM (insn, 10); + + /* Check to see whether we have a 1+i, and take + note of that fact. + + Note: because of the way we sort the table, + we will be matching 1+i rather than i+1, + so it is OK to assume that i is after +, + not before it. */ + if (found_plus) + imm_added_to_rs1 = 1; + + if (imm <= 9) + (*info->fprintf_func) (stream, "%d", imm); + else + (*info->fprintf_func) (stream, "%#x", imm); + } + break; + + case 'X': /* 5 bit unsigned immediate */ + case 'Y': /* 6 bit unsigned immediate */ + { + int imm = X_IMM (insn, *s == 'X' ? 5 : 6); + + if (imm <= 9) + (info->fprintf_func) (stream, "%d", imm); + else + (info->fprintf_func) (stream, "%#x", (unsigned) imm); + } + break; + + case 'K': + { + int mask = X_MEMBAR (insn); + int bit = 0x40, printed_one = 0; + const char *name; + + if (mask == 0) + (info->fprintf_func) (stream, "0"); + else + while (bit) + { + if (mask & bit) + { + if (printed_one) + (info->fprintf_func) (stream, "|"); + name = sparc_decode_membar (bit); + (info->fprintf_func) (stream, "%s", name); + printed_one = 1; + } + bit >>= 1; + } + break; + } + + case 'k': + info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'G': + info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4; + (*info->print_address_func) (info->target, info); + break; + + case '6': + case '7': + case '8': + case '9': + (*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0'); + break; + + case 'z': + (*info->fprintf_func) (stream, "%%icc"); + break; + + case 'Z': + (*info->fprintf_func) (stream, "%%xcc"); + break; + + case 'E': + (*info->fprintf_func) (stream, "%%ccr"); + break; + + case 's': + (*info->fprintf_func) (stream, "%%fprs"); + break; + + case 'o': + (*info->fprintf_func) (stream, "%%asi"); + break; + + case 'W': + (*info->fprintf_func) (stream, "%%tick"); + break; + + case 'P': + (*info->fprintf_func) (stream, "%%pc"); + break; + + case '?': + if (X_RS1 (insn) == 31) + (*info->fprintf_func) (stream, "%%ver"); + else if ((unsigned) X_RS1 (insn) < 16) + (*info->fprintf_func) (stream, "%%%s", + v9_priv_reg_names[X_RS1 (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '!': + if ((unsigned) X_RD (insn) < 15) + (*info->fprintf_func) (stream, "%%%s", + v9_priv_reg_names[X_RD (insn)]); + else + (*info->fprintf_func) (stream, "%%reserved"); + break; + + case '/': + if (X_RS1 (insn) < 16 || X_RS1 (insn) > 23) + (*info->fprintf_func) (stream, "%%reserved"); + else + (*info->fprintf_func) (stream, "%%%s", + v9a_asr_reg_names[X_RS1 (insn)-16]); + break; + + case '_': + if (X_RD (insn) < 16 || X_RD (insn) > 23) + (*info->fprintf_func) (stream, "%%reserved"); + else + (*info->fprintf_func) (stream, "%%%s", + v9a_asr_reg_names[X_RD (insn)-16]); + break; + + case '*': + { + const char *name = sparc_decode_prefetch (X_RD (insn)); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "%d", X_RD (insn)); + break; + } + + case 'M': + (*info->fprintf_func) (stream, "%%asr%d", X_RS1 (insn)); + break; + + case 'm': + (*info->fprintf_func) (stream, "%%asr%d", X_RD (insn)); + break; + + case 'L': + info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'n': + (*info->fprintf_func) + (stream, "%#x", SEX (X_DISP22 (insn), 22)); + break; + + case 'l': + info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4; + (*info->print_address_func) (info->target, info); + break; + + case 'A': + { + const char *name = sparc_decode_asi (X_ASI (insn)); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "(%d)", X_ASI (insn)); + break; + } + + case 'C': + (*info->fprintf_func) (stream, "%%csr"); + break; + + case 'F': + (*info->fprintf_func) (stream, "%%fsr"); + break; + + case 'p': + (*info->fprintf_func) (stream, "%%psr"); + break; + + case 'q': + (*info->fprintf_func) (stream, "%%fq"); + break; + + case 'Q': + (*info->fprintf_func) (stream, "%%cq"); + break; + + case 't': + (*info->fprintf_func) (stream, "%%tbr"); + break; + + case 'w': + (*info->fprintf_func) (stream, "%%wim"); + break; + + case 'x': + (*info->fprintf_func) (stream, "%d", + ((X_LDST_I (insn) << 8) + + X_ASI (insn))); + break; + + case 'y': + (*info->fprintf_func) (stream, "%%y"); + break; + + case 'u': + case 'U': + { + int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn); + const char *name = sparc_decode_sparclet_cpreg (val); + + if (name) + (*info->fprintf_func) (stream, "%s", name); + else + (*info->fprintf_func) (stream, "%%cpreg(%d)", val); + break; + } + } + } + } + + /* If we are adding or or'ing something to rs1, then + check to see whether the previous instruction was + a sethi to the same register as in the sethi. + If so, attempt to print the result of the add or + or (in this context add and or do the same thing) + and its symbolic value. */ + if (imm_added_to_rs1) + { + unsigned long prev_insn; + int errcode; + + errcode = + (*info->read_memory_func) + (memaddr - 4, buffer, sizeof (buffer), info); + if (info->endian == BFD_ENDIAN_BIG) + prev_insn = bfd_getb32 (buffer); + else + prev_insn = bfd_getl32 (buffer); + + if (errcode == 0) + { + /* If it is a delayed branch, we need to look at the + instruction before the delayed branch. This handles + sequences such as + + sethi %o1, %hi(_foo), %o1 + call _printf + or %o1, %lo(_foo), %o1 + */ + + if (is_delayed_branch (prev_insn)) + { + errcode = (*info->read_memory_func) + (memaddr - 8, buffer, sizeof (buffer), info); + if (info->endian == BFD_ENDIAN_BIG) + prev_insn = bfd_getb32 (buffer); + else + prev_insn = bfd_getl32 (buffer); + } + } + + /* If there was a problem reading memory, then assume + the previous instruction was not sethi. */ + if (errcode == 0) + { + /* Is it sethi to the same register? */ + if ((prev_insn & 0xc1c00000) == 0x01000000 + && X_RD (prev_insn) == X_RS1 (insn)) + { + (*info->fprintf_func) (stream, "\t! "); + info->target = + (0xFFFFFFFF & (int) X_IMM22 (prev_insn) << 10) + | X_SIMM (insn, 13); + (*info->print_address_func) (info->target, info); + info->insn_type = dis_dref; + info->data_size = 4; /* FIXME!!! */ + } + } + } + + if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR)) + { + /* FIXME -- check is_annulled flag */ + if (opcode->flags & F_UNBR) + info->insn_type = dis_branch; + if (opcode->flags & F_CONDBR) + info->insn_type = dis_condbranch; + if (opcode->flags & F_JSR) + info->insn_type = dis_jsr; + if (opcode->flags & F_DELAYED) + info->branch_delay_insns = 1; + } + + return sizeof (buffer); + } + } + + info->insn_type = dis_noninsn; /* Mark as non-valid instruction */ + (*info->fprintf_func) (stream, "unknown"); + return sizeof (buffer); +} + +/* Given BFD mach number, return a mask of SPARC_OPCODE_ARCH_FOO values. */ + +static int +compute_arch_mask (mach) + unsigned long mach; +{ + switch (mach) + { + case 0 : + case bfd_mach_sparc : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8); + case bfd_mach_sparc_sparclet : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET); + case bfd_mach_sparc_sparclite : + /* sparclites insns are recognized by default (because that's how + they've always been treated, for better or worse). Kludge this by + indicating generic v8 is also selected. */ + return (SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE) + | SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8)); + case bfd_mach_sparc_v8plus : + case bfd_mach_sparc_v9 : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9); + case bfd_mach_sparc_v8plusa : + case bfd_mach_sparc_v9a : + return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A); + } + abort (); +} + +/* Compare opcodes A and B. */ + +static int +compare_opcodes (a, b) + const PTR a; + const PTR b; +{ + struct sparc_opcode *op0 = * (struct sparc_opcode **) a; + struct sparc_opcode *op1 = * (struct sparc_opcode **) b; + unsigned long int match0 = op0->match, match1 = op1->match; + unsigned long int lose0 = op0->lose, lose1 = op1->lose; + register unsigned int i; + + /* If one (and only one) insn isn't supported by the current architecture, + prefer the one that is. If neither are supported, but they're both for + the same architecture, continue processing. Otherwise (both unsupported + and for different architectures), prefer lower numbered arch's (fudged + by comparing the bitmasks). */ + if (op0->architecture & current_arch_mask) + { + if (! (op1->architecture & current_arch_mask)) + return -1; + } + else + { + if (op1->architecture & current_arch_mask) + return 1; + else if (op0->architecture != op1->architecture) + return op0->architecture - op1->architecture; + } + + /* If a bit is set in both match and lose, there is something + wrong with the opcode table. */ + if (match0 & lose0) + { + fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", + op0->name, match0, lose0); + op0->lose &= ~op0->match; + lose0 = op0->lose; + } + + if (match1 & lose1) + { + fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", + op1->name, match1, lose1); + op1->lose &= ~op1->match; + lose1 = op1->lose; + } + + /* Because the bits that are variable in one opcode are constant in + another, it is important to order the opcodes in the right order. */ + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (match0 & x) != 0; + int x1 = (match1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (lose0 & x) != 0; + int x1 = (lose1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + /* They are functionally equal. So as long as the opcode table is + valid, we can put whichever one first we want, on aesthetic grounds. */ + + /* Our first aesthetic ground is that aliases defer to real insns. */ + { + int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS); + if (alias_diff != 0) + /* Put the one that isn't an alias first. */ + return alias_diff; + } + + /* Except for aliases, two "identical" instructions had + better have the same opcode. This is a sanity check on the table. */ + i = strcmp (op0->name, op1->name); + if (i) + { + if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */ + return i; + else + fprintf (stderr, + "Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n", + op0->name, op1->name); + } + + /* Fewer arguments are preferred. */ + { + int length_diff = strlen (op0->args) - strlen (op1->args); + if (length_diff != 0) + /* Put the one with fewer arguments first. */ + return length_diff; + } + + /* Put 1+i before i+1. */ + { + char *p0 = (char *) strchr (op0->args, '+'); + char *p1 = (char *) strchr (op1->args, '+'); + + if (p0 && p1) + { + /* There is a plus in both operands. Note that a plus + sign cannot be the first character in args, + so the following [-1]'s are valid. */ + if (p0[-1] == 'i' && p1[1] == 'i') + /* op0 is i+1 and op1 is 1+i, so op1 goes first. */ + return 1; + if (p0[1] == 'i' && p1[-1] == 'i') + /* op0 is 1+i and op1 is i+1, so op0 goes first. */ + return -1; + } + } + + /* Put 1,i before i,1. */ + { + int i0 = strncmp (op0->args, "i,1", 3) == 0; + int i1 = strncmp (op1->args, "i,1", 3) == 0; + + if (i0 ^ i1) + return i0 - i1; + } + + /* They are, as far as we can tell, identical. + Since qsort may have rearranged the table partially, there is + no way to tell which one was first in the opcode table as + written, so just say there are equal. */ + /* ??? This is no longer true now that we sort a vector of pointers, + not the table itself. */ + return 0; +} + +/* Build a hash table from the opcode table. + OPCODE_TABLE is a sorted list of pointers into the opcode table. */ + +static void +build_hash_table (opcode_table, hash_table, num_opcodes) + const struct sparc_opcode **opcode_table; + struct opcode_hash **hash_table; + int num_opcodes; +{ + register int i; + int hash_count[HASH_SIZE]; + static struct opcode_hash *hash_buf = NULL; + + /* Start at the end of the table and work backwards so that each + chain is sorted. */ + + memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0])); + memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0])); + if (hash_buf != NULL) + free (hash_buf); + hash_buf = (struct opcode_hash *) xmalloc (sizeof (struct opcode_hash) * num_opcodes); + for (i = num_opcodes - 1; i >= 0; --i) + { + register int hash = HASH_INSN (opcode_table[i]->match); + register struct opcode_hash *h = &hash_buf[i]; + h->next = hash_table[hash]; + h->opcode = opcode_table[i]; + hash_table[hash] = h; + ++hash_count[hash]; + } + +#if 0 /* for debugging */ + { + int min_count = num_opcodes, max_count = 0; + int total; + + for (i = 0; i < HASH_SIZE; ++i) + { + if (hash_count[i] < min_count) + min_count = hash_count[i]; + if (hash_count[i] > max_count) + max_count = hash_count[i]; + total += hash_count[i]; + } + + printf ("Opcode hash table stats: min %d, max %d, ave %f\n", + min_count, max_count, (double) total / HASH_SIZE); + } +#endif +} diff --git a/contrib/binutils/opcodes/sparc-opc.c b/contrib/binutils/opcodes/sparc-opc.c new file mode 100644 index 0000000..7f30766 --- /dev/null +++ b/contrib/binutils/opcodes/sparc-opc.c @@ -0,0 +1,1920 @@ +/* Table of opcodes for the sparc. + Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 1997 + Free Software Foundation, Inc. + +This file is part of the BFD library. + +BFD 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 2, or (at your option) any later +version. + +BFD is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this software; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* FIXME-someday: perhaps the ,a's and such should be embedded in the + instruction's name rather than the args. This would make gas faster, pinsn + slower, but would mess up some macros a bit. xoxorich. */ + +#include <stdio.h> +#include "ansidecl.h" +#include "opcode/sparc.h" + +/* Some defines to make life easy. */ +#define MASK_V6 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V6) +#define MASK_V7 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V7) +#define MASK_V8 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8) +#define MASK_SPARCLET SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET) +#define MASK_SPARCLITE SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE) +#define MASK_V9 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9) +#define MASK_V9A SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A) + +/* Bit masks of architectures supporting the insn. */ + +#define v6 (MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A) +/* v6 insns not supported on the sparclet */ +#define v6notlet (MASK_V6 | MASK_V7 | MASK_V8 \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A) +#define v7 (MASK_V7 | MASK_V8 | MASK_SPARCLET \ + | MASK_SPARCLITE | MASK_V9 | MASK_V9A) +/* Although not all insns are implemented in hardware, sparclite is defined + to be a superset of v8. Unimplemented insns trap and are then theoretically + implemented in software. + It's not clear that the same is true for sparclet, although the docs + suggest it is. Rather than complicating things, the sparclet assembler + recognizes all v8 insns. */ +#define v8 (MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE | MASK_V9 | MASK_V9A) +#define sparclet (MASK_SPARCLET) +#define sparclite (MASK_SPARCLITE) +#define v9 (MASK_V9 | MASK_V9A) +#define v9a (MASK_V9A) +/* v6 insns not supported by v9 */ +#define v6notv9 (MASK_V6 | MASK_V7 | MASK_V8 \ + | MASK_SPARCLET | MASK_SPARCLITE) +/* v9a instructions which would appear to be aliases to v9's impdep's + otherwise */ +#define v9notv9a (MASK_V9) + +/* Table of opcode architectures. + The order is defined in opcode/sparc.h. */ + +const struct sparc_opcode_arch sparc_opcode_archs[] = { + { "v6", MASK_V6 }, + { "v7", MASK_V6 | MASK_V7 }, + { "v8", MASK_V6 | MASK_V7 | MASK_V8 }, + { "sparclet", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET }, + { "sparclite", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLITE }, + /* ??? Don't some v8 priviledged insns conflict with v9? */ + { "v9", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 }, + /* v9 with ultrasparc additions */ + { "v9a", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A }, + { NULL, 0 } +}; + +/* Given NAME, return it's architecture entry. */ + +enum sparc_opcode_arch_val +sparc_opcode_lookup_arch (name) + const char *name; +{ + const struct sparc_opcode_arch *p; + + for (p = &sparc_opcode_archs[0]; p->name; ++p) + { + if (strcmp (name, p->name) == 0) + return (enum sparc_opcode_arch_val) (p - &sparc_opcode_archs[0]); + } + + return SPARC_OPCODE_ARCH_BAD; +} + +/* Branch condition field. */ +#define COND(x) (((x)&0xf)<<25) + +/* v9: Move (MOVcc and FMOVcc) condition field. */ +#define MCOND(x,i_or_f) ((((i_or_f)&1)<<18)|(((x)>>11)&(0xf<<14))) /* v9 */ + +/* v9: Move register (MOVRcc and FMOVRcc) condition field. */ +#define RCOND(x) (((x)&0x7)<<10) /* v9 */ + +#define CONDA (COND(0x8)) +#define CONDCC (COND(0xd)) +#define CONDCS (COND(0x5)) +#define CONDE (COND(0x1)) +#define CONDG (COND(0xa)) +#define CONDGE (COND(0xb)) +#define CONDGU (COND(0xc)) +#define CONDL (COND(0x3)) +#define CONDLE (COND(0x2)) +#define CONDLEU (COND(0x4)) +#define CONDN (COND(0x0)) +#define CONDNE (COND(0x9)) +#define CONDNEG (COND(0x6)) +#define CONDPOS (COND(0xe)) +#define CONDVC (COND(0xf)) +#define CONDVS (COND(0x7)) + +#define CONDNZ CONDNE +#define CONDZ CONDE +#define CONDGEU CONDCC +#define CONDLU CONDCS + +#define FCONDA (COND(0x8)) +#define FCONDE (COND(0x9)) +#define FCONDG (COND(0x6)) +#define FCONDGE (COND(0xb)) +#define FCONDL (COND(0x4)) +#define FCONDLE (COND(0xd)) +#define FCONDLG (COND(0x2)) +#define FCONDN (COND(0x0)) +#define FCONDNE (COND(0x1)) +#define FCONDO (COND(0xf)) +#define FCONDU (COND(0x7)) +#define FCONDUE (COND(0xa)) +#define FCONDUG (COND(0x5)) +#define FCONDUGE (COND(0xc)) +#define FCONDUL (COND(0x3)) +#define FCONDULE (COND(0xe)) + +#define FCONDNZ FCONDNE +#define FCONDZ FCONDE + +#define ICC (0) /* v9 */ +#define XCC (1<<12) /* v9 */ +#define FCC(x) (((x)&0x3)<<11) /* v9 */ +#define FBFCC(x) (((x)&0x3)<<20) /* v9 */ + +/* The order of the opcodes in the table is significant: + + * The assembler requires that all instances of the same mnemonic must + be consecutive. If they aren't, the assembler will bomb at runtime. + + * The disassembler should not care about the order of the opcodes. + +*/ + +/* Entries for commutative arithmetic operations. */ +/* ??? More entries can make use of this. */ +#define COMMUTEOP(opcode, op3, arch_mask) \ +{ opcode, F3(2, op3, 0), F3(~2, ~op3, ~0)|ASI(~0), "1,2,d", 0, arch_mask }, \ +{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "1,i,d", 0, arch_mask }, \ +{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "i,1,d", 0, arch_mask } + +const struct sparc_opcode sparc_opcodes[] = { + +{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 }, +{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ld [rs1+0],d */ +{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0), "[1+2],g", 0, v6 }, +{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0)|RS2_G0, "[1],g", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[1+i],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[i+1],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|RS1_G0, "[i],g", 0, v6 }, +{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ld [rs1+0],d */ + +{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RD(~0), "[1+2],F", 0, v6 }, +{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RS2_G0|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[1+i],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[i+1],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~0),"[i],F", 0, v6 }, +{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+0],d */ + +{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1],D", 0, v6notv9 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i],D", 0, v6notv9 }, +{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ld [rs1+0],d */ +{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0), "[1+2],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0)|RS2_G0, "[1],C", 0, v6notv9 }, /* ld [rs1+%g0],d */ +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[1+i],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[i+1],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|RS1_G0, "[i],C", 0, v6notv9 }, +{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|SIMM13(~0), "[1],C", 0, v6notv9 }, /* ld [rs1+0],d */ + +/* The v9 LDUW is the same as the old 'ld' opcode, it is not the same as the + 'ld' pseudo-op in v9. */ +{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", F_ALIAS, v9 }, /* ld [rs1+%g0],d */ +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", F_ALIAS, v9 }, +{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", F_ALIAS, v9 }, /* ld [rs1+0],d */ + +{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[1+i],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[i+1],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldd [rs1+0],d */ +{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI(~0), "[1+2],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI_RS2(~0), "[1],H", 0, v6 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[1+i],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[i+1],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|RS1_G0, "[i],H", 0, v6 }, +{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|SIMM13(~0), "[1],H", 0, v6 }, /* ldd [rs1+0],d */ + +{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI(~0), "[1+2],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI_RS2(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+%g0],d */ +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i],D", 0, v6notv9 }, +{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+0],d */ + +{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI(~0), "[1+2],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI_RS2(~0), "[1],J", 0, v9 }, /* ldd [rs1+%g0],d */ +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[1+i],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[i+1],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|RS1_G0, "[i],J", 0, v9 }, +{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|SIMM13(~0), "[1],J", 0, v9 }, /* ldd [rs1+0],d */ + +{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsb [rs1+%g0],d */ +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[1+i],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[i+1],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsb [rs1+0],d */ + +{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsh [rs1+%g0],d */ +{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[1+i],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[i+1],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsh [rs1+0],d */ + +{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldstub [rs1+%g0],d */ +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[1+i],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[i+1],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldstub [rs1+0],d */ + +{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI(~0), "[1+2],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldsw [rs1+%g0],d */ +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[1+i],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[i+1],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|RS1_G0, "[i],d", 0, v9 }, +{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldsw [rs1+0],d */ + +{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldub [rs1+%g0],d */ +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[1+i],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[i+1],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldub [rs1+0],d */ + +{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI(~0), "[1+2],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* lduh [rs1+%g0],d */ +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[1+i],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[i+1],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|RS1_G0, "[i],d", 0, v6 }, +{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* lduh [rs1+0],d */ + +{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI(~0), "[1+2],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldx [rs1+%g0],d */ +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[1+i],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[i+1],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|RS1_G0, "[i],d", 0, v9 }, +{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldx [rs1+0],d */ + +{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RD(~1), "[1+2],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RS2_G0|RD(~1), "[1],F", 0, v9 }, /* ld [rs1+%g0],d */ +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[1+i],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[i+1],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~1), "[i],F", 0, v9 }, +{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~1),"[1],F", 0, v9 }, /* ld [rs1+0],d */ + +{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", 0, v6 }, +{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lda [rs1+%g0],d */ +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ +{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2]A,g", 0, v9 }, +{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1]A,g", 0, v9 }, /* lda [rs1+%g0],d */ +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i]o,g", 0, v9 }, +{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1]o,g", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0), "[1+2]A,d", 0, v6 }, +{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldda [rs1+%g0],d */ +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[1+i]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[i+1]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0), "[1+2]A,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|RS2_G0, "[1]A,H", 0, v9 }, /* ldda [rs1+%g0],d */ +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i]o,H", 0, v9 }, +{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1]o,H", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0), "[1+2]A,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0)|RS2_G0, "[1]A,J", 0, v9 }, /* ldd [rs1+%g0],d */ +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[1+i]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[i+1]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|RS1_G0, "[i]o,J", 0, v9 }, +{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|SIMM13(~0), "[1]o,J", 0, v9 }, /* ldd [rs1+0],d */ + +{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0), "[1+2]A,d", 0, v6 }, +{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsba [rs1+%g0],d */ +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[1+i]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[i+1]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0), "[1+2]A,d", 0, v6 }, +{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsha [rs1+%g0],d */ +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[1+i]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[i+1]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0), "[1+2]A,d", 0, v6 }, +{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldstuba [rs1+%g0],d */ +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[1+i]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[i+1]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0), "[1+2]A,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */ +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[1+i]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[i+1]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0), "[1+2]A,d", 0, v6 }, +{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduba [rs1+%g0],d */ +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[1+i]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[i+1]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0), "[1+2]A,d", 0, v6 }, +{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduha [rs1+%g0],d */ +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[1+i]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[i+1]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", F_ALIAS, v9 }, /* lduwa === lda */ +{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", F_ALIAS, v9 }, /* lda [rs1+%g0],d */ +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", F_ALIAS, v9 }, +{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", F_ALIAS, v9 }, /* ld [rs1+0],d */ + +{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0), "[1+2]A,d", 0, v9 }, /* lduwa === lda */ +{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */ +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[1+i]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[i+1]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */ + +{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* st d,[rs1+0] */ +{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI(~0), "g,[1+2]", 0, v6 }, +{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* st d[rs1+%g0] */ +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[1+i]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[i+1]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|RS1_G0, "g,[i]", 0, v6 }, +{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* st d,[rs1+0] */ + +{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[1+i]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[i+1]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "D,[i]", 0, v6notv9 }, +{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+0] */ +{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI(~0), "C,[1+2]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI_RS2(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[1+i]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[i+1]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|RS1_G0, "C,[i]", 0, v6notv9 }, +{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|SIMM13(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+0] */ + +{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI(~0), "F,[1+2]", 0, v6 }, +{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI_RS2(~0), "F,[1]", 0, v6 }, /* st d,[rs1+%g0] */ +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[1+i]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[i+1]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|RS1_G0, "F,[i]", 0, v6 }, +{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|SIMM13(~0), "F,[1]", 0, v6 }, /* st d,[rs1+0] */ + +{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */ +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 }, +{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */ + +{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", 0, v6 }, +{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* sta d,[rs1+%g0] */ +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* st d,[rs1+0] */ + +{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0), "g,[1+2]A", 0, v9 }, +{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|RS2(~0), "g,[1]A", 0, v9 }, /* sta d,[rs1+%g0] */ +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[1+i]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[i+1]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "g,[i]o", 0, v9 }, +{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "g,[1]o", 0, v9 }, /* st d,[rs1+0] */ + +{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */ +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 }, +{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */ + +{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+%g0] */ +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+0] */ + +{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", 0, v6 }, +{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stba d,[rs1+%g0] */ +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stb d,[rs1+0] */ + +{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* std d,[rs1+0] */ + +{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "q,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "q,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ +{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI(~0), "H,[1+2]", 0, v6 }, +{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI_RS2(~0), "H,[1]", 0, v6 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[1+i]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[i+1]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|RS1_G0, "H,[i]", 0, v6 }, +{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|SIMM13(~0), "H,[1]", 0, v6 }, /* std d,[rs1+0] */ + +{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "Q,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "Q,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ +{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */ +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[1+i]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[i+1]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "D,[i]", 0, v6notv9 }, +{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+0] */ + +{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0), "d,[1+2]A", 0, v6 }, +{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stda d,[rs1+%g0] */ +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[1+i]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[i+1]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* std d,[rs1+0] */ +{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0), "H,[1+2]A", 0, v9 }, +{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|RS2(~0), "H,[1]A", 0, v9 }, /* stda d,[rs1+%g0] */ +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[1+i]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[i+1]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "H,[i]o", 0, v9 }, +{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "H,[1]o", 0, v9 }, /* std d,[rs1+0] */ + +{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", 0, v6 }, +{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+%g0] */ +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", 0, v6 }, +{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+0] */ + +{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", 0, v6 }, +{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stha ,[rs1+%g0] */ +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* sth d,[rs1+0] */ + +{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI(~0), "d,[1+2]", 0, v9 }, +{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI_RS2(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+%g0] */ +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[1+i]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[i+1]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RS1_G0, "d,[i]", 0, v9 }, +{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|SIMM13(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI(~0)|RD(~1), "F,[1+2]", 0, v9 }, +{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI_RS2(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+%g0] */ +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[1+i]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[i+1]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RS1_G0|RD(~1), "F,[i]", 0, v9 }, +{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|SIMM13(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0), "d,[1+2]A", 0, v9 }, +{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0)|RS2(~0), "d,[1]A", 0, v9 }, /* stxa d,[rs1+%g0] */ +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[1+i]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[i+1]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|RS1_G0, "d,[i]o", 0, v9 }, +{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stx d,[rs1+0] */ + +{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "J,[1+2]", 0, v9 }, +{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "J,[1]", 0, v9 }, /* stq [rs1+%g0] */ +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[1+i]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[i+1]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "J,[i]", 0, v9 }, +{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "J,[1]", 0, v9 }, /* stq [rs1+0] */ + +{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "J,[1+2]A", 0, v9 }, +{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "J,[1]A", 0, v9 }, /* stqa [rs1+%g0] */ +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[1+i]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[i+1]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "J,[i]o", 0, v9 }, +{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "J,[1]o", 0, v9 }, /* stqa [rs1+0] */ + +{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI(~0), "[1+2],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI_RS2(~0), "[1],d", 0, v7 }, /* swap [rs1+%g0],d */ +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[1+i],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[i+1],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|RS1_G0, "[i],d", 0, v7 }, +{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|SIMM13(~0), "[1],d", 0, v7 }, /* swap [rs1+0],d */ + +{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0), "[1+2]A,d", 0, v7 }, +{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0)|RS2(~0), "[1]A,d", 0, v7 }, /* swapa [rs1+%g0],d */ +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[1+i]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[i+1]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|RS1_G0, "[i]o,d", 0, v9 }, +{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* swap [rs1+0],d */ + +{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v6 }, /* restore %g0,%g0,%g0 */ +{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1), "1,i,d", 0, v6 }, +{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1)|RD_G0|RS1_G0|SIMM13(~0), "", 0, v6 }, /* restore %g0,0,%g0 */ + +{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* rett rs1+rs2 */ +{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1,%g0 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* rett rs1+X */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X */ +{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1+0 */ + +{ "save", F3(2, 0x3c, 0), F3(~2, ~0x3c, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "save", F3(2, 0x3c, 1), F3(~2, ~0x3c, ~1), "1,i,d", 0, v6 }, +{ "save", 0x81e00000, ~0x81e00000, "", F_ALIAS, v6 }, + +{ "ret", F3(2, 0x38, 1)|RS1(0x1f)|SIMM13(8), F3(~2, ~0x38, ~1)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %i7+8,%g0 */ +{ "retl", F3(2, 0x38, 1)|RS1(0x0f)|SIMM13(8), F3(~2, ~0x38, ~1)|RS1(~0x0f)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %o7+8,%g0 */ + +{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI(~0), "1+2,d", F_JSR|F_DELAYED, v6 }, +{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI_RS2(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|SIMM13(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,d */ +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "1+i,d", F_JSR|F_DELAYED, v6 }, +{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "i+1,d", F_JSR|F_DELAYED, v6 }, + +{ "done", F3(2, 0x3e, 0)|RD(0), F3(~2, ~0x3e, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "retry", F3(2, 0x3e, 0)|RD(1), F3(~2, ~0x3e, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "saved", F3(2, 0x31, 0)|RD(0), F3(~2, ~0x31, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "restored", F3(2, 0x31, 0)|RD(1), F3(~2, ~0x31, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 }, +{ "sir", F3(2, 0x30, 1)|RD(0xf), F3(~2, ~0x30, ~1)|RD(~0xf)|RS1_G0, "i", 0, v9 }, + +{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", 0, v8 }, +{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", 0, v8 }, /* flush rs1+%g0 */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", 0, v8 }, /* flush rs1+0 */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", 0, v8 }, /* flush %g0+i */ +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", 0, v8 }, +{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", 0, v8 }, + +/* IFLUSH was renamed to FLUSH in v8. */ +{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", F_ALIAS, v6 }, /* flush rs1+%g0 */ +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", F_ALIAS, v6 }, /* flush rs1+0 */ +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", F_ALIAS, v6 }, +{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", F_ALIAS, v6 }, + +{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI(~0), "1+2", 0, v9 }, +{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI_RS2(~0), "1", 0, v9 }, /* return rs1+%g0 */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|SIMM13(~0), "1", 0, v9 }, /* return rs1+0 */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RS1_G0, "i", 0, v9 }, /* return %g0+i */ +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "1+i", 0, v9 }, +{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "i+1", 0, v9 }, + +{ "flushw", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v9 }, + +{ "membar", F3(2, 0x28, 1)|RS1(0xf), F3(~2, ~0x28, ~1)|RD_G0|RS1(~0xf)|SIMM13(~127), "K", 0, v9 }, +{ "stbar", F3(2, 0x28, 0)|RS1(0xf), F3(~2, ~0x28, ~0)|RD_G0|RS1(~0xf)|SIMM13(~0), "", 0, v8 }, + +{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0), "[1+2],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0)|RS2_G0, "[1],*", 0, v9 }, /* prefetch [rs1+%g0],prefetch_fcn */ +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[1+i],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[i+1],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|RS1_G0, "[i],*", 0, v9 }, +{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|SIMM13(~0), "[1],*", 0, v9 }, /* prefetch [rs1+0],prefetch_fcn */ +{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0), "[1+2]A,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0)|RS2_G0, "[1]A,*", 0, v9 }, /* prefetcha [rs1+%g0],prefetch_fcn */ +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[1+i]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[i+1]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|RS1_G0, "[i]o,*", 0, v9 }, +{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|SIMM13(~0), "[1]o,*", 0, v9 }, /* prefetcha [rs1+0],d */ + +{ "sll", F3(2, 0x25, 0), F3(~2, ~0x25, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "sll", F3(2, 0x25, 1), F3(~2, ~0x25, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, +{ "sra", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "sra", F3(2, 0x27, 1), F3(~2, ~0x27, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, +{ "srl", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 }, +{ "srl", F3(2, 0x26, 1), F3(~2, ~0x26, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 }, + +{ "sllx", F3(2, 0x25, 0)|(1<<12), F3(~2, ~0x25, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "sllx", F3(2, 0x25, 1)|(1<<12), F3(~2, ~0x25, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, +{ "srax", F3(2, 0x27, 0)|(1<<12), F3(~2, ~0x27, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "srax", F3(2, 0x27, 1)|(1<<12), F3(~2, ~0x27, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, +{ "srlx", F3(2, 0x26, 0)|(1<<12), F3(~2, ~0x26, ~0)|(0x7f<<5), "1,2,d", 0, v9 }, +{ "srlx", F3(2, 0x26, 1)|(1<<12), F3(~2, ~0x26, ~1)|(0x3f<<6), "1,Y,d", 0, v9 }, + +{ "mulscc", F3(2, 0x24, 0), F3(~2, ~0x24, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "mulscc", F3(2, 0x24, 1), F3(~2, ~0x24, ~1), "1,i,d", 0, v6 }, + +{ "divscc", F3(2, 0x1d, 0), F3(~2, ~0x1d, ~0)|ASI(~0), "1,2,d", 0, sparclite }, +{ "divscc", F3(2, 0x1d, 1), F3(~2, ~0x1d, ~1), "1,i,d", 0, sparclite }, + +{ "scan", F3(2, 0x2c, 0), F3(~2, ~0x2c, ~0)|ASI(~0), "1,2,d", 0, sparclet|sparclite }, +{ "scan", F3(2, 0x2c, 1), F3(~2, ~0x2c, ~1), "1,i,d", 0, sparclet|sparclite }, + +{ "popc", F3(2, 0x2e, 0), F3(~2, ~0x2e, ~0)|RS2_G0|ASI(~0),"2,d", 0, v9 }, +{ "popc", F3(2, 0x2e, 1), F3(~2, ~0x2e, ~1)|RS2_G0, "i,d", 0, v9 }, + +{ "clr", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "d", F_ALIAS, v6 }, /* or %g0,%g0,d */ +{ "clr", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0|SIMM13(~0), "d", F_ALIAS, v6 }, /* or %g0,0,d */ +{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+%g0] */ +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+0] */ + +{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+%g0] */ +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+0] */ + +{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+%g0] */ +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 }, +{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+0] */ + +{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+%g0] */ +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[1+i]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[i+1]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v9 }, +{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+0] */ + +{ "orcc", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "1,i,d", 0, v6 }, +{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "i,1,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "orncc", F3(2, 0x16, 0), F3(~2, ~0x16, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "1,i,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "orn", F3(2, 0x06, 0), F3(~2, ~0x06, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "1,i,d", 0, v6 }, + +{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|ASI_RS2(~0), "1", 0, v6 }, /* orcc rs1, %g0, %g0 */ +{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|RS1_G0|ASI(~0), "2", 0, v6 }, /* orcc %g0, rs2, %g0 */ +{ "tst", F3(2, 0x12, 1), F3(~2, ~0x12, ~1)|RD_G0|SIMM13(~0), "1", 0, v6 }, /* orcc rs1, 0, %g0 */ + +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", 0, v8 }, /* wr r,r,%asrX */ +{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", 0, v8 }, /* wr r,i,%asrX */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", 0, v6 }, /* wr r,r,%y */ +{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", 0, v6 }, /* wr r,i,%y */ +{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */ +{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", 0, v6notv9 }, /* wr r,r,%psr */ +{ "wr", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", 0, v6notv9 }, /* wr r,i,%psr */ +{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */ +{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", 0, v6notv9 }, /* wr r,r,%wim */ +{ "wr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", 0, v6notv9 }, /* wr r,i,%wim */ +{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */ +{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", 0, v6notv9 }, /* wr r,r,%tbr */ +{ "wr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", 0, v6notv9 }, /* wr r,i,%tbr */ +{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */ + +{ "wr", F3(2, 0x30, 0)|RD(2), F3(~2, ~0x30, ~0)|RD(~2)|ASI(~0), "1,2,E", 0, v9 }, /* wr r,r,%ccr */ +{ "wr", F3(2, 0x30, 1)|RD(2), F3(~2, ~0x30, ~1)|RD(~2), "1,i,E", 0, v9 }, /* wr r,i,%ccr */ +{ "wr", F3(2, 0x30, 0)|RD(3), F3(~2, ~0x30, ~0)|RD(~3)|ASI(~0), "1,2,o", 0, v9 }, /* wr r,r,%asi */ +{ "wr", F3(2, 0x30, 1)|RD(3), F3(~2, ~0x30, ~1)|RD(~3), "1,i,o", 0, v9 }, /* wr r,i,%asi */ +{ "wr", F3(2, 0x30, 0)|RD(6), F3(~2, ~0x30, ~0)|RD(~6)|ASI(~0), "1,2,s", 0, v9 }, /* wr r,r,%fprs */ +{ "wr", F3(2, 0x30, 1)|RD(6), F3(~2, ~0x30, ~1)|RD(~6), "1,i,s", 0, v9 }, /* wr r,i,%fprs */ + +{ "wr", F3(2, 0x30, 0)|RD(16), F3(~2, ~0x30, ~0)|RD(~16)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pcr */ +{ "wr", F3(2, 0x30, 1)|RD(16), F3(~2, ~0x30, ~1)|RD(~16), "1,i,_", 0, v9a }, /* wr r,i,%pcr */ +{ "wr", F3(2, 0x30, 0)|RD(17), F3(~2, ~0x30, ~0)|RD(~17)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pic */ +{ "wr", F3(2, 0x30, 1)|RD(17), F3(~2, ~0x30, ~1)|RD(~17), "1,i,_", 0, v9a }, /* wr r,i,%pic */ +{ "wr", F3(2, 0x30, 0)|RD(18), F3(~2, ~0x30, ~0)|RD(~18)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%dcr */ +{ "wr", F3(2, 0x30, 1)|RD(18), F3(~2, ~0x30, ~1)|RD(~18), "1,i,_", 0, v9a }, /* wr r,i,%dcr */ +{ "wr", F3(2, 0x30, 0)|RD(19), F3(~2, ~0x30, ~0)|RD(~19)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%gsr */ +{ "wr", F3(2, 0x30, 1)|RD(19), F3(~2, ~0x30, ~1)|RD(~19), "1,i,_", 0, v9a }, /* wr r,i,%gsr */ +{ "wr", F3(2, 0x30, 0)|RD(20), F3(~2, ~0x30, ~0)|RD(~20)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%set_softint */ +{ "wr", F3(2, 0x30, 1)|RD(20), F3(~2, ~0x30, ~1)|RD(~20), "1,i,_", 0, v9a }, /* wr r,i,%set_softint */ +{ "wr", F3(2, 0x30, 0)|RD(21), F3(~2, ~0x30, ~0)|RD(~21)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%clear_softint */ +{ "wr", F3(2, 0x30, 1)|RD(21), F3(~2, ~0x30, ~1)|RD(~21), "1,i,_", 0, v9a }, /* wr r,i,%clear_softint */ +{ "wr", F3(2, 0x30, 0)|RD(22), F3(~2, ~0x30, ~0)|RD(~22)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%softint */ +{ "wr", F3(2, 0x30, 1)|RD(22), F3(~2, ~0x30, ~1)|RD(~22), "1,i,_", 0, v9a }, /* wr r,i,%softint */ +{ "wr", F3(2, 0x30, 0)|RD(23), F3(~2, ~0x30, ~0)|RD(~23)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%tick_cmpr */ +{ "wr", F3(2, 0x30, 1)|RD(23), F3(~2, ~0x30, ~1)|RD(~23), "1,i,_", 0, v9a }, /* wr r,i,%tick_cmpr */ + +{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", 0, v8 }, /* rd %asrX,r */ +{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", 0, v6 }, /* rd %y,r */ +{ "rd", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", 0, v6notv9 }, /* rd %psr,r */ +{ "rd", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", 0, v6notv9 }, /* rd %wim,r */ +{ "rd", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", 0, v6notv9 }, /* rd %tbr,r */ + +{ "rd", F3(2, 0x28, 0)|RS1(2), F3(~2, ~0x28, ~0)|RS1(~2)|SIMM13(~0), "E,d", 0, v9 }, /* rd %ccr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(3), F3(~2, ~0x28, ~0)|RS1(~3)|SIMM13(~0), "o,d", 0, v9 }, /* rd %asi,r */ +{ "rd", F3(2, 0x28, 0)|RS1(4), F3(~2, ~0x28, ~0)|RS1(~4)|SIMM13(~0), "W,d", 0, v9 }, /* rd %tick,r */ +{ "rd", F3(2, 0x28, 0)|RS1(5), F3(~2, ~0x28, ~0)|RS1(~5)|SIMM13(~0), "P,d", 0, v9 }, /* rd %pc,r */ +{ "rd", F3(2, 0x28, 0)|RS1(6), F3(~2, ~0x28, ~0)|RS1(~6)|SIMM13(~0), "s,d", 0, v9 }, /* rd %fprs,r */ + +{ "rd", F3(2, 0x28, 0)|RS1(16), F3(~2, ~0x28, ~0)|RS1(~16)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pcr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(17), F3(~2, ~0x28, ~0)|RS1(~17)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pic,r */ +{ "rd", F3(2, 0x28, 0)|RS1(18), F3(~2, ~0x28, ~0)|RS1(~18)|SIMM13(~0), "/,d", 0, v9a }, /* rd %dcr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(19), F3(~2, ~0x28, ~0)|RS1(~19)|SIMM13(~0), "/,d", 0, v9a }, /* rd %gsr,r */ +{ "rd", F3(2, 0x28, 0)|RS1(22), F3(~2, ~0x28, ~0)|RS1(~22)|SIMM13(~0), "/,d", 0, v9a }, /* rd %softint,r */ +{ "rd", F3(2, 0x28, 0)|RS1(23), F3(~2, ~0x28, ~0)|RS1(~23)|SIMM13(~0), "/,d", 0, v9a }, /* rd %tick_cmpr,r */ + +{ "rdpr", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|SIMM13(~0), "?,d", 0, v9 }, /* rdpr %priv,r */ +{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0), "1,2,!", 0, v9 }, /* wrpr r1,r2,%priv */ +{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|SIMM13(~0), "1,!", 0, v9 }, /* wrpr r1,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "1,i,!", 0, v9 }, /* wrpr r1,i,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "i,1,!", F_ALIAS, v9 }, /* wrpr i,r1,%priv */ +{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RS1(~0), "i,!", 0, v9 }, /* wrpr i,%priv */ + +/* ??? This group seems wrong. A three operand move? */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", F_ALIAS, v8 }, /* wr r,r,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", F_ALIAS, v8 }, /* wr r,i,%asrX */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", F_ALIAS, v6 }, /* wr r,r,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", F_ALIAS, v6 }, /* wr r,i,%y */ +{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", F_ALIAS, v6notv9 }, /* wr r,r,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", F_ALIAS, v6notv9 }, /* wr r,i,%psr */ +{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", F_ALIAS, v6notv9 }, /* wr r,r,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", F_ALIAS, v6notv9 }, /* wr r,i,%wim */ +{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", F_ALIAS, v6notv9 }, /* wr r,r,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", F_ALIAS, v6notv9 }, /* wr r,i,%tbr */ + +{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", F_ALIAS, v8 }, /* rd %asr1,r */ +{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", F_ALIAS, v6 }, /* rd %y,r */ +{ "mov", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", F_ALIAS, v6notv9 }, /* rd %psr,r */ +{ "mov", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", F_ALIAS, v6notv9 }, /* rd %wim,r */ +{ "mov", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", F_ALIAS, v6notv9 }, /* rd %tbr,r */ + +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "i,m", F_ALIAS, v8 }, /* wr %g0,i,%asrX */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|SIMM13(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,0,%asrX */ +{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "i,y", F_ALIAS, v6 }, /* wr %g0,i,%y */ +{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0|SIMM13(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,0,%y */ +{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "i,p", F_ALIAS, v6notv9 }, /* wr %g0,i,%psr */ +{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0|SIMM13(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,0,%psr */ +{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "i,w", F_ALIAS, v6notv9 }, /* wr %g0,i,%wim */ +{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0|SIMM13(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,0,%wim */ +{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "i,t", F_ALIAS, v6notv9 }, /* wr %g0,i,%tbr */ +{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0|SIMM13(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,0,%tbr */ + +{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RS1_G0|ASI(~0), "2,d", 0, v6 }, /* or %g0,rs2,d */ +{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0, "i,d", 0, v6 }, /* or %g0,i,d */ +{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI_RS2(~0), "1,d", 0, v6 }, /* or rs1,%g0,d */ +{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|SIMM13(~0), "1,d", 0, v6 }, /* or rs1,0,d */ + +{ "or", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "1,i,d", 0, v6 }, +{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,1,d", 0, v6 }, + +{ "bset", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* or rd,rs2,rd */ +{ "bset", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,r", F_ALIAS, v6 }, /* or rd,i,rd */ + +/* This is not a commutative instruction. */ +{ "andn", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "1,i,d", 0, v6 }, + +/* This is not a commutative instruction. */ +{ "andncc", F3(2, 0x15, 0), F3(~2, ~0x15, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "1,i,d", 0, v6 }, + +{ "bclr", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* andn rd,rs2,rd */ +{ "bclr", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,r", F_ALIAS, v6 }, /* andn rd,i,rd */ + +{ "cmp", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|RD_G0|ASI(~0), "1,2", 0, v6 }, /* subcc rs1,rs2,%g0 */ +{ "cmp", F3(2, 0x14, 1), F3(~2, ~0x14, ~1)|RD_G0, "1,i", 0, v6 }, /* subcc rs1,i,%g0 */ + +{ "sub", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "sub", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "1,i,d", 0, v6 }, + +{ "subcc", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "subcc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "1,i,d", 0, v6 }, + +{ "subx", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 }, +{ "subx", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v6notv9 }, +{ "subc", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "subc", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v9 }, + +{ "subxcc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 }, +{ "subxcc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v6notv9 }, +{ "subccc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "subccc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v9 }, + +{ "and", F3(2, 0x01, 0), F3(~2, ~0x01, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "1,i,d", 0, v6 }, +{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "i,1,d", 0, v6 }, + +{ "andcc", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "1,i,d", 0, v6 }, +{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "i,1,d", 0, v6 }, + +{ "dec", F3(2, 0x04, 1)|SIMM13(0x1), F3(~2, ~0x04, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* sub rd,1,rd */ +{ "dec", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "i,r", F_ALIAS, v8 }, /* sub rd,imm,rd */ +{ "deccc", F3(2, 0x14, 1)|SIMM13(0x1), F3(~2, ~0x14, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* subcc rd,1,rd */ +{ "deccc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "i,r", F_ALIAS, v8 }, /* subcc rd,imm,rd */ +{ "inc", F3(2, 0x00, 1)|SIMM13(0x1), F3(~2, ~0x00, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* add rd,1,rd */ +{ "inc", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,r", F_ALIAS, v8 }, /* add rd,imm,rd */ +{ "inccc", F3(2, 0x10, 1)|SIMM13(0x1), F3(~2, ~0x10, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* addcc rd,1,rd */ +{ "inccc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,r", F_ALIAS, v8 }, /* addcc rd,imm,rd */ + +{ "btst", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|RD_G0|ASI(~0), "1,2", F_ALIAS, v6 }, /* andcc rs1,rs2,%g0 */ +{ "btst", F3(2, 0x11, 1), F3(~2, ~0x11, ~1)|RD_G0, "i,1", F_ALIAS, v6 }, /* andcc rs1,i,%g0 */ + +{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "2,d", F_ALIAS, v6 }, /* sub %g0,rs2,rd */ +{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "O", F_ALIAS, v6 }, /* sub %g0,rd,rd */ + +{ "add", F3(2, 0x00, 0), F3(~2, ~0x00, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "1,i,d", 0, v6 }, +{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,1,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 0), F3(~2, ~0x10, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "1,i,d", 0, v6 }, +{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,1,d", 0, v6 }, + +{ "addx", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v6notv9 }, +{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v6notv9 }, +{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v6notv9 }, +{ "addc", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v9 }, +{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v9 }, + +{ "addxcc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v6notv9 }, +{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v6notv9 }, +{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v6notv9 }, +{ "addccc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v9 }, +{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v9 }, + +{ "smul", F3(2, 0x0b, 0), F3(~2, ~0x0b, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "1,i,d", 0, v8 }, +{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "i,1,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 0), F3(~2, ~0x1b, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "1,i,d", 0, v8 }, +{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "i,1,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 0), F3(~2, ~0x0a, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "1,i,d", 0, v8 }, +{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "i,1,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 0), F3(~2, ~0x1a, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "1,i,d", 0, v8 }, +{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "i,1,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 0), F3(~2, ~0x0f, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "1,i,d", 0, v8 }, +{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "i,1,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 0), F3(~2, ~0x1f, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "1,i,d", 0, v8 }, +{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "i,1,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 0), F3(~2, ~0x0e, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "1,i,d", 0, v8 }, +{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "i,1,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 0), F3(~2, ~0x1e, ~0)|ASI(~0), "1,2,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "1,i,d", 0, v8 }, +{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "i,1,d", 0, v8 }, + +{ "mulx", F3(2, 0x09, 0), F3(~2, ~0x09, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "mulx", F3(2, 0x09, 1), F3(~2, ~0x09, ~1), "1,i,d", 0, v9 }, +{ "sdivx", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "sdivx", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, v9 }, +{ "udivx", F3(2, 0x0d, 0), F3(~2, ~0x0d, ~0)|ASI(~0), "1,2,d", 0, v9 }, +{ "udivx", F3(2, 0x0d, 1), F3(~2, ~0x0d, ~1), "1,i,d", 0, v9 }, + +{ "call", F1(0x1), F1(~0x1), "L", F_JSR|F_DELAYED, v6 }, +{ "call", F1(0x1), F1(~0x1), "L,#", F_JSR|F_DELAYED, v6 }, + +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%o7 */ +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%o7 */ +{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+i,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1", F_JSR|F_DELAYED, v6 }, /* jmpl i+rs1,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i,#", F_JSR|F_DELAYED, v6 }, +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,%o7 */ +{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1,#", F_JSR|F_DELAYED, v6 }, + + +/* Conditional instructions. + + Because this part of the table was such a mess earlier, I have + macrofied it so that all the branches and traps are generated from + a single-line description of each condition value. John Gilmore. */ + +/* Define branches -- one annulled, one without, etc. */ +#define br(opcode, mask, lose, flags) \ + { opcode, (mask)|ANNUL, (lose), ",a l", (flags), v6 }, \ + { opcode, (mask) , (lose)|ANNUL, "l", (flags), v6 } + +#define brx(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), "Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), ",T Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a,T Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20), ANNUL|BPRED|(lose), ",N Z,G", (flags), v9 }, \ + { opcode, (mask)|(2<<20)|ANNUL, BPRED|(lose), ",a,N Z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), "z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), ",T z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a z,G", (flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a,T z,G", (flags), v9 }, \ + { opcode, (mask), ANNUL|BPRED|(lose)|(2<<20), ",N z,G", (flags), v9 }, \ + { opcode, (mask)|ANNUL, BPRED|(lose)|(2<<20), ",a,N z,G", (flags), v9 } + +/* Define four traps: reg+reg, reg + immediate, immediate alone, reg alone. */ +#define tr(opcode, mask, lose, flags) \ + { opcode, (mask)|(2<<11)|IMMED, (lose)|RS1_G0, "Z,i", (flags), v9 }, /* %g0 + imm */ \ + { opcode, (mask)|(2<<11)|IMMED, (lose), "Z,1+i", (flags), v9 }, /* rs1 + imm */ \ + { opcode, (mask)|(2<<11), IMMED|(lose), "Z,1+2", (flags), v9 }, /* rs1 + rs2 */ \ + { opcode, (mask)|(2<<11), IMMED|(lose)|RS2_G0, "Z,1", (flags), v9 }, /* rs1 + %g0 */ \ + { opcode, (mask)|IMMED, (lose)|RS1_G0, "z,i", (flags)|F_ALIAS, v9 }, /* %g0 + imm */ \ + { opcode, (mask)|IMMED, (lose), "z,1+i", (flags)|F_ALIAS, v9 }, /* rs1 + imm */ \ + { opcode, (mask), IMMED|(lose), "z,1+2", (flags)|F_ALIAS, v9 }, /* rs1 + rs2 */ \ + { opcode, (mask), IMMED|(lose)|RS2_G0, "z,1", (flags)|F_ALIAS, v9 }, /* rs1 + %g0 */ \ + { opcode, (mask)|IMMED, (lose)|RS1_G0, "i", (flags), v6 }, /* %g0 + imm */ \ + { opcode, (mask)|IMMED, (lose), "1+i", (flags), v6 }, /* rs1 + imm */ \ + { opcode, (mask), IMMED|(lose), "1+2", (flags), v6 }, /* rs1 + rs2 */ \ + { opcode, (mask), IMMED|(lose)|RS2_G0, "1", (flags), v6 } /* rs1 + %g0 */ + +/* v9: We must put `brx' before `br', to ensure that we never match something + v9: against an expression unless it is an expression. Otherwise, we end + v9: up with undefined symbol tables entries, because they get added, but + v9: are not deleted if the pattern fails to match. */ + +/* Define both branches and traps based on condition mask */ +#define cond(bop, top, mask, flags) \ + brx(bop, F2(0, 1)|(mask), F2(~0, ~1)|((~mask)&COND(~0)), F_DELAYED|(flags)), /* v9 */ \ + br(bop, F2(0, 2)|(mask), F2(~0, ~2)|((~mask)&COND(~0)), F_DELAYED|(flags)), \ + tr(top, F3(2, 0x3a, 0)|(mask), F3(~2, ~0x3a, 0)|((~mask)&COND(~0)), ((flags) & ~(F_UNBR|F_CONDBR))) + +/* Define all the conditions, all the branches, all the traps. */ + +/* Standard branch, trap mnemonics */ +cond ("b", "ta", CONDA, F_UNBR), +/* Alternative form (just for assembly, not for disassembly) */ +cond ("ba", "t", CONDA, F_UNBR|F_ALIAS), + +cond ("bcc", "tcc", CONDCC, F_CONDBR), +cond ("bcs", "tcs", CONDCS, F_CONDBR), +cond ("be", "te", CONDE, F_CONDBR), +cond ("beq", "teq", CONDE, F_CONDBR|F_ALIAS), +cond ("bg", "tg", CONDG, F_CONDBR), +cond ("bgt", "tgt", CONDG, F_CONDBR|F_ALIAS), +cond ("bge", "tge", CONDGE, F_CONDBR), +cond ("bgeu", "tgeu", CONDGEU, F_CONDBR|F_ALIAS), /* for cc */ +cond ("bgu", "tgu", CONDGU, F_CONDBR), +cond ("bl", "tl", CONDL, F_CONDBR), +cond ("blt", "tlt", CONDL, F_CONDBR|F_ALIAS), +cond ("ble", "tle", CONDLE, F_CONDBR), +cond ("bleu", "tleu", CONDLEU, F_CONDBR), +cond ("blu", "tlu", CONDLU, F_CONDBR|F_ALIAS), /* for cs */ +cond ("bn", "tn", CONDN, F_CONDBR), +cond ("bne", "tne", CONDNE, F_CONDBR), +cond ("bneg", "tneg", CONDNEG, F_CONDBR), +cond ("bnz", "tnz", CONDNZ, F_CONDBR|F_ALIAS), /* for ne */ +cond ("bpos", "tpos", CONDPOS, F_CONDBR), +cond ("bvc", "tvc", CONDVC, F_CONDBR), +cond ("bvs", "tvs", CONDVS, F_CONDBR), +cond ("bz", "tz", CONDZ, F_CONDBR|F_ALIAS), /* for e */ + +#undef cond +#undef br +#undef brr /* v9 */ +#undef tr + +#define brr(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask)|BPRED, ANNUL|(lose), "1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED, ANNUL|(lose), ",T 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose), ",a 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|BPRED|ANNUL, (lose), ",a,T 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask), ANNUL|BPRED|(lose), ",N 1,k", F_DELAYED|(flags), v9 }, \ + { opcode, (mask)|ANNUL, BPRED|(lose), ",a,N 1,k", F_DELAYED|(flags), v9 } + +#define condr(bop, mask, flags) /* v9 */ \ + brr(bop, F2(0, 3)|COND(mask), F2(~0, ~3)|COND(~(mask)), (flags)) /* v9 */ + +/* v9 */ condr("brnz", 0x5, F_CONDBR), +/* v9 */ condr("brz", 0x1, F_CONDBR), +/* v9 */ condr("brgez", 0x7, F_CONDBR), +/* v9 */ condr("brlz", 0x3, F_CONDBR), +/* v9 */ condr("brlez", 0x2, F_CONDBR), +/* v9 */ condr("brgz", 0x6, F_CONDBR), + +#undef condr /* v9 */ +#undef brr /* v9 */ + +#define movr(opcode, mask, flags) /* v9 */ \ + { opcode, F3(2, 0x2f, 0)|RCOND(mask), F3(~2, ~0x2f, ~0)|RCOND(~(mask)), "1,2,d", (flags), v9 }, \ + { opcode, F3(2, 0x2f, 1)|RCOND(mask), F3(~2, ~0x2f, ~1)|RCOND(~(mask)), "1,j,d", (flags), v9 } + +#define fmrrs(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,f,g", (flags) | F_FLOAT, v9 } +#define fmrrd(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,B,H", (flags) | F_FLOAT, v9 } +#define fmrrq(opcode, mask, lose, flags) /* v9 */ \ + { opcode, (mask), (lose), "1,R,J", (flags) | F_FLOAT, v9 } + +#define fmovrs(mop, mask, flags) /* v9 */ \ + fmrrs(mop, F3(2, 0x35, 0)|OPF_LOW5(5)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~5)|RCOND(~(mask)), (flags)) /* v9 */ +#define fmovrd(mop, mask, flags) /* v9 */ \ + fmrrd(mop, F3(2, 0x35, 0)|OPF_LOW5(6)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~6)|RCOND(~(mask)), (flags)) /* v9 */ +#define fmovrq(mop, mask, flags) /* v9 */ \ + fmrrq(mop, F3(2, 0x35, 0)|OPF_LOW5(7)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~7)|RCOND(~(mask)), (flags)) /* v9 */ + +/* v9 */ movr("movrne", 0x5, 0), +/* v9 */ movr("movre", 0x1, 0), +/* v9 */ movr("movrgez", 0x7, 0), +/* v9 */ movr("movrlz", 0x3, 0), +/* v9 */ movr("movrlez", 0x2, 0), +/* v9 */ movr("movrgz", 0x6, 0), +/* v9 */ movr("movrnz", 0x5, F_ALIAS), +/* v9 */ movr("movrz", 0x1, F_ALIAS), + +/* v9 */ fmovrs("fmovrsne", 0x5, 0), +/* v9 */ fmovrs("fmovrse", 0x1, 0), +/* v9 */ fmovrs("fmovrsgez", 0x7, 0), +/* v9 */ fmovrs("fmovrslz", 0x3, 0), +/* v9 */ fmovrs("fmovrslez", 0x2, 0), +/* v9 */ fmovrs("fmovrsgz", 0x6, 0), +/* v9 */ fmovrs("fmovrsnz", 0x5, F_ALIAS), +/* v9 */ fmovrs("fmovrsz", 0x1, F_ALIAS), + +/* v9 */ fmovrd("fmovrdne", 0x5, 0), +/* v9 */ fmovrd("fmovrde", 0x1, 0), +/* v9 */ fmovrd("fmovrdgez", 0x7, 0), +/* v9 */ fmovrd("fmovrdlz", 0x3, 0), +/* v9 */ fmovrd("fmovrdlez", 0x2, 0), +/* v9 */ fmovrd("fmovrdgz", 0x6, 0), +/* v9 */ fmovrd("fmovrdnz", 0x5, F_ALIAS), +/* v9 */ fmovrd("fmovrdz", 0x1, F_ALIAS), + +/* v9 */ fmovrq("fmovrqne", 0x5, 0), +/* v9 */ fmovrq("fmovrqe", 0x1, 0), +/* v9 */ fmovrq("fmovrqgez", 0x7, 0), +/* v9 */ fmovrq("fmovrqlz", 0x3, 0), +/* v9 */ fmovrq("fmovrqlez", 0x2, 0), +/* v9 */ fmovrq("fmovrqgz", 0x6, 0), +/* v9 */ fmovrq("fmovrqnz", 0x5, F_ALIAS), +/* v9 */ fmovrq("fmovrqz", 0x1, F_ALIAS), + +#undef movr /* v9 */ +#undef fmovr /* v9 */ +#undef fmrr /* v9 */ + +#define movicc(opcode, cond, flags) /* v9 */ \ + { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|XCC|(1<<11), "z,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|XCC|(1<<11), "z,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|(1<<11), "Z,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|(1<<11), "Z,I,d", flags, v9 } + +#define movfcc(opcode, fcond, flags) /* v9 */ \ + { opcode, F3(2, 0x2c, 0)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~0), "6,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~1), "6,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~0), "7,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~1), "7,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~0), "8,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~1), "8,I,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 0)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~0), "9,2,d", flags, v9 }, \ + { opcode, F3(2, 0x2c, 1)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~1), "9,I,d", flags, v9 } + +#define movcc(opcode, cond, fcond, flags) /* v9 */ \ + movfcc (opcode, fcond, flags), /* v9 */ \ + movicc (opcode, cond, flags) /* v9 */ + +/* v9 */ movcc ("mova", CONDA, FCONDA, 0), +/* v9 */ movicc ("movcc", CONDCC, 0), +/* v9 */ movicc ("movgeu", CONDGEU, F_ALIAS), +/* v9 */ movicc ("movcs", CONDCS, 0), +/* v9 */ movicc ("movlu", CONDLU, F_ALIAS), +/* v9 */ movcc ("move", CONDE, FCONDE, 0), +/* v9 */ movcc ("movg", CONDG, FCONDG, 0), +/* v9 */ movcc ("movge", CONDGE, FCONDGE, 0), +/* v9 */ movicc ("movgu", CONDGU, 0), +/* v9 */ movcc ("movl", CONDL, FCONDL, 0), +/* v9 */ movcc ("movle", CONDLE, FCONDLE, 0), +/* v9 */ movicc ("movleu", CONDLEU, 0), +/* v9 */ movfcc ("movlg", FCONDLG, 0), +/* v9 */ movcc ("movn", CONDN, FCONDN, 0), +/* v9 */ movcc ("movne", CONDNE, FCONDNE, 0), +/* v9 */ movicc ("movneg", CONDNEG, 0), +/* v9 */ movcc ("movnz", CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ movfcc ("movo", FCONDO, 0), +/* v9 */ movicc ("movpos", CONDPOS, 0), +/* v9 */ movfcc ("movu", FCONDU, 0), +/* v9 */ movfcc ("movue", FCONDUE, 0), +/* v9 */ movfcc ("movug", FCONDUG, 0), +/* v9 */ movfcc ("movuge", FCONDUGE, 0), +/* v9 */ movfcc ("movul", FCONDUL, 0), +/* v9 */ movfcc ("movule", FCONDULE, 0), +/* v9 */ movicc ("movvc", CONDVC, 0), +/* v9 */ movicc ("movvs", CONDVS, 0), +/* v9 */ movcc ("movz", CONDZ, FCONDZ, F_ALIAS), + +#undef movicc /* v9 */ +#undef movfcc /* v9 */ +#undef movcc /* v9 */ + +#define FM_SF 1 /* v9 - values for fpsize */ +#define FM_DF 2 /* v9 */ +#define FM_QF 3 /* v9 */ + +#define fmovicc(opcode, fpsize, cond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z,f,g", flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z,f,g", flags, v9 } + +#define fmovfcc(opcode, fpsize, fcond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6,f,g", flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7,f,g", flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8,f,g", flags, v9 }, \ +{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9,f,g", flags, v9 } + +/* FIXME: use fmovicc/fmovfcc? */ /* v9 */ +#define fmovcc(opcode, fpsize, cond, fcond, flags) /* v9 */ \ +{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z,f,g", flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6,f,g", flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z,f,g", flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7,f,g", flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8,f,g", flags | F_FLOAT, v9 }, \ +{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9,f,g", flags | F_FLOAT, v9 } + +/* v9 */ fmovcc ("fmovda", FM_DF, CONDA, FCONDA, 0), +/* v9 */ fmovcc ("fmovqa", FM_QF, CONDA, FCONDA, 0), +/* v9 */ fmovcc ("fmovsa", FM_SF, CONDA, FCONDA, 0), +/* v9 */ fmovicc ("fmovdcc", FM_DF, CONDCC, 0), +/* v9 */ fmovicc ("fmovqcc", FM_QF, CONDCC, 0), +/* v9 */ fmovicc ("fmovscc", FM_SF, CONDCC, 0), +/* v9 */ fmovicc ("fmovdcs", FM_DF, CONDCS, 0), +/* v9 */ fmovicc ("fmovqcs", FM_QF, CONDCS, 0), +/* v9 */ fmovicc ("fmovscs", FM_SF, CONDCS, 0), +/* v9 */ fmovcc ("fmovde", FM_DF, CONDE, FCONDE, 0), +/* v9 */ fmovcc ("fmovqe", FM_QF, CONDE, FCONDE, 0), +/* v9 */ fmovcc ("fmovse", FM_SF, CONDE, FCONDE, 0), +/* v9 */ fmovcc ("fmovdg", FM_DF, CONDG, FCONDG, 0), +/* v9 */ fmovcc ("fmovqg", FM_QF, CONDG, FCONDG, 0), +/* v9 */ fmovcc ("fmovsg", FM_SF, CONDG, FCONDG, 0), +/* v9 */ fmovcc ("fmovdge", FM_DF, CONDGE, FCONDGE, 0), +/* v9 */ fmovcc ("fmovqge", FM_QF, CONDGE, FCONDGE, 0), +/* v9 */ fmovcc ("fmovsge", FM_SF, CONDGE, FCONDGE, 0), +/* v9 */ fmovicc ("fmovdgeu", FM_DF, CONDGEU, F_ALIAS), +/* v9 */ fmovicc ("fmovqgeu", FM_QF, CONDGEU, F_ALIAS), +/* v9 */ fmovicc ("fmovsgeu", FM_SF, CONDGEU, F_ALIAS), +/* v9 */ fmovicc ("fmovdgu", FM_DF, CONDGU, 0), +/* v9 */ fmovicc ("fmovqgu", FM_QF, CONDGU, 0), +/* v9 */ fmovicc ("fmovsgu", FM_SF, CONDGU, 0), +/* v9 */ fmovcc ("fmovdl", FM_DF, CONDL, FCONDL, 0), +/* v9 */ fmovcc ("fmovql", FM_QF, CONDL, FCONDL, 0), +/* v9 */ fmovcc ("fmovsl", FM_SF, CONDL, FCONDL, 0), +/* v9 */ fmovcc ("fmovdle", FM_DF, CONDLE, FCONDLE, 0), +/* v9 */ fmovcc ("fmovqle", FM_QF, CONDLE, FCONDLE, 0), +/* v9 */ fmovcc ("fmovsle", FM_SF, CONDLE, FCONDLE, 0), +/* v9 */ fmovicc ("fmovdleu", FM_DF, CONDLEU, 0), +/* v9 */ fmovicc ("fmovqleu", FM_QF, CONDLEU, 0), +/* v9 */ fmovicc ("fmovsleu", FM_SF, CONDLEU, 0), +/* v9 */ fmovfcc ("fmovdlg", FM_DF, FCONDLG, 0), +/* v9 */ fmovfcc ("fmovqlg", FM_QF, FCONDLG, 0), +/* v9 */ fmovfcc ("fmovslg", FM_SF, FCONDLG, 0), +/* v9 */ fmovicc ("fmovdlu", FM_DF, CONDLU, F_ALIAS), +/* v9 */ fmovicc ("fmovqlu", FM_QF, CONDLU, F_ALIAS), +/* v9 */ fmovicc ("fmovslu", FM_SF, CONDLU, F_ALIAS), +/* v9 */ fmovcc ("fmovdn", FM_DF, CONDN, FCONDN, 0), +/* v9 */ fmovcc ("fmovqn", FM_QF, CONDN, FCONDN, 0), +/* v9 */ fmovcc ("fmovsn", FM_SF, CONDN, FCONDN, 0), +/* v9 */ fmovcc ("fmovdne", FM_DF, CONDNE, FCONDNE, 0), +/* v9 */ fmovcc ("fmovqne", FM_QF, CONDNE, FCONDNE, 0), +/* v9 */ fmovcc ("fmovsne", FM_SF, CONDNE, FCONDNE, 0), +/* v9 */ fmovicc ("fmovdneg", FM_DF, CONDNEG, 0), +/* v9 */ fmovicc ("fmovqneg", FM_QF, CONDNEG, 0), +/* v9 */ fmovicc ("fmovsneg", FM_SF, CONDNEG, 0), +/* v9 */ fmovcc ("fmovdnz", FM_DF, CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ fmovcc ("fmovqnz", FM_QF, CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ fmovcc ("fmovsnz", FM_SF, CONDNZ, FCONDNZ, F_ALIAS), +/* v9 */ fmovfcc ("fmovdo", FM_DF, FCONDO, 0), +/* v9 */ fmovfcc ("fmovqo", FM_QF, FCONDO, 0), +/* v9 */ fmovfcc ("fmovso", FM_SF, FCONDO, 0), +/* v9 */ fmovicc ("fmovdpos", FM_DF, CONDPOS, 0), +/* v9 */ fmovicc ("fmovqpos", FM_QF, CONDPOS, 0), +/* v9 */ fmovicc ("fmovspos", FM_SF, CONDPOS, 0), +/* v9 */ fmovfcc ("fmovdu", FM_DF, FCONDU, 0), +/* v9 */ fmovfcc ("fmovqu", FM_QF, FCONDU, 0), +/* v9 */ fmovfcc ("fmovsu", FM_SF, FCONDU, 0), +/* v9 */ fmovfcc ("fmovdue", FM_DF, FCONDUE, 0), +/* v9 */ fmovfcc ("fmovque", FM_QF, FCONDUE, 0), +/* v9 */ fmovfcc ("fmovsue", FM_SF, FCONDUE, 0), +/* v9 */ fmovfcc ("fmovdug", FM_DF, FCONDUG, 0), +/* v9 */ fmovfcc ("fmovqug", FM_QF, FCONDUG, 0), +/* v9 */ fmovfcc ("fmovsug", FM_SF, FCONDUG, 0), +/* v9 */ fmovfcc ("fmovduge", FM_DF, FCONDUGE, 0), +/* v9 */ fmovfcc ("fmovquge", FM_QF, FCONDUGE, 0), +/* v9 */ fmovfcc ("fmovsuge", FM_SF, FCONDUGE, 0), +/* v9 */ fmovfcc ("fmovdul", FM_DF, FCONDUL, 0), +/* v9 */ fmovfcc ("fmovqul", FM_QF, FCONDUL, 0), +/* v9 */ fmovfcc ("fmovsul", FM_SF, FCONDUL, 0), +/* v9 */ fmovfcc ("fmovdule", FM_DF, FCONDULE, 0), +/* v9 */ fmovfcc ("fmovqule", FM_QF, FCONDULE, 0), +/* v9 */ fmovfcc ("fmovsule", FM_SF, FCONDULE, 0), +/* v9 */ fmovicc ("fmovdvc", FM_DF, CONDVC, 0), +/* v9 */ fmovicc ("fmovqvc", FM_QF, CONDVC, 0), +/* v9 */ fmovicc ("fmovsvc", FM_SF, CONDVC, 0), +/* v9 */ fmovicc ("fmovdvs", FM_DF, CONDVS, 0), +/* v9 */ fmovicc ("fmovqvs", FM_QF, CONDVS, 0), +/* v9 */ fmovicc ("fmovsvs", FM_SF, CONDVS, 0), +/* v9 */ fmovcc ("fmovdz", FM_DF, CONDZ, FCONDZ, F_ALIAS), +/* v9 */ fmovcc ("fmovqz", FM_QF, CONDZ, FCONDZ, F_ALIAS), +/* v9 */ fmovcc ("fmovsz", FM_SF, CONDZ, FCONDZ, F_ALIAS), + +#undef fmovicc /* v9 */ +#undef fmovfcc /* v9 */ +#undef fmovcc /* v9 */ +#undef FM_DF /* v9 */ +#undef FM_QF /* v9 */ +#undef FM_SF /* v9 */ + +/* Coprocessor branches. */ +#define CBR(opcode, mask, lose, flags, arch) \ + { opcode, (mask), ANNUL|(lose), "l", flags|F_DELAYED, arch }, \ + { opcode, (mask)|ANNUL, (lose), ",a l", flags|F_DELAYED, arch } + +/* Floating point branches. */ +#define FBR(opcode, mask, lose, flags) \ + { opcode, (mask), ANNUL|(lose), "l", flags|F_DELAYED|F_FBR, v6 }, \ + { opcode, (mask)|ANNUL, (lose), ",a l", flags|F_DELAYED|F_FBR, v6 } + +/* V9 extended floating point branches. */ +#define FBRX(opcode, mask, lose, flags) /* v9 */ \ + { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), "6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), ",T 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a,T 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask), ANNUL|BPRED|FBFCC(~0)|(lose), ",N 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(0)|(mask)|ANNUL, BPRED|FBFCC(~0)|(lose), ",a,N 6,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), "7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), ",T 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a,T 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask), ANNUL|BPRED|FBFCC(~1)|(lose), ",N 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(1)|(mask)|ANNUL, BPRED|FBFCC(~1)|(lose), ",a,N 7,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), "8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), ",T 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a,T 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask), ANNUL|BPRED|FBFCC(~2)|(lose), ",N 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(2)|(mask)|ANNUL, BPRED|FBFCC(~2)|(lose), ",a,N 8,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), "9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), ",T 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a,T 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask), ANNUL|BPRED|FBFCC(~3)|(lose), ",N 9,G", flags|F_DELAYED|F_FBR, v9 }, \ + { opcode, FBFCC(3)|(mask)|ANNUL, BPRED|FBFCC(~3)|(lose), ",a,N 9,G", flags|F_DELAYED|F_FBR, v9 } + +/* v9: We must put `FBRX' before `FBR', to ensure that we never match + v9: something against an expression unless it is an expression. Otherwise, + v9: we end up with undefined symbol tables entries, because they get added, + v9: but are not deleted if the pattern fails to match. */ + +#define CONDFC(fop, cop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \ + CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6notlet) + +#define CONDFCL(fop, cop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \ + CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6) + +#define CONDF(fop, mask, flags) \ + FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \ + FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags) + +CONDFC ("fb", "cb", 0x8, 0), +CONDFCL ("fba", "cba", 0x8, F_ALIAS), +CONDFC ("fbe", "cb0", 0x9, 0), +CONDF ("fbz", 0x9, F_ALIAS), +CONDFC ("fbg", "cb2", 0x6, 0), +CONDFC ("fbge", "cb02", 0xb, 0), +CONDFC ("fbl", "cb1", 0x4, 0), +CONDFC ("fble", "cb01", 0xd, 0), +CONDFC ("fblg", "cb12", 0x2, 0), +CONDFCL ("fbn", "cbn", 0x0, 0), +CONDFC ("fbne", "cb123", 0x1, 0), +CONDF ("fbnz", 0x1, F_ALIAS), +CONDFC ("fbo", "cb012", 0xf, 0), +CONDFC ("fbu", "cb3", 0x7, 0), +CONDFC ("fbue", "cb03", 0xa, 0), +CONDFC ("fbug", "cb23", 0x5, 0), +CONDFC ("fbuge", "cb023", 0xc, 0), +CONDFC ("fbul", "cb13", 0x3, 0), +CONDFC ("fbule", "cb013", 0xe, 0), + +#undef CONDFC +#undef CONDFCL +#undef CONDF +#undef CBR +#undef FBR +#undef FBRX /* v9 */ + +{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%g0 */ +{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+i,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* jmpl i+rs1,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* jmpl %g0+i,%g0 */ +{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+0,%g0 */ + +{ "nop", F2(0, 4), 0xfeffffff, "", 0, v6 }, /* sethi 0, %g0 */ + +{ "set", F2(0x0, 0x4), F2(~0x0, ~0x4), "Sh,d", F_ALIAS, v6 }, +{ "setuw", F2(0x0, 0x4), F2(~0x0, ~0x4), "Sh,d", F_ALIAS, v9 }, +{ "setsw", F2(0x0, 0x4), F2(~0x0, ~0x4), "Sh,d", F_ALIAS, v9 }, +{ "setx", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,1,d", F_ALIAS, v9 }, + +{ "sethi", F2(0x0, 0x4), F2(~0x0, ~0x4), "h,d", 0, v6 }, + +{ "taddcc", F3(2, 0x20, 0), F3(~2, ~0x20, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "1,i,d", 0, v6 }, +{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "i,1,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 0), F3(~2, ~0x22, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "1,i,d", 0, v6 }, +{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "i,1,d", 0, v6 }, + +{ "tsubcc", F3(2, 0x21, 0), F3(~2, ~0x21, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "tsubcc", F3(2, 0x21, 1), F3(~2, ~0x21, ~1), "1,i,d", 0, v6 }, +{ "tsubcctv", F3(2, 0x23, 0), F3(~2, ~0x23, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "tsubcctv", F3(2, 0x23, 1), F3(~2, ~0x23, ~1), "1,i,d", 0, v6 }, + +{ "unimp", F2(0x0, 0x0), 0xffc00000, "n", 0, v6notv9 }, +{ "illtrap", F2(0, 0), F2(~0, ~0)|RD_G0, "n", 0, v9 }, + +/* This *is* a commutative instruction. */ +{ "xnor", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "1,i,d", 0, v6 }, +{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "i,1,d", 0, v6 }, +/* This *is* a commutative instruction. */ +{ "xnorcc", F3(2, 0x17, 0), F3(~2, ~0x17, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "1,i,d", 0, v6 }, +{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "i,1,d", 0, v6 }, +{ "xor", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "1,i,d", 0, v6 }, +{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,1,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 0), F3(~2, ~0x13, ~0)|ASI(~0), "1,2,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "1,i,d", 0, v6 }, +{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "i,1,d", 0, v6 }, + +{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,d", F_ALIAS, v6 }, /* xnor rs1,%0,rd */ +{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "r", F_ALIAS, v6 }, /* xnor rd,%0,rd */ + +{ "btog", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* xor rd,rs2,rd */ +{ "btog", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,r", F_ALIAS, v6 }, /* xor rd,i,rd */ + +/* FPop1 and FPop2 are not instructions. Don't accept them. */ + +{ "fdtoi", F3F(2, 0x34, 0x0d2), F3F(~2, ~0x34, ~0x0d2)|RS1_G0, "B,g", F_FLOAT, v6 }, +{ "fstoi", F3F(2, 0x34, 0x0d1), F3F(~2, ~0x34, ~0x0d1)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fqtoi", F3F(2, 0x34, 0x0d3), F3F(~2, ~0x34, ~0x0d3)|RS1_G0, "R,g", F_FLOAT, v8 }, + +{ "fdtox", F3F(2, 0x34, 0x082), F3F(~2, ~0x34, ~0x082)|RS1_G0, "B,g", F_FLOAT, v9 }, +{ "fstox", F3F(2, 0x34, 0x081), F3F(~2, ~0x34, ~0x081)|RS1_G0, "f,g", F_FLOAT, v9 }, +{ "fqtox", F3F(2, 0x34, 0x083), F3F(~2, ~0x34, ~0x083)|RS1_G0, "R,g", F_FLOAT, v9 }, + +{ "fitod", F3F(2, 0x34, 0x0c8), F3F(~2, ~0x34, ~0x0c8)|RS1_G0, "f,H", F_FLOAT, v6 }, +{ "fitos", F3F(2, 0x34, 0x0c4), F3F(~2, ~0x34, ~0x0c4)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fitoq", F3F(2, 0x34, 0x0cc), F3F(~2, ~0x34, ~0x0cc)|RS1_G0, "f,J", F_FLOAT, v8 }, + +{ "fxtod", F3F(2, 0x34, 0x088), F3F(~2, ~0x34, ~0x088)|RS1_G0, "f,H", F_FLOAT, v9 }, +{ "fxtos", F3F(2, 0x34, 0x084), F3F(~2, ~0x34, ~0x084)|RS1_G0, "f,g", F_FLOAT, v9 }, +{ "fxtoq", F3F(2, 0x34, 0x08c), F3F(~2, ~0x34, ~0x08c)|RS1_G0, "f,J", F_FLOAT, v9 }, + +{ "fdtoq", F3F(2, 0x34, 0x0ce), F3F(~2, ~0x34, ~0x0ce)|RS1_G0, "B,J", F_FLOAT, v8 }, +{ "fdtos", F3F(2, 0x34, 0x0c6), F3F(~2, ~0x34, ~0x0c6)|RS1_G0, "B,g", F_FLOAT, v6 }, +{ "fqtod", F3F(2, 0x34, 0x0cb), F3F(~2, ~0x34, ~0x0cb)|RS1_G0, "R,H", F_FLOAT, v8 }, +{ "fqtos", F3F(2, 0x34, 0x0c7), F3F(~2, ~0x34, ~0x0c7)|RS1_G0, "R,g", F_FLOAT, v8 }, +{ "fstod", F3F(2, 0x34, 0x0c9), F3F(~2, ~0x34, ~0x0c9)|RS1_G0, "f,H", F_FLOAT, v6 }, +{ "fstoq", F3F(2, 0x34, 0x0cd), F3F(~2, ~0x34, ~0x0cd)|RS1_G0, "f,J", F_FLOAT, v8 }, + +{ "fdivd", F3F(2, 0x34, 0x04e), F3F(~2, ~0x34, ~0x04e), "v,B,H", F_FLOAT, v6 }, +{ "fdivq", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT, v8 }, +{ "fdivs", F3F(2, 0x34, 0x04d), F3F(~2, ~0x34, ~0x04d), "e,f,g", F_FLOAT, v6 }, +{ "fmuld", F3F(2, 0x34, 0x04a), F3F(~2, ~0x34, ~0x04a), "v,B,H", F_FLOAT, v6 }, +{ "fmulq", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT, v8 }, +{ "fmuls", F3F(2, 0x34, 0x049), F3F(~2, ~0x34, ~0x049), "e,f,g", F_FLOAT, v6 }, + +{ "fdmulq", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT, v8 }, +{ "fsmuld", F3F(2, 0x34, 0x069), F3F(~2, ~0x34, ~0x069), "e,f,H", F_FLOAT, v8 }, + +{ "fsqrtd", F3F(2, 0x34, 0x02a), F3F(~2, ~0x34, ~0x02a)|RS1_G0, "B,H", F_FLOAT, v7 }, +{ "fsqrtq", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT, v8 }, +{ "fsqrts", F3F(2, 0x34, 0x029), F3F(~2, ~0x34, ~0x029)|RS1_G0, "f,g", F_FLOAT, v7 }, + +{ "fabsd", F3F(2, 0x34, 0x00a), F3F(~2, ~0x34, ~0x00a)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fabsq", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fabss", F3F(2, 0x34, 0x009), F3F(~2, ~0x34, ~0x009)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fmovd", F3F(2, 0x34, 0x002), F3F(~2, ~0x34, ~0x002)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fmovq", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fmovs", F3F(2, 0x34, 0x001), F3F(~2, ~0x34, ~0x001)|RS1_G0, "f,g", F_FLOAT, v6 }, +{ "fnegd", F3F(2, 0x34, 0x006), F3F(~2, ~0x34, ~0x006)|RS1_G0, "B,H", F_FLOAT, v9 }, +{ "fnegq", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT, v9 }, +{ "fnegs", F3F(2, 0x34, 0x005), F3F(~2, ~0x34, ~0x005)|RS1_G0, "f,g", F_FLOAT, v6 }, + +{ "faddd", F3F(2, 0x34, 0x042), F3F(~2, ~0x34, ~0x042), "v,B,H", F_FLOAT, v6 }, +{ "faddq", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT, v8 }, +{ "fadds", F3F(2, 0x34, 0x041), F3F(~2, ~0x34, ~0x041), "e,f,g", F_FLOAT, v6 }, +{ "fsubd", F3F(2, 0x34, 0x046), F3F(~2, ~0x34, ~0x046), "v,B,H", F_FLOAT, v6 }, +{ "fsubq", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT, v8 }, +{ "fsubs", F3F(2, 0x34, 0x045), F3F(~2, ~0x34, ~0x045), "e,f,g", F_FLOAT, v6 }, + +#define CMPFCC(x) (((x)&0x3)<<25) + +{ "fcmpd", F3F(2, 0x35, 0x052), F3F(~2, ~0x35, ~0x052)|RD_G0, "v,B", F_FLOAT, v6 }, +{ "fcmpd", CMPFCC(0)|F3F(2, 0x35, 0x052), CMPFCC(~0)|F3F(~2, ~0x35, ~0x052), "6,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(1)|F3F(2, 0x35, 0x052), CMPFCC(~1)|F3F(~2, ~0x35, ~0x052), "7,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(2)|F3F(2, 0x35, 0x052), CMPFCC(~2)|F3F(~2, ~0x35, ~0x052), "8,v,B", F_FLOAT, v9 }, +{ "fcmpd", CMPFCC(3)|F3F(2, 0x35, 0x052), CMPFCC(~3)|F3F(~2, ~0x35, ~0x052), "9,v,B", F_FLOAT, v9 }, +{ "fcmped", F3F(2, 0x35, 0x056), F3F(~2, ~0x35, ~0x056)|RD_G0, "v,B", F_FLOAT, v6 }, +{ "fcmped", CMPFCC(0)|F3F(2, 0x35, 0x056), CMPFCC(~0)|F3F(~2, ~0x35, ~0x056), "6,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(1)|F3F(2, 0x35, 0x056), CMPFCC(~1)|F3F(~2, ~0x35, ~0x056), "7,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(2)|F3F(2, 0x35, 0x056), CMPFCC(~2)|F3F(~2, ~0x35, ~0x056), "8,v,B", F_FLOAT, v9 }, +{ "fcmped", CMPFCC(3)|F3F(2, 0x35, 0x056), CMPFCC(~3)|F3F(~2, ~0x35, ~0x056), "9,v,B", F_FLOAT, v9 }, +{ "fcmpq", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT, v8 }, +{ "fcmpq", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT, v9 }, +{ "fcmpq", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT, v9 }, +{ "fcmpeq", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT, v8 }, +{ "fcmpeq", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT, v9 }, +{ "fcmpeq", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT, v9 }, +{ "fcmps", F3F(2, 0x35, 0x051), F3F(~2, ~0x35, ~0x051)|RD_G0, "e,f", F_FLOAT, v6 }, +{ "fcmps", CMPFCC(0)|F3F(2, 0x35, 0x051), CMPFCC(~0)|F3F(~2, ~0x35, ~0x051), "6,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(1)|F3F(2, 0x35, 0x051), CMPFCC(~1)|F3F(~2, ~0x35, ~0x051), "7,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(2)|F3F(2, 0x35, 0x051), CMPFCC(~2)|F3F(~2, ~0x35, ~0x051), "8,e,f", F_FLOAT, v9 }, +{ "fcmps", CMPFCC(3)|F3F(2, 0x35, 0x051), CMPFCC(~3)|F3F(~2, ~0x35, ~0x051), "9,e,f", F_FLOAT, v9 }, +{ "fcmpes", F3F(2, 0x35, 0x055), F3F(~2, ~0x35, ~0x055)|RD_G0, "e,f", F_FLOAT, v6 }, +{ "fcmpes", CMPFCC(0)|F3F(2, 0x35, 0x055), CMPFCC(~0)|F3F(~2, ~0x35, ~0x055), "6,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(1)|F3F(2, 0x35, 0x055), CMPFCC(~1)|F3F(~2, ~0x35, ~0x055), "7,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(2)|F3F(2, 0x35, 0x055), CMPFCC(~2)|F3F(~2, ~0x35, ~0x055), "8,e,f", F_FLOAT, v9 }, +{ "fcmpes", CMPFCC(3)|F3F(2, 0x35, 0x055), CMPFCC(~3)|F3F(~2, ~0x35, ~0x055), "9,e,f", F_FLOAT, v9 }, + +/* These Extended FPop (FIFO) instructions are new in the Fujitsu + MB86934, replacing the CPop instructions from v6 and later + processors. */ + +#define EFPOP1_2(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op)|RS1_G0, args, 0, sparclite } +#define EFPOP1_3(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op), args, 0, sparclite } +#define EFPOP2_2(name, op, args) { name, F3F(2, 0x37, op), F3F(~2, ~0x37, ~op)|RD_G0, args, 0, sparclite } + +EFPOP1_2 ("efitod", 0x0c8, "f,H"), +EFPOP1_2 ("efitos", 0x0c4, "f,g"), +EFPOP1_2 ("efdtoi", 0x0d2, "B,g"), +EFPOP1_2 ("efstoi", 0x0d1, "f,g"), +EFPOP1_2 ("efstod", 0x0c9, "f,H"), +EFPOP1_2 ("efdtos", 0x0c6, "B,g"), +EFPOP1_2 ("efmovs", 0x001, "f,g"), +EFPOP1_2 ("efnegs", 0x005, "f,g"), +EFPOP1_2 ("efabss", 0x009, "f,g"), +EFPOP1_2 ("efsqrtd", 0x02a, "B,H"), +EFPOP1_2 ("efsqrts", 0x029, "f,g"), +EFPOP1_3 ("efaddd", 0x042, "v,B,H"), +EFPOP1_3 ("efadds", 0x041, "e,f,g"), +EFPOP1_3 ("efsubd", 0x046, "v,B,H"), +EFPOP1_3 ("efsubs", 0x045, "e,f,g"), +EFPOP1_3 ("efdivd", 0x04e, "v,B,H"), +EFPOP1_3 ("efdivs", 0x04d, "e,f,g"), +EFPOP1_3 ("efmuld", 0x04a, "v,B,H"), +EFPOP1_3 ("efmuls", 0x049, "e,f,g"), +EFPOP1_3 ("efsmuld", 0x069, "e,f,H"), +EFPOP2_2 ("efcmpd", 0x052, "v,B"), +EFPOP2_2 ("efcmped", 0x056, "v,B"), +EFPOP2_2 ("efcmps", 0x051, "e,f"), +EFPOP2_2 ("efcmpes", 0x055, "e,f"), + +#undef EFPOP1_2 +#undef EFPOP1_3 +#undef EFPOP2_2 + +/* These are marked F_ALIAS, so that they won't conflict with sparclite insns + present. Otherwise, the F_ALIAS flag is ignored. */ +{ "cpop1", F3(2, 0x36, 0), F3(~2, ~0x36, ~1), "[1+2],d", F_ALIAS, v6notv9 }, +{ "cpop2", F3(2, 0x37, 0), F3(~2, ~0x37, ~1), "[1+2],d", F_ALIAS, v6notv9 }, + +/* sparclet specific insns */ + +COMMUTEOP ("umac", 0x3e, sparclet), +COMMUTEOP ("smac", 0x3f, sparclet), +COMMUTEOP ("umacd", 0x2e, sparclet), +COMMUTEOP ("smacd", 0x2f, sparclet), +COMMUTEOP ("umuld", 0x09, sparclet), +COMMUTEOP ("smuld", 0x0d, sparclet), + +{ "shuffle", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, sparclet }, +{ "shuffle", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, sparclet }, + +/* The manual isn't completely accurate on these insns. The `rs2' field is + treated as being 6 bits to account for 6 bit immediates to cpush. It is + assumed that it is intended that bit 5 is 0 when rs2 contains a reg. */ +#define BIT5 (1<<5) +{ "crdcxt", F3(2, 0x36, 0)|SLCPOP(4), F3(~2, ~0x36, ~0)|SLCPOP(~4)|BIT5|RS2(~0), "U,d", 0, sparclet }, +{ "cwrcxt", F3(2, 0x36, 0)|SLCPOP(3), F3(~2, ~0x36, ~0)|SLCPOP(~3)|BIT5|RS2(~0), "1,u", 0, sparclet }, +{ "cpush", F3(2, 0x36, 0)|SLCPOP(0), F3(~2, ~0x36, ~0)|SLCPOP(~0)|BIT5|RD(~0), "1,2", 0, sparclet }, +{ "cpush", F3(2, 0x36, 1)|SLCPOP(0), F3(~2, ~0x36, ~1)|SLCPOP(~0)|RD(~0), "1,Y", 0, sparclet }, +{ "cpusha", F3(2, 0x36, 0)|SLCPOP(1), F3(~2, ~0x36, ~0)|SLCPOP(~1)|BIT5|RD(~0), "1,2", 0, sparclet }, +{ "cpusha", F3(2, 0x36, 1)|SLCPOP(1), F3(~2, ~0x36, ~1)|SLCPOP(~1)|RD(~0), "1,Y", 0, sparclet }, +{ "cpull", F3(2, 0x36, 0)|SLCPOP(2), F3(~2, ~0x36, ~0)|SLCPOP(~2)|BIT5|RS1(~0)|RS2(~0), "d", 0, sparclet }, +#undef BIT5 + +/* sparclet coprocessor branch insns */ +#define SLCBCC2(opcode, mask, lose) \ + { opcode, (mask), ANNUL|(lose), "l", F_DELAYED|F_CONDBR, sparclet }, \ + { opcode, (mask)|ANNUL, (lose), ",a l", F_DELAYED|F_CONDBR, sparclet } +#define SLCBCC(opcode, mask) \ + SLCBCC2(opcode, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask))) + +/* cbn,cba can't be defined here because they're defined elsewhere and GAS + requires all mnemonics of the same name to be consecutive. */ +/*SLCBCC("cbn", 0), - already defined */ +SLCBCC("cbe", 1), +SLCBCC("cbf", 2), +SLCBCC("cbef", 3), +SLCBCC("cbr", 4), +SLCBCC("cber", 5), +SLCBCC("cbfr", 6), +SLCBCC("cbefr", 7), +/*SLCBCC("cba", 8), - already defined */ +SLCBCC("cbne", 9), +SLCBCC("cbnf", 10), +SLCBCC("cbnef", 11), +SLCBCC("cbnr", 12), +SLCBCC("cbner", 13), +SLCBCC("cbnfr", 14), +SLCBCC("cbnefr", 15), + +#undef SLCBCC2 +#undef SLCBCC + +{ "casa", F3(3, 0x3c, 0), F3(~3, ~0x3c, ~0), "[1]A,2,d", 0, v9 }, +{ "casa", F3(3, 0x3c, 1), F3(~3, ~0x3c, ~1), "[1]o,2,d", 0, v9 }, +{ "casxa", F3(3, 0x3e, 0), F3(~3, ~0x3e, ~0), "[1]A,2,d", 0, v9 }, +{ "casxa", F3(3, 0x3e, 1), F3(~3, ~0x3e, ~1), "[1]o,2,d", 0, v9 }, + +/* v9 synthetic insns */ +/* FIXME: still missing "signx d", and "clruw d". Can't be done here. */ +{ "iprefetch", F2(0, 1)|(2<<20)|BPRED, F2(~0, ~1)|(1<<20)|ANNUL|COND(~0), "G", 0, v9 }, /* bn,a,pt %xcc,label */ +{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* sra rs1,%g0,rd */ +{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* srl rs1,%g0,rd */ +{ "cas", F3(3, 0x3c, 0)|ASI(0x80), F3(~3, ~0x3c, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P,rs2,rd */ +{ "casl", F3(3, 0x3c, 0)|ASI(0x88), F3(~3, ~0x3c, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P_L,rs2,rd */ +{ "casx", F3(3, 0x3e, 0)|ASI(0x80), F3(~3, ~0x3e, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P,rs2,rd */ +{ "casxl", F3(3, 0x3e, 0)|ASI(0x88), F3(~3, ~0x3e, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P_L,rs2,rd */ + +/* Ultrasparc extensions */ +{ "shutdown", F3F(2, 0x36, 0x080), F3F(~2, ~0x36, ~0x080)|RD_G0|RS1_G0|RS2_G0, "", 0, v9a }, + +/* FIXME: Do we want to mark these as F_FLOAT, or something similar? */ +{ "fpadd16", F3F(2, 0x36, 0x050), F3F(~2, ~0x36, ~0x050), "v,B,H", 0, v9a }, +{ "fpadd16s", F3F(2, 0x36, 0x051), F3F(~2, ~0x36, ~0x051), "e,f,g", 0, v9a }, +{ "fpadd32", F3F(2, 0x36, 0x052), F3F(~2, ~0x36, ~0x052), "v,B,H", 0, v9a }, +{ "fpadd32s", F3F(2, 0x36, 0x053), F3F(~2, ~0x36, ~0x053), "e,f,g", 0, v9a }, +{ "fpsub16", F3F(2, 0x36, 0x054), F3F(~2, ~0x36, ~0x054), "v,B,H", 0, v9a }, +{ "fpsub16s", F3F(2, 0x36, 0x055), F3F(~2, ~0x36, ~0x055), "e,f,g", 0, v9a }, +{ "fpsub32", F3F(2, 0x36, 0x056), F3F(~2, ~0x36, ~0x056), "v,B,H", 0, v9a }, +{ "fpsub32s", F3F(2, 0x36, 0x057), F3F(~2, ~0x36, ~0x057), "e,f,g", 0, v9a }, + +{ "fpack32", F3F(2, 0x36, 0x03a), F3F(~2, ~0x36, ~0x03a), "v,B,H", 0, v9a }, +{ "fpack16", F3F(2, 0x36, 0x03b), F3F(~2, ~0x36, ~0x03b)|RS1_G0, "B,g", 0, v9a }, +{ "fpackfix", F3F(2, 0x36, 0x03d), F3F(~2, ~0x36, ~0x03d)|RS1_G0, "B,g", 0, v9a }, +{ "fexpand", F3F(2, 0x36, 0x04d), F3F(~2, ~0x36, ~0x04d)|RS1_G0, "f,H", 0, v9a }, +{ "fpmerge", F3F(2, 0x36, 0x04b), F3F(~2, ~0x36, ~0x04b), "e,f,H", 0, v9a }, + +/* Note that the mixing of 32/64 bit regs is intentional. */ +{ "fmul8x16", F3F(2, 0x36, 0x031), F3F(~2, ~0x36, ~0x031), "e,B,H", 0, v9a }, +{ "fmul8x16au", F3F(2, 0x36, 0x033), F3F(~2, ~0x36, ~0x033), "e,f,H", 0, v9a }, +{ "fmul8x16al", F3F(2, 0x36, 0x035), F3F(~2, ~0x36, ~0x035), "e,f,H", 0, v9a }, +{ "fmul8sux16", F3F(2, 0x36, 0x036), F3F(~2, ~0x36, ~0x036), "v,B,H", 0, v9a }, +{ "fmul8ulx16", F3F(2, 0x36, 0x037), F3F(~2, ~0x36, ~0x037), "v,B,H", 0, v9a }, +{ "fmuld8sux16", F3F(2, 0x36, 0x038), F3F(~2, ~0x36, ~0x038), "e,f,H", 0, v9a }, +{ "fmuld8ulx16", F3F(2, 0x36, 0x039), F3F(~2, ~0x36, ~0x039), "e,f,H", 0, v9a }, + +{ "alignaddr", F3F(2, 0x36, 0x018), F3F(~2, ~0x36, ~0x018), "1,2,d", 0, v9a }, +{ "alignaddrl", F3F(2, 0x36, 0x01a), F3F(~2, ~0x36, ~0x01a), "1,2,d", 0, v9a }, +{ "faligndata", F3F(2, 0x36, 0x048), F3F(~2, ~0x36, ~0x048), "v,B,H", 0, v9a }, + +{ "fzero", F3F(2, 0x36, 0x060), F3F(~2, ~0x36, ~0x060), "H", 0, v9a }, +{ "fzeros", F3F(2, 0x36, 0x061), F3F(~2, ~0x36, ~0x061), "g", 0, v9a }, +{ "fone", F3F(2, 0x36, 0x07e), F3F(~2, ~0x36, ~0x07e), "H", 0, v9a }, +{ "fones", F3F(2, 0x36, 0x07f), F3F(~2, ~0x36, ~0x07f), "g", 0, v9a }, +{ "fsrc1", F3F(2, 0x36, 0x074), F3F(~2, ~0x36, ~0x074), "v,H", 0, v9a }, +{ "fsrc1s", F3F(2, 0x36, 0x075), F3F(~2, ~0x36, ~0x075), "e,g", 0, v9a }, +{ "fsrc2", F3F(2, 0x36, 0x078), F3F(~2, ~0x36, ~0x078), "B,H", 0, v9a }, +{ "fsrc2s", F3F(2, 0x36, 0x079), F3F(~2, ~0x36, ~0x079), "f,g", 0, v9a }, +{ "fnot1", F3F(2, 0x36, 0x06a), F3F(~2, ~0x36, ~0x06a), "v,H", 0, v9a }, +{ "fnot1s", F3F(2, 0x36, 0x06b), F3F(~2, ~0x36, ~0x06b), "e,g", 0, v9a }, +{ "fnot2", F3F(2, 0x36, 0x066), F3F(~2, ~0x36, ~0x066), "B,H", 0, v9a }, +{ "fnot2s", F3F(2, 0x36, 0x067), F3F(~2, ~0x36, ~0x067), "f,g", 0, v9a }, +{ "for", F3F(2, 0x36, 0x07c), F3F(~2, ~0x36, ~0x07c), "v,B,H", 0, v9a }, +{ "fors", F3F(2, 0x36, 0x07d), F3F(~2, ~0x36, ~0x07d), "e,f,g", 0, v9a }, +{ "fnor", F3F(2, 0x36, 0x062), F3F(~2, ~0x36, ~0x062), "v,B,H", 0, v9a }, +{ "fnors", F3F(2, 0x36, 0x063), F3F(~2, ~0x36, ~0x063), "e,f,g", 0, v9a }, +{ "fand", F3F(2, 0x36, 0x070), F3F(~2, ~0x36, ~0x070), "v,B,H", 0, v9a }, +{ "fands", F3F(2, 0x36, 0x071), F3F(~2, ~0x36, ~0x071), "e,f,g", 0, v9a }, +{ "fnand", F3F(2, 0x36, 0x06e), F3F(~2, ~0x36, ~0x06e), "v,B,H", 0, v9a }, +{ "fnands", F3F(2, 0x36, 0x06f), F3F(~2, ~0x36, ~0x06f), "e,f,g", 0, v9a }, +{ "fxor", F3F(2, 0x36, 0x06c), F3F(~2, ~0x36, ~0x06c), "v,B,H", 0, v9a }, +{ "fxors", F3F(2, 0x36, 0x06d), F3F(~2, ~0x36, ~0x06d), "e,f,g", 0, v9a }, +{ "fxnor", F3F(2, 0x36, 0x072), F3F(~2, ~0x36, ~0x072), "v,B,H", 0, v9a }, +{ "fxnors", F3F(2, 0x36, 0x073), F3F(~2, ~0x36, ~0x073), "e,f,g", 0, v9a }, +{ "fornot1", F3F(2, 0x36, 0x07a), F3F(~2, ~0x36, ~0x07a), "v,B,H", 0, v9a }, +{ "fornot1s", F3F(2, 0x36, 0x07b), F3F(~2, ~0x36, ~0x07b), "e,f,g", 0, v9a }, +{ "fornot2", F3F(2, 0x36, 0x076), F3F(~2, ~0x36, ~0x076), "v,B,H", 0, v9a }, +{ "fornot2s", F3F(2, 0x36, 0x077), F3F(~2, ~0x36, ~0x077), "e,f,g", 0, v9a }, +{ "fandnot1", F3F(2, 0x36, 0x068), F3F(~2, ~0x36, ~0x068), "v,B,H", 0, v9a }, +{ "fandnot1s", F3F(2, 0x36, 0x069), F3F(~2, ~0x36, ~0x069), "e,f,g", 0, v9a }, +{ "fandnot2", F3F(2, 0x36, 0x064), F3F(~2, ~0x36, ~0x064), "v,B,H", 0, v9a }, +{ "fandnot2s", F3F(2, 0x36, 0x065), F3F(~2, ~0x36, ~0x065), "e,f,g", 0, v9a }, + +{ "fcmpgt16", F3F(2, 0x36, 0x028), F3F(~2, ~0x36, ~0x028), "v,B,d", 0, v9a }, +{ "fcmpgt32", F3F(2, 0x36, 0x02c), F3F(~2, ~0x36, ~0x02c), "v,B,d", 0, v9a }, +{ "fcmple16", F3F(2, 0x36, 0x020), F3F(~2, ~0x36, ~0x020), "v,B,d", 0, v9a }, +{ "fcmple32", F3F(2, 0x36, 0x024), F3F(~2, ~0x36, ~0x024), "v,B,d", 0, v9a }, +{ "fcmpne16", F3F(2, 0x36, 0x022), F3F(~2, ~0x36, ~0x022), "v,B,d", 0, v9a }, +{ "fcmpne32", F3F(2, 0x36, 0x026), F3F(~2, ~0x36, ~0x026), "v,B,d", 0, v9a }, +{ "fcmpeq16", F3F(2, 0x36, 0x02a), F3F(~2, ~0x36, ~0x02a), "v,B,d", 0, v9a }, +{ "fcmpeq32", F3F(2, 0x36, 0x02e), F3F(~2, ~0x36, ~0x02e), "v,B,d", 0, v9a }, + +{ "edge8", F3F(2, 0x36, 0x000), F3F(~2, ~0x36, ~0x000), "1,2,d", 0, v9a }, +{ "edge8l", F3F(2, 0x36, 0x002), F3F(~2, ~0x36, ~0x002), "1,2,d", 0, v9a }, +{ "edge16", F3F(2, 0x36, 0x004), F3F(~2, ~0x36, ~0x004), "1,2,d", 0, v9a }, +{ "edge16l", F3F(2, 0x36, 0x006), F3F(~2, ~0x36, ~0x006), "1,2,d", 0, v9a }, +{ "edge32", F3F(2, 0x36, 0x008), F3F(~2, ~0x36, ~0x008), "1,2,d", 0, v9a }, +{ "edge32l", F3F(2, 0x36, 0x00a), F3F(~2, ~0x36, ~0x00a), "1,2,d", 0, v9a }, + +{ "pdist", F3F(2, 0x36, 0x03e), F3F(~2, ~0x36, ~0x03e), "v,B,H", 0, v9a }, + +{ "array8", F3F(2, 0x36, 0x010), F3F(~2, ~0x36, ~0x010), "1,2,d", 0, v9a }, +{ "array16", F3F(2, 0x36, 0x012), F3F(~2, ~0x36, ~0x012), "1,2,d", 0, v9a }, +{ "array32", F3F(2, 0x36, 0x014), F3F(~2, ~0x36, ~0x014), "1,2,d", 0, v9a }, + +/* More v9 specific insns, these need to come last so they do not clash + with v9a instructions such as "edge8" which looks like impdep1. */ + +#define IMPDEP(name, code) \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0)|ASI(~0), "1,2,d", 0, v9notv9a }, \ +{ name, F3(2, code, 1), F3(~2, ~code, ~1), "1,i,d", 0, v9notv9a }, \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,1,2,d", 0, v9notv9a }, \ +{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,e,f,g", 0, v9notv9a } + +IMPDEP ("impdep1", 0x36), +IMPDEP ("impdep2", 0x37), + +#undef IMPDEP + +}; + +const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0])); + +/* Utilities for argument parsing. */ + +typedef struct +{ + int value; + const char *name; +} arg; + +/* Look up NAME in TABLE. */ + +static int lookup_name PARAMS ((const arg *, const char *)); +static const char *lookup_value PARAMS ((const arg *, int)); + +static int +lookup_name (table, name) + const arg *table; + const char *name; +{ + const arg *p; + + for (p = table; p->name; ++p) + if (strcmp (name, p->name) == 0) + return p->value; + + return -1; +} + +/* Look up VALUE in TABLE. */ + +static const char * +lookup_value (table, value) + const arg *table; + int value; +{ + const arg *p; + + for (p = table; p->name; ++p) + if (value == p->value) + return p->name; + + return (char *) 0; +} + +/* Handle ASI's. */ + +static arg asi_table[] = +{ + /* These are in the v9 architecture manual. */ + /* The shorter versions appear first, they're here because Sun's as has them. + Sun's as uses #ASI_P_L instead of #ASI_PL (which appears in the + UltraSPARC architecture manual). */ + { 0x04, "#ASI_N" }, + { 0x0c, "#ASI_N_L" }, + { 0x10, "#ASI_AIUP" }, + { 0x11, "#ASI_AIUS" }, + { 0x18, "#ASI_AIUP_L" }, + { 0x19, "#ASI_AIUS_L" }, + { 0x80, "#ASI_P" }, + { 0x81, "#ASI_S" }, + { 0x82, "#ASI_PNF" }, + { 0x83, "#ASI_SNF" }, + { 0x88, "#ASI_P_L" }, + { 0x89, "#ASI_S_L" }, + { 0x8a, "#ASI_PNF_L" }, + { 0x8b, "#ASI_SNF_L" }, + { 0x04, "#ASI_NUCLEUS" }, + { 0x0c, "#ASI_NUCLEUS_LITTLE" }, + { 0x10, "#ASI_AS_IF_USER_PRIMARY" }, + { 0x11, "#ASI_AS_IF_USER_SECONDARY" }, + { 0x18, "#ASI_AS_IF_USER_PRIMARY_LITTLE" }, + { 0x19, "#ASI_AS_IF_USER_SECONDARY_LITTLE" }, + { 0x80, "#ASI_PRIMARY" }, + { 0x81, "#ASI_SECONDARY" }, + { 0x82, "#ASI_PRIMARY_NOFAULT" }, + { 0x83, "#ASI_SECONDARY_NOFAULT" }, + { 0x88, "#ASI_PRIMARY_LITTLE" }, + { 0x89, "#ASI_SECONDARY_LITTLE" }, + { 0x8a, "#ASI_PRIMARY_NOFAULT_LITTLE" }, + { 0x8b, "#ASI_SECONDARY_NOFAULT_LITTLE" }, + /* These are UltraSPARC extensions. */ + /* FIXME: There are dozens of them. Not sure we want them all. + Most are for kernel building but some are for vis type stuff. */ + { 0, 0 } +}; + +/* Return the value for ASI NAME, or -1 if not found. */ + +int +sparc_encode_asi (name) + const char *name; +{ + return lookup_name (asi_table, name); +} + +/* Return the name for ASI value VALUE or NULL if not found. */ + +const char * +sparc_decode_asi (value) + int value; +{ + return lookup_value (asi_table, value); +} + +/* Handle membar masks. */ + +static arg membar_table[] = +{ + { 0x40, "#Sync" }, + { 0x20, "#MemIssue" }, + { 0x10, "#Lookaside" }, + { 0x08, "#StoreStore" }, + { 0x04, "#LoadStore" }, + { 0x02, "#StoreLoad" }, + { 0x01, "#LoadLoad" }, + { 0, 0 } +}; + +/* Return the value for membar arg NAME, or -1 if not found. */ + +int +sparc_encode_membar (name) + const char *name; +{ + return lookup_name (membar_table, name); +} + +/* Return the name for membar value VALUE or NULL if not found. */ + +const char * +sparc_decode_membar (value) + int value; +{ + return lookup_value (membar_table, value); +} + +/* Handle prefetch args. */ + +static arg prefetch_table[] = +{ + { 0, "#n_reads" }, + { 1, "#one_read" }, + { 2, "#n_writes" }, + { 3, "#one_write" }, + { 4, "#page" }, + { 0, 0 } +}; + +/* Return the value for prefetch arg NAME, or -1 if not found. */ + +int +sparc_encode_prefetch (name) + const char *name; +{ + return lookup_name (prefetch_table, name); +} + +/* Return the name for prefetch value VALUE or NULL if not found. */ + +const char * +sparc_decode_prefetch (value) + int value; +{ + return lookup_value (prefetch_table, value); +} + +/* Handle sparclet coprocessor registers. */ + +static arg sparclet_cpreg_table[] = +{ + { 0, "%ccsr" }, + { 1, "%ccfr" }, + { 2, "%cccrcr" }, + { 3, "%ccpr" }, + { 4, "%ccsr2" }, + { 5, "%cccrr" }, + { 6, "%ccrstr" }, + { 0, 0 } +}; + +/* Return the value for sparclet cpreg arg NAME, or -1 if not found. */ + +int +sparc_encode_sparclet_cpreg (name) + const char *name; +{ + return lookup_name (sparclet_cpreg_table, name); +} + +/* Return the name for sparclet cpreg value VALUE or NULL if not found. */ + +const char * +sparc_decode_sparclet_cpreg (value) + int value; +{ + return lookup_value (sparclet_cpreg_table, value); +} |