Branch data Line data Source code
1 : : /* Write changed data structures.
2 : : Copyright (C) 2000-2010, 2014, 2015, 2016, 2018 Red Hat, Inc.
3 : : This file is part of elfutils.
4 : : Written by Ulrich Drepper <drepper@redhat.com>, 2000.
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 <assert.h>
35 : : #include <errno.h>
36 : : #include <libelf.h>
37 : : #include <stdbool.h>
38 : : #include <stdlib.h>
39 : : #include <string.h>
40 : :
41 : : #include "libelfP.h"
42 : :
43 : :
44 : : #ifndef LIBELFBITS
45 : : # define LIBELFBITS 32
46 : : #endif
47 : :
48 : :
49 : : static int
50 : 29840348 : compare_sections (const void *a, const void *b)
51 : : {
52 : 29840348 : const Elf_Scn **scna = (const Elf_Scn **) a;
53 : 29840348 : const Elf_Scn **scnb = (const Elf_Scn **) b;
54 : :
55 : 29840348 : if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
56 [ + + ]: 29840348 : < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
57 : : return -1;
58 : :
59 [ + + ]: 1191480 : if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
60 : : > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
61 : : return 1;
62 : :
63 : 1190242 : if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
64 [ + + ]: 1190242 : < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
65 : : return -1;
66 : :
67 [ + + ]: 1185010 : if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
68 : : > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
69 : : return 1;
70 : :
71 [ - + ]: 1048840 : if ((*scna)->index < (*scnb)->index)
72 : : return -1;
73 : :
74 [ # # ]: 0 : if ((*scna)->index > (*scnb)->index)
75 : 0 : return 1;
76 : :
77 : : return 0;
78 : : }
79 : :
80 : :
81 : : /* Insert the sections in the list into the provided array and sort
82 : : them according to their start offsets. For sections with equal
83 : : start offsets, the size is used; for sections with equal start
84 : : offsets and sizes, the section index is used. Sorting by size
85 : : ensures that zero-length sections are processed first, which
86 : : is what we want since they do not advance our file writing position. */
87 : : static void
88 : 1242 : sort_sections (Elf_Scn **scns, Elf_ScnList *list)
89 : : {
90 : 1242 : Elf_Scn **scnp = scns;
91 : 3204 : do
92 [ + + ]: 3836052 : for (size_t cnt = 0; cnt < list->cnt; ++cnt)
93 : 3832848 : *scnp++ = &list->data[cnt];
94 [ + + ]: 3204 : while ((list = list->next) != NULL);
95 : :
96 : 1242 : qsort (scns, scnp - scns, sizeof (*scns), compare_sections);
97 : 1242 : }
98 : :
99 : :
100 : : static inline void
101 : 1142 : fill_mmap (size_t offset, char *last_position, char *scn_start,
102 : : char *const shdr_start, char *const shdr_end)
103 : : {
104 : 1142 : size_t written = 0;
105 : :
106 [ + + ]: 1142 : if (last_position < shdr_start)
107 : : {
108 [ + + ]: 1138 : written = MIN (scn_start + offset - last_position,
109 : : shdr_start - last_position);
110 : :
111 : 1138 : memset (last_position, __libelf_fill_byte, written);
112 : : }
113 : :
114 [ + + ]: 1142 : if (last_position + written != scn_start + offset
115 [ - + ]: 16 : && shdr_end < scn_start + offset)
116 : : {
117 : 0 : char *fill_start = MAX (shdr_end, scn_start);
118 : 1142 : memset (fill_start, __libelf_fill_byte,
119 : 0 : scn_start + offset - fill_start);
120 : : }
121 : 1142 : }
122 : :
123 : : int
124 : : internal_function
125 : 290 : __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
126 : : {
127 : 290 : bool previous_scn_changed = false;
128 : :
129 : : /* We need the ELF header several times. */
130 : 290 : ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
131 : :
132 : : /* Write out the ELF header. */
133 [ + + ]: 290 : if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
134 : : {
135 : : /* If the type sizes should be different at some time we have to
136 : : rewrite this code. */
137 [ - + ]: 286 : assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
138 : : == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
139 : :
140 [ + + ]: 286 : if (unlikely (change_bo))
141 : : {
142 : : /* Today there is only one version of the ELF header. */
143 : : #undef fctp
144 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
145 : :
146 : : /* Do the real work. */
147 : 88 : (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
148 : : sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
149 : : }
150 [ + + ]: 198 : else if (elf->map_address + elf->start_offset != ehdr)
151 : 184 : memcpy (elf->map_address + elf->start_offset, ehdr,
152 : : sizeof (ElfW2(LIBELFBITS,Ehdr)));
153 : :
154 : 286 : elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
155 : :
156 : : /* We start writing sections after the ELF header only if there is
157 : : no program header. */
158 : 286 : previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
159 : : }
160 : :
161 : 290 : size_t phnum;
162 [ - + ]: 290 : if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
163 : : return -1;
164 : :
165 : : /* Write out the program header table. */
166 [ + + ]: 290 : if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
167 : 116 : && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
168 [ + - ]: 116 : & ELF_F_DIRTY))
169 : : {
170 : : /* If the type sizes should be different at some time we have to
171 : : rewrite this code. */
172 [ - + ]: 116 : assert (sizeof (ElfW2(LIBELFBITS,Phdr))
173 : : == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
174 : :
175 : : /* Maybe the user wants a gap between the ELF header and the program
176 : : header. */
177 [ - + ]: 116 : if (ehdr->e_phoff > ehdr->e_ehsize)
178 : 52 : memset (elf->map_address + elf->start_offset + ehdr->e_ehsize,
179 : 0 : __libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize);
180 : :
181 [ + + ]: 116 : if (unlikely (change_bo))
182 : : {
183 : : /* Today there is only one version of the ELF header. */
184 : : #undef fctp
185 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
186 : :
187 : : /* Do the real work. */
188 : 28 : (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
189 : 28 : elf->state.ELFW(elf,LIBELFBITS).phdr,
190 : : sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
191 : : }
192 : : else
193 : 204 : memmove (elf->map_address + elf->start_offset + ehdr->e_phoff,
194 : 88 : elf->state.ELFW(elf,LIBELFBITS).phdr,
195 : : sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
196 : :
197 : 116 : elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
198 : :
199 : : /* We modified the program header. Maybe this created a gap so
200 : : we have to write fill bytes, if necessary. */
201 : 116 : previous_scn_changed = true;
202 : : }
203 : :
204 : : /* From now on we have to keep track of the last position to eventually
205 : : fill the gaps with the prescribed fill byte. */
206 : 290 : char *last_position = ((char *) elf->map_address + elf->start_offset
207 : 290 : + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
208 : : ehdr->e_phoff)
209 : 290 : + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));
210 : :
211 : : /* Write all the sections. Well, only those which are modified. */
212 [ + + ]: 290 : if (shnum > 0)
213 : : {
214 [ - + ]: 282 : if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *)))
215 : : return 1;
216 : :
217 : 282 : Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
218 : 282 : Elf_Scn **scns = malloc (shnum * sizeof (Elf_Scn *));
219 [ - + ]: 282 : if (unlikely (scns == NULL))
220 : : {
221 : 0 : __libelf_seterrno (ELF_E_NOMEM);
222 : 0 : return -1;
223 : : }
224 : 282 : char *const shdr_start = ((char *) elf->map_address + elf->start_offset
225 : 282 : + ehdr->e_shoff);
226 : 282 : char *const shdr_end = shdr_start + shnum * ehdr->e_shentsize;
227 : :
228 : : #undef shdr_fctp
229 : : #define shdr_fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
230 : : #define shdr_dest ((ElfW2(LIBELFBITS,Shdr) *) shdr_start)
231 : :
232 : : /* Get all sections into the array and sort them. */
233 : 282 : sort_sections (scns, list);
234 : :
235 : : /* We possibly have to copy the section header data because moving
236 : : the sections might overwrite the data. */
237 [ + + ]: 1057148 : for (size_t cnt = 0; cnt < shnum; ++cnt)
238 : : {
239 : 1056866 : Elf_Scn *scn = scns[cnt];
240 : :
241 [ + + ]: 1056866 : if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
242 [ + + ]: 1056646 : && (scn->shdr_flags & ELF_F_MALLOCED) == 0
243 [ + + ]: 210 : && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
244 : : {
245 [ - + ]: 198 : assert ((char *) elf->map_address + elf->start_offset
246 : : < (char *) scn->shdr.ELFW(e,LIBELFBITS));
247 [ - + ]: 198 : assert ((char *) scn->shdr.ELFW(e,LIBELFBITS)
248 : : < ((char *) elf->map_address + elf->start_offset
249 : : + elf->maximum_size));
250 : :
251 : 198 : void *p = malloc (sizeof (ElfW2(LIBELFBITS,Shdr)));
252 [ - + ]: 198 : if (unlikely (p == NULL))
253 : : {
254 : 0 : free (scns);
255 : 0 : __libelf_seterrno (ELF_E_NOMEM);
256 : 0 : return -1;
257 : : }
258 : 198 : scn->shdr.ELFW(e,LIBELFBITS)
259 : 198 : = memcpy (p, scn->shdr.ELFW(e,LIBELFBITS),
260 : : sizeof (ElfW2(LIBELFBITS,Shdr)));
261 : : }
262 : :
263 : : /* If the file is mmaped and the original position of the
264 : : section in the file is lower than the new position we
265 : : need to save the section content since otherwise it is
266 : : overwritten before it can be copied. If there are
267 : : multiple data segments in the list only the first can be
268 : : from the file. */
269 : 1056866 : if (((char *) elf->map_address + elf->start_offset
270 [ + + ]: 1056866 : <= (char *) scn->data_list.data.d.d_buf)
271 : 525128 : && ((char *) scn->data_list.data.d.d_buf
272 : : < ((char *) elf->map_address + elf->start_offset
273 [ + + ]: 525128 : + elf->maximum_size))
274 : 60 : && (((char *) elf->map_address + elf->start_offset
275 [ + + ]: 60 : + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset)
276 : : > (char *) scn->data_list.data.d.d_buf))
277 : : {
278 : 2 : void *p = malloc (scn->data_list.data.d.d_size);
279 [ - + ]: 2 : if (unlikely (p == NULL))
280 : : {
281 : 0 : free (scns);
282 : 0 : __libelf_seterrno (ELF_E_NOMEM);
283 : 0 : return -1;
284 : : }
285 : 2 : scn->data_list.data.d.d_buf = scn->data_base
286 : 2 : = memcpy (p, scn->data_list.data.d.d_buf,
287 : : scn->data_list.data.d.d_size);
288 : : }
289 : : }
290 : :
291 : : /* Iterate over all the section in the order in which they
292 : : appear in the output file. */
293 [ + + ]: 1057148 : for (size_t cnt = 0; cnt < shnum; ++cnt)
294 : : {
295 : 1056866 : Elf_Scn *scn = scns[cnt];
296 [ + + ]: 1056866 : if (scn->index == 0)
297 : : {
298 : : /* The dummy section header entry. It should not be
299 : : possible to mark this "section" as dirty. */
300 [ - + ]: 282 : assert ((scn->flags & ELF_F_DIRTY) == 0);
301 : 282 : continue;
302 : : }
303 : :
304 : 1056584 : ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
305 [ + + ]: 1056584 : if (shdr->sh_type == SHT_NOBITS)
306 : 258 : goto next;
307 : :
308 : 1056326 : char *scn_start = ((char *) elf->map_address
309 : 1056326 : + elf->start_offset + shdr->sh_offset);
310 : 1056326 : Elf_Data_List *dl = &scn->data_list;
311 : 1056326 : bool scn_changed = false;
312 : :
313 [ + + ]: 1056326 : if (scn->data_list_rear != NULL)
314 : 1056086 : do
315 : : {
316 [ - + ]: 1056086 : assert (dl->data.d.d_off >= 0);
317 [ - + ]: 1056086 : assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size);
318 [ - + ]: 1056086 : assert (dl->data.d.d_size <= (shdr->sh_size
319 : : - (GElf_Off) dl->data.d.d_off));
320 : :
321 : : /* If there is a gap, fill it. */
322 [ + + ]: 1056086 : if (scn_start + dl->data.d.d_off > last_position
323 [ - + ]: 1136 : && (dl->data.d.d_off == 0
324 : 0 : || ((scn->flags | dl->flags | elf->flags)
325 [ # # ]: 0 : & ELF_F_DIRTY) != 0))
326 : : {
327 : 1136 : fill_mmap (dl->data.d.d_off, last_position, scn_start,
328 : : shdr_start, shdr_end);
329 : : }
330 : :
331 : 1056086 : last_position = scn_start + dl->data.d.d_off;
332 : :
333 [ + + ]: 1056086 : if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
334 : : {
335 : : /* Let it go backward if the sections use a bogus
336 : : layout with overlaps. We'll overwrite the stupid
337 : : user's section data with the latest one, rather than
338 : : crashing. */
339 : :
340 [ + + + + : 1056070 : if (unlikely (change_bo
+ + ]
341 : : && dl->data.d.d_size != 0
342 : : && dl->data.d.d_type != ELF_T_BYTE))
343 : 348 : {
344 : : #undef fctp
345 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
346 : :
347 : 348 : size_t align;
348 : 348 : align = __libelf_type_align (ELFW(ELFCLASS,LIBELFBITS),
349 : : dl->data.d.d_type);
350 : 348 : if ((((uintptr_t) last_position)
351 [ + - ]: 348 : & (uintptr_t) (align - 1)) == 0)
352 : : {
353 : : /* No need to copy, we can convert directly. */
354 : 348 : (*fctp) (last_position, dl->data.d.d_buf,
355 : : dl->data.d.d_size, 1);
356 : : }
357 : : else
358 : : {
359 : : /* We have to do the conversion on properly
360 : : aligned memory first. align is a power of 2,
361 : : but posix_memalign only works for alignments
362 : : which are a multiple of sizeof (void *).
363 : : So use normal malloc for smaller alignments. */
364 : 0 : size_t size = dl->data.d.d_size;
365 : 0 : void *converted;
366 [ # # ]: 0 : if (align < sizeof (void *))
367 : 0 : converted = malloc (size);
368 : : else
369 : : {
370 : 0 : int res;
371 [ # # ]: 0 : res = posix_memalign (&converted, align, size);
372 : 0 : if (res != 0)
373 : : converted = NULL;
374 : : }
375 : :
376 [ # # ]: 0 : if (converted == NULL)
377 : : {
378 : 0 : free (scns);
379 : 0 : __libelf_seterrno (ELF_E_NOMEM);
380 : 0 : return 1;
381 : : }
382 : :
383 : 0 : (*fctp) (converted, dl->data.d.d_buf, size, 1);
384 : :
385 : : /* And then write it to the mmapped file. */
386 : 0 : memcpy (last_position, converted, size);
387 : 0 : free (converted);
388 : : }
389 : :
390 : 348 : last_position += dl->data.d.d_size;
391 : : }
392 [ + + ]: 1055722 : else if (dl->data.d.d_size != 0)
393 : : {
394 : 1055568 : memmove (last_position, dl->data.d.d_buf,
395 : : dl->data.d.d_size);
396 : 1055568 : last_position += dl->data.d.d_size;
397 : : }
398 : :
399 : : scn_changed = true;
400 : : }
401 : : else
402 : 16 : last_position += dl->data.d.d_size;
403 : :
404 [ - + ]: 1056086 : assert (scn_start + dl->data.d.d_off + dl->data.d.d_size
405 : : == last_position);
406 : :
407 : 1056086 : dl->flags &= ~ELF_F_DIRTY;
408 : :
409 : 1056086 : dl = dl->next;
410 : : }
411 [ + + ]: 1056086 : while (dl != NULL);
412 : : else
413 : : {
414 : : /* If the previous section (or the ELF/program
415 : : header) changed we might have to fill the gap. */
416 [ + + ]: 296 : if (scn_start > last_position && previous_scn_changed)
417 : 6 : fill_mmap (0, last_position, scn_start,
418 : : shdr_start, shdr_end);
419 : :
420 : : /* We have to trust the existing section header information. */
421 : 296 : last_position = scn_start + shdr->sh_size;
422 : : }
423 : :
424 : :
425 : : previous_scn_changed = scn_changed;
426 : 1056584 : next:
427 : 1056584 : scn->flags &= ~ELF_F_DIRTY;
428 : : }
429 : :
430 : : /* Fill the gap between last section and section header table if
431 : : necessary. */
432 [ + + ]: 282 : if ((elf->flags & ELF_F_DIRTY)
433 : 278 : && last_position < ((char *) elf->map_address + elf->start_offset
434 [ + + ]: 278 : + ehdr->e_shoff))
435 : 1057376 : memset (last_position, __libelf_fill_byte,
436 : : (char *) elf->map_address + elf->start_offset + ehdr->e_shoff
437 : 228 : - last_position);
438 : :
439 : : /* Write the section header table entry if necessary. */
440 [ + + ]: 1057148 : for (size_t cnt = 0; cnt < shnum; ++cnt)
441 : : {
442 : 1056866 : Elf_Scn *scn = scns[cnt];
443 : :
444 [ + + ]: 1056866 : if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY)
445 : : {
446 [ + + ]: 1056842 : if (unlikely (change_bo))
447 : 263522 : (*shdr_fctp) (&shdr_dest[scn->index],
448 : 263522 : scn->shdr.ELFW(e,LIBELFBITS),
449 : : sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
450 : : else
451 : 1850162 : memcpy (&shdr_dest[scn->index],
452 : 793320 : scn->shdr.ELFW(e,LIBELFBITS),
453 : : sizeof (ElfW2(LIBELFBITS,Shdr)));
454 : :
455 : : /* If we previously made a copy of the section header
456 : : entry we now have to adjust the pointer again so
457 : : point to new place in the mapping. */
458 [ + + ]: 1056842 : if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
459 [ + + ]: 1056634 : && (scn->shdr_flags & ELF_F_MALLOCED) == 0
460 [ + - ]: 198 : && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
461 : : {
462 : 198 : free (scn->shdr.ELFW(e,LIBELFBITS));
463 : 198 : scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index];
464 : : }
465 : :
466 : 1056842 : scn->shdr_flags &= ~ELF_F_DIRTY;
467 : : }
468 : : }
469 : 282 : free (scns);
470 : : }
471 : :
472 : : /* That was the last part. Clear the overall flag. */
473 : 290 : elf->flags &= ~ELF_F_DIRTY;
474 : :
475 : : /* Make sure the content hits the disk. */
476 : 580 : char *msync_start = ((char *) elf->map_address
477 : 290 : + (elf->start_offset & ~(sysconf (_SC_PAGESIZE) - 1)));
478 : 290 : char *msync_end = ((char *) elf->map_address
479 : 290 : + elf->start_offset + ehdr->e_shoff
480 : 290 : + ehdr->e_shentsize * shnum);
481 : 290 : (void) msync (msync_start, msync_end - msync_start, MS_SYNC);
482 : :
483 : 290 : return 0;
484 : : }
485 : :
486 : :
487 : : /* Size of the buffer we use to generate the blocks of fill bytes. */
488 : : #define FILLBUFSIZE 4096
489 : :
490 : : /* If we have to convert the section buffer contents we have to use
491 : : temporary buffer. Only buffers up to MAX_TMPBUF bytes are allocated
492 : : on the stack. */
493 : : #define MAX_TMPBUF 32768
494 : :
495 : :
496 : : /* Helper function to write out fill bytes. */
497 : : static int
498 : 5188 : fill (int fd, int64_t pos, size_t len, char *fillbuf, size_t *filledp)
499 : : {
500 : 5188 : size_t filled = *filledp;
501 : 5188 : size_t fill_len = MIN (len, FILLBUFSIZE);
502 : :
503 [ + + + - ]: 5188 : if (unlikely (fill_len > filled) && filled < FILLBUFSIZE)
504 : : {
505 : : /* Initialize a few more bytes. */
506 : 1854 : memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled);
507 : 1854 : *filledp = filled = fill_len;
508 : : }
509 : :
510 : 6210 : do
511 : : {
512 : : /* This many bytes we want to write in this round. */
513 : 6210 : size_t n = MIN (filled, len);
514 : :
515 [ - + ]: 6210 : if (unlikely ((size_t) pwrite_retry (fd, fillbuf, n, pos) != n))
516 : : {
517 : 0 : __libelf_seterrno (ELF_E_WRITE_ERROR);
518 : 0 : return 1;
519 : : }
520 : :
521 : 6210 : pos += n;
522 : 6210 : len -= n;
523 : : }
524 [ + + ]: 6210 : while (len > 0);
525 : :
526 : : return 0;
527 : : }
528 : :
529 : :
530 : : int
531 : : internal_function
532 : 972 : __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
533 : : {
534 : 972 : char fillbuf[FILLBUFSIZE];
535 : 972 : size_t filled = 0;
536 : 972 : bool previous_scn_changed = false;
537 : :
538 : : /* We need the ELF header several times. */
539 : 972 : ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
540 : :
541 : : /* Write out the ELF header. */
542 [ + + ]: 972 : if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
543 : : {
544 : 946 : ElfW2(LIBELFBITS,Ehdr) tmp_ehdr;
545 : 946 : ElfW2(LIBELFBITS,Ehdr) *out_ehdr = ehdr;
546 : :
547 : : /* If the type sizes should be different at some time we have to
548 : : rewrite this code. */
549 [ - + ]: 946 : assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
550 : : == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
551 : :
552 [ + + ]: 946 : if (unlikely (change_bo))
553 : : {
554 : : /* Today there is only one version of the ELF header. */
555 : : #undef fctp
556 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
557 : :
558 : : /* Write the converted ELF header in a temporary buffer. */
559 : 350 : (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
560 : :
561 : : /* This is the buffer we want to write. */
562 : 350 : out_ehdr = &tmp_ehdr;
563 : : }
564 : :
565 : : /* Write out the ELF header. */
566 [ - + ]: 946 : if (unlikely (pwrite_retry (elf->fildes, out_ehdr,
567 : : sizeof (ElfW2(LIBELFBITS,Ehdr)), 0)
568 : : != sizeof (ElfW2(LIBELFBITS,Ehdr))))
569 : : {
570 : 0 : __libelf_seterrno (ELF_E_WRITE_ERROR);
571 : 0 : return 1;
572 : : }
573 : :
574 : 946 : elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
575 : :
576 : : /* We start writing sections after the ELF header only if there is
577 : : no program header. */
578 : 946 : previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
579 : : }
580 : :
581 : : /* If the type sizes should be different at some time we have to
582 : : rewrite this code. */
583 [ - + ]: 972 : assert (sizeof (ElfW2(LIBELFBITS,Phdr))
584 : : == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
585 : :
586 : 972 : size_t phnum;
587 [ - + ]: 972 : if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
588 : : return -1;
589 : :
590 : : /* Write out the program header table. */
591 [ + + ]: 972 : if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
592 : 698 : && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
593 [ + + ]: 698 : & ELF_F_DIRTY))
594 : : {
595 : 678 : ElfW2(LIBELFBITS,Phdr) *tmp_phdr = NULL;
596 : 678 : ElfW2(LIBELFBITS,Phdr) *out_phdr = elf->state.ELFW(elf,LIBELFBITS).phdr;
597 : :
598 : : /* Maybe the user wants a gap between the ELF header and the program
599 : : header. */
600 [ - + ]: 678 : if (ehdr->e_phoff > ehdr->e_ehsize
601 [ # # ]: 0 : && unlikely (fill (elf->fildes, ehdr->e_ehsize,
602 : : ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled)
603 : : != 0))
604 : : return 1;
605 : :
606 [ + + ]: 678 : if (unlikely (change_bo))
607 : : {
608 : : /* Today there is only one version of the ELF header. */
609 : : #undef fctp
610 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
611 : :
612 : : /* Allocate sufficient memory. */
613 : 254 : tmp_phdr = (ElfW2(LIBELFBITS,Phdr) *)
614 : 254 : malloc (sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
615 [ - + ]: 254 : if (unlikely (tmp_phdr == NULL))
616 : : {
617 : 0 : __libelf_seterrno (ELF_E_NOMEM);
618 : 0 : return 1;
619 : : }
620 : :
621 : : /* Write the converted ELF header in a temporary buffer. */
622 : 254 : (*fctp) (tmp_phdr, elf->state.ELFW(elf,LIBELFBITS).phdr,
623 : : sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
624 : :
625 : : /* This is the buffer we want to write. */
626 : 254 : out_phdr = tmp_phdr;
627 : : }
628 : :
629 : : /* Write out the ELF header. */
630 : 678 : size_t phdr_size = sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum;
631 [ - + ]: 678 : if (unlikely ((size_t) pwrite_retry (elf->fildes, out_phdr,
632 : : phdr_size, ehdr->e_phoff)
633 : : != phdr_size))
634 : : {
635 : 0 : __libelf_seterrno (ELF_E_WRITE_ERROR);
636 : 0 : return 1;
637 : : }
638 : :
639 : : /* This is a no-op we we have not allocated any memory. */
640 : 678 : free (tmp_phdr);
641 : :
642 : 678 : elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
643 : :
644 : : /* We modified the program header. Maybe this created a gap so
645 : : we have to write fill bytes, if necessary. */
646 : 678 : previous_scn_changed = true;
647 : : }
648 : :
649 : : /* From now on we have to keep track of the last position to eventually
650 : : fill the gaps with the prescribed fill byte. */
651 : 972 : int64_t last_offset;
652 [ + + ]: 972 : if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
653 : 274 : last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
654 : : else
655 : 698 : last_offset = (ehdr->e_phoff + sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
656 : :
657 : : /* Write all the sections. Well, only those which are modified. */
658 [ + + ]: 972 : if (shnum > 0)
659 : : {
660 [ - + ]: 960 : if (unlikely (shnum > SIZE_MAX / (sizeof (Elf_Scn *)
661 : : + sizeof (ElfW2(LIBELFBITS,Shdr)))))
662 : : return 1;
663 : :
664 : 960 : int64_t shdr_offset = elf->start_offset + ehdr->e_shoff;
665 : : #undef shdr_fctp
666 : : #define shdr_fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
667 : :
668 : 960 : ElfW2(LIBELFBITS,Shdr) *shdr_data;
669 : 960 : ElfW2(LIBELFBITS,Shdr) *shdr_data_mem = NULL;
670 [ + + + + ]: 960 : if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
671 [ + + ]: 64 : || (elf->flags & ELF_F_DIRTY))
672 : : {
673 : 932 : shdr_data_mem = (ElfW2(LIBELFBITS,Shdr) *)
674 : 932 : malloc (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
675 [ - + ]: 932 : if (unlikely (shdr_data_mem == NULL))
676 : : {
677 : 0 : __libelf_seterrno (ELF_E_NOMEM);
678 : 0 : return -1;
679 : : }
680 : : shdr_data = shdr_data_mem;
681 : : }
682 : : else
683 : : shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr;
684 : 960 : int shdr_flags = elf->flags;
685 : :
686 : : /* Get all sections into the array and sort them. */
687 : 960 : Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
688 : 960 : Elf_Scn **scns = malloc (shnum * sizeof (Elf_Scn *));
689 [ - + ]: 960 : if (unlikely (scns == NULL))
690 : : {
691 : 0 : free (shdr_data_mem);
692 : 0 : __libelf_seterrno (ELF_E_NOMEM);
693 : 0 : return -1;
694 : : }
695 : 960 : sort_sections (scns, list);
696 : :
697 [ + + ]: 2776942 : for (size_t cnt = 0; cnt < shnum; ++cnt)
698 : : {
699 : 2775982 : Elf_Scn *scn = scns[cnt];
700 [ + + ]: 2775982 : if (scn->index == 0)
701 : : {
702 : : /* The dummy section header entry. It should not be
703 : : possible to mark this "section" as dirty. */
704 [ - + ]: 960 : assert ((scn->flags & ELF_F_DIRTY) == 0);
705 : 960 : goto next;
706 : : }
707 : :
708 : 2775022 : ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
709 [ + + ]: 2775022 : if (shdr->sh_type == SHT_NOBITS)
710 : 135024 : goto next;
711 : :
712 : 2639998 : int64_t scn_start = elf->start_offset + shdr->sh_offset;
713 : 2639998 : Elf_Data_List *dl = &scn->data_list;
714 : 2639998 : bool scn_changed = false;
715 : :
716 [ + + ]: 2639998 : if (scn->data_list_rear != NULL)
717 : 2114070 : do
718 : : {
719 : : /* If there is a gap, fill it. */
720 [ + + ]: 2114070 : if (scn_start + dl->data.d.d_off > last_offset
721 [ + + - + ]: 4420 : && ((previous_scn_changed && dl->data.d.d_off == 0)
722 : 22 : || ((scn->flags | dl->flags | elf->flags)
723 [ + + ]: 22 : & ELF_F_DIRTY) != 0))
724 : : {
725 [ - + ]: 4412 : if (unlikely (fill (elf->fildes, last_offset,
726 : : (scn_start + dl->data.d.d_off)
727 : : - last_offset, fillbuf,
728 : : &filled) != 0))
729 : : {
730 : 0 : fail_free:
731 : 0 : free (shdr_data_mem);
732 : 0 : free (scns);
733 : 0 : return 1;
734 : : }
735 : : }
736 : :
737 : 2114070 : last_offset = scn_start + dl->data.d.d_off;
738 : :
739 [ + + ]: 2114070 : if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
740 : : {
741 : 2114054 : char tmpbuf[MAX_TMPBUF];
742 : 2114054 : void *buf = dl->data.d.d_buf;
743 : :
744 : : /* Let it go backward if the sections use a bogus
745 : : layout with overlaps. We'll overwrite the stupid
746 : : user's section data with the latest one, rather than
747 : : crashing. */
748 : :
749 [ + + ]: 2114054 : if (unlikely (change_bo))
750 : : {
751 : : #undef fctp
752 : : #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
753 : :
754 : 792044 : buf = tmpbuf;
755 [ + + ]: 792044 : if (dl->data.d.d_size > MAX_TMPBUF)
756 : : {
757 : 42 : buf = malloc (dl->data.d.d_size);
758 [ - + ]: 42 : if (unlikely (buf == NULL))
759 : : {
760 : 0 : __libelf_seterrno (ELF_E_NOMEM);
761 : 0 : goto fail_free;
762 : : }
763 : : }
764 : :
765 : : /* Do the real work. */
766 : 792044 : (*fctp) (buf, dl->data.d.d_buf, dl->data.d.d_size, 1);
767 : : }
768 : :
769 : 2114054 : ssize_t n = pwrite_retry (elf->fildes, buf,
770 : : dl->data.d.d_size,
771 : : last_offset);
772 [ - + ]: 2114054 : if (unlikely ((size_t) n != dl->data.d.d_size))
773 : : {
774 [ # # # # ]: 0 : if (buf != dl->data.d.d_buf && buf != tmpbuf)
775 : 0 : free (buf);
776 : :
777 : 0 : __libelf_seterrno (ELF_E_WRITE_ERROR);
778 : 0 : goto fail_free;
779 : : }
780 : :
781 [ + + + + ]: 2114054 : if (buf != dl->data.d.d_buf && buf != tmpbuf)
782 : 42 : free (buf);
783 : :
784 : 2114054 : scn_changed = true;
785 : : }
786 : :
787 : 2114070 : last_offset += dl->data.d.d_size;
788 : :
789 : 2114070 : dl->flags &= ~ELF_F_DIRTY;
790 : :
791 : 2114070 : dl = dl->next;
792 : : }
793 [ + + ]: 2114070 : while (dl != NULL);
794 : : else
795 : : {
796 : : /* If the previous section (or the ELF/program
797 : : header) changed we might have to fill the gap. */
798 [ + + ]: 525980 : if (scn_start > last_offset && previous_scn_changed)
799 : : {
800 [ - + ]: 12 : if (unlikely (fill (elf->fildes, last_offset,
801 : : scn_start - last_offset, fillbuf,
802 : : &filled) != 0))
803 : 0 : goto fail_free;
804 : : }
805 : :
806 : 525980 : last_offset = scn_start + shdr->sh_size;
807 : : }
808 : :
809 : : previous_scn_changed = scn_changed;
810 : 2775982 : next:
811 : : /* Collect the section header table information. */
812 [ + + ]: 2775982 : if (unlikely (change_bo))
813 : 1055760 : (*shdr_fctp) (&shdr_data[scn->index],
814 : 1055760 : scn->shdr.ELFW(e,LIBELFBITS),
815 : : sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
816 [ + + ]: 1720222 : else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
817 [ + + ]: 1050468 : || (elf->flags & ELF_F_DIRTY))
818 : 1719276 : memcpy (&shdr_data[scn->index], scn->shdr.ELFW(e,LIBELFBITS),
819 : : sizeof (ElfW2(LIBELFBITS,Shdr)));
820 : :
821 : 2775982 : shdr_flags |= scn->shdr_flags;
822 : 2775982 : scn->shdr_flags &= ~ELF_F_DIRTY;
823 : : }
824 : :
825 : : /* Fill the gap between last section and section header table if
826 : : necessary. */
827 [ + + + + ]: 960 : if ((elf->flags & ELF_F_DIRTY) && last_offset < shdr_offset
828 [ - + ]: 764 : && unlikely (fill (elf->fildes, last_offset,
829 : : shdr_offset - last_offset,
830 : : fillbuf, &filled) != 0))
831 : 0 : goto fail_free;
832 : :
833 : : /* Write out the section header table. */
834 [ + + ]: 960 : if (shdr_flags & ELF_F_DIRTY
835 [ - + ]: 936 : && unlikely ((size_t) pwrite_retry (elf->fildes, shdr_data,
836 : : sizeof (ElfW2(LIBELFBITS,Shdr))
837 : : * shnum, shdr_offset)
838 : : != sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum))
839 : : {
840 : 0 : __libelf_seterrno (ELF_E_WRITE_ERROR);
841 : 0 : goto fail_free;
842 : : }
843 : :
844 : 960 : free (shdr_data_mem);
845 : 960 : free (scns);
846 : : }
847 : :
848 : : /* That was the last part. Clear the overall flag. */
849 : 972 : elf->flags &= ~ELF_F_DIRTY;
850 : :
851 : 972 : return 0;
852 : : }
|