[PATCH] readelf: Support SHT_RELR/DT_RELR for -r

Fangrui Song maskray@google.com
Tue Nov 16 07:23:55 GMT 2021


The -r output for SHT_RELR looks like:

Relocation section '.relr.dyn' at offset 0x530 contains 4 entries:
  7 offsets
00000000000028c0
00000000000028c8
0000000000003ad0
0000000000003ad8
0000000000003ae0
0000000000003ae8
0000000000003af0

For --use-dynamic, the header looks like

    'RELR' relocation section at offset 0x530 contains 32 bytes:

(I wish a GNU ld expert can work on
the feature request
https://sourceware.org/bugzilla/show_bug.cgi?id=27923)

include/
    * elf/common.h (DT_ENCODING): Bump to 38.
    * elf/external.h (Elf32_External_Relr): New.
    (Elf64_External_Relr): New.
binutils/
    * readelf.c (enum relocation_type): New.
    (slurp_relr_relocs): New.
    (dump_relocations): Change is_rela to rel_type.
    Dump RELR.
    (dynamic_relocations): Add DT_RELR.
    (process_relocs): Check SHT_RELR and DT_RELR.
    (process_dynamic_section): Store into dynamic_info for
    DT_RELR/DT_RELRENT/DT_RELRSZ.
---
 binutils/readelf.c     | 154 +++++++++++++++++++++++++++++++++--------
 include/elf/common.h   |   2 +-
 include/elf/external.h |   8 +++
 3 files changed, 134 insertions(+), 30 deletions(-)

diff --git a/binutils/readelf.c b/binutils/readelf.c
index 19c64918c06..116f879c892 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -342,7 +342,14 @@ typedef enum unicode_display_type
 
 static unicode_display_type unicode_display = unicode_default;
 
-  
+typedef enum
+{
+  reltype_unknown,
+  reltype_rel,
+  reltype_rela,
+  reltype_relr
+} relocation_type;
+
 /* Versioned symbol info.  */
 enum versioned_symbol_info
 {
@@ -1354,6 +1361,76 @@ slurp_rel_relocs (Filedata *            filedata,
   return true;
 }
 
+static bool
+slurp_relr_relocs (Filedata * filedata,
+		   unsigned long relr_offset,
+		   unsigned long relr_size,
+		   bfd_vma ** relrsp,
+		   unsigned long * nrelrsp)
+{
+  void *relrs;
+  size_t size = 0, nentries, i;
+  bfd_vma base = 0, addr, entry;
+
+  relrs = get_data (NULL, filedata, relr_offset, 1, relr_size,
+		    _("RELR relocation data"));
+  if (!relrs)
+    return false;
+
+  if (is_32bit_elf)
+    nentries = relr_size / sizeof (Elf32_External_Relr);
+  else
+    nentries = relr_size / sizeof (Elf64_External_Relr);
+  for (i = 0; i < nentries; i++)
+    {
+      if (is_32bit_elf)
+	entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data);
+      else
+	entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data);
+      if ((entry & 1) == 0)
+	size++;
+      else
+	while ((entry >>= 1) != 0)
+	  if ((entry & 1) == 1)
+	    size++;
+    }
+
+  *relrsp = (bfd_vma *) xmalloc (size * sizeof (bfd_vma));
+  if (*relrsp == NULL)
+    {
+      free (relrs);
+      error (_("out of memory parsing relocs\n"));
+      return false;
+    }
+
+  size = 0;
+  for (i = 0; i < nentries; i++)
+    {
+      const bfd_vma entry_bytes = is_32bit_elf ? 4 : 8;
+
+      if (is_32bit_elf)
+	entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data);
+      else
+	entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data);
+      if ((entry & 1) == 0)
+	{
+	  (*relrsp)[size++] = entry;
+	  base = entry + entry_bytes;
+	}
+      else
+	{
+	  for (addr = base; (entry >>= 1) != 0; addr += entry_bytes)
+	    if ((entry & 1) != 0)
+	      (*relrsp)[size++] = addr;
+	  base += entry_bytes * (entry_bytes * CHAR_BIT - 1);
+	}
+    }
+
+  *nrelrsp = size;
+  free (relrs);
+  return true;
+}
+
 /* Returns the reloc type extracted from the reloc info field.  */
 
 static unsigned int
