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