gdb/riscv: Allow breakpoints to be created at invalid addresses
[deliverable/binutils-gdb.git] / bfd / elfnn-riscv.c
index 88f491f9143329a92041956d7929c1472039857c..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.
@@ -1274,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.  */
@@ -2058,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;
@@ -2637,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.  */
 
@@ -2644,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;
@@ -2662,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;
@@ -2669,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;
     }
 
@@ -2969,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;
@@ -3626,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"
@@ -3668,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.028301 seconds and 4 git commands to generate.