This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 3/9] Rework RISC-V relocations


From: Andrew Waterman <andrew@sifive.com>

Before this commit we didn't cleanly support CFI directives because the
internal offsets used to get relaxed which broke them.  This patch
significantly reworks how we handle linker relaxations:

 * DWARF is now properly supported

 * There is a ".option norelax" to disable relaxations, for when users
   write assembly that can't be relaxed (if it's to be later patched up,
   for example).

 * There is an additional _RELAX relocation that specifies when previous
   relocations can be relaxed.

We're in the process of documenting the RISC-V ELF ABI, which will
include documentation of our relocations

  https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md

but we expect that this relocation set will remain ABI compatible in the
future (ie, it's safe to release).

Thanks to Kuan-Lin Chen for figuring out how to correctly relax the
debug info!

bfd/ChangeLog

2016-12-14 Andrew Waterman <andrew@sifive.com>
           Kuan-Lin Chen <kuanlinchentw@gmail.com>

        * reloc.c (BFD_RELOC_RISCV_TPREL_I): New relocation.
        (BFD_RELOC_RISCV_TPREL_S): Likewise.
        (BFD_RELOC_RISCV_RELAX): Likewise.
        (BFD_RELOC_RISCV_CFA): Likewise.
        (BFD_RELOC_RISCV_SUB6): Likewise.
        (BFD_RELOC_RISCV_SET8): Likewise.
        (BFD_RELOC_RISCV_SET8): Likewise.
        (BFD_RELOC_RISCV_SET16): Likewise.
        (BFD_RELOC_RISCV_SET32): Likewise.
        * bfd-in2.h: Regenerate.
        * libbfd.h: Likewise.
        * elfnn-riscv.c (perform_relocation): Handle the new
        relocations.
        (_bfd_riscv_relax_tls_le): Likewise.
        (_bfd_riscv_relax_align): Likewise.
        (_bfd_riscv_relax_section): Likewise.
        (howto_table): Likewise.
        (riscv_reloc_map): Likewise.
        * elfnn-riscv.c (relax_func_t): New type.
        (_bfd_riscv_relax_call): Add reserve_size argument, which
        controls the maximal offset pessimism.
        (_bfd_riscv_relax_lui): Likewise.
        (_bfd_riscv_relax_tls_le): Likewise.
        (_bfd_riscv_relax_align): Likewise.
        (_bfd_riscv_relax_section): Compute the required reserve size
        when relocating and use it to when calling relax_func.

gas/ChangeLog

2016-12-14 Andrew Waterman <andrew@sifive.com>
           Kuan-Lin Chen <kuanlinchentw@gmail.com>

        * config/tc-riscv.c (riscv_set_options): Add relax.
        (riscv_opts): Likewise.
        (s_riscv_option): Add relax and norelax.
        (riscv_apply_const_reloc): New function.
        (append_insn): Move constant relocation handling to
        riscv_apply_const_reloc.
        (md_pcrel_from): Likewise.
        (parse_relocation): Skip BFD_RELOC_UNUSED.
        (md_pcrel_from): Handle BFD_RELOC_RISCV_SUB6,
        BFD_RELOC_RISCV_RELAX, BFD_RELOC_RISCV_CFA.
        * config/tc-riscv.c (md_apply_fix): Likewise.
        (riscv_pre_output_hook): New function.
        * config/tc-riscv.h (md_pre_output_hook): Use
        riscv_pre_output_hook.
        (DWARF_CIE_DATA_ALIGNMENT): Always -4.
---
 bfd/bfd-in2.h         |   9 +++
 bfd/elfnn-riscv.c     |  93 ++++++++++++++++++----
 bfd/elfxx-riscv.c     | 128 ++++++++++++++++++++++++++++++
 bfd/libbfd.h          |   9 +++
 bfd/reloc.c           |  18 +++++
 gas/config/tc-riscv.c | 213 +++++++++++++++++++++++++++++++++++++-------------
 gas/config/tc-riscv.h |   7 +-
 include/elf/riscv.h   |   8 ++
 8 files changed, 416 insertions(+), 69 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 1c6b70f..b5ac178 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -4737,6 +4737,15 @@ number for the SBIC, SBIS, SBI and CBI instructions  */
   BFD_RELOC_RISCV_RVC_LUI,
   BFD_RELOC_RISCV_GPREL_I,
   BFD_RELOC_RISCV_GPREL_S,
+  BFD_RELOC_RISCV_TPREL_I,
+  BFD_RELOC_RISCV_TPREL_S,
+  BFD_RELOC_RISCV_RELAX,
+  BFD_RELOC_RISCV_CFA,
+  BFD_RELOC_RISCV_SUB6,
+  BFD_RELOC_RISCV_SET6,
+  BFD_RELOC_RISCV_SET8,
+  BFD_RELOC_RISCV_SET16,
+  BFD_RELOC_RISCV_SET32,
 
 /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index e18bd71..5976118 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1493,6 +1493,7 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_LO12_I:
     case R_RISCV_GPREL_I:
     case R_RISCV_TPREL_LO12_I:
+    case R_RISCV_TPREL_I:
     case R_RISCV_PCREL_LO12_I:
       value = ENCODE_ITYPE_IMM (value);
       break;
@@ -1500,6 +1501,7 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_LO12_S:
     case R_RISCV_GPREL_S:
     case R_RISCV_TPREL_LO12_S:
+    case R_RISCV_TPREL_S:
     case R_RISCV_PCREL_LO12_S:
       value = ENCODE_STYPE_IMM (value);
       break;
@@ -1548,10 +1550,15 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_ADD16:
     case R_RISCV_ADD32:
     case R_RISCV_ADD64:
+    case R_RISCV_SUB6:
     case R_RISCV_SUB8:
     case R_RISCV_SUB16:
     case R_RISCV_SUB32:
     case R_RISCV_SUB64:
+    case R_RISCV_SET6:
+    case R_RISCV_SET8:
+    case R_RISCV_SET16:
+    case R_RISCV_SET32:
     case R_RISCV_TLS_DTPREL32:
     case R_RISCV_TLS_DTPREL64:
       break;
@@ -1817,6 +1824,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
       switch (r_type)
 	{
 	case R_RISCV_NONE:
+	case R_RISCV_RELAX:
 	case R_RISCV_TPREL_ADD:
 	case R_RISCV_COPY:
 	case R_RISCV_JUMP_SLOT:
@@ -1830,6 +1838,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	case R_RISCV_RVC_LUI:
 	case R_RISCV_LO12_I:
 	case R_RISCV_LO12_S:
+	case R_RISCV_SET6:
+	case R_RISCV_SET8:
+	case R_RISCV_SET16:
+	case R_RISCV_SET32:
 	  /* These require no special handling beyond perform_relocation.  */
 	  break;
 
@@ -1923,6 +1935,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  }
 	  break;
 
+	case R_RISCV_SUB6:
 	case R_RISCV_SUB8:
 	case R_RISCV_SUB16:
 	case R_RISCV_SUB32:
@@ -1953,6 +1966,11 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	case R_RISCV_TPREL_LO12_I:
 	case R_RISCV_TPREL_LO12_S:
 	  relocation = tpoff (info, relocation);
+	  break;
+
+	case R_RISCV_TPREL_I:
+	case R_RISCV_TPREL_S:
+	  relocation = tpoff (info, relocation);
 	  if (VALID_ITYPE_IMM (relocation + rel->r_addend))
 	    {
 	      /* We can use tp as the base register.  */
@@ -1961,6 +1979,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	      insn |= X_TP << OP_SH_RS1;
 	      bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
 	    }
+	  else
+	    r = bfd_reloc_overflow;
 	  break;
 
 	case R_RISCV_GPREL_I:
@@ -2668,6 +2688,11 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count)
   return TRUE;
 }
 
+typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
+				     struct bfd_link_info *,
+				     Elf_Internal_Rela *,
+				     bfd_vma, bfd_vma, bfd_vma, bfd_boolean *);
+
 /* Relax AUIPC + JALR into JAL.  */
 
 static bfd_boolean
@@ -2675,7 +2700,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
 		       struct bfd_link_info *link_info,
 		       Elf_Internal_Rela *rel,
 		       bfd_vma symval,
-		       unsigned int max_alignment,
+		       bfd_vma max_alignment,
+		       bfd_vma reserve_size ATTRIBUTE_UNUSED,
 		       bfd_boolean *again)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2758,6 +2784,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
 		      Elf_Internal_Rela *rel,
 		      bfd_vma symval,
 		      unsigned int max_alignment,
+		      bfd_vma reserve_size,
 		      bfd_boolean *again)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2773,8 +2800,10 @@ _bfd_riscv_relax_lui (bfd *abfd,
   /* Is the reference in range of x0 or gp?
      Valid gp range conservatively because of alignment issue.  */
   if (VALID_ITYPE_IMM (symval)
-      || (symval >= gp && VALID_ITYPE_IMM (symval - gp + max_alignment))
-      || (symval < gp && VALID_ITYPE_IMM (symval - gp - max_alignment)))
+      || (symval >= gp
+	  && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
+      || (symval < gp
+	  && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
     {
       unsigned sym = ELFNN_R_SYM (rel->r_info);
       switch (ELFNN_R_TYPE (rel->r_info))
@@ -2832,20 +2861,35 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
 			 struct bfd_link_info *link_info,
 			 Elf_Internal_Rela *rel,
 			 bfd_vma symval,
-			 unsigned int max_alignment ATTRIBUTE_UNUSED,
+			 bfd_vma max_alignment ATTRIBUTE_UNUSED,
+			 bfd_vma reserve_size ATTRIBUTE_UNUSED,
 			 bfd_boolean *again)
 {
   /* See if this symbol is in range of tp.  */
   if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
     return TRUE;
 
-  /* We can delete the unnecessary LUI and tp add.  The LO12 reloc will be
-     made directly tp-relative.  */
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
-  rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+    case R_RISCV_TPREL_LO12_I:
+      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_I);
+      return TRUE;
 
-  *again = TRUE;
-  return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4);
+    case R_RISCV_TPREL_LO12_S:
+      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_S);
+      return TRUE;
+
+    case R_RISCV_TPREL_HI20:
+    case R_RISCV_TPREL_ADD:
+      /* We can delete the unnecessary instruction and reloc.  */
+      rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+      *again = TRUE;
+      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4);
+
+    default:
+      abort ();
+    }
 }
 
 /* Implement R_RISCV_ALIGN by deleting excess alignment NOPs.  */
