+/* Sort of like s_lcomm. */
+
+#ifndef OBJ_ELF
+static int max_alignment = 15;
+#endif
+
+static void
+s_reserve (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ 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"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = (int) get_absolute_expression ();
+
+#ifndef OBJ_ELF
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("alignment too large; assuming %d"), align);
+ }
+#endif
+
+ if (align < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (align != 0)
+ {
+ temp = log2 (align);
+ if (temp < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = temp;
+ }
+
+ record_alignment (bss_section, align);
+ }
+ 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;
+
+ /* Switch to bss. */
+ subseg_set (bss_section, 1);
+
+ if (align)
+ /* Do alignment. */
+ frag_align (align, 0, 0);
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = NULL;
+
+ symbol_set_frag (symbolP, 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);
+
+#ifdef OBJ_ELF
+ S_SET_SIZE (symbolP, size);
+#endif
+ }
+ }
+ 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 ATTRIBUTE_UNUSED;
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT 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;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_bad (_(".COMMon length (%lu) out of range ignored"),
+ (unsigned long) 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 %ld."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), (long) size);
+ }
+ }
+ else
+ {
+#ifndef OBJ_ELF
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_EXTERNAL (symbolP);
+#endif
+ }
+ know (symbol_get_frag (symbolP) == &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 (_("alignment too large; assuming %d"), temp);
+ }
+#endif
+
+ if (temp < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+#ifdef OBJ_ELF
+ if (symbol_get_obj (symbolP)->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *p;
+ int align;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ if (temp == 0)
+ align = 0;
+ else
+ align = log2 (temp);
+
+ if (align < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0);
+ if (align)
+ frag_align (align, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, 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);
+ S_SET_SIZE (symbolP, size);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+#endif /* OBJ_ELF */
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+#ifdef OBJ_ELF
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_SIZE (symbolP, size);
+#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
+ symbol_get_bfdsym (symbolP)->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 ATTRIBUTE_UNUSED;
+{
+ /* The easy way to implement is to just forget about the last
+ instruction. */
+ last_insn = NULL;
+}
+
+static void
+s_seg (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+
+ 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 ATTRIBUTE_UNUSED;
+{
+ 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 static variable is set by sparc_cons to emit requested types
+ of relocations in cons_fix_new_sparc. */
+
+static const char *sparc_cons_special_reloc;
+
+/* 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);
+ sparc_no_align_cons = 0;
+}
+
+/* This handles the native word allocation pseudo-op .nword.
+ For sparc_arch_size 32 it is equivalent to .word, for
+ sparc_arch_size 64 it is equivalent to .xword. */
+
+static void
+s_ncons (bytes)
+ int bytes ATTRIBUTE_UNUSED;
+{
+ cons (sparc_arch_size == 32 ? 4 : 8);
+}
+
+#ifdef OBJ_ELF
+/* Handle the SPARC ELF .register pseudo-op. This sets the binding of a
+ global register.
+ The syntax is:
+
+ .register %g[2367],{#scratch|symbolname|#ignore}
+*/
+
+static void
+s_register (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ char c;
+ int reg;
+ int flags;
+ const char *regname;
+
+ if (input_line_pointer[0] != '%'
+ || input_line_pointer[1] != 'g'
+ || ((input_line_pointer[2] & ~1) != '2'
+ && (input_line_pointer[2] & ~1) != '6')
+ || input_line_pointer[3] != ',')
+ as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
+ reg = input_line_pointer[2] - '0';
+ input_line_pointer += 4;
+
+ if (*input_line_pointer == '#')
+ {
+ ++input_line_pointer;
+ regname = input_line_pointer;
+ c = get_symbol_end ();
+ if (strcmp (regname, "scratch") && strcmp (regname, "ignore"))
+ as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
+ if (regname[0] == 'i')
+ regname = NULL;
+ else
+ regname = "";
+ }
+ else
+ {
+ regname = input_line_pointer;
+ c = get_symbol_end ();
+ }
+ if (sparc_arch_size == 64)
+ {
+ if (globals[reg])
+ {
+ if ((regname && globals[reg] != (symbolS *) 1
+ && strcmp (S_GET_NAME (globals[reg]), regname))
+ || ((regname != NULL) ^ (globals[reg] != (symbolS *) 1)))
+ as_bad (_("redefinition of global register"));
+ }
+ else
+ {
+ if (regname == NULL)
+ globals[reg] = (symbolS *) 1;
+ else
+ {
+ if (*regname)
+ {
+ if (symbol_find (regname))
+ as_bad (_("Register symbol %s already defined."),
+ regname);
+ }
+ globals[reg] = symbol_make (regname);
+ flags = symbol_get_bfdsym (globals[reg])->flags;
+ if (! *regname)
+ flags = flags & ~(BSF_GLOBAL|BSF_LOCAL|BSF_WEAK);
+ if (! (flags & (BSF_GLOBAL|BSF_LOCAL|BSF_WEAK)))
+ flags |= BSF_GLOBAL;
+ symbol_get_bfdsym (globals[reg])->flags = flags;
+ S_SET_VALUE (globals[reg], (valueT) reg);
+ S_SET_ALIGN (globals[reg], reg);
+ S_SET_SIZE (globals[reg], 0);
+ /* Although we actually want undefined_section here,
+ we have to use absolute_section, because otherwise
+ generic as code will make it a COM section.
+ We fix this up in sparc_adjust_symtab. */
+ S_SET_SEGMENT (globals[reg], absolute_section);
+ S_SET_OTHER (globals[reg], 0);
+ elf_symbol (symbol_get_bfdsym (globals[reg]))
+ ->internal_elf_sym.st_info =
+ ELF_ST_INFO(STB_GLOBAL, STT_REGISTER);
+ elf_symbol (symbol_get_bfdsym (globals[reg]))
+ ->internal_elf_sym.st_shndx = SHN_UNDEF;
+ }
+ }
+ }
+
+ *input_line_pointer = c;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Adjust the symbol table. We set undefined sections for STT_REGISTER
+ symbols which need it. */
+
+void
+sparc_adjust_symtab ()
+{
+ symbolS *sym;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+ ->internal_elf_sym.st_info) != STT_REGISTER)
+ continue;
+
+ if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+ ->internal_elf_sym.st_shndx != SHN_UNDEF))
+ continue;
+
+ S_SET_SEGMENT (sym, undefined_section);
+ }
+}
+#endif
+
+/* 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;
+
+ /* Don't align if this is an unaligned pseudo-op. */
+ if (sparc_no_align_cons)
+ return;
+
+ nalign = log2 (nbytes);
+ if (nalign == 0)
+ return;
+
+ assert (nalign > 0);
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_bad (_("misaligned data"));
+ return;
+ }
+
+ p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
+/* This is called from HANDLE_ALIGN in tc-sparc.h. */
+
+void
+sparc_handle_align (fragp)
+ fragS *fragp;
+{
+ int count, fix;
+ char *p;
+
+ count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+
+ switch (fragp->fr_type)
+ {
+ case rs_align_test:
+ if (count != 0)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("misaligned data"));
+ break;
+
+ case rs_align_code:
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (count & 3)
+ {
+ fix = count & 3;
+ memset (p, 0, fix);
+ p += fix;
+ count -= fix;
+ }
+
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+ {
+ unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (p, wval, 4);
+ else
+ number_to_chars_littleendian (p, wval, 4);
+ p += 4;
+ count -= 4;
+ fix += 4;
+ }
+
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (p, 0x01000000, 4);
+ else
+ number_to_chars_littleendian (p, 0x01000000, 4);
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 4;
+ break;
+
+ default:
+ break;
+ }
+}
+
+#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;
+ default:
+ 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;
+ else if (current_architecture == SPARC_OPCODE_ARCH_V9B)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1|EF_SPARC_SUN_US3;
+}
+
+void
+sparc_cons (exp, size)
+ expressionS *exp;
+ int size;
+{
+ char *save;
+
+ SKIP_WHITESPACE ();
+ sparc_cons_special_reloc = NULL;
+ save = input_line_pointer;
+ if (input_line_pointer[0] == '%'
+ && input_line_pointer[1] == 'r'
+ && input_line_pointer[2] == '_')
+ {
+ if (strncmp (input_line_pointer + 3, "disp", 4) == 0)
+ {
+ input_line_pointer += 7;
+ sparc_cons_special_reloc = "disp";
+ }
+ else if (strncmp (input_line_pointer + 3, "plt", 3) == 0)
+ {
+ if (size != 4 && size != 8)
+ as_bad (_("Illegal operands: %%r_plt in %d-byte data field"), size);
+ else
+ {
+ input_line_pointer += 6;
+ sparc_cons_special_reloc = "plt";
+ }
+ }
+ else if (strncmp (input_line_pointer + 3, "tls_dtpoff", 10) == 0)
+ {
+ if (size != 4 && size != 8)
+ as_bad (_("Illegal operands: %%r_tls_dtpoff in %d-byte data field"), size);
+ else
+ {
+ input_line_pointer += 13;
+ sparc_cons_special_reloc = "tls_dtpoff";
+ }
+ }
+ if (sparc_cons_special_reloc)
+ {
+ int bad = 0;
+
+ switch (size)
+ {
+ case 1:
+ if (*input_line_pointer != '8')
+ bad = 1;
+ input_line_pointer--;
+ break;
+ case 2:
+ if (input_line_pointer[0] != '1' || input_line_pointer[1] != '6')
+ bad = 1;
+ break;
+ case 4:
+ if (input_line_pointer[0] != '3' || input_line_pointer[1] != '2')
+ bad = 1;
+ break;
+ case 8:
+ if (input_line_pointer[0] != '6' || input_line_pointer[1] != '4')
+ bad = 1;
+ break;
+ default:
+ bad = 1;
+ break;
+ }
+
+ if (bad)
+ {
+ as_bad (_("Illegal operands: Only %%r_%s%d allowed in %d-byte data fields"),
+ sparc_cons_special_reloc, size * 8, size);
+ }
+ else
+ {
+ input_line_pointer += 2;
+ if (*input_line_pointer != '(')
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ bad = 1;
+ }
+ }
+
+ if (bad)
+ {
+ input_line_pointer = save;
+ sparc_cons_special_reloc = NULL;
+ }
+ else
+ {
+ int c;
+ char *end = ++input_line_pointer;
+ int npar = 0;
+
+ while (! is_end_of_line[(c = *end)])
+ {
+ if (c == '(')
+ npar++;
+ else if (c == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+ end++;
+ }
+
+ if (c != ')')
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ else
+ {
+ *end = '\0';
+ expression (exp);
+ *end = c;
+ if (input_line_pointer != end)
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ }
+ else
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (! is_end_of_line[c] && c != ',')
+ as_bad (_("Illegal operands: garbage after %%r_%s%d()"),
+ sparc_cons_special_reloc, size * 8);
+ }
+ }
+ }
+ }
+ }
+ if (sparc_cons_special_reloc == NULL)
+ expression (exp);
+}
+
+#endif
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. We could use the definition there, except that
+ we want to handle little endian relocs specially. */
+
+void
+cons_fix_new_sparc (frag, where, nbytes, exp)
+ fragS *frag;
+ int where;
+ unsigned int nbytes;
+ expressionS *exp;
+{
+ bfd_reloc_code_real_type r;
+
+ r = (nbytes == 1 ? BFD_RELOC_8 :
+ (nbytes == 2 ? BFD_RELOC_16 :
+ (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+
+ if (target_little_endian_data
+ && nbytes == 4
+ && now_seg->flags & SEC_ALLOC)
+ r = BFD_RELOC_SPARC_REV32;
+
+ if (sparc_cons_special_reloc)
+ {
+ if (*sparc_cons_special_reloc == 'd')
+ switch (nbytes)
+ {
+ case 1: r = BFD_RELOC_8_PCREL; break;
+ case 2: r = BFD_RELOC_16_PCREL; break;
+ case 4: r = BFD_RELOC_32_PCREL; break;
+ case 8: r = BFD_RELOC_64_PCREL; break;
+ default: abort ();
+ }
+ else if (*sparc_cons_special_reloc == 'p')
+ switch (nbytes)
+ {
+ case 4: r = BFD_RELOC_SPARC_PLT32; break;
+ case 8: r = BFD_RELOC_SPARC_PLT64; break;
+ }
+ else
+ switch (nbytes)
+ {
+ case 4: r = BFD_RELOC_SPARC_TLS_DTPOFF32; break;
+ case 8: r = BFD_RELOC_SPARC_TLS_DTPOFF64; break;
+ }
+ }
+ else if (sparc_no_align_cons)
+ {
+ switch (nbytes)
+ {
+ case 2: r = BFD_RELOC_SPARC_UA16; break;
+ case 4: r = BFD_RELOC_SPARC_UA32; break;
+ case 8: r = BFD_RELOC_SPARC_UA64; break;
+ default: abort ();
+ }
+ }
+
+ fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+ sparc_cons_special_reloc = NULL;
+}
+
+void
+sparc_cfi_frame_initial_instructions ()
+{
+ cfi_add_CFA_def_cfa (14, sparc_arch_size == 64 ? 0x7ff : 0);
+}
+
+int
+sparc_regname_to_dw2regnum (const char *regname)
+{
+ char *p, *q;
+
+ if (!regname[0])
+ return -1;
+
+ q = "goli";
+ p = strchr (q, regname[0]);
+ if (p)
+ {
+ if (regname[1] < '0' || regname[1] > '8' || regname[2])
+ return -1;
+ return (p - q) * 8 + regname[1] - '0';
+ }
+ if (regname[0] == 's' && regname[1] == 'p' && !regname[2])
+ return 14;
+ if (regname[0] == 'f' && regname[1] == 'p' && !regname[2])
+ return 30;
+ if (regname[0] == 'f' || regname[0] == 'r')
+ {
+ unsigned int regnum;
+
+ regnum = strtoul (regname + 1, &q, 10);
+ if (p == q || *q)
+ return -1;
+ if (regnum >= ((regname[0] == 'f'
+ && SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ ? 64 : 32))
+ return -1;
+ if (regname[0] == 'f')
+ {
+ regnum += 32;
+ if (regnum >= 64 && (regnum & 1))
+ return -1;
+ }
+ return regnum;
+ }
+ return -1;
+}
+
+void
+sparc_cfi_emit_pcrel_expr (expressionS *exp, unsigned int nbytes)
+{
+ sparc_cons_special_reloc = "disp";
+ sparc_no_align_cons = 1;
+ emit_expr (exp, nbytes);
+ sparc_no_align_cons = 0;
+ sparc_cons_special_reloc = NULL;
+}