[2/2] RISC-V: PR27180, update changed relocations when relocating, for --emit-relocs.

Message ID 20210520085724.8972-2-nelson.chu@sifive.com
State New
Headers show
Series
  • [1/2] RISC-V: Clarify the addends of pc-relative access.
Related show

Commit Message

Nelson Chu May 20, 2021, 8:57 a.m.
The %pcrel_hi and %got_pcrel_hi may be changed to %hi in the
riscv_zero_pcrel_hi_reloc, since the range between pc and symbols
are not enough, but the symbols are placed in 32-bit addressing,
so we can use lui + addi/load/store to access them directly.

Once we change the pc-relative access to absolute access, we will
get segmant fault when --emit-relocs is enbaled, since we encode
the symbol address into r_info directly.  The solution is that we
only update the relocation type, and then keep the symbol index and
addend as before.  Besids, we also need to update the %pcrel_lo to
%lo in the riscv_resolve_pcrel_lo_relocs, so we need know the symbol
and addend of %pcrel_hi.  That's why I record the high part internal
relocation in the riscv_pcrel_hi_reloc.

The case of %got_pcrel_hi is more complicated.  For now I have two
choices when --emit-relocs is enabled,

* Set the symindx to 0, and then encode the address of got entry into
r_addend directly, like what RELATIVE relocs do.

* Insert symbols for all got entries, then we can refer to those symbols
directly.  But there is a problem that where should we add these symbols?
I think we should add them into dynamic symbol table, since the got is
created by linker.  But how can we encode the dynamic symbol index
into the input relocation?

I'm confused by the second choice, so I choose the first one for now.

bfd/
    * elfnn-riscv.c (riscv_pcrel_hi_reloc): Added field to store
    internal relocation.
    (riscv_pcrel_lo_reloc): Removed const of Elf_Internal_Rela, since
    we need to update the %pcrel_lo later.
    (riscv_zero_pcrel_hi_reloc): For %pcrel_hi, keep the symbol index
    and addend as before, only update the relocation type to avoid
    the segmant fault.  For --emit-relocs and %got_pcrel_hi, set the
    symindx to 0, and then clean the input relocation to zero since
    it is already encoded into r_addend directly.
    (riscv_record_pcrel_hi_reloc): Updated.
    (riscv_record_pcrel_lo_reloc): Likewise.
    (riscv_resolve_pcrel_lo_relocs): For --emit-relocs, if the %pcrel_hi
    and %got_pcrel_hi are changed to %hi, we should also update the
    %pcrel_lo to %lo.  Encode the hi_symindx into r_info, and then
    plus the hi_addend into r_addend.
    (riscv_elf_relocate_section): Updated.
ld/
    * testsuite/ld-riscv-elf/ld-riscv-elf.exp: Updated.
    * testsuite/ld-riscv-elf/pcrel-emit-relocs-01.d: New testcase,
    the riscv_zero_pcrel_hi_reloc won't do anything.
    * testsuite/ld-riscv-elf/pcrel-emit-relocs-02.d: Likewise, but
    the riscv_zero_pcrel_hi_reloc will change pc-relative to absolute
    access directly.
---
 bfd/elfnn-riscv.c                             | 88 ++++++++++++++-----
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |  2 +
 .../ld-riscv-elf/pcrel-emit-relocs-01.d       | 15 ++++
 .../ld-riscv-elf/pcrel-emit-relocs-02.d       | 15 ++++
 4 files changed, 98 insertions(+), 22 deletions(-)
 create mode 100644 ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-02.d

-- 
2.30.2

Patch

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index 16e6c1e1484..57b8c5e795e 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1737,10 +1737,12 @@  typedef struct
 {
   /* PC value.  */
   bfd_vma address;
-  /* Relocation value with addend.  */
+  /* Relocation value without addend.  */
   bfd_vma value;
   /* Original reloc type.  */
   int type;
+  /* Internal relocation.  */
+  const Elf_Internal_Rela *reloc;
 } riscv_pcrel_hi_reloc;
 
 typedef struct riscv_pcrel_lo_reloc
@@ -1748,7 +1750,7 @@  typedef struct riscv_pcrel_lo_reloc
   /* PC value of auipc.  */
   bfd_vma addr;
   /* Internal relocation.  */
-  const Elf_Internal_Rela *reloc;
+  Elf_Internal_Rela *reloc;
   /* The next riscv_pcrel_lo_reloc.  */
   struct riscv_pcrel_lo_reloc *next;
 } riscv_pcrel_lo_reloc;
@@ -1803,7 +1805,7 @@  static bool
 riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
 			   struct bfd_link_info *info,
 			   bfd_vma pc,
-			   bfd_vma addr,
+			   bfd_vma *addr,
 			   bfd_byte *contents,
 			   const reloc_howto_type *howto)
 {
@@ -1819,17 +1821,30 @@  riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
 
   /* If it's possible to reference the symbol using auipc we do so, as that's
      more in the spirit of the PC-relative relocations we're processing.  */
-  bfd_vma offset = addr - pc;
+  bfd_vma offset = *addr - pc;
   if (ARCH_SIZE == 32 || VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (offset)))
     return false;
 
   /* If it's impossible to reference this with a LUI-based offset then don't
      bother to convert it at all so users still see the PC-relative relocation
      in the truncation message.  */
