[RFC,v2,2/4] RX: gas rx-linux target FDPIC support

Message ID 20260316093151.360635-3-yoshinori.sato@nifty.com
State New
Headers
Series Renesas RX FDPIC support |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm warning Skipped because it is an RFC
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 warning Skipped because it is an RFC

Commit Message

Yoshinori Sato March 16, 2026, 9:31 a.m. UTC
  * config/rx-parse.y
	(displacement): Fix expression displacement.
	(displacement): Add PIC relocation.
        * config/tc-rx.c
        (fdpic): New. in fdpic mode.
        (options): Add OPTION_FDPIC.
        (md_longopts): Add "--fdpic".
        (md_parse_option): Likewise.
        (md_show_usage): Likewise.
        (rx_end_of_match): New. Lookup relocation type helper.
        (rx_parse_name): New. parse symbol relocation type.
        (md_assemble): PIC relocation support.
        (md_convert_flag): Likewise.
        (md_cons_fix_new): Likewise.
        (md_apply_fix): Likewise.
        (rx_force_relocation): New.
	(rx_fix_adjustable): New.
	(rx_elf_final_processing): set EF_RX_FDPIC flag for fdpic output.
        * config/tc-rx.h
        (md_parse_name): New.
        (TC_FORCE_RELOCATION): New.
        (O_PIC_reloc): New.
        (tc_fix_adjustable): New.
	* config/rx-defs.h
	(rx_fix_adjustable): New prototype.
	* testsuite/gas/rx/mov.d: Add expression displacement case.
	* testsuite/gas/rx/mov.sm: Likewise.

Signed-off-by: Yoshinori Sato <yoshinori.sato@nifty.com>
---
 gas/config/rx-defs.h        |   1 +
 gas/config/rx-parse.y       |  16 ++-
 gas/config/tc-rx.c          | 269 ++++++++++++++++++++++++++++++++++--
 gas/config/tc-rx.h          |  18 ++-
 gas/testsuite/gas/rx/mov.d  |   3 +
 gas/testsuite/gas/rx/mov.sm |   7 +
 6 files changed, 298 insertions(+), 16 deletions(-)
  

Patch

diff --git a/gas/config/rx-defs.h b/gas/config/rx-defs.h
index 8fa708df311..d57abfeae96 100644
--- a/gas/config/rx-defs.h
+++ b/gas/config/rx-defs.h
@@ -68,6 +68,7 @@  extern int    rx_parse (void);
 extern int    rx_wrap (void);
 extern void   rx_note_string_insn_use (void);
 extern void   rx_post (char);
+extern bool   rx_fix_adjustable (fixS *fixP);
 
 extern char * rx_lex_start;
 extern char * rx_lex_end;
diff --git a/gas/config/rx-parse.y b/gas/config/rx-parse.y
index 4d657b5df67..224c7ba208b 100644
--- a/gas/config/rx-parse.y
+++ b/gas/config/rx-parse.y
@@ -1934,8 +1934,12 @@  displacement (expressionS exp, int msize)
   valueT val;
   int vshift = 0;
 
-  if (exp.X_op == O_symbol
-      && exp.X_md)
+  /* "foo - bar[Rn]" case, use dsp16[Rn] addressing. */
+  if (exp.X_op == O_subtract)
+    exp.X_md = BFD_RELOC_RX_DIFF;
+
+  if (((exp.X_op == O_symbol || exp.X_op == O_PIC_reloc) && exp.X_md) ||
+      (exp.X_op == O_subtract))
     {
       switch (exp.X_md)
 	{
@@ -1954,6 +1958,14 @@  displacement (expressionS exp, int msize)
 	    }
 	  O2 (exp);
 	  return 2;
+	case BFD_RELOC_16_GOT_PCREL:
+	  exp.X_md = BFD_RELOC_RX_GOT;
+	  O2 (exp);
+	  return 2;
+	case  BFD_RELOC_RX_GOTFUNCDESC:
+	  exp.X_md = BFD_RELOC_RX_GOTFUNCDESC;
+	  O2 (exp);
+	  return 2;
 	}
     }
 
diff --git a/gas/config/tc-rx.c b/gas/config/tc-rx.c
index 794f64a54f4..9dd14af4c1a 100644
--- a/gas/config/tc-rx.c
+++ b/gas/config/tc-rx.c
@@ -40,13 +40,16 @@  const char line_separator_chars[] = "!";
 
 const char EXP_CHARS[]            = "eE";
 const char FLT_CHARS[]            = "dD";
