From 6d258ceb8dbf41e255397ed0db7d7635f398134c Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Wed, 16 Jan 2013 15:19:20 +0100 Subject: 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 --- src/ChangeLog | 9 ++++ src/readelf.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) 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 + + * 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 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); } -- cgit v1.2.1