Blame libelf/elf_update.c

Packit 032894
/* Update data structures for changes and write them out.
Packit 032894
   Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2015 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Contributed by Ulrich Drepper <drepper@redhat.com>, 1999.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
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 GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <libelf.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <unistd.h>
Packit 032894
#include <sys/mman.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
Packit 032894
#include "libelfP.h"
Packit 032894
Packit 032894
Packit 032894
static int64_t
Packit 032894
write_file (Elf *elf, int64_t size, int change_bo, size_t shnum)
Packit 032894
{
Packit 032894
  int class = elf->class;
Packit 032894
Packit 032894
  /* Check the mode bits now, before modification might change them.  */
Packit 032894
  struct stat st;
Packit 032894
  if (unlikely (fstat (elf->fildes, &st) != 0))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Adjust the size in any case.  We do this even if we use `write'.
Packit 032894
     We cannot do this if this file is in an archive.  We also don't
Packit 032894
     do it *now* if we are shortening the file since this would
Packit 032894
     prevent programs to use the data of the file in generating the
Packit 032894
     new file.  We truncate the file later in this case.  */
Packit 032894
  if (elf->parent == NULL
Packit 032894
      && (elf->maximum_size == ~((size_t) 0)
Packit 032894
	  || (size_t) size > elf->maximum_size)
Packit 032894
      && unlikely (ftruncate (elf->fildes, size) != 0))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Try to map the file if this isn't done yet.  */
Packit 032894
  if (elf->map_address == NULL && elf->cmd == ELF_C_WRITE_MMAP)
Packit 032894
    {
Packit 032894
      elf->map_address = mmap (NULL, size, PROT_READ | PROT_WRITE,
Packit 032894
			       MAP_SHARED, elf->fildes, 0);
Packit 032894
      if (unlikely (elf->map_address == MAP_FAILED))
Packit 032894
	elf->map_address = NULL;
Packit 032894
      else
Packit 032894
	elf->flags |= ELF_F_MMAPPED;
Packit 032894
    }
Packit 032894
Packit 032894
  if (elf->map_address != NULL)
Packit 032894
    {
Packit 032894
      /* When using mmap we want to make sure the file content is
Packit 032894
	 really there. Only using ftruncate might mean the file is
Packit 032894
	 extended, but space isn't allocated yet.  This might cause a
Packit 032894
	 SIGBUS once we write into the mmapped space and the disk is
Packit 032894
	 full.  In glibc posix_fallocate is required to extend the
Packit 032894
	 file and allocate enough space even if the underlying
Packit 032894
	 filesystem would normally return EOPNOTSUPP.  But other
Packit 032894
	 implementations might not work as expected.  And the glibc
Packit 032894
	 fallback case might fail (with unexpected errnos) in some cases.
Packit 032894
	 So we only report an error when the call fails and errno is
Packit 032894
	 ENOSPC. Otherwise we ignore the error and treat it as just hint.  */
Packit 032894
      if (elf->parent == NULL
Packit 032894
	  && (elf->maximum_size == ~((size_t) 0)
Packit 032894
	      || (size_t) size > elf->maximum_size))
Packit 032894
	{
Packit 032894
	  if (unlikely (posix_fallocate (elf->fildes, 0, size) != 0))
Packit 032894
	    if (errno == ENOSPC)
Packit 032894
	      {
Packit 032894
		__libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
		return -1;
Packit 032894
	      }
Packit 032894
Packit 032894
	  /* Extend the mmap address if needed.  */
Packit 032894
	  if (elf->cmd == ELF_C_RDWR_MMAP
Packit 032894
	      && (size_t) size > elf->maximum_size)
Packit 032894
	    {
Packit 032894
	      if (mremap (elf->map_address, elf->maximum_size,
Packit 032894
			  size, 0) == MAP_FAILED)
Packit 032894
		{
Packit 032894
		  __libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
		  return -1;
Packit 032894
		}
Packit 032894
	      elf->maximum_size = size;
Packit 032894
	    }
Packit 032894
Packit 032894
	}
Packit 032894
Packit 032894
      /* The file is mmaped.  */
Packit 032894
      if ((class == ELFCLASS32
Packit 032894
	   ? __elf32_updatemmap (elf, change_bo, shnum)
Packit 032894
	   : __elf64_updatemmap (elf, change_bo, shnum)) != 0)
Packit 032894
	/* Some problem while writing.  */
Packit 032894
	size = -1;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      /* The file is not mmaped.  */
Packit 032894
      if ((class == ELFCLASS32
Packit 032894
	   ? __elf32_updatefile (elf, change_bo, shnum)
Packit 032894
	   : __elf64_updatefile (elf, change_bo, shnum)) != 0)
Packit 032894
	/* Some problem while writing.  */
Packit 032894
	size = -1;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Reduce the file size if necessary.  */
Packit 032894
  if (size != -1
Packit 032894
      && elf->parent == NULL
Packit 032894
      && elf->maximum_size != ~((size_t) 0)
Packit 032894
      && (size_t) size < elf->maximum_size
Packit 032894
      && unlikely (ftruncate (elf->fildes, size) != 0))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
      size = -1;
Packit 032894
    }
Packit 032894
Packit 032894
  /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID
Packit 032894
     mode bits.  So make sure we restore them afterwards if they were set.
Packit 032894
     This is not atomic if someone else chmod's the file while we operate.  */
Packit 032894
  if (size != -1
Packit 032894
      && unlikely (st.st_mode & (S_ISUID | S_ISGID))
Packit 032894
      /* fchmod ignores the bits we cannot change.  */
Packit 032894
      && unlikely (fchmod (elf->fildes, st.st_mode) != 0))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_WRITE_ERROR);
Packit 032894
      size = -1;
Packit 032894
    }
Packit 032894
Packit 032894
  if (size != -1 && elf->parent == NULL)
Packit 032894
    elf->maximum_size = size;
Packit 032894
Packit 032894
  return size;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
int64_t
Packit 032894
elf_update (Elf *elf, Elf_Cmd cmd)
Packit 032894
{
Packit 032894
  size_t shnum;
Packit 032894
  int64_t size;
Packit 032894
  int change_bo = 0;
Packit 032894
Packit 032894
  if (cmd != ELF_C_NULL
Packit 032894
      && cmd != ELF_C_WRITE
Packit 032894
      && unlikely (cmd != ELF_C_WRITE_MMAP))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_INVALID_CMD);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  if (elf == NULL)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  if (elf->kind != ELF_K_ELF)
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_INVALID_HANDLE);
Packit 032894
      return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  rwlock_wrlock (elf->lock);
Packit 032894
Packit 032894
  /* Make sure we have an ELF header.  */
Packit 032894
  if (elf->state.elf.ehdr == NULL)
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR);
Packit 032894
      size = -1;
Packit 032894
      goto out;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Determine the number of sections.  */
Packit 032894
  shnum = (elf->state.elf.scns_last->cnt == 0
Packit 032894
	   ? 0
Packit 032894
	   : 1 + elf->state.elf.scns_last->data[elf->state.elf.scns_last->cnt - 1].index);
Packit 032894
Packit 032894
  /* Update the ELF descriptor.  First, place the program header.  It
Packit 032894
     will come right after the ELF header.  The count the size of all
Packit 032894
     sections and finally place the section table.  */
Packit 032894
  size = (elf->class == ELFCLASS32
Packit 032894
	  ? __elf32_updatenull_wrlock (elf, &change_bo, shnum)
Packit 032894
	  : __elf64_updatenull_wrlock (elf, &change_bo, shnum));
Packit 032894
  if (likely (size != -1)
Packit 032894
      /* See whether we actually have to write out the data.  */
Packit 032894
      && (cmd == ELF_C_WRITE || cmd == ELF_C_WRITE_MMAP))
Packit 032894
    {
Packit 032894
      if (elf->cmd != ELF_C_RDWR
Packit 032894
	  && elf->cmd != ELF_C_RDWR_MMAP
Packit 032894
	  && elf->cmd != ELF_C_WRITE
Packit 032894
	  && unlikely (elf->cmd != ELF_C_WRITE_MMAP))
Packit 032894
	{
Packit 032894
	  __libelf_seterrno (ELF_E_UPDATE_RO);
Packit 032894
	  size = -1;
Packit 032894
	}
Packit 032894
      else if (unlikely (elf->fildes == -1))
Packit 032894
	{
Packit 032894
	  /* We closed the file already.  */
Packit 032894
	  __libelf_seterrno (ELF_E_FD_DISABLED);
Packit 032894
	  size = -1;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	size = write_file (elf, size, change_bo, shnum);
Packit 032894
    }
Packit 032894
Packit 032894
 out:
Packit 032894
  rwlock_unlock (elf->lock);
Packit 032894
Packit 032894
  return size;
Packit 032894
}