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