Fix the cast used to prevent compile time warning about an always false test.
[deliverable/binutils-gdb.git] / bfd / elfnn-riscv.c
index dba1025994fbc2d5e37d4ac6b2feba181dd8de1c..46f0100ace3da966ee94932df17cfc1721266451 100644 (file)
@@ -1,5 +1,5 @@
 /* RISC-V-specific support for NN-bit ELF.
-   Copyright (C) 2011-2019 Free Software Foundation, Inc.
+   Copyright (C) 2011-2020 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on TILE-Gx and MIPS targets.
@@ -310,13 +310,13 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
                                          (bed->dynamic_sec_flags
                                           | SEC_READONLY));
   if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+      || !bfd_set_section_alignment (s, bed->s->log_file_align))
     return FALSE;
   htab->srelgot = s;
 
   s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
   if (s == NULL
-      || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+      || !bfd_set_section_alignment (s, bed->s->log_file_align))
     return FALSE;
   htab->sgot = s;
 
@@ -327,8 +327,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
     {
       s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags);
       if (s == NULL
-         || !bfd_set_section_alignment (abfd, s,
-                                        bed->s->log_file_align))
+         || !bfd_set_section_alignment (s, bed->s->log_file_align))
        return FALSE;
       htab->sgotplt = s;
 
@@ -373,9 +372,23 @@ riscv_elf_create_dynamic_sections (bfd *dynobj,
 
   if (!bfd_link_pic (info))
     {
+      /* Technically, this section doesn't have contents.  It is used as the
+        target of TLS copy relocs, to copy TLS data from shared libraries into
+        the executable.  However, if we don't mark it as loadable, then it
+        matches the IS_TBSS test in ldlang.c, and there is no run-time address
+        space allocated for it even though it has SEC_ALLOC.  That test is
+        correct for .tbss, but not correct for this section.  There is also
+        a second problem that having a section with no contents can only work
+        if it comes after all sections with contents in the same segment,
+        but the linker script does not guarantee that.  This is just mixed in
+        with other .tdata.* sections.  We can fix both problems by lying and
+        saying that there are contents.  This section is expected to be small
+        so this should not cause a significant extra program startup cost.  */
       htab->sdyntdata =
        bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn",
                                            (SEC_ALLOC | SEC_THREAD_LOCAL
+                                            | SEC_LOAD | SEC_DATA
+                                            | SEC_HAS_CONTENTS
                                             | SEC_LINKER_CREATED));
     }
 
@@ -1482,9 +1495,21 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_RVC_LUI:
-      if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)))
+      if (RISCV_CONST_HIGH_PART (value) == 0)
+       {
+         /* Linker relaxation can convert an address equal to or greater than
+            0x800 to slightly below 0x800.  C.LUI does not accept zero as a
+            valid immediate.  We can fix this by converting it to a C.LI.  */
+         bfd_vma insn = bfd_get (howto->bitsize, input_bfd,
+                                 contents + rel->r_offset);
+         insn = (insn & ~MATCH_C_LUI) | MATCH_C_LI;
+         bfd_put (howto->bitsize, input_bfd, insn, contents + rel->r_offset);
+         value = ENCODE_RVC_IMM (0);
+       }
+      else if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)))
        return bfd_reloc_overflow;
-      value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value));
+      else
+       value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value));
       break;
 
     case R_RISCV_32:
@@ -1762,6 +1787,7 @@ 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;
+      char *msg_buf = NULL;
       bfd_boolean resolved_to_zero;
 
       if (howto == NULL
@@ -1814,7 +1840,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
          name = (bfd_elf_string_from_elf_section
                  (input_bfd, symtab_hdr->sh_link, sym->st_name));
          if (name == NULL || *name == '\0')
-           name = bfd_section_name (input_bfd, sec);
+           name = bfd_section_name (sec);
        }
 
       resolved_to_zero = (h != NULL
@@ -1961,10 +1987,11 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_RISCV_CALL:
+       case R_RISCV_CALL_PLT:
          /* 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)
+             && (!bfd_link_pic (info) || h->plt.offset == MINUS_ONE))
            {
              /* We can use x0 as the base register.  */
              bfd_vma insn = bfd_get_32 (input_bfd,
@@ -1977,9 +2004,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
            }
          /* Fall through.  */
 
-       case R_RISCV_CALL_PLT:
        case R_RISCV_JAL:
        case R_RISCV_RVC_JUMP:
+         /* This line has to match the check in _bfd_riscv_relax_section.  */
          if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE)
            {
              /* Refer to the PLT entry.  */
@@ -2063,6 +2090,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
               || (h != NULL && h->type == STT_SECTION))
              && rel->r_addend)
            {
+             msg = _("%pcrel_lo section symbol with an addend");
              r = bfd_reloc_dangerous;
              break;
            }
