readelf: report DF_1_PIE as "Position-Independent Executable"
authorAlan Modra <amodra@gmail.com>
Fri, 11 Jun 2021 04:53:57 +0000 (14:23 +0930)
committerAlan Modra <amodra@gmail.com>
Tue, 15 Jun 2021 03:54:57 +0000 (13:24 +0930)
I finally found time to teach readelf to identify PIEs in the file
header display and program header display.  So in place of
"DYN (Shared object file)" which isn't completely true, show
"DYN (Position-Independent Executable file)".

It requires a little bit of untangling code in readelf due to
process_program_headers setting up dynamic_addr and dynamic_size,
needed to scan .dynamic for the DT_FLAGS_1 entry, and
process_program_headers itself wanting to display the file type in
some cases.  At first I modified process_program_header using a
"probe" parameter similar to get_section_headers in order to inhibit
output, but decided it was cleaner to separate out
locate_dynamic_sections.

binutils/
* readelf.c (locate_dynamic_section, is_pie): New functions.
(get_file_type): Replace e_type parameter with filedata.  Call
is_pie for ET_DYN.  Update all callers.
(process_program_headers): Use local variables dynamic_addr and
dynamic_size, updating filedata on exit from function.  Set
dynamic_size of 1 to indicate no dynamic section or segment.
Update tests of dynamic_size throughout.
* testsuite/binutils-all/x86-64/pr27708.dump: Update expected output.
ld/
* testsuite/ld-pie/vaddr-0.d: Update expected output.
gdb/
* testsuite/lib/gdb.exp (exec_is_pie): Match new PIE readelf output.

binutils/ChangeLog
binutils/readelf.c
binutils/testsuite/binutils-all/x86-64/pr27708.dump
gdb/ChangeLog
gdb/testsuite/lib/gdb.exp
ld/ChangeLog
ld/testsuite/ld-pie/vaddr-0.d

index 7a9fa1de0641635d1da27bd3fa9c80fb5823dd0c..c70a45a6a962f02476c5df0e4f947accdb597b84 100644 (file)
@@ -1,3 +1,14 @@
+2021-06-15  Alan Modra  <amodra@gmail.com>
+
+       * readelf.c (locate_dynamic_section, is_pie): New functions.
+       (get_file_type): Replace e_type parameter with filedata.  Call
+       is_pie for ET_DYN.  Update all callers.
+       (process_program_headers): Use local variables dynamic_addr and
+       dynamic_size, updating filedata on exit from function.  Set
+       dynamic_size of 1 to indicate no dynamic section or segment.
+       Update tests of dynamic_size throughout.
+       * testsuite/binutils-all/x86-64/pr27708.dump: Update expected output.
+
 2021-06-14  Eric Botcazou  <ebotcazou@adacore.com>
 
        * dwarf.c (struct abbrev_attr): Change type of implicit_const.
index 79724e0549467c28d3af0511554cce11adcff489..e104a7ed2a16b171c0fc692bf7572c702ea60d07 100644 (file)
@@ -2339,9 +2339,96 @@ get_dynamic_type (Filedata * filedata, unsigned long type)
     }
 }
 