@@ -1406,30 +1483,46 @@ dump_relocations (Filedata *          filedata,
 		  unsigned long       nsyms,
 		  char *              strtab,
 		  unsigned long       strtablen,
-		  int                 is_rela,
+		  relocation_type     rel_type,
 		  bool                is_dynsym)
 {
   unsigned long i;
   Elf_Internal_Rela * rels;
   bool res = true;
 
-  if (is_rela == UNKNOWN)
-    is_rela = guess_is_rela (filedata->file_header.e_machine);
+  if (rel_type == reltype_unknown)
+    rel_type = guess_is_rela (filedata->file_header.e_machine) ? reltype_rela : reltype_rel;
 
-  if (is_rela)
+  if (rel_type == reltype_rela)
     {
       if (!slurp_rela_relocs (filedata, rel_offset, rel_size, &rels, &rel_size))
 	return false;
     }
-  else
+  else if (rel_type == reltype_rel)
     {
       if (!slurp_rel_relocs (filedata, rel_offset, rel_size, &rels, &rel_size))
 	return false;
     }
+  else if (rel_type == reltype_relr)
+    {
+      bfd_vma * relrs;
+      const char *format
+	  = is_32bit_elf ? "%08" BFD_VMA_FMT "x\n" : "%016" BFD_VMA_FMT "x\n";
+
+      if (!slurp_relr_relocs (filedata, rel_offset, rel_size, &relrs,
+			      &rel_size))
+	return false;
+
+      printf (ngettext ("  %lu offset\n", "  %lu offsets\n", rel_size), rel_size);
+      for (i = 0; i < rel_size; i++)
+	printf (format, relrs[i]);
+      free (relrs);
+      return true;
+    }
 
   if (is_32bit_elf)
     {
-      if (is_rela)
+      if (rel_type == reltype_rela)
 	{
 	  if (do_wide)
 	    printf (_(" Offset     Info    Type                Sym. Value  Symbol's Name + Addend\n"));
@@ -1446,7 +1539,7 @@ dump_relocations (Filedata *          filedata,
     }
   else
     {
-      if (is_rela)
+      if (rel_type == reltype_rela)
 	{
 	  if (do_wide)
 	    printf (_("    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend\n"));
@@ -1841,7 +1934,7 @@ dump_relocations (Filedata *          filedata,
       if (filedata->file_header.e_machine == EM_ALPHA
 	  && rtype != NULL
 	  && streq (rtype, "R_ALPHA_LITUSE")
-	  && is_rela)
+	  && rel_type == reltype_rela)
 	{
 	  switch (rels[i].r_addend)
 	    {
@@ -1989,7 +2082,7 @@ dump_relocations (Filedata *          filedata,
 			    version_string);
 		}
 
-	      if (is_rela)
+	      if (rel_type == reltype_rela)
 		{
 		  bfd_vma off = rels[i].r_addend;
 
@@ -2000,7 +2093,7 @@ dump_relocations (Filedata *          filedata,
 		}
 	    }
 	}
-      else if (is_rela)
+      else if (rel_type == reltype_rela)
 	{
 	  bfd_vma off = rels[i].r_addend;
 
@@ -8021,13 +8114,14 @@ static struct
   const char * name;
   int reloc;
   int size;
-  int rela;
+  relocation_type rel_type;
 }
   dynamic_relocations [] =
 {
-  { "REL", DT_REL, DT_RELSZ, false },
-  { "RELA", DT_RELA, DT_RELASZ, true },
-  { "PLT", DT_JMPREL, DT_PLTRELSZ, UNKNOWN }
+  { "REL", DT_REL, DT_RELSZ, reltype_rel },
+  { "RELA", DT_RELA, DT_RELASZ, reltype_rela },
+  { "RELR", DT_RELR, DT_RELRSZ, reltype_relr },
+  { "PLT", DT_JMPREL, DT_PLTRELSZ, reltype_unknown }
 };
 
 /* Process the reloc section.  */
@@ -8043,7 +8137,7 @@ process_relocs (Filedata * filedata)
 
   if (do_using_dynamic)
     {
-      int          is_rela;
+      relocation_type rel_type;
       const char * name;
       bool  has_dynamic_reloc;
       unsigned int i;
@@ -8052,7 +8146,7 @@ process_relocs (Filedata * filedata)
 
       for (i = 0; i < ARRAY_SIZE (dynamic_relocations); i++)
 	{
-	  is_rela = dynamic_relocations [i].rela;
+	  rel_type = dynamic_relocations [i].rel_type;
 	  name = dynamic_relocations [i].name;
 	  rel_size = filedata->dynamic_info[dynamic_relocations [i].size];
 	  rel_offset = filedata->dynamic_info[dynamic_relocations [i].reloc];
@@ -8060,16 +8154,16 @@ process_relocs (Filedata * filedata)
 	  if (rel_size)
 	    has_dynamic_reloc = true;
 
-	  if (is_rela == UNKNOWN)
+	  if (rel_type == reltype_unknown)
 	    {
 	      if (dynamic_relocations [i].reloc == DT_JMPREL)
 		switch (filedata->dynamic_info[DT_PLTREL])
 		  {
 		  case DT_REL:
-		    is_rela = false;
+		    rel_type = reltype_rel;
 		    break;
 		  case DT_RELA:
-		    is_rela = true;
+		    rel_type = reltype_rela;
 		    break;
 		  }
 	    }
@@ -8092,7 +8186,7 @@ process_relocs (Filedata * filedata)
 				filedata->num_dynamic_syms,
 				filedata->dynamic_strings,
 				filedata->dynamic_strings_length,
-				is_rela, true /* is_dynamic */);
+				rel_type, true /* is_dynamic */);
 	    }
 	}
 
@@ -8120,7 +8214,8 @@ process_relocs (Filedata * filedata)
 	   i++, section++)
 	{
 	  if (   section->sh_type != SHT_RELA
-	      && section->sh_type != SHT_REL)
+	      && section->sh_type != SHT_REL
+	      && section->sh_type != SHT_RELR)
 	    continue;
 
 	  rel_offset = section->sh_offset;
@@ -8128,7 +8223,7 @@ process_relocs (Filedata * filedata)
 
 	  if (rel_size)
 	    {
-	      int is_rela;
+	      relocation_type rel_type;
 	      unsigned long num_rela;
 
 	      if (filedata->is_separate)
@@ -8148,7 +8243,8 @@ process_relocs (Filedata * filedata)
 				num_rela),
 		      rel_offset, num_rela);
 
-	      is_rela = section->sh_type == SHT_RELA;
+	      rel_type = section->sh_type == SHT_RELA ? reltype_rela :
+		section->sh_type == SHT_REL ? reltype_rel : reltype_relr;
 
 	      if (section->sh_link != 0
 		  && section->sh_link < filedata->file_header.e_shnum)
@@ -8170,15 +8266,14 @@ process_relocs (Filedata * filedata)
 
 		  dump_relocations (filedata, rel_offset, rel_size,
 				    symtab, nsyms, strtab, strtablen,
-				    is_rela,
+				    rel_type,
 				    symsec->sh_type == SHT_DYNSYM);
 		  free (strtab);
 		  free (symtab);
 		}
 	      else
 		dump_relocations (filedata, rel_offset, rel_size,
-				  NULL, 0, NULL, 0, is_rela,
-				  false /* is_dynamic */);
+				  NULL, 0, NULL, 0, rel_type, false /* is_dynamic */);
 
 	      found = true;
 	    }
