gdb/riscv: Allow breakpoints to be created at invalid addresses
[deliverable/binutils-gdb.git] / bfd / elfnn-riscv.c
index 63ff07e13ac96d805979946be0f78f74b8d94869..964b6bdcbcc6caef76b14584d74d77ef4e15bd96 100644 (file)
@@ -1,5 +1,5 @@
 /* RISC-V-specific support for NN-bit ELF.
-   Copyright (C) 2011-2018 Free Software Foundation, Inc.
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on TILE-Gx and MIPS targets.
@@ -169,12 +169,21 @@ riscv_elf_got_plt_val (bfd_vma plt_index, struct bfd_link_info *info)
 
 /* Generate a PLT header.  */
 
-static void
-riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry)
+static bfd_boolean
+riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr,
+                      uint32_t *entry)
 {
   bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, addr);
   bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr);
 
+  /* RVE has no t3 register, so this won't work, and is not supported.  */
+  if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+    {
+      _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+                         output_bfd);
+      return FALSE;
+    }
+
   /* auipc  t2, %hi(.got.plt)
      sub    t1, t1, t3              # shifted .got.plt offset + hdr size + 12
      l[w|d] t3, %lo(.got.plt)(t2)    # _dl_runtime_resolve
@@ -192,13 +201,24 @@ riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry)
   entry[5] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES);
   entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES);
   entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0);
+
+  return TRUE;
 }
 
 /* Generate a PLT entry.  */
 
-static void
-riscv_make_plt_entry (bfd_vma got, bfd_vma addr, uint32_t *entry)
+static bfd_boolean
+riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr,
+                     uint32_t *entry)
 {
+  /* RVE has no t3 register, so this won't work, and is not supported.  */
+  if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+    {
+      _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+                         output_bfd);
+      return FALSE;
+    }
+
   /* auipc  t3, %hi(.got.plt entry)
      l[w|d] t3, %lo(.got.plt entry)(t3)
      jalr   t1, t3
@@ -208,6 +228,8 @@ riscv_make_plt_entry (bfd_vma got, bfd_vma addr, uint32_t *entry)
   entry[1] = RISCV_ITYPE (LREG,  X_T3, X_T3, RISCV_PCREL_LOW_PART (got, addr));
   entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0);
   entry[3] = RISCV_NOP;
+
+  return TRUE;
 }
 
 /* Create an entry in an RISC-V ELF linker hash table.  */
@@ -1252,7 +1274,8 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
          || s == htab->elf.sgot
          || s == htab->elf.sgotplt
          || s == htab->elf.sdynbss