+static bool get_program_headers (Filedata *);
+static bool get_dynamic_section (Filedata *);
+
+static void
+locate_dynamic_section (Filedata *filedata)
+{
+  unsigned long dynamic_addr = 0;
+  bfd_size_type dynamic_size = 0;
+
+  if (filedata->file_header.e_phnum != 0
+      && get_program_headers (filedata))
+    {
+      Elf_Internal_Phdr *segment;
+      unsigned int i;
+
+      for (i = 0, segment = filedata->program_headers;
+          i < filedata->file_header.e_phnum;
+          i++, segment++)
+       {
+         if (segment->p_type == PT_DYNAMIC)
+           {
+             dynamic_addr = segment->p_offset;
+             dynamic_size = segment->p_filesz;
+
+             if (filedata->section_headers != NULL)
+               {
+                 Elf_Internal_Shdr *sec;
+
+                 sec = find_section (filedata, ".dynamic");
+                 if (sec != NULL)
+                   {
+                     if (sec->sh_size == 0
+                         || sec->sh_type == SHT_NOBITS)
+                       {
+                         dynamic_addr = 0;
+                         dynamic_size = 0;
+                       }
+                     else
+                       {
+                         dynamic_addr = sec->sh_offset;
+                         dynamic_size = sec->sh_size;
+                       }
+                   }
+               }
+
+             if (dynamic_addr > filedata->file_size
+                 || (dynamic_size > filedata->file_size - dynamic_addr))
+               {
+                 dynamic_addr = 0;
+                 dynamic_size = 0;
+               }
+             break;
+           }
+       }
+    }
+  filedata->dynamic_addr = dynamic_addr;
+  filedata->dynamic_size = dynamic_size ? dynamic_size : 1;
+}
+
+static bool
+is_pie (Filedata *filedata)
+{
+  Elf_Internal_Dyn *entry;
+
+  if (filedata->dynamic_size == 0)
+    locate_dynamic_section (filedata);
+  if (filedata->dynamic_size <= 1)
+    return false;
+
+  if (!get_dynamic_section (filedata))
+    return false;
+
+  for (entry = filedata->dynamic_section;
+       entry < filedata->dynamic_section + filedata->dynamic_nent;
+       entry++)
+    {
+      if (entry->d_tag == DT_FLAGS_1)
+       {
+         if ((entry->d_un.d_val & DF_1_PIE) != 0)
+           return true;
+         break;
+       }
+    }
+  return false;
+}
+
 static char *
-get_file_type (unsigned e_type)
+get_file_type (Filedata *filedata)
 {
+  unsigned e_type = filedata->file_header.e_type;
   static char buff[64];
 
   switch (e_type)
@@ -2349,7 +2436,11 @@ get_file_type (unsigned e_type)
     case ET_NONE: return _("NONE (None)");
     case ET_REL:  return _("REL (Relocatable file)");
     case ET_EXEC: return _("EXEC (Executable file)");
-    case ET_DYN:  return _("DYN (Shared object file)");
+    case ET_DYN:
+      if (is_pie (filedata))
+       return _("DYN (Position-Independent Executable file)");
+      else
+       return _("DYN (Shared object file)");
     case ET_CORE: return _("CORE (Core file)");
 
     default:
@@ -5168,7 +5259,7 @@ process_file_header (Filedata * filedata)
       printf (_("  ABI Version:                       %d\n"),
              header->e_ident[EI_ABIVERSION]);
       printf (_("  Type:                              %s\n"),
-             get_file_type (header->e_type));
+             get_file_type (filedata));
       printf (_("  Machine:                           %s\n"),
              get_machine_name (header->e_machine));
       printf (_("  Version:                           0x%lx\n"),
@@ -5379,27 +5470,21 @@ get_program_headers (Filedata * filedata)
   return false;
 }
 
-/* Returns TRUE if the program headers were loaded.  */
+/* Print program header info and locate dynamic section.  */
 
