Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libelf / elf_update.c
1 /* Update data structures for changes and write them out.
2    Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006 Red Hat, Inc.
3    This file is part of elfutils.
4    Contributed by Ulrich Drepper <drepper@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 <unistd.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38
39 #include "libelfP.h"
40
41
42 static off_t
43 write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
44 {
45   int class = elf->class;
46
47   /* Check the mode bits now, before modification might change them.  */
48   struct stat st;
49   if (unlikely (fstat (elf->fildes, &st) != 0))
50     {
51       __libelf_seterrno (ELF_E_WRITE_ERROR);
52       return -1;
53     }
54
55   /* Adjust the size in any case.  We do this even if we use `write'.
56      We cannot do this if this file is in an archive.  We also don't
57      do it *now* if we are shortening the file since this would
58      prevent programs to use the data of the file in generating the
59      new file.  We truncate the file later in this case.  */
60   if (elf->parent == NULL
61       && (elf->maximum_size == ~((size_t) 0)
62           || (size_t) size > elf->maximum_size)
63       && unlikely (ftruncate (elf->fildes, size) != 0))
64     {
65       __libelf_seterrno (ELF_E_WRITE_ERROR);
66       return -1;
67     }
68
69   /* Try to map the file if this isn't done yet.  */
70   if (elf->map_address == NULL && elf->cmd == ELF_C_WRITE_MMAP)
71     {
72 #if _MUDFLAP
73       /* Mudflap doesn't grok that our mmap'd data is ok.  */
74 #else
75       elf->map_address = mmap (NULL, size, PROT_READ | PROT_WRITE,
76                                MAP_SHARED, elf->fildes, 0);
77       if (unlikely (elf->map_address == MAP_FAILED))
78         elf->map_address = NULL;
79 #endif
80     }
81
82   if (elf->map_address != NULL)
83     {
84       /* The file is mmaped.  */
85       if ((class == ELFCLASS32
86            ? __elf32_updatemmap (elf, change_bo, shnum)
87            : __elf64_updatemmap (elf, change_bo, shnum)) != 0)
88         /* Some problem while writing.  */
89         size = -1;
90     }
91   else
92     {
93       /* The file is not mmaped.  */
94       if ((class == ELFCLASS32
95            ? __elf32_updatefile (elf, change_bo, shnum)
96            : __elf64_updatefile (elf, change_bo, shnum)) != 0)
97         /* Some problem while writing.  */
98         size = -1;
99     }
100
101   if (size != -1
102       && elf->parent == NULL
103       && elf->maximum_size != ~((size_t) 0)
104       && (size_t) size < elf->maximum_size
105       && unlikely (ftruncate (elf->fildes, size) != 0))
106     {
107       __libelf_seterrno (ELF_E_WRITE_ERROR);
108       size = -1;
109     }
110
111   /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID
112      mode bits.  So make sure we restore them afterwards if they were set.
113      This is not atomic if someone else chmod's the file while we operate.  */
114   if (size != -1
115       && unlikely (st.st_mode & (S_ISUID | S_ISGID))
116       /* fchmod ignores the bits we cannot change.  */
117       && unlikely (fchmod (elf->fildes, st.st_mode) != 0))
118     {
119       __libelf_seterrno (ELF_E_WRITE_ERROR);
120       size = -1;
121     }
122
123   if (size != -1 && elf->parent == NULL)
124     elf->maximum_size = size;
125
126   return size;
127 }
128
129
130 off_t
131 elf_update (elf, cmd)
132      Elf *elf;
133      Elf_Cmd cmd;
134 {
135   size_t shnum;
136   off_t size;
137   int change_bo = 0;
138
139   if (cmd != ELF_C_NULL
140       && cmd != ELF_C_WRITE
141       && unlikely (cmd != ELF_C_WRITE_MMAP))
142     {
143       __libelf_seterrno (ELF_E_INVALID_CMD);
144       return -1;
145     }
146
147   if (elf == NULL)
148     return -1;
149
150   if (elf->kind != ELF_K_ELF)
151     {
152       __libelf_seterrno (ELF_E_INVALID_HANDLE);
153       return -1;
154     }
155
156   rwlock_wrlock (elf->lock);
157
158   /* Make sure we have an ELF header.  */
159   if (elf->state.elf.ehdr == NULL)
160     {
161       __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR);
162       size = -1;
163       goto out;
164     }
165
166   /* Determine the number of sections.  */
167   shnum = (elf->state.elf.scns_last->cnt == 0
168            ? 0
169            : 1 + elf->state.elf.scns_last->data[elf->state.elf.scns_last->cnt - 1].index);
170
171   /* Update the ELF descriptor.  First, place the program header.  It
172      will come right after the ELF header.  The count the size of all
173      sections and finally place the section table.  */
174   size = (elf->class == ELFCLASS32
175           ? __elf32_updatenull_wrlock (elf, &change_bo, shnum)
176           : __elf64_updatenull_wrlock (elf, &change_bo, shnum));
177   if (likely (size != -1)
178       /* See whether we actually have to write out the data.  */
179       && (cmd == ELF_C_WRITE || cmd == ELF_C_WRITE_MMAP))
180     {
181       if (elf->cmd != ELF_C_RDWR
182           && elf->cmd != ELF_C_RDWR_MMAP
183           && elf->cmd != ELF_C_WRITE
184           && unlikely (elf->cmd != ELF_C_WRITE_MMAP))
185         {
186           __libelf_seterrno (ELF_E_UPDATE_RO);
187           size = -1;
188         }
189       else if (unlikely (elf->fildes == -1))
190         {
191           /* We closed the file already.  */
192           __libelf_seterrno (ELF_E_FD_DISABLED);
193           size = -1;
194         }
195       else
196         size = write_file (elf, size, change_bo, shnum);
197     }
198
199  out:
200   rwlock_unlock (elf->lock);
201
202   return size;
203 }