Blame src/elfcompress.c

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