Blame libelf/elf_update.c

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