[PATCH] LoongArch: Use copy relocation for %pc_lo12 against external symbol

Xi Ruoyao xry111@xry111.site
Wed Aug 31 13:22:59 GMT 2022


We'd like to use PC-relative addressing instead of GOT for external
symbols in main executable images.  This can improve code locality.
Doing so will need to implement copy relocation.

Despite there was a comment saying "Glibc does not support copy
relocation yet", I've run a very simple test and it seems copy
relocation is handled by ld.so without a problem:

	pcalau12i       $a1, %pc_hi20(stdout)
	ld.d            $a1, $a1, %pc_lo12(stdout)
	pcalau12i       $a0, %pc_hi20(msg)
	addi.d          $a0, $a0, %pc_lo12(msg)
	pcalau12i       $ra, %pc_hi20(fputs)
	jirl            $ra, $ra, %pc_lo12(fputs)

With this patch, R_LARCH_COPY is correctly emitted for "stdout" in
Glibc, and the test program runs and outputs "Hello world" in "msg"
successfully.
---
 bfd/elfnn-loongarch.c | 93 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 9 deletions(-)

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index ed42b8b6770..0cf24bd24d8 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -743,14 +743,26 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    h->non_got_ref = 1;
 	  break;
 
-	case R_LARCH_PCALA_HI20:
+	case R_LARCH_PCALA_LO12:
 	  if (h != NULL)
 	    {
-	      /* For pcalau12i + jirl.  */
-	      h->needs_plt = 1;
-	      if (h->plt.refcount < 0)
-		h->plt.refcount = 0;
-	      h->plt.refcount++;
+	      /* Check if it's a jirl instruction.  */
+	      bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+	      uint32_t insn = 0;
+
+	      if (contents || bfd_malloc_and_get_section (abfd, sec, &contents))
+		memcpy(&insn, contents + rel->r_offset, sizeof(insn));
+
+	      if ((insn & 0xfc000000) == 0x4c000000)
+		{
+		  /* For pcalau12i + jirl.  */
+		  h->needs_plt = 1;
+		  if (h->plt.refcount < 0)
+		    h->plt.refcount = 0;
+		  h->plt.refcount++;
+		}
+	      else
+		need_dynreloc = 1;
 
 	      h->non_got_ref = 1;
 	      h->pointer_equality_needed = 1;
@@ -949,7 +961,9 @@ loongarch_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 				     struct elf_link_hash_entry *h)
 {
   struct loongarch_elf_link_hash_table *htab;
+  struct loongarch_elf_link_hash_entry *hent;
   bfd *dynobj;
+  asection *s, *srel;
 
   htab = loongarch_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
@@ -999,9 +1013,52 @@ loongarch_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       return true;
     }
 
-  /* R_LARCH_COPY is not adept glibc, not to generate.  */
-  /* Can not print anything, because make check ld.  */
-  return true;
+  /* If copy relocations is disabled via -z nocopyreloc, or we don't find any
+     dynamic relocs in read-only sections, avoid the copy reloc.  */
+  if (info->nocopyreloc || !_bfd_elf_readonly_dynrelocs (h))
+    h->non_got_ref = 0;
+
+  if (!h->non_got_ref)
+    return true;
+
+  /* We must allocate the symbol in our .dynbss section, which will
+     become part of the .bss section of the executable.  There will be
+     an entry for this symbol in the .dynsym section.  The dynamic
+     object will contain position independent code, so all references
+     from the dynamic object to this symbol will go through the global
+     offset table.  The dynamic linker will use the .dynsym entry to
+     determine the address it must put in the global offset table, so
+     both the dynamic object and the regular object will refer to the
+     same memory location for the variable.  */
+
+  /* Generate a copy relocation to tell the dynamic linker to copy the
+     initial value out of the dynamic object and into the runtime process
+     image.  We need to remember the offset into the .rel.bss section we
+     are going to use.  */
+  hent = (struct loongarch_elf_link_hash_entry *) h;
+  if (hent->tls_type & ~GOT_NORMAL)
+    {
+      s = htab->sdyntdata;
+      srel = htab->elf.srelbss;
+    }
+  else if (h->root.u.def.section->flags & SEC_READONLY)
+    {
+      s = htab->elf.sdynrelro;
+      srel = htab->elf.sreldynrelro;
+    }
+  else
+    {
+      s = htab->elf.sdynbss;
+      srel = htab->elf.srelbss;
+    }
+
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
+    {
+      srel->size += sizeof (ElfNN_External_Rela);
+      h->needs_copy = 1;
+    }
+
+  return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
@@ -3702,6 +3759,24 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
       loongarch_elf_append_rela (output_bfd, srela, &rela);
     }
 
+  if (h->needs_copy)
+    {
+      Elf_Internal_Rela rela;
+      asection *s;
+
+      /* This symbols needs a copy reloc.  Set it up.  */
+      BFD_ASSERT (h->dynindx != -1);
+
+      rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value;
+      rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_COPY);
+      rela.r_addend = 0;
+      if (h->root.u.def.section == htab->elf.sdynrelro)
+	s = htab->elf.sreldynrelro;
+      else
+	s = htab->elf.srelbss;
+      loongarch_elf_append_rela (output_bfd, s, &rela);
+    }
+
   /* Mark some specially defined symbols as absolute.  */
   if (h == htab->elf.hdynamic || h == htab->elf.hgot || h == htab->elf.hplt)
     sym->st_shndx = SHN_ABS;
-- 
2.37.0



More information about the Binutils mailing list