@@ -11499,6 +11594,7 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n"));
 	case DT_RPATH	:
 	case DT_SYMBOLIC:
 	case DT_REL	:
+	case DT_RELR    :
 	case DT_DEBUG	:
 	case DT_TEXTREL	:
 	case DT_JMPREL	:
@@ -11555,6 +11651,8 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n"));
 	case DT_STRSZ	:
 	case DT_RELSZ	:
 	case DT_RELAENT	:
+	case DT_RELRENT	:
+	case DT_RELRSZ	:
 	case DT_SYMENT	:
 	case DT_RELENT	:
 	  filedata->dynamic_info[entry->d_tag] = entry->d_un.d_val;
@@ -11562,8 +11660,6 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n"));
 	case DT_PLTPADSZ:
 	case DT_MOVEENT	:
 	case DT_MOVESZ	:
-	case DT_RELRENT	:
-	case DT_RELRSZ	:
 	case DT_PREINIT_ARRAYSZ:
 	case DT_INIT_ARRAYSZ:
 	case DT_FINI_ARRAYSZ:
diff --git a/include/elf/common.h b/include/elf/common.h
index 4da28fa24a4..5d31e35ec89 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -1102,13 +1102,13 @@
 #define DT_FINI_ARRAYSZ 28
 #define DT_RUNPATH	29
 #define DT_FLAGS	30
-#define DT_ENCODING	32
 #define DT_PREINIT_ARRAY   32
 #define DT_PREINIT_ARRAYSZ 33
 #define DT_SYMTAB_SHNDX    34
 #define DT_RELRSZ	35
 #define DT_RELR		36
 #define DT_RELRENT	37
+#define DT_ENCODING	38
 
 /* Note, the Oct 4, 1999 draft of the ELF ABI changed the values
    for DT_LOOS and DT_HIOS.  Some implementations however, use
diff --git a/include/elf/external.h b/include/elf/external.h
index b24985687e6..815e39c2837 100644
--- a/include/elf/external.h
+++ b/include/elf/external.h
@@ -211,6 +211,10 @@ typedef struct {
   unsigned char	r_addend[4];	/* Constant addend used to compute value */
 } Elf32_External_Rela;
 
+typedef struct {
+  unsigned char r_data[4];	/* RELR entry */
+} Elf32_External_Relr;
+
 typedef struct {
   unsigned char r_offset[8];	/* Location at which to apply the action */
   unsigned char	r_info[8];	/* index and type of relocation */
@@ -222,6 +226,10 @@ typedef struct {
   unsigned char	r_addend[8];	/* Constant addend used to compute value */
 } Elf64_External_Rela;
 
+typedef struct {
+  unsigned char r_data[8];	/* RELR entry */
+} Elf64_External_Relr;
+
 /* dynamic section structure */
 
 typedef struct {
-- 
2.34.0.rc1.387.gb447b232ab-goog



More information about the Binutils mailing list