/* * update.c - implementation of the elf_update(3) function. * Copyright (C) 1995 - 2006 Michael Riepe * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #ifndef lint static const char rcsid[] = "@(#) $Id: update.c,v 1.34 2009/05/22 17:08:09 michael Exp $"; #endif /* lint */ #include #if HAVE_MMAP #include #endif /* HAVE_MMAP */ static const unsigned short __encoding = ELFDATA2LSB + (ELFDATA2MSB << 8); #define native_encoding (*(unsigned char*)&__encoding) #define rewrite(var,val,f) \ do{if((var)!=(val)){(var)=(val);(f)|=ELF_F_DIRTY;}}while(0) #define align(var,val) \ do{if((val)>1){(var)+=(val)-1;(var)-=(var)%(val);}}while(0) #undef max #define max(a,b) ((a)>(b)?(a):(b)) static off_t scn_data_layout(Elf_Scn *scn, unsigned v, unsigned type, size_t *algn, unsigned *flag) { Elf *elf = scn->s_elf; Elf_Data *data; int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0; size_t scn_align = 1; size_t len = 0; Scn_Data *sd; size_t fsize; if (!(sd = scn->s_data_1)) { /* no data in section */ *algn = scn_align; return (off_t)len; } /* load data from file, if any */ if (!(data = elf_getdata(scn, NULL))) { return (off_t)-1; } elf_assert(data == &sd->sd_data); for (; sd; sd = sd->sd_link) { elf_assert(sd->sd_magic == DATA_MAGIC); elf_assert(sd->sd_scn == scn); if (!valid_version(sd->sd_data.d_version)) { return (off_t)-1; } fsize = sd->sd_data.d_size; if (fsize && type != SHT_NOBITS && valid_type(sd->sd_data.d_type)) { if (elf->e_class == ELFCLASS32) { fsize = _elf32_xltsize(&sd->sd_data, v, ELFDATA2LSB, 1); } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { fsize = _elf64_xltsize(&sd->sd_data, v, ELFDATA2LSB, 1); } #endif /* __LIBELF64 */ else { elf_assert(valid_class(elf->e_class)); seterr(ERROR_UNIMPLEMENTED); return (off_t)-1; } if (fsize == (size_t)-1) { return (off_t)-1; } } if (layout) { align(len, sd->sd_data.d_align); scn_align = max(scn_align, sd->sd_data.d_align); rewrite(sd->sd_data.d_off, (off_t)len, sd->sd_data_flags); len += fsize; } else { len = max(len, sd->sd_data.d_off + fsize); } *flag |= sd->sd_data_flags; } *algn = scn_align; return (off_t)len; } static size_t scn_entsize(const Elf *elf, unsigned version, unsigned stype) { Elf_Type type; switch ((type = _elf_scn_type(stype))) { case ELF_T_BYTE: return 0; case ELF_T_VDEF: case ELF_T_VNEED: return 0; /* What else can I do? Thank you, Sun! */ default: return _fsize(elf->e_class, version, type); } } static off_t _elf32_layout(Elf *elf, unsigned *flag) { int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0; int allow_overlap = (elf->e_elf_flags & ELF_F_LAYOUT_OVERLAP) != 0; Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf->e_ehdr; size_t off = 0; unsigned version; unsigned encoding; size_t align_addr; size_t entsize; unsigned phnum; unsigned shnum; Elf_Scn *scn; *flag = elf->e_elf_flags | elf->e_phdr_flags; if ((version = ehdr->e_version) == EV_NONE) { version = EV_CURRENT; } if (!valid_version(version)) { seterr(ERROR_UNKNOWN_VERSION); return -1; } if ((encoding = ehdr->e_ident[EI_DATA]) == ELFDATANONE) { encoding = native_encoding; } if (!valid_encoding(encoding)) { seterr(ERROR_UNKNOWN_ENCODING); return -1; } entsize = _fsize(ELFCLASS32, version, ELF_T_EHDR); elf_assert(entsize); rewrite(ehdr->e_ehsize, entsize, elf->e_ehdr_flags); off = entsize; align_addr = _fsize(ELFCLASS32, version, ELF_T_ADDR); elf_assert(align_addr); if ((phnum = elf->e_phnum)) { entsize = _fsize(ELFCLASS32, version, ELF_T_PHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_phoff, off, elf->e_ehdr_flags); off += phnum * entsize; } else { off = max(off, ehdr->e_phoff + phnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_phoff, 0, elf->e_ehdr_flags); } } if (phnum >= PN_XNUM) { Elf_Scn *scn = elf->e_scn_1; Elf32_Shdr *shdr = &scn->s_shdr32; elf_assert(scn); elf_assert(scn->s_index == 0); rewrite(shdr->sh_info, phnum, scn->s_shdr_flags); *flag |= scn->s_shdr_flags; phnum = PN_XNUM; } rewrite(ehdr->e_phnum, phnum, elf->e_ehdr_flags); rewrite(ehdr->e_phentsize, entsize, elf->e_ehdr_flags); for (scn = elf->e_scn_1, shnum = 0; scn; scn = scn->s_link, ++shnum) { Elf32_Shdr *shdr = &scn->s_shdr32; size_t scn_align = 1; off_t len; elf_assert(scn->s_index == shnum); *flag |= scn->s_scn_flags; if (scn->s_index == SHN_UNDEF) { rewrite(shdr->sh_entsize, 0, scn->s_shdr_flags); if (layout) { rewrite(shdr->sh_offset, 0, scn->s_shdr_flags); rewrite(shdr->sh_size, 0, scn->s_shdr_flags); rewrite(shdr->sh_addralign, 0, scn->s_shdr_flags); } *flag |= scn->s_shdr_flags; continue; } if (shdr->sh_type == SHT_NULL) { *flag |= scn->s_shdr_flags; continue; } len = scn_data_layout(scn, version, shdr->sh_type, &scn_align, flag); if (len == -1) { return -1; } /* * Never override the program's choice. */ if (shdr->sh_entsize == 0) { entsize = scn_entsize(elf, version, shdr->sh_type); if (entsize > 1) { rewrite(shdr->sh_entsize, entsize, scn->s_shdr_flags); } } if (layout) { align(off, scn_align); rewrite(shdr->sh_offset, off, scn->s_shdr_flags); rewrite(shdr->sh_size, (size_t)len, scn->s_shdr_flags); rewrite(shdr->sh_addralign, scn_align, scn->s_shdr_flags); if (shdr->sh_type != SHT_NOBITS) { off += (size_t)len; } } else if ((size_t)len > shdr->sh_size) { seterr(ERROR_SCN2SMALL); return -1; } else { Elf_Scn *scn2; size_t end1, end2; end1 = shdr->sh_offset; if (shdr->sh_type != SHT_NOBITS) { end1 += shdr->sh_size; } if (!allow_overlap && shdr->sh_offset < off) { /* * check for overlapping sections */ for (scn2 = elf->e_scn_1; scn2; scn2 = scn2->s_link) { if (scn2 == scn) { break; } end2 = scn2->s_shdr32.sh_offset; if (scn2->s_shdr32.sh_type != SHT_NOBITS) { end2 += scn2->s_shdr32.sh_size; } if (end1 > scn2->s_shdr32.sh_offset && end2 > shdr->sh_offset) { seterr(ERROR_SCN_OVERLAP); return -1; } } } if (off < end1) { off = end1; } } *flag |= scn->s_shdr_flags; } if (shnum) { entsize = _fsize(ELFCLASS32, version, ELF_T_SHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_shoff, off, elf->e_ehdr_flags); off += shnum * entsize; } else { off = max(off, ehdr->e_shoff + shnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_shoff, 0, elf->e_ehdr_flags); } } if (shnum >= SHN_LORESERVE) { Elf_Scn *scn = elf->e_scn_1; Elf32_Shdr *shdr = &scn->s_shdr32; elf_assert(scn->s_index == 0); rewrite(shdr->sh_size, shnum, scn->s_shdr_flags); *flag |= scn->s_shdr_flags; shnum = 0; } rewrite(ehdr->e_shnum, shnum, elf->e_ehdr_flags); rewrite(ehdr->e_shentsize, entsize, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG0], ELFMAG0, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG1], ELFMAG1, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG2], ELFMAG2, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG3], ELFMAG3, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_CLASS], ELFCLASS32, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_DATA], encoding, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_VERSION], version, elf->e_ehdr_flags); rewrite(ehdr->e_version, version, elf->e_ehdr_flags); *flag |= elf->e_ehdr_flags; return off; } #if __LIBELF64 static off_t _elf64_layout(Elf *elf, unsigned *flag) { int layout = (elf->e_elf_flags & ELF_F_LAYOUT) == 0; int allow_overlap = (elf->e_elf_flags & ELF_F_LAYOUT_OVERLAP) != 0; Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf->e_ehdr; size_t off = 0; unsigned version; unsigned encoding; size_t align_addr; size_t entsize; unsigned phnum; unsigned shnum; Elf_Scn *scn; *flag = elf->e_elf_flags | elf->e_phdr_flags; if ((version = ehdr->e_version) == EV_NONE) { version = EV_CURRENT; } if (!valid_version(version)) { seterr(ERROR_UNKNOWN_VERSION); return -1; } if ((encoding = ehdr->e_ident[EI_DATA]) == ELFDATANONE) { encoding = native_encoding; } if (!valid_encoding(encoding)) { seterr(ERROR_UNKNOWN_ENCODING); return -1; } entsize = _fsize(ELFCLASS64, version, ELF_T_EHDR); elf_assert(entsize); rewrite(ehdr->e_ehsize, entsize, elf->e_ehdr_flags); off = entsize; align_addr = _fsize(ELFCLASS64, version, ELF_T_ADDR); elf_assert(align_addr); if ((phnum = elf->e_phnum)) { entsize = _fsize(ELFCLASS64, version, ELF_T_PHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_phoff, off, elf->e_ehdr_flags); off += phnum * entsize; } else { off = max(off, ehdr->e_phoff + phnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_phoff, 0, elf->e_ehdr_flags); } } if (phnum >= PN_XNUM) { Elf_Scn *scn = elf->e_scn_1; Elf32_Shdr *shdr = &scn->s_shdr32; /* modify first section header, too! */ elf_assert(scn); elf_assert(scn->s_index == 0); rewrite(shdr->sh_info, phnum, scn->s_shdr_flags); *flag |= scn->s_shdr_flags; phnum = PN_XNUM; } rewrite(ehdr->e_phnum, phnum, elf->e_ehdr_flags); rewrite(ehdr->e_phentsize, entsize, elf->e_ehdr_flags); for (scn = elf->e_scn_1, shnum = 0; scn; scn = scn->s_link, ++shnum) { Elf64_Shdr *shdr = &scn->s_shdr64; size_t scn_align = 1; off_t len; elf_assert(scn->s_index == shnum); *flag |= scn->s_scn_flags; if (scn->s_index == SHN_UNDEF) { rewrite(shdr->sh_entsize, 0, scn->s_shdr_flags); if (layout) { rewrite(shdr->sh_offset, 0, scn->s_shdr_flags); rewrite(shdr->sh_size, 0, scn->s_shdr_flags); rewrite(shdr->sh_addralign, 0, scn->s_shdr_flags); } *flag |= scn->s_shdr_flags; continue; } if (shdr->sh_type == SHT_NULL) { *flag |= scn->s_shdr_flags; continue; } len = scn_data_layout(scn, version, shdr->sh_type, &scn_align, flag); if (len == -1) { return -1; } /* * Never override the program's choice. */ if (shdr->sh_entsize == 0) { entsize = scn_entsize(elf, version, shdr->sh_type); if (entsize > 1) { rewrite(shdr->sh_entsize, entsize, scn->s_shdr_flags); } } if (layout) { align(off, scn_align); rewrite(shdr->sh_offset, off, scn->s_shdr_flags); rewrite(shdr->sh_size, (size_t)len, scn->s_shdr_flags); rewrite(shdr->sh_addralign, scn_align, scn->s_shdr_flags); if (shdr->sh_type != SHT_NOBITS) { off += (size_t)len; } } else if ((size_t)len > shdr->sh_size) { seterr(ERROR_SCN2SMALL); return -1; } else { Elf_Scn *scn2; size_t end1, end2; end1 = shdr->sh_offset; if (shdr->sh_type != SHT_NOBITS) { end1 += shdr->sh_size; } if (!allow_overlap && shdr->sh_offset < off) { /* * check for overlapping sections */ for (scn2 = elf->e_scn_1; scn2; scn2 = scn2->s_link) { if (scn2 == scn) { break; } end2 = scn2->s_shdr64.sh_offset; if (scn2->s_shdr64.sh_type != SHT_NOBITS) { end2 += scn2->s_shdr64.sh_size; } if (end1 > scn2->s_shdr64.sh_offset && end2 > shdr->sh_offset) { seterr(ERROR_SCN_OVERLAP); return -1; } } } if (off < end1) { off = end1; } } *flag |= scn->s_shdr_flags; } if (shnum) { entsize = _fsize(ELFCLASS64, version, ELF_T_SHDR); elf_assert(entsize); if (layout) { align(off, align_addr); rewrite(ehdr->e_shoff, off, elf->e_ehdr_flags); off += shnum * entsize; } else { off = max(off, ehdr->e_shoff + shnum * entsize); } } else { entsize = 0; if (layout) { rewrite(ehdr->e_shoff, 0, elf->e_ehdr_flags); } } if (shnum >= SHN_LORESERVE) { Elf_Scn *scn = elf->e_scn_1; Elf64_Shdr *shdr = &scn->s_shdr64; elf_assert(scn->s_index == 0); rewrite(shdr->sh_size, shnum, scn->s_shdr_flags); *flag |= scn->s_shdr_flags; shnum = 0; } rewrite(ehdr->e_shnum, shnum, elf->e_ehdr_flags); rewrite(ehdr->e_shentsize, entsize, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG0], ELFMAG0, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG1], ELFMAG1, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG2], ELFMAG2, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_MAG3], ELFMAG3, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_CLASS], ELFCLASS64, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_DATA], encoding, elf->e_ehdr_flags); rewrite(ehdr->e_ident[EI_VERSION], version, elf->e_ehdr_flags); rewrite(ehdr->e_version, version, elf->e_ehdr_flags); *flag |= elf->e_ehdr_flags; return off; } #endif /* __LIBELF64 */ #define ptrinside(p,a,l) ((p)>=(a)&&(p)<(a)+(l)) #define newptr(p,o,n) ((p)=((p)-(o))+(n)) static int _elf_update_pointers(Elf *elf, char *outbuf, size_t len) { Elf_Scn *scn; Scn_Data *sd; char *data, *rawdata; elf_assert(elf); elf_assert(elf->e_data); elf_assert(!elf->e_parent); elf_assert(!elf->e_unmap_data); elf_assert(elf->e_kind == ELF_K_ELF); elf_assert(len >= EI_NIDENT); /* resize memory images */ if (len <= elf->e_dsize) { /* don't shorten the memory image */ data = elf->e_data; } else if ((data = (char*)realloc(elf->e_data, len))) { elf->e_dsize = len; } else { seterr(ERROR_IO_2BIG); return -1; } if (elf->e_rawdata == elf->e_data) { /* update frozen raw image */ memcpy(data, outbuf, len); elf->e_data = elf->e_rawdata = data; /* cooked data is stored outside the raw image */ return 0; } if (elf->e_rawdata) { /* update raw image */ if (!(rawdata = (char*)realloc(elf->e_rawdata, len))) { seterr(ERROR_IO_2BIG); return -1; } memcpy(rawdata, outbuf, len); elf->e_rawdata = rawdata; } if (data == elf->e_data) { /* nothing more to do */ return 0; } /* adjust internal pointers */ for (scn = elf->e_scn_1; scn; scn = scn->s_link) { elf_assert(scn->s_magic == SCN_MAGIC); elf_assert(scn->s_elf == elf); if ((sd = scn->s_data_1)) { elf_assert(sd->sd_magic == DATA_MAGIC); elf_assert(sd->sd_scn == scn); if (sd->sd_memdata && !sd->sd_free_data) { elf_assert(ptrinside(sd->sd_memdata, elf->e_data, elf->e_dsize)); if (sd->sd_data.d_buf == sd->sd_memdata) { newptr(sd->sd_memdata, elf->e_data, data); sd->sd_data.d_buf = sd->sd_memdata; } else { newptr(sd->sd_memdata, elf->e_data, data); } } } if ((sd = scn->s_rawdata)) { elf_assert(sd->sd_magic == DATA_MAGIC); elf_assert(sd->sd_scn == scn); if (sd->sd_memdata && sd->sd_free_data) { size_t off, len; if (elf->e_class == ELFCLASS32) { off = scn->s_shdr32.sh_offset; len = scn->s_shdr32.sh_size; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { off = scn->s_shdr64.sh_offset; len = scn->s_shdr64.sh_size; } #endif /* __LIBELF64 */ else { seterr(ERROR_UNIMPLEMENTED); return -1; } if (!(rawdata = (char*)realloc(sd->sd_memdata, len))) { seterr(ERROR_IO_2BIG); return -1; } memcpy(rawdata, outbuf + off, len); if (sd->sd_data.d_buf == sd->sd_memdata) { sd->sd_data.d_buf = rawdata; } sd->sd_memdata = rawdata; } } } elf->e_data = data; return 0; } #undef ptrinside #undef newptr static off_t _elf32_write(Elf *elf, char *outbuf, size_t len) { Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; Elf_Scn *scn; Scn_Data *sd; Elf_Data src; Elf_Data dst; unsigned encode; elf_assert(len); elf_assert(elf->e_ehdr); ehdr = (Elf32_Ehdr*)elf->e_ehdr; encode = ehdr->e_ident[EI_DATA]; src.d_buf = ehdr; src.d_type = ELF_T_EHDR; src.d_size = _msize(ELFCLASS32, _elf_version, ELF_T_EHDR); src.d_version = _elf_version; dst.d_buf = outbuf; dst.d_size = ehdr->e_ehsize; dst.d_version = ehdr->e_version; if (!elf32_xlatetof(&dst, &src, encode)) { return -1; } if (elf->e_phnum) { src.d_buf = elf->e_phdr; src.d_type = ELF_T_PHDR; src.d_size = elf->e_phnum * _msize(ELFCLASS32, _elf_version, ELF_T_PHDR); src.d_version = _elf_version; dst.d_buf = outbuf + ehdr->e_phoff; dst.d_size = elf->e_phnum * ehdr->e_phentsize; dst.d_version = ehdr->e_version; if (!elf32_xlatetof(&dst, &src, encode)) { return -1; } } for (scn = elf->e_scn_1; scn; scn = scn->s_link) { elf_assert(scn->s_magic == SCN_MAGIC); elf_assert(scn->s_elf == elf); src.d_buf = &scn->s_uhdr; src.d_type = ELF_T_SHDR; src.d_size = _msize(ELFCLASS32, EV_CURRENT, ELF_T_SHDR); src.d_version = EV_CURRENT; dst.d_buf = outbuf + ehdr->e_shoff + scn->s_index * ehdr->e_shentsize; dst.d_size = ehdr->e_shentsize; dst.d_version = ehdr->e_version; if (!elf32_xlatetof(&dst, &src, encode)) { return -1; } if (scn->s_index == SHN_UNDEF) { continue; } shdr = &scn->s_shdr32; if (shdr->sh_type == SHT_NULL || shdr->sh_type == SHT_NOBITS) { continue; } /* XXX: this is probably no longer necessary */ if (scn->s_data_1 && !elf_getdata(scn, NULL)) { return -1; } for (sd = scn->s_data_1; sd; sd = sd->sd_link) { elf_assert(sd->sd_magic == DATA_MAGIC); elf_assert(sd->sd_scn == scn); src = sd->sd_data; if (!src.d_size) { continue; } if (!src.d_buf) { seterr(ERROR_NULLBUF); return -1; } dst.d_buf = outbuf + shdr->sh_offset + src.d_off; dst.d_size = src.d_size; dst.d_version = ehdr->e_version; if (valid_type(src.d_type)) { size_t tmp; tmp = _elf32_xltsize(&src, dst.d_version, ELFDATA2LSB, 1); if (tmp == (size_t)-1) { return -1; } dst.d_size = tmp; } else { src.d_type = ELF_T_BYTE; } if (!elf32_xlatetof(&dst, &src, encode)) { return -1; } } } /* cleanup */ if (elf->e_readable && _elf_update_pointers(elf, outbuf, len)) { return -1; } /* NOTE: ehdr is no longer valid! */ ehdr = (Elf32_Ehdr*)elf->e_ehdr; elf_assert(ehdr); elf->e_encoding = ehdr->e_ident[EI_DATA]; elf->e_version = ehdr->e_ident[EI_VERSION]; elf->e_elf_flags &= ~ELF_F_DIRTY; elf->e_ehdr_flags &= ~ELF_F_DIRTY; elf->e_phdr_flags &= ~ELF_F_DIRTY; for (scn = elf->e_scn_1; scn; scn = scn->s_link) { scn->s_scn_flags &= ~ELF_F_DIRTY; scn->s_shdr_flags &= ~ELF_F_DIRTY; for (sd = scn->s_data_1; sd; sd = sd->sd_link) { sd->sd_data_flags &= ~ELF_F_DIRTY; } if (elf->e_readable) { shdr = &scn->s_shdr32; scn->s_type = shdr->sh_type; scn->s_size = shdr->sh_size; scn->s_offset = shdr->sh_offset; } } elf->e_size = len; return len; } #if __LIBELF64 static off_t _elf64_write(Elf *elf, char *outbuf, size_t len) { Elf64_Ehdr *ehdr; Elf64_Shdr *shdr; Elf_Scn *scn; Scn_Data *sd; Elf_Data src; Elf_Data dst; unsigned encode; elf_assert(len); elf_assert(elf->e_ehdr); ehdr = (Elf64_Ehdr*)elf->e_ehdr; encode = ehdr->e_ident[EI_DATA]; src.d_buf = ehdr; src.d_type = ELF_T_EHDR; src.d_size = _msize(ELFCLASS64, _elf_version, ELF_T_EHDR); src.d_version = _elf_version; dst.d_buf = outbuf; dst.d_size = ehdr->e_ehsize; dst.d_version = ehdr->e_version; if (!elf64_xlatetof(&dst, &src, encode)) { return -1; } if (elf->e_phnum) { src.d_buf = elf->e_phdr; src.d_type = ELF_T_PHDR; src.d_size = elf->e_phnum * _msize(ELFCLASS64, _elf_version, ELF_T_PHDR); src.d_version = _elf_version; dst.d_buf = outbuf + ehdr->e_phoff; dst.d_size = elf->e_phnum * ehdr->e_phentsize; dst.d_version = ehdr->e_version; if (!elf64_xlatetof(&dst, &src, encode)) { return -1; } } for (scn = elf->e_scn_1; scn; scn = scn->s_link) { elf_assert(scn->s_magic == SCN_MAGIC); elf_assert(scn->s_elf == elf); src.d_buf = &scn->s_uhdr; src.d_type = ELF_T_SHDR; src.d_size = _msize(ELFCLASS64, EV_CURRENT, ELF_T_SHDR); src.d_version = EV_CURRENT; dst.d_buf = outbuf + ehdr->e_shoff + scn->s_index * ehdr->e_shentsize; dst.d_size = ehdr->e_shentsize; dst.d_version = ehdr->e_version; if (!elf64_xlatetof(&dst, &src, encode)) { return -1; } if (scn->s_index == SHN_UNDEF) { continue; } shdr = &scn->s_shdr64; if (shdr->sh_type == SHT_NULL || shdr->sh_type == SHT_NOBITS) { continue; } /* XXX: this is probably no longer necessary */ if (scn->s_data_1 && !elf_getdata(scn, NULL)) { return -1; } for (sd = scn->s_data_1; sd; sd = sd->sd_link) { elf_assert(sd->sd_magic == DATA_MAGIC); elf_assert(sd->sd_scn == scn); src = sd->sd_data; if (!src.d_size) { continue; } if (!src.d_buf) { seterr(ERROR_NULLBUF); return -1; } dst.d_buf = outbuf + shdr->sh_offset + src.d_off; dst.d_size = src.d_size; dst.d_version = ehdr->e_version; if (valid_type(src.d_type)) { size_t tmp; tmp = _elf64_xltsize(&src, dst.d_version, ELFDATA2LSB, 1); if (tmp == (size_t)-1) { return -1; } dst.d_size = tmp; } else { src.d_type = ELF_T_BYTE; } if (!elf64_xlatetof(&dst, &src, encode)) { return -1; } } } /* cleanup */ if (elf->e_readable && _elf_update_pointers(elf, outbuf, len)) { return -1; } /* NOTE: ehdr is no longer valid! */ ehdr = (Elf64_Ehdr*)elf->e_ehdr; elf_assert(ehdr); elf->e_encoding = ehdr->e_ident[EI_DATA]; elf->e_version = ehdr->e_ident[EI_VERSION]; elf->e_elf_flags &= ~ELF_F_DIRTY; elf->e_ehdr_flags &= ~ELF_F_DIRTY; elf->e_phdr_flags &= ~ELF_F_DIRTY; for (scn = elf->e_scn_1; scn; scn = scn->s_link) { scn->s_scn_flags &= ~ELF_F_DIRTY; scn->s_shdr_flags &= ~ELF_F_DIRTY; for (sd = scn->s_data_1; sd; sd = sd->sd_link) { sd->sd_data_flags &= ~ELF_F_DIRTY; } if (elf->e_readable) { shdr = &scn->s_shdr64; scn->s_type = shdr->sh_type; scn->s_size = shdr->sh_size; scn->s_offset = shdr->sh_offset; } } elf->e_size = len; return len; } #endif /* __LIBELF64 */ static int xwrite(int fd, char *buffer, size_t len) { size_t done = 0; size_t n; while (done < len) { n = write(fd, buffer + done, len - done); if (n == 0) { /* file system full */ return -1; } else if (n != (size_t)-1) { /* some bytes written, continue */ done += n; } else if (errno != EAGAIN && errno != EINTR) { /* real error */ return -1; } } return 0; } static off_t _elf_output(Elf *elf, int fd, size_t len, off_t (*_elf_write)(Elf*, char*, size_t)) { char *buf; off_t err; elf_assert(len); #if HAVE_FTRUNCATE ftruncate(fd, 0); #endif /* HAVE_FTRUNCATE */ #if HAVE_MMAP /* * Make sure the file is (at least) len bytes long */ #if HAVE_FTRUNCATE lseek(fd, (off_t)len, SEEK_SET); if (ftruncate(fd, len)) { #else /* HAVE_FTRUNCATE */ { #endif /* HAVE_FTRUNCATE */ if (lseek(fd, (off_t)len - 1, SEEK_SET) != (off_t)len - 1) { seterr(ERROR_IO_SEEK); return -1; } if (xwrite(fd, "", 1)) { seterr(ERROR_IO_WRITE); return -1; } } buf = (void*)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf != (char*)-1) { if ((char)_elf_fill && !(elf->e_elf_flags & ELF_F_LAYOUT)) { memset(buf, _elf_fill, len); } err = _elf_write(elf, buf, len); munmap(buf, len); return err; } #endif /* HAVE_MMAP */ if (!(buf = (char*)malloc(len))) { seterr(ERROR_MEM_OUTBUF); return -1; } memset(buf, _elf_fill, len); err = _elf_write(elf, buf, len); if (err != -1 && (size_t)err == len) { if (lseek(fd, (off_t)0, SEEK_SET)) { seterr(ERROR_IO_SEEK); err = -1; } else if (xwrite(fd, buf, len)) { seterr(ERROR_IO_WRITE); err = -1; } } free(buf); return err; } off_t elf_update(Elf *elf, Elf_Cmd cmd) { unsigned flag; off_t len; if (!elf) { return -1; } elf_assert(elf->e_magic == ELF_MAGIC); if (cmd == ELF_C_WRITE) { if (!elf->e_writable) { seterr(ERROR_RDONLY); return -1; } if (elf->e_disabled) { seterr(ERROR_FDDISABLED); return -1; } } else if (cmd != ELF_C_NULL) { seterr(ERROR_INVALID_CMD); return -1; } if (!elf->e_ehdr) { seterr(ERROR_NOEHDR); } else if (elf->e_kind != ELF_K_ELF) { seterr(ERROR_NOTELF); } else if (elf->e_class == ELFCLASS32) { len = _elf32_layout(elf, &flag); if (len != -1 && cmd == ELF_C_WRITE && (flag & ELF_F_DIRTY)) { len = _elf_output(elf, elf->e_fd, (size_t)len, _elf32_write); } return len; } #if __LIBELF64 else if (elf->e_class == ELFCLASS64) { len = _elf64_layout(elf, &flag); if (len != -1 && cmd == ELF_C_WRITE && (flag & ELF_F_DIRTY)) { len = _elf_output(elf, elf->e_fd, (size_t)len, _elf64_write); } return len; } #endif /* __LIBELF64 */ else if (valid_class(elf->e_class)) { seterr(ERROR_UNIMPLEMENTED); } else { seterr(ERROR_UNKNOWN_CLASS); } return -1; }