+/*
+ * 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"));
+ 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;
+
+ 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)
+ 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;
+{
+ 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 (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;
+{
+ /* 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);
+}
+
+/* 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;
+{
+ cons (sparc_arch_size == 32 ? 4 : 8);
+}
+
+/* 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 = 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_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++)
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian ((char *)p, 0x01000000, 4); /* emit nops */
+ else
+ number_to_chars_littleendian ((char *)p, 0x10000000, 4);
+
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+ {
+ char *waddr = &fragp->fr_literal[fragp->fr_fix];
+ unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (waddr, wval, 4);
+ else
+ number_to_chars_littleendian (waddr, wval, 4);
+ }
+ 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;
+ 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;
+}
+#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;
+ fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+#ifdef OBJ_ELF
+int
+elf32_sparc_force_relocation (fixp)
+ struct fix *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
+#endif
+