-  if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (addr)))
+  if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (*addr)))
     return false;
 
-  rel->r_info = ELFNN_R_INFO (addr, R_RISCV_HI20);
+  unsigned long hi_symindx = ELFNN_R_SYM (rel->r_info);
+  bfd_vma hi_addend = rel->r_addend;
+  if (info->emitrelocations
+      && ELFNN_R_TYPE (rel->r_info) == R_RISCV_GOT_HI20)
+    {
+      /* For --emit-relocs, set symindx to 0, and then encode the
+        address of got entry into r_addend directly, like what
+        RELATIVE relocs do.  */
+      hi_symindx = 0;
+      hi_addend = *addr;
+      *addr = 0;
+    }
+  rel->r_info = ELFNN_R_INFO (hi_symindx, R_RISCV_HI20);
+  rel->r_addend = hi_addend;
 
   bfd_vma insn = riscv_get_insn (howto->bitsize, contents + rel->r_offset);
   insn = (insn & ~MASK_AUIPC) | MATCH_LUI;
@@ -1842,10 +1857,11 @@  riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p,
 			     bfd_vma addr,
 			     bfd_vma value,
 			     int type,
+			     Elf_Internal_Rela *reloc,
 			     bool absolute)
 {
   bfd_vma offset = absolute ? value : value - addr;
-  riscv_pcrel_hi_reloc entry = {addr, offset, type};
+  riscv_pcrel_hi_reloc entry = {addr, offset, type, reloc};
   riscv_pcrel_hi_reloc **slot =
     (riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT);
 
@@ -1860,7 +1876,7 @@  riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p,
 static bool
 riscv_record_pcrel_lo_reloc (riscv_pcrel_relocs *p,
 			     bfd_vma addr,
-			     const Elf_Internal_Rela *reloc)
+			     Elf_Internal_Rela *reloc)
 {
   riscv_pcrel_lo_reloc *entry;
   entry = (riscv_pcrel_lo_reloc *) bfd_malloc (sizeof (riscv_pcrel_lo_reloc));
@@ -1882,7 +1898,7 @@  riscv_resolve_pcrel_lo_relocs (struct bfd_link_info *info,
 
   for (r = p->lo_relocs; r != NULL; r = r->next)
     {
-      riscv_pcrel_hi_reloc search = {r->addr, 0, 0};
+      riscv_pcrel_hi_reloc search = {r->addr, 0, 0, NULL};
       riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
 
       /* There may be a risk if the %pcrel_lo with addend refers to
@@ -1894,8 +1910,10 @@  riscv_resolve_pcrel_lo_relocs (struct bfd_link_info *info,
       else if (entry->type == R_RISCV_GOT_HI20
 	       && r->reloc->r_addend != 0)
 	string = _("%pcrel_lo with addend isn't allowed for R_RISCV_GOT_HI20");
-      else if (RISCV_CONST_HIGH_PART (entry->value)
+      else if (RISCV_CONST_HIGH_PART (entry->value
+				      + entry->reloc->r_addend)
 	       != RISCV_CONST_HIGH_PART (entry->value
+					 + entry->reloc->r_addend
 					 + r->reloc->r_addend))
 	{
 	  /* Check the overflow when adding reloc addend.  */
@@ -1904,9 +1922,13 @@  riscv_resolve_pcrel_lo_relocs (struct bfd_link_info *info,
 			  "value of %%pcrel_hi is 0x%" PRIx64 " without "
 			  "any addend, but may be 0x%" PRIx64 " after "
 			  "adding the %%pcrel_lo addend"),
-			(int64_t) RISCV_CONST_HIGH_PART (entry->value),
 			(int64_t) RISCV_CONST_HIGH_PART
-				(entry->value + r->reloc->r_addend)) == -1)
+				(entry->value
+				 + entry->reloc->r_addend),
+			(int64_t) RISCV_CONST_HIGH_PART
+				(entry->value
+				 + entry->reloc->r_addend
+				 + r->reloc->r_addend)) == -1)
 	    string = _("%pcrel_lo overflow with an addend");
 	}
 
@@ -1919,8 +1941,30 @@  riscv_resolve_pcrel_lo_relocs (struct bfd_link_info *info,
 
       int type = ELFNN_R_TYPE (r->reloc->r_info);
       reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, type);