-static bool
+static void
 process_program_headers (Filedata * filedata)
 {
   Elf_Internal_Phdr * segment;
   unsigned int i;
   Elf_Internal_Phdr * previous_load = NULL;
 
-  filedata->dynamic_addr = 0;
-  filedata->dynamic_size = 0;
-
   if (filedata->file_header.e_phnum == 0)
     {
       /* PR binutils/12467.  */
       if (filedata->file_header.e_phoff != 0)
-       {
-         warn (_("possibly corrupt ELF header - it has a non-zero program"
-                 " header offset, but no program headers\n"));
-         return false;
-       }
+       warn (_("possibly corrupt ELF header - it has a non-zero program"
+               " header offset, but no program headers\n"));
       else if (do_segments)
        {
          if (filedata->is_separate)
@@ -5408,17 +5493,16 @@ process_program_headers (Filedata * filedata)
          else
            printf (_("\nThere are no program headers in this file.\n"));
        }
-      return true;
+      goto no_headers;
     }
 
   if (do_segments && !do_header)
     {
       if (filedata->is_separate)
        printf ("\nIn linked file '%s' the ELF file type is %s\n",
-               filedata->file_name,
-               get_file_type (filedata->file_header.e_type));
+               filedata->file_name, get_file_type (filedata));
       else
-       printf (_("\nElf file type is %s\n"), get_file_type (filedata->file_header.e_type));
+       printf (_("\nElf file type is %s\n"), get_file_type (filedata));
       printf (_("Entry point 0x%s\n"), bfd_vmatoa ("x", filedata->file_header.e_entry));
       printf (ngettext ("There is %d program header, starting at offset %s\n",
                        "There are %d program headers, starting at offset %s\n",
@@ -5428,7 +5512,7 @@ process_program_headers (Filedata * filedata)
     }
 
   if (! get_program_headers (filedata))
-    return true;
+    goto no_headers;
 
   if (do_segments)
     {
@@ -5452,6 +5536,8 @@ process_program_headers (Filedata * filedata)
        }
     }
 
+  unsigned long dynamic_addr = 0;
+  bfd_size_type dynamic_size = 0;
   for (i = 0, segment = filedata->program_headers;
        i < filedata->file_header.e_phnum;
        i++, segment++)
@@ -5577,13 +5663,13 @@ process_program_headers (Filedata * filedata)
          break;
 
        case PT_DYNAMIC:
-         if (filedata->dynamic_addr)
+         if (dynamic_addr)
            error (_("more than one dynamic segment\n"));
 
          /* By default, assume that the .dynamic section is the first
             section in the DYNAMIC segment.  */
-         filedata->dynamic_addr = segment->p_offset;
-         filedata->dynamic_size = segment->p_filesz;
+         dynamic_addr = segment->p_offset;
+         dynamic_size = segment->p_filesz;
 
          /* Try to locate the .dynamic section. If there is
             a section header table, we can easily locate it.  */
@@ -5594,27 +5680,28 @@ process_program_headers (Filedata * filedata)
              sec = find_section (filedata, ".dynamic");
              if (sec == NULL || sec->sh_size == 0)
                {
-                  /* A corresponding .dynamic section is expected, but on
-                     IA-64/OpenVMS it is OK for it to be missing.  */
-                  if (!is_ia64_vms (filedata))
-                    error (_("no .dynamic section in the dynamic segment\n"));
+                 /* A corresponding .dynamic section is expected, but on
+                    IA-64/OpenVMS it is OK for it to be missing.  */
+                 if (!is_ia64_vms (filedata))
+                   error (_("no .dynamic section in the dynamic segment\n"));
                  break;
                }
 
              if (sec->sh_type == SHT_NOBITS)
                {
-                 filedata->dynamic_size = 0;
+                 dynamic_addr = 0;
+                 dynamic_size = 0;
                  break;
                }
 
-             filedata->dynamic_addr = sec->sh_offset;
-             filedata->dynamic_size = sec->sh_size;
+             dynamic_addr = sec->sh_offset;
+             dynamic_size = sec->sh_size;
 
              /* The PT_DYNAMIC segment, which is used by the run-time
                 loader,  should exactly match the .dynamic section.  */
              if (do_checks
-                 && (filedata->dynamic_addr != segment->p_offset
-                     || filedata->dynamic_size != segment->p_filesz))
+                 && (dynamic_addr != segment->p_offset
+                     || dynamic_size != segment->p_filesz))
                warn (_("\
 the .dynamic section is not the same as the dynamic segment\n"));
            }
@@ -5623,12 +5710,12 @@ the .dynamic section is not the same as the dynamic segment\n"));
             segment.  Check this after matching against the section headers
             so we don't warn on debuginfo file (which have NOBITS .dynamic
             sections).  */
-         if (filedata->dynamic_addr > filedata->file_size
-             || (filedata->dynamic_size
-                 > filedata->file_size - filedata->dynamic_addr))
+         if (dynamic_addr > filedata->file_size
+             || (dynamic_size > filedata->file_size - dynamic_addr))
            {
              error (_("the dynamic segment offset + size exceeds the size of the file\n"));
-             filedata->dynamic_addr = filedata->dynamic_size = 0;
+             dynamic_addr = 0;
+             dynamic_size = 0;
            }
          break;
 
