PowerPC __tls_get_addr arg parsing
authorAlan Modra <amodra@gmail.com>
Thu, 21 Feb 2019 07:11:47 +0000 (17:41 +1030)
committerAlan Modra <amodra@gmail.com>
Thu, 21 Feb 2019 07:35:05 +0000 (18:05 +1030)
The syntax we ended up with for -m32 -fPIC calls to __tls_get_addr is
rather weird.
    bl __tls_get_addr+0x8000(gd0@tlsgd)@plt
This came about by accident, probably due to requiring the arg reloc
before the call reloc.

Of course the @plt really belongs with __tls_get_addr since it affects
the call rather than the call arg, and it isn't a great deal of
trouble to ensure the relocs are emitted in the correct order.  This
patch supports a newer syntax, like so:
    bl __tls_get_addr+0x8000@plt(gd0@tlsgd)

gas/
* config/tc-ppc.c (parse_tls_arg): New function, extracted..
(md_assembler): ..from here.  Call it after parsing other
suffix modifiers too.
ld/
* testsuite/ld-powerpc/tls32.s: Test new @plt syntax.

gas/config/tc-ppc.c
ld/testsuite/ld-powerpc/tls32.s

index e8deb32882cbba000cfd0c97897427cf66f56f15..35d85a4520cba50ba71d498d5efccecae51f2716 100644 (file)
@@ -2999,6 +2999,43 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
   return size;
 }
 
+/* If we have parsed a call to __tls_get_addr, parse an argument like
+   (gd0@tlsgd).  *STR is the leading parenthesis on entry.  If an arg
+   is successfully parsed, *STR is updated past the trailing
+   parenthesis and trailing white space, and *TLS_FIX contains the
+   reloc and arg expression.  */
+
+static int
+parse_tls_arg (char **str, const expressionS *exp, struct ppc_fixup *tls_fix)
+{
+  const char *sym_name = S_GET_NAME (exp->X_add_symbol);
+  if (sym_name[0] == '.')
+    ++sym_name;
+
+  tls_fix->reloc = BFD_RELOC_NONE;
+  if (strcasecmp (sym_name, "__tls_get_addr") == 0)
+    {
+      char *hold = input_line_pointer;
+      input_line_pointer = *str + 1;
+      expression (&tls_fix->exp);
+      if (tls_fix->exp.X_op == O_symbol)
+       {
+         if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
+           tls_fix->reloc = BFD_RELOC_PPC_TLSGD;
+         else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
+           tls_fix->reloc = BFD_RELOC_PPC_TLSLD;
+         if (tls_fix->reloc != BFD_RELOC_NONE)
+           {
+             input_line_pointer += 7;
+             SKIP_WHITESPACE ();
+             *str = input_line_pointer;
+           }
+       }
+      input_line_pointer = hold;
+    }
+  return tls_fix->reloc != BFD_RELOC_NONE;
+}
+
 /* This routine is called for each instruction to be assembled.  */
 
 void
@@ -3388,47 +3425,12 @@ md_assemble (char *str)
        {
          bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
 #ifdef OBJ_ELF
-         if (ex.X_op == O_symbol && str[0] == '(')
+         /* Look for a __tls_get_addr arg using the insane old syntax.  */
+         if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS
+             && parse_tls_arg (&str, &ex, &fixups[fc]))
            {
-             const char *sym_name = S_GET_NAME (ex.X_add_symbol);
-             if (sym_name[0] == '.')
-               ++sym_name;
-
-             if (strcasecmp (sym_name, "__tls_get_addr") == 0)
-               {
-                 expressionS tls_exp;
-
-                 hold = input_line_pointer;
-                 input_line_pointer = str + 1;
-                 expression (&tls_exp);
-                 if (tls_exp.X_op == O_symbol)
-                   {
-                     reloc = BFD_RELOC_NONE;
-                     if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
-                       {
-                         reloc = BFD_RELOC_PPC_TLSGD;
-                         input_line_pointer += 7;
-                       }
-                     else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
-                       {
-                         reloc = BFD_RELOC_PPC_TLSLD;
-                         input_line_pointer += 7;
-                       }
-                     if (reloc != BFD_RELOC_NONE)
-                       {
-                         SKIP_WHITESPACE ();
-                         str = input_line_pointer;
-
-                         if (fc >= MAX_INSN_FIXUPS)
-                           as_fatal (_("too many fixups"));
-                         fixups[fc].exp = tls_exp;
-                         fixups[fc].opindex = *opindex_ptr;
-                         fixups[fc].reloc = reloc;
-                         ++fc;
-                       }
-                   }
-                 input_line_pointer = hold;
-               }
+             fixups[fc].opindex = *opindex_ptr;
+             ++fc;
            }
 
          if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
@@ -3703,6 +3705,16 @@ md_assemble (char *str)
                  break;
                }
            }
+
+         /* Look for a __tls_get_addr arg after any __tls_get_addr
+            modifiers like @plt.  This fixup must be emitted before
+            the usual call fixup.  */
+         if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS
+             && parse_tls_arg (&str, &ex, &fixups[fc]))
+           {
+             fixups[fc].opindex = *opindex_ptr;
+             ++fc;
+           }
 #endif
 
          /* We need to generate a fixup for this expression.  */
index 1c7a890402fc0b9659044f3bb27a1f4379bf5470..2893ad2103af3821e266d5af389f1272659ac1f7 100644 (file)
@@ -62,7 +62,8 @@ _start:
 #LD
  addi 3,31,ld0@got@tlsld       #R_PPC_GOT_TLSLD16      ld0
  .ifdef TLSMARK
- bl __tls_get_addr+0x8000(ld0@tlsld)@plt #R_PPC_TLSLD  ld0
+#exercise saner new syntax with @plt before the arg
+ bl __tls_get_addr+0x8000@plt(ld0@tlsld) #R_PPC_TLSLD  ld0
                                #R_PPC_PLTREL24         __tls_get_addr+0x8000
  .else
  bl __tls_get_addr+0x8000@plt  #R_PPC_PLTREL24         __tls_get_addr+0x8000