PowerPC __tls_get_addr arg parsing

Alan Modra amodra@gmail.com
Thu Feb 21 08:49:00 GMT 2019


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.

diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index e8deb32882..35d85a4520 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -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
+   (sym@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.  */
diff --git a/ld/testsuite/ld-powerpc/tls32.s b/ld/testsuite/ld-powerpc/tls32.s
index 1c7a890402..2893ad2103 100644
--- a/ld/testsuite/ld-powerpc/tls32.s
+++ b/ld/testsuite/ld-powerpc/tls32.s
@@ -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

-- 
Alan Modra
Australia Development Lab, IBM



More information about the Binutils mailing list