+
+/* Whether --fdpic was given.  */
+static bool fdpic = false;
 
 #ifndef TE_LINUX
 bool rx_use_conventional_section_names = false;
 static int elf_flags = E_FLAG_RX_ABI;
 #else
 bool rx_use_conventional_section_names = true;
-static int elf_flags;
+static int elf_flags = 0;
 #endif
 
 static bool rx_use_small_data_limit = false;
@@ -75,6 +78,7 @@  enum options
   OPTION_USES_RX_ABI,
   OPTION_CPU,
   OPTION_DISALLOW_STRING_INSNS,
+  OPTION_FDPIC,
 };
 
 #define RX_SHORTOPTS ""
@@ -103,6 +107,7 @@  const struct option md_longopts[] =
   {"mrx-abi", no_argument, NULL, OPTION_USES_RX_ABI},
   {"mcpu", required_argument, NULL, OPTION_CPU},
   {"mno-allow-string-insns", no_argument, NULL, OPTION_DISALLOW_STRING_INSNS},
+  {"fdpic", no_argument, NULL, OPTION_FDPIC},
   {NULL, no_argument, NULL, 0}
 };
 const size_t md_longopts_size = sizeof (md_longopts);
@@ -198,6 +203,9 @@  md_parse_option (int c ATTRIBUTE_UNUSED, const char * arg ATTRIBUTE_UNUSED)
     case OPTION_DISALLOW_STRING_INSNS:
       elf_flags |= E_FLAG_RX_SINSNS_SET | E_FLAG_RX_SINSNS_NO;
       return 1;
+    case OPTION_FDPIC:
+      fdpic = true;
+      return 1;
     }
 
   return 0;
@@ -218,7 +226,8 @@  md_show_usage (FILE * stream)
   fprintf (stream, _("  --mpid\n"));
   fprintf (stream, _("  --mint-register=<value>\n"));
   fprintf (stream, _("  --mcpu=<rx100|rx200|rx600|rx610|rxv2|rxv3|rxv3-dfpu>\n"));
-  fprintf (stream, _("  --mno-allow-string-insns"));
+  fprintf (stream, _("  --mno-allow-string-insns\n"));
+  fprintf (stream, _("  --fdpic"));
 }
 
 static void
@@ -1120,6 +1129,97 @@  scan_for_infix_rx_pseudo_ops (char * str)
   return true;
 }
 
+inline static char *
+rx_end_of_match (char *cont, const char *what)
+{
+  int len = strlen (what);
+
+  if (strncasecmp (cont, what, strlen (what)) == 0
+      && ! is_part_of_name (cont[len]))
+    return cont + len;
+
+  return NULL;
+}
+
+#ifdef TE_LINUX
+int
+rx_parse_name (char const *name, expressionS *exprP,
+	       enum expr_mode mode, char *nextcharP)
+{
+  char *next = input_line_pointer;
+  char *next_end;
+  int reloc_type;
+  segT segment;
+
+  static const struct suffix_list {
+    const char *name;
+    int reloc;
+  } suffixes[] = {
+    { "GOTOFFFUNCDESC", BFD_RELOC_RX_GOTOFFFUNCDESC },
+    { "GOTFUNCDESC", BFD_RELOC_RX_GOTFUNCDESC },
+    { "GOTOFF", BFD_RELOC_32_GOTOFF },
+    { "FUNCDESC", BFD_RELOC_RX_FUNCDESC },
+    { "GOT", BFD_RELOC_16_GOT_PCREL },
+    { "PLT", BFD_RELOC_32_PLT_PCREL },
+    { NULL, -1 },
+  };
+  const struct suffix_list *s;
+
+  if (!fdpic)
+    return 0;
+
+  exprP->X_op_symbol = NULL;
+  exprP->X_add_symbol = symbol_find_or_make (name);
+
+  if (*nextcharP != '@')
+    goto no_suffix;
+  for (s = suffixes; s->name != NULL; s++)
+    {
+      if ((next_end = rx_end_of_match (next + 1, s->name)))
+	{
+	  reloc_type = s->reloc;
+	  break;
+	}
+    }
+  if (s->name == NULL)
+    goto no_suffix;
+
+  *input_line_pointer = *nextcharP;
+  input_line_pointer = next_end;
+  *nextcharP = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  exprP->X_op = O_PIC_reloc;
+  exprP->X_add_number = 0;
+  exprP->X_md = reloc_type;
+  return 1;
+
+ no_suffix:
+  /* If we have an absolute symbol or a reg, then we know its
+     value now.  */
+  segment = S_GET_SEGMENT (exprP->X_add_symbol);
+  if (mode != expr_defer && segment == absolute_section)
+    {
+      exprP->X_op = O_constant;
+      exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+      exprP->X_add_symbol = NULL;
+    }
+  else if (mode != expr_defer && segment == reg_section)
+    {
+      exprP->X_op = O_register;
+      exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+      exprP->X_add_symbol = NULL;
+    }
+  else
+    {
+      exprP->X_op = O_symbol;
+      exprP->X_add_number = 0;
+    }
+
+  return 1;
+}
+#endif
+
 void
 md_assemble (char * str)
 {
@@ -1168,7 +1268,9 @@  md_assemble (char * str)
   APPEND (ops, n_ops);
   APPEND (post, n_post);
 
-  if (rx_bytes.link_relax && rx_bytes.n_fixups)
+#ifdef TE_LINUX
+  if (rx_bytes.link_relax && rx_bytes.n_fixups &&
+      rx_bytes.fixups[0].exp.X_op != O_PIC_reloc)
     {
       fixS * f;
 
@@ -1181,6 +1283,7 @@  md_assemble (char * str)
 		   BFD_RELOC_RX_RELAX);
       frag_then->tc_frag_data->link_relax_fixP = f;
     }
