summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChih-Hung Hsieh <chh@google.com>2016-01-25 10:12:35 -0800
committerChih-Hung Hsieh <chh@google.com>2016-01-25 11:25:11 -0800
commit3ad5c39aed9ad8b3847015a8349053b7a1ac094f (patch)
tree482d3d6644a743b44c3fc79c95e2b6b5e30e6ab2 /src
parent97c644e689be3b7b3b7898513a38ead399d2285a (diff)
parent203f0a3eec8c630c5183fb9984d66339c1ea3c31 (diff)
downloadandroid_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.tar.gz
android_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.tar.bz2
android_external_elfutils-3ad5c39aed9ad8b3847015a8349053b7a1ac094f.zip
Merge upstream 0.165 SHA '203f0a3'
* git merge 203f0a3 * See all upstream changes since the previous merge in branch aosp/upstream-master: git diff 9d1e236..203f0a3 * Android relevant upstream changes: * Version number changed from 0.164 to 0.165 * libelf now depends on zlib * Changed version number in generated files: version.h, config.h * No change to generated files: libdw/known-dwarf.h * Updated libelf/Android.mk: * added new source files: gelf_getchdr.c elf{32,64}_getchdr.c elf_compress.c * added LOCAL_STATIC_LIBRARIES := libz Change-Id: Ieb306d84ffcaf680ad6e74da28fcc8efbf00592a
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog49
-rw-r--r--src/Makefile.am3
-rw-r--r--src/elfcompress.c1321
-rw-r--r--src/elflint.c57
-rw-r--r--src/readelf.c188
5 files changed, 1581 insertions, 37 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index b5b5e4a4..707c2717 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,52 @@
+2016-01-13 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_elf_header): Recognize ELFOSABI_FREEBSD.
+
+2016-01-08 Mark Wielaard <mjw@redhat.com>
+
+ * elfcompress.c (compress_section): Use %zu to print size_t.
+ * readelf.c (print_shdr): Use %zx to print size_t.
+
+2015-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * elfcompress.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add elfcompress.
+ (elfcompress_LDADD): New variable.
+
+2015-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (section_flags_string): Add NEWFLAG COMPRESSED.
+ (check_sections): SHF_COMPRESSED can be on any special section.
+ SHF_COMPRESSED is a valid section flag. SHF_COMPRESSED cannot
+ be used together with SHF_ALLOC or with SHT_NOBITS. Should have
+ a valid Chdr.
+
+2015-10-20 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (options): Expand -z help text.
+ (dump_data_section): Check whether we need and can decompress section
+ data and call elf_rawzdata if so,
+ (print_string_section): Likewise.
+ (elf_contains_chdrs): New function.
+ (process_elf_file): Rename print_unrelocated to print_unchanged,
+ use elf_contains_chdrs.
+ (print_scngrp): Check whether section is compressed before use.
+ (print_symtab): Likewise.
+ (handle_hash): Likewise.
+
+2015-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (argp_option): Describe --decompress,-z.
+ (print_decompress): New bool.
+ (parse_opt): Handle -z.
+ (elf_ch_type_name): New function.
+ (print_shdr): Print section compress information.
+
+2015-12-31 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Add _edata and _end (plus extra underscore
+ variants) to to the list of possibly dangling symbols.
+
2015-12-02 Mark Wielaard <mjw@redhat.com>
* nm.c (process_file): Accept fd and pass it on.
diff --git a/src/Makefile.am b/src/Makefile.am
index e2c494c0..a39df279 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,7 +35,7 @@ native_ld = @native_ld@
base_cpu = @base_cpu@
bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \
- elfcmp objdump ranlib strings ar unstrip stack
+ elfcmp objdump ranlib strings ar unstrip stack elfcompress
ld_dsos = libld_elf_i386_pic.a
@@ -125,6 +125,7 @@ strings_LDADD = $(libelf) $(libeu) $(argp_LDADD)
ar_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD)
unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl
stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl $(demanglelib)
+elfcompress_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/elfcompress.c b/src/elfcompress.c
new file mode 100644
index 00000000..23939db8
--- /dev/null
+++ b/src/elfcompress.c
@@ -0,0 +1,1321 @@
+/* Compress or decompress an ELF file.
+ Copyright (C) 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include ELFUTILS_HEADER(elf)
+#include ELFUTILS_HEADER(ebl)
+#include <gelf.h>
+#include "system.h"
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+static int verbose = 0; /* < 0, no warnings, > 0 extra verbosity. */
+static bool force = false;
+static bool permissive = false;
+static const char *foutput = NULL;
+
+#define T_UNSET 0
+#define T_DECOMPRESS 1 /* none */
+#define T_COMPRESS_ZLIB 2 /* zlib */
+#define T_COMPRESS_GNU 3 /* zlib-gnu */
+static int type = T_UNSET;
+
+static void
+print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
+{
+ fprintf (stream, "elfcompress (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+}
+
+struct section_pattern
+{
+ char *pattern;
+ struct section_pattern *next;
+};
+
+static struct section_pattern *patterns = NULL;
+
+static void
+add_pattern (const char *pattern)
+{
+ struct section_pattern *p = xmalloc (sizeof *p);
+ p->pattern = xstrdup (pattern);
+ p->next = patterns;
+ patterns = p;
+}
+
+static void
+free_patterns (void)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ struct section_pattern *p = pattern;
+ pattern = p->next;
+ free (p->pattern);
+ free (p);
+ }
+}
+
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'v':
+ verbose++;
+ break;
+
+ case 'q':
+ verbose--;
+
+ case 'f':
+ force = true;
+ break;
+
+ case 'p':
+ permissive = true;
+ break;
+
+ case 'n':
+ add_pattern (arg);
+ break;
+
+ case 'o':
+ if (foutput != NULL)
+ argp_error (state, N_("-o option specified twice"));
+ else
+ foutput = arg;
+ break;
+
+ case 't':
+ if (type != T_UNSET)
+ argp_error (state, N_("-t option specified twice"));
+
+ if (strcmp ("none", arg) == 0)
+ type = T_DECOMPRESS;
+ else if (strcmp ("zlib", arg) == 0 || strcmp ("zlib-gabi", arg) == 0)
+ type = T_COMPRESS_ZLIB;
+ else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0)
+ type = T_COMPRESS_GNU;
+ else
+ argp_error (state, N_("unknown compression type '%s'"), arg);
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ if (type == T_UNSET)
+ type = T_COMPRESS_ZLIB;
+ if (patterns == NULL)
+ add_pattern (".?(z)debug*");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ /* We need at least one input file. */
+ argp_error (state, N_("No input file given"));
+ break;
+
+ case ARGP_KEY_ARGS:
+ if (foutput != NULL && state->argc - state->next > 1)
+ argp_error (state,
+ N_("Only one input file allowed together with '-o'"));
+ /* We only use this for checking the number of arguments, we don't
+ actually want to consume them, so fallthrough. */
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static bool
+section_name_matches (const char *name)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0)
+ return true;
+ pattern = pattern->next;
+ }
+ return false;
+}
+
+static int
+setshdrstrndx (Elf *elf, GElf_Ehdr *ehdr, size_t ndx)
+{
+ if (ndx < SHN_LORESERVE)
+ ehdr->e_shstrndx = ndx;
+ else
+ {
+ ehdr->e_shstrndx = SHN_XINDEX;
+ Elf_Scn *zscn = elf_getscn (elf, 0);
+ GElf_Shdr zshdr_mem;
+ GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
+ if (zshdr == NULL)
+ return -1;
+ zshdr->sh_link = ndx;
+ if (gelf_update_shdr (zscn, zshdr) == 0)
+ return -1;
+ }
+
+ if (gelf_update_ehdr (elf, ehdr) == 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+compress_section (Elf_Scn *scn, size_t orig_size, const char *name,
+ const char *newname, size_t ndx,
+ bool gnu, bool compress, bool report_verbose)
+{
+ int res;
+ unsigned int flags = compress && force ? ELF_CHF_FORCE : 0;
+ if (gnu)
+ res = elf_compress_gnu (scn, compress ? 1 : 0, flags);
+ else
+ res = elf_compress (scn, compress ? ELFCOMPRESS_ZLIB : 0, flags);
+
+ if (res < 0)
+ error (0, 0, "Couldn't decompress section [%zd] %s: %s",
+ ndx, name, elf_errmsg (-1));
+ else
+ {
+ if (compress && res == 0)
+ {
+ if (verbose >= 0)
+ printf ("[%zd] %s NOT compressed, wouldn't be smaller\n",
+ ndx, name);
+ }
+
+ if (report_verbose && res > 0)
+ {
+ printf ("[%zd] %s %s", ndx, name,
+ compress ? "compressed" : "decompressed");
+ if (newname != NULL)
+ printf (" -> %s", newname);
+
+ /* Reload shdr, it has changed. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section [%zd]", ndx);
+ return -1;
+ }
+ float new = shdr->sh_size;
+ float orig = orig_size ?: 1;
+ printf (" (%zu => %" PRIu64 " %.2f%%)\n",
+ orig_size, shdr->sh_size, (new / orig) * 100);
+ }
+ }
+
+ return res;
+}
+
+static int
+process_file (const char *fname)
+{
+ if (verbose > 0)
+ printf ("processing: %s\n", fname);
+
+ /* The input ELF. */
+ int fd = -1;
+ Elf *elf = NULL;
+
+ /* The output ELF. */
+ char *fnew = NULL;
+ int fdnew = -1;
+ Elf *elfnew = NULL;
+
+ /* Buffer for (one) new section name if necessary. */
+ char *snamebuf = NULL;
+
+ /* String table (and symbol table), if section names need adjusting. */
+ struct Ebl_Strtab *names = NULL;
+ struct Ebl_Strent **scnstrents = NULL;
+ struct Ebl_Strent **symstrents = NULL;
+ char **scnnames = NULL;
+
+ /* Section data from names. */
+ void *namesbuf = NULL;
+
+ /* Which sections match and need to be (un)compressed. */
+ unsigned int *sections = NULL;
+
+ /* How many sections are we talking about? */
+ size_t shnum = 0;
+
+#define WORD_BITS (8U * sizeof (unsigned int))
+ void set_section (size_t ndx)
+ {
+ sections[ndx / WORD_BITS] |= (1U << (ndx % WORD_BITS));
+ }
+
+ bool get_section (size_t ndx)
+ {
+ return (sections[ndx / WORD_BITS] & (1U << (ndx % WORD_BITS))) != 0;
+ }
+
+ int cleanup (int res)
+ {
+ elf_end (elf);
+ close (fd);
+
+ elf_end (elfnew);
+ close (fdnew);
+
+ if (fnew != NULL)
+ {
+ unlink (fnew);
+ free (fnew);
+ fnew = NULL;
+ }
+
+ free (snamebuf);
+ if (names != NULL)
+ {
+ ebl_strtabfree (names);
+ free (scnstrents);
+ free (symstrents);
+ free (namesbuf);
+ if (scnnames != NULL)
+ {
+ for (size_t n = 0; n < shnum; n++)
+ free (scnnames[n]);
+ free (scnnames);
+ }
+ }
+
+ free (sections);
+
+ return res;
+ }
+
+ fd = open (fname, O_RDONLY);
+ if (fd < 0)
+ {
+ error (0, errno, "Couldn't open %s\n", fname);
+ return cleanup (-1);
+ }
+
+ elf = elf_begin (fd, ELF_C_READ, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "Couldn't open ELF file %s for reading: %s",
+ fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* We dont' handle ar files (or anything else), we probably should. */
+ Elf_Kind kind = elf_kind (elf);
+ if (kind != ELF_K_ELF)
+ {
+ if (kind == ELF_K_AR)
+ error (0, 0, "Cannot handle ar files: %s", fname);
+ else
+ error (0, 0, "Unknown file type: %s", fname);
+ return cleanup (-1);
+ }
+
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, "Couldn't fstat %s", fname);
+ return cleanup (-1);
+ }
+
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get ehdr for %s: %s", fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Get the section header string table. */
+ size_t shdrstrndx;
+ if (elf_getshdrstrndx (elf, &shdrstrndx) != 0)
+ {
+ error (0, 0, "Couldn't get section header string table index in %s: %s",
+ fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* How many sections are we talking about? */
+ if (elf_getshdrnum (elf, &shnum) != 0)
+ {
+ error (0, 0, "Couldn't get number of sections in %s: %s",
+ fname, elf_errmsg (1));
+ return cleanup (-1);
+ }
+
+ if (shnum == 0)
+ {
+ error (0, 0, "ELF file %s has no sections", fname);
+ return cleanup (-1);
+ }
+
+ sections = xcalloc (shnum / 8 + 1, sizeof (unsigned int));
+
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ {
+ error (0, 0, "Couldn't get phdrnum: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Whether we need to adjust any section names (going to/from GNU
+ naming). If so we'll need to build a new section header string
+ table. */
+ bool adjust_names = false;
+
+ /* If there are phdrs we want to maintain the layout of the
+ allocated sections in the file. */
+ bool layout = phnum != 0;
+
+ /* While going through all sections keep track of last section data
+ offset if needed to keep the layout. We are responsible for
+ adding the section offsets and headers (e_shoff) in that case
+ (which we will place after the last section). */
+ GElf_Off last_offset = 0;
+ if (layout)
+ last_offset = (ehdr.e_phoff
+ + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT));
+
+ /* Which section, if any, is a symbol table that shares a string
+ table with the section header string table? */
+ size_t symtabndx = 0;
+
+ /* We do three passes over all sections.
+
+ First an inspection pass over the old Elf to see which section
+ data needs to be copied and/or transformed, which sections need a
+ names change and whether there is a symbol table that might need
+ to be adjusted be if the section header name table is changed.
+
+ Second a collection pass that creates the Elf sections and copies
+ the data. This pass will compress/decompress section data when
+ needed. And it will collect all data needed if we'll need to
+ construct a new string table. Afterwards the new string table is
+ constructed.
+
+ Third a fixup/adjustment pass over the new Elf that will adjust
+ any section references (names) and adjust the layout based on the
+ new sizes of the sections if necessary. This pass is optional if
+ we aren't responsible for the layout and the section header
+ string table hasn't been changed. */
+
+ /* Inspection pass. */
+ size_t maxnamelen = 0;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+ if (ndx > shnum)
+ {
+ error (0, 0, "Unexpected section number %zd, expected only %zd",
+ ndx, shnum);
+ cleanup (-1);
+ }
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (sname == NULL)
+ {
+ error (0, 0, "Couldn't get name for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (section_name_matches (sname))
+ {
+ if (shdr->sh_type != SHT_NOBITS
+ && (shdr->sh_flags & SHF_ALLOC) == 0)
+ {
+ set_section (ndx);
+ /* Check if we might want to change this section name. */
+ if (! adjust_names
+ && ((type != T_COMPRESS_GNU
+ && strncmp (sname, ".zdebug",
+ strlen (".zdebug")) == 0)
+ || (type == T_COMPRESS_GNU
+ && strncmp (sname, ".debug",
+ strlen (".debug")) == 0)))
+ adjust_names = true;
+
+ /* We need a buffer this large if we change the names. */
+ if (adjust_names)
+ {
+ size_t slen = strlen (sname);
+ if (slen > maxnamelen)
+ maxnamelen = slen;
+ }
+ }
+ else
+ if (verbose >= 0)
+ printf ("[%zd] %s ignoring %s section\n", ndx, sname,
+ (shdr->sh_type == SHT_NOBITS ? "no bits" : "allocated"));
+ }
+
+ if (shdr->sh_type == SHT_SYMTAB)
+ {
+ /* Check if we might have to adjust the symbol name indexes. */
+ if (shdr->sh_link == shdrstrndx)
+ {
+ if (symtabndx != 0)
+ {
+ error (0, 0,
+ "Multiple symbol tables (%zd, %zd) using the same string table unsupported", symtabndx, ndx);
+ return cleanup (-1);
+ }
+ symtabndx = ndx;
+ }
+ }
+
+ /* Keep track of last allocated data offset. */
+ if (layout)
+ if ((shdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS
+ ? shdr->sh_size : 0);
+ if (last_offset < off)
+ last_offset = off;
+ }
+ }
+
+ if (adjust_names)
+ {
+ names = ebl_strtabinit (true);
+ if (names == NULL)
+ {
+ error (0, 0, "Not enough memory for new strtab");
+ return cleanup (-1);
+ }
+ scnstrents = xmalloc (shnum
+ * sizeof (struct Ebl_Strent *));
+ scnnames = xcalloc (shnum, sizeof (char *));
+ }
+
+ /* Create a new (temporary) ELF file for the result. */
+ if (foutput == NULL)
+ {
+ size_t fname_len = strlen (fname);
+ fnew = xmalloc (fname_len + sizeof (".XXXXXX"));
+ strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
+ fdnew = mkstemp (fnew);
+ }
+ else
+ {
+ fnew = xstrdup (foutput);
+ fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS);
+ }
+
+ if (fdnew < 0)
+ {
+ error (0, errno, "Couldn't create output file %s", fnew);
+ /* Since we didn't create it we don't want to try to unlink it. */
+ free (fnew);
+ fnew = NULL;
+ return cleanup (-1);
+ }
+
+ elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
+ if (elfnew == NULL)
+ {
+ error (0, 0, "Couldn't open new ELF %s for writing: %s",
+ fnew, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Create the new ELF header and copy over all the data. */
+ if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0)
+ {
+ error (0, 0, "Couldn't create new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ GElf_Ehdr newehdr;
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA];
+ newehdr.e_type = ehdr.e_type;
+ newehdr.e_machine = ehdr.e_machine;
+ newehdr.e_version = ehdr.e_version;
+ newehdr.e_entry = ehdr.e_entry;
+ newehdr.e_flags = ehdr.e_flags;
+
+ if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+ {
+ error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Copy over the phdrs as is. */
+ if (phnum != 0)
+ {
+ if (gelf_newphdr (elfnew, phnum) == 0)
+ {
+ error (0, 0, "Couldn't create phdrs: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL)
+ {
+ error (0, 0, "Couldn't get phdr %zd: %s", cnt, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
+ {
+ error (0, 0, "Couldn't create phdr %zd: %s", cnt,
+ elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ }
+ }
+
+ /* Possibly add a 'z' and zero terminator. */
+ if (maxnamelen > 0)
+ snamebuf = xmalloc (maxnamelen + 2);
+
+ /* We might want to read/adjust the section header strings and
+ symbol tables. If so, and those sections are to be compressed
+ then we will have to decompress it during the collection pass and
+ compress it again in the fixup pass. Don't compress unnecessary
+ and keep track of whether or not to compress them (later in the
+ fixup pass). Also record the original size, so we can report the
+ difference later when we do compress. */
+ int shstrtab_compressed = T_UNSET;
+ size_t shstrtab_size = 0;
+ char *shstrtab_name = NULL;
+ char *shstrtab_newname = NULL;
+ int symtab_compressed = T_UNSET;
+ size_t symtab_size = 0;
+ char *symtab_name = NULL;
+ char *symtab_newname = NULL;
+
+ /* Collection pass. Copy over the sections, (de)compresses matching
+ sections, collect names of sections and symbol table if
+ necessary. */
+ scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+ assert (ndx < shnum);
+
+ /* (de)compress if section matched. */
+ char *sname = NULL;
+ char *newname = NULL;
+ if (get_section (ndx))
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ uint64_t size = shdr->sh_size;
+ sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (sname == NULL)
+ {
+ error (0, 0, "Couldn't get name for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* strdup sname, the shdrstrndx section itself might be
+ (de)compressed, invalidating the string pointers. */
+ sname = xstrdup (sname);
+
+ /* We might want to decompress (and rename), but not
+ compress during this pass since we might need the section
+ data in later passes. Skip those sections for now and
+ compress them in the fixup pass. */
+ bool skip_compress_section = (adjust_names
+ && (ndx == shdrstrndx
+ || ndx == symtabndx));
+
+ switch (type)
+ {
+ case T_DECOMPRESS:
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (compress_section (scn, size, sname, NULL, ndx,
+ false, false, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ snamebuf[0] = '.';
+ strcpy (&snamebuf[1], &sname[2]);
+ newname = snamebuf;
+ if (compress_section (scn, size, sname, newname, ndx,
+ true, false, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (verbose > 0)
+ printf ("[%zd] %s already decompressed\n", ndx, sname);
+ break;
+
+ case T_COMPRESS_GNU:
+ if (strncmp (sname, ".debug", strlen (".debug")) == 0)
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ /* First decompress to recompress GNU style.
+ Don't report even when verbose. */
+ if (compress_section (scn, size, sname, NULL, ndx,
+ false, false, false) < 0)
+ return cleanup (-1);
+ }
+
+ snamebuf[0] = '.';
+ snamebuf[1] = 'z';
+ strcpy (&snamebuf[2], &sname[1]);
+ newname = snamebuf;
+
+ if (skip_compress_section)
+ {
+ if (ndx == shdrstrndx)
+ {
+ shstrtab_size = size;
+ shstrtab_compressed = T_COMPRESS_GNU;
+ shstrtab_name = xstrdup (sname);
+ shstrtab_newname = xstrdup (newname);
+ }
+ else
+ {
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_GNU;
+ symtab_name = xstrdup (sname);
+ symtab_newname = xstrdup (newname);
+ }
+ }
+ else
+ {
+ int res = compress_section (scn, size, sname, newname,
+ ndx, true, true,
+ verbose > 0);
+ if (res < 0)
+ return cleanup (-1);
+
+ if (res == 0)
+ newname = NULL;
+ }
+ }
+ else if (verbose >= 0)
+ {
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ printf ("[%zd] %s unchanged, already GNU compressed",
+ ndx, sname);
+ else
+ printf ("[%zd] %s cannot GNU compress section not starting with .debug\n",
+ ndx, sname);
+ }
+ break;
+
+ case T_COMPRESS_ZLIB:
+ if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
+ {
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ /* First decompress to recompress zlib style.
+ Don't report even when verbose. */
+ if (compress_section (scn, size, sname, NULL, ndx,
+ true, false, false) < 0)
+ return cleanup (-1);
+
+ snamebuf[0] = '.';
+ strcpy (&snamebuf[1], &sname[2]);
+ newname = snamebuf;
+ }
+
+ if (skip_compress_section)
+ {
+ if (ndx == shdrstrndx)
+ {
+ shstrtab_size = size;
+ shstrtab_compressed = T_COMPRESS_ZLIB;
+ shstrtab_name = xstrdup (sname);
+ shstrtab_newname = (newname == NULL
+ ? NULL : xstrdup (newname));
+ }
+ else
+ {
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_ZLIB;
+ symtab_name = xstrdup (sname);
+ symtab_newname = (newname == NULL
+ ? NULL : xstrdup (newname));
+ }
+ }
+ else if (compress_section (scn, size, sname, newname, ndx,
+ false, true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (verbose > 0)
+ printf ("[%zd] %s already compressed\n", ndx, sname);
+ break;
+ }
+
+ free (sname);
+ }
+
+ Elf_Scn *newscn = elf_newscn (elfnew);
+ if (newscn == NULL)
+ {
+ error (0, 0, "Couldn't create new section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (gelf_update_shdr (newscn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update section header %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* Except for the section header string table all data can be
+ copied as is. The section header string table will be
+ created later and the symbol table might be fixed up if
+ necessary. */
+ if (! adjust_names || ndx != shdrstrndx)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ {
+ error (0, 0, "Couldn't get data from section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ Elf_Data *newdata = elf_newdata (newscn);
+ if (newdata == NULL)
+ {
+ error (0, 0, "Couldn't create new data for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ *newdata = *data;
+ }
+
+ /* Keep track of the (new) section names. */
+ if (adjust_names)
+ {
+ char *name;
+ if (newname != NULL)
+ name = newname;
+ else
+ {
+ name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (name == NULL)
+ {
+ error (0, 0, "Couldn't get name for section [%zd]", ndx);
+ return cleanup (-1);
+ }
+ }
+
+ /* We need to keep a copy of the name till the strtab is done. */
+ name = scnnames[ndx] = xstrdup (name);
+ if ((scnstrents[ndx] = ebl_strtabadd (names, name, 0)) == NULL)
+ {
+ error (0, 0, "No memory to add section name string table");
+ return cleanup (-1);
+ }
+
+ /* If the symtab shares strings then add those too. */
+ if (ndx == symtabndx)
+ {
+ /* If the section is (still) compressed we'll need to
+ uncompress it first to adjust the data, then
+ recompress it in the fixup pass. */
+ if (symtab_compressed == T_UNSET)
+ {
+ size_t size = shdr->sh_size;
+ if ((shdr->sh_flags == SHF_COMPRESSED) != 0)
+ {
+ /* Don't report the (internal) uncompression. */
+ if (compress_section (newscn, size, sname, NULL, ndx,
+ false, false, false) < 0)
+ return cleanup (-1);
+
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_ZLIB;
+ }
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ /* Don't report the (internal) uncompression. */
+ if (compress_section (newscn, size, sname, NULL, ndx,
+ true, false, false) < 0)
+ return cleanup (-1);
+
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_GNU;
+ }
+ }
+
+ Elf_Data *symd = elf_getdata (newscn, NULL);
+ if (symd == NULL)
+ {
+ error (0, 0, "Couldn't get symtab data for section [%zd] %s",
+ ndx, name);
+ return cleanup (-1);
+ }
+ size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
+ size_t syms = symd->d_size / elsize;
+ symstrents = xmalloc (syms * sizeof (struct Ebl_Strent *));
+ for (size_t i = 0; i < syms; i++)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
+ if (sym == NULL)
+ {
+ error (0, 0, "Couldn't get symbol %zd", i);
+ return cleanup (-1);
+ }
+ if (sym->st_name != 0)
+ {
+ /* Note we take the name from the original ELF,
+ since the new one will not have setup the
+ strtab yet. */
+ const char *symname = elf_strptr (elf, shdrstrndx,
+ sym->st_name);
+ if (symname == NULL)
+ {
+ error (0, 0, "Couldn't get symbol %zd name", i);
+ return cleanup (-1);
+ }
+ symstrents[i] = ebl_strtabadd (names, symname, 0);
+ if (symstrents[i] == NULL)
+ {
+ error (0, 0, "No memory to add to symbol name");
+ return cleanup (-1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (adjust_names)
+ {
+ /* We got all needed strings, put the new data in the shstrtab. */
+ if (verbose > 0)
+ printf ("[%zd] Updating section string table\n", shdrstrndx);
+
+ scn = elf_getscn (elfnew, shdrstrndx);
+ if (scn == NULL)
+ {
+ error (0, 0, "Couldn't get new section header string table [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ Elf_Data *data = elf_newdata (scn);
+ if (data == NULL)
+ {
+ error (0, 0, "Couldn't create new section header string table data");
+ return cleanup (-1);
+ }
+ ebl_strtabfinalize (names, data);
+ namesbuf = data->d_buf;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for new section strings %zd",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ /* Note that we also might have to compress and possibly set
+ sh_off below */
+ shdr->sh_name = ebl_strtaboffset (scnstrents[shdrstrndx]);
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_flags = 0;
+ shdr->sh_addr = 0;
+ shdr->sh_offset = 0;
+ shdr->sh_size = data->d_size;
+ shdr->sh_link = SHN_UNDEF;
+ shdr->sh_info = SHN_UNDEF;
+ shdr->sh_addralign = 1;
+ shdr->sh_entsize = 0;
+
+ if (gelf_update_shdr (scn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update new section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ /* We might have to compress the data if the user asked us to,
+ or if the section was already compressed (and the user didn't
+ ask for decompression). Note somewhat identical code for
+ symtab below. */
+ if (shstrtab_compressed == T_UNSET)
+ {
+ /* The user didn't ask for compression, but maybe it was
+ compressed in the original ELF file. */
+ Elf_Scn *oldscn = elf_getscn (elf, shdrstrndx);
+ if (oldscn == NULL)
+ {
+ error (0, 0, "Couldn't get section header string table [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shdr = gelf_getshdr (oldscn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for old section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shstrtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (shstrtab_name == NULL)
+ {
+ error (0, 0, "Couldn't get name for old section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shstrtab_size = shdr->sh_size;
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ shstrtab_compressed = T_COMPRESS_ZLIB;
+ else if (strncmp (shstrtab_name, ".zdebug", strlen (".zdebug")) == 0)
+ shstrtab_compressed = T_COMPRESS_GNU;
+ }
+
+ /* Should we (re)compress? */
+ if (shstrtab_compressed != T_UNSET)
+ {
+ if (compress_section (scn, shstrtab_size, shstrtab_name,
+ shstrtab_newname, shdrstrndx,
+ shstrtab_compressed == T_COMPRESS_GNU,
+ true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ }
+
+ /* Make sure to re-get the new ehdr. Adding phdrs and shdrs will
+ have changed it. */
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't re-get new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Set this after the sections have been created, otherwise section
+ zero might not exist yet. */
+ if (setshdrstrndx (elfnew, &newehdr, shdrstrndx) != 0)
+ {
+ error (0, 0, "Couldn't set new shdrstrndx: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Fixup pass. Adjust string table references, symbol table and
+ layout if necessary. */
+ if (layout || adjust_names)
+ {
+ scn = NULL;
+ while ((scn = elf_nextscn (elfnew, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* Keep the offset of allocated sections so they are at the
+ same place in the file. Add (possibly changed)
+ unallocated ones after the allocated ones. */
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ {
+ /* Zero means one. No alignment constraints. */
+ size_t addralign = shdr->sh_addralign ?: 1;
+ last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
+ shdr->sh_offset = last_offset;
+ if (shdr->sh_type != SHT_NOBITS)
+ last_offset += shdr->sh_size;
+ }
+
+ if (adjust_names)
+ shdr->sh_name = ebl_strtaboffset (scnstrents[ndx]);
+
+ if (gelf_update_shdr (scn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update section header %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (adjust_names && ndx == symtabndx)
+ {
+ if (verbose > 0)
+ printf ("[%zd] Updating symbol table\n", symtabndx);
+
+ Elf_Data *symd = elf_getdata (scn, NULL);
+ if (symd == NULL)
+ {
+ error (0, 0, "Couldn't get new symtab data section [%zd]",
+ ndx);
+ return cleanup (-1);
+ }
+ size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
+ size_t syms = symd->d_size / elsize;
+ for (size_t i = 0; i < syms; i++)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
+ if (sym == NULL)
+ {
+ error (0, 0, "2 Couldn't get symbol %zd", i);
+ return cleanup (-1);
+ }
+
+ if (sym->st_name != 0)
+ {
+ sym->st_name = ebl_strtaboffset (symstrents[i]);
+
+ if (gelf_update_sym (symd, i, sym) == 0)
+ {
+ error (0, 0, "Couldn't update symbol %zd", i);
+ return cleanup (-1);
+ }
+ }
+ }
+
+ /* We might have to compress the data if the user asked
+ us to, or if the section was already compressed (and
+ the user didn't ask for decompression). Note
+ somewhat identical code for shstrtab above. */
+ if (symtab_compressed == T_UNSET)
+ {
+ /* The user didn't ask for compression, but maybe it was
+ compressed in the original ELF file. */
+ Elf_Scn *oldscn = elf_getscn (elf, symtabndx);
+ if (oldscn == NULL)
+ {
+ error (0, 0, "Couldn't get symbol table [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ shdr = gelf_getshdr (oldscn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get old symbol table shdr [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ symtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (symtab_name == NULL)
+ {
+ error (0, 0, "Couldn't get old symbol table name [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ symtab_size = shdr->sh_size;
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ symtab_compressed = T_COMPRESS_ZLIB;
+ else if (strncmp (symtab_name, ".zdebug",
+ strlen (".zdebug")) == 0)
+ symtab_compressed = T_COMPRESS_GNU;
+ }
+
+ /* Should we (re)compress? */
+ if (symtab_compressed != T_UNSET)
+ {
+ if (compress_section (scn, symtab_size, symtab_name,
+ symtab_newname, symtabndx,
+ symtab_compressed == T_COMPRESS_GNU,
+ true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ }
+ }
+ }
+
+ /* If we have phdrs we want elf_update to layout the SHF_ALLOC
+ sections precisely as in the original file. In that case we are
+ also responsible for setting phoff and shoff */
+ if (layout)
+ {
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Position the shdrs after the last (unallocated) section. */
+ const size_t offsize = gelf_fsize (elfnew, ELF_T_OFF, 1, EV_CURRENT);
+ newehdr.e_shoff = ((last_offset + offsize - 1)
+ & ~((GElf_Off) (offsize - 1)));
+
+ /* The phdrs go in the same place as in the original file.
+ Normally right after the ELF header. */
+ newehdr.e_phoff = ehdr.e_phoff;
+
+ if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+ {
+ error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ }
+
+ elf_flagelf (elfnew, ELF_C_SET, ((layout ? ELF_F_LAYOUT : 0)
+ | (permissive ? ELF_F_PERMISSIVE : 0)));
+
+ if (elf_update (elfnew, ELF_C_WRITE) < 0)
+ {
+ error (0, 0, "Couldn't write %s: %s", fnew, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ elf_end (elfnew);
+ elfnew = NULL;
+
+ /* Try to match mode and owner.group of the original file. */
+ if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
+ if (verbose >= 0)
+ error (0, errno, "Couldn't fchmod %s", fnew);
+ if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
+ if (verbose >= 0)
+ error (0, errno, "Couldn't fchown %s", fnew);
+
+ /* Finally replace the old file with the new file. */
+ if (foutput == NULL)
+ if (rename (fnew, fname) != 0)
+ {
+ error (0, errno, "Couldn't rename %s to %s", fnew, fname);
+ return cleanup (-1);
+ }
+
+ /* We are finally done with the new file, don't unlink it now. */
+ free (fnew);
+ fnew = NULL;
+
+ return cleanup (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ const struct argp_option options[] =
+ {
+ { "output", 'o', "FILE", 0,
+ N_("Place (de)compressed output into FILE"),
+ 0 },
+ { "type", 't', "TYPE", 0,
+ N_("What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias) or 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias)"),
+ 0 },
+ { "name", 'n', "SECTION", 0,
+ N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"),
+ 0 },
+ { "verbose", 'v', NULL, 0,
+ N_("Print a message for each section being (de)compressed"),
+ 0 },
+ { "force", 'f', NULL, 0,
+ N_("Force compression of section even if it would become larger"),
+ 0 },
+ { "permissive", 'p', NULL, 0,
+ N_("Relax a few rules to handle slightly broken ELF files"),
+ 0 },
+ { "quiet", 'q', NULL, 0,
+ N_("Be silent when a section cannot be compressed"),
+ 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+ };
+
+ const struct argp argp =
+ {
+ .options = options,
+ .parser = parse_opt,
+ .args_doc = N_("FILE..."),
+ .doc = N_("Compress or decompress sections in an ELF file.")
+ };
+
+ int remaining;
+ if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
+ return EXIT_FAILURE;
+
+ /* Should already be handled by ARGP_KEY_NO_ARGS case above,
+ just sanity check. */
+ if (remaining >= argc)
+ error (EXIT_FAILURE, 0, N_("No input file given"));
+
+ /* Likewise for the ARGP_KEY_ARGS case above, an extra sanity check. */
+ if (foutput != NULL && remaining + 1 < argc)
+ error (EXIT_FAILURE, 0,
+ N_("Only one input file allowed together with '-o'"));
+
+ elf_version (EV_CURRENT);
+
+ /* Process all the remaining files. */
+ int result = 0;
+ do
+ result |= process_file (argv[remaining]);
+ while (++remaining < argc);
+
+ free_patterns ();
+ return result;
+}
diff --git a/src/elflint.c b/src/elflint.c
index 7d72a1f7..eae77614 100644
--- a/src/elflint.c
+++ b/src/elflint.c
@@ -1,5 +1,5 @@
/* Pedantic checking of ELF files compliance with gABI/psABI spec.
- Copyright (C) 2001-2014 Red Hat, Inc.
+ Copyright (C) 2001-2015 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2001.
@@ -380,9 +380,11 @@ check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size)
ERROR (gettext ("unknown ELF header version number e_ident[%d] == %d\n"),
EI_VERSION, ehdr->e_ident[EI_VERSION]);
- /* We currently don't handle any OS ABIs other than Linux. */
+ /* We currently don't handle any OS ABIs other than Linux and the
+ kFreeBSD variant of Debian. */
if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE
- && ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX)
+ && ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX
+ && ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD)
ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"),
EI_OSABI,
ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf)));
@@ -801,7 +803,11 @@ section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"),
&& strcmp (name, "__bss_start") != 0
&& strcmp (name, "__bss_start__") != 0
&& strcmp (name, "__TMC_END__") != 0
- && strcmp (name, ".TOC.") != 0))
+ && strcmp (name, ".TOC.") != 0
+ && strcmp (name, "_edata") != 0
+ && strcmp (name, "__edata") != 0
+ && strcmp (name, "_end") != 0
+ && strcmp (name, "__end") != 0))
ERROR (gettext ("\
section [%2d] '%s': symbol %zu: st_value out of bounds\n"),
idx, section_name (ebl, idx), cnt);
@@ -2746,7 +2752,8 @@ section_flags_string (GElf_Word flags, char *buf, size_t len)
NEWFLAG (LINK_ORDER),
NEWFLAG (OS_NONCONFORMING),
NEWFLAG (GROUP),
- NEWFLAG (TLS)
+ NEWFLAG (TLS),
+ NEWFLAG (COMPRESSED)
};
#undef NEWFLAG
const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]);
@@ -3694,7 +3701,8 @@ zeroth section has nonzero link value while ELF header does not signal overflow
size_t versym_scnndx = 0;
for (size_t cnt = 1; cnt < shnum; ++cnt)
{
- shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem);
+ Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
+ shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr == NULL)
{
ERROR (gettext ("\
@@ -3744,9 +3752,11 @@ section [%2d] '%s' has wrong type: expected %s, is %s\n"),
if (special_sections[s].attrflag == exact
|| special_sections[s].attrflag == exact_or_gnuld)
{
- /* Except for the link order and group bit all the
- other bits should match exactly. */
- if ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP))
+ /* Except for the link order, group bit and
+ compression flag all the other bits should
+ match exactly. */
+ if ((shdr->sh_flags
+ & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED))
!= special_sections[s].attr
&& (special_sections[s].attrflag == exact || !gnuld))
ERROR (gettext ("\
@@ -3762,9 +3772,10 @@ section [%2zu] '%s' has wrong flags: expected %s, is %s\n"),
{
if ((shdr->sh_flags & special_sections[s].attr)
!= special_sections[s].attr
- || ((shdr->sh_flags & ~(SHF_LINK_ORDER | SHF_GROUP
- | special_sections[s].attr
- | special_sections[s].attr2))
+ || ((shdr->sh_flags
+ & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED
+ | special_sections[s].attr
+ | special_sections[s].attr2))
!= 0))
ERROR (gettext ("\
section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"),
@@ -3867,7 +3878,8 @@ section [%2zu] '%s': size not multiple of entry size\n"),
#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \
| SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \
- | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS)
+ | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS \
+ | SHF_COMPRESSED)
if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS)
{
GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS;
@@ -3897,6 +3909,25 @@ section [%2zu] '%s': thread-local data sections address not zero\n"),
// XXX TODO more tests!?
}
+ if (shdr->sh_flags & SHF_COMPRESSED)
+ {
+ if (shdr->sh_flags & SHF_ALLOC)
+ ERROR (gettext ("\
+section [%2zu] '%s': allocated section cannot be compressed\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (shdr->sh_type == SHT_NOBITS)
+ ERROR (gettext ("\
+section [%2zu] '%s': nobits section cannot be compressed\n"),
+ cnt, section_name (ebl, cnt));
+
+ GElf_Chdr chdr;
+ if (gelf_getchdr (scn, &chdr) == NULL)
+ ERROR (gettext ("\
+section [%2zu] '%s': compressed section with no compression header: %s\n"),
+ cnt, section_name (ebl, cnt), elf_errmsg (-1));
+ }
+
if (shdr->sh_link >= shnum)
ERROR (gettext ("\
section [%2zu] '%s': invalid section reference in link value\n"),
diff --git a/src/readelf.c b/src/readelf.c
index 5f6e4edd..0db192ee 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -48,6 +48,7 @@
#include "../libelf/libelfP.h"
#include "../libelf/common.h"
#include "../libebl/libeblP.h"
+#include "../libdwelf/libdwelf.h"
#include "../libdw/libdwP.h"
#include "../libdwfl/libdwflP.h"
#include "../libdw/memory-access.h"
@@ -112,6 +113,8 @@ static const struct argp_option options[] =
N_("Display just offsets instead of resolving values to addresses in DWARF data"), 0 },
{ "wide", 'W', NULL, 0,
N_("Ignored for compatibility (lines always wide)"), 0 },
+ { "decompress", 'z', NULL, 0,
+ N_("Show compression information for compressed sections (when used with -S); decompress section before dumping data (when used with -p or -x)"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
@@ -190,6 +193,9 @@ static bool decodedaranges = false;
/* True if we should print the .debug_aranges section using libdw. */
static bool decodedline = false;
+/* True if we want to show more information about compressed sections. */
+static bool print_decompress = false;
+
/* Select printing of debugging sections. */
static enum section_e
{
@@ -479,6 +485,9 @@ parse_opt (int key, char *arg,
break;
case 'W': /* Ignored. */
break;
+ case 'z':
+ print_decompress = true;
+ break;
case ELF_INPUT_SECTION:
if (arg == NULL)
elf_input_section = ".gnu_debugdata";
@@ -797,6 +806,20 @@ process_file (int fd, const char *fname, bool only_one)
close (fd);
}
+/* Check whether there are any compressed sections in the ELF file. */
+static bool
+elf_contains_chdrs (Elf *elf)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+ return true;
+ }
+ return false;
+}
/* Process one ELF file. */
static void
@@ -835,17 +858,21 @@ process_elf_file (Dwfl_Module *dwflmod, int fd)
gettext ("cannot determine number of program headers: %s"),
elf_errmsg (-1));
- /* For an ET_REL file, libdwfl has adjusted the in-core shdrs
- and may have applied relocation to some sections.
- So we need to get a fresh Elf handle on the file to display those. */
- bool print_unrelocated = (print_section_header
- || print_relocations
- || dump_data_sections != NULL
- || print_notes);
+ /* For an ET_REL file, libdwfl has adjusted the in-core shdrs and
+ may have applied relocation to some sections. If there are any
+ compressed sections, any pass (or libdw/libdwfl) might have
+ uncompressed them. So we need to get a fresh Elf handle on the
+ file to display those. */
+ bool print_unchanged = ((print_section_header
+ || print_relocations
+ || dump_data_sections != NULL
+ || print_notes)
+ && (ehdr->e_type == ET_REL
+ || elf_contains_chdrs (ebl->elf)));
Elf *pure_elf = NULL;
Ebl *pure_ebl = ebl;
- if (ehdr->e_type == ET_REL && print_unrelocated)
+ if (print_unchanged)
{
/* Read the file afresh. */
off_t aroff = elf_getaroff (elf);
@@ -1065,6 +1092,17 @@ get_visibility_type (int value)
}
}
+static const char *
+elf_ch_type_name (unsigned int code)
+{
+ if (code == 0)
+ return "NONE";
+
+ if (code == ELFCOMPRESS_ZLIB)
+ return "ZLIB";
+
+ return "UNKNOWN";
+}
/* Print the section headers. */
static void
@@ -1091,6 +1129,14 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\
else
puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
+ if (print_decompress)
+ {
+ if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ puts (gettext (" [Compression Size Al]"));
+ else
+ puts (gettext (" [Compression Size Al]"));
+ }
+
for (cnt = 0; cnt < shnum; ++cnt)
{
Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
@@ -1128,25 +1174,57 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\
*cp++ = 'G';
if (shdr->sh_flags & SHF_TLS)
*cp++ = 'T';
+ if (shdr->sh_flags & SHF_COMPRESSED)
+ *cp++ = 'C';
if (shdr->sh_flags & SHF_ORDERED)
*cp++ = 'O';
if (shdr->sh_flags & SHF_EXCLUDE)
*cp++ = 'E';
*cp = '\0';
+ const char *sname;
char buf[128];
+ sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "<corrupt>";
printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64
" %2" PRId64 " %-5s %2" PRId32 " %3" PRId32
" %2" PRId64 "\n",
- cnt,
- elf_strptr (ebl->elf, shstrndx, shdr->sh_name)
- ?: "<corrupt>",
+ cnt, sname,
ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)),
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size,
shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info,
shdr->sh_addralign);
+
+ if (print_decompress)
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ GElf_Chdr chdr;
+ if (gelf_getchdr (scn, &chdr) != NULL)
+ printf (" [ELF %s (%" PRId32 ") %0*" PRIx64
+ " %2" PRId64 "]\n",
+ elf_ch_type_name (chdr.ch_type),
+ chdr.ch_type,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8,
+ chdr.ch_size, chdr.ch_addralign);
+ else
+ error (0, 0,
+ gettext ("bad compression header for section %zd: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+ else if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0)
+ {
+ ssize_t size;
+ if ((size = dwelf_scn_gnu_compressed_size (scn)) >= 0)
+ printf (" [GNU ZLIB %0*zx ]\n",
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, size);
+ else
+ error (0, 0,
+ gettext ("bad gnu compressed size for section %zd: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+ }
}
fputc_unlocked ('\n', stdout);
@@ -1443,7 +1521,17 @@ print_scngrp (Ebl *ebl)
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == SHT_GROUP)
- handle_scngrp (ebl, scn, shdr);
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ }
+ handle_scngrp (ebl, scn, shdr);
+ }
}
}
@@ -2142,7 +2230,17 @@ print_symtab (Ebl *ebl, int type)
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == (GElf_Word) type)
- handle_symtab (ebl, scn, shdr);
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ }
+ handle_symtab (ebl, scn, shdr);
+ }
}
}
@@ -3211,6 +3309,16 @@ handle_hash (Ebl *ebl)
if (likely (shdr != NULL))
{
+ if ((shdr->sh_type == SHT_HASH || shdr->sh_type == SHT_GNU_HASH)
+ && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ }
+
if (shdr->sh_type == SHT_HASH)
{
if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword))
@@ -8260,11 +8368,9 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
int n;
for (n = 0; n < ndebug_sections; ++n)
if (strcmp (name, debug_sections[n].name) == 0
-#if USE_ZLIB
|| (name[0] == '.' && name[1] == 'z'
&& debug_sections[n].name[1] == 'd'
&& strcmp (&name[2], &debug_sections[n].name[1]) == 0)
-#endif
)
{
if ((print_debug_sections | implicit_debug_sections)
@@ -9396,16 +9502,34 @@ dump_data_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name)
elf_ndxscn (scn), name);
else
{
+ if (print_decompress)
+ {
+ /* We try to decompress the section, but keep the old shdr around
+ so we can show both the original shdr size and the uncompressed
+ data size. */
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ elf_compress (scn, 0, 0);
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ elf_compress_gnu (scn, 0, 0);
+ }
+
Elf_Data *data = elf_rawdata (scn, NULL);
if (data == NULL)
error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"),
elf_ndxscn (scn), name, elf_errmsg (-1));
else
{
- printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64
- " bytes at offset %#0" PRIx64 ":\n"),
- elf_ndxscn (scn), name,
- shdr->sh_size, shdr->sh_offset);
+ if (data->d_size == shdr->sh_size)
+ printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64
+ " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, shdr->sh_offset);
+ else
+ printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64
+ " bytes (%zd uncompressed) at offset %#0"
+ PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, data->d_size, shdr->sh_offset);
hex_dump (data->d_buf, data->d_size);
}
}
@@ -9419,16 +9543,34 @@ print_string_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name)
elf_ndxscn (scn), name);
else
{
+ if (print_decompress)
+ {
+ /* We try to decompress the section, but keep the old shdr around
+ so we can show both the original shdr size and the uncompressed
+ data size. */
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ elf_compress (scn, 0, 0);
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ elf_compress_gnu (scn, 0, 0);
+ }
+
Elf_Data *data = elf_rawdata (scn, NULL);
if (data == NULL)
error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"),
elf_ndxscn (scn), name, elf_errmsg (-1));
else
{
- printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64
- " bytes at offset %#0" PRIx64 ":\n"),
- elf_ndxscn (scn), name,
- shdr->sh_size, shdr->sh_offset);
+ if (data->d_size == shdr->sh_size)
+ printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64
+ " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, shdr->sh_offset);
+ else
+ printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64
+ " bytes (%zd uncompressed) at offset %#0"
+ PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, data->d_size, shdr->sh_offset);
const char *start = data->d_buf;
const char *const limit = start + data->d_size;