@@ -5685,7 +5772,13 @@ the .dynamic section is not the same as the dynamic segment\n"));
        }
     }
 
-  return true;
+  filedata->dynamic_addr = dynamic_addr;
+  filedata->dynamic_size = dynamic_size ? dynamic_size : 1;
+  return;
+
+ no_headers:
+  filedata->dynamic_addr = 0;
+  filedata->dynamic_size = 1;
 }
 
 
@@ -10610,7 +10703,7 @@ process_dynamic_section (Filedata * filedata)
 {
   Elf_Internal_Dyn * entry;
 
-  if (filedata->dynamic_size == 0)
+  if (filedata->dynamic_size <= 1)
     {
       if (do_dynamic)
        {
@@ -21374,9 +21467,9 @@ process_object (Filedata * filedata)
     /* Without loaded section groups we cannot process unwind.  */
     do_unwind = false;
 
-  res = process_program_headers (filedata);
-  if (res)
-    res = process_dynamic_section (filedata);
+  process_program_headers (filedata);
+
+  res = process_dynamic_section (filedata);
 
   if (! process_relocs (filedata))
     res = false;
@@ -21422,8 +21515,7 @@ process_object (Filedata * filedata)
            {
              if (! process_section_groups (d->handle))
                res = false;
-             if (! process_program_headers (d->handle))
-               res = false;
+             process_program_headers (d->handle);
              if (! process_dynamic_section (d->handle))
                res = false;
              if (! process_relocs (d->handle))
index e9123df9c42e0d7f6a9446c47e9e842a2c5e2b80..c99147140034174decf2201ccad19b1ea26dd14e 100644 (file)
@@ -1,5 +1,5 @@
 
-Elf file type is DYN (Shared object file)
+Elf file type is DYN (Position-Independent Executable file)
 Entry point 0x5f0
 There are 11 program headers, starting at offset 64
 
index 4188bc8e4d3db7d6e33db505998359c7e8c42005..c56c129d0b1f7b66d3f7c78303cbabd7bdaf0757 100644 (file)
@@ -1,3 +1,7 @@
+2021-06-15  Alan Modra  <amodra@gmail.com>
+
+       * testsuite/lib/gdb.exp (exec_is_pie): Match new PIE readelf output.
+
 2021-06-14  Mike Frysinger  <vapier@gentoo.org>
 
        * Makefile.in (GNULIB_BUILDDIR): Rename to ...
index d8c684c72389ca63e0adc198426c607acdff3e42..4bb2da31c1f3139403faf807337fca7a03b36a80 100644 (file)
@@ -6078,7 +6078,7 @@ proc exec_is_pie { executable } {
     if { $res != 0 } {
        return -1
     }
-    set res [regexp -line {^[ \t]*Type:[ \t]*DYN \(Shared object file\)$} \
+    set res [regexp -line {^[ \t]*Type:[ \t]*DYN \((Position-Independent Executable|Shared object) file\)$} \
                 $output]
     if { $res == 1 } {
        return 1
index 2d130b455ff96aeab6106d25add5dc3d29feda51..a1b85b1eea53355b638f293f7bf373bdc3cd3347 100644 (file)
@@ -1,3 +1,7 @@
+2021-06-15  Alan Modra  <amodra@gmail.com>
+
+       * testsuite/ld-pie/vaddr-0.d: Update expected output.
+
 2021-06-11  Alan Modra  <amodra@gmail.com>
 
        PR 27952
index e0722226b2cc9baef3dcdb0bdf5bcffb76a8d75c..69f7b10e7a8f98ea6ff49881ae6c2f0d9fbf089f 100644 (file)
@@ -5,5 +5,5 @@
 
 ELF Header:
 #...
-  Type:                              DYN \(Shared object file\)
+  Type:                              DYN \(Position-Independent Executable file\)
 #pass