+/* Helper for parsing an argument, used for sorting out the relocation
+ type. */
+
+static void
+parse_reloc_symbol (expressionS *resultP)
+{
+ char *reloc_name, c, *sym_name;
+ size_t len;
+ int i;
+ const struct arc_reloc_op_tag *r;
+ expressionS right;
+ symbolS *base;
+
+ /* A relocation operand has the following form
+ @identifier@relocation_type. The identifier is already in
+ tok! */
+ if (resultP->X_op != O_symbol)
+ {
+ as_bad (_("No valid label relocation operand"));
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ /* Parse @relocation_type. */
+ input_line_pointer++;
+ c = get_symbol_name (&reloc_name);
+ len = input_line_pointer - reloc_name;
+ if (len == 0)
+ {
+ as_bad (_("No relocation operand"));
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ /* Go through known relocation and try to find a match. */
+ r = &arc_reloc_op[0];
+ for (i = arc_num_reloc_op - 1; i >= 0; i--, r++)
+ if (len == r->length
+ && memcmp (reloc_name, r->name, len) == 0)
+ break;
+ if (i < 0)
+ {
+ as_bad (_("Unknown relocation operand: @%s"), reloc_name);
+ resultP->X_op = O_illegal;
+ return;
+ }
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE_AFTER_NAME ();
+ /* Extra check for TLS: base. */
+ if (*input_line_pointer == '@')
+ {
+ if (resultP->X_op_symbol != NULL
+ || resultP->X_op != O_symbol)
+ {
+ as_bad (_("Unable to parse TLS base: %s"),
+ input_line_pointer);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ input_line_pointer++;
+ c = get_symbol_name (&sym_name);
+ base = symbol_find_or_make (sym_name);
+ resultP->X_op = O_subtract;
+ resultP->X_op_symbol = base;
+ restore_line_pointer (c);
+ right.X_add_number = 0;
+ }
+
+ if ((*input_line_pointer != '+')
+ && (*input_line_pointer != '-'))
+ right.X_add_number = 0;
+ else
+ {
+ /* Parse the constant of a complex relocation expression
+ like @identifier@reloc +/- const. */
+ if (! r->complex_expr)
+ {
+ as_bad (_("@%s is not a complex relocation."), r->name);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ expression (&right);
+ if (right.X_op != O_constant)
+ {
+ as_bad (_("Bad expression: @%s + %s."),
+ r->name, input_line_pointer);
+ resultP->X_op = O_illegal;
+ return;
+ }
+ }
+
+ resultP->X_md = r->op;
+ resultP->X_add_number = right.X_add_number;
+}
+