-         || s == htab->elf.sdynrelro)
+         || s == htab->elf.sdynrelro
+         || s == htab->sdyntdata)
        {
          /* Strip this section if we don't need it; see the
             comment below.  */
@@ -2036,7 +2059,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
             all relocs to update these addends.  This is also ambiguous, as
             we do allow offsets to be added to the target address, which are
             not to be used to find the auipc address.  */
-         if ((ELF_ST_TYPE (sym->st_info) == STT_SECTION) && rel->r_addend)
+         if (((sym != NULL && (ELF_ST_TYPE (sym->st_info) == STT_SECTION))
+              || (h != NULL && h->type == STT_SECTION))
+             && rel->r_addend)
            {
              r = bfd_reloc_dangerous;
              break;
@@ -2353,8 +2378,11 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
       loc = htab->elf.splt->contents + h->plt.offset;
 
       /* Fill in the PLT entry itself.  */
-      riscv_make_plt_entry (got_address, header_address + h->plt.offset,
-                           plt_entry);
+      if (! riscv_make_plt_entry (output_bfd, got_address,
+                                 header_address + h->plt.offset,
+                                 plt_entry))
+       return FALSE;
+
       for (i = 0; i < PLT_ENTRY_INSNS; i++)
        bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
 
@@ -2529,8 +2557,11 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
        {
          int i;
          uint32_t plt_header[PLT_HEADER_INSNS];
-         riscv_make_plt_header (sec_addr (htab->elf.sgotplt),
-                                sec_addr (splt), plt_header);
+         ret = riscv_make_plt_header (output_bfd,
+                                      sec_addr (htab->elf.sgotplt),
+                                      sec_addr (splt), plt_header);
+         if (!ret)
+           return ret;
 
          for (i = 0; i < PLT_HEADER_INSNS; i++)
            bfd_put_32 (output_bfd, plt_header[i], splt->contents + 4*i);
@@ -2609,6 +2640,445 @@ riscv_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
     }
 }
 
+/* Given the ELF header flags in FLAGS, it returns a string that describes the
+   float ABI.  */
+
+static const char *
+riscv_float_abi_string (flagword flags)
+{
+  switch (flags & EF_RISCV_FLOAT_ABI)
+    {
+    case EF_RISCV_FLOAT_ABI_SOFT:
+      return "soft-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_SINGLE:
+      return "single-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_DOUBLE:
+      return "double-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_QUAD:
+      return "quad-float";
+      break;
+    default:
+      abort ();
+    }
+}
+
+/* The information of architecture attribute.  */
+static riscv_subset_list_t in_subsets;
+static riscv_subset_list_t out_subsets;
+static riscv_subset_list_t merged_subsets;
+
+/* Predicator for standard extension.  */
+
+static bfd_boolean
+riscv_std_ext_p (const char *name)
+{
+  return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
+}
+
+/* Predicator for non-standard extension.  */
+
+static bfd_boolean
+riscv_non_std_ext_p (const char *name)
+{
+  return (strlen (name) >= 2) && (name[0] == 'x');
+}
+
+/* Predicator for standard supervisor extension.  */
+
+static bfd_boolean
+riscv_std_sv_ext_p (const char *name)
+{
+  return (strlen (name) >= 2) && (name[0] == 's') && (name[1] != 'x');
+}
+
+/* Predicator for non-standard supervisor extension.  */
+
+static bfd_boolean
+riscv_non_std_sv_ext_p (const char *name)
+{
+  return (strlen (name) >= 3) && (name[0] == 's') && (name[1] == 'x');
+}
+
+/* Error handler when version mis-match.  */
+
+static void
+riscv_version_mismatch (bfd *ibfd,
+                       struct riscv_subset_t *in,
+                       struct riscv_subset_t *out)
+{
+  _bfd_error_handler
+    (_("error: %pB: Mis-matched ISA version for '%s' extension. "
+       "%d.%d vs %d.%d"),
+       ibfd, in->name,
+       in->major_version, in->minor_version,
+       out->major_version, out->minor_version);
+}
+
+/* Return true if subset is 'i' or 'e'.  */
+
+static bfd_boolean
+riscv_i_or_e_p (bfd *ibfd,
+               const char *arch,
+               struct riscv_subset_t *subset)
+{
+  if ((strcasecmp (subset->name, "e") != 0)
+      && (strcasecmp (subset->name, "i") != 0))
+    {
+      _bfd_error_handler
+       (_("error: %pB: corrupted ISA string '%s'. "
+          "First letter should be 'i' or 'e' but got '%s'."),
+          ibfd, arch, subset->name);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/* Merge standard extensions.
+
+   Return Value:
+     Return FALSE if failed to merge.
+
+   Arguments:
+     `bfd`: bfd handler.
+     `in_arch`: Raw arch string for input object.
+     `out_arch`: Raw arch string for output object.
+     `pin`: subset list for input object, and it'll skip all merged subset after
+            merge.
+     `pout`: Like `pin`, but for output object.  */
+
+static bfd_boolean
+riscv_merge_std_ext (bfd *ibfd,
+                    const char *in_arch,
+                    const char *out_arch,
+                    struct riscv_subset_t **pin,
+                    struct riscv_subset_t **pout)
+{
+  const char *standard_exts = riscv_supported_std_ext ();
+  const char *p;
+  struct riscv_subset_t *in = *pin;
+  struct riscv_subset_t *out = *pout;
+
+  /* First letter should be 'i' or 'e'.  */
+  if (!riscv_i_or_e_p (ibfd, in_arch, in))
+    return FALSE;
+
+  if (!riscv_i_or_e_p (ibfd, out_arch, out))
+    return FALSE;
+
+  if (in->name[0] != out->name[0])
+    {
+      /* TODO: We might allow merge 'i' with 'e'.  */
+      _bfd_error_handler
+       (_("error: %pB: Mis-matched ISA string to merge '%s' and '%s'."),
+        ibfd, in->name, out->name);
+      return FALSE;
+    }
+  else if ((in->major_version != out->major_version) ||
+          (in->minor_version != out->minor_version))
+    {
+      /* TODO: Allow different merge policy.  */
+      riscv_version_mismatch (ibfd, in, out);
+      return FALSE;
+    }
+  else
+    riscv_add_subset (&merged_subsets,
+                     in->name, in->major_version, in->minor_version);
+
+  in = in->next;
+  out = out->next;
+
+  /* Handle standard extension first.  */
+  for (p = standard_exts; *p; ++p)
+    {
+      char find_ext[2] = {*p, '\0'};
+      struct riscv_subset_t *find_in =
+       riscv_lookup_subset (&in_subsets, find_ext);
+      struct riscv_subset_t *find_out =
+       riscv_lookup_subset (&out_subsets, find_ext);
+
+      if (find_in == NULL && find_out == NULL)
+       continue;
+
+      /* Check version is same or not.  */
+      /* TODO: Allow different merge policy.  */
+      if ((find_in != NULL && find_out != NULL)
+         && ((find_in->major_version != find_out->major_version)
+             || (find_in->minor_version != find_out->minor_version)))
+       {
+         riscv_version_mismatch (ibfd, in, out);
+         return FALSE;
+       }
+
+      struct riscv_subset_t *merged = find_in ? find_in : find_out;
+      riscv_add_subset (&merged_subsets, merged->name,
+                       merged->major_version, merged->minor_version);
+    }
+
+  /* Skip all standard extensions.  */
+  while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
+  while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
+
+  *pin = in;
+  *pout = out;
+
+  return TRUE;
+}
+
+/* Merge non-standard and supervisor extensions.
+   Return Value:
+     Return FALSE if failed to merge.
+
+   Arguments:
+     `bfd`: bfd handler.
+     `in_arch`: Raw arch string for input object.
+     `out_arch`: Raw arch string for output object.
+     `pin`: subset list for input object, and it'll skip all merged subset after
+            merge.
+     `pout`: Like `pin`, but for output object. */
+
+static bfd_boolean
+riscv_merge_non_std_and_sv_ext (bfd *ibfd,
+                               riscv_subset_t **pin,
+                               riscv_subset_t **pout,
+                               bfd_boolean (*predicate_func) (const char *))
+{
+  riscv_subset_t *in = *pin;
+  riscv_subset_t *out = *pout;
+
+  for (in = *pin; in != NULL && predicate_func (in->name); in = in->next)
+    riscv_add_subset (&merged_subsets, in->name, in->major_version,
+                     in->minor_version);
+
+  for (out = *pout; out != NULL && predicate_func (out->name); out = out->next)
+    {
+      riscv_subset_t *find_ext =
+       riscv_lookup_subset (&merged_subsets, out->name);
+      if (find_ext != NULL)
+       {
+         /* Check version is same or not. */
+         /* TODO: Allow different merge policy.  */
+         if ((find_ext->major_version != out->major_version)
+             || (find_ext->minor_version != out->minor_version))
+           {
+             riscv_version_mismatch (ibfd, find_ext, out);
+             return FALSE;
+           }
+       }
+      else
+       riscv_add_subset (&merged_subsets, out->name,
+                         out->major_version, out->minor_version);
+    }
+
+  *pin = in;
+  *pout = out;
+  return TRUE;
+}
+
+/* Merge Tag_RISCV_arch attribute.  */
+
+static char *
+riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
+{
+  riscv_subset_t *in, *out;
+  char *merged_arch_str;
+
+  unsigned xlen_in, xlen_out;
+  merged_subsets.head = NULL;
+  merged_subsets.tail = NULL;
+
+  riscv_parse_subset_t rpe_in;
+  riscv_parse_subset_t rpe_out;
+
+  rpe_in.subset_list = &in_subsets;
+  rpe_in.error_handler = _bfd_error_handler;
+  rpe_in.xlen = &xlen_in;
+
+  rpe_out.subset_list = &out_subsets;
+  rpe_out.error_handler = _bfd_error_handler;
+  rpe_out.xlen = &xlen_out;
+
+  if (in_arch == NULL && out_arch == NULL)
+    return NULL;
+
+  if (in_arch == NULL && out_arch != NULL)
+    return out_arch;
+
+  if (in_arch != NULL && out_arch == NULL)
+    return in_arch;
+
+  /* Parse subset from arch string.  */
+  if (!riscv_parse_subset (&rpe_in, in_arch))
+    return NULL;
+
+  if (!riscv_parse_subset (&rpe_out, out_arch))
+    return NULL;
+
+  /* Checking XLEN.  */
+  if (xlen_out != xlen_in)
+    {
+      _bfd_error_handler
+       (_("error: %pB: ISA string of input (%s) doesn't match "
+          "output (%s)."), ibfd, in_arch, out_arch);
+      return NULL;
+    }
+
+  /* Merge subset list.  */
+  in = in_subsets.head;
+  out = out_subsets.head;
+
+  /* Merge standard extension.  */
+  if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out))
+    return NULL;
+  /* Merge non-standard extension.  */
+  if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_non_std_ext_p))
+    return NULL;
+  /* Merge standard supervisor extension.  */
+  if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_std_sv_ext_p))
+    return NULL;
+  /* Merge non-standard supervisor extension.  */
+  if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_non_std_sv_ext_p))
+    return NULL;
+
+  if (xlen_in != xlen_out)
+    {
+      _bfd_error_handler
+       (_("error: %pB: XLEN of input (%u) doesn't match "
+          "output (%u)."), ibfd, xlen_in, xlen_out);
+      return NULL;
+    }
+
+  if (xlen_in != ARCH_SIZE)
+    {
+      _bfd_error_handler
+       (_("error: %pB: Unsupported XLEN (%u), you might be "
+          "using wrong emulation."), ibfd, xlen_in);
+      return NULL;
+    }
+
+  merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets);
+
+  /* Release the subset lists.  */
+  riscv_release_subset_list (&in_subsets);
+  riscv_release_subset_list (&out_subsets);
+  riscv_release_subset_list (&merged_subsets);
+
+  return merged_arch_str;
+}
+
+/* Merge object attributes from IBFD into output_bfd of INFO.
+   Raise an error if there are conflicting attributes.  */
+
+static bfd_boolean
+riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+  bfd *obfd = info->output_bfd;
+  obj_attribute *in_attr;
+  obj_attribute *out_attr;
+  bfd_boolean result = TRUE;
+  const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
+  unsigned int i;
+
+  /* Skip linker created files.  */
+  if (ibfd->flags & BFD_LINKER_CREATED)
+    return TRUE;
+
+  /* Skip any input that doesn't have an attribute section.
+     This enables to link object files without attribute section with
+     any others.  */
+  if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
+    return TRUE;
+
+  if (!elf_known_obj_attributes_proc (obfd)[0].i)
+    {
+      /* This is the first object.  Copy the attributes.  */
+      _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+      out_attr = elf_known_obj_attributes_proc (obfd);
+
+      /* Use the Tag_null value to indicate the attributes have been
+        initialized.  */
+      out_attr[0].i = 1;
+
+      return TRUE;
+    }
+
+  in_attr = elf_known_obj_attributes_proc (ibfd);
+  out_attr = elf_known_obj_attributes_proc (obfd);
+
+  for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
+    {
+    switch (i)
+      {
+      case Tag_RISCV_arch:
+       if (!out_attr[Tag_RISCV_arch].s)
+         out_attr[Tag_RISCV_arch].s = in_attr[Tag_RISCV_arch].s;
+       else if (in_attr[Tag_RISCV_arch].s
+                && out_attr[Tag_RISCV_arch].s)
+         {
+           /* Check arch compatible.  */
+           char *merged_arch =
+               riscv_merge_arch_attr_info (ibfd,
+                                           in_attr[Tag_RISCV_arch].s,
+                                           out_attr[Tag_RISCV_arch].s);
+           if (merged_arch == NULL)
+             {
+               result = FALSE;
+               out_attr[Tag_RISCV_arch].s = "";
+             }
+           else
+             out_attr[Tag_RISCV_arch].s = merged_arch;
+         }
+       break;
+      case Tag_RISCV_priv_spec:
+      case Tag_RISCV_priv_spec_minor:
+      case Tag_RISCV_priv_spec_revision:
+       if (out_attr[i].i != in_attr[i].i)
+         {
+           _bfd_error_handler
+             (_("error: %pB: conflicting priv spec version "
+                "(major/minor/revision)."), ibfd);
+           result = FALSE;
+         }
+       break;
+      case Tag_RISCV_unaligned_access:
+       out_attr[i].i |= in_attr[i].i;
+       break;
+      case Tag_RISCV_stack_align:
+       if (out_attr[i].i == 0)
+         out_attr[i].i = in_attr[i].i;
+       else if (in_attr[i].i != 0
+                && out_attr[i].i != 0
+                && out_attr[i].i != in_attr[i].i)
+         {
+           _bfd_error_handler
+             (_("error: %pB use %u-byte stack aligned but the output "
+                "use %u-byte stack aligned."),
+              ibfd, in_attr[i].i, out_attr[i].i);
+           result = FALSE;
+         }
+       break;
+      default:
+       result &= _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
+      }
+
+      /* If out_attr was copied from in_attr then it won't have a type yet.  */
+      if (in_attr[i].type && !out_attr[i].type)
+       out_attr[i].type = in_attr[i].type;
+    }
+
+  /* Merge Tag_compatibility attributes and any common GNU ones.  */
+  if (!_bfd_elf_merge_object_attributes (ibfd, info))
+    return FALSE;
+
+  /* Check for any attributes not known on RISC-V.  */
+  result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
+
+  return result;
+}
+
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 
@@ -2616,8 +3086,7 @@ static bfd_boolean
 _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
   bfd *obfd = info->output_bfd;