@@ -2277,21 +2305,42 @@ riscv_elf_relocate_section (bfd *output_bfd,
          && _bfd_elf_section_offset (output_bfd, info, input_section,
                                      rel->r_offset) != (bfd_vma) -1)
        {
-         (*_bfd_error_handler)
-           (_("%pB(%pA+%#" PRIx64 "): "
-              "unresolvable %s relocation against symbol `%s'"),
-            input_bfd,
-            input_section,
-            (uint64_t) rel->r_offset,
-            howto->name,
-            h->root.root.string);
-         continue;
+         switch (r_type)
+           {
+           case R_RISCV_CALL:
+           case R_RISCV_JAL:
+           case R_RISCV_RVC_JUMP:
+             if (asprintf (&msg_buf,
+                           _("%%X%%P: relocation %s against `%s' can "
+                             "not be used when making a shared object; "
+                             "recompile with -fPIC\n"),
+                           howto->name,
+                           h->root.root.string) == -1)
+               msg_buf = NULL;
+             break;
+
+           default:
+             if (asprintf (&msg_buf,
+                           _("%%X%%P: unresolvable %s relocation against "
+                             "symbol `%s'\n"),
+                           howto->name,
+                           h->root.root.string) == -1)
+               msg_buf = NULL;
+             break;
+           }
+
+         msg = msg_buf;
+         r = bfd_reloc_notsupported;
        }
 
       if (r == bfd_reloc_ok)
        r = perform_relocation (howto, rel, relocation, input_section,
                                input_bfd, contents);
 
+      /* We should have already detected the error and set message before.
+        If the error message isn't set since the linker runs out of memory
+        or we don't set it before, then we should set the default message
+        with the "internal error" string here.  */
       switch (r)
        {
        case bfd_reloc_ok:
@@ -2310,17 +2359,21 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
 
        case bfd_reloc_outofrange:
-         msg = _("%X%P: internal error: out of range error\n");
+         if (msg == NULL)
+           msg = _("%X%P: internal error: out of range error\n");
          break;
 
        case bfd_reloc_notsupported:
-         msg = _("%X%P: internal error: unsupported relocation error\n");
+         if (msg == NULL)
+           msg = _("%X%P: internal error: unsupported relocation error\n");
          break;
 
        case bfd_reloc_dangerous:
+         /* The error message should already be set.  */
+         if (msg == NULL)
+           msg = _("dangerous relocation error");
          info->callbacks->reloc_dangerous
-           (info, "%pcrel_lo section symbol with an addend", input_bfd,
-            input_section, rel->r_offset);
+           (info, msg, input_bfd, input_section, rel->r_offset);
          break;
 
        default:
@@ -2328,9 +2381,14 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
        }
 
-      if (msg)
+      /* Do not report error message for the dangerous relocation again.  */
+      if (msg && r != bfd_reloc_dangerous)
        info->callbacks->einfo (msg);
 
+      /* Free the unused `msg_buf` if needed.  */
+      if (msg_buf)
+       free (msg_buf);
+
       /* We already reported the error via a callback, so don't try to report
         it again by returning false.  That leads to spurious errors.  */
       ret = TRUE;
@@ -3086,8 +3144,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;
@@ -3107,6 +3164,9 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   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;
@@ -3114,6 +3174,34 @@ _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_section_flags (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)
     {
@@ -3261,6 +3349,7 @@ struct riscv_pcgp_hi_reloc
   bfd_vma hi_addr;
   unsigned hi_sym;
   asection *sym_sec;
+  bfd_boolean undefined_weak;
   riscv_pcgp_hi_reloc *next;
 };
 
@@ -3319,7 +3408,8 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
 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,
-                           unsigned hi_sym, asection *sym_sec)
+                           unsigned hi_sym, asection *sym_sec,
+                           bfd_boolean undefined_weak)
 {
   riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new));
   if (!new)
