Elfutils fiddles fo fix striptofile.
[platform/upstream/rpm.git] / elfutils / libelf / elf32_updatefile.c
1 /* Write changed data structures.
2    Copyright (C) 2000, 2001, 2002 Red Hat, Inc.
3    Written by Ulrich Drepper <drepper@redhat.com>, 2000.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <assert.h>
23 #include <libelf.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/param.h>
29
30 #include "libelfP.h"
31
32
33 #ifndef LIBELFBITS
34 # define LIBELFBITS 32
35 #endif
36
37
38 int
39 internal_function
40 __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
41 {
42   ElfW2(LIBELFBITS,Ehdr) *ehdr;
43   xfct_t fctp;
44   char *last_position;
45
46   /* We need the ELF header several times.  */
47   ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
48
49   /* Write out the ELF header.  */
50   if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
51     {
52       /* If the type sizes should be different at some time we have to
53          rewrite this code.  */
54       assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
55               == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
56
57       if (change_bo)
58         {
59           /* Today there is only one version of the ELF header.  */
60 #if EV_NUM != 2
61           fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
62 #else
63 # undef fctp
64 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
65 #endif
66
67           /* Do the real work.  */
68           (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
69                    sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
70         }
71       else
72         memcpy (elf->map_address + elf->start_offset, ehdr,
73                 sizeof (ElfW2(LIBELFBITS,Ehdr)));
74
75       elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
76     }
77
78   /* Write out the program header table.  */
79   if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
80       && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
81           & ELF_F_DIRTY))
82     {
83       /* If the type sizes should be different at some time we have to
84          rewrite this code.  */
85       assert (sizeof (ElfW2(LIBELFBITS,Phdr))
86               == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
87
88       /* Maybe the user wants a gap between the ELF header and the program
89          header.  */
90       if (ehdr->e_phoff > ehdr->e_ehsize)
91         memset (elf->map_address + elf->start_offset + ehdr->e_ehsize,
92                 __libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize);
93
94       if (change_bo)
95         {
96           /* Today there is only one version of the ELF header.  */
97 #if EV_NUM != 2
98           fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
99 #else
100 # undef fctp
101 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
102 #endif
103
104           /* Do the real work.  */
105           (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
106                    elf->state.ELFW(elf,LIBELFBITS).phdr,
107                    sizeof (ElfW2(LIBELFBITS,Phdr)), ehdr->e_phnum);
108         }
109       else
110         memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff,
111                 elf->state.ELFW(elf,LIBELFBITS).phdr,
112                 sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
113
114       elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
115     }
116
117   /* From now on we have to keep track of the last position to eventually
118      fill the gaps with the prescribed fill byte.  */
119   last_position = ((char *) elf->map_address + elf->start_offset
120                    + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
121                           ehdr->e_phoff)
122                    + elf_typesize (LIBELFBITS, ELF_T_PHDR, ehdr->e_phnum));
123
124   /* Write all the sections.  Well, only those which are modified.  */
125   if (shnum > 0)
126     {
127       ElfW2(LIBELFBITS,Shdr) *shdr_dest;
128       Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
129
130 #if EV_NUM != 2
131       xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
132 #else
133 # undef shdr_fctp
134 # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
135 #endif
136       shdr_dest = (ElfW2(LIBELFBITS,Shdr) *)
137         ((char *) elf->map_address + elf->start_offset + ehdr->e_shoff);
138
139       do
140         {
141           int cnt;
142
143           for (cnt = 0; cnt < list->cnt; ++cnt)
144             {
145               ElfW2(LIBELFBITS,Shdr) *shdr;
146               char *scn_start;
147               Elf_Data_List *dl;
148
149               shdr = list->data[cnt].shdr.ELFW(e,LIBELFBITS);
150
151               scn_start = ((char *) elf->map_address
152                            + elf->start_offset + shdr->sh_offset);
153               dl = &list->data[cnt].data_list;
154
155               if (shdr->sh_type != SHT_NOBITS
156                   && list->data[cnt].data_list_rear != NULL)
157                 do
158                   {
159                     if ((list->data[cnt].flags | dl->flags
160                          | elf->flags) & ELF_F_DIRTY)
161                       {
162                         if (scn_start + dl->data.d.d_off != last_position)
163                           {
164                             if (scn_start + dl->data.d.d_off > last_position)
165                               memset (last_position, __libelf_fill_byte,
166                                       scn_start + dl->data.d.d_off
167                                       - last_position);
168                             last_position = scn_start + dl->data.d.d_off;
169                           }
170
171                         if (change_bo)
172                           {
173                             size_t recsize;
174
175 #if EV_NUM != 2
176                             fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
177                             recsize = __libelf_type_sizes[dl->d_version - 1][ELFW(ELFCLASS,LIBELFBITS) - 1][dl->data.d.d_type];
178 #else
179 # undef fctp
180                             fctp = __elf_xfctstom[0][0][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
181                             recsize = __libelf_type_sizes[0][ELFW(ELFCLASS,LIBELFBITS) - 1][dl->data.d.d_type];
182 #endif
183
184                             /* Make sure the data size matches the
185                                record size.  */
186                             assert (dl->data.d.d_size % recsize == 0);
187
188                             /* Do the real work.  */
189                             (*fctp) (last_position, dl->data.d.d_buf, recsize,
190                                      dl->data.d.d_size / recsize);
191
192                             last_position += dl->data.d.d_size;
193                           }
194                         else
195                           last_position = mempcpy (last_position,
196                                                    dl->data.d.d_buf,
197                                                    dl->data.d.d_size);
198                       }
199                     else
200                       last_position += dl->data.d.d_size;
201
202                     dl->flags &= ~ELF_F_DIRTY;
203
204                     dl = dl->next;
205                   }
206                 while (dl != NULL);
207
208               /* Write the section header table entry if necessary.  */
209               if ((list->data[cnt].shdr_flags | elf->flags) & ELF_F_DIRTY)
210                 {
211                   if (change_bo)
212                     (*shdr_fctp) (shdr_dest,
213                                   list->data[cnt].shdr.ELFW(e,LIBELFBITS),
214                                   sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
215                   else
216                     memcpy (shdr_dest, list->data[cnt].shdr.ELFW(e,LIBELFBITS),
217                             sizeof (ElfW2(LIBELFBITS,Shdr)));
218
219                   list->data[cnt].shdr_flags  &= ~ELF_F_DIRTY;
220                 }
221               ++shdr_dest;
222
223               list->data[cnt].flags &= ~ELF_F_DIRTY;
224             }
225
226           assert (list->next == NULL || list->cnt == list->max);
227         }
228       while ((list = list->next) != NULL);
229
230
231       /* Fill the gap between last section and section header table if
232          necessary.  */
233       if ((elf->flags & ELF_F_DIRTY)
234           && last_position != ((char *) elf->map_address + elf->start_offset
235                                + ehdr->e_shoff))
236         {
237           assert ((char *) elf->map_address + elf->start_offset + ehdr->e_shoff
238                   > last_position);
239           memset (last_position, __libelf_fill_byte,
240                   (char *) elf->map_address + elf->start_offset + ehdr->e_shoff
241                   - last_position);
242         }
243     }
244
245   /* That was the last part.  Clear the overall flag.  */
246   elf->flags &= ~ELF_F_DIRTY;
247
248   return 0;
249 }
250
251
252 /* Size of the buffer we use to generate the blocks of fill bytes.  */
253 #define FILLBUFSIZE     4096
254
255 /* If we have to convert the section buffer contents we have to use
256    temporary buffer.  Only buffers up to MAX_TMPBUF bytes are allocated
257    on the stack.  */
258 #define MAX_TMPBUF      32768
259
260 /* Helper function to write out fill bytes.  */
261 static int
262 fill (int fd, off_t pos, size_t len, char *fillbuf, size_t *filledp)
263         /*@modifies fillbuf, filledp @*/
264 {
265   size_t filled = *filledp;
266   size_t fill_len = MIN (len, FILLBUFSIZE);
267
268   if (unlikely (fill_len > filled) && (filled < FILLBUFSIZE))
269     {
270       /* Initialize a few more bytes.  */
271       memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled);
272       *filledp = filled = fill_len;
273     }
274
275   do
276     {
277       /* This many bytes we want to write in this round.  */
278       size_t n = MIN (filled, len);
279
280       if (unlikely (pwrite (fd, fillbuf, n, pos) != n))
281         {
282           __libelf_seterrno (ELF_E_WRITE_ERROR);
283           return 1;
284         }
285
286       pos += n;
287       len -= n;
288     }
289   while (len > 0);
290
291   return 0;
292 }
293
294
295 int
296 internal_function
297 __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
298 {
299   char fillbuf[FILLBUFSIZE];
300   size_t filled = 0;
301   ElfW2(LIBELFBITS,Ehdr) *ehdr;
302 #if EV_NUM != 2
303   xfct_t fctp;
304 #endif
305   off_t last_offset;
306
307   /* We need the ELF header several times.  */
308   ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
309
310   /* Write out the ELF header.  */
311   if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
312     {
313       ElfW2(LIBELFBITS,Ehdr) tmp_ehdr;
314       ElfW2(LIBELFBITS,Ehdr) *out_ehdr = ehdr;
315
316       /* If the type sizes should be different at some time we have to
317          rewrite this code.  */
318       assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
319               == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));
320
321       if (change_bo)
322         {
323           /* Today there is only one version of the ELF header.  */
324 #if EV_NUM != 2
325           fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
326 #else
327 # undef fctp
328 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
329 #endif
330
331           /* Write the converted ELF header in a temporary buffer.  */
332           (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
333
334           /* This is the buffer we want to write.  */
335           out_ehdr = &tmp_ehdr;
336         }
337
338       /* Write out the ELF header.  */
339       if (unlikely (pwrite (elf->fildes, out_ehdr,
340                             sizeof (ElfW2(LIBELFBITS,Ehdr)), 0)
341                     != sizeof (ElfW2(LIBELFBITS,Ehdr))))
342         {
343           __libelf_seterrno (ELF_E_WRITE_ERROR);
344           return 1;
345         }
346
347       elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
348     }
349
350   /* If the type sizes should be different at some time we have to
351      rewrite this code.  */
352   assert (sizeof (ElfW2(LIBELFBITS,Phdr))
353           == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
354
355   /* Write out the program header table.  */
356   if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
357       && (elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags) & ELF_F_DIRTY)
358     {
359       ElfW2(LIBELFBITS,Phdr) tmp_phdr;
360       ElfW2(LIBELFBITS,Phdr) *out_phdr = elf->state.ELFW(elf,LIBELFBITS).phdr;
361
362       /* Maybe the user wants a gap between the ELF header and the program
363          header.  */
364       if (ehdr->e_phoff > ehdr->e_ehsize
365           && unlikely (fill (elf->fildes, ehdr->e_ehsize,
366                              ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled)
367                        != 0))
368         return 1;
369
370       if (change_bo)
371         {
372           /* Today there is only one version of the ELF header.  */
373 #if EV_NUM != 2
374           fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
375 #else
376 # undef fctp
377 # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
378 #endif
379
380           /* Write the converted ELF header in a temporary buffer.  */
381           (*fctp) (&tmp_phdr, elf->state.ELFW(elf,LIBELFBITS).phdr,
382                    sizeof (ElfW2(LIBELFBITS,Phdr)), ehdr->e_phnum);
383
384           /* This is the buffer we want to write.  */
385           out_phdr = &tmp_phdr;
386         }
387
388       /* Write out the ELF header.  */
389       if (unlikely (pwrite (elf->fildes, out_phdr,
390                             sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum,
391                             ehdr->e_phoff)
392                     != sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum))
393         {
394           __libelf_seterrno (ELF_E_WRITE_ERROR);
395           return 1;
396         }
397
398       elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
399     }
400
401   /* From now on we have to keep track of the last position to eventually
402      fill the gaps with the prescribed fill byte.  */
403   last_offset = (ehdr->e_phoff
404                  + sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
405
406   /* Write all the sections.  Well, only those which are modified.  */
407   if (shnum > 0)
408     {
409       off_t shdr_offset;
410       xfct_t shdr_fctp;
411       Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
412       ElfW2(LIBELFBITS,Shdr) *shdr_data;
413       ElfW2(LIBELFBITS,Shdr) *shdr_data_begin;
414       int shdr_flags;
415
416       shdr_offset = elf->start_offset + ehdr->e_shoff;
417 #if EV_NUM != 2
418       shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
419 #else
420 # undef shdr_fctp
421 # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
422 #endif
423
424       if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
425         shdr_data = (ElfW2(LIBELFBITS,Shdr) *)
426           alloca (shnum * sizeof (ElfW2(LIBELFBITS,Shdr)));
427       else
428         shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr;
429       shdr_data_begin = shdr_data;
430       shdr_flags = elf->flags;
431
432       do
433         {
434           int cnt;
435
436           for (cnt = 0; cnt < list->cnt; ++cnt)
437             {
438               ElfW2(LIBELFBITS,Shdr) *shdr;
439               off_t scn_start;
440               Elf_Data_List *dl;
441
442               shdr = list->data[cnt].shdr.ELFW(e,LIBELFBITS);
443
444               scn_start = elf->start_offset + shdr->sh_offset;
445               dl = &list->data[cnt].data_list;
446
447               if (shdr->sh_type != SHT_NOBITS
448                   && list->data[cnt].data_list_rear != NULL
449                   && list->data[cnt].index != 0)
450                 do
451                   {
452                     if ((list->data[cnt].flags | dl->flags
453                          | elf->flags) & ELF_F_DIRTY)
454                       {
455                         char tmpbuf[MAX_TMPBUF];
456                         void *buf = dl->data.d.d_buf;
457
458                         if (scn_start + dl->data.d.d_off != last_offset)
459                           {
460                             assert (last_offset
461                                     < scn_start + dl->data.d.d_off);
462
463                             if (unlikely (fill (elf->fildes, last_offset,
464                                                 (scn_start + dl->data.d.d_off)
465                                                 - last_offset, fillbuf,
466                                                 &filled) != 0))
467                               return 1;
468
469                             last_offset = scn_start + dl->data.d.d_off;
470                           }
471
472                         if (change_bo)
473                           {
474                             size_t recsize;
475
476 #if EV_NUM != 2
477                             fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
478                             recsize = __libelf_type_sizes[dl->d_version - 1][ELFW(ELFCLASS,LIBELFBITS) - 1][dl->data.d.d_type];
479 #else
480                             fctp = __elf_xfctstom[0][0][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
481                             recsize = __libelf_type_sizes[0][ELFW(ELFCLASS,LIBELFBITS) - 1][dl->data.d.d_type];
482 #endif
483
484                             /* Make sure the data size matches the
485                                record size.  */
486                             assert (dl->data.d.d_size % recsize == 0);
487
488                             buf = tmpbuf;
489                             if (dl->data.d.d_size > MAX_TMPBUF)
490                               {
491                                 buf = malloc (dl->data.d.d_size);
492                                 if (buf == NULL)
493                                   {
494                                     __libelf_seterrno (ELF_E_NOMEM);
495                                     return 1;
496                                   }
497                               }
498
499                             /* Do the real work.  */
500                             (*fctp) (buf, dl->data.d.d_buf, recsize,
501                                      dl->data.d.d_size / recsize);
502                           }
503
504                         if (unlikely (pwrite (elf->fildes, buf,
505                                               dl->data.d.d_size, last_offset)
506                                       != dl->data.d.d_size))
507                           {
508                             if (buf != dl->data.d.d_buf && buf != tmpbuf)
509                               free (buf);
510
511                             __libelf_seterrno (ELF_E_WRITE_ERROR);
512                             return 1;
513                           }
514
515                         if (buf != dl->data.d.d_buf && buf != tmpbuf)
516                           free (buf);
517                       }
518
519                     last_offset += dl->data.d.d_size;
520
521                     dl->flags &= ~ELF_F_DIRTY;
522
523                     dl = dl->next;
524                   }
525                 while (dl != NULL);
526
527               /* Collect the section header table information.  */
528               if (change_bo)
529                 (*shdr_fctp) (shdr_data++,
530                               list->data[cnt].shdr.ELFW(e,LIBELFBITS),
531                               sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
532               else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
533                 shdr_data = mempcpy (shdr_data,
534                                      list->data[cnt].shdr.ELFW(e,LIBELFBITS),
535                                      sizeof (ElfW2(LIBELFBITS,Shdr)));
536
537               shdr_flags |= list->data[cnt].shdr_flags;
538               list->data[cnt].shdr_flags  &= ~ELF_F_DIRTY;
539             }
540         }
541       while ((list = list->next) != NULL);
542
543       if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL)
544         assert (shdr_data == &shdr_data_begin[shnum]);
545
546       /* Write out the section header table.  */
547       if (shdr_flags & ELF_F_DIRTY
548           && unlikely (pwrite (elf->fildes, shdr_data_begin,
549                                sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum,
550                                shdr_offset)
551                        != sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum))
552         {
553           __libelf_seterrno (ELF_E_WRITE_ERROR);
554           return 1;
555         }
556     }
557
558   /* That was the last part.  Clear the overall flag.  */
559   elf->flags &= ~ELF_F_DIRTY;
560
561   return 0;
562 }