-  flagword new_flags = elf_elfheader (ibfd)->e_flags;
-  flagword old_flags = elf_elfheader (obfd)->e_flags;
+  flagword new_flags, old_flags;
 
   if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd))
     return TRUE;
@@ -2634,6 +3103,12 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   if (!_bfd_elf_merge_object_attributes (ibfd, info))
     return FALSE;
 
+  if (!riscv_merge_attributes (ibfd, info))
+    return FALSE;
+
+  new_flags = elf_elfheader (ibfd)->e_flags;
+  old_flags = elf_elfheader (obfd)->e_flags;
+
   if (! elf_flags_init (obfd))
     {
       elf_flags_init (obfd) = TRUE;
@@ -2641,11 +3116,41 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
       return TRUE;
     }
 
+  /* Check to see if the input BFD actually contains any sections.  If not,
+     its flags may not have been initialized either, but it cannot actually
+     cause any incompatibility.  Do not short-circuit dynamic objects; their
+     section list may be emptied by elf_link_add_object_symbols.
+
+     Also check to see if there are no code sections in the input.  In this
+     case, there is no need to check for code specific flags.  */
+  if (!(ibfd->flags & DYNAMIC))
+    {
+      bfd_boolean null_input_bfd = TRUE;
+      bfd_boolean only_data_sections = TRUE;
+      asection *sec;
+
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       {
+         if ((bfd_get_section_flags (ibfd, sec)
+              & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+             == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+           only_data_sections = FALSE;
+
+         null_input_bfd = FALSE;
+         break;
+       }
+
+      if (null_input_bfd || only_data_sections)
+       return TRUE;
+    }
+
   /* Disallow linking different float ABIs.  */
   if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI)
     {
       (*_bfd_error_handler)
-       (_("%pB: can't link hard-float modules with soft-float modules"), ibfd);
+       (_("%pB: can't link %s modules with %s modules"), ibfd,
+        riscv_float_abi_string (new_flags),
+        riscv_float_abi_string (old_flags));
       goto fail;
     }
 
@@ -2802,6 +3307,8 @@ typedef struct
   riscv_pcgp_lo_reloc *lo;
 } riscv_pcgp_relocs;
 