@@ -2857,6 +2901,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
 			Elf_Internal_Rela *rel,
 			bfd_vma symval,
 			unsigned int max_alignment ATTRIBUTE_UNUSED,
+			bfd_vma reserve_size ATTRIBUTE_UNUSED,
 			bfd_boolean *again ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2909,7 +2954,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   Elf_Internal_Rela *relocs;
   bfd_boolean ret = FALSE;
   unsigned int i;
-  unsigned int max_alignment;
+  bfd_vma max_alignment, reserve_size = 0;
 
   *again = FALSE;
 
@@ -2935,7 +2980,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
     {
       asection *sym_sec;
       Elf_Internal_Rela *rel = relocs + i;
-      typeof (&_bfd_riscv_relax_call) relax_func = NULL;
+      relax_func_t relax_func;
       int type = ELFNN_R_TYPE (rel->r_info);
       bfd_vma symval;
 
@@ -2947,13 +2992,26 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 		   || type == R_RISCV_LO12_I
 		   || type == R_RISCV_LO12_S)
 	    relax_func = _bfd_riscv_relax_lui;
-	  else if (type == R_RISCV_TPREL_HI20 || type == R_RISCV_TPREL_ADD)
+	  else if (type == R_RISCV_TPREL_HI20
+		   || type == R_RISCV_TPREL_ADD
+		   || type == R_RISCV_TPREL_LO12_I
+		   || type == R_RISCV_TPREL_LO12_S)
 	    relax_func = _bfd_riscv_relax_tls_le;