-      perform_relocation (howto, r->reloc, entry->value, input_section,
-			  input_bfd, contents);
+      perform_relocation (howto, r->reloc, entry->value + entry->reloc->r_addend,
+			  input_section, input_bfd, contents);
+      /* For --emit-relocs, if the corresponding R_RISCV_PCREL_HI20 is
+	 converted to R_RISCV_HI20, try to convert the R_RISCV_PCREL_LO12
+	 to R_RISCV_LO12.  */
+      if (ELFNN_R_TYPE (entry->reloc->r_info) == R_RISCV_HI20
+	  && info->emitrelocations)
+	{
+	  unsigned long hi_symindx = ELFNN_R_SYM (entry->reloc->r_info);
+	  switch (type)
+	    {
+	    case R_RISCV_PCREL_LO12_I:
+	      r->reloc->r_info = ELFNN_R_INFO (hi_symindx, R_RISCV_LO12_I);
+	      r->reloc->r_addend += entry->reloc->r_addend;
+	      break;
+	    case R_RISCV_PCREL_LO12_S:
+	      r->reloc->r_info = ELFNN_R_INFO (hi_symindx, R_RISCV_LO12_S);
+	      r->reloc->r_addend += entry->reloc->r_addend;
+	      break;
+	    default:
+	      /* This shouldn't happen, so just skip it.  */
+	      break;
+	    }
+	}
     }
 
   return true;
@@ -2237,7 +2281,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 
 		if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
 						  relocation, r_type,
-						  false))
+						  rel, false))
 		  r = bfd_reloc_overflow;
 		goto do_relocation;
 
@@ -2251,7 +2295,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	      case R_RISCV_PCREL_HI20:
 		if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
 						  relocation, r_type,
-						  false))
+						  rel, false))
 		  r = bfd_reloc_overflow;
 		goto do_relocation;
 
@@ -2399,7 +2443,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	      /* Address of got entry.  */
 	      relocation = sec_addr (htab->elf.sgot) + off;
 	      absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc,
-						    relocation, contents,
+						    &relocation, contents,
 						    howto);
 	      /* Update howto if relocation is changed.  */
 	      howto = riscv_elf_rtype_to_howto (input_bfd,
@@ -2408,7 +2452,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 		r = bfd_reloc_notsupported;
 	      else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
 						     relocation, r_type,
-						     absolute))
+						     rel, absolute))
 		r = bfd_reloc_overflow;
 	    }
 	  break;
@@ -2511,7 +2555,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	  }
 
 	case R_RISCV_PCREL_HI20:
-	  absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, relocation,
+	  absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, &relocation,
 						contents, howto);
 	  /* Update howto if relocation is changed.  */
 	  howto = riscv_elf_rtype_to_howto (input_bfd,
@@ -2519,8 +2563,8 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	  if (howto == NULL)
 	    r = bfd_reloc_notsupported;
 	  else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-						 relocation + rel->r_addend,
-						 r_type, absolute))
+						 relocation, r_type,
+						 rel, absolute))
 	    r = bfd_reloc_overflow;
 	  break;
 
@@ -2733,7 +2777,7 @@  riscv_elf_relocate_section (bfd *output_bfd,
 	  relocation = sec_addr (htab->elf.sgot) + off + (is_ie ? ie_off : 0);
 	  if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
 					    relocation, r_type,
-					    false))
+					    rel, false))
 	    r = bfd_reloc_overflow;
 	  unresolved_reloc = false;
 	  break;
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index bff1b479c80..0dca2d3fc5b 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -90,6 +90,8 @@  if [istarget "riscv*-*-*"] {
     run_dump_test "pcrel-lo-addend-3a"
     run_dump_test "pcrel-lo-addend-3b"
     run_dump_test "pcrel-lo-addend-3c"
+    run_dump_test "pcrel-emit-relocs-01"
+    run_dump_test "pcrel-emit-relocs-02"
     run_dump_test "restart-relax"
     run_dump_test "attr-merge-arch-01"
     run_dump_test "attr-merge-arch-02"
diff --git a/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-01.d b/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-01.d
new file mode 100644
index 00000000000..0d3395b14c1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-01.d
@@ -0,0 +1,15 @@ 
+#source: pcrel-lo-addend-3a.s
+#as: -march=rv64i -mabi=lp64 -mno-relax
+#ld: -m[riscv_choose_lp64_emul] --emit-relocs
+#readelf: -rW
+
+Relocation section '.rela.text' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_HI20[ 	]+[0-9a-f]+[ 	]+ll \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.LA0 \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.LA0 \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_HI20[ 	]+[0-9a-f]+[ 	]+ll \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.LA1 \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.LA1 \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_GOT_HI20[ 	]+[0-9a-f]+[ 	]+ll \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.LA2 \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-02.d b/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-02.d
new file mode 100644
index 00000000000..c9071e9f8c6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/pcrel-emit-relocs-02.d
@@ -0,0 +1,15 @@ 
+#source: pcrel-lo-addend-3a.s
+#as: -march=rv64i -mabi=lp64 -mno-relax
+#ld: -m[riscv_choose_lp64_emul] --emit-relocs -Tpcrel-lo-addend-3.ld
+#readelf: -rW
+
+Relocation section '.rela.text' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_HI20[ 	]+[0-9a-f]+[ 	]+ll \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_LO12_I[ 	]+[0-9a-f]+[ 	]+ll \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_LO12_I[ 	]+[0-9a-f]+[ 	]+ll \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_HI20[ 	]+[0-9a-f]+[ 	]+ll \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_LO12_I[ 	]+[0-9a-f]+[ 	]+ll \+ 4
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_LO12_I[ 	]+[0-9a-f]+[ 	]+ll \+ 8
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_HI20[ 	]+1008
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_LO12_I[ 	]+1008