+/* Initialize the pcgp reloc info in P.  */
+
 static bfd_boolean
 riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
 {
@@ -2810,6 +3317,8 @@ riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
   return TRUE;
 }
 
+/* Free the pcgp reloc info in P.  */
+
 static void
 riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
                        bfd *abfd ATTRIBUTE_UNUSED,
@@ -2833,6 +3342,10 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
     }
 }
 
+/* Record pcgp hi part reloc info in P, using HI_SEC_OFF as the lookup index.
+   The HI_ADDEND, HI_ADDR, HI_SYM, and SYM_SEC args contain info required to
+   relax the corresponding lo part reloc.  */
+
 static bfd_boolean
 riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
                            bfd_vma hi_addend, bfd_vma hi_addr,
@@ -2851,6 +3364,9 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
   return TRUE;
 }
 
+/* Look up hi part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
+   This is used by a lo part reloc to find the corresponding hi part reloc.  */
+
 static riscv_pcgp_hi_reloc *
 riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
 {
@@ -2862,31 +3378,8 @@ riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
   return NULL;
 }
 
-static bfd_boolean
-riscv_delete_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
-{
-  bfd_boolean out = FALSE;
-  riscv_pcgp_hi_reloc *c;
-
-  for (c = p->hi; c != NULL; c = c->next)
-      if (c->hi_sec_off == hi_sec_off)
-       out = TRUE;
-
-  return out;
-}
-
-static bfd_boolean
-riscv_use_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
-{
-  bfd_boolean out = FALSE;
-  riscv_pcgp_hi_reloc *c;
-
-  for (c = p->hi; c != NULL; c = c->next)
-    if (c->hi_sec_off == hi_sec_off)
-      out = TRUE;
-
-  return out;
-}
+/* Record pcgp lo part reloc info in P, using HI_SEC_OFF as the lookup info.
+   This is used to record relocs that can't be relaxed.  */
 
 static bfd_boolean
 riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
