/*
* 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);
}
}