@@ -3329,6 +3419,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
   new->hi_addr = hi_addr;
   new->hi_sym = hi_sym;
   new->sym_sec = sym_sec;
+  new->undefined_weak = undefined_weak;
   new->next = p->hi;
   p->hi = new;
   return TRUE;
@@ -3381,7 +3472,8 @@ typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
                                     struct bfd_link_info *,
                                     Elf_Internal_Rela *,
                                     bfd_vma, bfd_vma, bfd_vma, bfd_boolean *,
-                                    riscv_pcgp_relocs *);
+                                    riscv_pcgp_relocs *,
+                                    bfd_boolean undefined_weak);
 
 /* Relax AUIPC + JALR into JAL.  */
 
@@ -3393,7 +3485,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
                       bfd_vma max_alignment,
                       bfd_vma reserve_size ATTRIBUTE_UNUSED,
                       bfd_boolean *again,
-                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                      bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset);
@@ -3402,9 +3495,16 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
 
   /* If the call crosses section boundaries, an alignment directive could
-     cause the PC-relative offset to later increase.  */
-  if (VALID_UJTYPE_IMM (foff) && sym_sec->output_section != sec->output_section)
-    foff += (foff < 0 ? -max_alignment : max_alignment);
+     cause the PC-relative offset to later increase, so we need to add in the
+     max alignment of any section inclusive from the call to the target.
+     Otherwise, we only need to use the alignment of the current section.  */
+  if (VALID_UJTYPE_IMM (foff))
+    {
+      if (sym_sec->output_section == sec->output_section
+         && sym_sec->output_section != bfd_abs_section_ptr)
+       max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+      foff += (foff < 0 ? -max_alignment : max_alignment);
+    }
 
   /* See if this function call can be shortened.  */
   if (!VALID_UJTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
@@ -3481,46 +3581,61 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
                      bfd_boolean *again,
-                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                     bfd_boolean undefined_weak)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
   int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
 
-  /* Mergeable symbols and code might later move out of range.  */
-  if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
-    return TRUE;
-
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
 
   if (gp)
     {
-      /* If gp and the symbol are in the same output section, then
-        consider only that section's alignment.  */
+      /* If gp and the symbol are in the same output section, which is not the
+        abs section, then consider only that output section's alignment.  */
       struct bfd_link_hash_entry *h =
        bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE,
                              TRUE);
-      if (h->u.def.section->output_section == sym_sec->output_section)
+      if (h->u.def.section->output_section == sym_sec->output_section
+         && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
     }
 
   /* 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 + reserve_size))
-      || (symval < gp
-         && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
+  if (undefined_weak
+      || (VALID_ITYPE_IMM (symval)
+         || (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))
        {
        case R_RISCV_LO12_I:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+           }
+         else
+           rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
          return TRUE;
 
        case R_RISCV_LO12_S:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+           }
+         else
+           rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
          return TRUE;
 
        case R_RISCV_HI20:
@@ -3536,11 +3651,16 @@ _bfd_riscv_relax_lui (bfd *abfd,
     }
 
   /* Can we relax LUI to C.LUI?  Alignment might move the section forward;
-     account for this assuming page alignment at worst.  */
+     account for this assuming page alignment at worst. In the presence of 
+     RELRO segment the linker aligns it by one page size, therefore sections
+     after the segment can be moved more than one page. */
+
   if (use_rvc
       && ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20
       && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
-      && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval + ELF_MAXPAGESIZE)))
+      && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval)
+                           + (link_info->relro ? 2 * ELF_MAXPAGESIZE
+                              : ELF_MAXPAGESIZE)))
     {
       /* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp).  */
       bfd_vma lui = bfd_get_32 (abfd, contents + rel->r_offset);
@@ -3574,7 +3694,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
                         bfd_boolean *again,
-                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED)
+                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED,
+                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   /* See if this symbol is in range of tp.  */
   if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
@@ -3614,7 +3735,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        bfd_vma max_alignment ATTRIBUTE_UNUSED,
                        bfd_vma reserve_size ATTRIBUTE_UNUSED,
                        bfd_boolean *again ATTRIBUTE_UNUSED,
