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