| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024 |
- /*
- * 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 <private.h>
- #ifndef lint
- static const char rcsid[] = "@(#) $Id: update.c,v 1.34 2009/05/22 17:08:09 michael Exp $";
- #endif /* lint */
- #include <errno.h>
- #if HAVE_MMAP
- #include <sys/mman.h>
- #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
- if (ftruncate(fd, 0)) {
- seterr(ERROR_IO_WRITE);
- return -1;
- }
- #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;
- }
|