+ bfd_vma vma;
+
+ if (!sec)
+ return;
+ vma = bfd_get_section_vma (foo, sec);
+ if (vma && vma < alpha_gp_value)
+ alpha_gp_value = vma;
+}
+
+static void
+select_gp_value (void)
+{
+ assert (alpha_gp_value == 0);
+
+ /* Get minus-one in whatever width... */
+ alpha_gp_value = 0;
+ alpha_gp_value--;
+
+ /* Select the smallest VMA of these existing sections. */
+ maybe_set_gp (alpha_lita_section);
+
+/* @@ Will a simple 0x8000 work here? If not, why not? */
+#define GP_ADJUSTMENT (0x8000 - 0x10)
+
+ alpha_gp_value += GP_ADJUSTMENT;
+
+ S_SET_VALUE (alpha_gp_symbol, alpha_gp_value);
+
+#ifdef DEBUG1
+ printf (_("Chose GP value of %lx\n"), alpha_gp_value);
+#endif
+}
+#endif /* OBJ_ECOFF */
+
+#ifdef OBJ_ELF
+/* Map 's' to SHF_ALPHA_GPREL. */
+
+int
+alpha_elf_section_letter (int letter, char **ptr_msg)
+{
+ if (letter == 's')
+ return SHF_ALPHA_GPREL;
+
+ *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
+ return -1;
+}
+
+/* Map SHF_ALPHA_GPREL to SEC_SMALL_DATA. */
+
+flagword
+alpha_elf_section_flags (flagword flags, int attr, int type ATTRIBUTE_UNUSED)
+{
+ if (attr & SHF_ALPHA_GPREL)
+ flags |= SEC_SMALL_DATA;
+ return flags;
+}
+#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+alpha_handle_align (fragS *fragp)
+{
+ static char const unop[4] = { 0x00, 0x00, 0xfe, 0x2f };
+ static char const nopunop[8] =
+ {
+ 0x1f, 0x04, 0xff, 0x47,
+ 0x00, 0x00, 0xfe, 0x2f
+ };
+
+ int bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (bytes & 3)
+ {
+ fix = bytes & 3;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ if (bytes & 4)
+ {
+ memcpy (p, unop, 4);
+ p += 4;
+ bytes -= 4;
+ fix += 4;
+ }
+
+ memcpy (p, nopunop, 8);
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 8;
+}
+\f
+/* Public interface functions. */
+
+/* This function is called once, at assembler startup time. It sets
+ up all the tables, etc. that the MD part of the assembler will
+ need, that can be determined before arguments are parsed. */
+
+void
+md_begin (void)
+{
+ unsigned int i;
+
+ /* Verify that X_op field is wide enough. */
+ {
+ expressionS e;
+
+ e.X_op = O_max;
+ assert (e.X_op == O_max);
+ }
+
+ /* Create the opcode hash table. */
+ alpha_opcode_hash = hash_new ();
+
+ for (i = 0; i < alpha_num_opcodes;)
+ {
+ const char *name, *retval, *slash;
+
+ name = alpha_opcodes[i].name;
+ retval = hash_insert (alpha_opcode_hash, name, (void *) &alpha_opcodes[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash opcode `%s': %s"),
+ name, retval);
+
+ /* Some opcodes include modifiers of various sorts with a "/mod"
+ syntax, like the architecture manual suggests. However, for
+ use with gcc at least, we also need access to those same opcodes
+ without the "/". */
+
+ if ((slash = strchr (name, '/')) != NULL)
+ {
+ char *p = xmalloc (strlen (name));
+
+ memcpy (p, name, slash - name);
+ strcpy (p + (slash - name), slash + 1);
+
+ (void) hash_insert (alpha_opcode_hash, p, (void *) &alpha_opcodes[i]);
+ /* Ignore failures -- the opcode table does duplicate some
+ variants in different forms, like "hw_stq" and "hw_st/q". */
+ }
+
+ while (++i < alpha_num_opcodes
+ && (alpha_opcodes[i].name == name
+ || !strcmp (alpha_opcodes[i].name, name)))
+ continue;
+ }
+
+ /* Create the macro hash table. */
+ alpha_macro_hash = hash_new ();
+
+ for (i = 0; i < alpha_num_macros;)
+ {
+ const char *name, *retval;
+
+ name = alpha_macros[i].name;
+ retval = hash_insert (alpha_macro_hash, name, (void *) &alpha_macros[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash macro `%s': %s"),
+ name, retval);
+
+ while (++i < alpha_num_macros
+ && (alpha_macros[i].name == name
+ || !strcmp (alpha_macros[i].name, name)))
+ continue;
+ }
+
+ /* Construct symbols for each of the registers. */
+ for (i = 0; i < 32; ++i)
+ {
+ char name[4];
+
+ sprintf (name, "$%d", i);
+ alpha_register_table[i] = symbol_create (name, reg_section, i,
+ &zero_address_frag);
+ }
+
+ for (; i < 64; ++i)
+ {
+ char name[5];
+
+ sprintf (name, "$f%d", i - 32);
+ alpha_register_table[i] = symbol_create (name, reg_section, i,
+ &zero_address_frag);
+ }
+
+ /* Create the special symbols and sections we'll be using. */
+
+ /* So .sbss will get used for tiny objects. */
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+#ifdef OBJ_ECOFF
+ create_literal_section (".lita", &alpha_lita_section, &alpha_lita_symbol);
+
+ /* For handling the GP, create a symbol that won't be output in the
+ symbol table. We'll edit it out of relocs later. */
+ alpha_gp_symbol = symbol_create ("<GP value>", alpha_lita_section, 0x8000,
+ &zero_address_frag);
+#endif
+
+#ifdef OBJ_EVAX
+ create_literal_section (".link", &alpha_link_section, &alpha_link_symbol);
+#endif
+
+#ifdef OBJ_ELF
+ if (ECOFF_DEBUGGING)
+ {
+ segT sec = subseg_new (".mdebug", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, SEC_HAS_CONTENTS | SEC_READONLY);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+ }
+#endif
+
+ /* Create literal lookup hash table. */
+ alpha_literal_hash = hash_new ();
+
+ subseg_set (text_section, 0);
+}
+
+/* The public interface to the instruction assembler. */
+
+void
+md_assemble (char *str)
+{
+ /* Current maximum is 13. */
+ char opname[32];
+ expressionS tok[MAX_INSN_ARGS];
+ int ntok, trunclen;
+ size_t opnamelen;
+
+ /* Split off the opcode. */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/46819");
+ trunclen = (opnamelen < sizeof (opname) - 1
+ ? opnamelen
+ : sizeof (opname) - 1);
+ memcpy (opname, str, trunclen);
+ opname[trunclen] = '\0';
+
+ /* Tokenize the rest of the line. */
+ if ((ntok = tokenize_arguments (str + opnamelen, tok, MAX_INSN_ARGS)) < 0)
+ {
+ if (ntok != TOKENIZE_ERROR_REPORT)
+ as_bad (_("syntax error"));
+
+ return;
+ }
+
+ /* Finish it off. */
+ assemble_tokens (opname, tok, ntok, alpha_macros_on);
+}
+
+/* Round up a section's size to the appropriate boundary. */
+
+valueT
+md_section_align (segT seg, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ valueT mask = ((valueT) 1 << align) - 1;
+
+ return (size + mask) & ~mask;
+}
+
+/* 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
+
+extern char *vax_md_atof (int, char *, int *);
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ /* VAX floats. */
+ case 'G':
+ /* VAX md_atof doesn't like "G" for some reason. */
+ type = 'g';
+ case 'F':
+ case 'D':
+ return vax_md_atof (type, litP, sizeP);
+
+ /* IEEE floats. */
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ 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);
+
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+/* Take care of the target-specific command-line options. */
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'F':
+ alpha_nofloats_on = 1;
+ break;
+
+ case OPTION_32ADDR:
+ alpha_addr32_on = 1;
+ break;
+
+ case 'g':
+ alpha_debug = 1;
+ break;
+
+ case 'G':
+ g_switch_value = atoi (arg);
+ break;
+
+ case 'm':
+ {
+ const struct cpu_type *p;
+
+ for (p = cpu_types; p->name; ++p)
+ if (strcmp (arg, p->name) == 0)
+ {
+ alpha_target_name = p->name, alpha_target = p->flags;
+ goto found;
+ }
+ as_warn (_("Unknown CPU identifier `%s'"), arg);
+ found:;
+ }
+ break;
+
+#ifdef OBJ_EVAX
+ case '+': /* For g++. Hash any name > 63 chars long. */
+ alpha_flag_hash_long_names = 1;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation. */
+ alpha_flag_show_after_trunc = 1;
+ break;
+
+ case 'h': /* For gnu-c/vax compatibility. */
+ break;
+#endif
+
+ case OPTION_RELAX:
+ alpha_flag_relax = 1;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_MDEBUG:
+ alpha_flag_mdebug = 1;
+ break;
+ case OPTION_NO_MDEBUG:
+ alpha_flag_mdebug = 0;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Print a description of the command-line options that we accept. */
+
+void
+md_show_usage (FILE *stream)
+{
+ fputs (_("\
+Alpha options:\n\
+-32addr treat addresses as 32-bit values\n\
+-F lack floating point instructions support\n\
+-mev4 | -mev45 | -mev5 | -mev56 | -mpca56 | -mev6 | -mev67 | -mev68 | -mall\n\
+ specify variant of Alpha architecture\n\
+-m21064 | -m21066 | -m21164 | -m21164a | -m21164pc | -m21264 | -m21264a | -m21264b\n\
+ these variants include PALcode opcodes\n"),
+ stream);
+#ifdef OBJ_EVAX
+ fputs (_("\
+VMS options:\n\
+-+ hash encode (don't truncate) names longer than 64 characters\n\
+-H show new symbol after hash truncation\n"),
+ stream);
+#endif
+}
+
+/* Decide from what point a pc-relative relocation is relative to,
+ relative to the pc-relative fixup. Er, relatively speaking. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_ALPHA_HINT:
+ case BFD_RELOC_ALPHA_BRSGP:
+ return addr + 4;
+ default:
+ return addr;
+ }
+}
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+
+ For ELF, here it is that we transform the GPDISP_HI16 reloc we used
+ internally into the GPDISP reloc used externally. We had to do
+ this so that we'd have the GPDISP_LO16 reloc as a tag to compute
+ the distance to the "lda" instruction for setting the addend to
+ GPDISP. */
+
+void
+md_apply_fix3 (fixS *fixP, valueT * valP, segT seg)
+{
+ char * const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = * valP;
+ unsigned image, size;
+
+ switch (fixP->fx_r_type)
+ {
+ /* The GPDISP relocations are processed internally with a symbol
+ referring to the current function's section; we need to drop
+ in a value which, when added to the address of the start of
+ the function, gives the desired GP. */
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ {
+ fixS *next = fixP->fx_next;
+
+ /* With user-specified !gpdisp relocations, we can be missing
+ the matching LO16 reloc. We will have already issued an
+ error message. */
+ if (next)
+ fixP->fx_offset = (next->fx_frag->fr_address + next->fx_where
+ - fixP->fx_frag->fr_address - fixP->fx_where);
+
+ value = (value - sign_extend_16 (value)) >> 16;
+ }
+#ifdef OBJ_ELF
+ fixP->fx_r_type = BFD_RELOC_ALPHA_GPDISP;
+#endif
+ goto do_reloc_gp;
+
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ value = sign_extend_16 (value);
+ fixP->fx_offset = 0;
+#ifdef OBJ_ELF
+ fixP->fx_done = 1;
+#endif
+
+ do_reloc_gp:
+ fixP->fx_addsy = section_symbol (seg);
+ md_number_to_chars (fixpos, value, 2);
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ size = 2;
+ goto do_reloc_xx;
+
+ case BFD_RELOC_32:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ size = 4;
+ goto do_reloc_xx;
+
+ case BFD_RELOC_64:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ size = 8;
+
+ do_reloc_xx:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ md_number_to_chars (fixpos, value, size);
+ goto done;
+ }
+ return;
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_GPREL32:
+ assert (fixP->fx_subsy == alpha_gp_symbol);
+ fixP->fx_subsy = 0;
+ /* FIXME: inherited this obliviousness of `value' -- why? */
+ md_number_to_chars (fixpos, -alpha_gp_value, 4);
+ break;
+#else
+ case BFD_RELOC_GPREL32:
+#endif
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ return;
+
+ case BFD_RELOC_23_PCREL_S2:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32 (fixpos);
+ image = (image & ~0x1FFFFF) | ((value >> 2) & 0x1FFFFF);
+ goto write_done;
+ }
+ return;
+
+ case BFD_RELOC_ALPHA_HINT:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32 (fixpos);
+ image = (image & ~0x3FFF) | ((value >> 2) & 0x3FFF);
+ goto write_done;
+ }
+ return;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_BRSGP:
+ return;
+
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+ if (fixP->fx_addsy)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ return;
+#endif
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+ md_number_to_chars (fixpos, value, 2);
+ return;
+#endif
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ return;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return;
+
+ default:
+ {
+ const struct alpha_operand *operand;
+
+ if ((int) fixP->fx_r_type >= 0)
+ as_fatal (_("unhandled relocation type %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+
+ assert (-(int) fixP->fx_r_type < (int) alpha_num_operands);
+ operand = &alpha_operands[-(int) fixP->fx_r_type];
+
+ /* The rest of these fixups only exist internally during symbol
+ resolution and have no representation in the object file.
+ Therefore they must be completely resolved as constants. */
+
+ if (fixP->fx_addsy != 0
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("non-absolute expression in constant field"));
+
+ image = bfd_getl32 (fixpos);
+ image = insert_operand (image, operand, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+ }
+ goto write_done;
+ }
+
+ if (fixP->fx_addsy != 0 || fixP->fx_pcrel != 0)
+ return;
+ else
+ {
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("type %d reloc done?\n"), (int) fixP->fx_r_type);
+ goto done;
+ }
+
+write_done:
+ md_number_to_chars (fixpos, image, 4);
+
+done:
+ fixP->fx_done = 1;
+}
+
+/* Look for a register name in the given symbol. */
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ if (*name == '$')
+ {
+ int is_float = 0, num;
+
+ switch (*++name)
+ {
+ case 'f':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_FP];
+ is_float = 32;
+ /* Fall through. */
+
+ case 'r':
+ if (!ISDIGIT (*++name))
+ break;
+ /* Fall through. */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (name[1] == '\0')
+ num = name[0] - '0';
+ else if (name[0] != '0' && ISDIGIT (name[1]) && name[2] == '\0')
+ {
+ num = (name[0] - '0') * 10 + name[1] - '0';
+ if (num >= 32)
+ break;
+ }
+ else
+ break;
+
+ if (!alpha_noat_on && (num + is_float) == AXP_REG_AT)
+ as_warn (_("Used $at without \".set noat\""));
+ return alpha_register_table[num + is_float];
+
+ case 'a':
+ if (name[1] == 't' && name[2] == '\0')
+ {
+ if (!alpha_noat_on)
+ as_warn (_("Used $at without \".set noat\""));
+ return alpha_register_table[AXP_REG_AT];
+ }
+ break;
+
+ case 'g':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[alpha_gp_register];
+ break;
+
+ case 's':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_SP];
+ break;
+ }
+ }
+ return NULL;
+}
+
+#ifdef OBJ_ECOFF
+/* @@@ Magic ECOFF bits. */
+
+void
+alpha_frob_ecoff_data (void)
+{
+ select_gp_value ();
+ /* $zero and $f31 are read-only. */
+ alpha_gprmask &= ~1;
+ alpha_fprmask &= ~1;
+}
+#endif
+
+/* Hook to remember a recently defined label so that the auto-align
+ code can adjust the symbol after we know what alignment will be
+ required. */
+
+void
+alpha_define_label (symbolS *sym)
+{
+ alpha_insn_label = sym;
+}
+
+/* Return true if we must always emit a reloc for a type and false if
+ there is some hope of resolving it at assembly time. */
+
+int
+alpha_force_relocation (fixS *f)
+{
+ if (alpha_flag_relax)
+ return 1;
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_GPDISP:
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ case BFD_RELOC_ALPHA_BRSGP:
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+ return 1;