Home | History | Annotate | Download | only in libelf
      1 /* Update data structures for changes and write them out.
      2    Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2015 Red Hat, Inc.
      3    This file is part of elfutils.
      4    Contributed by Ulrich Drepper <drepper (at) redhat.com>, 1999.
      5 
      6    This file is free software; you can redistribute it and/or modify
      7    it under the terms of either
      8 
      9      * the GNU Lesser General Public License as published by the Free
     10        Software Foundation; either version 3 of the License, or (at
     11        your option) any later version
     12 
     13    or
     14 
     15      * the GNU General Public License as published by the Free
     16        Software Foundation; either version 2 of the License, or (at
     17        your option) any later version
     18 
     19    or both in parallel, as here.
     20 
     21    elfutils is distributed in the hope that it will be useful, but
     22    WITHOUT ANY WARRANTY; without even the implied warranty of
     23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     24    General Public License for more details.
     25 
     26    You should have received copies of the GNU General Public License and
     27    the GNU Lesser General Public License along with this program.  If
     28    not, see <http://www.gnu.org/licenses/>.  */
     29 
     30 #ifdef HAVE_CONFIG_H
     31 # include <config.h>
     32 #endif
     33 
     34 #include <libelf.h>
     35 #include <fcntl.h>
     36 #include <unistd.h>
     37 #include <sys/mman.h>
     38 #include <sys/stat.h>
     39 
     40 #include "libelfP.h"
     41 
     42 
     43 static off_t
     44 write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
     45 {
     46   int class = elf->class;
     47 
     48   /* Check the mode bits now, before modification might change them.  */
     49   struct stat st;
     50   if (unlikely (fstat (elf->fildes, &st) != 0))
     51     {
     52       __libelf_seterrno (ELF_E_WRITE_ERROR);
     53       return -1;
     54     }
     55 
     56   /* Adjust the size in any case.  We do this even if we use `write'.
     57      We cannot do this if this file is in an archive.  We also don't
     58      do it *now* if we are shortening the file since this would
     59      prevent programs to use the data of the file in generating the
     60      new file.  We truncate the file later in this case.  */
     61   if (elf->parent == NULL
     62       && (elf->maximum_size == ~((size_t) 0)
     63 	  || (size_t) size > elf->maximum_size)
     64       && unlikely (ftruncate (elf->fildes, size) != 0))
     65     {
     66       __libelf_seterrno (ELF_E_WRITE_ERROR);
     67       return -1;
     68     }
     69 
     70   /* Try to map the file if this isn't done yet.  */
     71   if (elf->map_address == NULL && elf->cmd == ELF_C_WRITE_MMAP)
     72     {
     73       elf->map_address = mmap (NULL, size, PROT_READ | PROT_WRITE,
     74 			       MAP_SHARED, elf->fildes, 0);
     75       if (unlikely (elf->map_address == MAP_FAILED))
     76 	elf->map_address = NULL;
     77     }
     78 
     79   if (elf->map_address != NULL)
     80     {
     81       /* When using mmap we want to make sure the file content is
     82 	 really there. Only using ftruncate might mean the file is
     83 	 extended, but space isn't allocated yet.  This might cause a
     84 	 SIGBUS once we write into the mmapped space and the disk is
     85 	 full.  In glibc posix_fallocate is required to extend the
     86 	 file and allocate enough space even if the underlying
     87 	 filesystem would normally return EOPNOTSUPP.  But other
     88 	 implementations might not work as expected.  And the glibc
     89 	 fallback case might fail (with unexpected errnos) in some cases.
     90 	 So we only report an error when the call fails and errno is
     91 	 ENOSPC. Otherwise we ignore the error and treat it as just hint.  */
     92       if (elf->parent == NULL
     93 	  && (elf->maximum_size == ~((size_t) 0)
     94 	      || (size_t) size > elf->maximum_size)
     95 	  && unlikely (posix_fallocate (elf->fildes, 0, size) != 0))
     96 	if (errno == ENOSPC)
     97 	  {
     98 	    __libelf_seterrno (ELF_E_WRITE_ERROR);
     99 	    return -1;
    100 	  }
    101 
    102       /* The file is mmaped.  */
    103       if ((class == ELFCLASS32
    104 	   ? __elf32_updatemmap (elf, change_bo, shnum)
    105 	   : __elf64_updatemmap (elf, change_bo, shnum)) != 0)
    106 	/* Some problem while writing.  */
    107 	size = -1;
    108     }
    109   else
    110     {
    111       /* The file is not mmaped.  */
    112       if ((class == ELFCLASS32
    113 	   ? __elf32_updatefile (elf, change_bo, shnum)
    114 	   : __elf64_updatefile (elf, change_bo, shnum)) != 0)
    115 	/* Some problem while writing.  */
    116 	size = -1;
    117     }
    118 
    119   /* Reduce the file size if necessary.  */
    120   if (size != -1
    121       && elf->parent == NULL
    122       && elf->maximum_size != ~((size_t) 0)
    123       && (size_t) size < elf->maximum_size
    124       && unlikely (ftruncate (elf->fildes, size) != 0))
    125     {
    126       __libelf_seterrno (ELF_E_WRITE_ERROR);
    127       size = -1;
    128     }
    129 
    130   /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID
    131      mode bits.  So make sure we restore them afterwards if they were set.
    132      This is not atomic if someone else chmod's the file while we operate.  */
    133   if (size != -1
    134       && unlikely (st.st_mode & (S_ISUID | S_ISGID))
    135       /* fchmod ignores the bits we cannot change.  */
    136       && unlikely (fchmod (elf->fildes, st.st_mode) != 0))
    137     {
    138       __libelf_seterrno (ELF_E_WRITE_ERROR);
    139       size = -1;
    140     }
    141 
    142   if (size != -1 && elf->parent == NULL)
    143     elf->maximum_size = size;
    144 
    145   return size;
    146 }
    147 
    148 
    149 off_t
    150 elf_update (Elf *elf, Elf_Cmd cmd)
    151 {
    152   size_t shnum;
    153   off_t size;
    154   int change_bo = 0;
    155 
    156   if (cmd != ELF_C_NULL
    157       && cmd != ELF_C_WRITE
    158       && unlikely (cmd != ELF_C_WRITE_MMAP))
    159     {
    160       __libelf_seterrno (ELF_E_INVALID_CMD);
    161       return -1;
    162     }
    163 
    164   if (elf == NULL)
    165     return -1;
    166 
    167   if (elf->kind != ELF_K_ELF)
    168     {
    169       __libelf_seterrno (ELF_E_INVALID_HANDLE);
    170       return -1;
    171     }
    172 
    173   rwlock_wrlock (elf->lock);
    174 
    175   /* Make sure we have an ELF header.  */
    176   if (elf->state.elf.ehdr == NULL)
    177     {
    178       __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR);
    179       size = -1;
    180       goto out;
    181     }
    182 
    183   /* Determine the number of sections.  */
    184   shnum = (elf->state.elf.scns_last->cnt == 0
    185 	   ? 0
    186 	   : 1 + elf->state.elf.scns_last->data[elf->state.elf.scns_last->cnt - 1].index);
    187 
    188   /* Update the ELF descriptor.  First, place the program header.  It
    189      will come right after the ELF header.  The count the size of all
    190      sections and finally place the section table.  */
    191   size = (elf->class == ELFCLASS32
    192 	  ? __elf32_updatenull_wrlock (elf, &change_bo, shnum)
    193 	  : __elf64_updatenull_wrlock (elf, &change_bo, shnum));
    194   if (likely (size != -1)
    195       /* See whether we actually have to write out the data.  */
    196       && (cmd == ELF_C_WRITE || cmd == ELF_C_WRITE_MMAP))
    197     {
    198       if (elf->cmd != ELF_C_RDWR
    199 	  && elf->cmd != ELF_C_RDWR_MMAP
    200 	  && elf->cmd != ELF_C_WRITE
    201 	  && unlikely (elf->cmd != ELF_C_WRITE_MMAP))
    202 	{
    203 	  __libelf_seterrno (ELF_E_UPDATE_RO);
    204 	  size = -1;
    205 	}
    206       else if (unlikely (elf->fildes == -1))
    207 	{
    208 	  /* We closed the file already.  */
    209 	  __libelf_seterrno (ELF_E_FD_DISABLED);
    210 	  size = -1;
    211 	}
    212       else
    213 	size = write_file (elf, size, change_bo, shnum);
    214     }
    215 
    216  out:
    217   rwlock_unlock (elf->lock);
    218 
    219   return size;
    220 }
    221