/* * Copyright (c) 1995 by Sun Microsystems, Inc. * All rights reserved. * * This source code is a product of Sun Microsystems, Inc. and is provided * for unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify this source code without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * THIS PROGRAM CONTAINS SOURCE CODE COPYRIGHTED BY SUN MICROSYSTEMS, INC. * SUN MICROSYSTEMS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABLITY * OF SUCH SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT * EXPRESS OR IMPLIED WARRANTY OF ANY KIND. SUN MICROSYSTEMS, INC. DISCLAIMS * ALL WARRANTIES WITH REGARD TO SUCH SOURCE CODE, INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN * NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM USE OF SUCH SOURCE CODE, REGARDLESS OF THE THEORY OF LIABILITY. * * This source code is provided with no support and without any obligation on * the part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS * SOURCE CODE OR ANY PART THEREOF. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 *//* * dynodump(3x) dumps a running executable into a specified ELF file. The new * file consists of the memory contents of the original file together with any * heap. This heap is assigned to a new `.heap' section within the new file. * * The new file may be re-executed, and will contain any data modifications * made to the original image up until the time dynodump(3x) was called. * * The original image may have undergone relocations (performed by ld.so.1) * prior to control being transferred to the image. These relocations will * reside as the data copied from the image. To prevent subsequent executions * of the new image from undergoing the same relocations, any relocation entries * (besides copy or jump slot relocations) are nulled out. Note that copy * relocations such as required for __iob must be reinitialized each time the * process starts, so it is not sufficient to simply null out the .dynamic * sections relocation information. The effect of this is that if the new * image was bound to definitions in any shared object dependencies, then these * dependencies *must* reside in the same location as when dynodump(3x) was * called. Any changes to the shared object dependencies of the new image, or * uses of such things as LD_PRELOAD, may result in the bindings encoded in the * image becoming invalid. * * The following flags modify the data of the image created: * * RTLD_SAVREL save the original relocation data. Under this option any * relocation offset is reset to contain the same data as was * found in the images original file. * * This option allows relocation information to be retained in the * new image so that it may be re-executed when the new image is * run. This allows far greater flexibility as the new image can * now take advantage of new shared objects. * * Note. under this mechanism, any data item that undergoes * relocation and is then further modified during the execution of * the image before dynodump(3x) is called will lose the * modification that occured during the applications execution. * * N.B. The above commentary is not quite correct in the flags have been hardwired * to RTLD_SAVREL. */#pragma ident "@(#) $Id: dynodump.c,v 1.6 1998/03/31 20:10:55 steve Exp $ - SMI"#define __EXTENSIONS__ 1#include <sys/param.h>#include <sys/procfs.h>#include <fcntl.h>#include <stdio.h>#include <libelf.h>#include <link.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <malloc.h>#include "machdep.h"#include "_dynodump.h"/* * Generic elf error message generator */staticintelferr(constchar*str){fprintf(stderr,"%s: %s\n",str,elf_errmsg(elf_errno()));return(1);}intdynodump(constchar*file);intdynodump(constchar*file){Elf*ielf,*oelf;Ehdr*iehdr,*oehdr;Phdr*iphdr,*ophdr,*data_phdr=0;Cache*icache,*ocache,*_icache,*_ocache;Cache*data_cache=0,*shstr_cache=0;Cache*heap_cache=0;Wordheap_sz=0;Elf_Scn*scn;Shdr*shdr;Elf_Data*data,rundata;Halfndx,_ndx;intfd,_fd;Addredata,_addr;char*istrs,*ostrs,*_ostrs,proc[16];constcharheap[]=".heap";prstatus_tpstat;/* make a call to the processor specific un-init stuff */dynodump_uninit();/* * Obtain a file descriptor for this process, * for the executable and get a prstatus_t * structure. */sprintf(proc,"/proc/%ld",getpid());if(((_fd=open(proc,O_RDONLY,0))==-1)||((fd=ioctl(_fd,PIOCOPENM,(void*)0))==-1)||(ioctl(_fd,PIOCSTATUS,&pstat)==-1)){fprintf(stderr,"/proc: initialization error: %s\n",strerror(errno));close(_fd);return(1);}close(_fd);/* * Initialize with the ELF library and make sure this is an executable * ELF file we're dealing with. */elf_version(EV_CURRENT);if((ielf=elf_begin(fd,ELF_C_READ,NULL))==NULL){close(fd);return(elferr("elf_begin"));}close(fd);if((elf_kind(ielf)!=ELF_K_ELF)||((iehdr=elf_getehdr(ielf))==NULL)||(iehdr->e_type!=ET_EXEC)){fprintf(stderr,"image is not an ELF executable\n");elf_end(ielf);return(1);}/* * Elf_elf_header(iehdr); *//* * Create the new output file. */if((fd=open(file,O_RDWR|O_CREAT|O_TRUNC,0777))==-1){fprintf(stderr,"%s: open failed: %s\n",file,strerror(errno));elf_end(ielf);return(1);}if((oelf=elf_begin(fd,ELF_C_WRITE,NULL))==NULL){elf_end(ielf);close(fd);return(elferr("elf_begin"));}/* * Obtain the input program headers. Remember the data segments * program header entry as this will be updated later to reflect the * new .heap sections size. */if((iphdr=elf_getphdr(ielf))==NULL)return(elferr("elf_getphdr"));for(ndx=0,ophdr=iphdr;ndx!=iehdr->e_phnum;ndx++,ophdr++){/* * Save the program header that contains the NOBITS section, or * the last loadable program header if no NOBITS exists. * A NOBITS section translates to a memory size requirement that * is greater than the file data it is mapped from. */if(ophdr->p_type==PT_LOAD){if(ophdr->p_filesz!=ophdr->p_memsz)data_phdr=ophdr;elseif(data_phdr){if(data_phdr->p_vaddr<ophdr->p_vaddr)data_phdr=ophdr;}elsedata_phdr=ophdr;}}if(data_phdr==0){fprintf(stderr,"no data segment found!\n");return(0);}/* * Obtain the input files section header string table. */if((scn=elf_getscn(ielf,iehdr->e_shstrndx))==NULL)return(elferr("elf_getscn"));if((data=elf_getdata(scn,NULL))==NULL)return(elferr("elf_getdata"));istrs=(char*)data->d_buf;/* * Construct a cache to maintain the input files section information. */if((icache=(Cache*)malloc(iehdr->e_shnum*sizeof(Cache)))==0){fprintf(stderr,"malloc failed: %s\n",strerror(errno));return(1);}_icache=icache;_icache++;/* * Traverse each section from the input file. */for(ndx=1,scn=0;(_icache->c_scn=elf_nextscn(ielf,scn));ndx++,scn=_icache->c_scn,_icache++){if((_icache->c_shdr=shdr=elf_getshdr(_icache->c_scn))==NULL)return(elferr("elf_getshdr"));if((_icache->c_data=elf_getdata(_icache->c_scn,NULL))==NULL)return(elferr("elf_getdata"));_icache->c_name=istrs+(size_t)(shdr->sh_name);/* * For each section that has a virtual address reestablish the * data buffer to point to the memory image. * * if (shdr->sh_addr) * _icache->c_data->d_buf = (void *)shdr->sh_addr; *//* * Remember the last section of the data segment, the new .heap * section will be added after this section. * If we already have one, then set data_cache to the previous * section and set heap_cache to this one. */if((shdr->sh_addr+shdr->sh_size)==(data_phdr->p_vaddr+data_phdr->p_memsz)){if(strcmp(_icache->c_name,heap)==0){#ifdef DEBUGprintf("Found a previous .heap section\n");#endifdata_cache=_icache-1;heap_cache=_icache;heap_sz=shdr->sh_size;}else{data_cache=_icache;}}/* * Remember the section header string table as this will be * rewritten with the new .heap name. */if((shdr->sh_type==SHT_STRTAB)&&((strcmp(_icache->c_name,".shstrtab"))==0))shstr_cache=_icache;}if(data_cache==0){fprintf(stderr,"final data section not found!\n");return(0);}/* * Determine the new .heap section to create. */rundata.d_buf=(void*)(data_cache->c_shdr->sh_addr+data_cache->c_shdr->sh_size);rundata.d_size=(int)sbrk(0)-(int)rundata.d_buf;rundata.d_type=ELF_T_BYTE;rundata.d_off=0;rundata.d_align=1;rundata.d_version=EV_CURRENT;/* * From the new data buffer determine the new value for _end and _edata. * This will also be used to update the data segment program header. * * If we had a .heap section, then its size is part of the program * headers notion of data size. Because we're only going to output one * heap section (ignoring the one in the running binary) we need to * subract the size of that which we're ignoring. */if(heap_cache){edata=S_ROUND((data_phdr->p_vaddr+data_phdr->p_memsz-heap_sz),rundata.d_align)+rundata.d_size;}else{edata=S_ROUND((data_phdr->p_vaddr+data_phdr->p_memsz),rundata.d_align)+rundata.d_size;}/* * We're now ready to construct the new elf image. * * Obtain a new elf header and initialize it with any basic information * that isn't calculated as part of elf_update(). Bump the section * header string table index to account for the .heap section we'll be * adding. */if((oehdr=elf_newehdr(oelf))==NULL)return(elferr("elf_newehdr"));oehdr->e_entry=iehdr->e_entry;oehdr->e_machine=iehdr->e_machine;oehdr->e_type=iehdr->e_type;oehdr->e_flags=iehdr->e_flags;/* * If we already have a heap section, we don't need any adjustment */if(heap_cache)oehdr->e_shstrndx=iehdr->e_shstrndx;elseoehdr->e_shstrndx=iehdr->e_shstrndx+1;#ifdef DEBUGprintf("iehdr->e_flags = %x\n",iehdr->e_flags);printf("iehdr->e_entry = %x\n",iehdr->e_entry);printf("iehdr->e_shstrndx= %d\n",iehdr->e_shstrndx);printf("iehdr->e_machine = %d\n",iehdr->e_machine);printf("iehdr->e_type = 0x%x\n",iehdr->e_type);printf("oehdr->e_machine = %d\n",oehdr->e_machine);printf("oehdr->e_type = 0x%x\n",oehdr->e_type);#endif/* * Obtain a new set of program headers. Initialize these with the same * information as the input program headers and update the data segment * to reflect the new .heap section. */if((ophdr=elf_newphdr(oelf,iehdr->e_phnum))==NULL)return(elferr("elf_newphdr"));for(ndx=0;ndx!=iehdr->e_phnum;ndx++,iphdr++,ophdr++){*ophdr=*iphdr;if(data_phdr==iphdr)ophdr->p_filesz=ophdr->p_memsz=edata-ophdr->p_vaddr;}/* * Obtain a new set of sections. */_icache=icache;_icache++;for(ndx=1;ndx!=iehdr->e_shnum;ndx++,_icache++){/* * Skip the heap section of the running executable */if(_icache==heap_cache)continue;/* * Create a matching section header in the output file. */if((scn=elf_newscn(oelf))==NULL)return(elferr("elf_newscn"));if((shdr=elf_getshdr(scn))==NULL)return(elferr("elf_getshdr"));*shdr=*_icache->c_shdr;/* * Create a matching data buffer for this section. */if((data=elf_newdata(scn))==NULL)return(elferr("elf_newdata"));*data=*_icache->c_data;/* * For each section that has a virtual address reestablish the * data buffer to point to the memory image. Note, we skip * the plt section. */if((shdr->sh_addr)&&(!((shdr->sh_type==SHT_PROGBITS)&&(strcmp(_icache->c_name,".plt")==0))))data->d_buf=(void*)shdr->sh_addr;/* * Update any NOBITS section to indicate that it now contains * data. */if(shdr->sh_type==SHT_NOBITS)shdr->sh_type=SHT_PROGBITS;/* * Add the new .heap section after the last section of the * present data segment. If we had a heap section, then * this is the section preceding it. */if(data_cache==_icache){if((scn=elf_newscn(oelf))==NULL)return(elferr("elf_newscn"));if((shdr=elf_getshdr(scn))==NULL)return(elferr("elf_getshdr"));shdr->sh_type=SHT_PROGBITS;shdr->sh_flags=SHF_ALLOC|SHF_WRITE;if((data=elf_newdata(scn))==NULL)return(elferr("elf_newdata"));*data=rundata;}/* * Update the section header string table size to reflect the * new section name (only if we didn't already have a heap). */if(!heap_cache){if(shstr_cache&&(shstr_cache==_icache)){data->d_size+=sizeof(heap);}}}/* * Write out the new image, and obtain a new elf descriptor that will * allow us to write to the new image. */if(elf_update(oelf,ELF_C_WRITE)==-1)return(elferr("elf_update"));elf_end(oelf);if((oelf=elf_begin(fd,ELF_C_RDWR,NULL))==NULL)return(elferr("elf_begin"));if((oehdr=elf_getehdr(oelf))==NULL)return(elferr("elf_getehdr"));/* * Obtain the output files section header string table. */if((scn=elf_getscn(oelf,oehdr->e_shstrndx))==NULL)return(elferr("elf_getscn"));if((data=elf_getdata(scn,NULL))==NULL)return(elferr("elf_getdata"));ostrs=_ostrs=(char*)data->d_buf;*_ostrs++='\0';/* * Construct a cache to maintain the output files section information. */if((ocache=(Cache*)malloc(oehdr->e_shnum*sizeof(Cache)))==0){fprintf(stderr,"malloc failed: %s\n",strerror(errno));return(1);}_ocache=ocache;_ocache++;_icache=icache;_icache++;/* * Traverse each section from the input file rebuilding the section * header string table as we go. */_ndx=_addr=0;for(ndx=1,scn=0;(_ocache->c_scn=elf_nextscn(oelf,scn));ndx++,scn=_ocache->c_scn,_ocache++,_icache++){constchar*strs;if(_icache==heap_cache){#ifdef DEBUGprintf("ignoring .heap section in input\n");#endif_icache++;}if((_ocache->c_shdr=shdr=elf_getshdr(_ocache->c_scn))==NULL)return(elferr("elf_getshdr"));if((_ocache->c_data=elf_getdata(_ocache->c_scn,NULL))==NULL)return(elferr("elf_getdata"));/* * If were inserting the new .heap section, insert the new * section name and initialize its virtual address. */if(_addr){strs=heap;shdr->sh_addr=S_ROUND(_addr,shdr->sh_addralign);_addr=0;}else{strs=istrs+(size_t)(_icache->c_shdr->sh_name);}strcpy(_ostrs,strs);shdr->sh_name=_ostrs-ostrs;_ocache->c_name=_ostrs;_ostrs+=strlen(strs)+1;/* * If we've inserted a new section any later section may need * their sh_link fields updated. * If we already had a heap section, then this is not required. */if(!heap_cache){if(_ndx){if(_ocache->c_shdr->sh_link>=_ndx)_ocache->c_shdr->sh_link++;}}/* * If this is the last section of the original data segment * determine sufficient information to initialize the new .heap * section which will be obtained next. */if(data_cache==_icache){_ndx=ndx+1;_addr=shdr->sh_addr+shdr->sh_size;_icache--;data_cache=0;}}/* * Now that we have a complete description of the new image update any * sections that are required. * * o update the value of _edata and _end. * * o reset any relocation entries if necessary. */_ocache=&ocache[1];_icache=&icache[1];for(ndx=1;ndx<oehdr->e_shnum;ndx++,_ocache++,_icache++){if((_ocache->c_shdr->sh_type==SHT_SYMTAB)||(_ocache->c_shdr->sh_type==SHT_DYNSYM))update_sym(ocache,_ocache,edata);if(_ocache->c_shdr->sh_type==M_REL_SHT_TYPE)update_reloc(ocache,_ocache,icache,_icache,oehdr->e_shnum);}if(elf_update(oelf,ELF_C_WRITE)==-1)return(elferr("elf_update"));elf_end(oelf);elf_end(ielf);return(0);}