Blame tests/elfcopy.c

Packit Service 97d2fb
/* Test program for copying a whole ELF file using libelf.
Packit Service 97d2fb
   Copyright (C) 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
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <errno.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <inttypes.h>
Packit Service 97d2fb
#include <stdbool.h>
Packit Service 97d2fb
#include <stdio.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
#include <sys/types.h>
Packit Service 97d2fb
#include <sys/stat.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include ELFUTILS_HEADER(elf)
Packit Service 97d2fb
#include <gelf.h>
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* shstrndx is special, might overflow into section zero header sh_link.  */
Packit Service 97d2fb
static int
Packit Service 97d2fb
setshstrndx (Elf *elf, size_t ndx)
Packit Service 97d2fb
{
Packit Service 97d2fb
  printf ("setshstrndx: %zd\n", ndx);
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Ehdr ehdr_mem;
Packit Service 97d2fb
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
Packit Service 97d2fb
  if (ehdr == NULL)
Packit Service 97d2fb
    return -1;
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
/* Copies all elements of an ELF file either using mmap or read.  */
Packit Service 97d2fb
static void
Packit Service 97d2fb
copy_elf (const char *in, const char *out, bool use_mmap, bool reverse_offs)
Packit Service 97d2fb
{
Packit Service 97d2fb
  printf ("\ncopy_elf: %s -> %s (%s,%s)\n", in, out,
Packit Service 97d2fb
	  use_mmap ? "mmap" : "read",
Packit Service 97d2fb
	  reverse_offs ? "reverse" : "same");
Packit Service 97d2fb
Packit Service 97d2fb
  /* Existing ELF file.  */
Packit Service 97d2fb
  int fda = open (in, O_RDONLY);
Packit Service 97d2fb
  if (fda < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fprintf (stderr, "Couldn't open file '%s': %s\n",
Packit Service 97d2fb
	       in, strerror (errno));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Elf *elfa = elf_begin (fda, use_mmap ? ELF_C_READ_MMAP : ELF_C_READ, NULL);
Packit Service 97d2fb
  if (elfa == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fprintf (stderr, "Couldn't open ELF file '%s': %s\n",
Packit Service 97d2fb
	       in, elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Open new file.  */
Packit Service 97d2fb
  int fdb = open (out, O_RDWR | O_CREAT | O_TRUNC, 0644);
Packit Service 97d2fb
  if (fdb < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fprintf (stderr, "Couldn't create file '%s': %s\n",
Packit Service 97d2fb
	       out, strerror (errno));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Elf *elfb = elf_begin (fdb, use_mmap ? ELF_C_WRITE_MMAP : ELF_C_WRITE, NULL);
Packit Service 97d2fb
  if (elfb == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fprintf (stderr, "Couldn't create ELF file '%s': %s\n",
Packit Service 97d2fb
	       out, elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  // Copy ELF header.
Packit Service 97d2fb
  GElf_Ehdr ehdr_mema;
Packit Service 97d2fb
  GElf_Ehdr *ehdra = gelf_getehdr (elfa, &ehdr_mema);
Packit Service 97d2fb
  if (ehdra == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot get ELF header: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  int class = gelf_getclass (elfa);
Packit Service 97d2fb
  // Create an ELF header.
Packit Service 97d2fb
  if (gelf_newehdr (elfb, class) == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot create ELF header: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* New elf header is an exact copy.  */
Packit Service 97d2fb
  GElf_Ehdr ehdr_memb;
Packit Service 97d2fb
  GElf_Ehdr *ehdrb = &ehdr_memb;
Packit Service 97d2fb
  *ehdrb = *ehdra;
Packit Service 97d2fb
  if (gelf_update_ehdr (elfb, ehdrb) == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot update ELF header: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* shstrndx is special.  (Technically phdrnum and shdrnum are also
Packit Service 97d2fb
     special, but they are handled by libelf.)  */
Packit Service 97d2fb
  size_t shstrndx;
Packit Service 97d2fb
  if (elf_getshdrstrndx (elfa, &shstrndx) < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot get shstrndx: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (setshstrndx (elfb, shstrndx) < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot set shstrndx: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* If there are phdrs, copy them over.  */
Packit Service 97d2fb
  size_t phnum;
Packit Service 97d2fb
  if (elf_getphdrnum (elfa, &phnum) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("cannot get phdrs: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (phnum > 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (gelf_newphdr (elfb, phnum) == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("cannot create phdrs: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	  exit (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 (elfa, cnt, &phdr_mem);
Packit Service 97d2fb
	  if (phdr == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("couldn't get phdr %zd: %s\n", cnt, elf_errmsg (-1));
Packit Service 97d2fb
	      exit (1);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  if (gelf_update_phdr (elfb, cnt, phdr) == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("couldn't update phdr %zd: %s\n", cnt, elf_errmsg (-1));
Packit Service 97d2fb
	      exit (1);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Off *offs = NULL;
Packit Service 97d2fb
  size_t shnum;
Packit Service 97d2fb
  if (reverse_offs)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (elf_getshdrnum (elfa, &shnum) < 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("couldn't get shdrnum: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	  exit (1);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      offs = (GElf_Off *) malloc (shnum * sizeof (GElf_Off));
Packit Service 97d2fb
      if (offs == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("couldn't allocate memory for offs\n");
Packit Service 97d2fb
	  exit (1);
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Copy all sections, headers and data.  */
Packit Service 97d2fb
  Elf_Scn *scn = NULL;
Packit Service 97d2fb
  size_t last_off = 0;
Packit Service 97d2fb
  GElf_Shdr last_shdr = { .sh_type = SHT_NULL };
Packit Service 97d2fb
  while ((scn = elf_nextscn (elfa, scn)) != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Get the header.  */
Packit Service 97d2fb
      GElf_Shdr shdr;
Packit Service 97d2fb
      if (gelf_getshdr (scn, &shdr) == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("couldn't get shdr: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	  exit (1);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (reverse_offs)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  offs[last_off] = shdr.sh_offset;
Packit Service 97d2fb
Packit Service 97d2fb
	  if (last_shdr.sh_type != SHT_NULL
Packit Service 97d2fb
	      && last_shdr.sh_addralign == shdr.sh_addralign
Packit Service 97d2fb
	      && shdr.sh_addralign == 1
Packit Service 97d2fb
	      && last_shdr.sh_type != SHT_NOBITS
Packit Service 97d2fb
	      && shdr.sh_type != SHT_NOBITS
Packit Service 97d2fb
	      && last_shdr.sh_offset + last_shdr.sh_size == shdr.sh_offset
Packit Service 97d2fb
	      && (phnum == 0
Packit Service 97d2fb
		  || ((shdr.sh_flags & SHF_ALLOC) == 0
Packit Service 97d2fb
		      && (last_shdr.sh_flags & SHF_ALLOC) == 0)))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("Swapping offsets of section %zd and %zd\n",
Packit Service 97d2fb
		      last_off, last_off + 1);
Packit Service 97d2fb
	      GElf_Word off = offs[last_off - 1];
Packit Service 97d2fb
	      offs[last_off - 1] = off + shdr.sh_size;
Packit Service 97d2fb
	      offs[last_off] = off;
Packit Service 97d2fb
	      last_shdr.sh_type = SHT_NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  else
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      last_shdr = shdr;
Packit Service 97d2fb
	      offs[last_off] = shdr.sh_offset;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  last_off++;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Create new section.  */
Packit Service 97d2fb
      Elf_Scn *new_scn = elf_newscn (elfb);
Packit Service 97d2fb
      if (new_scn == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("couldn't create new section: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	  exit (1);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (gelf_update_shdr (new_scn, &shdr) == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  printf ("couldn't update shdr: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	  exit (1);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Copy over section data.  */
Packit Service 97d2fb
      Elf_Data *data = NULL;
Packit Service 97d2fb
      while ((data = elf_getdata (scn, data)) != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf_Data *new_data = elf_newdata (new_scn);
Packit Service 97d2fb
	  if (new_data == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("couldn't create new section data: %s\n",
Packit Service 97d2fb
		      elf_errmsg (-1));
Packit Service 97d2fb
	      exit (1);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  *new_data = *data;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (reverse_offs)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      last_off = 0;
Packit Service 97d2fb
      scn = NULL;
Packit Service 97d2fb
      while ((scn = elf_nextscn (elfb, scn)) != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  GElf_Shdr shdr;
Packit Service 97d2fb
	  if (gelf_getshdr (scn, &shdr) == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("couldn't get shdr for updating: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	      exit (1);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  shdr.sh_offset = offs[last_off++];
Packit Service 97d2fb
Packit Service 97d2fb
	  if (gelf_update_shdr (scn, &shdr) == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      printf ("couldn't update shdr sh_off: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
	      exit (1);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      free (offs);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Write everything to disk.  If there are any phdrs, or we want to
Packit Service 97d2fb
     update the offsets, then we want the exact same layout.  Do we
Packit Service 97d2fb
     want ELF_F_PERMISSIVE?  */
Packit Service 97d2fb
  if (phnum > 0 || reverse_offs)
Packit Service 97d2fb
    elf_flagelf (elfb, ELF_C_SET, ELF_F_LAYOUT);
Packit Service 97d2fb
  if (elf_update (elfb, ELF_C_WRITE) < 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("failure in elf_update: %s\n", elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (elf_end (elfa) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("couldn't cleanup elf '%s': %s\n", in, elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (close (fda) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("couldn't close '%s': %s\n", in, strerror (errno));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (elf_end (elfb) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("couldn't cleanup elf '%s': %s\n", out, elf_errmsg (-1));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (close (fdb) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      printf ("couldn't close '%s': %s\n", out, strerror (errno));
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
main (int argc, const char *argv[])
Packit Service 97d2fb
{
Packit Service 97d2fb
  elf_version (EV_CURRENT);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Takes the given file, and create a new identical one.  */
Packit Service 97d2fb
  if (argc < 3 || argc > 5)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      fprintf (stderr, "elfcopy [--mmap] [--reverse-offs] in.elf out.elf\n");
Packit Service 97d2fb
      exit (1);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  int argn = 1;
Packit Service 97d2fb
  bool use_mmap = false;
Packit Service 97d2fb
  if (strcmp (argv[argn], "--mmap") == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      use_mmap = true;
Packit Service 97d2fb
      argn++;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  bool reverse_offs = false;
Packit Service 97d2fb
  if (strcmp (argv[argn], "--reverse-offs") == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      reverse_offs = true;
Packit Service 97d2fb
      argn++;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  const char *in = argv[argn++];
Packit Service 97d2fb
  const char *out = argv[argn];
Packit Service 97d2fb
  copy_elf (in, out, use_mmap, reverse_offs);
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}