@@ -2900,6 +3393,9 @@ riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
   return TRUE;
 }
 
+/* Look up lo part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
+   This is used by a hi part reloc to find the corresponding lo part reloc.  */
+
 static bfd_boolean
 riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
 {
@@ -2911,14 +3407,6 @@ riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
   return FALSE;
 }
 
-static bfd_boolean
-riscv_delete_pcgp_lo_reloc (riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
-                           bfd_vma lo_sec_off ATTRIBUTE_UNUSED,
-                           size_t bytes ATTRIBUTE_UNUSED)
-{
-  return TRUE;
-}
-
 typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
                                     struct bfd_link_info *,
                                     Elf_Internal_Rela *,
@@ -2958,9 +3446,12 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   auipc = bfd_get_32 (abfd, contents + rel->r_offset);
   jalr = bfd_get_32 (abfd, contents + rel->r_offset + 4);
   rd = (jalr >> OP_SH_RD) & OP_MASK_RD;
-  rvc = rvc && VALID_RVC_J_IMM (foff) && ARCH_SIZE == 32;
+  rvc = rvc && VALID_RVC_J_IMM (foff);
+
+  /* C.J exists on RV32 and RV64, but C.JAL is RV32-only.  */
+  rvc = rvc && (rd == 0 || (rd == X_RA && ARCH_SIZE == 32));
 
