Allow BFD to recognize macOS universal libraries
[deliverable/binutils-gdb.git] / bfd / elfnn-riscv.c
index 6403af91f2f252510614e29f424ae118d7a0ae38..934704a87e78604b526f1a7c8f138a471bb46c5b 100644 (file)
@@ -121,12 +121,13 @@ struct riscv_elf_link_hash_table
   (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
   == RISCV_ELF_DATA ? ((struct riscv_elf_link_hash_table *) ((p)->hash)) : NULL)
 
-static void
+static bfd_boolean
 riscv_info_to_howto_rela (bfd *abfd,
                          arelent *cache_ptr,
                          Elf_Internal_Rela *dst)
 {
   cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info));
+  return cache_ptr->howto != NULL;
 }
 
 static void
@@ -467,11 +468,13 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
 static bfd_boolean
 bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
 {
+  reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
+
   (*_bfd_error_handler)
     (_("%pB: relocation %s against `%s' can not be used when making a shared "
        "object; recompile with -fPIC"),
-      abfd, riscv_elf_rtype_to_howto (abfd, r_type)->name,
-      h != NULL ? h->root.root.string : "a local symbol");
+     abfd, r ? r->name : _("<unknown>"),
+     h != NULL ? h->root.root.string : "a local symbol");
   bfd_set_error (bfd_error_bad_value);
   return FALSE;
 }
@@ -624,9 +627,11 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
             may need to keep relocations for symbols satisfied by a
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
+         reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
+
          if ((bfd_link_pic (info)
               && (sec->flags & SEC_ALLOC) != 0
-              && (! riscv_elf_rtype_to_howto (abfd, r_type)->pc_relative
+              && ((r != NULL && ! r->pc_relative)
                   || (h != NULL
                       && (! info->symbolic
                           || h->root.type == bfd_link_hash_defweak
@@ -696,7 +701,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                }
 
              p->count += 1;
-             p->pc_count += riscv_elf_rtype_to_howto (abfd, r_type)->pc_relative;
+             p->pc_count += r == NULL ? 0 : r->pc_relative;
            }
 
          break;
@@ -996,7 +1001,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       else
        {
          s->size += RISCV_ELF_WORD_BYTES;
-         if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h))
+         if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
+             && ! UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
            htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
        }
     }
@@ -1035,7 +1041,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       if (eh->dyn_relocs != NULL
          && h->root.type == bfd_link_hash_undefweak)
        {
-         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+             || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
            eh->dyn_relocs = NULL;
 
          /* Make sure undefined weak symbols are output as a dynamic
@@ -1726,8 +1733,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
       int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
       reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
       const char *msg = NULL;
+      bfd_boolean resolved_to_zero;
 
-      if (r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
+      if (howto == NULL
+         || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
        continue;
 
       /* This is a final link.  */
@@ -1779,6 +1788,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
            name = bfd_section_name (input_bfd, sec);
        }
 
+      resolved_to_zero = (h != NULL
+                         && UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
+
       switch (r_type)
        {
        case R_RISCV_NONE:
@@ -1889,8 +1901,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
                                                input_bfd);
          r_type = ELFNN_R_TYPE (rel->r_info);
          howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
-         if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                           relocation, absolute))
+         if (howto == NULL)
+           r = bfd_reloc_notsupported;
+         else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+                                                relocation, absolute))
            r = bfd_reloc_overflow;
          break;
 
@@ -1917,8 +1931,24 @@ riscv_elf_relocate_section (bfd *output_bfd,
          }
          break;
 
-       case R_RISCV_CALL_PLT:
        case R_RISCV_CALL:
+         /* Handle a call to an undefined weak function.  This won't be
+            relaxed, so we have to handle it here.  */
+         if (h != NULL && h->root.type == bfd_link_hash_undefweak
+             && h->plt.offset == MINUS_ONE)
+           {
+             /* We can use x0 as the base register.  */
+             bfd_vma insn = bfd_get_32 (input_bfd,
+                                        contents + rel->r_offset + 4);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset + 4);
+             /* Set the relocation value so that we get 0 after the pc
+                relative adjustment.  */
+             relocation = sec_addr (input_section) + rel->r_offset;
+           }
+         /* Fall through.  */
+
+       case R_RISCV_CALL_PLT:
        case R_RISCV_JAL:
        case R_RISCV_RVC_JUMP:
          if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE)
@@ -1985,9 +2015,11 @@ riscv_elf_relocate_section (bfd *output_bfd,
                                                input_bfd);
          r_type = ELFNN_R_TYPE (rel->r_info);
          howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