-                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED)
+                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED,
+                       bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma alignment = 1, pos;
@@ -3672,8 +3794,10 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
                      bfd_boolean *again ATTRIBUTE_UNUSED,
-                     riscv_pcgp_relocs *pcgp_relocs)
+                     riscv_pcgp_relocs *pcgp_relocs,
+                     bfd_boolean undefined_weak)
 {
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
 
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
@@ -3703,12 +3827,19 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
        hi_reloc = *hi;
        symval = hi_reloc.hi_addr;
        sym_sec = hi_reloc.sym_sec;
+
+       /* We can not know whether the undefined weak symbol is referenced
+          according to the information of R_RISCV_PCREL_LO12_I/S.  Therefore,
+          we have to record the 'undefined_weak' flag when handling the
+          corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc.  */
+       undefined_weak = hi_reloc.undefined_weak;
       }
       break;
 
     case R_RISCV_PCREL_HI20:
       /* Mergeable symbols and code might later move out of range.  */
-      if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
+      if (! undefined_weak
+         && sym_sec->flags & (SEC_MERGE | SEC_CODE))
        return TRUE;
 
       /* If the cooresponding lo relocation has already been seen then it's not
@@ -3724,33 +3855,62 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
 
   if (gp)
     {
-      /* If gp and the symbol are in the same output section, then
-        consider only that section's alignment.  */
+      /* If gp and the symbol are in the same output section, which is not the
+        abs section, then consider only that output section's alignment.  */
       struct bfd_link_hash_entry *h =
-       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE);
-      if (h->u.def.section->output_section == sym_sec->output_section)
+       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE,
+                             TRUE);
+      if (h->u.def.section->output_section == sym_sec->output_section
+         && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
     }
 
   /* 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 + reserve_size))
-      || (symval < gp
-         && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
+  if (undefined_weak
+      || (VALID_ITYPE_IMM (symval)
+         || (symval >= gp
+             && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
+         || (symval < gp
+             && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))))
     {
       unsigned sym = hi_reloc.hi_sym;
       switch (ELFNN_R_TYPE (rel->r_info))
        {
        case R_RISCV_PCREL_LO12_I:
-         rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
-         rel->r_addend += hi_reloc.hi_addend;
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero, and then modify the relocation
+                type to R_RISCV_LO12_I.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_I);
+             rel->r_addend = hi_reloc.hi_addend;
+           }
+         else
+           {
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
+             rel->r_addend += hi_reloc.hi_addend;
+           }
          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;
+         if (undefined_weak)
+           {
+             /* Change the RS1 to zero, and then modify the relocation
+                type to R_RISCV_LO12_S.  */
+             bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
+             insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+             bfd_put_32 (abfd, insn, contents + rel->r_offset);
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_S);
+             rel->r_addend = hi_reloc.hi_addend;
+           }
+         else
+           {
+             rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
+             rel->r_addend += hi_reloc.hi_addend;
+           }
          return TRUE;
 
        case R_RISCV_PCREL_HI20:
@@ -3759,7 +3919,8 @@ _bfd_riscv_relax_pc  (bfd *abfd ATTRIBUTE_UNUSED,
                                      rel->r_addend,
                                      symval,
                                      ELFNN_R_SYM(rel->r_info),
-                                     sym_sec);
+                                     sym_sec,
+                                     undefined_weak);
          /* We can delete the unnecessary AUIPC and reloc.  */
          rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
          rel->r_addend = 4;
@@ -3785,7 +3946,8 @@ _bfd_riscv_relax_delete (bfd *abfd,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
                         bfd_boolean *again ATTRIBUTE_UNUSED,
-                        riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+                        riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
 {
   if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend,
                                link_info))
@@ -3851,6 +4013,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       relax_func_t relax_func;
       int type = ELFNN_R_TYPE (rel->r_info);
       bfd_vma symval;
+      char symtype;
+      bfd_boolean undefined_weak = FALSE;
 
       relax_func = NULL;
       if (info->relax_pass == 0)
@@ -3916,7 +4080,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
            ? 0 : isym->st_size - rel->r_addend;
 
          if (isym->st_shndx == SHN_UNDEF)