+	  else
+	    continue;
+
+	  /* Only relax this reloc if it is paired with R_RISCV_RELAX.  */
+	  if (i == sec->reloc_count - 1
+	      || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
+	      || rel->r_offset != (rel + 1)->r_offset)
+	    continue;
+
+	  /* Skip over the R_RISCV_RELAX.  */
+	  i++;
 	}
       else if (type == R_RISCV_ALIGN)
 	relax_func = _bfd_riscv_relax_align;
-
-      if (!relax_func)
+      else
 	continue;
 
       data->relocs = relocs;
@@ -2978,6 +3036,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  /* A local symbol.  */
 	  Elf_Internal_Sym *isym = ((Elf_Internal_Sym *) symtab_hdr->contents
 				    + ELFNN_R_SYM (rel->r_info));
+	  reserve_size = (isym->st_size - rel->r_addend) > isym->st_size
+	    ? 0 : isym->st_size - rel->r_addend;
 
 	  if (isym->st_shndx == SHN_UNDEF)
 	    sym_sec = sec, symval = sec_addr (sec) + rel->r_offset;
@@ -3011,13 +3071,16 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  else
 	    symval = sec_addr (h->root.u.def.section) + h->root.u.def.value;
 
+	  if (h->type != STT_FUNC)
+	    reserve_size =
+	      (h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend;
 	  sym_sec = h->root.u.def.section;
 	}
 
       symval += rel->r_addend;
 
       if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
