#include <libelf.h> #include <stdbool.h> #include <string.h> #include <sys/param.h> #include "libelfP.h" #include "elf-knowledge.h" #ifndef LIBELFBITS # define LIBELFBITS 32 #endif static int ELFW(default_ehdr,LIBELFBITS) (Elf *elf, ElfW2(LIBELFBITS,Ehdr) *ehdr, size_t shnum, int *change_bop) { /* Always write the magic bytes. */ if (memcmp (&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0) { memcpy (&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG); elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY; } /* Always set the file class. */ update_if_changed (ehdr->e_ident[EI_CLASS], ELFW(ELFCLASS,LIBELFBITS), elf->state.ELFW(elf,LIBELFBITS).ehdr_flags); /* Set the data encoding if necessary. */ if (unlikely (ehdr->e_ident[EI_DATA] == ELFDATANONE))
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 **) malloc (shnum * sizeof (Elf_Scn *)); if (unlikely (scns == NULL)) { __libelf_seterrno (ELF_E_NOMEM); return -1; } 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 = malloc (sizeof (ElfW2(LIBELFBITS,Shdr))); if (unlikely (p == NULL)) { __libelf_seterrno (ELF_E_NOMEM); return -1; } 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 (unlikely (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 if (dl->data.d.d_size != 0) 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]) { free (scn->shdr.ELFW(e,LIBELFBITS)); scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index]; } scn->shdr_flags &= ~ELF_F_DIRTY; } } free (scns); }