Blame tests/elfstrmerge.c

Packit Service 97d2fb
/* Merge string sections.
Packit Service 97d2fb
   Copyright (C) 2015, 2016 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
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <errno.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <sys/types.h>
Packit Service 97d2fb
#include <sys/stat.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <stdio.h>
Packit Service 97d2fb
#include <inttypes.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
#include <gelf.h>
Packit Service 97d2fb
#include ELFUTILS_HEADER(dwelf)
Packit Service 97d2fb
#include "elf-knowledge.h"
Packit Service 97d2fb
Packit Service 97d2fb
/* The original ELF file.  */
Packit Service 97d2fb
static int fd = -1;
Packit Service 97d2fb
static Elf *elf = NULL;
Packit Service 97d2fb
static bool replace;
Packit Service 97d2fb
Packit Service 97d2fb
/* The new ELF file.  */
Packit Service 97d2fb
static char *fnew = NULL;
Packit Service 97d2fb
static int fdnew = -1;
Packit Service 97d2fb
static Elf *elfnew = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
/* The merged string table.  */
Packit Service 97d2fb
static Dwelf_Strtab *strings = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
/* Section name strents.  */
Packit Service 97d2fb
static Dwelf_Strent **scnstrents = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
/* Symbol name strends.  */
Packit Service 97d2fb
static Dwelf_Strent **symstrents = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
/* New ELF file buffers.  */
Packit Service 97d2fb
static Elf_Data newstrtabdata = { .d_buf = NULL };
Packit Service 97d2fb
static size_t newshnums = 0;
Packit Service 97d2fb
static void **newscnbufs = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
/* Release all files and resources allocated.  */
Packit Service 97d2fb
static void
Packit Service 97d2fb
release (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* The new string table.  */
Packit Service 97d2fb
  if (strings != NULL)
Packit Service 97d2fb
    dwelf_strtab_free (strings);
Packit Service 97d2fb
Packit Service 97d2fb
  free (scnstrents);
Packit Service 97d2fb
  free (symstrents);
Packit Service 97d2fb
  free (newstrtabdata.d_buf);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Any new data buffers allocated.  */
Packit Service 97d2fb
  for (size_t i = 0; i < newshnums; i++)
Packit Service 97d2fb
    free (newscnbufs[i]);
Packit Service 97d2fb
  free (newscnbufs);
Packit Service 97d2fb
Packit Service 97d2fb
  /* The new ELF file.  */
Packit Service 97d2fb
  if (fdnew != -1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      unlink (fnew);
Packit Service 97d2fb
      elf_end (elfnew);
Packit Service 97d2fb
      close (fdnew);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  // Don't release, we might need it in the error message.
Packit Service 97d2fb
  // if (replace)
Packit Service 97d2fb
  //   free (fnew);
Packit Service 97d2fb
Packit Service 97d2fb
  /* The original ELF file.  */
Packit Service 97d2fb
  elf_end (elf);
Packit Service 97d2fb
  close (fd);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* The various ways we can fail... Cleanup and show some message to
Packit Service 97d2fb
   the user.  The file name may be NULL.  */
Packit Service 97d2fb
static void __attribute__ ((noreturn))
Packit Service 97d2fb
fail (const char *msg, const char *fname)
Packit Service 97d2fb
{
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  if (fname != NULL)
Packit Service 97d2fb
    error (1, 0, "%s: %s", fname, msg);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    error (1, 0, "%s", msg);
Packit Service 97d2fb
  abort();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void __attribute__ ((noreturn))
Packit Service 97d2fb
fail_errno (const char *msg, const char *fname)
Packit Service 97d2fb
{
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  if (fname != NULL)
Packit Service 97d2fb
    error (1, errno, "%s: %s", fname, msg);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    error (1, errno, "%s", msg);
Packit Service 97d2fb
  abort();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void __attribute__ ((noreturn))
Packit Service 97d2fb
fail_idx (const char *msg, const char *fname, size_t idx)
Packit Service 97d2fb
{
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  if (fname != NULL)
Packit Service 97d2fb
    error (1, 0, "%s: %s %zd", fname, msg, idx);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    error (1, 0, "%s %zd", msg, idx);
Packit Service 97d2fb
  abort();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void __attribute__ ((noreturn))
Packit Service 97d2fb
fail_elf (const char *msg, const char *fname)
Packit Service 97d2fb
{
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  if (fname != NULL)
Packit Service 97d2fb
    error (1, 0, "%s: %s: %s", fname, msg, elf_errmsg (-1));
Packit Service 97d2fb
  else
Packit Service 97d2fb
    error (1, 0, "%s: %s", msg, elf_errmsg (-1));
Packit Service 97d2fb
  abort();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void __attribute__ ((noreturn))
Packit Service 97d2fb
fail_elf_idx (const char *msg, const char *fname, size_t idx)
Packit Service 97d2fb
{
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  if (fname != NULL)
Packit Service 97d2fb
    error (1, 0, "%s: %s %zd: %s", fname, msg, idx, elf_errmsg (-1));
Packit Service 97d2fb
  else
Packit Service 97d2fb
    error (1, 0, "%s %zd: %s", msg, idx, elf_errmsg (-1));
Packit Service 97d2fb
  abort();
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
main (int argc, char **argv)
Packit Service 97d2fb
{
Packit Service 97d2fb
  elf_version (EV_CURRENT);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Basic command line handling.  Need to replace the input file?  */
Packit Service 97d2fb
  if ((argc != 2 && argc != 4)
Packit Service 97d2fb
      || (argc == 4 && strcmp (argv[1], "-o") != 0))
Packit Service 97d2fb
    fail ("Usage argument: [-o <outputfile>] <inputfile>", NULL);
Packit Service 97d2fb
  replace = argc == 2;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Get the ELF file.  */
Packit Service 97d2fb
  const char *fname;
Packit Service 97d2fb
  if (replace)
Packit Service 97d2fb
    fname = argv[1];
Packit Service 97d2fb
  else
Packit Service 97d2fb
    fname = argv[3];
Packit Service 97d2fb
  fd = open (fname, O_RDONLY);
Packit Service 97d2fb
  if (fd < 0)
Packit Service 97d2fb
    fail_errno ("couldn't open", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  elf = elf_begin (fd, ELF_C_READ, NULL);
Packit Service 97d2fb
  if (elf == NULL)
Packit Service 97d2fb
    fail_elf ("couldn't open ELF file for reading", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Ehdr ehdr;
Packit Service 97d2fb
  if (gelf_getehdr (elf, &ehdr) == NULL)
Packit Service 97d2fb
    fail_elf ("Couldn't get ehdr", fname);
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
    fail_elf ("couldn't get section header string table index", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  Elf_Scn *shdrstrscn = elf_getscn (elf, shdrstrndx);
Packit Service 97d2fb
  GElf_Shdr shdrstrshdr_mem;
Packit Service 97d2fb
  GElf_Shdr *shdrstrshdr = gelf_getshdr (shdrstrscn, &shdrstrshdr_mem);
Packit Service 97d2fb
  if (shdrstrshdr == NULL)
Packit Service 97d2fb
    fail_elf ("couldn't get section header string table section", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  if ((shdrstrshdr->sh_flags & SHF_ALLOC) != 0)
Packit Service 97d2fb
    fail ("section header string table is an allocated section", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Get the symtab section.  */
Packit Service 97d2fb
  size_t symtabndx = 0;
Packit Service 97d2fb
  Elf_Scn *symtabscn = NULL;
Packit Service 97d2fb
  GElf_Shdr symtabshdr_mem;
Packit Service 97d2fb
  GElf_Shdr *symtabshdr = NULL;
Packit Service 97d2fb
  while ((symtabscn = elf_nextscn (elf, symtabscn)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      symtabshdr = gelf_getshdr (symtabscn, &symtabshdr_mem);
Packit Service 97d2fb
      if (symtabshdr == NULL)
Packit Service 97d2fb
	fail_elf ("couldn't get shdr", fname);
Packit Service 97d2fb
Packit Service 97d2fb
      if (symtabshdr->sh_type == SHT_SYMTAB)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Just pick the first, we don't expect more than one. */
Packit Service 97d2fb
	  symtabndx = elf_ndxscn (symtabscn);
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (symtabshdr == NULL)
Packit Service 97d2fb
    fail ("No symtab found", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  if ((symtabshdr->sh_flags & SHF_ALLOC) != 0)
Packit Service 97d2fb
    fail ("symtab is an allocated section", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Get the strtab of the symtab.  */
Packit Service 97d2fb
  size_t strtabndx = symtabshdr->sh_link;
Packit Service 97d2fb
  Elf_Scn *strtabscn = elf_getscn (elf, strtabndx);
Packit Service 97d2fb
  GElf_Shdr strtabshdr_mem;
Packit Service 97d2fb
  GElf_Shdr *strtabshdr = gelf_getshdr (strtabscn, &strtabshdr_mem);
Packit Service 97d2fb
  if (strtabshdr == NULL)
Packit Service 97d2fb
    fail_elf ("Couldn't get strtab section", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  if (shdrstrndx == strtabndx)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      error (0, 0, "%s: Nothing to do, shstrtab == strtab", fname);
Packit Service 97d2fb
      release ();
Packit Service 97d2fb
      return 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if ((strtabshdr->sh_flags & SHF_ALLOC) != 0)
Packit Service 97d2fb
    fail ("strtab is an allocated section", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  size_t phnum;
Packit Service 97d2fb
  if (elf_getphdrnum (elf, &phnum) != 0)
Packit Service 97d2fb
    fail_elf ("Couldn't get number of phdrs", fname);
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
  /* Create a new merged strings table that starts with the empty string.  */
Packit Service 97d2fb
  strings = dwelf_strtab_init (true);
Packit Service 97d2fb
  if (strings == NULL)
Packit Service 97d2fb
    fail ("No memory to create merged string table", NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Add the strings from all the sections.  */
Packit Service 97d2fb
  size_t shdrnum;
Packit Service 97d2fb
  if (elf_getshdrnum (elf, &shdrnum) != 0)
Packit Service 97d2fb
    fail_elf ("Couldn't get number of sections", fname);
Packit Service 97d2fb
  scnstrents = malloc (shdrnum * sizeof (Dwelf_Strent *));
Packit Service 97d2fb
  if (scnstrents == NULL)
Packit Service 97d2fb
    fail ("couldn't allocate memory for section strings", NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  /* While going through all sections keep track of last allocated
Packit Service 97d2fb
     offset if needed to keep the layout.  We'll put any unallocated
Packit Service 97d2fb
     sections behind those (strtab is unallocated and will change
Packit Service 97d2fb
     size).  */
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
  Elf_Scn *scn = NULL;
Packit Service 97d2fb
  while ((scn = elf_nextscn (elf, scn)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t scnnum = elf_ndxscn (scn);
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
	fail_elf_idx ("couldn't get shdr", fname, scnnum);
Packit Service 97d2fb
      /* Don't add the .shstrtab section itself, we'll not use it.  */
Packit Service 97d2fb
      if (shdr->sh_name != 0 && scnnum != shdrstrndx)
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
	    fail_elf_idx ("couldn't get section name", fname, scnnum);
Packit Service 97d2fb
	  if ((scnstrents[scnnum] = dwelf_strtab_add (strings, sname)) == NULL)
Packit Service 97d2fb
	    fail ("No memory to add to merged string table", NULL);
Packit Service 97d2fb
	}
Packit Service 97d2fb
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
  /* Add the strings from all the symbols.  */
Packit Service 97d2fb
  size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
Packit Service 97d2fb
  Elf_Data *symd = elf_getdata (symtabscn, NULL);
Packit Service 97d2fb
  if (symd == NULL)
Packit Service 97d2fb
    fail_elf ("couldn't get symtab data", fname);
Packit Service 97d2fb
  size_t symsnum = symd->d_size / elsize;
Packit Service 97d2fb
  symstrents = malloc (symsnum * sizeof (Dwelf_Strent *));
Packit Service 97d2fb
  if (symstrents == NULL)
Packit Service 97d2fb
    fail_errno ("Couldn't allocate memory for symbol strings", NULL);
Packit Service 97d2fb
  for (size_t i = 0; i < symsnum; 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
	fail_elf_idx ("Couldn't get symbol", fname, i);
Packit Service 97d2fb
      if (sym->st_name != 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  const char *sname = elf_strptr (elf, strtabndx, sym->st_name);
Packit Service 97d2fb
	  if (sname == NULL)
Packit Service 97d2fb
	    fail_elf_idx ("Couldn't get symbol name", fname, i);
Packit Service 97d2fb
	  if ((symstrents[i] = dwelf_strtab_add (strings, sname)) == NULL)
Packit Service 97d2fb
	    fail_idx ("No memory to add to merged string table symbol",
Packit Service 97d2fb
		      fname, i);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* We got all strings, build the new string table and store it as
Packit Service 97d2fb
     new strtab.  */
Packit Service 97d2fb
  dwelf_strtab_finalize (strings, &newstrtabdata);
Packit Service 97d2fb
Packit Service 97d2fb
  /* We share at least the empty string so the result is at least 1
Packit Service 97d2fb
     byte smaller.  */
Packit Service 97d2fb
  if (newstrtabdata.d_size >= shdrstrshdr->sh_size + strtabshdr->sh_size)
Packit Service 97d2fb
    fail ("Impossible, merged string table is larger", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  /* section index mapping and sanity checking.  */
Packit Service 97d2fb
  size_t newsecndx (size_t secndx, const char *what, size_t widx,
Packit Service 97d2fb
		    const char *member, size_t midx)
Packit Service 97d2fb
  {
Packit Service 97d2fb
    if (unlikely (secndx == 0 || secndx == shdrstrndx || secndx >= shdrnum))
Packit Service 97d2fb
      {
Packit Service 97d2fb
	/* Don't use fail... too specialized messages.  Call release
Packit Service 97d2fb
	   outselves and then error.  Ignores midx if widx is
Packit Service 97d2fb
	   zero.  */
Packit Service 97d2fb
	release ();
Packit Service 97d2fb
	if (widx == 0)
Packit Service 97d2fb
	  error (1, 0, "%s: bad section index %zd in %s for %s",
Packit Service 97d2fb
		 fname, secndx, what, member);
Packit Service 97d2fb
	else if (midx == 0)
Packit Service 97d2fb
	  error (1, 0, "%s: bad section index %zd in %s %zd for %s",
Packit Service 97d2fb
		 fname, secndx, what, widx, member);
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  error (1, 0, "%s: bad section index %zd in %s %zd for %s %zd",
Packit Service 97d2fb
		 fname, secndx, what, widx, member, midx);
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
    return secndx < shdrstrndx ? secndx : secndx - 1;
Packit Service 97d2fb
  }
Packit Service 97d2fb
Packit Service 97d2fb
  struct stat st;
Packit Service 97d2fb
  if (fstat (fd, &st) != 0)
Packit Service 97d2fb
    fail_errno("Couldn't fstat", fname);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create a new (temporary) ELF file for the result.  */
Packit Service 97d2fb
  if (replace)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t fname_len = strlen (fname);
Packit Service 97d2fb
      fnew = malloc (fname_len + sizeof (".XXXXXX"));
Packit Service 97d2fb
      if (fnew == NULL)
Packit Service 97d2fb
	fail_errno ("couldn't allocate memory for new file name", NULL);
Packit Service 97d2fb
      strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
Packit Service 97d2fb
Packit Service 97d2fb
      fdnew = mkstemp (fnew);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fnew = argv[2];
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
    fail_errno ("couldn't create output file", fnew);
Packit Service 97d2fb
Packit Service 97d2fb
  elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
Packit Service 97d2fb
  if (elfnew == NULL)
Packit Service 97d2fb
    fail_elf ("couldn't open new ELF for writing", fnew);
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
    fail_elf ("Couldn't create new ehdr", fnew);
Packit Service 97d2fb
  GElf_Ehdr newehdr;
Packit Service 97d2fb
  if (gelf_getehdr (elfnew, &newehdr) == NULL)
Packit Service 97d2fb
    fail_elf ("Couldn't get ehdr", fnew);
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
  /* The new file uses the new strtab as shstrtab.  */
Packit Service 97d2fb
  size_t newstrtabndx = newsecndx (strtabndx, "ehdr", 0, "e_shstrndx", 0);
Packit Service 97d2fb
  if (newstrtabndx < SHN_LORESERVE)
Packit Service 97d2fb
    newehdr.e_shstrndx = newstrtabndx;
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf_Scn *zscn = elf_getscn (elfnew, 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
	fail_elf ("Couldn't get section zero", fnew);
Packit Service 97d2fb
      zshdr->sh_link = strtabndx;
Packit Service 97d2fb
      if (gelf_update_shdr (zscn, zshdr) == 0)
Packit Service 97d2fb
	fail_elf ("Couldn't update section zero", fnew);
Packit Service 97d2fb
      newehdr.e_shstrndx = SHN_XINDEX;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (gelf_update_ehdr (elfnew, &newehdr) == 0)
Packit Service 97d2fb
    fail ("Couldn't update ehdr", fnew);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Copy the program headers if any.  */
Packit Service 97d2fb
  if (phnum != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (gelf_newphdr (elfnew, phnum) == 0)
Packit Service 97d2fb
	fail_elf ("Couldn't create phdrs", fnew);
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
	    fail_elf_idx ("Couldn't get phdr", fname, cnt);
Packit Service 97d2fb
	  if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
Packit Service 97d2fb
	    fail_elf_idx ("Couldn't create phdr", fnew, cnt);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  newshnums = shdrnum - 1;
Packit Service 97d2fb
  newscnbufs = calloc (sizeof (void *), newshnums);
Packit Service 97d2fb
  if (newscnbufs == NULL)
Packit Service 97d2fb
    fail_errno ("Couldn't allocate memory for new section buffers", NULL);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Copy the sections, except the shstrtab, fill the strtab with the
Packit Service 97d2fb
     combined strings and adjust section references.  */
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
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
	fail_elf_idx ("Couldn't get shdr", fname, ndx);
Packit Service 97d2fb
Packit Service 97d2fb
      /* Section zero is always created.  Skip the shtrtab.  */
Packit Service 97d2fb
      if (ndx == 0 || ndx == shdrstrndx)
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
Packit Service 97d2fb
      Elf_Scn *newscn = elf_newscn (elfnew);
Packit Service 97d2fb
      if (newscn == NULL)
Packit Service 97d2fb
	fail_elf_idx ("couldn't create new section", fnew, ndx);
Packit Service 97d2fb
Packit Service 97d2fb
      GElf_Shdr newshdr;
Packit Service 97d2fb
      newshdr.sh_name = (shdr->sh_name != 0
Packit Service 97d2fb
			 ? dwelf_strent_off (scnstrents[ndx]) : 0);
Packit Service 97d2fb
      newshdr.sh_type = shdr->sh_type;
Packit Service 97d2fb
      newshdr.sh_flags = shdr->sh_flags;
Packit Service 97d2fb
      newshdr.sh_addr = shdr->sh_addr;
Packit Service 97d2fb
      newshdr.sh_size = shdr->sh_size;
Packit Service 97d2fb
      if (shdr->sh_link != 0)
Packit Service 97d2fb
	newshdr.sh_link = newsecndx (shdr->sh_link, "shdr", ndx, "sh_link", 0);
Packit Service 97d2fb
      else
Packit Service 97d2fb
	newshdr.sh_link = 0;
Packit Service 97d2fb
      if (SH_INFO_LINK_P (shdr) && shdr->sh_info != 0)
Packit Service 97d2fb
	newshdr.sh_info = newsecndx (shdr->sh_info, "shdr", ndx, "sh_info", 0);
Packit Service 97d2fb
      else
Packit Service 97d2fb
	newshdr.sh_info = shdr->sh_info;
Packit Service 97d2fb
      newshdr.sh_entsize = shdr->sh_entsize;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Some sections need a new data buffer because they need to
Packit Service 97d2fb
	 manipulate the original data.  Allocate and check here, so we
Packit Service 97d2fb
	 have a list of all data buffers we might need to release when
Packit Service 97d2fb
	 done.  */
Packit Service 97d2fb
      void new_data_buf (Elf_Data *d)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	size_t s = d->d_size;
Packit Service 97d2fb
	if (s == 0)
Packit Service 97d2fb
	  fail_idx ("Expected data in section", fname, ndx);
Packit Service 97d2fb
	void *b = malloc (d->d_size);
Packit Service 97d2fb
	if (b == NULL)
Packit Service 97d2fb
	  fail_idx ("Couldn't allocated buffer for section", NULL, ndx);
Packit Service 97d2fb
	newscnbufs[newsecndx (ndx, "section", ndx, "d_buf", 0)] = d->d_buf = b;
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
      Elf_Data *newdata = elf_newdata (newscn);
Packit Service 97d2fb
      if (newdata == NULL)
Packit Service 97d2fb
	fail_elf_idx ("Couldn't create new data for section", fnew, ndx);
Packit Service 97d2fb
      if (ndx == strtabndx)
Packit Service 97d2fb
	*newdata = newstrtabdata;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* The symtab, dynsym, group and symtab_shndx sections
Packit Service 97d2fb
	     contain section indexes. Symbol tables (symtab and
Packit Service 97d2fb
	     dynsym) contain indexes to strings. Update both if
Packit Service 97d2fb
	     necessary.  */
Packit Service 97d2fb
	  Elf_Data *data = elf_getdata (scn, NULL);
Packit Service 97d2fb
	  if (data == NULL)
Packit Service 97d2fb
	    fail_elf_idx ("Couldn't get data from section", fname, ndx);
Packit Service 97d2fb
	  *newdata = *data;
Packit Service 97d2fb
	  switch (shdr->sh_type)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	    case SHT_SYMTAB:
Packit Service 97d2fb
	    case SHT_DYNSYM:
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		/* We need to update the section numbers of the
Packit Service 97d2fb
		   symbols and if this symbol table uses the strtab
Packit Service 97d2fb
		   section also the name indexes.  */
Packit Service 97d2fb
		const bool update_name = shdr->sh_link == strtabndx;
Packit Service 97d2fb
		if (update_name && ndx != symtabndx)
Packit Service 97d2fb
		  fail ("Only one symbol table using strtab expected", fname);
Packit Service 97d2fb
		new_data_buf (newdata);
Packit Service 97d2fb
		size_t syms = (data->d_size
Packit Service 97d2fb
			       / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT));
Packit Service 97d2fb
		for (size_t i = 0; i < syms; i++)
Packit Service 97d2fb
		  {
Packit Service 97d2fb
		    GElf_Sym sym;
Packit Service 97d2fb
		    if (gelf_getsym (data, i, &sym) == NULL)
Packit Service 97d2fb
		      fail_elf_idx ("Couldn't get symbol", fname, i);
Packit Service 97d2fb
Packit Service 97d2fb
		    if (GELF_ST_TYPE (sym.st_info) == STT_SECTION
Packit Service 97d2fb
			&& sym.st_shndx == shdrstrndx)
Packit Service 97d2fb
		      fprintf (stderr, "WARNING:"
Packit Service 97d2fb
			       " symbol table [%zd] contains section symbol %zd"
Packit Service 97d2fb
			       " for old shdrstrndx %zd\n", ndx, i, shdrstrndx);
Packit Service 97d2fb
		    else if (sym.st_shndx != SHN_UNDEF
Packit Service 97d2fb
			     && sym.st_shndx < SHN_LORESERVE)
Packit Service 97d2fb
		      sym.st_shndx = newsecndx (sym.st_shndx, "section", ndx,
Packit Service 97d2fb
						"symbol", i);
Packit Service 97d2fb
		    if (update_name && sym.st_name != 0)
Packit Service 97d2fb
		      sym.st_name = dwelf_strent_off (symstrents[i]);
Packit Service 97d2fb
Packit Service 97d2fb
		    /* We explicitly don't update the SHNDX table at
Packit Service 97d2fb
		       the same time, we do that below.  */
Packit Service 97d2fb
		    if (gelf_update_sym (newdata, i, &sym) == 0)
Packit Service 97d2fb
		      fail_elf_idx ("Couldn't update symbol", fnew, i);
Packit Service 97d2fb
		  }
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
Packit Service 97d2fb
	    case SHT_GROUP:
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		new_data_buf (newdata);
Packit Service 97d2fb
		/* A section group contains Elf32_Words. The first
Packit Service 97d2fb
		   word is a falg value, the rest of the words are
Packit Service 97d2fb
		   indexes of the sections belonging to the group.  */
Packit Service 97d2fb
		Elf32_Word *group = (Elf32_Word *) data->d_buf;
Packit Service 97d2fb
		Elf32_Word *newgroup = (Elf32_Word *) newdata->d_buf;
Packit Service 97d2fb
		size_t words = data->d_size / sizeof (Elf32_Word);
Packit Service 97d2fb
		if (words == 0)
Packit Service 97d2fb
		  fail_idx ("Not enough data in group section", fname, ndx);
Packit Service 97d2fb
		newgroup[0] = group[0];
Packit Service 97d2fb
		for (size_t i = 1; i < words; i++)
Packit Service 97d2fb
		  newgroup[i] = newsecndx (group[i], "section", ndx,
Packit Service 97d2fb
					   "group", i);
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
Packit Service 97d2fb
	    case SHT_SYMTAB_SHNDX:
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		new_data_buf (newdata);
Packit Service 97d2fb
		/* A SHNDX just contains an array of section indexes
Packit Service 97d2fb
		   for the corresponding symbol table.  The entry is
Packit Service 97d2fb
		   SHN_UNDEF unless the corresponding symbol is
Packit Service 97d2fb
		   SHN_XINDEX.  */
Packit Service 97d2fb
		Elf32_Word *shndx = (Elf32_Word *) data->d_buf;
Packit Service 97d2fb
		Elf32_Word *newshndx = (Elf32_Word *) newdata->d_buf;
Packit Service 97d2fb
		size_t words = data->d_size / sizeof (Elf32_Word);
Packit Service 97d2fb
		for (size_t i = 0; i < words; i++)
Packit Service 97d2fb
		  if (shndx[i] == SHN_UNDEF)
Packit Service 97d2fb
		    newshndx[i] = SHN_UNDEF;
Packit Service 97d2fb
		  else
Packit Service 97d2fb
		    newshndx[i] = newsecndx (shndx[i], "section", ndx,
Packit Service 97d2fb
					     "shndx", i);
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
Packit Service 97d2fb
	    case SHT_DYNAMIC:
Packit Service 97d2fb
	      FALLTHROUGH;
Packit Service 97d2fb
	      /* There are string indexes in here, but
Packit Service 97d2fb
		 they (should) point to a allocated string table,
Packit Service 97d2fb
		 which we don't alter.  */
Packit Service 97d2fb
	    default:
Packit Service 97d2fb
	      /* Nothing to do.  Section data doesn't contain section
Packit Service 97d2fb
		 or strtab indexes.  */
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* When we are responsible for the layout explicitly set
Packit Service 97d2fb
	 sh_addralign, sh_size and sh_offset.  Otherwise libelf will
Packit Service 97d2fb
	 calculate those from the Elf_Data.  */
Packit Service 97d2fb
      if (layout)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* We have just one Elf_Data.  */
Packit Service 97d2fb
	  newshdr.sh_size = newdata->d_size;
Packit Service 97d2fb
	  newshdr.sh_addralign = newdata->d_align;
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 unallocated ones after the
Packit Service 97d2fb
	     allocated ones.  */
Packit Service 97d2fb
	  if ((shdr->sh_flags & SHF_ALLOC) != 0)
Packit Service 97d2fb
	    newshdr.sh_offset = shdr->sh_offset;
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* Zero means one.  No alignment constraints.  */
Packit Service 97d2fb
	      size_t addralign = newshdr.sh_addralign ?: 1;
Packit Service 97d2fb
	      last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
Packit Service 97d2fb
	      newshdr.sh_offset = last_offset;
Packit Service 97d2fb
	      if (newshdr.sh_type != SHT_NOBITS)
Packit Service 97d2fb
		last_offset += newshdr.sh_size;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  newshdr.sh_addralign = 0;
Packit Service 97d2fb
	  newshdr.sh_size = 0;
Packit Service 97d2fb
	  newshdr.sh_offset = 0;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (gelf_update_shdr (newscn, &newshdr) == 0)
Packit Service 97d2fb
	fail_elf_idx ("Couldn't update section header", fnew, ndx);
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
      /* Position the shdrs after the last (unallocated) section.  */
Packit Service 97d2fb
      if (gelf_getehdr (elfnew, &newehdr) == NULL)
Packit Service 97d2fb
	fail_elf ("Couldn't get ehdr", fnew);
Packit Service 97d2fb
      const size_t offsize = gelf_fsize (elf, 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
	fail_elf ("Couldn't update ehdr", fnew);
Packit Service 97d2fb
Packit Service 97d2fb
      elf_flagelf (elfnew, ELF_C_SET, ELF_F_LAYOUT);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (elf_update (elfnew, ELF_C_WRITE) == -1)
Packit Service 97d2fb
    fail_elf ("Couldn't write ELF", fnew);
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
  if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
Packit Service 97d2fb
    error (0, errno, "Couldn't fchmod %s", fnew);
Packit Service 97d2fb
  if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
Packit Service 97d2fb
    error (0, errno, "Couldn't fchown %s", fnew);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Finally replace the old file with the new merged strings file.  */
Packit Service 97d2fb
  if (replace)
Packit Service 97d2fb
    if (rename (fnew, fname) != 0)
Packit Service 97d2fb
      fail_errno ("rename", fnew);
Packit Service 97d2fb
Packit Service 97d2fb
  /* We are finally done with the new file, don't unlink it now.  */
Packit Service 97d2fb
  close (fdnew);
Packit Service 97d2fb
  if (replace)
Packit Service 97d2fb
    free (fnew);
Packit Service 97d2fb
  fnew = NULL;
Packit Service 97d2fb
  fdnew = -1;
Packit Service 97d2fb
Packit Service 97d2fb
  release ();
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}