-  if (rvc && (rd == 0 || rd == X_RA))
+  if (rvc)
     {
       /* Relax to C.J[AL] rd, addr.  */
       r_type = R_RISCV_RVC_JUMP;
@@ -3202,7 +3693,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
 /* Relax PC-relative references to GP-relative references.  */
 
 static bfd_boolean
-_bfd_riscv_relax_pc  (bfd *abfd,
+_bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
                      asection *sec,
                      asection *sym_sec,
                      struct bfd_link_info *link_info,
@@ -3226,22 +3717,22 @@ _bfd_riscv_relax_pc  (bfd *abfd,
     case R_RISCV_PCREL_LO12_I:
     case R_RISCV_PCREL_LO12_S:
       {
+       /* If the %lo has an addend, it isn't for the label pointing at the
+          hi part instruction, but rather for the symbol pointed at by the
+          hi part instruction.  So we must subtract it here for the lookup.
+          It is still used below in the final symbol address.  */
+       bfd_vma hi_sec_off = symval - sec_addr (sym_sec) - rel->r_addend;
        riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs,
-                                                           symval - sec_addr(sym_sec));
+                                                           hi_sec_off);
        if (hi == NULL)
          {
-           riscv_record_pcgp_lo_reloc (pcgp_relocs, symval - sec_addr(sym_sec));
+           riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
            return TRUE;
          }
 
        hi_reloc = *hi;
        symval = hi_reloc.hi_addr;
        sym_sec = hi_reloc.sym_sec;
-       if (!riscv_use_pcgp_hi_reloc(pcgp_relocs, hi->hi_sec_off))
-         _bfd_error_handler
-           (_("%pB(%pA+%#" PRIx64 "): Unable to clear RISCV_PCREL_HI20 reloc "
-              "for corresponding RISCV_PCREL_LO12 reloc"),
-            abfd, sec, (uint64_t) rel->r_offset);
       }
       break;
 
@@ -3285,12 +3776,12 @@ _bfd_riscv_relax_pc  (bfd *abfd,
        case R_RISCV_PCREL_LO12_I:
          rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
          rel->r_addend += hi_reloc.hi_addend;
-         return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+         return TRUE;
 
        case R_RISCV_PCREL_LO12_S:
          rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
          rel->r_addend += hi_reloc.hi_addend;
-         return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+         return TRUE;
 
        case R_RISCV_PCREL_HI20:
          riscv_record_pcgp_hi_reloc (pcgp_relocs,
@@ -3302,7 +3793,7 @@ _bfd_riscv_relax_pc  (bfd *abfd,
          /* We can delete the unnecessary AUIPC and reloc.  */
          rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
          rel->r_addend = 4;
-         return riscv_delete_pcgp_hi_reloc (pcgp_relocs, rel->r_offset);
+         return TRUE;
 
        default:
          abort ();
@@ -3615,6 +4106,14 @@ riscv_elf_object_p (bfd *abfd)
   return TRUE;
 }
 
+/* Determine whether an object attribute tag takes an integer, a
+   string or both.  */
+
+static int
+riscv_elf_obj_attrs_arg_type (int tag)
+{
+  return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
+}
 
 #define TARGET_LITTLE_SYM              riscv_elfNN_vec
 #define TARGET_LITTLE_NAME             "elfNN-littleriscv"
@@ -3657,4 +4156,13 @@ riscv_elf_object_p (bfd *abfd)
 #define elf_backend_rela_normal                1
 #define elf_backend_default_execstack  0
 
+#undef  elf_backend_obj_attrs_vendor
+#define elf_backend_obj_attrs_vendor            "riscv"
+#undef  elf_backend_obj_attrs_arg_type
+#define elf_backend_obj_attrs_arg_type          riscv_elf_obj_attrs_arg_type
+#undef  elf_backend_obj_attrs_section_type
+#define elf_backend_obj_attrs_section_type      SHT_RISCV_ATTRIBUTES
+#undef  elf_backend_obj_attrs_section
+#define elf_backend_obj_attrs_section           ".riscv.attributes"
+
 #include "elfNN-target.h"
This page took 0.031029 seconds and 4 git commands to generate.