Blame tests/elfstrmerge.c

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