• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
compare_sections(const void * a,const void * b)50 compare_sections (const void *a, const void *b)
51 {
52   const Elf_Scn **scna = (const Elf_Scn **) a;
53   const Elf_Scn **scnb = (const Elf_Scn **) b;
54 
55   if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
56       < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
57     return -1;
58 
59   if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset
60       > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset)
61     return 1;
62 
63   if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
64       < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
65     return -1;
66 
67   if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size
68       > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size)
69     return 1;
70 
71   if ((*scna)->index < (*scnb)->index)
72     return -1;
73 
74   if ((*scna)->index > (*scnb)->index)
75     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
sort_sections(Elf_Scn ** scns,Elf_ScnList * list)88 sort_sections (Elf_Scn **scns, Elf_ScnList *list)
89 {
90   Elf_Scn **scnp = scns;
91   do
92     for (size_t cnt = 0; cnt < list->cnt; ++cnt)
93       *scnp++ = &list->data[cnt];
94   while ((list = list->next) != NULL);
95 
96   qsort (scns, scnp - scns, sizeof (*scns), compare_sections);
97 }
98 
99 
100 static inline void
fill_mmap(size_t offset,char * last_position,char * scn_start,char * const shdr_start,char * const shdr_end)101 fill_mmap (size_t offset, char *last_position, char *scn_start,
102            char *const shdr_start, char *const shdr_end)
103 {
104   size_t written = 0;
105 
106   if (last_position < shdr_start)
107     {
108       written = MIN (scn_start + offset - last_position,
109                      shdr_start - last_position);
110 
111       memset (last_position, __libelf_fill_byte, written);
112     }
113 
114   if (last_position + written != scn_start + offset
115       && shdr_end < scn_start + offset)
116     {
117       char *fill_start = MAX (shdr_end, scn_start);
118       memset (fill_start, __libelf_fill_byte,
119               scn_start + offset - fill_start);
120     }
121 }
122 
123 int
124 internal_function
__elfw2(LIBELFBITS,updatemmap)125 __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
126 {
127   bool previous_scn_changed = false;
128 
129   /* We need the ELF header several times.  */
130   ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
131 
132   /* Write out the ELF header.  */
133   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       assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
138 	      == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
139 
140       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 	  (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
148 		   sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
149 	}
150       else if (elf->map_address + elf->start_offset != ehdr)
151 	memcpy (elf->map_address + elf->start_offset, ehdr,
152 		sizeof (ElfW2(LIBELFBITS,Ehdr)));
153 
154       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       previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
159     }
160 
161   size_t phnum;
162   if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
163     return -1;
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 #undef fctp
185 #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
186 
187 	  /* Do the real work.  */
188 	  (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
189 		   elf->state.ELFW(elf,LIBELFBITS).phdr,
190 		   sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
191 	}
192       else
193 	memmove (elf->map_address + elf->start_offset + ehdr->e_phoff,
194 		elf->state.ELFW(elf,LIBELFBITS).phdr,
195 		sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
196 
197       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       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   char *last_position = ((char *) elf->map_address + elf->start_offset
207 			 + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
208 				ehdr->e_phoff)
209 			 + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));
210 
211   /* Write all the sections.  Well, only those which are modified.  */
212   if (shnum > 0)
213     {
214       if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *)))
215 	return 1;
216 
217       Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
218       Elf_Scn **scns = malloc (shnum * sizeof (Elf_Scn *));
219       if (unlikely (scns == NULL))
220 	{
221 	  __libelf_seterrno (ELF_E_NOMEM);
222 	  return -1;
223 	}
224       char *const shdr_start = ((char *) elf->map_address + elf->start_offset
225 				+ ehdr->e_shoff);
226       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       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       for (size_t cnt = 0; cnt < shnum; ++cnt)
238 	{
239 	  Elf_Scn *scn = scns[cnt];
240 
241 	  if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
242 	      && (scn->shdr_flags & ELF_F_MALLOCED) == 0
243 	      && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
244 	    {
245 	      assert ((char *) elf->map_address + elf->start_offset
246 		      < (char *) scn->shdr.ELFW(e,LIBELFBITS));
247 	      assert ((char *) scn->shdr.ELFW(e,LIBELFBITS)
248 		      < ((char *) elf->map_address + elf->start_offset
249 			 + elf->maximum_size));
250 
251 	      void *p = malloc (sizeof (ElfW2(LIBELFBITS,Shdr)));
252 	      if (unlikely (p == NULL))
253 		{
254 		  free (scns);
255 		  __libelf_seterrno (ELF_E_NOMEM);
256 		  return -1;
257 		}
258 	      scn->shdr.ELFW(e,LIBELFBITS)
259 		= 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 	  if (((char *) elf->map_address + elf->start_offset
270 	       <= (char  *) scn->data_list.data.d.d_buf)
271 	      && ((char *) scn->data_list.data.d.d_buf
272 		  < ((char *) elf->map_address + elf->start_offset
273 		     + elf->maximum_size))
274 	      && (((char *) elf->map_address + elf->start_offset
275 		   + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset)
276 		  > (char *) scn->data_list.data.d.d_buf))
277 	    {
278 	      void *p = malloc (scn->data_list.data.d.d_size);
279 	      if (unlikely (p == NULL))
280 		{
281 		  free (scns);
282 		  __libelf_seterrno (ELF_E_NOMEM);
283 		  return -1;
284 		}
285 	      scn->data_list.data.d.d_buf = scn->data_base
286 		= 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       for (size_t cnt = 0; cnt < shnum; ++cnt)
294 	{
295 	  Elf_Scn *scn = scns[cnt];
296 	  if (scn->index == 0)
297 	    {
298 	      /* The dummy section header entry.  It should not be
299 		 possible to mark this "section" as dirty.  */
300 	      assert ((scn->flags & ELF_F_DIRTY) == 0);
301 	      continue;
302 	    }
303 
304 	  ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
305 	  if (shdr->sh_type == SHT_NOBITS)
306 	    goto next;
307 
308 	  char *scn_start = ((char *) elf->map_address
309 			     + elf->start_offset + shdr->sh_offset);
310 	  Elf_Data_List *dl = &scn->data_list;
311 	  bool scn_changed = false;
312 
313 	  if (scn->data_list_rear != NULL)
314 	    do
315 	      {
316 		assert (dl->data.d.d_off >= 0);
317 		assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size);
318 		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 		if (scn_start + dl->data.d.d_off > last_position
323 		    && (dl->data.d.d_off == 0
324 			|| ((scn->flags | dl->flags | elf->flags)
325 			    & ELF_F_DIRTY) != 0))
326 		  {
327 		    fill_mmap (dl->data.d.d_off, last_position, scn_start,
328 		               shdr_start, shdr_end);
329 		  }
330 
331 		last_position = scn_start + dl->data.d.d_off;
332 
333 		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 		    if (unlikely (change_bo
341 				  && dl->data.d.d_size != 0
342 				  && dl->data.d.d_type != ELF_T_BYTE))
343 		      {
344 #undef fctp
345 #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
346 
347 			size_t align;
348 			align = __libelf_type_align (ELFW(ELFCLASS,LIBELFBITS),
349 						     dl->data.d.d_type);
350 			if ((((uintptr_t) last_position)
351 			     & (uintptr_t) (align - 1)) == 0)
352 			  {
353 			    /* No need to copy, we can convert directly.  */
354 			    (*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 			    size_t size = dl->data.d.d_size;
365 			    void *converted;
366 			    if (align < sizeof (void *))
367 			      converted = malloc (size);
368 			    else
369 			      {
370 				int res;
371 				res = posix_memalign (&converted, align, size);
372 				if (res != 0)
373 				  converted = NULL;
374 			      }
375 
376 			    if (converted == NULL)
377 			      {
378 				free (scns);
379 				__libelf_seterrno (ELF_E_NOMEM);
380 				return 1;
381 			      }
382 
383 			    (*fctp) (converted, dl->data.d.d_buf, size, 1);
384 
385 			    /* And then write it to the mmapped file.  */
386 			    memcpy (last_position, converted, size);
387 			    free (converted);
388 			  }
389 
390 			last_position += dl->data.d.d_size;
391 		      }
392 		    else if (dl->data.d.d_size != 0)
393 		      {
394 			memmove (last_position, dl->data.d.d_buf,
395 				 dl->data.d.d_size);
396 			last_position += dl->data.d.d_size;
397 		      }
398 
399 		    scn_changed = true;
400 		  }
401 		else
402 		  last_position += dl->data.d.d_size;
403 
404 		assert (scn_start + dl->data.d.d_off + dl->data.d.d_size
405 			== last_position);
406 
407 		dl->flags &= ~ELF_F_DIRTY;
408 
409 		dl = dl->next;
410 	      }
411 	    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 	      if (scn_start > last_position && previous_scn_changed)
417 		fill_mmap (0, last_position, scn_start,
418 		           shdr_start, shdr_end);
419 
420 	      /* We have to trust the existing section header information.  */
421 	      last_position = scn_start + shdr->sh_size;
422 	    }
423 
424 
425 	  previous_scn_changed = scn_changed;
426 	next:
427 	  scn->flags &= ~ELF_F_DIRTY;
428 	}
429 
430       /* Fill the gap between last section and section header table if
431 	 necessary.  */
432       if ((elf->flags & ELF_F_DIRTY)
433 	  && last_position < ((char *) elf->map_address + elf->start_offset
434 			      + ehdr->e_shoff))
435 	memset (last_position, __libelf_fill_byte,
436 		(char *) elf->map_address + elf->start_offset + ehdr->e_shoff
437 		- last_position);
438 
439       /* Write the section header table entry if necessary.  */
440       for (size_t cnt = 0; cnt < shnum; ++cnt)
441 	{
442 	  Elf_Scn *scn = scns[cnt];
443 
444 	  if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY)
445 	    {
446 	      if (unlikely (change_bo))
447 		(*shdr_fctp) (&shdr_dest[scn->index],
448 			      scn->shdr.ELFW(e,LIBELFBITS),
449 			      sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
450 	      else
451 		memcpy (&shdr_dest[scn->index],
452 			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 	      if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
459 		  && (scn->shdr_flags & ELF_F_MALLOCED) == 0
460 		  && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
461 		{
462 		  free (scn->shdr.ELFW(e,LIBELFBITS));
463 		  scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index];
464 		}
465 
466 	      scn->shdr_flags &= ~ELF_F_DIRTY;
467 	    }
468 	}
469       free (scns);
470     }
471 
472   /* That was the last part.  Clear the overall flag.  */
473   elf->flags &= ~ELF_F_DIRTY;
474 
475   /* Make sure the content hits the disk.  */
476   char *msync_start = ((char *) elf->map_address
477 		       + (elf->start_offset & ~(sysconf (_SC_PAGESIZE) - 1)));
478   char *msync_end = ((char *) elf->map_address
479 		     + elf->start_offset + ehdr->e_shoff
480 		     + ehdr->e_shentsize * shnum);
481   (void) msync (msync_start, msync_end - msync_start, MS_SYNC);
482 
483   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
fill(int fd,int64_t pos,size_t len,char * fillbuf,size_t * filledp)498 fill (int fd, int64_t pos, size_t len, char *fillbuf, size_t *filledp)
499 {
500   size_t filled = *filledp;
501   size_t fill_len = MIN (len, FILLBUFSIZE);
502 
503   if (unlikely (fill_len > filled) && filled < FILLBUFSIZE)
504     {
505       /* Initialize a few more bytes.  */
506       memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled);
507       *filledp = filled = fill_len;
508     }
509 
510   do
511     {
512       /* This many bytes we want to write in this round.  */
513       size_t n = MIN (filled, len);
514 
515       if (unlikely ((size_t) pwrite_retry (fd, fillbuf, n, pos) != n))
516 	{
517 	  __libelf_seterrno (ELF_E_WRITE_ERROR);
518 	  return 1;
519 	}
520 
521       pos += n;
522       len -= n;
523     }
524   while (len > 0);
525 
526   return 0;
527 }
528 
529 
530 int
531 internal_function
__elfw2(LIBELFBITS,updatefile)532 __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
533 {
534   char fillbuf[FILLBUFSIZE];
535   size_t filled = 0;
536   bool previous_scn_changed = false;
537 
538   /* We need the ELF header several times.  */
539   ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
540 
541   /* Write out the ELF header.  */
542   if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
543     {
544       ElfW2(LIBELFBITS,Ehdr) tmp_ehdr;
545       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       assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
550 	      == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
551 
552       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 	  (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
560 
561 	  /* This is the buffer we want to write.  */
562 	  out_ehdr = &tmp_ehdr;
563 	}
564 
565       /* Write out the ELF header.  */
566       if (unlikely (pwrite_retry (elf->fildes, out_ehdr,
567 				  sizeof (ElfW2(LIBELFBITS,Ehdr)), 0)
568 		    != sizeof (ElfW2(LIBELFBITS,Ehdr))))
569 	{
570 	  __libelf_seterrno (ELF_E_WRITE_ERROR);
571 	  return 1;
572 	}
573 
574       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       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   assert (sizeof (ElfW2(LIBELFBITS,Phdr))
584 	  == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
585 
586   size_t phnum;
587   if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
588     return -1;
589 
590   /* Write out the program header table.  */
591   if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
592       && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
593 	  & ELF_F_DIRTY))
594     {
595       ElfW2(LIBELFBITS,Phdr) *tmp_phdr = NULL;
596       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       if (ehdr->e_phoff > ehdr->e_ehsize
601 	  && unlikely (fill (elf->fildes, ehdr->e_ehsize,
602 			     ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled)
603 		       != 0))
604 	return 1;
605 
606       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 	  tmp_phdr = (ElfW2(LIBELFBITS,Phdr) *)
614 	    malloc (sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
615 	  if (unlikely (tmp_phdr == NULL))
616 	    {
617 	      __libelf_seterrno (ELF_E_NOMEM);
618 	      return 1;
619 	    }
620 
621 	  /* Write the converted ELF header in a temporary buffer.  */
622 	  (*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 	  out_phdr = tmp_phdr;
627 	}
628 
629       /* Write out the ELF header.  */
630       size_t phdr_size = sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum;
631       if (unlikely ((size_t) pwrite_retry (elf->fildes, out_phdr,
632 					   phdr_size, ehdr->e_phoff)
633 		    != phdr_size))
634 	{
635 	  __libelf_seterrno (ELF_E_WRITE_ERROR);
636 	  return 1;
637 	}
638 
639       /* This is a no-op we we have not allocated any memory.  */
640       free (tmp_phdr);
641 
642       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       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   int64_t last_offset;
652   if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
653     last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
654   else
655     last_offset = (ehdr->e_phoff + sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);
656 
657   /* Write all the sections.  Well, only those which are modified.  */
658   if (shnum > 0)
659     {
660       if (unlikely (shnum > SIZE_MAX / (sizeof (Elf_Scn *)
661 					+ sizeof (ElfW2(LIBELFBITS,Shdr)))))
662 	return 1;
663 
664       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       ElfW2(LIBELFBITS,Shdr) *shdr_data;
669       ElfW2(LIBELFBITS,Shdr) *shdr_data_mem = NULL;
670       if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
671 	  || (elf->flags & ELF_F_DIRTY))
672 	{
673 	  shdr_data_mem = (ElfW2(LIBELFBITS,Shdr) *)
674 	    malloc (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
675 	  if (unlikely (shdr_data_mem == NULL))
676 	    {
677 	      __libelf_seterrno (ELF_E_NOMEM);
678 	      return -1;
679 	    }
680 	  shdr_data = shdr_data_mem;
681 	}
682       else
683 	shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr;
684       int shdr_flags = elf->flags;
685 
686       /* Get all sections into the array and sort them.  */
687       Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
688       Elf_Scn **scns = malloc (shnum * sizeof (Elf_Scn *));
689       if (unlikely (scns == NULL))
690 	{
691 	  free (shdr_data_mem);
692 	  __libelf_seterrno (ELF_E_NOMEM);
693 	  return -1;
694 	}
695       sort_sections (scns, list);
696 
697       for (size_t cnt = 0; cnt < shnum; ++cnt)
698 	{
699 	  Elf_Scn *scn = scns[cnt];
700 	  if (scn->index == 0)
701 	    {
702 	      /* The dummy section header entry.  It should not be
703 		 possible to mark this "section" as dirty.  */
704 	      assert ((scn->flags & ELF_F_DIRTY) == 0);
705 	      goto next;
706 	    }
707 
708 	  ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
709 	  if (shdr->sh_type == SHT_NOBITS)
710 	    goto next;
711 
712 	  int64_t scn_start = elf->start_offset + shdr->sh_offset;
713 	  Elf_Data_List *dl = &scn->data_list;
714 	  bool scn_changed = false;
715 
716 	  if (scn->data_list_rear != NULL)
717 	    do
718 	      {
719 		/* If there is a gap, fill it.  */
720 		if (scn_start + dl->data.d.d_off > last_offset
721 		    && ((previous_scn_changed && dl->data.d.d_off == 0)
722 			|| ((scn->flags | dl->flags | elf->flags)
723 			    & ELF_F_DIRTY) != 0))
724 		  {
725 		    if (unlikely (fill (elf->fildes, last_offset,
726 					(scn_start + dl->data.d.d_off)
727 					- last_offset, fillbuf,
728 					&filled) != 0))
729 		      {
730 		      fail_free:
731 			free (shdr_data_mem);
732 			free (scns);
733 			return 1;
734 		      }
735 		  }
736 
737 		last_offset = scn_start + dl->data.d.d_off;
738 
739 		if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
740 		  {
741 		    char tmpbuf[MAX_TMPBUF];
742 		    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 		    if (unlikely (change_bo))
750 		      {
751 #undef fctp
752 #define fctp __elf_xfctstom[ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
753 
754 			buf = tmpbuf;
755 			if (dl->data.d.d_size > MAX_TMPBUF)
756 			  {
757 			    buf = malloc (dl->data.d.d_size);
758 			    if (unlikely (buf == NULL))
759 			      {
760 				__libelf_seterrno (ELF_E_NOMEM);
761 				goto fail_free;
762 			      }
763 			  }
764 
765 			/* Do the real work.  */
766 			(*fctp) (buf, dl->data.d.d_buf, dl->data.d.d_size, 1);
767 		      }
768 
769 		    ssize_t n = pwrite_retry (elf->fildes, buf,
770 					      dl->data.d.d_size,
771 					      last_offset);
772 		    if (unlikely ((size_t) n != dl->data.d.d_size))
773 		      {
774 			if (buf != dl->data.d.d_buf && buf != tmpbuf)
775 			  free (buf);
776 
777 			__libelf_seterrno (ELF_E_WRITE_ERROR);
778 			goto fail_free;
779 		      }
780 
781 		    if (buf != dl->data.d.d_buf && buf != tmpbuf)
782 		      free (buf);
783 
784 		    scn_changed = true;
785 		  }
786 
787 		last_offset += dl->data.d.d_size;
788 
789 		dl->flags &= ~ELF_F_DIRTY;
790 
791 		dl = dl->next;
792 	      }
793 	    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 	      if (scn_start > last_offset && previous_scn_changed)
799 		{
800 		  if (unlikely (fill (elf->fildes, last_offset,
801 				      scn_start - last_offset, fillbuf,
802 				      &filled) != 0))
803 		    goto fail_free;
804 		}
805 
806 	      last_offset = scn_start + shdr->sh_size;
807 	    }
808 
809 	  previous_scn_changed = scn_changed;
810 	next:
811 	  /* Collect the section header table information.  */
812 	  if (unlikely (change_bo))
813 	    (*shdr_fctp) (&shdr_data[scn->index],
814 			  scn->shdr.ELFW(e,LIBELFBITS),
815 			  sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
816 	  else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL
817 		   || (elf->flags & ELF_F_DIRTY))
818 	    memcpy (&shdr_data[scn->index], scn->shdr.ELFW(e,LIBELFBITS),
819 		    sizeof (ElfW2(LIBELFBITS,Shdr)));
820 
821 	  shdr_flags |= scn->shdr_flags;
822 	  scn->shdr_flags &= ~ELF_F_DIRTY;
823 	}
824 
825       /* Fill the gap between last section and section header table if
826 	 necessary.  */
827       if ((elf->flags & ELF_F_DIRTY) && last_offset < shdr_offset
828 	  && unlikely (fill (elf->fildes, last_offset,
829 			     shdr_offset - last_offset,
830 			     fillbuf, &filled) != 0))
831 	goto fail_free;
832 
833       /* Write out the section header table.  */
834       if (shdr_flags & ELF_F_DIRTY
835 	  && 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 	  __libelf_seterrno (ELF_E_WRITE_ERROR);
841 	  goto fail_free;
842 	}
843 
844       free (shdr_data_mem);
845       free (scns);
846     }
847 
848   /* That was the last part.  Clear the overall flag.  */
849   elf->flags &= ~ELF_F_DIRTY;
850 
851   return 0;
852 }
853