/* * Copyright (C) 1996-1998 Ilya Ryzhenkov (orangy@inetlab.com) * This file maintains general DLM image functionality */ #include #include #include "helpdefs.h" #include "dlmimage.h" #include "dlm_sym.h" #include "dlm_rslv.h" #include "dlm.h" int dlmerrno; int dlmflags=DLMFLAG_DUP_OVRIDE; /* This function destroys DLM structure */ static void _dlm_free_image(TDlm *dlm) { if (!dlm) return; /* Just in case someone will use free'd struct */ FREE(dlm->filename); dlm->filename=0; FREE(dlm->Sections); dlm->Sections=0; FREE(dlm->Symbols); dlm->Symbols=0; FREE(dlm->Relocs); dlm->Relocs=0; FREE(dlm->Strings); dlm->Strings=0; FREE(dlm->Base); dlm->Base=0; FREE(dlm->RelVal); dlm->RelVal=0; FREE(dlm); } /* this function loads DLM structures and data into internal sturcture for further processing Arguments : path to file with DLM and offset in file Return : Pointer to newely allocated structure on success NULL on file or memory fail */ static TDlm *_dlm_load_image(char *path,unsigned long offset,char *loadname) { DEFNEW(dlm,TDlm); DEFNEW(fh,DLMHDR); int f,i,err=0,sz; if (!dlm || !fh) { FREE(fh); FREE(dlm); ERR(NOMEM); return 0; } f=open(path, O_RDWR | O_BINARY); if (f==-1) { FREE(fh); FREE(dlm); ERR(FILE); return 0; } if (lseek(f,offset,SEEK_SET)!=offset) { close(f); FREE(fh); FREE(dlm); ERR(FILE); return 0; } if (!READ(f,fh,1)) { close(f); FREE(fh); FREE(dlm); ERR(FILE); return 0; } if (fh->f_magic!=DLM_MAGIC) /* Just to be sure */ { close(f); FREE(fh); FREE(dlm); ERR(INVF); return 0; } /* Copy general DLM info */ dlm->LoadCount=1; dlm->filename=strdup(loadname); dlm->NumSect=fh->f_nscns; /* Number of sections */ dlm->NumSym=fh->f_nsyms; /* Number of symbols */ dlm->NumRel=fh->f_nreloc; /* Number of relocations */ dlm->StrSize=fh->f_strsz; /* Size of string table */ dlm->VSize=fh->f_vsize; /* Total size of section's data */ dlm->eh_buf=0; dlm->cached_pos=0xFFFFFFF; dlm->cached_idx=0xFFFFFFF; FREE(fh); /* Allocating memory for structures */ dlm->Sections=NEWAR(DLMSCN,dlm->NumSect); dlm->Relocs =NEWAR(DLMREL,dlm->NumRel); dlm->Symbols =NEWAR(DLMSYM,dlm->NumSym); dlm->Strings =NEWAR(char,dlm->StrSize); dlm->Base =NEWAR(char,dlm->VSize); dlm->RelVal =NEWAR(unsigned long,dlm->NumRel); if (!dlm->Sections || !dlm->Symbols || !dlm->Relocs || !dlm->Strings || !dlm->Base || !dlm->RelVal) err=2; else /* Loading structures into memory */ if (!READ(f,dlm->Sections,dlm->NumSect)) err=1; else if (!READ(f,dlm->Symbols,dlm->NumSym)) err=1; else if (!READ(f,dlm->Relocs,dlm->NumRel)) err=1; else if (!READ(f,dlm->Strings,dlm->StrSize)) err=1; else { /* Loading section's data */ for (i=0; iNumSect; i++) if (dlm->Sections[i].s_size) { /* Does section's data stored in file ? */ if (dlm->Sections[i].s_scnptr) { /* Yes, load it */ sz=lseek(f,offset+dlm->Sections[i].s_scnptr,SEEK_SET); if (sz!=offset+dlm->Sections[i].s_scnptr) { err=1; break; } sz=read(f,dlm->Base+dlm->Sections[i].s_vaddr,dlm->Sections[i].s_size); if (sz!=dlm->Sections[i].s_size) { err=1; break; } } else { /* No, zero fill section */ memset(dlm->Base+dlm->Sections[i].s_vaddr,0,dlm->Sections[i].s_size); } } //*((unsigned *)(dlm->Base+dlm->VSize))=0; /* Save original data from relocation places for later use Unreferenced symbols will point to 0xFFFFFFFF, thus we need this data for future re-referencing */ for (i=0; iNumRel; i++) dlm->RelVal[i]=*((unsigned long *)(dlm->Base+dlm->Relocs[i].r_vaddr)); } close(f); if (err) { if (err==2) ERR(NOMEM); else ERR(FILE); _dlm_free_image(dlm); return 0; } else return dlm; } void _dlm_dup_fail(char *name, TDlm* dlm) { printf("\nExiting due to duplicate symbol %s\n",name); if ((int)dlm>0) printf("Duplicate found in DLM %s\n",dlm->filename); exit(-1); } static void _dlm_process_symbols(TDlm *dlm) { int i; DLMSYM *eh; void *symaddr; if (!dlm) return; /* Loop thru DLM symbols */ for (i=0; iNumSym; i++) { eh=dlm->Symbols+i; switch (eh->e_flags) { case 1: /* Import symbol */ _sym_add_import(dlm->Strings+eh->e_name,dlm); if ((symaddr=_sym_find_export(dlm->Strings+eh->e_name,0))) _dlm_resolve_exact(dlm,i,symaddr); else /* resolve it to 0xffffffff to produce GPF on use */ _dlm_resolve_exact(dlm,i,(void*)0xffffffff); break; case 2: /* Export symbol */ if (strncmp(dlm->Strings+eh->e_name,"_constructor",12) && strncmp(dlm->Strings+eh->e_name,"_destructor",11)) { symaddr=_sym_find_export(dlm->Strings+eh->e_name,0); if (symaddr && ((dlmflags&DLMFLAG_DUP)==DLMFLAG_DUP_FAIL)) _dlm_dup_fail(dlm->Strings+eh->e_name,dlm); _dlm_resolve_exact(dlm,i,eh->e_value+dlm->Base); if (!symaddr || ((dlmflags&DLMFLAG_DUP)!=DLMFLAG_DUP_IGNORE)) { if (_sym_add_export(dlm->Strings+eh->e_name,dlm->Base+eh->e_value,dlm)) { if (!symaddr || ((dlmflags&DLMFLAG_DUP)==DLMFLAG_DUP_OVRIDE)) _dlm_resolve_to_sym(dlm->Strings+eh->e_name); } else ERR(NOMEM); } } break; case 4: /* Section symbol */ _dlm_resolve_exact(dlm,i,eh->e_value+dlm->Base); break; case 8: case 10: /* Common symbol */ symaddr=_sym_find_export(dlm->Strings+eh->e_name,0); /* Is it already exist ? */ if (!symaddr) { /* No, allocate, put and resolve */ symaddr=malloc(eh->e_value); if (symaddr) { memset(symaddr,0,eh->e_value); if (_sym_add_export(dlm->Strings+eh->e_name,symaddr,dlm)) _dlm_resolve_to_sym(dlm->Strings+eh->e_name); else ERR(NOMEM); } else symaddr=(void*)0xffffffff; } else _sym_add_export(dlm->Strings+eh->e_name,symaddr,dlm); _dlm_resolve_exact(dlm,i,symaddr); break; default : } } } static void _dlm_unprocess_symbols(TDlm *dlm) { int i; DLMSYM *eh; void *symaddr; if (!dlm) return; /* Loop thru DLM symbols */ for (i=0; iNumSym; i++) { eh=dlm->Symbols+i; switch (eh->e_flags) { case 1: /* Import symbol */ _sym_del_import(dlm->Strings+eh->e_name,dlm); break; case 2: /* Export symbol */ //printf("removing export %s\n",dlm->Strings+eh->e_name); _sym_del_export(dlm->Strings+eh->e_name,dlm); _dlm_resolve_to_sym(dlm->Strings+eh->e_name); break; case 8: case 10: /* Common symbol */ symaddr=_sym_find_export(dlm->Strings+eh->e_name,0); _sym_del_export(dlm->Strings+eh->e_name,dlm); if (!_sym_find_export(dlm->Strings+eh->e_name,0)) FREE(symaddr); _dlm_resolve_to_sym(dlm->Strings+eh->e_name); break; default : } } } static int _dlm_call_local(char *name,TDlm *dlm) { int i; DLMSYM *eh; int (*f)(); if (!dlm) return -1; /* Loop thru DLM symbols */ for (i=0; iNumSym; i++) { eh=dlm->Symbols+i; if (strncmp(name,dlm->Strings+eh->e_name,strlen(name))) continue; if (!(eh->e_flags&DLMSYM_EXPORT) || (eh->e_flags&DLMSYM_COMMON)) return -2; f=(int (*)())(dlm->Base+eh->e_value); return f(); } return -1; } static void _dlm_call_cpp(TDlm *dlm, char *sname) { int i,j; DLMSCN *sh; unsigned long *ptr; for (i=0; iNumSect; i++) { sh=dlm->Sections+i; if (!strncmp(sname,sh->s_name,8)) { ptr=(unsigned long*)(dlm->Base+sh->s_vaddr); for (j=0; js_size/4; j++) ((void (*)())(*ptr++))(); } } } // from libgcc.a void __register_frame_info (void *begin, void *buf); void __deregister_frame_info(void *begin); static void _dlm_reg_eh(TDlm *dlm) { int i; DLMSCN *sh; for (i=0; iNumSect; i++) { sh=dlm->Sections+i; if (!strncmp(".eh_fram",sh->s_name,8)) { dlm->eh_buf=malloc(32); memset(dlm->eh_buf,0,32); //printf("Registering frame info object %p for %s\n",dlm->eh_buf,dlm->filename); __register_frame_info(dlm->Base+sh->s_vaddr,dlm->eh_buf); } } } static void _dlm_dereg_eh(TDlm *dlm) { int i; DLMSCN *sh; for (i=0; iNumSect; i++) { sh=dlm->Sections+i; if (!strncmp(".eh_fram",sh->s_name,8)) { //printf("Deregistering frame info at address %p\n",dlm->Base+sh->s_vaddr); __deregister_frame_info(dlm->Base+sh->s_vaddr); free(dlm->eh_buf); } } } static int _dlm_autoload(TDlm *dlm) { int i,retval=1,res; int (*loadfunc)(char *name); loadfunc=(typeof(loadfunc))_sym_find_export("_LoadDLM",0); if (!loadfunc) // hmmm... may be use _dlm_load in this case ? { printf("Internal Fatal Error (LoadDLM not defined)\n"); exit(-1); } for (i=0; iNumSect; i++) if (!strncmp(".aload",dlm->Sections[i].s_name,8)) { res=loadfunc(dlm->Base+dlm->Sections[i].s_vaddr); if (!res) retval=0; } return retval; } static int _dlm_autounload(TDlm *dlm) { int i,retval=1,res; int (*unloadfunc)(char *name); unloadfunc=(typeof(unloadfunc))_sym_find_export("_UnloadDLM",0); if (!unloadfunc) // hmmm... may be use _dlm_unload in this case ? { printf("Internal Fatal Error (UnloadDLM not defined)\n"); exit(-1); } for (i=0; iNumSect; i++) if (!strncmp(".aload",dlm->Sections[i].s_name,8)) { res=unloadfunc(dlm->Base+dlm->Sections[i].s_vaddr); if (!res) retval=0; } return retval; } static char *_dlm_search_path=0; void _dlm_set_search_path(char* path) { if (_dlm_search_path) free(_dlm_search_path); _dlm_search_path=strdup(path); } static char *_dlm_make_full_name(char *path) { static char drive[MAXDRIVE+1],dir[MAXDIR+1],name[MAXFILE+1],ext[MAXEXT+1]; static char retbuf[MAXPATH+1],*dlmpath,*curdir; static int flags; if (!_dlm_search_path) return path; flags=fnsplit(path,drive,dir,name,ext); if (!(flags&(DRIVE|DIRECTORY))) /* it's just a name, let's search */ { dlmpath=strdup(_dlm_search_path); curdir=strtok(dlmpath,";"); while (curdir) { strncpy(retbuf,curdir,MAXPATH); strncat(retbuf,path,MAXPATH-strlen(retbuf)-1); if (__file_exists(retbuf)) break; curdir=strtok(0,";"); } FREE(dlmpath); if (!curdir) return path; else return retbuf; } else return path; } static int _dlm_do_unload(TDlm *dlm) { _dlm_autounload(dlm); _dlm_unprocess_symbols(dlm); _sym_del_dlm(dlm->filename); _dlm_free_image(dlm); return 1; } int _dlm_unload(char *path) { TDlm *dlm; if (!(dlm=_sym_find_dlm(path))) { ERR(NODLM); return 0; } if (--(dlm->LoadCount)) return 1; _dlm_call_cpp(dlm,".dtor"); _dlm_call_local("_destructor",dlm); _dlm_dereg_eh(dlm); return _dlm_do_unload(dlm); } int _dlm_load(char *origpath) { TDlm *dlm; unsigned long magic,offset; int f,retval; char *path; if ((dlm=_sym_find_dlm(origpath))) { dlm->LoadCount++; return 1; } path=_dlm_make_full_name(origpath); /* Now we are looking for DLM offset in file */ f=open(path, O_RDWR | O_BINARY); if (f==-1) { ERR(FILE); return 0; } if (read(f,&magic,sizeof(magic))!=sizeof(magic)) { close(f); ERR(FILE); return 0; } if (magic==DLM_MAGIC) { close(f); dlm=_dlm_load_image(path,0,origpath); /* this is standalone DLM */ } else /* Check for embeded DLM - last 4 bytes in files is offset */ { lseek(f,-sizeof(offset),SEEK_END); read(f,&offset,sizeof(offset)); /* Get offset */ lseek(f,offset,SEEK_SET); /* Check magic at offset */ if (read(f,&magic,sizeof(magic))!=sizeof(magic)) { close(f); ERR(FILE); return 0; } if (magic!=DLM_MAGIC) { close(f); ERR(INVF); return 0; } close(f); dlm=_dlm_load_image(path,offset,origpath); /* this is embeded DLM */ } if (!dlm) return 0; if (!_sym_add_dlm(origpath,dlm)) { ERR(NOMEM); _dlm_do_unload(dlm); return 0; } _dlm_process_symbols(dlm); if (!_dlm_autoload(dlm)) { ERR(AUTO); return 0; } _dlm_reg_eh(dlm); retval=_dlm_call_local("_constructor",dlm); if (!retval) { _dlm_do_unload(dlm); ERR(CTOR); return 0; } _dlm_call_cpp(dlm,".ctor"); return 1; } void __main() /* dummy function, workaround to allow main */ { return; } void _dlm_unload_all() { TDlm *dlm; while (1) // this will force unloading of multiply-loaded dlms { if (!_sym_dlmsearch()) return; while ((dlm=_sym_dlmnext())) _dlm_unload(dlm->filename); } }