]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
readelf: Add --elf-section input option to inspect an embedded ELF file.
authorMark Wielaard <mjw@redhat.com>
Wed, 16 Jan 2013 14:19:20 +0000 (15:19 +0100)
committerMark Wielaard <mjw@redhat.com>
Wed, 16 Jan 2013 14:19:20 +0000 (15:19 +0100)
Some binaries might have (compressed) embedded ELF files inside a section.
The default section name for this is .gnu_debugdata.  This normally
consists of just those sections needed to provide an auxiluary symbol
table.  But can theoretically contain other (debug) sections.  The new
--elf-section arguments makes it possible to easily inspect it as if it
is a normal ELF file.  libdwfl takes care of automatically decompressing
any data in the section if necessary.

https://fedoraproject.org/wiki/Features/MiniDebugInfo

ELF input selection:
   --elf-section[=SECTION]  Use the named SECTION (default .gnu_debugdata)
                            as (compressed) ELF input data

Signed-off-by: Mark Wielaard <mjw@redhat.com>
src/ChangeLog
src/readelf.c

index 11a9cb51fa05fa6189f867251a631caae98e0ed0..377c12415863a7c3cbd1ec1f32f39cc857819b82 100644 (file)
@@ -1,3 +1,12 @@
+2012-12-18  Mark Wielaard  <mark@bordewijk.wildebeest.org>
+
+       * readelf.c (ELF_INPUT_SECTION): New argp key value.
+       (argp_option): Add elf-section.
+       (elf_input_section): New static.
+       (parse_opt): Handle ELF_INPUT_SECTION and set elf_input_section.
+       (open_input_section): New function.
+       (process_file): Call open_input_section if elf_input_section set.
+
 2013-01-13  David Abdurachmanov  <David.Abdurachmanov@cern.ch>
 
        ar.c (do_oper_delete): Fix num passed to memset.
index 0a9629a7ea354b60a0e1d0761bf7317a90145657..0b4645965903ab9f17e5582f236c732522f9412f 100644 (file)
@@ -61,9 +61,16 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
 /* Bug report address.  */
 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
 
+/* argp key value for --elf-section, non-ascii.  */
+#define ELF_INPUT_SECTION 256
+
 /* Definitions of arguments for argp functions.  */
 static const struct argp_option options[] =
 {
+  { NULL, 0, NULL, 0, N_("ELF input selection:"), 0 },
+  { "elf-section", ELF_INPUT_SECTION, "SECTION", OPTION_ARG_OPTIONAL,
+    N_("Use the named SECTION (default .gnu_debugdata) as (compressed) ELF "
+       "input data"), 0 },
   { NULL, 0, NULL, 0, N_("ELF output selection:"), 0 },
   { "all", 'a', NULL, 0,
     N_("All these plus -p .strtab -p .dynstr -p .comment"), 0 },
@@ -121,6 +128,8 @@ static struct argp argp =
   options, parse_opt, args_doc, doc, NULL, NULL, NULL
 };
 
+/* If non-null, the section from which we should read to (compressed) ELF.  */
+static const char *elf_input_section = NULL;
 
 /* Flags set by the option controlling the output.  */
 
