summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2013-01-16 15:19:20 +0100
committerMark Wielaard <mjw@redhat.com>2013-01-16 15:19:20 +0100
commit6d258ceb8dbf41e255397ed0db7d7635f398134c (patch)
tree7692ddea4453557d897fed3fe7d9af047b008920
parent1a4d0668d18bf1090c5c08cdb5cb3ba2b8eb5410 (diff)
downloadelfutils-6d258ceb8dbf41e255397ed0db7d7635f398134c.tar.gz
readelf: Add --elf-section input option to inspect an embedded ELF file.
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>
-rw-r--r--src/ChangeLog9
-rw-r--r--src/readelf.c150
2 files changed, 159 insertions, 0 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 11a9cb51..377c1241 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -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.
diff --git a/src/readelf.c b/src/readelf.c
index 0a9629a7..0b464596 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -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);
}