Blame src/elfcompress.c

Packit 032894
/* Compress or decompress an ELF file.
Packit 032894
   Copyright (C) 2015, 2016, 2018 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#include <config.h>
Packit 032894
#include <assert.h>
Packit 032894
#include <argp.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <inttypes.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <string.h>
Packit 032894
#include <locale.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <fnmatch.h>
Packit 032894
#include <sys/types.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
#include <unistd.h>
Packit 032894
#include ELFUTILS_HEADER(elf)
Packit 032894
#include ELFUTILS_HEADER(ebl)
Packit 032894
#include ELFUTILS_HEADER(dwelf)
Packit 032894
#include <gelf.h>
Packit 032894
#include "system.h"
Packit 032894
#include "libeu.h"
Packit 032894
#include "printversion.h"
Packit 032894
Packit 032894
/* Name and version of program.  */
Packit 032894
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit 032894
Packit 032894
/* Bug report address.  */
Packit 032894
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit 032894
Packit 032894
static int verbose = 0; /* < 0, no warnings, > 0 extra verbosity.  */
Packit 032894
static bool force = false;
Packit 032894
static bool permissive = false;
Packit 032894
static const char *foutput = NULL;
Packit 032894
Packit 032894
#define T_UNSET 0
Packit 032894
#define T_DECOMPRESS 1    /* none */
Packit 032894
#define T_COMPRESS_ZLIB 2 /* zlib */
Packit 032894
#define T_COMPRESS_GNU  3 /* zlib-gnu */
Packit 032894
static int type = T_UNSET;
Packit 032894
Packit 032894
struct section_pattern
Packit 032894
{
Packit 032894
  char *pattern;
Packit 032894
  struct section_pattern *next;
Packit 032894
};
Packit 032894
Packit 032894
static struct section_pattern *patterns = NULL;
Packit 032894
Packit 032894
static void
Packit 032894
add_pattern (const char *pattern)
Packit 032894
{
Packit 032894
  struct section_pattern *p = xmalloc (sizeof *p);
Packit 032894
  p->pattern = xstrdup (pattern);
Packit 032894
  p->next = patterns;
Packit 032894
  patterns = p;
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
free_patterns (void)
Packit 032894
{
Packit 032894
  struct section_pattern *pattern = patterns;
Packit 032894
  while (pattern != NULL)
Packit 032894
    {
Packit 032894
      struct section_pattern *p = pattern;
Packit 032894
      pattern = p->next;
Packit 032894
      free (p->pattern);
Packit 032894
      free (p);
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
static error_t
Packit 032894
parse_opt (int key, char *arg __attribute__ ((unused)),
Packit 032894
	   struct argp_state *state __attribute__ ((unused)))
Packit 032894
{
Packit 032894
  switch (key)
Packit 032894
    {
Packit 032894
    case 'v':
Packit 032894
      verbose++;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'q':
Packit 032894
      verbose--;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'f':
Packit 032894
      force = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'p':
Packit 032894
      permissive = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'n':
Packit 032894
      add_pattern (arg);
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'o':
Packit 032894
      if (foutput != NULL)
Packit 032894
	argp_error (state, N_("-o option specified twice"));
Packit 032894
      else
Packit 032894
	foutput = arg;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 't':
Packit 032894
      if (type != T_UNSET)
Packit 032894
	argp_error (state, N_("-t option specified twice"));
Packit 032894
Packit 032894
      if (strcmp ("none", arg) == 0)
Packit 032894
	type = T_DECOMPRESS;
Packit 032894
      else if (strcmp ("zlib", arg) == 0 || strcmp ("zlib-gabi", arg) == 0)
Packit 032894
	type = T_COMPRESS_ZLIB;
Packit 032894
      else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0)
Packit 032894
	type = T_COMPRESS_GNU;
Packit 032894
      else
Packit 032894
	argp_error (state, N_("unknown compression type '%s'"), arg);
Packit 032894
      break;
Packit 032894
Packit 032894
    case ARGP_KEY_SUCCESS:
Packit 032894
      if (type == T_UNSET)
Packit 032894
	type = T_COMPRESS_ZLIB;
Packit 032894
      if (patterns == NULL)
Packit 032894
	add_pattern (".?(z)debug*");
Packit 032894
      break;
Packit 032894
Packit 032894
    case ARGP_KEY_NO_ARGS:
Packit 032894
      /* We need at least one input file.  */
Packit 032894
      argp_error (state, N_("No input file given"));
Packit 032894
      break;
Packit 032894
Packit 032894
    case ARGP_KEY_ARGS:
Packit 032894
      if (foutput != NULL && state->argc - state->next > 1)
Packit 032894
	argp_error (state,
Packit 032894
		    N_("Only one input file allowed together with '-o'"));
Packit 032894
      /* We only use this for checking the number of arguments, we don't
Packit 032894
	 actually want to consume them.  */
Packit 032894
      FALLTHROUGH;
Packit 032894
    default:
Packit 032894
      return ARGP_ERR_UNKNOWN;
Packit 032894
    }
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
section_name_matches (const char *name)
Packit 032894
{
Packit 032894
  struct section_pattern *pattern = patterns;
Packit 032894
  while (pattern != NULL)
Packit 032894
    {
Packit 032894
      if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0)
Packit 032894
	return true;
Packit 032894
      pattern = pattern->next;
Packit 032894
    }
Packit 032894
  return false;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
setshdrstrndx (Elf *elf, GElf_Ehdr *ehdr, size_t ndx)
Packit 032894
{
Packit 032894
  if (ndx < SHN_LORESERVE)
Packit 032894
    ehdr->e_shstrndx = ndx;
Packit 032894
  else
Packit 032894
    {
Packit 032894
      ehdr->e_shstrndx = SHN_XINDEX;
Packit 032894
      Elf_Scn *zscn = elf_getscn (elf, 0);
Packit 032894
      GElf_Shdr zshdr_mem;
Packit 032894
      GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
Packit 032894
      if (zshdr == NULL)
Packit 032894
	return -1;
Packit 032894
      zshdr->sh_link = ndx;
Packit 032894
      if (gelf_update_shdr (zscn, zshdr) == 0)
Packit 032894
	return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  if (gelf_update_ehdr (elf, ehdr) == 0)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
compress_section (Elf_Scn *scn, size_t orig_size, const char *name,
Packit 032894
		  const char *newname, size_t ndx,
Packit 032894
		  bool gnu, bool compress, bool report_verbose)
Packit 032894
{
Packit 032894
  int res;
Packit 032894
  unsigned int flags = compress && force ? ELF_CHF_FORCE : 0;
Packit 032894
  if (gnu)
Packit 032894
    res = elf_compress_gnu (scn, compress ? 1 : 0, flags);
Packit 032894
  else
Packit 032894
    res = elf_compress (scn, compress ? ELFCOMPRESS_ZLIB : 0, flags);
Packit 032894
Packit 032894
  if (res < 0)
Packit 032894
    error (0, 0, "Couldn't decompress section [%zd] %s: %s",
Packit 032894
	   ndx, name, elf_errmsg (-1));
Packit 032894
  else
Packit 032894
    {
Packit 032894
      if (compress && res == 0)
Packit 032894
	{
Packit 032894
	  if (verbose >= 0)
Packit 032894
	    printf ("[%zd] %s NOT compressed, wouldn't be smaller\n",
Packit 032894
		    ndx, name);
Packit 032894
	}
Packit 032894
Packit 032894
      if (report_verbose && res > 0)
Packit 032894
	{
Packit 032894
	  printf ("[%zd] %s %s", ndx, name,
Packit 032894
		  compress ? "compressed" : "decompressed");
Packit 032894
	  if (newname != NULL)
Packit 032894
	    printf (" -> %s", newname);
Packit 032894
Packit 032894
	  /* Reload shdr, it has changed.  */
Packit 032894
	  GElf_Shdr shdr_mem;
Packit 032894
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
	  if (shdr == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get shdr for section [%zd]", ndx);
Packit 032894
	      return -1;
Packit 032894
	    }
Packit 032894
	  float new = shdr->sh_size;
Packit 032894
	  float orig = orig_size ?: 1;
Packit 032894
	  printf (" (%zu => %" PRIu64 " %.2f%%)\n",
Packit 032894
		  orig_size, shdr->sh_size, (new / orig) * 100);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  return res;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
process_file (const char *fname)
Packit 032894
{
Packit 032894
  if (verbose > 0)
Packit 032894
    printf ("processing: %s\n", fname);
Packit 032894
Packit 032894
  /* The input ELF.  */
Packit 032894
  int fd = -1;
Packit 032894
  Elf *elf = NULL;
Packit 032894
Packit 032894
  /* The output ELF.  */
Packit 032894
  char *fnew = NULL;
Packit 032894
  int fdnew = -1;
Packit 032894
  Elf *elfnew = NULL;
Packit 032894
Packit 032894
  /* Buffer for (one) new section name if necessary.  */
Packit 032894
  char *snamebuf = NULL;
Packit 032894
Packit 032894
  /* String table (and symbol table), if section names need adjusting.  */
Packit 032894
  Dwelf_Strtab *names = NULL;
Packit 032894
  Dwelf_Strent **scnstrents = NULL;
Packit 032894
  Dwelf_Strent **symstrents = NULL;
Packit 032894
  char **scnnames = NULL;
Packit 032894
Packit 032894
  /* Section data from names.  */
Packit 032894
  void *namesbuf = NULL;
Packit 032894
Packit 032894
  /* Which sections match and need to be (un)compressed.  */
Packit 032894
  unsigned int *sections = NULL;
Packit 032894
Packit 032894
  /* How many sections are we talking about?  */
Packit 032894
  size_t shnum = 0;
Packit 032894
Packit 032894
#define WORD_BITS (8U * sizeof (unsigned int))
Packit 032894
  void set_section (size_t ndx)
Packit 032894
  {
Packit 032894
    sections[ndx / WORD_BITS] |= (1U << (ndx % WORD_BITS));
Packit 032894
  }
Packit 032894
Packit 032894
  bool get_section (size_t ndx)
Packit 032894
  {
Packit 032894
    return (sections[ndx / WORD_BITS] & (1U << (ndx % WORD_BITS))) != 0;
Packit 032894
  }
Packit 032894
Packit 032894
  /* How many sections are we going to change?  */
Packit 032894
  size_t get_sections (void)
Packit 032894
  {
Packit 032894
    size_t s = 0;
Packit 032894
    for (size_t i = 0; i < shnum / WORD_BITS + 1; i++)
Packit 032894
      s += __builtin_popcount (sections[i]);
Packit 032894
    return s;
Packit 032894
  }
Packit 032894
Packit 032894
  int cleanup (int res)
Packit 032894
  {
Packit 032894
    elf_end (elf);
Packit 032894
    close (fd);
Packit 032894
Packit 032894
    elf_end (elfnew);
Packit 032894
    close (fdnew);
Packit 032894
Packit 032894
    if (fnew != NULL)
Packit 032894
      {
Packit 032894
	unlink (fnew);
Packit 032894
	free (fnew);
Packit 032894
	fnew = NULL;
Packit 032894
      }
Packit 032894
Packit 032894
    free (snamebuf);
Packit 032894
    if (names != NULL)
Packit 032894
      {
Packit 032894
	dwelf_strtab_free (names);
Packit 032894
	free (scnstrents);
Packit 032894
	free (symstrents);
Packit 032894
	free (namesbuf);
Packit 032894
	if (scnnames != NULL)
Packit 032894
	  {
Packit 032894
	    for (size_t n = 0; n < shnum; n++)
Packit 032894
	      free (scnnames[n]);
Packit 032894
	    free (scnnames);
Packit 032894
	  }
Packit 032894
      }
Packit 032894
Packit 032894
    free (sections);
Packit 032894
Packit 032894
    return res;
Packit 032894
  }
Packit 032894
Packit 032894
  fd = open (fname, O_RDONLY);
Packit 032894
  if (fd < 0)
Packit 032894
    {
Packit 032894
      error (0, errno, "Couldn't open %s\n", fname);
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  elf = elf_begin (fd, ELF_C_READ, NULL);
Packit 032894
  if (elf == NULL)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't open ELF file %s for reading: %s",
Packit 032894
	     fname, elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* We dont' handle ar files (or anything else), we probably should.  */
Packit 032894
  Elf_Kind kind = elf_kind (elf);
Packit 032894
  if (kind != ELF_K_ELF)
Packit 032894
    {
Packit 032894
      if (kind == ELF_K_AR)
Packit 032894
	error (0, 0, "Cannot handle ar files: %s", fname);
Packit 032894
      else
Packit 032894
	error (0, 0, "Unknown file type: %s", fname);
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  struct stat st;
Packit 032894
  if (fstat (fd, &st) != 0)
Packit 032894
    {
Packit 032894
      error (0, errno, "Couldn't fstat %s", fname);
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  GElf_Ehdr ehdr;
Packit 032894
  if (gelf_getehdr (elf, &ehdr) == NULL)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't get ehdr for %s: %s", fname, elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Get the section header string table.  */
Packit 032894
  size_t shdrstrndx;
Packit 032894
  if (elf_getshdrstrndx (elf, &shdrstrndx) != 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't get section header string table index in %s: %s",
Packit 032894
	     fname, elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* How many sections are we talking about?  */
Packit 032894
  if (elf_getshdrnum (elf, &shnum) != 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't get number of sections in %s: %s",
Packit 032894
	     fname, elf_errmsg (1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  if (shnum == 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "ELF file %s has no sections", fname);
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  sections = xcalloc (shnum / 8 + 1, sizeof (unsigned int));
Packit 032894
Packit 032894
  size_t phnum;
Packit 032894
  if (elf_getphdrnum (elf, &phnum) != 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't get phdrnum: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Whether we need to adjust any section names (going to/from GNU
Packit 032894
     naming).  If so we'll need to build a new section header string
Packit 032894
     table.  */
Packit 032894
  bool adjust_names = false;
Packit 032894
Packit 032894
  /* If there are phdrs we want to maintain the layout of the
Packit 032894
     allocated sections in the file.  */
Packit 032894
  bool layout = phnum != 0;
Packit 032894
Packit 032894
  /* While going through all sections keep track of last section data
Packit 032894
     offset if needed to keep the layout.  We are responsible for
Packit 032894
     adding the section offsets and headers (e_shoff) in that case
Packit 032894
     (which we will place after the last section).  */
Packit 032894
  GElf_Off last_offset = 0;
Packit 032894
  if (layout)
Packit 032894
    last_offset = (ehdr.e_phoff
Packit 032894
		   + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT));
Packit 032894
Packit 032894
  /* Which section, if any, is a symbol table that shares a string
Packit 032894
     table with the section header string table?  */
Packit 032894
  size_t symtabndx = 0;
Packit 032894
Packit 032894
  /* We do three passes over all sections.
Packit 032894
Packit 032894
     First an inspection pass over the old Elf to see which section
Packit 032894
     data needs to be copied and/or transformed, which sections need a
Packit 032894
     names change and whether there is a symbol table that might need
Packit 032894
     to be adjusted be if the section header name table is changed.
Packit 032894
Packit 032894
     If nothing needs changing, and the input and output file are the
Packit 032894
     same, we are done.
Packit 032894
Packit 032894
     Second a collection pass that creates the Elf sections and copies
Packit 032894
     the data.  This pass will compress/decompress section data when
Packit 032894
     needed.  And it will collect all data needed if we'll need to
Packit 032894
     construct a new string table. Afterwards the new string table is
Packit 032894
     constructed.
Packit 032894
Packit 032894
     Third a fixup/adjustment pass over the new Elf that will adjust
Packit 032894
     any section references (names) and adjust the layout based on the
Packit 032894
     new sizes of the sections if necessary.  This pass is optional if
Packit 032894
     we aren't responsible for the layout and the section header
Packit 032894
     string table hasn't been changed.  */
Packit 032894
Packit 032894
  /* Inspection pass.  */
Packit 032894
  size_t maxnamelen = 0;
Packit 032894
  Elf_Scn *scn = NULL;
Packit 032894
  while ((scn = elf_nextscn (elf, scn)) != NULL)
Packit 032894
    {
Packit 032894
      size_t ndx = elf_ndxscn (scn);
Packit 032894
      if (ndx > shnum)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Unexpected section number %zd, expected only %zd",
Packit 032894
		 ndx, shnum);
Packit 032894
	  cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
      if (shdr == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get shdr for section %zd", ndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
Packit 032894
      if (sname == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get name for section %zd", ndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      if (section_name_matches (sname))
Packit 032894
	{
Packit 032894
	  if (!force && type == T_DECOMPRESS
Packit 032894
	      && (shdr->sh_flags & SHF_COMPRESSED) == 0
Packit 032894
	      && strncmp (sname, ".zdebug", strlen (".zdebug")) != 0)
Packit 032894
	    {
Packit 032894
	      if (verbose > 0)
Packit 032894
		printf ("[%zd] %s already decompressed\n", ndx, sname);
Packit 032894
	    }
Packit 032894
	  else if (!force && type == T_COMPRESS_ZLIB
Packit 032894
		   && (shdr->sh_flags & SHF_COMPRESSED) != 0)
Packit 032894
	    {
Packit 032894
	      if (verbose > 0)
Packit 032894
		printf ("[%zd] %s already compressed\n", ndx, sname);
Packit 032894
	    }
Packit 032894
	  else if (!force && type == T_COMPRESS_GNU
Packit 032894
		   && strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
	    {
Packit 032894
	      if (verbose > 0)
Packit 032894
		printf ("[%zd] %s already GNU compressed\n", ndx, sname);
Packit 032894
	    }
Packit 032894
	  else if (shdr->sh_type != SHT_NOBITS
Packit 032894
	      && (shdr->sh_flags & SHF_ALLOC) == 0)
Packit 032894
	    {
Packit 032894
	      set_section (ndx);
Packit 032894
	      /* Check if we might want to change this section name.  */
Packit 032894
	      if (! adjust_names
Packit 032894
		  && ((type != T_COMPRESS_GNU
Packit 032894
		       && strncmp (sname, ".zdebug",
Packit 032894
				   strlen (".zdebug")) == 0)
Packit 032894
		      || (type == T_COMPRESS_GNU
Packit 032894
			  && strncmp (sname, ".debug",
Packit 032894
				      strlen (".debug")) == 0)))
Packit 032894
		adjust_names = true;
Packit 032894
Packit 032894
	      /* We need a buffer this large if we change the names.  */
Packit 032894
	      if (adjust_names)
Packit 032894
		{
Packit 032894
		  size_t slen = strlen (sname);
Packit 032894
		  if (slen > maxnamelen)
Packit 032894
		    maxnamelen = slen;
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	  else
Packit 032894
	    if (verbose >= 0)
Packit 032894
	      printf ("[%zd] %s ignoring %s section\n", ndx, sname,
Packit 032894
		      (shdr->sh_type == SHT_NOBITS ? "no bits" : "allocated"));
Packit 032894
	}
Packit 032894
Packit 032894
      if (shdr->sh_type == SHT_SYMTAB)
Packit 032894
	{
Packit 032894
	  /* Check if we might have to adjust the symbol name indexes.  */
Packit 032894
	  if (shdr->sh_link == shdrstrndx)
Packit 032894
	    {
Packit 032894
	      if (symtabndx != 0)
Packit 032894
		{
Packit 032894
		  error (0, 0,
Packit 032894
			 "Multiple symbol tables (%zd, %zd) using the same string table unsupported", symtabndx, ndx);
Packit 032894
		  return cleanup (-1);
Packit 032894
		}
Packit 032894
	      symtabndx = ndx;
Packit 032894
	    }
Packit 032894
	}
Packit 032894
Packit 032894
      /* Keep track of last allocated data offset.  */
Packit 032894
      if (layout)
Packit 032894
	if ((shdr->sh_flags & SHF_ALLOC) != 0)
Packit 032894
	  {
Packit 032894
	    GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS
Packit 032894
					      ? shdr->sh_size : 0);
Packit 032894
	    if (last_offset < off)
Packit 032894
	      last_offset = off;
Packit 032894
	  }
Packit 032894
    }
Packit 032894
Packit 032894
  if (foutput == NULL && get_sections () == 0)
Packit 032894
    {
Packit 032894
      if (verbose > 0)
Packit 032894
	printf ("Nothing to do.\n");
Packit 032894
      fnew = NULL;
Packit 032894
      return cleanup (0);
Packit 032894
    }
Packit 032894
Packit 032894
  if (adjust_names)
Packit 032894
    {
Packit 032894
      names = dwelf_strtab_init (true);
Packit 032894
      if (names == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Not enough memory for new strtab");
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
      scnstrents = xmalloc (shnum
Packit 032894
			    * sizeof (Dwelf_Strent *));
Packit 032894
      scnnames = xcalloc (shnum, sizeof (char *));
Packit 032894
    }
Packit 032894
Packit 032894
  /* Create a new (temporary) ELF file for the result.  */
Packit 032894
  if (foutput == NULL)
Packit 032894
    {
Packit 032894
      size_t fname_len = strlen (fname);
Packit 032894
      fnew = xmalloc (fname_len + sizeof (".XXXXXX"));
Packit 032894
      strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
Packit 032894
      fdnew = mkstemp (fnew);
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      fnew = xstrdup (foutput);
Packit 032894
      fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS);
Packit 032894
    }
Packit 032894
Packit 032894
  if (fdnew < 0)
Packit 032894
    {
Packit 032894
      error (0, errno, "Couldn't create output file %s", fnew);
Packit 032894
      /* Since we didn't create it we don't want to try to unlink it.  */
Packit 032894
      free (fnew);
Packit 032894
      fnew = NULL;
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
Packit 032894
  if (elfnew == NULL)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't open new ELF %s for writing: %s",
Packit 032894
	     fnew, elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Create the new ELF header and copy over all the data.  */
Packit 032894
  if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't create new ehdr: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  GElf_Ehdr newehdr;
Packit 032894
  if (gelf_getehdr (elfnew, &newehdr) == NULL)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't get new ehdr: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA];
Packit 032894
  newehdr.e_type = ehdr.e_type;
Packit 032894
  newehdr.e_machine = ehdr.e_machine;
Packit 032894
  newehdr.e_version = ehdr.e_version;
Packit 032894
  newehdr.e_entry = ehdr.e_entry;
Packit 032894
  newehdr.e_flags = ehdr.e_flags;
Packit 032894
Packit 032894
  if (gelf_update_ehdr (elfnew, &newehdr) == 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Copy over the phdrs as is.  */
Packit 032894
  if (phnum != 0)
Packit 032894
    {
Packit 032894
      if (gelf_newphdr (elfnew, phnum) == 0)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't create phdrs: %s", elf_errmsg (-1));
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      for (size_t cnt = 0; cnt < phnum; ++cnt)
Packit 032894
	{
Packit 032894
	  GElf_Phdr phdr_mem;
Packit 032894
	  GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
Packit 032894
	  if (phdr == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get phdr %zd: %s", cnt, elf_errmsg (-1));
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
	  if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't create phdr %zd: %s", cnt,
Packit 032894
		     elf_errmsg (-1));
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* Possibly add a 'z' and zero terminator.  */
Packit 032894
  if (maxnamelen > 0)
Packit 032894
    snamebuf = xmalloc (maxnamelen + 2);
Packit 032894
Packit 032894
  /* We might want to read/adjust the section header strings and
Packit 032894
     symbol tables.  If so, and those sections are to be compressed
Packit 032894
     then we will have to decompress it during the collection pass and
Packit 032894
     compress it again in the fixup pass.  Don't compress unnecessary
Packit 032894
     and keep track of whether or not to compress them (later in the
Packit 032894
     fixup pass).  Also record the original size, so we can report the
Packit 032894
     difference later when we do compress.  */
Packit 032894
  int shstrtab_compressed = T_UNSET;
Packit 032894
  size_t shstrtab_size = 0;
Packit 032894
  char *shstrtab_name = NULL;
Packit 032894
  char *shstrtab_newname = NULL;
Packit 032894
  int symtab_compressed = T_UNSET;
Packit 032894
  size_t symtab_size = 0;
Packit 032894
  char *symtab_name = NULL;
Packit 032894
  char *symtab_newname = NULL;
Packit 032894
Packit 032894
  /* Collection pass.  Copy over the sections, (de)compresses matching
Packit 032894
     sections, collect names of sections and symbol table if
Packit 032894
     necessary.  */
Packit 032894
  scn = NULL;
Packit 032894
  while ((scn = elf_nextscn (elf, scn)) != NULL)
Packit 032894
    {
Packit 032894
      size_t ndx = elf_ndxscn (scn);
Packit 032894
      assert (ndx < shnum);
Packit 032894
Packit 032894
      /* (de)compress if section matched.  */
Packit 032894
      char *sname = NULL;
Packit 032894
      char *newname = NULL;
Packit 032894
      if (get_section (ndx))
Packit 032894
	{
Packit 032894
	  GElf_Shdr shdr_mem;
Packit 032894
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
	  if (shdr == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get shdr for section %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  uint64_t size = shdr->sh_size;
Packit 032894
	  sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
Packit 032894
	  if (sname == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get name for section %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  /* strdup sname, the shdrstrndx section itself might be
Packit 032894
	     (de)compressed, invalidating the string pointers.  */
Packit 032894
	  sname = xstrdup (sname);
Packit 032894
Packit 032894
	  /* We might want to decompress (and rename), but not
Packit 032894
	     compress during this pass since we might need the section
Packit 032894
	     data in later passes.  Skip those sections for now and
Packit 032894
	     compress them in the fixup pass.  */
Packit 032894
	  bool skip_compress_section = (adjust_names
Packit 032894
					&& (ndx == shdrstrndx
Packit 032894
					    || ndx == symtabndx));
Packit 032894
Packit 032894
	  switch (type)
Packit 032894
	    {
Packit 032894
	    case T_DECOMPRESS:
Packit 032894
	      if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
Packit 032894
		{
Packit 032894
		  if (compress_section (scn, size, sname, NULL, ndx,
Packit 032894
					false, false, verbose > 0) < 0)
Packit 032894
		    return cleanup (-1);
Packit 032894
		}
Packit 032894
	      else if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
		{
Packit 032894
		  snamebuf[0] = '.';
Packit 032894
		  strcpy (&snamebuf[1], &sname[2]);
Packit 032894
		  newname = snamebuf;
Packit 032894
		  if (compress_section (scn, size, sname, newname, ndx,
Packit 032894
					true, false, verbose > 0) < 0)
Packit 032894
		    return cleanup (-1);
Packit 032894
		}
Packit 032894
	      else if (verbose > 0)
Packit 032894
		printf ("[%zd] %s already decompressed\n", ndx, sname);
Packit 032894
	      break;
Packit 032894
Packit 032894
	    case T_COMPRESS_GNU:
Packit 032894
	      if (strncmp (sname, ".debug", strlen (".debug")) == 0)
Packit 032894
		{
Packit 032894
		  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
Packit 032894
		    {
Packit 032894
		      /* First decompress to recompress GNU style.
Packit 032894
			 Don't report even when verbose.  */
Packit 032894
		      if (compress_section (scn, size, sname, NULL, ndx,
Packit 032894
					    false, false, false) < 0)
Packit 032894
			return cleanup (-1);
Packit 032894
		    }
Packit 032894
Packit 032894
		  snamebuf[0] = '.';
Packit 032894
		  snamebuf[1] = 'z';
Packit 032894
		  strcpy (&snamebuf[2], &sname[1]);
Packit 032894
		  newname = snamebuf;
Packit 032894
Packit 032894
		  if (skip_compress_section)
Packit 032894
		    {
Packit 032894
		      if (ndx == shdrstrndx)
Packit 032894
			{
Packit 032894
			  shstrtab_size = size;
Packit 032894
			  shstrtab_compressed = T_COMPRESS_GNU;
Packit 032894
			  shstrtab_name = xstrdup (sname);
Packit 032894
			  shstrtab_newname = xstrdup (newname);
Packit 032894
			}
Packit 032894
		      else
Packit 032894
			{
Packit 032894
			  symtab_size = size;
Packit 032894
			  symtab_compressed = T_COMPRESS_GNU;
Packit 032894
			  symtab_name = xstrdup (sname);
Packit 032894
			  symtab_newname = xstrdup (newname);
Packit 032894
			}
Packit 032894
		    }
Packit 032894
		  else
Packit 032894
		    {
Packit 032894
		      int res = compress_section (scn, size, sname, newname,
Packit 032894
						  ndx, true, true,
Packit 032894
						  verbose > 0);
Packit 032894
		      if (res < 0)
Packit 032894
			return cleanup (-1);
Packit 032894
Packit 032894
		      if (res == 0)
Packit 032894
			newname = NULL;
Packit 032894
		    }
Packit 032894
		}
Packit 032894
	      else if (verbose >= 0)
Packit 032894
		{
Packit 032894
		  if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
		    printf ("[%zd] %s unchanged, already GNU compressed",
Packit 032894
			    ndx, sname);
Packit 032894
		  else
Packit 032894
		    printf ("[%zd] %s cannot GNU compress section not starting with .debug\n",
Packit 032894
			    ndx, sname);
Packit 032894
		}
Packit 032894
	      break;
Packit 032894
Packit 032894
	    case T_COMPRESS_ZLIB:
Packit 032894
	      if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
Packit 032894
		{
Packit 032894
		  if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
		    {
Packit 032894
		      /* First decompress to recompress zlib style.
Packit 032894
			 Don't report even when verbose.  */
Packit 032894
		      if (compress_section (scn, size, sname, NULL, ndx,
Packit 032894
					    true, false, false) < 0)
Packit 032894
			return cleanup (-1);
Packit 032894
Packit 032894
		      snamebuf[0] = '.';
Packit 032894
		      strcpy (&snamebuf[1], &sname[2]);
Packit 032894
		      newname = snamebuf;
Packit 032894
		    }
Packit 032894
Packit 032894
		  if (skip_compress_section)
Packit 032894
		    {
Packit 032894
		      if (ndx == shdrstrndx)
Packit 032894
			{
Packit 032894
			  shstrtab_size = size;
Packit 032894
			  shstrtab_compressed = T_COMPRESS_ZLIB;
Packit 032894
			  shstrtab_name = xstrdup (sname);
Packit 032894
			  shstrtab_newname = (newname == NULL
Packit 032894
					      ? NULL : xstrdup (newname));
Packit 032894
			}
Packit 032894
		      else
Packit 032894
			{
Packit 032894
			  symtab_size = size;
Packit 032894
			  symtab_compressed = T_COMPRESS_ZLIB;
Packit 032894
			  symtab_name = xstrdup (sname);
Packit 032894
			  symtab_newname = (newname == NULL
Packit 032894
					    ? NULL : xstrdup (newname));
Packit 032894
			}
Packit 032894
		    }
Packit 032894
		  else if (compress_section (scn, size, sname, newname, ndx,
Packit 032894
					     false, true, verbose > 0) < 0)
Packit 032894
		    return cleanup (-1);
Packit 032894
		}
Packit 032894
	      else if (verbose > 0)
Packit 032894
		printf ("[%zd] %s already compressed\n", ndx, sname);
Packit 032894
	      break;
Packit 032894
	    }
Packit 032894
Packit 032894
	  free (sname);
Packit 032894
	}
Packit 032894
Packit 032894
      Elf_Scn *newscn = elf_newscn (elfnew);
Packit 032894
      if (newscn == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't create new section %zd", ndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
      if (shdr == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get shdr for section %zd", ndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      if (gelf_update_shdr (newscn, shdr) == 0)
Packit 032894
        {
Packit 032894
	  error (0, 0, "Couldn't update section header %zd", ndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      /* Except for the section header string table all data can be
Packit 032894
	 copied as is.  The section header string table will be
Packit 032894
	 created later and the symbol table might be fixed up if
Packit 032894
	 necessary.  */
Packit 032894
      if (! adjust_names || ndx != shdrstrndx)
Packit 032894
	{
Packit 032894
	  Elf_Data *data = elf_getdata (scn, NULL);
Packit 032894
	  if (data == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get data from section %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  Elf_Data *newdata = elf_newdata (newscn);
Packit 032894
	  if (newdata == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't create new data for section %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  *newdata = *data;
Packit 032894
	}
Packit 032894
Packit 032894
      /* Keep track of the (new) section names.  */
Packit 032894
      if (adjust_names)
Packit 032894
	{
Packit 032894
	  char *name;
Packit 032894
	  if (newname != NULL)
Packit 032894
	    name = newname;
Packit 032894
	  else
Packit 032894
	    {
Packit 032894
	      name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
Packit 032894
	      if (name == NULL)
Packit 032894
		{
Packit 032894
		  error (0, 0, "Couldn't get name for section [%zd]", ndx);
Packit 032894
		  return cleanup (-1);
Packit 032894
		}
Packit 032894
	    }
Packit 032894
Packit 032894
	  /* We need to keep a copy of the name till the strtab is done.  */
Packit 032894
	  name = scnnames[ndx] = xstrdup (name);
Packit 032894
	  if ((scnstrents[ndx] = dwelf_strtab_add (names, name)) == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "No memory to add section name string table");
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  /* If the symtab shares strings then add those too.  */
Packit 032894
	  if (ndx == symtabndx)
Packit 032894
	    {
Packit 032894
	      /* If the section is (still) compressed we'll need to
Packit 032894
		 uncompress it first to adjust the data, then
Packit 032894
		 recompress it in the fixup pass.  */
Packit 032894
	      if (symtab_compressed == T_UNSET)
Packit 032894
		{
Packit 032894
		  size_t size = shdr->sh_size;
Packit 032894
		  if ((shdr->sh_flags == SHF_COMPRESSED) != 0)
Packit 032894
		    {
Packit 032894
		      /* Don't report the (internal) uncompression.  */
Packit 032894
		      if (compress_section (newscn, size, sname, NULL, ndx,
Packit 032894
					    false, false, false) < 0)
Packit 032894
			return cleanup (-1);
Packit 032894
Packit 032894
		      symtab_size = size;
Packit 032894
		      symtab_compressed = T_COMPRESS_ZLIB;
Packit 032894
		    }
Packit 032894
		  else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
		    {
Packit 032894
		      /* Don't report the (internal) uncompression.  */
Packit 032894
		      if (compress_section (newscn, size, sname, NULL, ndx,
Packit 032894
					    true, false, false) < 0)
Packit 032894
			return cleanup (-1);
Packit 032894
Packit 032894
		      symtab_size = size;
Packit 032894
		      symtab_compressed = T_COMPRESS_GNU;
Packit 032894
		    }
Packit 032894
		}
Packit 032894
Packit 032894
	      Elf_Data *symd = elf_getdata (newscn, NULL);
Packit 032894
	      if (symd == NULL)
Packit 032894
		{
Packit 032894
		  error (0, 0, "Couldn't get symtab data for section [%zd] %s",
Packit 032894
			 ndx, name);
Packit 032894
		  return cleanup (-1);
Packit 032894
		}
Packit 032894
	      size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
Packit 032894
	      size_t syms = symd->d_size / elsize;
Packit 032894
	      symstrents = xmalloc (syms * sizeof (Dwelf_Strent *));
Packit 032894
	      for (size_t i = 0; i < syms; i++)
Packit 032894
		{
Packit 032894
		  GElf_Sym sym_mem;
Packit 032894
		  GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
Packit 032894
		  if (sym == NULL)
Packit 032894
		    {
Packit 032894
		      error (0, 0, "Couldn't get symbol %zd", i);
Packit 032894
		      return cleanup (-1);
Packit 032894
		    }
Packit 032894
		  if (sym->st_name != 0)
Packit 032894
		    {
Packit 032894
		      /* Note we take the name from the original ELF,
Packit 032894
			 since the new one will not have setup the
Packit 032894
			 strtab yet.  */
Packit 032894
		      const char *symname = elf_strptr (elf, shdrstrndx,
Packit 032894
							sym->st_name);
Packit 032894
		      if (symname == NULL)
Packit 032894
			{
Packit 032894
			  error (0, 0, "Couldn't get symbol %zd name", i);
Packit 032894
			  return cleanup (-1);
Packit 032894
			}
Packit 032894
		      symstrents[i] = dwelf_strtab_add (names, symname);
Packit 032894
		      if (symstrents[i] == NULL)
Packit 032894
			{
Packit 032894
			  error (0, 0, "No memory to add to symbol name");
Packit 032894
			  return cleanup (-1);
Packit 032894
			}
Packit 032894
		    }
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (adjust_names)
Packit 032894
    {
Packit 032894
      /* We got all needed strings, put the new data in the shstrtab.  */
Packit 032894
      if (verbose > 0)
Packit 032894
	printf ("[%zd] Updating section string table\n", shdrstrndx);
Packit 032894
Packit 032894
      scn = elf_getscn (elfnew, shdrstrndx);
Packit 032894
      if (scn == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get new section header string table [%zd]",
Packit 032894
		 shdrstrndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      Elf_Data *data = elf_newdata (scn);
Packit 032894
      if (data == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't create new section header string table data");
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
      if (dwelf_strtab_finalize (names, data) == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Not enough memory to create string table");
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
      namesbuf = data->d_buf;
Packit 032894
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
      if (shdr == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get shdr for new section strings %zd",
Packit 032894
		 shdrstrndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      /* Note that we also might have to compress and possibly set
Packit 032894
	 sh_off below */
Packit 032894
      shdr->sh_name = dwelf_strent_off (scnstrents[shdrstrndx]);
Packit 032894
      shdr->sh_type = SHT_STRTAB;
Packit 032894
      shdr->sh_flags = 0;
Packit 032894
      shdr->sh_addr = 0;
Packit 032894
      shdr->sh_offset = 0;
Packit 032894
      shdr->sh_size = data->d_size;
Packit 032894
      shdr->sh_link = SHN_UNDEF;
Packit 032894
      shdr->sh_info = SHN_UNDEF;
Packit 032894
      shdr->sh_addralign = 1;
Packit 032894
      shdr->sh_entsize = 0;
Packit 032894
Packit 032894
      if (gelf_update_shdr (scn, shdr) == 0)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't update new section strings [%zd]",
Packit 032894
		 shdrstrndx);
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      /* We might have to compress the data if the user asked us to,
Packit 032894
	 or if the section was already compressed (and the user didn't
Packit 032894
	 ask for decompression).  Note somewhat identical code for
Packit 032894
	 symtab below.  */
Packit 032894
      if (shstrtab_compressed == T_UNSET)
Packit 032894
	{
Packit 032894
	  /* The user didn't ask for compression, but maybe it was
Packit 032894
	     compressed in the original ELF file.  */
Packit 032894
	  Elf_Scn *oldscn = elf_getscn (elf, shdrstrndx);
Packit 032894
	  if (oldscn == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get section header string table [%zd]",
Packit 032894
		     shdrstrndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  shdr = gelf_getshdr (oldscn, &shdr_mem);
Packit 032894
	  if (shdr == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get shdr for old section strings [%zd]",
Packit 032894
		     shdrstrndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  shstrtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
Packit 032894
	  if (shstrtab_name == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get name for old section strings [%zd]",
Packit 032894
		     shdrstrndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  shstrtab_size = shdr->sh_size;
Packit 032894
	  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
Packit 032894
	    shstrtab_compressed = T_COMPRESS_ZLIB;
Packit 032894
	  else if (strncmp (shstrtab_name, ".zdebug", strlen (".zdebug")) == 0)
Packit 032894
	    shstrtab_compressed = T_COMPRESS_GNU;
Packit 032894
	}
Packit 032894
Packit 032894
      /* Should we (re)compress?  */
Packit 032894
      if (shstrtab_compressed != T_UNSET)
Packit 032894
	{
Packit 032894
	  if (compress_section (scn, shstrtab_size, shstrtab_name,
Packit 032894
				shstrtab_newname, shdrstrndx,
Packit 032894
				shstrtab_compressed == T_COMPRESS_GNU,
Packit 032894
				true, verbose > 0) < 0)
Packit 032894
	    return cleanup (-1);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* Make sure to re-get the new ehdr.  Adding phdrs and shdrs will
Packit 032894
     have changed it.  */
Packit 032894
  if (gelf_getehdr (elfnew, &newehdr) == NULL)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't re-get new ehdr: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Set this after the sections have been created, otherwise section
Packit 032894
     zero might not exist yet.  */
Packit 032894
  if (setshdrstrndx (elfnew, &newehdr, shdrstrndx) != 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't set new shdrstrndx: %s", elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Fixup pass.  Adjust string table references, symbol table and
Packit 032894
     layout if necessary.  */
Packit 032894
  if (layout || adjust_names)
Packit 032894
    {
Packit 032894
      scn = NULL;
Packit 032894
      while ((scn = elf_nextscn (elfnew, scn)) != NULL)
Packit 032894
	{
Packit 032894
	  size_t ndx = elf_ndxscn (scn);
Packit 032894
Packit 032894
	  GElf_Shdr shdr_mem;
Packit 032894
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
	  if (shdr == NULL)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't get shdr for section %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  /* Keep the offset of allocated sections so they are at the
Packit 032894
	     same place in the file. Add (possibly changed)
Packit 032894
	     unallocated ones after the allocated ones.  */
Packit 032894
	  if ((shdr->sh_flags & SHF_ALLOC) == 0)
Packit 032894
	    {
Packit 032894
	      /* Zero means one.  No alignment constraints.  */
Packit 032894
	      size_t addralign = shdr->sh_addralign ?: 1;
Packit 032894
	      last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
Packit 032894
	      shdr->sh_offset = last_offset;
Packit 032894
	      if (shdr->sh_type != SHT_NOBITS)
Packit 032894
		last_offset += shdr->sh_size;
Packit 032894
	    }
Packit 032894
Packit 032894
	  if (adjust_names)
Packit 032894
	    shdr->sh_name = dwelf_strent_off (scnstrents[ndx]);
Packit 032894
Packit 032894
	  if (gelf_update_shdr (scn, shdr) == 0)
Packit 032894
	    {
Packit 032894
	      error (0, 0, "Couldn't update section header %zd", ndx);
Packit 032894
	      return cleanup (-1);
Packit 032894
	    }
Packit 032894
Packit 032894
	  if (adjust_names && ndx == symtabndx)
Packit 032894
	    {
Packit 032894
	      if (verbose > 0)
Packit 032894
		printf ("[%zd] Updating symbol table\n", symtabndx);
Packit 032894
Packit 032894
	      Elf_Data *symd = elf_getdata (scn, NULL);
Packit 032894
	      if (symd == NULL)
Packit 032894
		{
Packit 032894
		  error (0, 0, "Couldn't get new symtab data section [%zd]",
Packit 032894
			 ndx);
Packit 032894
		  return cleanup (-1);
Packit 032894
		}
Packit 032894
	      size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
Packit 032894
	      size_t syms = symd->d_size / elsize;
Packit 032894
	      for (size_t i = 0; i < syms; i++)
Packit 032894
		{
Packit 032894
		  GElf_Sym sym_mem;
Packit 032894
		  GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
Packit 032894
		  if (sym == NULL)
Packit 032894
		    {
Packit 032894
		      error (0, 0, "2 Couldn't get symbol %zd", i);
Packit 032894
		      return cleanup (-1);
Packit 032894
		    }
Packit 032894
Packit 032894
		  if (sym->st_name != 0)
Packit 032894
		    {
Packit 032894
		      sym->st_name = dwelf_strent_off (symstrents[i]);
Packit 032894
Packit 032894
		      if (gelf_update_sym (symd, i, sym) == 0)
Packit 032894
			{
Packit 032894
			  error (0, 0, "Couldn't update symbol %zd", i);
Packit 032894
			  return cleanup (-1);
Packit 032894
			}
Packit 032894
		    }
Packit 032894
		}
Packit 032894
Packit 032894
	      /* We might have to compress the data if the user asked
Packit 032894
		 us to, or if the section was already compressed (and
Packit 032894
		 the user didn't ask for decompression).  Note
Packit 032894
		 somewhat identical code for shstrtab above.  */
Packit 032894
	      if (symtab_compressed == T_UNSET)
Packit 032894
		{
Packit 032894
		  /* The user didn't ask for compression, but maybe it was
Packit 032894
		     compressed in the original ELF file.  */
Packit 032894
		  Elf_Scn *oldscn = elf_getscn (elf, symtabndx);
Packit 032894
		  if (oldscn == NULL)
Packit 032894
		    {
Packit 032894
		      error (0, 0, "Couldn't get symbol table [%zd]",
Packit 032894
			     symtabndx);
Packit 032894
		      return cleanup (-1);
Packit 032894
		    }
Packit 032894
Packit 032894
		  shdr = gelf_getshdr (oldscn, &shdr_mem);
Packit 032894
		  if (shdr == NULL)
Packit 032894
		    {
Packit 032894
		      error (0, 0, "Couldn't get old symbol table shdr [%zd]",
Packit 032894
			     symtabndx);
Packit 032894
		      return cleanup (-1);
Packit 032894
		    }
Packit 032894
Packit 032894
		  symtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
Packit 032894
		  if (symtab_name == NULL)
Packit 032894
		    {
Packit 032894
		      error (0, 0, "Couldn't get old symbol table name [%zd]",
Packit 032894
			     symtabndx);
Packit 032894
		      return cleanup (-1);
Packit 032894
		    }
Packit 032894
Packit 032894
		  symtab_size = shdr->sh_size;
Packit 032894
		  if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
Packit 032894
		    symtab_compressed = T_COMPRESS_ZLIB;
Packit 032894
		  else if (strncmp (symtab_name, ".zdebug",
Packit 032894
				    strlen (".zdebug")) == 0)
Packit 032894
		    symtab_compressed = T_COMPRESS_GNU;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* Should we (re)compress?  */
Packit 032894
	      if (symtab_compressed != T_UNSET)
Packit 032894
		{
Packit 032894
		  if (compress_section (scn, symtab_size, symtab_name,
Packit 032894
					symtab_newname, symtabndx,
Packit 032894
					symtab_compressed == T_COMPRESS_GNU,
Packit 032894
					true, verbose > 0) < 0)
Packit 032894
		    return cleanup (-1);
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* If we have phdrs we want elf_update to layout the SHF_ALLOC
Packit 032894
     sections precisely as in the original file.  In that case we are
Packit 032894
     also responsible for setting phoff and shoff */
Packit 032894
  if (layout)
Packit 032894
    {
Packit 032894
      if (gelf_getehdr (elfnew, &newehdr) == NULL)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't get ehdr: %s", elf_errmsg (-1));
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
Packit 032894
      /* Position the shdrs after the last (unallocated) section.  */
Packit 032894
      const size_t offsize = gelf_fsize (elfnew, ELF_T_OFF, 1, EV_CURRENT);
Packit 032894
      newehdr.e_shoff = ((last_offset + offsize - 1)
Packit 032894
			 & ~((GElf_Off) (offsize - 1)));
Packit 032894
Packit 032894
      /* The phdrs go in the same place as in the original file.
Packit 032894
	 Normally right after the ELF header.  */
Packit 032894
      newehdr.e_phoff = ehdr.e_phoff;
Packit 032894
Packit 032894
      if (gelf_update_ehdr (elfnew, &newehdr) == 0)
Packit 032894
	{
Packit 032894
	  error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
Packit 032894
	  return cleanup (-1);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  elf_flagelf (elfnew, ELF_C_SET, ((layout ? ELF_F_LAYOUT : 0)
Packit 032894
				   | (permissive ? ELF_F_PERMISSIVE : 0)));
Packit 032894
Packit 032894
  if (elf_update (elfnew, ELF_C_WRITE) < 0)
Packit 032894
    {
Packit 032894
      error (0, 0, "Couldn't write %s: %s", fnew, elf_errmsg (-1));
Packit 032894
      return cleanup (-1);
Packit 032894
    }
Packit 032894
Packit 032894
  elf_end (elfnew);
Packit 032894
  elfnew = NULL;
Packit 032894
Packit 032894
  /* Try to match mode and owner.group of the original file.
Packit 032894
     Note to set suid bits we have to make sure the owner is setup
Packit 032894
     correctly first. Otherwise fchmod will drop them silently
Packit 032894
     or fchown may clear them.  */
Packit 032894
  if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
Packit 032894
    if (verbose >= 0)
Packit 032894
      error (0, errno, "Couldn't fchown %s", fnew);
Packit 032894
  if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
Packit 032894
    if (verbose >= 0)
Packit 032894
      error (0, errno, "Couldn't fchmod %s", fnew);
Packit 032894
Packit 032894
  /* Finally replace the old file with the new file.  */
Packit 032894
  if (foutput == NULL)
Packit 032894
    if (rename (fnew, fname) != 0)
Packit 032894
      {
Packit 032894
	error (0, errno, "Couldn't rename %s to %s", fnew, fname);
Packit 032894
	return cleanup (-1);
Packit 032894
      }
Packit 032894
Packit 032894
  /* We are finally done with the new file, don't unlink it now.  */
Packit 032894
  free (fnew);
Packit 032894
  fnew = NULL;
Packit 032894
Packit 032894
  return cleanup (0);
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
main (int argc, char **argv)
Packit 032894
{
Packit 032894
  const struct argp_option options[] =
Packit 032894
    {
Packit 032894
      { "output", 'o', "FILE", 0,
Packit 032894
	N_("Place (de)compressed output into FILE"),
Packit 032894
	0 },
Packit 032894
      { "type", 't', "TYPE", 0,
Packit 032894
	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)"),
Packit 032894
	0 },
Packit 032894
      { "name", 'n', "SECTION", 0,
Packit 032894
	N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"),
Packit 032894
	0 },
Packit 032894
      { "verbose", 'v', NULL, 0,
Packit 032894
	N_("Print a message for each section being (de)compressed"),
Packit 032894
	0 },
Packit 032894
      { "force", 'f', NULL, 0,
Packit 032894
	N_("Force compression of section even if it would become larger or update/rewrite the file even if no section would be (de)compressed"),
Packit 032894
	0 },
Packit 032894
      { "permissive", 'p', NULL, 0,
Packit 032894
	N_("Relax a few rules to handle slightly broken ELF files"),
Packit 032894
	0 },
Packit 032894
      { "quiet", 'q', NULL, 0,
Packit 032894
	N_("Be silent when a section cannot be compressed"),
Packit 032894
	0 },
Packit 032894
      { NULL, 0, NULL, 0, NULL, 0 }
Packit 032894
    };
Packit 032894
Packit 032894
  const struct argp argp =
Packit 032894
    {
Packit 032894
      .options = options,
Packit 032894
      .parser = parse_opt,
Packit 032894
      .args_doc = N_("FILE..."),
Packit 032894
      .doc = N_("Compress or decompress sections in an ELF file.")
Packit 032894
    };
Packit 032894
Packit 032894
  int remaining;
Packit 032894
  if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
Packit 032894
    return EXIT_FAILURE;
Packit 032894
Packit 032894
  /* Should already be handled by ARGP_KEY_NO_ARGS case above,
Packit 032894
     just sanity check.  */
Packit 032894
  if (remaining >= argc)
Packit 032894
    error (EXIT_FAILURE, 0, N_("No input file given"));
Packit 032894
Packit 032894
  /* Likewise for the ARGP_KEY_ARGS case above, an extra sanity check.  */
Packit 032894
  if (foutput != NULL && remaining + 1 < argc)
Packit 032894
    error (EXIT_FAILURE, 0,
Packit 032894
	   N_("Only one input file allowed together with '-o'"));
Packit 032894
Packit 032894
  elf_version (EV_CURRENT);
Packit 032894
Packit 032894
  /* Process all the remaining files.  */
Packit 032894
  int result = 0;
Packit 032894
  do
Packit 032894
    result |= process_file (argv[remaining]);
Packit 032894
  while (++remaining < argc);
Packit 032894
Packit 032894
  free_patterns ();
Packit 032894
  return result;
Packit 032894
}