-         if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                           relocation + rel->r_addend,
-                                           absolute))
+         if (howto == NULL)
+           r = bfd_reloc_notsupported;
+         else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+                                                relocation + rel->r_addend,
+                                                absolute))
            r = bfd_reloc_overflow;
          break;
 
@@ -2022,7 +2054,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 
          if ((bfd_link_pic (info)
               && (h == NULL
-                  || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                  || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                      && !resolved_to_zero)
                   || h->root.type != bfd_link_hash_undefweak)
               && (! howto->pc_relative
                   || !SYMBOL_CALLS_LOCAL (info, h)))
@@ -2346,7 +2379,8 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
     }
 
   if (h->got.offset != (bfd_vma) -1
-      && !(riscv_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE)))
+      && !(riscv_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE))
+      && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
     {
       asection *sgot;
       asection *srela;
@@ -2360,15 +2394,15 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       rela.r_offset = sec_addr (sgot) + (h->got.offset &~ (bfd_vma) 1);
 
-      /* If this is a -Bsymbolic link, and the symbol is defined
-        locally, we just want to emit a RELATIVE reloc.  Likewise if
+      /* If this is a local symbol reference, we just want to emit a RELATIVE
+        reloc.  This can happen if it is a -Bsymbolic link, or a pie link, or
         the symbol was forced to be local because of a version file.
         The entry in the global offset table will already have been
         initialized in the relocate_section function.  */
       if (bfd_link_pic (info)
-         && (info->symbolic || h->dynindx == -1)
-         && h->def_regular)
+         && SYMBOL_REFERENCES_LOCAL (info, h))
        {
+         BFD_ASSERT((h->got.offset & 1) != 0);
          asection *sec = h->root.u.def.section;
          rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
          rela.r_addend = (h->root.u.def.value
@@ -2377,6 +2411,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
        }
       else
        {
+         BFD_ASSERT((h->got.offset & 1) == 0);
          BFD_ASSERT (h->dynindx != -1);
          rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN);
          rela.r_addend = 0;
@@ -2607,6 +2642,14 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
       goto fail;
     }
 
+  /* Disallow linking RVE and non-RVE.  */
+  if ((old_flags ^ new_flags) & EF_RISCV_RVE)
+    {
+      (*_bfd_error_handler)
+       (_("%pB: can't link RVE with other target"), ibfd);
+      goto fail;
+    }
+
   /* Allow linking RVC and non-RVC, and keep the RVC flag.  */
   elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
 
@@ -2655,10 +2698,16 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
 
          /* If the symbol *spans* the bytes we just deleted (i.e. its
             *end* is in the moved bytes but its *start* isn't), then we
-            must adjust its size.  */
-         if (sym->st_value <= addr
-             && sym->st_value + sym->st_size > addr
-             && sym->st_value + sym->st_size <= toaddr)
+            must adjust its size.
+
+            This test needs to use the original value of st_value, otherwise
+            we might accidentally decrease size when deleting bytes right
+            before the symbol.  But since deleted relocs can't span across
+            symbols, we can't have both a st_value and a st_size decrease,
+            so it is simpler to just use an else.  */
+         else if (sym->st_value <= addr
+                  && sym->st_value + sym->st_size > addr
+                  && sym->st_value + sym->st_size <= toaddr)
            sym->st_size -= count;
        }
     }
@@ -2676,9 +2725,12 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
         call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
         the same symbol (which is __wrap_SYMBOL), but still exist as two
         different symbols in 'sym_hashes', we don't want to adjust
-        the global symbol __wrap_SYMBOL twice.
-        This check is only relevant when symbols are being wrapped.  */
-      if (link_info->wrap_hash != NULL)
+        the global symbol __wrap_SYMBOL twice.  */
+      /* The same problem occurs with symbols that are versioned_hidden, as
+        foo becomes an alias for foo@BAR, and hence they need the same
+        treatment.  */
+      if (link_info->wrap_hash != NULL
+         || sym_hash->versioned == versioned_hidden)
        {
          struct elf_link_hash_entry **cur_sym_hashes;
 
@@ -2706,9 +2758,9 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
            sym_hash->root.u.def.value -= count;
 
          /* As above, adjust the size if needed.  */
-         if (sym_hash->root.u.def.value <= addr
-             && sym_hash->root.u.def.value + sym_hash->size > addr
-             && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
+         else if (sym_hash->root.u.def.value <= addr
+                  && sym_hash->root.u.def.value + sym_hash->size > addr
+                  && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
            sym_hash->size -= count;
        }
     }
This page took 0.028567 seconds and 4 git commands to generate.