-		       max_alignment, again))
+		       max_alignment, reserve_size, again))
 	goto fail;
     }
 
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 0fb250d..3d935cf 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -713,6 +713,126 @@ static reloc_howto_type howto_table[] =
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 FALSE),			/* pcrel_offset */
+
+  /* TP-relative TLS LE load.  */
+  HOWTO (R_RISCV_TPREL_I,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_TPREL_I",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* TP-relative TLS LE store.  */
+  HOWTO (R_RISCV_TPREL_S,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_TPREL_S",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* The paired relocation may be relaxed.  */
+  HOWTO (R_RISCV_RELAX,			/* type */
+	 0,				/* rightshift */
+	 3,				/* size */
+	 0,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_RELAX",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0,				/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* 6-bit in-place addition, for local label subtraction.  */
+  HOWTO (R_RISCV_SUB6,			/* type */
+	 0,				/* rightshift */
+	 0,				/* size */
+	 8,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_SUB6",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0x3f,				/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* 6-bit in-place setting, for local label subtraction.  */
+  HOWTO (R_RISCV_SET6,			/* type */
+	 0,				/* rightshift */
+	 0,				/* size */
+	 8,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_SET6",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 0x3f,				/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* 8-bit in-place setting, for local label subtraction.  */
+  HOWTO (R_RISCV_SET8,			/* type */
+	 0,				/* rightshift */
+	 0,				/* size */
+	 8,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_SET8",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 MINUS_ONE,			/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* 16-bit in-place setting, for local label subtraction.  */
+  HOWTO (R_RISCV_SET16,			/* type */
+	 0,				/* rightshift */
+	 1,				/* size */
+	 16,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_SET16",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 MINUS_ONE,			/* dst_mask */
+	 FALSE),			/* pcrel_offset */
+
+  /* 32-bit in-place setting, for local label subtraction.  */
+  HOWTO (R_RISCV_SET32,			/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 FALSE,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_SET32",		/* name */
+	 FALSE,				/* partial_inplace */
+	 0,				/* src_mask */
+	 MINUS_ONE,			/* dst_mask */
+	 FALSE),			/* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -766,6 +886,14 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_RVC_LUI, R_RISCV_RVC_LUI },
   { BFD_RELOC_RISCV_GPREL_I, R_RISCV_GPREL_I },
   { BFD_RELOC_RISCV_GPREL_S, R_RISCV_GPREL_S },
+  { BFD_RELOC_RISCV_TPREL_I, R_RISCV_TPREL_I },
+  { BFD_RELOC_RISCV_TPREL_S, R_RISCV_TPREL_S },
+  { BFD_RELOC_RISCV_RELAX, R_RISCV_RELAX },
+  { BFD_RELOC_RISCV_SUB6, R_RISCV_SUB6 },
+  { BFD_RELOC_RISCV_SET6, R_RISCV_SET6 },
+  { BFD_RELOC_RISCV_SET8, R_RISCV_SET8 },
+  { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
+  { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 5ec3993..76bbd09 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2204,6 +2204,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_RISCV_RVC_LUI",
   "BFD_RELOC_RISCV_GPREL_I",
   "BFD_RELOC_RISCV_GPREL_S",
+  "BFD_RELOC_RISCV_TPREL_I",
+  "BFD_RELOC_RISCV_TPREL_S",
+  "BFD_RELOC_RISCV_RELAX",
+  "BFD_RELOC_RISCV_CFA",
+  "BFD_RELOC_RISCV_SUB6",
+  "BFD_RELOC_RISCV_SET6",
+  "BFD_RELOC_RISCV_SET8",
+  "BFD_RELOC_RISCV_SET16",
+  "BFD_RELOC_RISCV_SET32",
   "BFD_RELOC_RL78_NEG8",
   "BFD_RELOC_RL78_NEG16",
   "BFD_RELOC_RL78_NEG24",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 56cd79b..3c7b606 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5124,6 +5124,24 @@ ENUMX
   BFD_RELOC_RISCV_GPREL_I
 ENUMX
   BFD_RELOC_RISCV_GPREL_S
+ENUMX
+  BFD_RELOC_RISCV_TPREL_I
+ENUMX
+  BFD_RELOC_RISCV_TPREL_S
+ENUMX
+  BFD_RELOC_RISCV_RELAX
+ENUMX
+  BFD_RELOC_RISCV_CFA
+ENUMX
+  BFD_RELOC_RISCV_SUB6
+ENUMX
+  BFD_RELOC_RISCV_SET6
+ENUMX
+  BFD_RELOC_RISCV_SET8
+ENUMX
+  BFD_RELOC_RISCV_SET16
+ENUMX
+  BFD_RELOC_RISCV_SET32
 ENUMDOC
   RISC-V relocations.
 
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index d8a627d..d011864 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -74,12 +74,14 @@ struct riscv_set_options
 {
   int pic; /* Generate position-independent code.  */
   int rvc; /* Generate RVC code.  */
+  int relax; /* Emit relocs the linker is allowed to relax.  */
 };
 
 static struct riscv_set_options riscv_opts =
 {
   0,	/* pic */
   0,	/* rvc */
+  1,	/* relax */
 };
 
 static void
@@ -648,6 +650,28 @@ md_begin (void)
   record_alignment (text_section, riscv_opts.rvc ? 1 : 2);
 }
 
+static insn_t
+riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
+{
+  switch (reloc_type)
+    {
+    case BFD_RELOC_32:
+      return value;
+
+    case BFD_RELOC_RISCV_HI20:
+      return ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
+
+    case BFD_RELOC_RISCV_LO12_S:
+      return ENCODE_STYPE_IMM (value);
+
+    case BFD_RELOC_RISCV_LO12_I:
+      return ENCODE_ITYPE_IMM (value);
+
+    default:
+      abort ();
+    }
+}
+
 /* Output an instruction.  IP is the instruction information.
    ADDRESS_EXPR is an operand of the instruction to be used with
    RELOC_TYPE.  */
@@ -676,43 +700,22 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 	  return;
 	}
       else if (address_expr->X_op == O_constant)
+	ip->insn_opcode |= riscv_apply_const_reloc (reloc_type,
+						    address_expr->X_add_number);
+      else
 	{
-	  switch (reloc_type)
-	    {
-	    case BFD_RELOC_32:
-	      ip->insn_opcode |= address_expr->X_add_number;
-	      goto append;
+	  howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
+	  if (howto == NULL)
+	    as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
 
-	    case BFD_RELOC_RISCV_HI20:
-	      {
-		insn_t imm = RISCV_CONST_HIGH_PART (address_expr->X_add_number);
-		ip->insn_opcode |= ENCODE_UTYPE_IMM (imm);
-		goto append;
-	      }
-
-	    case BFD_RELOC_RISCV_LO12_S:
-	      ip->insn_opcode |= ENCODE_STYPE_IMM (address_expr->X_add_number);
-	      goto append;
+	  ip->fixp = fix_new_exp (ip->frag, ip->where,
+				  bfd_get_reloc_size (howto),
+				  address_expr, FALSE, reloc_type);
 
-	    case BFD_RELOC_RISCV_LO12_I:
-	      ip->insn_opcode |= ENCODE_ITYPE_IMM (address_expr->X_add_number);
-	      goto append;
-
-	    default:
-	      break;
-	    }
+	  ip->fixp->fx_tcbit = riscv_opts.relax;
 	}
-
-	howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
-	if (howto == NULL)
-	  as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
-
-	ip->fixp = fix_new_exp (ip->frag, ip->where,
-				bfd_get_reloc_size (howto),
-				address_expr, FALSE, reloc_type);
     }
 
-append:
   add_fixed_insn (ip);
   install_insn (ip);
 }
@@ -1085,7 +1088,8 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
 
 	/* Check whether the output BFD supports this relocation.
 	   If not, issue an error and fall back on something safe.  */
-	if (!bfd_reloc_type_lookup (stdoutput, percent_op->reloc))
+	if (*reloc != BFD_RELOC_UNUSED
+	    && !bfd_reloc_type_lookup (stdoutput, *reloc))
 	  {
 	    as_bad ("relocation %s isn't supported by the current ABI",
 		    percent_op->str);
@@ -1826,45 +1830,56 @@ md_pcrel_from (fixS *fixP)
 void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
+  unsigned int subtype;
   bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+  bfd_boolean relaxable = FALSE;
 
   /* Remember value for tc_gen_reloc.  */
   fixP->fx_addnumber = *valP;
 
   switch (fixP->fx_r_type)
     {
-    case BFD_RELOC_RISCV_TLS_GOT_HI20:
-    case BFD_RELOC_RISCV_TLS_GD_HI20:
-    case BFD_RELOC_RISCV_TLS_DTPREL32:
-    case BFD_RELOC_RISCV_TLS_DTPREL64:
-    case BFD_RELOC_RISCV_TPREL_HI20:
-    case BFD_RELOC_RISCV_TPREL_LO12_I:
-    case BFD_RELOC_RISCV_TPREL_LO12_S:
-    case BFD_RELOC_RISCV_TPREL_ADD:
-      S_SET_THREAD_LOCAL (fixP->fx_addsy);
-      /* Fall through.  */
-
-    case BFD_RELOC_RISCV_GOT_HI20:
-    case BFD_RELOC_RISCV_PCREL_HI20:
     case BFD_RELOC_RISCV_HI20:
     case BFD_RELOC_RISCV_LO12_I:
     case BFD_RELOC_RISCV_LO12_S:
+      bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
+		  | bfd_getl32 (buf), buf);
+      relaxable = TRUE;
+      break;
+
+    case BFD_RELOC_RISCV_GOT_HI20:
+    case BFD_RELOC_RISCV_PCREL_HI20:
     case BFD_RELOC_RISCV_ADD8:
     case BFD_RELOC_RISCV_ADD16:
     case BFD_RELOC_RISCV_ADD32:
     case BFD_RELOC_RISCV_ADD64:
+    case BFD_RELOC_RISCV_SUB6:
     case BFD_RELOC_RISCV_SUB8:
     case BFD_RELOC_RISCV_SUB16:
     case BFD_RELOC_RISCV_SUB32:
     case BFD_RELOC_RISCV_SUB64:
-      gas_assert (fixP->fx_addsy != NULL);
-      /* Nothing needed to do.  The value comes from the reloc entry.  */
+    case BFD_RELOC_RISCV_RELAX:
+      break;
+
+    case BFD_RELOC_RISCV_TPREL_HI20:
+    case BFD_RELOC_RISCV_TPREL_LO12_I:
+    case BFD_RELOC_RISCV_TPREL_LO12_S:
+    case BFD_RELOC_RISCV_TPREL_ADD:
+      relaxable = TRUE;
+      /* Fall through.  */
+
+    case BFD_RELOC_RISCV_TLS_GOT_HI20:
+    case BFD_RELOC_RISCV_TLS_GD_HI20:
+    case BFD_RELOC_RISCV_TLS_DTPREL32:
+    case BFD_RELOC_RISCV_TLS_DTPREL64:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
       break;
 
     case BFD_RELOC_64:
     case BFD_RELOC_32:
     case BFD_RELOC_16:
     case BFD_RELOC_8:
+    case BFD_RELOC_RISCV_CFA:
       if (fixP->fx_addsy && fixP->fx_subsy)
 	{
 	  fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
@@ -1895,6 +1910,49 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 	      fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
 	      break;
 
+	    case BFD_RELOC_RISCV_CFA:
+	      /* Load the byte to get the subtype.  */
+	      subtype = bfd_get_8 (NULL, &fixP->fx_frag->fr_literal[fixP->fx_where]);
+	      switch (subtype)
+		{
+		case DW_CFA_advance_loc1:
+		  fixP->fx_where++;
+		  fixP->fx_next->fx_where++;
+		  fixP->fx_r_type = BFD_RELOC_RISCV_SET8;
+		  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
+		  break;
+
+		case DW_CFA_advance_loc2:
+		  fixP->fx_size = 2;
+		  fixP->fx_where++;
+		  fixP->fx_next->fx_size = 2;
+		  fixP->fx_next->fx_where++;
+		  fixP->fx_r_type = BFD_RELOC_RISCV_SET16;
+		  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16;
+		  break;
+
+		case DW_CFA_advance_loc4:
+		  fixP->fx_size = 4;
+		  fixP->fx_where++;
+		  fixP->fx_next->fx_size = 4;
+		  fixP->fx_next->fx_where++;
+		  fixP->fx_r_type = BFD_RELOC_RISCV_SET32;
+		  fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32;
+		  break;
+
+		default:
+		  if (subtype < 0x80 && (subtype & 0x40))
+		    {
+		      /* DW_CFA_advance_loc */
+		      fixP->fx_r_type = BFD_RELOC_RISCV_SET6;
+		      fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
+		    }
+		  else
+		    as_fatal (_("internal error: bad CFA value #%d"), subtype);
+		  break;
+		}
+	      break;
+
 	    default:
 	      /* This case is unreachable.  */
 	      abort ();
@@ -1954,10 +2012,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 	}
       break;
 
-    case BFD_RELOC_RISCV_PCREL_LO12_S:
-    case BFD_RELOC_RISCV_PCREL_LO12_I:
     case BFD_RELOC_RISCV_CALL:
     case BFD_RELOC_RISCV_CALL_PLT:
+      relaxable = TRUE;
+      break;
+
+    case BFD_RELOC_RISCV_PCREL_LO12_S:
+    case BFD_RELOC_RISCV_PCREL_LO12_I:
     case BFD_RELOC_RISCV_ALIGN:
       break;
 
@@ -1966,8 +2027,54 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
 	as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
     }
+
+  /* Add an R_RISCV_RELAX reloc if the reloc is relaxable.  */
+  if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
+    {
+      fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
+      fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL;
+      fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX;
+    }
 }
 