-           sym_sec = sec, symval = sec_addr (sec) + rel->r_offset;
+           sym_sec = sec, symval = rel->r_offset;
          else
            {
              BFD_ASSERT (isym->st_shndx < elf_numsections (abfd));
@@ -3929,8 +4093,9 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
              if (sec_addr (sym_sec) == 0)
                continue;
 #endif
-             symval = sec_addr (sym_sec) + isym->st_value;
+             symval = isym->st_value;
            }
+         symtype = ELF_ST_TYPE (isym->st_info);
        }
       else
        {
@@ -3944,26 +4109,91 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
-         if (h->plt.offset != MINUS_ONE)
-           symval = sec_addr (htab->elf.splt) + h->plt.offset;
+         if (h->root.type == bfd_link_hash_undefweak
+             && (relax_func == _bfd_riscv_relax_lui
+                 || relax_func == _bfd_riscv_relax_pc))
+           {
+             /* For the lui and auipc relaxations, since the symbol
+                value of an undefined weak symbol is always be zero,
+                we can optimize the patterns into a single LI/MV/ADDI
+                instruction.
+
+                Note that, creating shared libraries and pie output may
+                break the rule above.  Fortunately, since we do not relax
+                pc relocs when creating shared libraries and pie output,
+                and the absolute address access for R_RISCV_HI20 isn't
+                allowed when "-fPIC" is set, the problem of creating shared
+                libraries can not happen currently.  Once we support the
+                auipc relaxations when creating shared libraries, then we will
+                need the more rigorous checking for this optimization.  */
+             undefined_weak = TRUE;
+           }
+
+         /* This line has to match the check in riscv_elf_relocate_section
+            in the R_RISCV_CALL[_PLT] case.  */
+         if (bfd_link_pic (info) && h->plt.offset != MINUS_ONE)
+           {
+             sym_sec = htab->elf.splt;
+             symval = h->plt.offset;
+           }
+         else if (undefined_weak)
+           {
+             symval = 0;
+             sym_sec = bfd_und_section_ptr;
+           }
          else if (h->root.u.def.section->output_section == NULL
                   || (h->root.type != bfd_link_hash_defined
                       && h->root.type != bfd_link_hash_defweak))
            continue;
          else
-           symval = sec_addr (h->root.u.def.section) + h->root.u.def.value;
+           {
+             symval = h->root.u.def.value;
+             sym_sec = h->root.u.def.section;
+           }
 
          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;
+         symtype = h->type;
        }
 
-      symval += rel->r_addend;
+      if (sym_sec->sec_info_type == SEC_INFO_TYPE_MERGE
+          && (sym_sec->flags & SEC_MERGE))
+       {
+         /* At this stage in linking, no SEC_MERGE symbol has been
+            adjusted, so all references to such symbols need to be
+            passed through _bfd_merged_section_offset.  (Later, in
+            relocate_section, all SEC_MERGE symbols *except* for
+            section symbols have been adjusted.)
+
+            gas may reduce relocations against symbols in SEC_MERGE
+            sections to a relocation against the section symbol when
+            the original addend was zero.  When the reloc is against
+            a section symbol we should include the addend in the
+            offset passed to _bfd_merged_section_offset, since the
+            location of interest is the original symbol.  On the
+            other hand, an access to "sym+addend" where "sym" is not
+            a section symbol should not include the addend;  Such an
+            access is presumed to be an offset from "sym";  The
+            location of interest is just "sym".  */
+          if (symtype == STT_SECTION)
+            symval += rel->r_addend;
+
+          symval = _bfd_merged_section_offset (abfd, &sym_sec,
+                                               elf_section_data (sym_sec)->sec_info,
+                                               symval);
+
+          if (symtype != STT_SECTION)
+            symval += rel->r_addend;
+       }
+      else
+       symval += rel->r_addend;
+
+      symval += sec_addr (sym_sec);
 
       if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
                       max_alignment, reserve_size, again,
-                      &pcgp_relocs))
+                      &pcgp_relocs, undefined_weak))
        goto fail;
     }
 
@@ -3978,7 +4208,7 @@ fail:
 }
 
 #if ARCH_SIZE == 32
-# define PRSTATUS_SIZE                 0 /* FIXME */
+# define PRSTATUS_SIZE                 204
 # define PRSTATUS_OFFSET_PR_CURSIG     12
 # define PRSTATUS_OFFSET_PR_PID                24
 # define PRSTATUS_OFFSET_PR_REG                72
This page took 0.036899 seconds and 4 git commands to generate.