]>
Commit | Line | Data |
---|---|---|
b08d5a8f | 1 | /* Update data structures for changes and write them out. |
77482c4b | 2 | Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2015 Red Hat, Inc. |
de2ed97f | 3 | This file is part of elfutils. |
b08d5a8f UD |
4 | Contributed by Ulrich Drepper <drepper@redhat.com>, 1999. |
5 | ||
de2ed97f MW |
6 | This file is free software; you can redistribute it and/or modify |
7 | it under the terms of either | |
b08d5a8f | 8 | |
de2ed97f MW |
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 | |
361df7da UD |
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. | |
b08d5a8f | 25 | |
de2ed97f MW |
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/>. */ | |
b08d5a8f UD |
29 | |
30 | #ifdef HAVE_CONFIG_H | |
31 | # include <config.h> | |
32 | #endif | |
33 | ||
34 | #include <libelf.h> | |
77482c4b | 35 | #include <fcntl.h> |
89757447 | 36 | #include <sys/stat.h> |
b08d5a8f UD |
37 | |
38 | #include "libelfP.h" | |
39 | ||
40 | ||
a46200f6 MW |
41 | static int64_t |
42 | write_file (Elf *elf, int64_t size, int change_bo, size_t shnum) | |
b08d5a8f UD |
43 | { |
44 | int class = elf->class; | |
45 | ||
89757447 RM |
46 | /* Check the mode bits now, before modification might change them. */ |
47 | struct stat st; | |
48 | if (unlikely (fstat (elf->fildes, &st) != 0)) | |
49 | { | |
50 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
51 | return -1; | |
52 | } | |
53 | ||
b08d5a8f UD |
54 | /* Adjust the size in any case. We do this even if we use `write'. |
55 | We cannot do this if this file is in an archive. We also don't | |
56 | do it *now* if we are shortening the file since this would | |
57 | prevent programs to use the data of the file in generating the | |
ae1e85ea | 58 | new file. We truncate the file later in this case. */ |
b08d5a8f UD |
59 | if (elf->parent == NULL |
60 | && (elf->maximum_size == ~((size_t) 0) | |
61 | || (size_t) size > elf->maximum_size) | |
ae1e85ea | 62 | && unlikely (ftruncate (elf->fildes, size) != 0)) |
b08d5a8f UD |
63 | { |
64 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
65 | return -1; | |
66 | } | |
67 | ||
68 | /* Try to map the file if this isn't done yet. */ | |
69 | if (elf->map_address == NULL && elf->cmd == ELF_C_WRITE_MMAP) | |
70 | { | |
b08d5a8f UD |
71 | elf->map_address = mmap (NULL, size, PROT_READ | PROT_WRITE, |
72 | MAP_SHARED, elf->fildes, 0); | |
73 | if (unlikely (elf->map_address == MAP_FAILED)) | |
74 | elf->map_address = NULL; | |
a6c6fb4a UH |
75 | else |
76 | elf->flags |= ELF_F_MMAPPED; | |
b08d5a8f UD |
77 | } |
78 | ||
79 | if (elf->map_address != NULL) | |
80 | { | |
ae1e85ea MW |
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) | |
fb0457f4 MW |
94 | || (size_t) size > elf->maximum_size)) |
95 | { | |
96 | if (unlikely (posix_fallocate (elf->fildes, 0, size) != 0)) | |
97 | if (errno == ENOSPC) | |
98 | { | |
99 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
100 | return -1; | |
101 | } | |
102 | ||
103 | /* Extend the mmap address if needed. */ | |
104 | if (elf->cmd == ELF_C_RDWR_MMAP | |
105 | && (size_t) size > elf->maximum_size) | |
106 | { | |
3cf38698 | 107 | #ifdef HAVE_MREMAP |
fb0457f4 MW |
108 | if (mremap (elf->map_address, elf->maximum_size, |
109 | size, 0) == MAP_FAILED) | |
3cf38698 | 110 | #endif |
fb0457f4 MW |
111 | { |
112 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
113 | return -1; | |
114 | } | |
115 | elf->maximum_size = size; | |
116 | } | |
117 | ||
118 | } | |
ae1e85ea | 119 | |
b08d5a8f UD |
120 | /* The file is mmaped. */ |
121 | if ((class == ELFCLASS32 | |
122 | ? __elf32_updatemmap (elf, change_bo, shnum) | |
123 | : __elf64_updatemmap (elf, change_bo, shnum)) != 0) | |
124 | /* Some problem while writing. */ | |
125 | size = -1; | |
126 | } | |
127 | else | |
128 | { | |
129 | /* The file is not mmaped. */ | |
130 | if ((class == ELFCLASS32 | |
131 | ? __elf32_updatefile (elf, change_bo, shnum) | |
132 | : __elf64_updatefile (elf, change_bo, shnum)) != 0) | |
133 | /* Some problem while writing. */ | |
134 | size = -1; | |
135 | } | |
136 | ||
77482c4b | 137 | /* Reduce the file size if necessary. */ |
b08d5a8f UD |
138 | if (size != -1 |
139 | && elf->parent == NULL | |
140 | && elf->maximum_size != ~((size_t) 0) | |
141 | && (size_t) size < elf->maximum_size | |
142 | && unlikely (ftruncate (elf->fildes, size) != 0)) | |
143 | { | |
144 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
89757447 RM |
145 | size = -1; |
146 | } | |
147 | ||
148 | /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID | |
149 | mode bits. So make sure we restore them afterwards if they were set. | |
150 | This is not atomic if someone else chmod's the file while we operate. */ | |
151 | if (size != -1 | |
152 | && unlikely (st.st_mode & (S_ISUID | S_ISGID)) | |
153 | /* fchmod ignores the bits we cannot change. */ | |
154 | && unlikely (fchmod (elf->fildes, st.st_mode) != 0)) | |
155 | { | |
156 | __libelf_seterrno (ELF_E_WRITE_ERROR); | |
b08d5a8f UD |
157 | size = -1; |
158 | } | |
159 | ||
160 | if (size != -1 && elf->parent == NULL) | |
161 | elf->maximum_size = size; | |
162 | ||
163 | return size; | |
164 | } | |
165 | ||
166 | ||
a46200f6 | 167 | int64_t |
1ccdfb68 | 168 | elf_update (Elf *elf, Elf_Cmd cmd) |
b08d5a8f UD |
169 | { |
170 | size_t shnum; | |
a46200f6 | 171 | int64_t size; |
b08d5a8f UD |
172 | int change_bo = 0; |
173 | ||
174 | if (cmd != ELF_C_NULL | |
175 | && cmd != ELF_C_WRITE | |
176 | && unlikely (cmd != ELF_C_WRITE_MMAP)) | |
177 | { | |
178 | __libelf_seterrno (ELF_E_INVALID_CMD); | |
179 | return -1; | |
180 | } | |
181 | ||
182 | if (elf == NULL) | |
183 | return -1; | |
184 | ||
185 | if (elf->kind != ELF_K_ELF) | |
186 | { | |
187 | __libelf_seterrno (ELF_E_INVALID_HANDLE); | |
188 | return -1; | |
189 | } | |
190 | ||
b4d6f0f8 | 191 | rwlock_wrlock (elf->lock); |
b08d5a8f UD |
192 | |
193 | /* Make sure we have an ELF header. */ | |
194 | if (elf->state.elf.ehdr == NULL) | |
195 | { | |
196 | __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR); | |
197 | size = -1; | |
198 | goto out; | |
199 | } | |
200 | ||
201 | /* Determine the number of sections. */ | |
202 | shnum = (elf->state.elf.scns_last->cnt == 0 | |
203 | ? 0 | |
204 | : 1 + elf->state.elf.scns_last->data[elf->state.elf.scns_last->cnt - 1].index); | |
205 | ||
206 | /* Update the ELF descriptor. First, place the program header. It | |
207 | will come right after the ELF header. The count the size of all | |
208 | sections and finally place the section table. */ | |
209 | size = (elf->class == ELFCLASS32 | |
b4d6f0f8 RM |
210 | ? __elf32_updatenull_wrlock (elf, &change_bo, shnum) |
211 | : __elf64_updatenull_wrlock (elf, &change_bo, shnum)); | |
b08d5a8f UD |
212 | if (likely (size != -1) |
213 | /* See whether we actually have to write out the data. */ | |
214 | && (cmd == ELF_C_WRITE || cmd == ELF_C_WRITE_MMAP)) | |
215 | { | |
216 | if (elf->cmd != ELF_C_RDWR | |
217 | && elf->cmd != ELF_C_RDWR_MMAP | |
218 | && elf->cmd != ELF_C_WRITE | |
219 | && unlikely (elf->cmd != ELF_C_WRITE_MMAP)) | |
220 | { | |
221 | __libelf_seterrno (ELF_E_UPDATE_RO); | |
222 | size = -1; | |
223 | } | |
224 | else if (unlikely (elf->fildes == -1)) | |
225 | { | |
226 | /* We closed the file already. */ | |
227 | __libelf_seterrno (ELF_E_FD_DISABLED); | |
228 | size = -1; | |
229 | } | |
230 | else | |
697d8d28 | 231 | size = write_file (elf, size, change_bo, shnum); |
b08d5a8f UD |
232 | } |
233 | ||
234 | out: | |
b4d6f0f8 | 235 | rwlock_unlock (elf->lock); |
b08d5a8f UD |
236 | |
237 | return size; | |
238 | } |