@@ -445,6 +454,12 @@ parse_opt (int key, char *arg,
       break;
     case 'W':                  /* Ignored.  */
       break;
+    case ELF_INPUT_SECTION:
+      if (arg == NULL)
+       elf_input_section = ".gnu_debugdata";
+      else
+       elf_input_section = arg;
+      break;
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -466,6 +481,121 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 }
 
 
+/* Create a file descriptor to read the data from the
+   elf_input_section given a file descriptor to an ELF file.  */
+static int
+open_input_section (int fd)
+{
+  size_t shnums;
+  size_t cnt;
+  size_t shstrndx;
+  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+  if (elf == NULL)
+    {
+      error (0, 0, gettext ("cannot generate Elf descriptor: %s"),
+            elf_errmsg (-1));
+      return -1;
+    }
+
+  if (elf_getshdrnum (elf, &shnums) < 0)
+    {
+      error (0, 0, gettext ("cannot determine number of sections: %s"),
+            elf_errmsg (-1));
+    open_error:
+      elf_end (elf);
+      return -1;
+    }
+
+  if (elf_getshdrstrndx (elf, &shstrndx) < 0)
+    {
+      error (0, 0, gettext ("cannot get section header string table index"));
+      goto open_error;
+    }
+
+  for (cnt = 0; cnt < shnums; ++cnt)
+    {
+      Elf_Scn *scn = elf_getscn (elf, cnt);
+      if (scn == NULL)
+       {
+         error (0, 0, gettext ("cannot get section: %s"),
+                elf_errmsg (-1));
+         goto open_error;
+       }
+
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      if (unlikely (shdr == NULL))
+       {
+         error (0, 0, gettext ("cannot get section header: %s"),
+                elf_errmsg (-1));
+         goto open_error;
+       }
+
+      const char *sname = elf_strptr (elf, shstrndx, shdr->sh_name);
+      if (sname == NULL)
+       {
+         error (0, 0, gettext ("cannot get section name"));
+         goto open_error;
+       }
+
+      if (strcmp (sname, elf_input_section) == 0)
+       {
+         Elf_Data *data = elf_rawdata (scn, NULL);
+         if (data == NULL)
+           {
+             error (0, 0, gettext ("cannot get %s content: %s"),
+                    sname, elf_errmsg (-1));
+             goto open_error;
+           }
+
+         /* Create (and immediately unlink) a temporary file to store
+            section data in to create a file descriptor for it.  */
+         const char *tmpdir = getenv ("TMPDIR") ?: P_tmpdir;
+         static const char suffix[] = "/readelfXXXXXX";
+         int tmplen = strlen (tmpdir) + sizeof (suffix);
+         char *tempname = alloca (tmplen);
+         sprintf (tempname, "%s%s", tmpdir, suffix);
+
+         int sfd = mkstemp (tempname);
+         if (sfd == -1)
+           {
+             error (0, 0, gettext ("cannot create temp file '%s'"),
+                    tempname);
+             goto open_error;
+           }
+         unlink (tempname);
+
+         ssize_t size = data->d_size;
+         if (write_retry (sfd, data->d_buf, size) != size)
+           {
+             error (0, 0, gettext ("cannot write section data"));
+             goto open_error;
+           }
+
+         if (elf_end (elf) != 0)
+           {
+             error (0, 0, gettext ("error while closing Elf descriptor: %s"),
+                    elf_errmsg (-1));
+             return -1;
+           }
+
+         if (lseek (sfd, 0, SEEK_SET) == -1)
+           {
+             error (0, 0, gettext ("error while rewinding file descriptor"));
+             return -1;
+           }
+
+         return sfd;
+       }
+    }
+
+  /* Named section not found.  */
+  if (elf_end (elf) != 0)
+    error (0, 0, gettext ("error while closing Elf descriptor: %s"),
+          elf_errmsg (-1));
+  return -1;
+}
+
 /* Check if the file is an archive, and if so dump its index.  */
 static void
 check_archive_index (int fd, const char *fname, bool only_one)
@@ -562,6 +692,21 @@ process_file (int fd, const char *fname, bool only_one)
   if (!any_control_option)
     return;
 
+  if (elf_input_section != NULL)
+    {
+      /* Replace fname and fd with section content. */
+      char *fnname = alloca (strlen (fname) + strlen (elf_input_section) + 2);
+      sprintf (fnname, "%s:%s", fname, elf_input_section);
+      fd = open_input_section (fd);
+      if (fd == -1)
+        {
+          error (0, 0, gettext ("No such section '%s' in '%s'"),
+                elf_input_section, fname);
+          return;
+        }
+      fname = fnname;
+    }
+
   /* Duplicate an fd for dwfl_report_offline to swallow.  */
   int dwfl_fd = dup (fd);
   if (unlikely (dwfl_fd < 0))
@@ -606,6 +751,11 @@ process_file (int fd, const char *fname, bool only_one)
       dwfl_getmodules (dwfl, &process_dwflmod, &a, 0);
     }
   dwfl_end (dwfl);
+
+  /* Need to close the replaced fd if we created it.  Caller takes
+     care of original.  */
+  if (elf_input_section != NULL)
+    close (fd);
 }