Rework RISC-V relocations
authorAndrew Waterman <andrew@sifive.com>
Mon, 19 Dec 2016 06:53:48 +0000 (22:53 -0800)
committerAlan Modra <amodra@gmail.com>
Tue, 20 Dec 2016 01:56:33 +0000 (12:26 +1030)
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!

include/
* elf/riscv.h: Add R_RISCV_TPREL_I through R_RISCV_SET32.
bfd/
* 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.
* 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.
(relax_func_t): New type.
(_bfd_riscv_relax_call): Add reserve_size argument, which
controls the maximal offset pessimism.  Correct type of max_alignment.
(_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.
* bfd-in2.h: Regenerate.
* libbfd.h: Likewise.
gas/
* 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.
(md_apply_fix): Likewise.
(riscv_pre_output_hook): New function.
* config/tc-riscv.h (md_pre_output_hook): Define.
(riscv_pre_output_hook): Declare.
(DWARF_CIE_DATA_ALIGNMENT): Always -4.

bfd/ChangeLog
bfd/bfd-in2.h
bfd/elfnn-riscv.c
bfd/elfxx-riscv.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-riscv.c
gas/config/tc-riscv.h
include/ChangeLog
include/elf/riscv.h

index 500003dff5801d7307a02d33e51c989994e9b176..b6bba2ac3e0a0e02ed3919c7d1987227ffa3a394 100644 (file)
@@ -1,3 +1,33 @@
+2016-12-20  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.
+       * 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.
+       (relax_func_t): New type.
+       (_bfd_riscv_relax_call): Add reserve_size argument, which
+       controls the maximal offset pessimism.  Correct type of max_alignment.
+       (_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.
+       * bfd-in2.h: Regenerate.
+       * libbfd.h: Likewise.
+
 2016-12-20  Andrew Waterman  <andrew@sifive.com>
 
        * elfnn-riscv.c: Formatting and comment fixes throughout.
index 1c6b70fc56c109ee37301f252a7c1370fcfe4d56..b5ac178cb7c07fb9f1322e6e1bb15779bcb79c17 100644 (file)
@@ -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,
index f9b3e2ceeeec16d7a7ea470d874dc8285336b7e7..51a2a10e03a539589fc56902a80f0848eeebeee4 100644 (file)
@@ -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:
@@ -1952,6 +1965,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))
            {
@@ -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;
@@ -2757,7 +2783,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      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,
                      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.  */
@@ -2856,7 +2900,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
                        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 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;
     }
 
index 0fb250d0f56e1f944964e5b5e537f1bce085e451..3d935cfb6d310e1283179b589b3c742291df1127 100644 (file)
@@ -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.  */
index 5ec39939c3976e9275a4b7b34588aa72f0f0bb42..76bbd09c8b6734a68a40b3b1be1a6c1723a1de3a 100644 (file)
@@ -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",
index 56cd79b6254037fac89e31b4cf88696f2900d616..3c7b6060b09c8ac5d87ead2dca3068323a3c8ba5 100644 (file)
@@ -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.
 
index 36f1c2fb573aa3e4af7d1c0d3952873d88a0cb01..0e652b13469fcd0c8b69821ab70d500525951f14 100644 (file)
@@ -1,3 +1,22 @@
+2016-12-20  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.
+       (md_apply_fix): Likewise.
+       (riscv_pre_output_hook): New function.
+       * config/tc-riscv.h (md_pre_output_hook): Define.
+       (riscv_pre_output_hook): Declare.
+       (DWARF_CIE_DATA_ALIGNMENT): Always -4.
+
 2016-12-20  Andrew Waterman  <andrew@sifive.com>
 
        * config/tc-riscv.c: Formatting and comment fixes throughout.
index d8a627d14ab8db0e0f32021eed6235ae0351ba27..d011864fd700db841bf70d3441c70f3d5e95e66b 100644 (file)
@@ -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;
index c2a11cefe9a7b44f2ff00941f725aabae4270fcd..32cf3eea4038174bf890d36eaabb20896896f6d4 100644 (file)
@@ -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);
index 7e69c42f4862eb1d34acbe3fd05255c15e7b21e1..51792be23bc8e195c479f5bfea737e717361a38f 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-20  Andrew Waterman  <andrew@sifive.com>
+           Kuan-Lin Chen  <kuanlinchentw@gmail.com>
+
+       * elf/riscv.h: Add R_RISCV_TPREL_I through R_RISCV_SET32.
+
 2016-12-16  fincs  <fincs.alt1@gmail.com>
 
        * bfdlink.h (struct bfd_link_info): Add gc_keep_exported.
index 8415659092b2d96aca7438b899413cffdf918229..44076119400ab6eae6836e6fc553356ab3ebc94b 100644 (file)
@@ -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.  */