readelf: Support SHT_RELR/DT_RELR for -r
authorFangrui Song <maskray@google.com>
Tue, 16 Nov 2021 21:03:57 +0000 (13:03 -0800)
committerFangrui Song <maskray@google.com>
Tue, 16 Nov 2021 21:04:33 +0000 (13:04 -0800)
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:

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/ChangeLog
binutils/NEWS
binutils/readelf.c
include/ChangeLog
include/elf/common.h
include/elf/external.h

index 6850edb9dd54dc052ac1e47f64a46d9d1cd4b79f..8d2f0413f9f4a7c2322fb0f9966147189213849a 100644 (file)
@@ -1,3 +1,14 @@
+2021-11-16  Fangrui Song  <maskray@google.com>
+
+       * 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.
+
 2021-11-09  Nick Clifton  <nickc@redhat.com>
 
        * nm.c: Add --unicode option to control how unicode characters are
index f948d34f0918084bcaada6eada32fb2de9eb14ca..f3881250fce4dc02b498a4e20205068c235b68ed 100644 (file)
@@ -11,6 +11,8 @@
   using --unicode=highlight will display them as unicode escape sequences
   highlighted in red (if supported by the output device).
 
+* readelf -r dumps RELR relative relocations now.
+
 Changes in 2.37:
 
 * The readelf tool has a new command line option which can be used to specify
index 19c64918c0696ee0d4c8c219d7138827ce054351..116f879c892a05e3297332c17194a5d1a540f659 100644 (file)
@@ -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:
index 0e99cf39170d915d70f7f0b78e4962c4e853c8e0..52483238ffb7508a4aa8e66a357f299dc450fef3 100644 (file)
@@ -1,3 +1,9 @@
+2021-11-16  Fangrui Song  <maskray@google.com>
+
+       * elf/common.h (DT_ENCODING): Bump to 38.
+       * elf/external.h (Elf32_External_Relr): New.
+       (Elf64_External_Relr): New.
+
 2021-09-07  Luis Machado  <luis.machado@linaro.org>
 
        Revert: [AArch64] MTE corefile support
index 4da28fa24a490e61e5be3876e205eabd919e7dc9..5d31e35ec8953f97e7bf0b7a4c06ecc6ec0c5b8b 100644 (file)
 #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
index b24985687e6774f05badbb414285b1eaead38647..815e39c2837690330409d2694fb93025887a6e20 100644 (file)
@@ -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 {