1 /* Write changed data structures.
2 Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2000.
5
6 Red Hat elfutils is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 2 of the License.
9
10 Red Hat elfutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with Red Hat elfutils; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19 In addition, as a special exception, Red Hat, Inc. gives You the
20 additional right to link the code of Red Hat elfutils with code licensed
21 under any Open Source Initiative certified open source license
22 (http://www.opensource.org/licenses/index.php) which requires the
23 distribution of source code with any binary distribution and to
24 distribute linked combinations of the two. Non-GPL Code permitted under
25 this exception must only link to the code of Red Hat elfutils through
26 those well defined interfaces identified in the file named EXCEPTION
27 found in the source code files (the "Approved Interfaces"). The files
28 of Non-GPL Code may instantiate templates or use macros or inline
29 functions from the Approved Interfaces without causing the resulting
30 work to be covered by the GNU General Public License. Only Red Hat,
31 Inc. may make changes or additions to the list of Approved Interfaces.
32 Red Hat's grant of this exception is conditioned upon your not adding
33 any new exceptions. If you wish to add a new Approved Interface or
34 exception, please contact Red Hat. You must obey the GNU General Public
35 License in all respects for all of the Red Hat elfutils code and other
36 code used in conjunction with Red Hat elfutils except the Non-GPL Code
37 covered by this exception. If you modify this file, you may extend this
38 exception to your version of the file, but you are not obligated to do
39 so. If you do not wish to provide this exception without modification,
40 you must delete this exception statement from your version and license
41 this file solely under the GPL without exception.
42
43 Red Hat elfutils is an included package of the Open Invention Network.
44 An included package of the Open Invention Network is a package for which
45 Open Invention Network licensees cross-license their patents. No patent
46 license is granted, either expressly or impliedly, by designation as an
47 included package. Should you wish to participate in the Open Invention
48 Network licensing program, please visit www.openinventionnetwork.com
49 <http://www.openinventionnetwork.com>. */
50
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif
54
55 #include <assert.h>
56 #include <errno.h>
57 #include <libelf.h>
58 #include <stdbool.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <sys/mman.h>
63 #include <sys/param.h>
64
65 #include <system.h>
66 #include "libelfP.h"
67
68
69 #ifndef LIBELFBITS
70 # define LIBELFBITS 32
71 #endif
72
73
74 static int
compare_sections(const void * a,const void * b)75 compare_sections (const void *a, const void *b)
76 {
77 const Elf_Scn **scna = (const Elf_Scn **) a;
78 const Elf_Scn **scnb = (const Elf_Scn **) b;
79
80 if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
81 < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
82 return -1;
83
84 if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
85 > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
86 return 1;
87
88 if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
89 < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
90 return -1;
91
92 if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
93 > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
94 return 1;
95
96 if ((*scna)->index < (*scnb)->index)
97 return -1;
98
99 if ((*scna)->index > (*scnb)->index)
100 return 1;
101
102 return 0;
103 }
104
105
106 /* Insert the sections in the list into the provided array and sort
107 them according to their start offsets. For sections with equal
108 start offsets, the size is used; for sections with equal start
109 offsets and sizes, the section index is used. Sorting by size
110 ensures that zero-length sections are processed first, which
111 is what we want since they do not advance our file writing position. */
112 static void
sort_sections(Elf_Scn ** scns,Elf_ScnList * list)113 sort_sections (Elf_Scn **scns, Elf_ScnList *list)
114 {
115 Elf_Scn **scnp = scns;
116 do
117 for (size_t cnt = 0; cnt < list->cnt; ++cnt)
118 *scnp++ = &list->data[cnt];
119 while ((list = list->next) != NULL);
120
121 qsort (scns, scnp - scns, sizeof (*scns), compare_sections);
122 }
123
124
125 int
126 internal_function
__elfw2(LIBELFBITS,updatemmap)127 __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
128 {
129 ElfW2(LIBELFBITS,Ehdr) *ehdr;
130 char *last_position;
131
132 /* We need the ELF header several times. */
133 ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
134
135 /* Write out the ELF header. */
136 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 assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
141 == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
142
143 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 (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
156 sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
157 }
158 else
159 memcpy (elf->map_address + elf->start_offset, ehdr,
160 sizeof (ElfW2(LIBELFBITS,Ehdr)));
161
162 elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
163 }
164
165 /* Write out the program header table. */
166 if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
167 && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
168 & ELF_F_DIRTY))
169 {
170 /* If the type sizes should be different at some time we have to
171 rewrite this code. */
172 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 if (ehdr->e_phoff > ehdr->e_ehsize)
178 memset (elf->map_address + elf->start_offset + ehdr->e_ehsize,
179 __libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize);
180
181 if (unlikely (change_bo))
182 {
183 /* Today there is only one version of the ELF header. */
184 #if EV_NUM != 2
185 xfct_t fctp;
186 fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
187 #else
188 # undef fctp
189 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
190 #endif
191
192 /* Do the real work. */
193 (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
194 elf->state.ELFW(elf,LIBELFBITS).phdr,
195 sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum, 1);
196 }
197 else
198 memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff,
199 elf->state.ELFW(elf,LIBELFBITS).phdr,
200 sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
201
202 elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
203 }
204
205 /* From now on we have to keep track of the last position to eventually
206 fill the gaps with the prescribed fill byte. */
207 last_position = ((char *) elf->map_address + elf->start_offset
208 + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
209 ehdr->e_phoff)
210 + elf_typesize (LIBELFBITS, ELF_T_PHDR, ehdr->e_phnum));
211
212 /* Write all the sections. Well, only those which are modified. */
213 if (shnum > 0)
214 {
215 Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
216 Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *));
217 char *const shdr_start = ((char *) elf->map_address + elf->start_offset
218 + ehdr->e_shoff);
219 char *const shdr_end = shdr_start + ehdr->e_shnum * ehdr->e_shentsize;
220
221 #if EV_NUM != 2
222 xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
223 #else
224 # undef shdr_fctp
225 # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
226 #endif
227 #define shdr_dest ((ElfW2(LIBELFBITS,Shdr) *) shdr_start)
228
229 /* Get all sections into the array and sort them. */
230 sort_sections (scns, list);
231
232 /* We possibly have to copy the section header data because moving
233 the sections might overwrite the data. */
234 for (size_t cnt = 0; cnt < shnum; ++cnt)
235 {
236 Elf_Scn *scn = scns[cnt];
237
238 if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
239 && (scn->shdr_flags & ELF_F_MALLOCED) == 0
240 && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
241 {
242 assert ((char *) elf->map_address + elf->start_offset
243 < (char *) scn->shdr.ELFW(e,LIBELFBITS));
244 assert ((char *) scn->shdr.ELFW(e,LIBELFBITS)
245 < ((char *) elf->map_address + elf->start_offset
246 + elf->maximum_size));
247
248 void *p = alloca (sizeof (ElfW2(LIBELFBITS,Shdr)));
249 scn->shdr.ELFW(e,LIBELFBITS)
250 = memcpy (p, scn->shdr.ELFW(e,LIBELFBITS),
251 sizeof (ElfW2(LIBELFBITS,Shdr)));
252 }
253
254 /* If the file is mmaped and the original position of the
255 section in the file is lower than the new position we
256 need to save the section content since otherwise it is
257 overwritten before it can be copied. If there are
258 multiple data segments in the list only the first can be
259 from the file. */
260 if (((char *) elf->map_address + elf->start_offset
261 <= (char *) scn->data_list.data.d.d_buf)
262 && ((char *) scn->data_list.data.d.d_buf
263 < ((char *) elf->map_address + elf->start_offset
264 + elf->maximum_size))
265 && (((char *) elf->map_address + elf->start_offset
266 + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset)
267 > (char *) scn->data_list.data.d.d_buf))
268 {
269 void *p = malloc (scn->data_list.data.d.d_size);
270 if (p == NULL)
271 {
272 __libelf_seterrno (ELF_E_NOMEM);
273 return -1;
274 }
275 scn->data_list.data.d.d_buf = scn->data_base
276 = memcpy (p, scn->data_list.data.d.d_buf,
277 scn->data_list.data.d.d_size);
278 }
279 }
280
281 /* Iterate over all the section in the order in which they
282 appear in the output file. */
283 for (size_t cnt = 0; cnt < shnum; ++cnt)
284 {
285 Elf_Scn *scn = scns[cnt];
286
287 ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
288
289 char *scn_start = ((char *) elf->map_address
290 + elf->start_offset + shdr->sh_offset);
291 Elf_Data_List *dl = &scn->data_list;
292
293 if (shdr->sh_type != SHT_NOBITS && scn->data_list_rear != NULL)
294 do
295 {
296 assert (dl->data.d.d_off >= 0);
297 assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size);
298 assert (dl->data.d.d_size <= (shdr->sh_size
299 - (GElf_Off) dl->data.d.d_off));
300
301 if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
302 {
303 if (scn_start + dl->data.d.d_off > last_position)
304 {
305 /* This code assumes that the data blocks for
306 a section are ordered by offset. */
307 size_t written = 0;
308
309 if (last_position < shdr_start)
310 {
311 written = MIN (scn_start + dl->data.d.d_off
312 - last_position,
313 shdr_start - last_position);
314
315 memset (last_position, __libelf_fill_byte,
316 written);
317 }
318
319 if (last_position + written
320 != scn_start + dl->data.d.d_off
321 && shdr_end < scn_start + dl->data.d.d_off)
322 memset (shdr_end, __libelf_fill_byte,
323 scn_start + dl->data.d.d_off - shdr_end);
324 }
325
326 /* Let it go backward if the sections use a bogus
327 layout with overlaps. We'll overwrite the stupid
328 user's section data with the latest one, rather than
329 crashing. */
330
331 last_position = scn_start + dl->data.d.d_off;
332
333 if (unlikely (change_bo))
334 {
335 #if EV_NUM != 2
336 xfct_t fctp;
337 fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
338 #else
339 # undef fctp
340 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
341 #endif
342
343 /* Do the real work. */
344 (*fctp) (last_position, dl->data.d.d_buf,
345 dl->data.d.d_size, 1);
346
347 last_position += dl->data.d.d_size;
348 }
349 else
350 last_position = mempcpy (last_position,
351 dl->data.d.d_buf,
352 dl->data.d.d_size);
353 }
354 else
355 last_position += dl->data.d.d_size;
356
357 assert (scn_start + dl->data.d.d_off + dl->data.d.d_size
358 == last_position);
359
360 dl->flags &= ~ELF_F_DIRTY;
361
362 dl = dl->next;
363 }
364 while (dl != NULL);
365 else if (shdr->sh_type != SHT_NOBITS && scn->index != 0)
366 /* We have to trust the existing section header information. */
367 last_position += shdr->sh_size;
368
369 scn->flags &= ~ELF_F_DIRTY;
370 }
371
372 /* Fill the gap between last section and section header table if
373 necessary. */
374 if ((elf->flags & ELF_F_DIRTY)
375 && last_position < ((char *) elf->map_address + elf->start_offset
376 + ehdr->e_shoff))
377 memset (last_position, __libelf_fill_byte,
378 (char *) elf->map_address + elf->start_offset + ehdr->e_shoff
379 - last_position);
380
381 /* Write the section header table entry if necessary. */
382 for (size_t cnt = 0; cnt < shnum; ++cnt)
383 {
384 Elf_Scn *scn = scns[cnt];
385
386 if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY)
387 {
388 if (unlikely (change_bo))
389 (*shdr_fctp) (&shdr_dest[scn->index],
390 scn->shdr.ELFW(e,LIBELFBITS),
391 sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
392 else
393 memcpy (&shdr_dest[scn->index],
394 scn->shdr.ELFW(e,LIBELFBITS),
395 sizeof (ElfW2(LIBELFBITS,Shdr)));
396
397 /* If we previously made a copy of the section header
398 entry we now have to adjust the pointer again so
399 point to new place in the mapping. */
400 if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
401 && (scn->shdr_flags & ELF_F_MALLOCED) == 0)
402 scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index];
403
404 scn->shdr_flags &= ~ELF_F_DIRTY;
405 }
406 }
407 }
408
409 /* That was the last part. Clear the overall flag. */
410 elf->flags &= ~ELF_F_DIRTY;
411
412 /* Make sure the content hits the disk. */
413 char *msync_start = ((char *) elf->map_address
414 + (elf->start_offset & ~(sysconf (_SC_PAGESIZE) - 1)));
415 char *msync_end = ((char *) elf->map_address
416 + elf->start_offset + ehdr->e_shoff
417 + ehdr->e_shentsize * shnum);
418 (void) msync (msync_start, msync_end - msync_start, MS_SYNC);
419
420 return 0;
421 }
422
423
424 /* Size of the buffer we use to generate the blocks of fill bytes. */
425 #define FILLBUFSIZE 4096
426
427 /* If we have to convert the section buffer contents we have to use
428 temporary buffer. Only buffers up to MAX_TMPBUF bytes are allocated
429 on the stack. */
430 #define MAX_TMPBUF 32768
431
432
433 /* Helper function to write out fill bytes. */
434 static int
fill(int fd,off_t pos,size_t len,char * fillbuf,size_t * filledp)435 fill (int fd, off_t pos, size_t len, char *fillbuf, size_t *filledp)
436 {
437 size_t filled = *filledp;
438 size_t fill_len = MIN (len, FILLBUFSIZE);
439
440 if (unlikely (fill_len > filled) && filled < FILLBUFSIZE)
441 {
442 /* Initialize a few more bytes. */
443 memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled);
444 *filledp = filled = fill_len;
445 }
446
447 do
448 {
449 /* This many bytes we want to write in this round. */
450 size_t n = MIN (filled, len);
451
452 if (unlikely ((size_t) pwrite_retry (fd, fillbuf, n, pos) != n))
453 {
454 __libelf_seterrno (ELF_E_WRITE_ERROR);
455 return 1;
456 }
457
458 pos += n;
459 len -= n;
460 }
461 while (len > 0);
462
463 return 0;
464 }
465
466
467 int
468 internal_function
__elfw2(LIBELFBITS,updatefile)469 __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
470 {
471 char fillbuf[FILLBUFSIZE];
472 size_t filled = 0;
473
474 /* We need the ELF header several times. */
475 ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
476
477 /* Write out the ELF header. */
478 if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
479 {
480 ElfW2(LIBELFBITS,Ehdr) tmp_ehdr;
481 ElfW2(LIBELFBITS,Ehdr) *out_ehdr = ehdr;
482
483 /* If the type sizes should be different at some time we have to
484 rewrite this code. */
485 assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
486 == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
487
488 if (unlikely (change_bo))
489 {
490 /* Today there is only one version of the ELF header. */
491 #if EV_NUM != 2
492 xfct_t fctp;
493 fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
494 #else
495 # undef fctp
496 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
497 #endif
498
499 /* Write the converted ELF header in a temporary buffer. */
500 (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
501
502 /* This is the buffer we want to write. */
503 out_ehdr = &tmp_ehdr;
504 }
505
506 /* Write out the ELF header. */
507 if (unlikely (pwrite_retry (elf->fildes, out_ehdr,
508 sizeof (ElfW2(LIBELFBITS,Ehdr)), 0)
509 != sizeof (ElfW2(LIBELFBITS,Ehdr))))
510 {
511 __libelf_seterrno (ELF_E_WRITE_ERROR);
512 return 1;
513 }
514
515 elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
516 }
517
518 /* If the type sizes should be different at some time we have to
519 rewrite this code. */
520 assert (sizeof (ElfW2(LIBELFBITS,Phdr))
521 == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
522
523 /* Write out the program header table. */
524 if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
525 && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
526 & ELF_F_DIRTY))
527 {
528 ElfW2(LIBELFBITS,Phdr) *tmp_phdr = NULL;
529 ElfW2(LIBELFBITS,Phdr) *out_phdr = elf->state.ELFW(elf,LIBELFBITS).phdr;
530
531 /* Maybe the user wants a gap between the ELF header and the program
532 header. */
533 if (ehdr->e_phoff > ehdr->e_ehsize
534 && unlikely (fill (elf->fildes, ehdr->e_ehsize,
535 ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled)
536 != 0))
537 return 1;
538
539 if (unlikely (change_bo))
540 {
541 /* Today there is only one version of the ELF header. */
542 #if EV_NUM != 2
543 xfct_t fctp;
544 fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
545 #else
546 # undef fctp
547 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
548 #endif
549
550 /* Allocate sufficient memory. */
551 tmp_phdr = (ElfW2(LIBELFBITS,Phdr) *)
552 malloc (sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
553 if (tmp_phdr == NULL)
554 {
555 __libelf_seterrno (ELF_E_NOMEM);
556 return 1;
557 }
558
559 /* Write the converted ELF header in a temporary buffer. */
560 (*fctp) (tmp_phdr, elf->state.ELFW(elf,LIBELFBITS).phdr,
561 sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum, 1);
562
563 /* This is the buffer we want to write. */
564 out_phdr = tmp_phdr;
565 }
566
567 /* Write out the ELF header. */
568 size_t phdr_size = sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum;
569 if (unlikely ((size_t) pwrite_retry (elf->fildes, out_phdr,
570 phdr_size, ehdr->e_phoff)
571 != phdr_size))
572 {
573 __libelf_seterrno (ELF_E_WRITE_ERROR);
574 return 1;
575 }
576
577 /* This is a no-op we we have not allocated any memory. */
578 free (tmp_phdr);
579
580 elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
581 }
582
583 /* From now on we have to keep track of the last position to eventually
584 fill the gaps with the prescribed fill byte. */
585 off_t last_offset;
586 if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
587 last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
588 else
589 last_offset = (ehdr->e_phoff
590 + sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
591
592 /* Write all the sections. Well, only those which are modified. */
593 if (shnum > 0)
594 {
595 off_t shdr_offset = elf->start_offset + ehdr->e_shoff;
596 #if EV_NUM != 2
597 xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
598 #else
599 # undef shdr_fctp
600 # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
601 #endif
602
603 ElfW2(LIBELFBITS,Shdr) *shdr_data;
604 if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
605 shdr_data = (ElfW2(LIBELFBITS,Shdr) *)
606 alloca (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
607 else
608 shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr;
609 int shdr_flags = elf->flags;
610
611 /* Get all sections into the array and sort them. */
612 Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
613 Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *));
614 sort_sections (scns, list);
615
616 for (size_t cnt = 0; cnt < shnum; ++cnt)
617 {
618 Elf_Scn *scn = scns[cnt];
619
620 ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
621
622 off_t scn_start = elf->start_offset + shdr->sh_offset;
623 Elf_Data_List *dl = &scn->data_list;
624
625 if (shdr->sh_type != SHT_NOBITS && scn->data_list_rear != NULL
626 && scn->index != 0)
627 do
628 {
629 if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
630 {
631 char tmpbuf[MAX_TMPBUF];
632 void *buf = dl->data.d.d_buf;
633
634 if (scn_start + dl->data.d.d_off > last_offset)
635 {
636 if (unlikely (fill (elf->fildes, last_offset,
637 (scn_start + dl->data.d.d_off)
638 - last_offset, fillbuf,
639 &filled) != 0))
640 return 1;
641 }
642
643 /* Let it go backward if the sections use a bogus
644 layout with overlaps. We'll overwrite the stupid
645 user's section data with the latest one, rather than
646 crashing. */
647
648 last_offset = scn_start + dl->data.d.d_off;
649
650 if (unlikely (change_bo))
651 {
652 #if EV_NUM != 2
653 xfct_t fctp;
654 fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
655 #else
656 # undef fctp
657 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
658 #endif
659
660 buf = tmpbuf;
661 if (dl->data.d.d_size > MAX_TMPBUF)
662 {
663 buf = malloc (dl->data.d.d_size);
664 if (buf == NULL)
665 {
666 __libelf_seterrno (ELF_E_NOMEM);
667 return 1;
668 }
669 }
670
671 /* Do the real work. */
672 (*fctp) (buf, dl->data.d.d_buf, dl->data.d.d_size, 1);
673 }
674
675 ssize_t n = pwrite_retry (elf->fildes, buf,
676 dl->data.d.d_size,
677 last_offset);
678 if (unlikely ((size_t) n != dl->data.d.d_size))
679 {
680 if (buf != dl->data.d.d_buf && buf != tmpbuf)
681 free (buf);
682
683 __libelf_seterrno (ELF_E_WRITE_ERROR);
684 return 1;
685 }
686
687 if (buf != dl->data.d.d_buf && buf != tmpbuf)
688 free (buf);
689 }
690
691 last_offset += dl->data.d.d_size;
692
693 dl->flags &= ~ELF_F_DIRTY;
694
695 dl = dl->next;
696 }
697 while (dl != NULL);
698 else if (shdr->sh_type != SHT_NOBITS && scn->index != 0)
699 last_offset = scn_start + shdr->sh_size;
700
701 /* Collect the section header table information. */
702 if (unlikely (change_bo))
703 (*shdr_fctp) (&shdr_data[scn->index],
704 scn->shdr.ELFW(e,LIBELFBITS),
705 sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
706 else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
707 memcpy (&shdr_data[scn->index], scn->shdr.ELFW(e,LIBELFBITS),
708 sizeof (ElfW2(LIBELFBITS,Shdr)));
709
710 shdr_flags |= scn->shdr_flags;
711 scn->shdr_flags &= ~ELF_F_DIRTY;
712 }
713
714 /* Fill the gap between last section and section header table if
715 necessary. */
716 if ((elf->flags & ELF_F_DIRTY) && last_offset < shdr_offset
717 && unlikely (fill (elf->fildes, last_offset,
718 shdr_offset - last_offset,
719 fillbuf, &filled) != 0))
720 return 1;
721
722 /* Write out the section header table. */
723 if (shdr_flags & ELF_F_DIRTY
724 && unlikely ((size_t) pwrite_retry (elf->fildes, shdr_data,
725 sizeof (ElfW2(LIBELFBITS,Shdr))
726 * shnum, shdr_offset)
727 != sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum))
728 {
729 __libelf_seterrno (ELF_E_WRITE_ERROR);
730 return 1;
731 }
732 }
733
734 /* That was the last part. Clear the overall flag. */
735 elf->flags &= ~ELF_F_DIRTY;
736
737 return 0;
738 }
739