+#endif
 
   for (i = 0; i < rx_bytes.n_fixups; i ++)
     {
@@ -1206,12 +1309,41 @@  md_assemble (char * str)
       else
 	exp = & rx_bytes.fixups[i].exp;
 
-      f = fix_new_exp (frag_then,
-		       (char *) bytes + idx - frag_then->fr_literal,
-		       rx_bytes.fixups[i].nbits / 8,
-		       exp,
-		       rx_bytes.fixups[i].type == RXREL_PCREL ? 1 : 0,
-		       rel);
+#ifdef TE_LINUX
+      if (exp->X_op == O_PIC_reloc
+	  && exp->X_md != BFD_RELOC_RX_GOT
+	  && exp->X_md != BFD_RELOC_RX_GOTFUNCDESC)
+	{
+	  exp->X_op = O_symbol;
+	  f = fix_new_exp (frag_then,
+			   (char *) bytes + idx - frag_then->fr_literal,
+			   4,
+			   exp,
+			   0,
+			   exp->X_md);
+	}
+      else if (exp->X_op == O_PIC_reloc &&
+	       (exp->X_md == BFD_RELOC_RX_GOT
+		|| exp->X_md == BFD_RELOC_RX_GOTFUNCDESC))
+	{
+	  exp->X_op = O_symbol;
+	  f = fix_new_exp (frag_then,
+			   (char *) bytes + idx - frag_then->fr_literal,
+			   2,
+			   exp,
+			   0,
+			   exp->X_md);
+	}
+      else
+#endif
+	{
+	  f = fix_new_exp (frag_then,
+			   (char *) bytes + idx - frag_then->fr_literal,
+			   rx_bytes.fixups[i].nbits / 8,
+			   exp,
+			   rx_bytes.fixups[i].type == RXREL_PCREL ? 1 : 0,
+			   rel);
+	}
       if (frag_then->tc_frag_data)
 	frag_then->tc_frag_data->fixups[i].fixP = f;
     }
@@ -1788,7 +1920,7 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
   int fi = (rxb->n_fixups > 1) ? 1 : 0;
   fixS * fix = rxb->fixups[fi].fixP;
 
