diff options
Diffstat (limited to 'libelf/elf32_updatefile.c')
| -rw-r--r-- | libelf/elf32_updatefile.c | 164 |
1 files changed, 117 insertions, 47 deletions
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c index e88f4a45..a4d83a1c 100644 --- a/libelf/elf32_updatefile.c +++ b/libelf/elf32_updatefile.c @@ -126,11 +126,10 @@ int internal_function __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) { - ElfW2(LIBELFBITS,Ehdr) *ehdr; - char *last_position; + bool previous_scn_changed = false; /* We need the ELF header several times. */ - ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr; + 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) @@ -160,6 +159,10 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) 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; } /* Write out the program header table. */ @@ -200,14 +203,19 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_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. */ - 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, ehdr->e_phnum)); + 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, + ehdr->e_phnum)); /* Write all the sections. Well, only those which are modified. */ if (shnum > 0) @@ -283,14 +291,42 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) 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) + memset (shdr_end, __libelf_fill_byte, + scn_start + offset - shdr_end); + } - if (shdr->sh_type != SHT_NOBITS && scn->data_list_rear != NULL) + if (scn->data_list_rear != NULL) do { assert (dl->data.d.d_off >= 0); @@ -298,31 +334,15 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) 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 + && ((previous_scn_changed && dl->data.d.d_off == 0) + || ((scn->flags | dl->flags | elf->flags) + & ELF_F_DIRTY) != 0)) + fill_mmap (dl->data.d.d_off); + if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY) { - if (scn_start + dl->data.d.d_off > last_position) - { - /* This code assumes that the data blocks for - a section are ordered by offset. */ - size_t written = 0; - - if (last_position < shdr_start) - { - written = MIN (scn_start + dl->data.d.d_off - - last_position, - shdr_start - last_position); - - memset (last_position, __libelf_fill_byte, - written); - } - - if (last_position + written - != scn_start + dl->data.d.d_off - && shdr_end < scn_start + dl->data.d.d_off) - memset (shdr_end, __libelf_fill_byte, - scn_start + dl->data.d.d_off - shdr_end); - } - /* 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 @@ -350,6 +370,8 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) 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; @@ -362,10 +384,20 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum) dl = dl->next; } while (dl != NULL); - else if (shdr->sh_type != SHT_NOBITS && scn->index != 0) - /* We have to trust the existing section header information. */ - last_position += shdr->sh_size; + 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; } @@ -470,6 +502,7 @@ __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; @@ -513,6 +546,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) } 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 @@ -578,6 +615,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) 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 @@ -616,30 +657,43 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) 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 (shdr->sh_type != SHT_NOBITS && scn->data_list_rear != NULL - && scn->index != 0) + 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; - if (scn_start + dl->data.d.d_off > last_offset) - { - if (unlikely (fill (elf->fildes, last_offset, - (scn_start + dl->data.d.d_off) - - last_offset, fillbuf, - &filled) != 0)) - return 1; - } - /* 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 @@ -686,6 +740,8 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) if (buf != dl->data.d.d_buf && buf != tmpbuf) free (buf); + + scn_changed = true; } last_offset += dl->data.d.d_size; @@ -695,9 +751,23 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum) dl = dl->next; } while (dl != NULL); - else if (shdr->sh_type != SHT_NOBITS && scn->index != 0) - last_offset = scn_start + shdr->sh_size; + 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], |