+/* Because the value of .cfi_remember_state may changed after relaxation,
+   we insert a fix to relocate it again in link-time.  */
+
+void
+riscv_pre_output_hook (void)
+{
+  const frchainS *frch;
+  const asection *s;
+
+  for (s = stdoutput->sections; s; s = s->next)
+    for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next)
+      {
+	const fragS *frag;
+
+	for (frag = frch->frch_root; frag; frag = frag->fr_next)
+	  {
+	    if (frag->fr_type == rs_cfa)
+	      {
+		const fragS *loc4_frag;
+		expressionS exp;
+
+		symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol;
+		symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol;
+
+		exp.X_op = O_subtract;
+		exp.X_add_symbol = add_symbol;
+		exp.X_add_number = 0;
+		exp.X_op_symbol = op_symbol;
+
+		loc4_frag = (fragS *) frag->fr_opcode;
+		fix_new_exp (loc4_frag, (int) frag->fr_offset, 1, &exp, 0,
+			     BFD_RELOC_RISCV_CFA);
+	      }
+	  }
+      }
+}
+
+
 /* This structure is used to hold a stack of .option values.  */
 
 struct riscv_option_stack
@@ -1998,10 +2105,10 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
     riscv_opts.pic = TRUE;
   else if (strcmp (name, "nopic") == 0)
     riscv_opts.pic = FALSE;
