| /* Write changed data structures. |
| Copyright (C) 2000-2010, 2014 Red Hat, Inc. |
| This file is part of elfutils. |
| Written by Ulrich Drepper <drepper@redhat.com>, 2000. |
| |
| This file is free software; you can redistribute it and/or modify |
| it under the terms of either |
| |
| * the GNU Lesser General Public License as published by the Free |
| Software Foundation; either version 3 of the License, or (at |
| your option) any later version |
| |
| or |
| |
| * the GNU General Public License as published by the Free |
| Software Foundation; either version 2 of the License, or (at |
| your option) any later version |
| |
| or both in parallel, as here. |
| |
| elfutils is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received copies of the GNU General Public License and |
| the GNU Lesser General Public License along with this program. If |
| not, see <http://www.gnu.org/licenses/>. */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <libelf.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/mman.h> |
| #include <sys/param.h> |
| |
| #include <system.h> |
| #include "libelfP.h" |
| |
| |
| #ifndef LIBELFBITS |
| # define LIBELFBITS 32 |
| #endif |
| |
| |
| static int |
| compare_sections (const void *a, const void *b) |
| { |
| const Elf_Scn **scna = (const Elf_Scn **) a; |
| const Elf_Scn **scnb = (const Elf_Scn **) b; |
| |
| if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset |
| < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset) |
| return -1; |
| |
| if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_offset |
| > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_offset) |
| return 1; |
| |
| if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size |
| < (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size) |
| return -1; |
| |
| if ((*scna)->shdr.ELFW(e,LIBELFBITS)->sh_size |
| > (*scnb)->shdr.ELFW(e,LIBELFBITS)->sh_size) |
| return 1; |
| |
| if ((*scna)->index < (*scnb)->index) |
| return -1; |
| |
| if ((*scna)->index > (*scnb)->index) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| /* Insert the sections in the list into the provided array and sort |
| them according to their start offsets. For sections with equal |
| start offsets, the size is used; for sections with equal start |
| offsets and sizes, the section index is used. Sorting by size |
| ensures that zero-length sections are processed first, which |
| is what we want since they do not advance our file writing position. */ |
| static void |
| sort_sections (Elf_Scn **scns, Elf_ScnList *list) |
| { |
| Elf_Scn **scnp = scns; |
| do |
| for (size_t cnt = 0; cnt < list->cnt; ++cnt) |
| *scnp++ = &list->data[cnt]; |
| while ((list = list->next) != NULL); |
| |
| qsort (scns, scnp - scns, sizeof (*scns), compare_sections); |
| } |
| |
| |
| int |
| internal_function |
| __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) |
| { |
| bool previous_scn_changed = false; |
| |
| /* We need the ELF header several times. */ |
| ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr; |
| |
| /* Write out the ELF header. */ |
| if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY) |
| { |
| /* If the type sizes should be different at some time we have to |
| rewrite this code. */ |
| assert (sizeof (ElfW2(LIBELFBITS,Ehdr)) |
| == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1)); |
| |
| if (unlikely (change_bo)) |
| { |
| /* Today there is only one version of the ELF header. */ |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR] |
| #endif |
| |
| /* Do the real work. */ |
| (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr, |
| sizeof (ElfW2(LIBELFBITS,Ehdr)), 1); |
| } |
| else if (elf->map_address + elf->start_offset != ehdr) |
| memcpy (elf->map_address + elf->start_offset, ehdr, |
| sizeof (ElfW2(LIBELFBITS,Ehdr))); |
| |
| elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY; |
| |
| /* We start writing sections after the ELF header only if there is |
| no program header. */ |
| previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL; |
| } |
| |
| size_t phnum; |
| if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0)) |
| return -1; |
| |
| /* Write out the program header table. */ |
| if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL |
| && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags) |
| & ELF_F_DIRTY)) |
| { |
| /* If the type sizes should be different at some time we have to |
| rewrite this code. */ |
| assert (sizeof (ElfW2(LIBELFBITS,Phdr)) |
| == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1)); |
| |
| /* Maybe the user wants a gap between the ELF header and the program |
| header. */ |
| if (ehdr->e_phoff > ehdr->e_ehsize) |
| memset (elf->map_address + elf->start_offset + ehdr->e_ehsize, |
| __libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize); |
| |
| if (unlikely (change_bo)) |
| { |
| /* Today there is only one version of the ELF header. */ |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR] |
| #endif |
| |
| /* Do the real work. */ |
| (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff, |
| elf->state.ELFW(elf,LIBELFBITS).phdr, |
| sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1); |
| } |
| else |
| memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff, |
| elf->state.ELFW(elf,LIBELFBITS).phdr, |
| sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum); |
| |
| elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY; |
| |
| /* We modified the program header. Maybe this created a gap so |
| we have to write fill bytes, if necessary. */ |
| previous_scn_changed = true; |
| } |
| |
| /* From now on we have to keep track of the last position to eventually |
| fill the gaps with the prescribed fill byte. */ |
| char *last_position = ((char *) elf->map_address + elf->start_offset |
| + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1), |
| ehdr->e_phoff) |
| + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum)); |
| |
| /* Write all the sections. Well, only those which are modified. */ |
| if (shnum > 0) |
| { |
| if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *))) |
| return 1; |
| |
| Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns; |
| Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *)); |
| char *const shdr_start = ((char *) elf->map_address + elf->start_offset |
| + ehdr->e_shoff); |
| char *const shdr_end = shdr_start + ehdr->e_shnum * ehdr->e_shentsize; |
| |
| #if EV_NUM != 2 |
| xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]; |
| #else |
| # undef shdr_fctp |
| # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR] |
| #endif |
| #define shdr_dest ((ElfW2(LIBELFBITS,Shdr) *) shdr_start) |
| |
| /* Get all sections into the array and sort them. */ |
| sort_sections (scns, list); |
| |
| /* We possibly have to copy the section header data because moving |
| the sections might overwrite the data. */ |
| for (size_t cnt = 0; cnt < shnum; ++cnt) |
| { |
| Elf_Scn *scn = scns[cnt]; |
| |
| if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced |
| && (scn->shdr_flags & ELF_F_MALLOCED) == 0 |
| && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index]) |
| { |
| assert ((char *) elf->map_address + elf->start_offset |
| < (char *) scn->shdr.ELFW(e,LIBELFBITS)); |
| assert ((char *) scn->shdr.ELFW(e,LIBELFBITS) |
| < ((char *) elf->map_address + elf->start_offset |
| + elf->maximum_size)); |
| |
| void *p = alloca (sizeof (ElfW2(LIBELFBITS,Shdr))); |
| scn->shdr.ELFW(e,LIBELFBITS) |
| = memcpy (p, scn->shdr.ELFW(e,LIBELFBITS), |
| sizeof (ElfW2(LIBELFBITS,Shdr))); |
| } |
| |
| /* If the file is mmaped and the original position of the |
| section in the file is lower than the new position we |
| need to save the section content since otherwise it is |
| overwritten before it can be copied. If there are |
| multiple data segments in the list only the first can be |
| from the file. */ |
| if (((char *) elf->map_address + elf->start_offset |
| <= (char *) scn->data_list.data.d.d_buf) |
| && ((char *) scn->data_list.data.d.d_buf |
| < ((char *) elf->map_address + elf->start_offset |
| + elf->maximum_size)) |
| && (((char *) elf->map_address + elf->start_offset |
| + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset) |
| > (char *) scn->data_list.data.d.d_buf)) |
| { |
| void *p = malloc (scn->data_list.data.d.d_size); |
| if (p == NULL) |
| { |
| __libelf_seterrno (ELF_E_NOMEM); |
| return -1; |
| } |
| scn->data_list.data.d.d_buf = scn->data_base |
| = memcpy (p, scn->data_list.data.d.d_buf, |
| scn->data_list.data.d.d_size); |
| } |
| } |
| |
| /* Iterate over all the section in the order in which they |
| appear in the output file. */ |
| for (size_t cnt = 0; cnt < shnum; ++cnt) |
| { |
| Elf_Scn *scn = scns[cnt]; |
| if (scn->index == 0) |
| { |
| /* The dummy section header entry. It should not be |
| possible to mark this "section" as dirty. */ |
| assert ((scn->flags & ELF_F_DIRTY) == 0); |
| continue; |
| } |
| |
| ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS); |
| if (shdr->sh_type == SHT_NOBITS) |
| goto next; |
| |
| char *scn_start = ((char *) elf->map_address |
| + elf->start_offset + shdr->sh_offset); |
| Elf_Data_List *dl = &scn->data_list; |
| bool scn_changed = false; |
| |
| void fill_mmap (size_t offset) |
| { |
| size_t written = 0; |
| |
| if (last_position < shdr_start) |
| { |
| written = MIN (scn_start + offset - last_position, |
| shdr_start - last_position); |
| |
| memset (last_position, __libelf_fill_byte, written); |
| } |
| |
| if (last_position + written != scn_start + offset |
| && shdr_end < scn_start + offset) |
| { |
| char *fill_start = MAX (shdr_end, scn_start); |
| memset (fill_start, __libelf_fill_byte, |
| scn_start + offset - fill_start); |
| } |
| } |
| |
| if (scn->data_list_rear != NULL) |
| do |
| { |
| assert (dl->data.d.d_off >= 0); |
| assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size); |
| assert (dl->data.d.d_size <= (shdr->sh_size |
| - (GElf_Off) dl->data.d.d_off)); |
| |
| /* If there is a gap, fill it. */ |
| if (scn_start + dl->data.d.d_off > last_position |
| && (dl->data.d.d_off == 0 |
| || ((scn->flags | dl->flags | elf->flags) |
| & ELF_F_DIRTY) != 0)) |
| { |
| fill_mmap (dl->data.d.d_off); |
| last_position = scn_start + dl->data.d.d_off; |
| } |
| |
| if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY) |
| { |
| /* Let it go backward if the sections use a bogus |
| layout with overlaps. We'll overwrite the stupid |
| user's section data with the latest one, rather than |
| crashing. */ |
| |
| last_position = scn_start + dl->data.d.d_off; |
| |
| if (unlikely (change_bo)) |
| { |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type] |
| #endif |
| |
| /* Do the real work. */ |
| (*fctp) (last_position, dl->data.d.d_buf, |
| dl->data.d.d_size, 1); |
| |
| last_position += dl->data.d.d_size; |
| } |
| else |
| last_position = mempcpy (last_position, |
| dl->data.d.d_buf, |
| dl->data.d.d_size); |
| |
| scn_changed = true; |
| } |
| else |
| last_position += dl->data.d.d_size; |
| |
| assert (scn_start + dl->data.d.d_off + dl->data.d.d_size |
| == last_position); |
| |
| dl->flags &= ~ELF_F_DIRTY; |
| |
| dl = dl->next; |
| } |
| while (dl != NULL); |
| else |
| { |
| /* If the previous section (or the ELF/program |
| header) changed we might have to fill the gap. */ |
| if (scn_start > last_position && previous_scn_changed) |
| fill_mmap (0); |
| |
| /* We have to trust the existing section header information. */ |
| last_position = scn_start + shdr->sh_size; |
| } |
| |
| |
| previous_scn_changed = scn_changed; |
| next: |
| scn->flags &= ~ELF_F_DIRTY; |
| } |
| |
| /* Fill the gap between last section and section header table if |
| necessary. */ |
| if ((elf->flags & ELF_F_DIRTY) |
| && last_position < ((char *) elf->map_address + elf->start_offset |
| + ehdr->e_shoff)) |
| memset (last_position, __libelf_fill_byte, |
| (char *) elf->map_address + elf->start_offset + ehdr->e_shoff |
| - last_position); |
| |
| /* Write the section header table entry if necessary. */ |
| for (size_t cnt = 0; cnt < shnum; ++cnt) |
| { |
| Elf_Scn *scn = scns[cnt]; |
| |
| if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY) |
| { |
| if (unlikely (change_bo)) |
| (*shdr_fctp) (&shdr_dest[scn->index], |
| scn->shdr.ELFW(e,LIBELFBITS), |
| sizeof (ElfW2(LIBELFBITS,Shdr)), 1); |
| else |
| memcpy (&shdr_dest[scn->index], |
| scn->shdr.ELFW(e,LIBELFBITS), |
| sizeof (ElfW2(LIBELFBITS,Shdr))); |
| |
| /* If we previously made a copy of the section header |
| entry we now have to adjust the pointer again so |
| point to new place in the mapping. */ |
| if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced |
| && (scn->shdr_flags & ELF_F_MALLOCED) == 0) |
| scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index]; |
| |
| scn->shdr_flags &= ~ELF_F_DIRTY; |
| } |
| } |
| } |
| |
| /* That was the last part. Clear the overall flag. */ |
| elf->flags &= ~ELF_F_DIRTY; |
| |
| /* Make sure the content hits the disk. */ |
| char *msync_start = ((char *) elf->map_address |
| + (elf->start_offset & ~(sysconf (_SC_PAGESIZE) - 1))); |
| char *msync_end = ((char *) elf->map_address |
| + elf->start_offset + ehdr->e_shoff |
| + ehdr->e_shentsize * shnum); |
| (void) msync (msync_start, msync_end - msync_start, MS_SYNC); |
| |
| return 0; |
| } |
| |
| |
| /* Size of the buffer we use to generate the blocks of fill bytes. */ |
| #define FILLBUFSIZE 4096 |
| |
| /* If we have to convert the section buffer contents we have to use |
| temporary buffer. Only buffers up to MAX_TMPBUF bytes are allocated |
| on the stack. */ |
| #define MAX_TMPBUF 32768 |
| |
| |
| /* Helper function to write out fill bytes. */ |
| static int |
| fill (int fd, off_t pos, size_t len, char *fillbuf, size_t *filledp) |
| { |
| size_t filled = *filledp; |
| size_t fill_len = MIN (len, FILLBUFSIZE); |
| |
| if (unlikely (fill_len > filled) && filled < FILLBUFSIZE) |
| { |
| /* Initialize a few more bytes. */ |
| memset (fillbuf + filled, __libelf_fill_byte, fill_len - filled); |
| *filledp = filled = fill_len; |
| } |
| |
| do |
| { |
| /* This many bytes we want to write in this round. */ |
| size_t n = MIN (filled, len); |
| |
| if (unlikely ((size_t) pwrite_retry (fd, fillbuf, n, pos) != n)) |
| { |
| __libelf_seterrno (ELF_E_WRITE_ERROR); |
| return 1; |
| } |
| |
| pos += n; |
| len -= n; |
| } |
| while (len > 0); |
| |
| return 0; |
| } |
| |
| |
| int |
| internal_function |
| __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) |
| { |
| char fillbuf[FILLBUFSIZE]; |
| size_t filled = 0; |
| bool previous_scn_changed = false; |
| |
| /* We need the ELF header several times. */ |
| ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr; |
| |
| /* Write out the ELF header. */ |
| if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY) |
| { |
| ElfW2(LIBELFBITS,Ehdr) tmp_ehdr; |
| ElfW2(LIBELFBITS,Ehdr) *out_ehdr = ehdr; |
| |
| /* If the type sizes should be different at some time we have to |
| rewrite this code. */ |
| assert (sizeof (ElfW2(LIBELFBITS,Ehdr)) |
| == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1)); |
| |
| if (unlikely (change_bo)) |
| { |
| /* Today there is only one version of the ELF header. */ |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR] |
| #endif |
| |
| /* Write the converted ELF header in a temporary buffer. */ |
| (*fctp) (&tmp_ehdr, ehdr, sizeof (ElfW2(LIBELFBITS,Ehdr)), 1); |
| |
| /* This is the buffer we want to write. */ |
| out_ehdr = &tmp_ehdr; |
| } |
| |
| /* Write out the ELF header. */ |
| if (unlikely (pwrite_retry (elf->fildes, out_ehdr, |
| sizeof (ElfW2(LIBELFBITS,Ehdr)), 0) |
| != sizeof (ElfW2(LIBELFBITS,Ehdr)))) |
| { |
| __libelf_seterrno (ELF_E_WRITE_ERROR); |
| return 1; |
| } |
| |
| elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY; |
| |
| /* We start writing sections after the ELF header only if there is |
| no program header. */ |
| previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL; |
| } |
| |
| /* If the type sizes should be different at some time we have to |
| rewrite this code. */ |
| assert (sizeof (ElfW2(LIBELFBITS,Phdr)) |
| == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1)); |
| |
| size_t phnum; |
| if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0)) |
| return -1; |
| |
| /* Write out the program header table. */ |
| if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL |
| && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags) |
| & ELF_F_DIRTY)) |
| { |
| ElfW2(LIBELFBITS,Phdr) *tmp_phdr = NULL; |
| ElfW2(LIBELFBITS,Phdr) *out_phdr = elf->state.ELFW(elf,LIBELFBITS).phdr; |
| |
| /* Maybe the user wants a gap between the ELF header and the program |
| header. */ |
| if (ehdr->e_phoff > ehdr->e_ehsize |
| && unlikely (fill (elf->fildes, ehdr->e_ehsize, |
| ehdr->e_phoff - ehdr->e_ehsize, fillbuf, &filled) |
| != 0)) |
| return 1; |
| |
| if (unlikely (change_bo)) |
| { |
| /* Today there is only one version of the ELF header. */ |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR] |
| #endif |
| |
| /* Allocate sufficient memory. */ |
| tmp_phdr = (ElfW2(LIBELFBITS,Phdr) *) |
| malloc (sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum); |
| if (tmp_phdr == NULL) |
| { |
| __libelf_seterrno (ELF_E_NOMEM); |
| return 1; |
| } |
| |
| /* Write the converted ELF header in a temporary buffer. */ |
| (*fctp) (tmp_phdr, elf->state.ELFW(elf,LIBELFBITS).phdr, |
| sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1); |
| |
| /* This is the buffer we want to write. */ |
| out_phdr = tmp_phdr; |
| } |
| |
| /* Write out the ELF header. */ |
| size_t phdr_size = sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum; |
| if (unlikely ((size_t) pwrite_retry (elf->fildes, out_phdr, |
| phdr_size, ehdr->e_phoff) |
| != phdr_size)) |
| { |
| __libelf_seterrno (ELF_E_WRITE_ERROR); |
| return 1; |
| } |
| |
| /* This is a no-op we we have not allocated any memory. */ |
| free (tmp_phdr); |
| |
| elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY; |
| |
| /* We modified the program header. Maybe this created a gap so |
| we have to write fill bytes, if necessary. */ |
| previous_scn_changed = true; |
| } |
| |
| /* From now on we have to keep track of the last position to eventually |
| fill the gaps with the prescribed fill byte. */ |
| off_t last_offset; |
| if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL) |
| last_offset = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1); |
| else |
| last_offset = (ehdr->e_phoff + sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum); |
| |
| /* Write all the sections. Well, only those which are modified. */ |
| if (shnum > 0) |
| { |
| if (unlikely (shnum > SIZE_MAX / (sizeof (Elf_Scn *) |
| + sizeof (ElfW2(LIBELFBITS,Shdr))))) |
| return 1; |
| |
| off_t shdr_offset = elf->start_offset + ehdr->e_shoff; |
| #if EV_NUM != 2 |
| xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]; |
| #else |
| # undef shdr_fctp |
| # define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR] |
| #endif |
| |
| ElfW2(LIBELFBITS,Shdr) *shdr_data; |
| if (change_bo || elf->state.ELFW(elf,LIBELFBITS).shdr == NULL |
| || (elf->flags & ELF_F_DIRTY)) |
| shdr_data = (ElfW2(LIBELFBITS,Shdr) *) |
| alloca (shnum * sizeof (ElfW2(LIBELFBITS,Shdr))); |
| else |
| shdr_data = elf->state.ELFW(elf,LIBELFBITS).shdr; |
| int shdr_flags = elf->flags; |
| |
| /* Get all sections into the array and sort them. */ |
| Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns; |
| Elf_Scn **scns = (Elf_Scn **) alloca (shnum * sizeof (Elf_Scn *)); |
| sort_sections (scns, list); |
| |
| for (size_t cnt = 0; cnt < shnum; ++cnt) |
| { |
| Elf_Scn *scn = scns[cnt]; |
| if (scn->index == 0) |
| { |
| /* The dummy section header entry. It should not be |
| possible to mark this "section" as dirty. */ |
| assert ((scn->flags & ELF_F_DIRTY) == 0); |
| goto next; |
| } |
| |
| ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS); |
| if (shdr->sh_type == SHT_NOBITS) |
| goto next; |
| |
| off_t scn_start = elf->start_offset + shdr->sh_offset; |
| Elf_Data_List *dl = &scn->data_list; |
| bool scn_changed = false; |
| |
| if (scn->data_list_rear != NULL) |
| do |
| { |
| /* If there is a gap, fill it. */ |
| if (scn_start + dl->data.d.d_off > last_offset |
| && ((previous_scn_changed && dl->data.d.d_off == 0) |
| || ((scn->flags | dl->flags | elf->flags) |
| & ELF_F_DIRTY) != 0)) |
| { |
| if (unlikely (fill (elf->fildes, last_offset, |
| (scn_start + dl->data.d.d_off) |
| - last_offset, fillbuf, |
| &filled) != 0)) |
| return 1; |
| } |
| |
| if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY) |
| { |
| char tmpbuf[MAX_TMPBUF]; |
| void *buf = dl->data.d.d_buf; |
| |
| /* Let it go backward if the sections use a bogus |
| layout with overlaps. We'll overwrite the stupid |
| user's section data with the latest one, rather than |
| crashing. */ |
| |
| last_offset = scn_start + dl->data.d.d_off; |
| |
| if (unlikely (change_bo)) |
| { |
| #if EV_NUM != 2 |
| xfct_t fctp; |
| fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]; |
| #else |
| # undef fctp |
| # define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type] |
| #endif |
| |
| buf = tmpbuf; |
| if (dl->data.d.d_size > MAX_TMPBUF) |
| { |
| buf = malloc (dl->data.d.d_size); |
| if (buf == NULL) |
| { |
| __libelf_seterrno (ELF_E_NOMEM); |
| return 1; |
| } |
| } |
| |
| /* Do the real work. */ |
| (*fctp) (buf, dl->data.d.d_buf, dl->data.d.d_size, 1); |
| } |
| |
| ssize_t n = pwrite_retry (elf->fildes, buf, |
| dl->data.d.d_size, |
| last_offset); |
| if (unlikely ((size_t) n != dl->data.d.d_size)) |
| { |
| if (buf != dl->data.d.d_buf && buf != tmpbuf) |
| free (buf); |
| |
| __libelf_seterrno (ELF_E_WRITE_ERROR); |
| return 1; |
| } |
| |
| if (buf != dl->data.d.d_buf && buf != tmpbuf) |
| free (buf); |
| |
| scn_changed = true; |
| } |
| |
| last_offset += dl->data.d.d_size; |
| |
| dl->flags &= ~ELF_F_DIRTY; |
| |
| dl = dl->next; |
| } |
| while (dl != NULL); |
| else |
| { |
| /* If the previous section (or the ELF/program |
| header) changed we might have to fill the gap. */ |
| if (scn_start > last_offset && previous_scn_changed) |
| { |
| if (unlikely (fill (elf->fildes, last_offset, |
| scn_start - last_offset, fillbuf, |
| &filled) != 0)) |
| return 1; |
| } |
| |
| last_offset = scn_start + shdr->sh_size; |
| } |
| |
| previous_scn_changed = scn_changed; |
| next: |
| /* Collect the section header table information. */ |
| if (unlikely (change_bo)) |
| (*shdr_fctp) (&shdr_data[scn->index], |
| scn->shdr.ELFW(e,LIBELFBITS), |
| sizeof (ElfW2(LIBELFBITS,Shdr)), 1); |
| else if (elf->state.ELFW(elf,LIBELFBITS).shdr == NULL |
| || (elf->flags & ELF_F_DIRTY)) |
| memcpy (&shdr_data[scn->index], scn->shdr.ELFW(e,LIBELFBITS), |
| sizeof (ElfW2(LIBELFBITS,Shdr))); |
| |
| shdr_flags |= scn->shdr_flags; |
| scn->shdr_flags &= ~ELF_F_DIRTY; |
| } |
| |
| /* Fill the gap between last section and section header table if |
| necessary. */ |
| if ((elf->flags & ELF_F_DIRTY) && last_offset < shdr_offset |
| && unlikely (fill (elf->fildes, last_offset, |
| shdr_offset - last_offset, |
| fillbuf, &filled) != 0)) |
| return 1; |
| |
| /* Write out the section header table. */ |
| if (shdr_flags & ELF_F_DIRTY |
| && unlikely ((size_t) pwrite_retry (elf->fildes, shdr_data, |
| sizeof (ElfW2(LIBELFBITS,Shdr)) |
| * shnum, shdr_offset) |
| != sizeof (ElfW2(LIBELFBITS,Shdr)) * shnum)) |
| { |
| __libelf_seterrno (ELF_E_WRITE_ERROR); |
| return 1; |
| } |
| } |
| |
| /* That was the last part. Clear the overall flag. */ |
| elf->flags &= ~ELF_F_DIRTY; |
| |
| return 0; |
| } |