+ static const struct tls_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;
+ /* 1 if call. */
+ int call;
+ } tls_ops[] = {
+ { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 },
+ { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 },
+ { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 },
+ { "tldm_call", 9, BFD_RELOC_SPARC_TLS_LDM_CALL, 1 },
+ { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 },
+ { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 },
+ { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 },
+ { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 }
+ };
+ const struct tls_ops *o;
+ char *s1;
+ int npar = 0;
+
+ for (o = tls_ops; o->name; o++)
+ if (strncmp (s + 2, o->name, o->len) == 0)
+ break;
+ if (o->name == NULL)
+ break;
+
+ if (s[o->len + 2] != '(')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ return special_case;
+ }
+
+ if (! o->call && the_insn.reloc != BFD_RELOC_NONE)
+ {
+ as_bad (_("Illegal operands: %%%s cannot be used together with other relocs in the insn ()"),
+ o->name);
+ return special_case;
+ }
+
+ if (o->call
+ && (the_insn.reloc != BFD_RELOC_32_PCREL_S2
+ || the_insn.exp.X_add_number != 0
+ || the_insn.exp.X_add_symbol
+ != symbol_find_or_make ("__tls_get_addr")))
+ {
+ as_bad (_("Illegal operands: %%%s can be only used with call __tls_get_addr"),
+ o->name);
+ return special_case;
+ }
+
+ the_insn.reloc = o->reloc;
+ memset (&the_insn.exp, 0, sizeof (the_insn.exp));
+ s += o->len + 3;
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+ if (*s1 == '(')
+ npar++;
+ else if (*s1 == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+
+ if (*s1 != ')')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ return special_case;
+ }
+
+ *s1 = '\0';
+ (void) get_expression (s);
+ *s1 = ')';
+ s = s1 + 1;