-  else if (strcmp (name, "soft-float") == 0)
-    float_mode = FLOAT_MODE_SOFT;
-  else if (strcmp (name, "hard-float") == 0)
-    float_mode = FLOAT_MODE_HARD;
+  else if (strcmp (name, "relax") == 0)
+    riscv_opts.relax = TRUE;
+  else if (strcmp (name, "norelax") == 0)
+    riscv_opts.relax = FALSE;
   else if (strcmp (name, "push") == 0)
     {
       struct riscv_option_stack *s;
diff --git a/gas/config/tc-riscv.h b/gas/config/tc-riscv.h
index c2a11ce..32cf3ee 100644
--- a/gas/config/tc-riscv.h
+++ b/gas/config/tc-riscv.h
@@ -61,6 +61,9 @@ extern void riscv_after_parse_args (void);
 #define md_parse_long_option(arg) riscv_parse_long_option (arg)
 extern int riscv_parse_long_option (const char *);
 
+#define md_pre_output_hook riscv_pre_output_hook()
+extern void riscv_pre_output_hook (void);
+
 /* Let the linker resolve all the relocs due to relaxation.  */
 #define tc_fix_adjustable(fixp) 0
 #define md_allow_local_subtract(l,r,s) 0
@@ -93,7 +96,9 @@ extern int tc_riscv_regname_to_dw2regnum (char *);
 
 extern unsigned xlen;
 #define DWARF2_DEFAULT_RETURN_COLUMN X_RA
-#define DWARF2_CIE_DATA_ALIGNMENT (-(int) (xlen / 8))
+
+/* Even on RV64, use 4-byte alignment, as F registers may be only 32 bits.  */
+#define DWARF2_CIE_DATA_ALIGNMENT -4
 
 #define elf_tc_final_processing riscv_elf_final_processing
 extern void riscv_elf_final_processing (void);
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 8415659..4407611 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -79,6 +79,14 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   RELOC_NUMBER (R_RISCV_RVC_LUI, 46)
   RELOC_NUMBER (R_RISCV_GPREL_I, 47)
   RELOC_NUMBER (R_RISCV_GPREL_S, 48)
+  RELOC_NUMBER (R_RISCV_TPREL_I, 49)
+  RELOC_NUMBER (R_RISCV_TPREL_S, 50)
+  RELOC_NUMBER (R_RISCV_RELAX, 51)
+  RELOC_NUMBER (R_RISCV_SUB6, 52)
+  RELOC_NUMBER (R_RISCV_SET6, 53)
+  RELOC_NUMBER (R_RISCV_SET8, 54)
+  RELOC_NUMBER (R_RISCV_SET16, 55)
+  RELOC_NUMBER (R_RISCV_SET32, 56)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* Processor specific flags for the ELF header e_flags field.  */
-- 
2.10.2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]