-  tprintf ("\033[31mconvrt frag: addr %08lx fix %ld var %ld ofs %ld lit %p opc %p type %d sub %d\033[0m\n",
+  tprintf ("\033[31mconvert frag: addr %08lx fix %ld var %ld ofs %ld lit %p opc %p type %d sub %d\033[0m\n",
 	   (unsigned long) (fragP->fr_address
 			    + (fragP->fr_opcode - fragP->fr_literal)),
 	   (long) fragP->fr_fix, (long) fragP->fr_var, (long) fragP->fr_offset,
@@ -1895,7 +2027,14 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	  op[2] = (disp >> 8) & 0xff;
 	  op[1] = disp;
 #endif
-	  reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+	  if (fragP->tc_frag_data->fixups->exp.X_md != BFD_RELOC_32_PLT_PCREL)
+	    {
+	      reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+	    }
+	  else
+	    {
+	      reloc_type = BFD_RELOC_32_PLT_PCREL;
+	    }
 	  reloc_adjust = 1;
 	  break;
 
@@ -1998,7 +2137,14 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	  op[2] = (disp >> 8) & 0xff;
 	  op[1] = disp;
 #endif
-	  reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+	  if (fragP->tc_frag_data->fixups->exp.X_md != BFD_RELOC_32_PLT_PCREL)
+	    {
+	      reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+	    }
+	  else
+	    {
+	      reloc_type = BFD_RELOC_32_PLT_PCREL;
+	    }
 	  reloc_adjust = 0;
 	  break;
 
@@ -2053,6 +2199,23 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	int li;
 	char * imm = op + fragP->tc_frag_data->relax[ri].val_ofs;
 
+	if (rxb->fixups[fi].exp.X_op == O_symbol)
+	  {
+#ifdef TE_LINUX
+	    switch(rxb->fixups[fi].exp.X_md)
+	      {
+	      case BFD_RELOC_16_GOT_PCREL:
+		reloc_type = BFD_RELOC_RX_GOT;
+		break;
+	      case BFD_RELOC_32_GOTOFF:
+	      case BFD_RELOC_RX_GOTFUNCDESC:
+	      case BFD_RELOC_RX_GOTOFFFUNCDESC:
+	      case BFD_RELOC_RX_FUNCDESC:
+		reloc_type = rxb->fixups[fi].exp.X_md;
+		break;
+	      }
+#endif
+	  }
 	switch (nbytes)
 	  {
 	  case 1:
@@ -2097,7 +2260,27 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	    imm[2] = addr0 >> 16;
 	    imm[3] = addr0 >> 24;
 #endif
-	    reloc_type = BFD_RELOC_RX_32_OP;
+#ifdef TE_LINUX
+	    if (rxb->fixups[fi].exp.X_op == O_symbol)
+	      {
+		switch(rxb->fixups[fi].exp.X_md)
+		  {
+		  case BFD_RELOC_16_GOT_PCREL:
+		    reloc_type = BFD_RELOC_RX_GOT;
+		    break;
+		  case BFD_RELOC_32_GOTOFF:
+		  case BFD_RELOC_RX_GOTFUNCDESC:
+		  case BFD_RELOC_RX_GOTOFFFUNCDESC:
+		  case BFD_RELOC_RX_FUNCDESC:
+		    reloc_type = rxb->fixups[fi].exp.X_md;
+		    break;
+		  default:
+		    reloc_type = BFD_RELOC_RX_32_OP;
+		  }
+	      }
+	    else
+#endif
+	      reloc_type = BFD_RELOC_RX_32_OP;
 	    break;
 	  default:
 	    as_bad (_("invalid immediate size"));
@@ -2152,6 +2335,9 @@  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	  break;
 	case BFD_RELOC_24_PCREL:
 	case BFD_RELOC_RX_24_OP:
+#ifdef TE_LINUX
+	case BFD_RELOC_32_PLT_PCREL:
+#endif
 	  fix->fx_size = 3;
 	  break;
 	case BFD_RELOC_RX_32_OP:
@@ -2220,6 +2406,9 @@  rx_cons_fix_new (fragS *	frag,
 		 expressionS *  exp,
 		 bfd_reloc_code_real_type type)
 {
+
+  type = BFD_RELOC_UNUSED;
+
   switch (size)
     {
     case 1:
@@ -2247,6 +2436,13 @@  rx_cons_fix_new (fragS *	frag,
 	type = BFD_RELOC_RX_DIFF;
     }
 
+#ifdef TE_LINUX
+  if (exp->X_op == O_PIC_reloc)
+    {
+      type = BFD_RELOC_RX_FUNCDESC;
+      exp->X_op = O_symbol;
+    }
+#endif
   fix_new_exp (frag, where, size, exp, 0, type);
 }
 
@@ -2433,6 +2629,21 @@  md_apply_fix (struct fix *f,
 #endif
       break;
 
+#ifdef TE_LINUX
+    case BFD_RELOC_RX_GOT:
+    case BFD_RELOC_RX_GOTFUNCDESC:
+      f->fx_no_overflow = 1;
+      op[0] = op[1] = 0;
+      break;
+    case BFD_RELOC_RX_GOTOFFFUNCDESC:
+    case BFD_RELOC_RX_FUNCDESC:
+    case BFD_RELOC_32_PLT_PCREL:
+    case BFD_RELOC_32_GOTOFF:
+      /* This field fill in ld */
+      op[0] = op[1] = op[2] = op[3] = 0;
+      break;
+#endif
+
     default:
       as_bad (_("Unknown reloc in md_apply_fix: %s"),
 	      bfd_get_reloc_code_name (f->fx_r_type));
@@ -2681,12 +2892,44 @@  rx_note_string_insn_use (void)
   elf_flags |= E_FLAG_RX_SINSNS_SET | E_FLAG_RX_SINSNS_YES;
 }
 
+#ifdef TE_LINUX
+int
+rx_force_relocation (fixS *fix)
+{
+  /* Make sure some relocations get emitted.  */
+  if (fix->fx_r_type == BFD_RELOC_RX_GOT
+      || fix->fx_r_type == BFD_RELOC_RX_GOTOFF
+      || fix->fx_r_type == BFD_RELOC_RX_GOTFUNCDESC
+      || fix->fx_r_type == BFD_RELOC_RX_GOTOFFFUNCDESC
+      || generic_force_reloc (fix))
+    return 1;
+  return 0;
+}
+
+bool
+rx_fix_adjustable (fixS *fixP)
+{
+  if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_16_GOT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_RX_GOT
+      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_RX_GOTFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_RX_GOTOFFFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_RX_FUNCDESC)
+    return false;
+  return true;
+}
+#endif
+
 /* Set the ELF specific flags.  */
 
 void
 rx_elf_final_processing (void)
 {
   elf_elfheader (stdoutput)->e_flags |= elf_flags;
+
+  if (fdpic)
+    elf_elfheader (stdoutput)->e_flags |= EF_RX_FDPIC;
 }
 
 /* Scan the current input line for occurrences of Renesas
diff --git a/gas/config/tc-rx.h b/gas/config/tc-rx.h
index 8a37b36275d..72e176eb7b7 100644
--- a/gas/config/tc-rx.h
+++ b/gas/config/tc-rx.h
@@ -75,12 +75,28 @@  extern void rx_frag_init (fragS *);
   rx_validate_fix_sub (FIX)
 extern int rx_validate_fix_sub (struct fix *);
 
+#ifdef TE_LINUX
+#define md_parse_name(name, exprP, mode, nextcharP) \
+  rx_parse_name ((name), (exprP), (mode), (nextcharP))
+int rx_parse_name (char const *, expressionS *,
+		   enum expr_mode, char *);
+/* We need to force out some relocations when relaxing.  */
+#define TC_FORCE_RELOCATION(fix) rx_force_relocation (fix)
+extern int rx_force_relocation (struct fix *);
+#endif
 #define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC)	\
   rx_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC)
 extern void rx_cons_fix_new (fragS *, int, int, expressionS *,
 			     bfd_reloc_code_real_type);
 
-#define tc_fix_adjustable(x) 0
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+   symbols.  The relocation type is stored in X_md.  */
+#define O_PIC_reloc O_md1
+
+#ifdef TE_LINUX
+#define tc_fix_adjustable(x) rx_fix_adjustable(x)
+extern bool rx_fix_adjustable (struct fix *);
+#endif
 
 #define md_do_align(n, fill, len, max, around)				\
   if ((n)								\
diff --git a/gas/testsuite/gas/rx/mov.d b/gas/testsuite/gas/rx/mov.d
index 276b6d9d898..4c5d2389746 100644
--- a/gas/testsuite/gas/rx/mov.d
+++ b/gas/testsuite/gas/rx/mov.d
@@ -528,3 +528,6 @@  Disassembly of section .*:
  [0-9a-f]+:	fd 2e 0f                      	mov.l	\[-r0\], r15
  [0-9a-f]+:	fd 2e f0                      	mov.l	\[-r15\], r0
  [0-9a-f]+:	fd 2e ff                      	mov.l	\[-r15\], r15
+ [0-9a-f]+:	ce 00 04 00                   	mov.b	4\[r0\], r0
+ [0-9a-f]+:	de 00 02 00                   	mov.w	4\[r0\], r0
+ [0-9a-f]+:	ee 00 01 00                   	mov.l	4\[r0\], r0
diff --git a/gas/testsuite/gas/rx/mov.sm b/gas/testsuite/gas/rx/mov.sm
index 8bebbf5908a..03bec4a7bc1 100644
--- a/gas/testsuite/gas/rx/mov.sm
+++ b/gas/testsuite/gas/rx/mov.sm
@@ -31,3 +31,10 @@ 
 	mov{bwl}	[{reg}+],{reg}
 	mov{bwl}	{reg},[-{reg}]
 	mov{bwl}	[-{reg}],{reg}
+
+1:	mov.B		2f - 1b[r0], r0
+2:
+1:	mov.W		2f - 1b[r0], r0
+2:
+1:	mov.L		2f - 1b[r0], r0
+2: