|
|
|
|
|
|
|
#include <global.h> |
#include "fdlib.h" |
#define DL_INTERNAL |
#include "pike_dlfcn.h" |
#include "pike_memory.h" |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <errno.h> |
#include <malloc.h> |
#include <windows.h> |
#include <memory.h> |
#include <sys/stat.h> |
#include <assert.h> |
|
static char *dlerr=0; |
|
|
|
|
|
|
|
|
|
#define DL_VERBOSE 1 |
|
#define REALLY_FLUSH() /* do{ fflush(stderr); Sleep(500); }while(0) */ |
|
#ifdef DLDEBUG |
#define FLUSH() REALLY_FLUSH() |
#define DO_IF_DLDEBUG(X) X |
#else |
#define FLUSH() |
#define DO_IF_DLDEBUG(X) |
#endif |
|
#ifndef PIKE_CONCAT |
|
#include <io.h> |
#include <fcntl.h> |
|
#define INT8 char |
#define INT16 short |
#define INT32 int |
#define ptrdiff_t long |
#define STRTOL strtol |
|
#define RTLD_GLOBAL 1 |
#define RTLD_LAZY 0 /* never */ |
#define RTLD_NOW 0 /* always */ |
|
#define fd_open open |
#define fd_close close |
#define fd_read read |
#define fd_RDONLY O_RDONLY |
#define fd_BINARY O_BINARY |
|
long hashmem(char *mem, ptrdiff_t size) |
{ |
long ret=size * 9278234247; |
while(--size>=0) ret+=(ret<<4) + *(mem++); |
return ret; |
} |
|
size_t STRNLEN(char *s, size_t maxlen) |
{ |
char *tmp=memchr(s,0,maxlen); |
if(tmp) return tmp-s; |
return maxlen; |
} |
|
#else /* PIKE_CONCAT */ |
|
RCSID("$Id: dlopen.c,v 1.11 2001/01/26 12:40:47 hubbe Exp $"); |
|
#endif |
|
|
|
struct Sym |
{ |
struct Sym *next; |
void *sym; |
size_t len; |
char name[1]; |
}; |
|
struct Htable |
{ |
size_t size; |
size_t entries; |
struct Sym *symbols[1]; |
}; |
|
static struct Htable *alloc_htable(size_t size) |
{ |
int e; |
struct Htable *ret; |
#ifdef DLDEBUG |
fprintf(stderr,"alloc_htable(%d)\n",size); |
FLUSH(); |
#endif |
ret=(struct Htable *)malloc(sizeof(struct Htable) + |
sizeof(void *)*(size-1)); |
ret->size=size; |
ret->entries=0; |
for(e=0;e<size;e++) ret->symbols[e]=0; |
return ret; |
} |
|
static struct Htable *htable_add_space(struct Htable *h, |
size_t extra_space) |
{ |
#if 0 |
if(h->entries+extra_space > h->size) |
{ |
size_t new_size = (h->size * 2)+1; |
if(h->entries+extra_space > new_size) |
new_size = (h->entries+extra_space) | 0x10101; |
|
|
} |
#endif |
return h; |
} |
|
|
static struct Sym *htable_get_low(struct Htable *h, |
char *name, |
size_t len, |
size_t hval) |
{ |
struct Sym *tmp; |
|
#ifdef DLDEBUG |
if(name[len]) |
fprintf(stderr,"htable_get_low(%c%c%c%c%c%c%c%c) len=%d hval=%x/%x\n", |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7], |
len, |
hval,h->size); |
else |
fprintf(stderr,"htable_get_low(%s) len=%d hval=%x/%x\n",name,len, |
hval,h->size); |
#endif |
|
for(tmp=h->symbols[hval];tmp;tmp=tmp->next) |
{ |
|
|
if(tmp->len == len && !memcmp(name, tmp->name, len)) |
{ |
return tmp; |
} |
} |
return 0; |
} |
|
static void *htable_get(struct Htable *h, char *name, size_t l) |
{ |
struct Sym * tmp; |
size_t hval; |
DO_HASHMEM(hval, name, l, 128); |
|
if((tmp=htable_get_low(h, name, l, hval % h->size))) |
return tmp->sym; |
return 0; |
} |
|
static void htable_put(struct Htable *h, char *name, size_t l, void *ptr) |
{ |
struct Sym *tmp; |
|
size_t hval; |
DO_HASHMEM(hval, name, l, 128); |
hval %= h->size; |
|
#ifdef DLDEBUG |
if(name[l]) |
{ |
fprintf(stderr,"DL: htable_put(%c%c%c%c%c%c%c%c,%p)\n", |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7], |
ptr); |
}else{ |
fprintf(stderr,"DL: htable_put(%s,%p)\n",name,ptr); |
} |
#endif |
|
|
if((tmp=htable_get_low(h, name, l, hval))) |
{ |
tmp->sym=ptr; |
return; |
} |
tmp=(struct Sym *)malloc(sizeof(struct Sym) + l); |
if(!tmp) |
{ |
|
exit(1); |
} |
memcpy(tmp->name, name, l); |
tmp->name[l]=0; |
tmp->len=l; |
tmp->next=h->symbols[hval]; |
tmp->sym=ptr; |
h->symbols[hval]=tmp; |
h->entries++; |
} |
|
static void htable_free(struct Htable *h, void(*hfree)(void *)) |
{ |
size_t hval; |
struct Sym *tmp,*next; |
if(hfree) |
{ |
for(hval=0; hval<h->size; hval++) |
{ |
for(next=h->symbols[hval];tmp=next;) |
{ |
next=tmp->next; |
hfree(tmp->sym); |
free((char *)tmp); |
} |
} |
}else{ |
for(hval=0; hval<h->size; hval++) |
{ |
for(next=h->symbols[hval];tmp=next;) |
{ |
next=tmp->next; |
free((char *)tmp); |
} |
} |
} |
free((char *)h); |
} |
|
|
|
static int filesize(char *filename) |
{ |
struct stat st; |
#ifdef DLDEBUG |
fprintf(stderr,"filesize(%s)\n",filename); |
#endif |
if(stat(filename, &st)<0) return -1; |
return st.st_size; |
} |
|
static char *read_file(char *name, size_t *len) |
{ |
ptrdiff_t tmp; |
char *buffer; |
int fd; |
int l=filesize(name); |
if(l<0) return 0; |
|
len[0]=(size_t)l; |
do{ |
#ifdef DLDEBUG |
fprintf(stderr,"Opening file: %s\n",name); |
#endif |
fd=fd_open(name,fd_RDONLY | fd_BINARY, 0); |
}while(fd<0 && errno == EINTR); |
|
if(fd < 0) return 0; |
|
buffer=(unsigned char *)malloc(l); |
|
for(tmp=0;tmp<l;) |
{ |
ptrdiff_t x; |
do{ |
x=fd_read(fd, buffer + tmp, l - tmp); |
} while(x < 0 && errno == EINTR); |
#ifdef DLDEBUG |
fprintf(stderr,"Reading ... tmp=%d len=%d x=%d errno=%d\n",tmp,l,x,errno); |
#endif |
if(x > 0) tmp+=x; |
if(x<0) |
{ |
free(buffer); |
return 0; |
} |
} |
while(fd_close(fd) < 0 && errno==EINTR); |
#ifdef DLDEBUG |
fprintf(stderr,"Done reading\n"); |
fflush(stderr); |
Sleep(500); |
#endif |
return buffer; |
} |
|
|
|
struct DLHandle |
{ |
int refs; |
char *filename; |
int flags; |
struct DLHandle *next; |
|
size_t memsize; |
void *memory; |
struct Htable *htable; |
struct DLLList *dlls; |
}; |
|
struct DLLList |
{ |
struct DLLList *next; |
HINSTANCE dll; |
}; |
|
static struct DLHandle *first; |
static struct DLHandle global_dlhandle; |
|
|
static void *lookup_dlls(struct DLLList *l, char *name) |
{ |
void *ret; |
char *tmp,*tmp2; |
if(name[0]=='_') name++; |
#ifdef DLDEBUG |
fprintf(stderr,"DL: lookup_dlls(%s)\n",name); |
#endif |
#if 1 |
if((tmp=STRCHR(name,'@'))) |
{ |
tmp2=(char *)alloca(tmp - name + 1); |
MEMCPY(tmp2,name,tmp-name); |
tmp2[tmp-name]=0; |
#ifdef DLDEBUG |
fprintf(stderr,"DL: Ordinal cutoff: %s -> %s\n",name,tmp2); |
#endif |
name=tmp2; |
} |
#endif |
|
while(l) |
{ |
#ifdef DLDEBUG |
char modname[4711]; |
GetModuleFileName(l->dll, modname, 4710); |
fprintf(stderr,"Looking for %s in %s ...\n", |
name, |
modname); |
#endif |
if((ret=GetProcAddress(l->dll,name))) return ret; |
l=l->next; |
} |
return 0; |
} |
|
static void dlllist_free(struct DLLList *l) |
{ |
if(l) |
{ |
FreeLibrary(l->dll); |
dlllist_free(l->next); |
free((char *)l); |
} |
} |
|
static int append_dlllist(struct DLLList **l, |
char *name) |
{ |
struct DLLList *n; |
HINSTANCE tmp; |
#ifdef DLDEBUG |
fprintf(stderr,"append_dlllist(%s)\n",name); |
FLUSH(); |
#endif |
tmp=LoadLibrary(name); |
if(!tmp) return 0; |
n=(struct DLLList *)malloc(sizeof(struct DLLList)); |
if(!n) |
{ |
dlerr="Out of memory"; |
return 0; |
} |
n->dll=tmp; |
#ifdef DLDEBUG |
fprintf(stderr,"append_dlllist(%s)->%p\n",name,n->dll); |
FLUSH(); |
#endif |
n->next=0; |
while( *l ) l= & (*l)->next; |
*l=n; |
return 1; |
} |
|
#define i1(X) (data->buffer[(X)]) |
#define i2(X) (i1((X))|(i1((X)+1)<<8)) |
#define i4(X) (i1((X))|(i1((X)+1)<<8)|\ |
(i1((X)+2)<<16)|(i1((X)+3)<<24)) |
|
#define i8(X) (i1((X))|(i1((X)+1)<<8)|\ |
(i1((X)+2)<<16)|(i1((X)+3)<<24)) |
|
#define COFF_SECT_NOLOAD (1<<1) |
#define COFF_SECT_MEM_DISCARDABLE (1<<25) |
#define COFF_SECT_MEM_LNK_REMOVE (1<<11) |
#define COFF_SECT_LNK_INFO 0x200 |
|
#define COFF_SYMBOL_EXTERN 2 |
|
struct COFF |
{ |
INT16 machine; |
INT16 num_sections; |
INT32 timestamp; |
INT32 symboltable; |
INT32 num_symbols; |
INT16 sizeof_optheader; |
INT16 characteristics; |
}; |
|
union COFFname |
{ |
INT32 ptr[2]; |
char text[8]; |
}; |
|
struct COFFsection |
{ |
union COFFname name; |
|
INT32 virtual_size; |
INT32 virtual_addr; |
|
INT32 raw_data_size; |
INT32 ptr2_raw_data; |
INT32 ptr2_relocs; |
INT32 ptr2_linenums; |
INT16 num_relocs; |
INT16 num_linenums; |
INT32 characteristics; |
}; |
|
struct COFFSymbol |
{ |
union COFFname name; |
INT32 value; |
INT16 secnum; |
INT16 type; |
INT8 class; |
INT8 aux; |
}; |
|
|
#define COFFReloc_type_dir32 6 |
#define COFFReloc_type_dir32nb 7 |
#define COFFReloc_type_sect 10 |
#define COFFReloc_type_sectrel 11 |
#define COFFReloc_type_rel32 20 |
|
|
|
|
struct COFFReloc |
{ |
INT32 location; |
INT32 symbol; |
INT16 type; |
}; |
|
|
struct DLTempData |
{ |
unsigned char *buffer; |
ptrdiff_t buflen; |
int flags; |
}; |
|
|
struct DLObjectTempData |
{ |
unsigned char *buffer; |
size_t buflen; |
int flags; |
|
struct COFF *coff; |
struct COFFSymbol *symbols; |
char *stringtable; |
struct COFFsection *sections; |
|
|
char **symbol_addresses; |
char **section_addresses; |
}; |
|
|
static void *low_dlsym(struct DLHandle *handle, |
char *name, |
size_t len, |
int self) |
{ |
void *ptr; |
char *tmp; |
if(len > 6 && !memcmp(name,"__imp_",6)) |
{ |
name+=6; |
len-=6; |
}else{ |
self=0; |
} |
tmp=name; |
#ifdef DLDEBUG |
if(name[len]) |
fprintf(stderr,"low_dlsym(%c%c%c%c%c%c%c%c)\n", |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7]); |
else |
fprintf(stderr,"low_dlsym(%s)\n",name); |
#endif |
if(!self) |
ptr=htable_get(handle->htable,name,len); |
else |
ptr=0; |
|
if(!ptr) |
{ |
if(name[len]) |
{ |
tmp=(char *)alloca(len+1); |
MEMCPY(tmp, name, len); |
tmp[len]=0; |
} |
ptr=lookup_dlls(handle->dlls, tmp); |
} |
#ifdef DLDEBUG |
if(!ptr) |
{ |
fprintf(stderr,"Failed to find identifier %s\n",tmp); |
} |
#endif |
return ptr; |
} |
|
void *dlsym(struct DLHandle *handle, char *name) |
{ |
return low_dlsym(handle, name, strlen(name), 0); |
} |
|
const char *dlerror(void) |
{ |
return dlerr; |
} |
|
static parse_link_info(struct DLHandle *ret, |
struct DLObjectTempData *data, |
char *info, |
int len) |
{ |
int ptr=0; |
char buffer[1024]; |
|
#ifdef DLDEBUG |
{ |
int z; |
fprintf(stderr,"DLINFO(%d): ",len); |
for(z=0;z<len;z++) fprintf(stderr,"%c",info[z]); |
fprintf(stderr,"\n"); |
FLUSH(); |
} |
#endif |
|
while(ptr < len) |
{ |
int l,x=ptr; |
char *end; |
|
#ifdef DLDEBUG |
fprintf(stderr,"Parse link info ptr=%d\n",ptr,l); |
FLUSH(); |
#endif |
|
end=MEMCHR(info+ptr,' ',len-ptr); |
if(end) |
l=end - (info+x); |
else |
l=len-ptr; |
|
#ifdef DLDEBUG |
fprintf(stderr,"Parse link info ptr=%d len=%d '%c%c%c%c%c%c%c%c'\n", |
ptr,l, |
info[x], |
info[x+1], |
info[x+2], |
info[x+3], |
info[x+4], |
info[x+5], |
info[x+6], |
info[x+7]); |
FLUSH(); |
#endif |
|
if(info[x] == '-') |
{ |
x++; |
if(info[x]=='?') x++; |
if(!memcmp(info+x,"lib:",4) || !memcmp(info+x,"LIB:",4)) |
{ |
x+=4; |
#ifdef DLDEBUG |
fprintf(stderr,"Found lib: ptr=%d len=%d '%c%c%c%c%c%c%c%c'\n", |
x, |
l-(x-ptr), |
info[x], |
info[x+1], |
info[x+2], |
info[x+3], |
info[x+4], |
info[x+5], |
info[x+6], |
info[x+7]); |
FLUSH(); |
#endif |
memcpy(buffer,info+x,l-(x-ptr)); |
buffer[l-(x-ptr)]=0; |
append_dlllist(&ret->dlls, buffer); |
} |
} |
ptr+=l+1; |
} |
#ifdef DLDEBUG |
fprintf(stderr,"Parse link info done.\n"); |
FLUSH(); |
#endif |
} |
|
static int dl_load_coff_files(struct DLHandle *ret, |
struct DLObjectTempData *tmp, |
int num) |
{ |
int e=0,s,r; |
size_t ptr; |
size_t num_exports=0; |
|
#define data (tmp+e) |
#define SYMBOLS(X) (*(struct COFFSymbol *)(18 * (X) + (char *)data->symbols)) |
|
#ifdef DLDEBUG |
fprintf(stderr,"dl_load_coff_files(%p,%p,%d)\n",ret,tmp,num); |
FLUSH(); |
#endif |
|
if(!num) return 0; |
|
ret->memsize=0; |
|
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: counting\n"); |
#endif |
for(e=0;e<num;e++) |
{ |
data->coff=(struct COFF *)data->buffer; |
data->symbols=(struct COFFSymbol *)(data->buffer + |
data->coff->symboltable); |
|
data->stringtable=(unsigned char *)( ((char *)data->symbols) + |
18 * data->coff->num_symbols); |
|
data->sections=(struct COFFsection *)( ((char *)data->coff) + |
sizeof(struct COFF)+ |
data->coff->sizeof_optheader); |
#ifdef DLDEBUG |
fprintf(stderr,"DL: %x %d %d %d\n", |
data->coff, |
sizeof(data->coff[0]), |
data->coff->sizeof_optheader, |
sizeof(struct COFFsection)); |
#endif |
|
for(s=0;s<data->coff->num_sections;s++) |
{ |
size_t align; |
if(data->sections[s].characteristics & COFF_SECT_LNK_INFO) |
{ |
parse_link_info(ret,data, |
(char *)data->buffer + data->sections[s].ptr2_raw_data, |
data->sections[s].raw_data_size); |
continue; |
} |
|
if(data->sections[s].characteristics & |
(COFF_SECT_NOLOAD | COFF_SECT_MEM_DISCARDABLE | COFF_SECT_MEM_LNK_REMOVE)) |
continue; |
|
align=(data->sections[s].characteristics>>20) & 0xf; |
if(align) |
align=(1<<(align-1))-1; |
#ifdef DLDEBUG |
fprintf(stderr,"DL: section[%d,%d], %d bytes, align=%d (0x%x)\n",e,s,data->sections[s].raw_data_size,align+1,data->sections[s].characteristics); |
#endif |
ret->memsize+=align; |
ret->memsize&=~align; |
ret->memsize+=data->sections[s].raw_data_size; |
} |
if(data->coff->num_symbols) |
{ |
|
ret->memsize+=data->coff->num_symbols * 4 + 3; |
ret->memsize&=~3; |
} |
|
|
for(s=0;s<data->coff->num_symbols;s++) |
{ |
size_t align; |
if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN) |
num_exports++; |
|
|
|
|
if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN && |
!SYMBOLS(s).secnum && |
SYMBOLS(s).value) |
{ |
switch(SYMBOLS(s).value) |
{ |
case 0: |
case 1: align=0; break; |
case 2: |
case 3: align=1; break; |
case 4: |
case 5: |
case 6: |
case 7: align=3; break; |
default: align=7; |
} |
ret->memsize+=align; |
ret->memsize&=~align; |
ret->memsize+=SYMBOLS(s).value; |
} |
} |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: allocating %d bytes\n",ret->memsize); |
#endif |
|
|
ret->memory=VirtualAlloc(0, |
ret->memsize, |
MEM_COMMIT, |
PAGE_EXECUTE_READWRITE); |
|
if(!ret->memory) |
{ |
static char buf[300]; |
sprintf(buf, "Failed to allocate %d bytes RWX-memory.", ret->memsize); |
#ifdef DLDEBUG |
fprintf(stderr, "%s\n", buf); |
#endif |
dlerr=buf; |
return -1; |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: Got %d bytes RWX memory at %p\n",ret->memsize,ret->memory); |
#endif |
|
|
|
ret->htable=alloc_htable(num_exports); |
|
if(data->flags & RTLD_GLOBAL) |
global_dlhandle.htable = htable_add_space(global_dlhandle.htable, num_exports); |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: moving code\n"); |
FLUSH(); |
#endif |
|
|
ptr=0; |
for(e=0;e<num;e++) |
{ |
data->section_addresses = |
(char **)calloc(sizeof(char *),data->coff->num_sections); |
|
for(s=0;s<data->coff->num_sections;s++) |
{ |
size_t align; |
if(data->sections[s].characteristics & |
(COFF_SECT_NOLOAD | COFF_SECT_MEM_DISCARDABLE | COFF_SECT_MEM_LNK_REMOVE)) |
continue; |
|
align=(data->sections[s].characteristics>>20) & 0xf; |
if(align) |
align=(1<<(align-1))-1; |
ptr+=align; |
ptr&=~align; |
|
data->section_addresses[s]=(char *)ret->memory + ptr; |
#ifdef DLDEBUG |
fprintf(stderr,"DL: section[%d]=%p { %d, %d }\n",s, |
data->section_addresses[s], |
data->sections[s].ptr2_raw_data, |
data->sections[s].raw_data_size); |
FLUSH(); |
#endif |
|
if(data->sections[s].ptr2_raw_data) |
{ |
memcpy((char *)ret->memory + ptr, |
data->buffer + data->sections[s].ptr2_raw_data, |
data->sections[s].raw_data_size); |
}else{ |
memset((char *)ret->memory + ptr, |
0, |
data->sections[s].raw_data_size); |
} |
ptr+=data->sections[s].raw_data_size; |
} |
|
if(data->coff->num_symbols) |
{ |
|
ptr+=3; |
ptr&=~3; |
data->symbol_addresses=(char **)( ((char *)ret->memory) + ptr); |
MEMSET(data->symbol_addresses, |
0, |
data->coff->num_symbols * 4); |
#ifdef DLDEBUG |
fprintf(stderr,"DL: data->symbol_addresses=%p\n", |
data->symbol_addresses); |
#endif |
ptr+=data->coff->num_symbols * 4; |
} |
|
|
|
for(s=0;s<data->coff->num_symbols;s++) |
{ |
size_t align; |
|
|
|
if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN && |
!SYMBOLS(s).secnum && |
SYMBOLS(s).value) |
{ |
switch(SYMBOLS(s).value) |
{ |
case 0: |
case 1: align=0; break; |
case 2: |
case 3: align=1; break; |
case 4: |
case 5: |
case 6: |
case 7: align=3; break; |
default: align=7; |
} |
ptr+=align; |
ptr&=~align; |
data->symbol_addresses[s]=((char *)ret->memory + ptr); |
memset((char *)ret->memory + ptr, |
0, |
SYMBOLS(s).value); |
ptr+=SYMBOLS(s).value; |
} |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: exporting symbols (%d)\n",data->coff->num_symbols); |
#endif |
|
|
for(s=0;s<data->coff->num_symbols;s++) |
{ |
#if defined(DLDEBUG) && 0 |
fprintf(stderr,"DL: xporting, class=%d secnum=%d\n", |
SYMBOLS(s).class, |
SYMBOLS(s).secnum); |
#endif |
|
if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN) |
{ |
char *value; |
char *name; |
size_t len; |
|
if(SYMBOLS(s).secnum>0) |
{ |
value=data->section_addresses[SYMBOLS(s).secnum-1]+ |
SYMBOLS(s).value; |
} |
else if(!SYMBOLS(s).secnum && SYMBOLS(s).value) |
{ |
value=data->symbol_addresses[s]; |
if(!value) |
{ |
fprintf(stderr,"Something is fishy here!!!\n"); |
exit(99); |
} |
} |
else |
continue; |
|
|
if(!SYMBOLS(s).name.ptr[0]) |
{ |
name=data->stringtable + SYMBOLS(s).name.ptr[1]; |
len=strlen(name); |
#ifdef DLDEBUG |
fprintf(stderr,"DL: exporting %s -> %p\n",name,value); |
#endif |
}else{ |
name=SYMBOLS(s).name.text; |
len=STRNLEN(name,8); |
#ifdef DLDEBUG |
fprintf(stderr,"DL: exporting %c%c%c%c%c%c%c%c -> %p\n", |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7],value); |
#endif |
} |
|
htable_put(ret->htable, name, len, value); |
if(data->flags & RTLD_GLOBAL) |
htable_put(global_dlhandle.htable, name, len, value); |
} |
} |
} |
|
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: resolving\n"); |
FLUSH(); |
#endif |
|
|
for(e=0;e<num;e++) |
{ |
for(s=0;s<data->coff->num_sections;s++) |
{ |
struct COFFReloc *relocs; |
if(data->sections[s].characteristics & |
(COFF_SECT_NOLOAD | COFF_SECT_MEM_DISCARDABLE | COFF_SECT_MEM_LNK_REMOVE)) |
continue; |
|
|
relocs=(struct COFFReloc *)(data->buffer + |
data->sections[s].ptr2_relocs); |
|
dlerr=0; |
for(r=0;r<data->sections[s].num_relocs;r++) |
{ |
|
|
|
|
#define RELOCS(X) (*(struct COFFReloc *)( 10*(X) + (char *)relocs )) |
|
char *loc=data->section_addresses[s] + RELOCS(r).location; |
char *ptr; |
ptrdiff_t sym=RELOCS(r).symbol; |
char *name; |
size_t len; |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: Reloc[%d] sym=%d loc=%d type=%d\n", |
r, |
sym, |
RELOCS(r).location, |
RELOCS(r).type); |
#endif |
|
if(!(ptr=data->symbol_addresses[sym])) |
{ |
|
|
if(SYMBOLS(sym).secnum > 0) |
{ |
ptr=data->symbol_addresses[sym]= |
data->section_addresses[SYMBOLS(sym).secnum-1]+ |
SYMBOLS(sym).value; |
#ifdef DLDEBUG |
if(!SYMBOLS(sym).name.ptr[0]) |
{ |
name=data->stringtable + SYMBOLS(sym).name.ptr[1]; |
len=strlen(name); |
}else{ |
name=SYMBOLS(sym).name.text; |
len=STRNLEN(name,8); |
} |
if(name[len]) |
{ |
fprintf(stderr,"DL: reloc name=%s\n",name); |
}else{ |
fprintf(stderr,"DL: reloc name=%c%c%c%c%c%c%c%c\n", |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7]); |
} |
#endif |
} |
else if(!SYMBOLS(sym).secnum ) |
{ |
if(!SYMBOLS(sym).name.ptr[0]) |
{ |
name=data->stringtable + SYMBOLS(sym).name.ptr[1]; |
len=strlen(name); |
}else{ |
name=SYMBOLS(sym).name.text; |
len=STRNLEN(name,8); |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: resolving symbol[%d], type=%d class=%d secnum=%d aux=%d value=%d\n", |
sym, |
SYMBOLS(sym).type, |
SYMBOLS(sym).class, |
SYMBOLS(sym).secnum, |
SYMBOLS(sym).aux, |
SYMBOLS(sym).value); |
#endif |
|
ptr=low_dlsym(ret, name, len, 1); |
if(!ptr) |
ptr=low_dlsym(&global_dlhandle, name, len, 0); |
if(!ptr) |
{ |
static char err[256]; |
MEMCPY(err,"Symbol '",8); |
MEMCPY(err+8,name,MINIMUM(len, 128)); |
MEMCPY(err+8+MINIMUM(len, 128),"' not found.\0",13); |
dlerr=err; |
#ifndef DL_VERBOSE |
return -1; |
#else |
fprintf(stderr,"DL: %s\n",err); |
#endif |
} |
}else{ |
static char err[200]; |
#ifdef DLDEBUG |
fprintf(stderr,"Gnapp??\n"); |
#endif |
sprintf(err,"Unknown symbol[%d] type=%d class=%d section=%d value=%d", |
sym, |
SYMBOLS(sym).type, |
SYMBOLS(sym).class, |
SYMBOLS(sym).secnum, |
SYMBOLS(sym).value); |
dlerr=err; |
return -1; |
} |
|
data->symbol_addresses[sym]=ptr; |
} |
|
#ifndef DL_VERBOSE |
if(!ptr || !data->symbol_addresses) |
{ |
fprintf(stderr,"How on earth did this happen??\n"); |
dlerr="Zero symbol?"; |
return -1; |
} |
#endif |
|
switch(RELOCS(r).type) |
{ |
default: |
{ |
static char err[100]; |
sprintf(err,"Unknown relocation type: %d",RELOCS(r).type); |
dlerr=err; |
return -1; |
} |
|
|
case COFFReloc_type_dir32: |
if( (SYMBOLS(sym).type >> 4) == 2 && !SYMBOLS(sym).secnum) |
{ |
#ifdef DLDEBUG |
fprintf(stderr,"DL: Indirect *%p = %d secnum=%d value=%d!!!\n", loc, *(INT32 *)loc,SYMBOLS(sym).secnum,SYMBOLS(sym).value); |
REALLY_FLUSH(); |
#endif |
ptr=(char *)(data->symbol_addresses + sym); |
((INT32 *)loc)[0]=(INT32)ptr; |
}else{ |
((INT32 *)loc)[0]+=(INT32)ptr; |
} |
#ifdef DLDEBUG |
fprintf(stderr,"DL: reloc absolute: loc %p = %p\n", loc,ptr); |
#endif |
break; |
|
case COFFReloc_type_rel32: |
#ifdef DLDEBUG |
fprintf(stderr,"DL: reloc relative: loc %p = %p = %d\n", |
loc, |
ptr, |
ptr - (loc+sizeof(INT32))); |
#endif |
((INT32*)loc)[0]+=ptr - (loc+sizeof(INT32)); |
break; |
} |
} |
|
#ifdef DL_VERBOSE |
if(dlerr) |
return -1; |
#endif |
|
} |
} |
|
return 0; |
#undef data |
} |
|
static int dl_loadarchive(struct DLHandle *ret, |
struct DLTempData *data) |
{ |
ptrdiff_t pos,len,o; |
struct DLTempData save=*data; |
int retcode; |
|
struct DLObjectTempData *tmp; |
INT32 object_files=0; |
|
|
#ifdef DLDEBUG |
fprintf(stderr,"dl_loadarchive\n"); |
FLUSH(); |
#endif |
|
|
for(pos=8;pos < save.buflen;pos+=60+(len+1)&~1) |
{ |
len=STRTOL(save.buffer+pos+48,0,10); |
if(save.buffer[pos]!='/') object_files++; |
#ifdef DLDEBUG |
fprintf(stderr,"DLARCH: pos=%d len=%d\n",pos,len); |
#endif |
} |
#ifdef DLDEBUG |
fprintf(stderr,"DLARCH: object files in archive: %d\n",object_files); |
#endif |
|
|
tmp=(struct DLObjectTempData *)malloc(sizeof(struct DLObjectTempData) * object_files); |
if(!tmp) |
{ |
dlerr="Failed to allocate temporary storage"; |
return -1; |
} |
o=0; |
|
|
for(pos=8;pos < save.buflen;pos+=60+(len+1)&~1) |
{ |
len=STRTOL(save.buffer+pos+48,0,10); |
if(save.buffer[pos]!='/') |
{ |
tmp[o].buflen=len; |
tmp[o].buffer=data->buffer+pos+60; |
o++; |
} |
} |
|
retcode=dl_load_coff_files(ret, tmp, object_files); |
free((char *)tmp); |
return retcode; |
} |
|
static int dl_loadcoff(struct DLHandle *ret, |
struct DLTempData *data) |
{ |
struct DLObjectTempData tmp; |
|
#ifdef DLDEBUG |
fprintf(stderr,"dl_loadcoff\n"); |
FLUSH(); |
#endif |
|
tmp.buflen=data->buflen; |
tmp.buffer=data->buffer; |
tmp.flags = data->flags; |
return dl_load_coff_files(ret, &tmp, 1); |
} |
|
static int dl_loadpe(struct DLHandle *ret, |
struct DLTempData *data) |
{ |
dlerr="PE images not supported yet."; |
return -1; |
} |
|
static int dl_load_file(struct DLHandle *ret, |
struct DLTempData *data) |
{ |
INT32 tmp; |
#ifdef DLDEBUG |
fprintf(stderr,"dl_load_file\n"); |
FLUSH(); |
#endif |
|
if(data->buflen > 8 && ! memcmp(data->buffer,"!<arch>\n",8)) |
return dl_loadarchive(ret,data); |
|
tmp=i4(0x3c); |
if(tmp >=0 && tmp<data->buflen-4 &&!memcmp(data->buffer+tmp,"PE\0\0",4)) |
return dl_loadpe(ret, data); |
|
return dl_loadcoff(ret, data); |
} |
|
static void init_dlopen(void); |
|
struct DLHandle *dlopen(char *name, int flags) |
{ |
struct DLHandle *ret; |
struct DLTempData tmpdata; |
int retcode; |
tmpdata.flags=flags; |
|
if(!global_dlhandle.htable) init_dlopen(); |
|
if(!name) |
{ |
global_dlhandle.refs++; |
return &global_dlhandle; |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"dlopen(%s,%d)\n",name,flags); |
#endif |
|
for(ret=first;ret;ret=ret->next) |
{ |
if(!strcmp(name, ret->filename)) |
{ |
ret->refs++; |
return ret; |
} |
} |
|
|
tmpdata.buflen=filesize(name); |
if(tmpdata.buflen==-1) return 0; |
|
ret=(struct DLHandle *)calloc(sizeof(struct DLHandle),1); |
ret->refs=1; |
ret->filename=strdup(name); |
ret->flags=flags; |
|
tmpdata.buffer = (unsigned char *)read_file(name, &tmpdata.buflen); |
if(!tmpdata.buffer) |
{ |
dlerr="Failed to read file."; |
dlclose(ret); |
return 0; |
} |
|
retcode=dl_load_file(ret, &tmpdata); |
free(tmpdata.buffer); |
|
if(retcode < 0) |
{ |
dlclose(ret); |
return 0; |
} |
|
|
ret->next=first; |
first=ret; |
|
return ret; |
} |
|
int dlclose(struct DLHandle *h) |
{ |
#ifdef DLDEBUG |
fprintf(stderr,"Dlclose(%s)\n",h->filename); |
#endif |
if(! --h->refs) |
{ |
struct DLHandle **ptr; |
for(ptr=&first;*ptr;ptr=&(ptr[0]->next)) |
{ |
if(*ptr == h) |
{ |
*ptr=h->next; |
break; |
} |
} |
|
if(h->filename) free(h->filename); |
if(h->htable) htable_free(h->htable,0); |
if(h->dlls) dlllist_free(h->dlls); |
if(h->memory) VirtualFree(h->memory,0,MEM_RELEASE); |
free((char *)h); |
} |
return 0; |
} |
|
static void init_dlopen(void) |
{ |
int tmp; |
extern char ** ARGV; |
INT32 offset; |
struct DLObjectTempData objtmp; |
HINSTANCE h; |
|
#ifdef DLDEBUG |
fprintf(stderr,"dlopen_init(%s)\n",ARGV[0]); |
#endif |
|
global_dlhandle.refs=1; |
global_dlhandle.filename=ARGV[0]; |
global_dlhandle.next=0; |
first=&global_dlhandle; |
|
h=LoadLibrary(ARGV[0]); |
|
#undef data |
#define data (&objtmp) |
assert(sizeof(union COFFname) == 8); |
assert(sizeof(struct COFFsection) == 40); |
|
data->buffer=(char *)h; |
offset=i4(0x3c); |
|
if(!memcmp(data->buffer+offset, "PE\0\0", 4)) |
{ |
int s; |
unsigned char *buf; |
size_t len; |
objtmp.coff=(struct COFF *)(data->buffer+offset+4); |
#ifdef DLDEBUG |
fprintf(stderr,"Found PE header.\n"); |
fprintf(stderr,"num_sections=%d num_symbols=%d sizeof(optheader)=%d\n", |
objtmp.coff->num_sections, |
objtmp.coff->num_symbols, |
objtmp.coff->sizeof_optheader); |
#endif |
|
data->sections=(struct COFFsection *)( ((char *)data->coff) + |
sizeof(struct COFF)+ |
data->coff->sizeof_optheader); |
|
|
buf= (unsigned char *)read_file(ARGV[0], &len); |
|
data->symbols=(struct COFFSymbol *)(buf + |
data->coff->symboltable); |
|
data->stringtable=(unsigned char *)( ((char *)data->symbols) + |
18 * data->coff->num_symbols); |
|
|
global_dlhandle.htable=alloc_htable(data->coff->num_symbols); |
|
#ifdef DLDEBUG |
fprintf(stderr,"buffer=%p\n",data->buffer); |
fprintf(stderr,"buf=%p\n",buf); |
fprintf(stderr,"symbols=%p\n",data->symbols); |
fprintf(stderr,"stringtable=%p\n",data->stringtable); |
fprintf(stderr,"sections=%p\n",data->sections); |
|
|
for(s=0;s<data->coff->num_sections;s++) |
{ |
fprintf(stderr,"sect[%d]=%p\n", |
s, |
data->buffer + data->sections[s].virtual_addr); |
} |
|
fprintf(stderr,"data->coff->num_symbols=%d\n",data->coff->num_symbols); |
#endif |
|
|
for(s=0;s<data->coff->num_symbols;s++) |
{ |
char *name; |
int len; |
if(SYMBOLS(s).class != 2) continue; |
if(SYMBOLS(s).secnum <= 0) continue; |
|
if(!SYMBOLS(s).name.ptr[0]) |
{ |
name=data->stringtable + SYMBOLS(s).name.ptr[1]; |
if(name[0]=='?') continue; |
len=strlen(name); |
#ifdef DLDEBUG |
fprintf(stderr,"Sym[%04d] %s : ",s,name); |
#endif |
}else{ |
name=SYMBOLS(s).name.text; |
if(name[0]=='?') continue; |
len=STRNLEN(name,8); |
#ifdef DLDEBUG |
fprintf(stderr,"Sym[%04d] %c%c%c%c%c%c%c%c = ",s, |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7]); |
#endif |
} |
#ifdef DLDEBUG |
fprintf(stderr,"sect=%d value=%d class=%d type=%d", |
SYMBOLS(s).secnum, |
SYMBOLS(s).value, |
SYMBOLS(s).class, |
SYMBOLS(s).type); |
|
fprintf(stderr," addr=%p+%x-%x+%x = %p", |
data->buffer, |
data->sections[SYMBOLS(s).secnum-1].virtual_addr, |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data, |
SYMBOLS(s).value, |
data->buffer + |
data->sections[SYMBOLS(s).secnum-1].virtual_addr - |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data + |
SYMBOLS(s).value); |
|
fprintf(stderr,"\n"); |
#endif |
|
htable_put(global_dlhandle.htable, name, len, |
data->buffer + |
data->sections[SYMBOLS(s).secnum-1].virtual_addr - |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data + |
SYMBOLS(s).value); |
|
} |
free(buf); |
}else{ |
#ifdef DLDEBUG |
fprintf(stderr,"Couldn't find PE header.\n"); |
#endif |
append_dlllist(&global_dlhandle.dlls, ARGV[0]); |
global_dlhandle.htable=alloc_htable(997); |
#define EXPORT(X) \ |
DO_IF_DLDEBUG( fprintf(stderr,"EXP: %s\n",#X); ) \ |
htable_put(global_dlhandle.htable,"_" #X,sizeof(#X)-sizeof("")+1, &X) |
|
|
fprintf(stderr,"Fnord, rand()=%d\n",rand()); |
EXPORT(fprintf); |
EXPORT(_iob); |
EXPORT(abort); |
EXPORT(rand); |
EXPORT(srand); |
EXPORT(getc); |
EXPORT(ungetc); |
EXPORT(printf); |
EXPORT(perror); |
EXPORT(sscanf); |
EXPORT(abs); |
EXPORT(putchar); |
} |
|
#ifdef DLDEBUG |
fprintf(stderr,"DL: init done\n"); |
#endif |
} |
|
|
|
#ifdef TEST |
|
__declspec(dllexport) void func1(void) |
{ |
fprintf(stderr,"func1()\n"); |
fprintf(stdout,"func1()\n"); |
} |
|
__declspec(dllexport) void func3(void) |
{ |
fprintf(stderr,"func3()\n"); |
} |
|
__declspec(dllexport) void func4(void) |
{ |
fprintf(stderr,"func4()\n"); |
} |
|
__declspec(dllexport) void func5(void) |
{ |
fprintf(stderr,"func5()\n"); |
} |
|
int main(int argc, char ** argv) |
{ |
int tmp; |
assert(sizeof(union COFFname) == 8); |
assert(sizeof(struct COFFsection) == 40); |
#if 0 |
HINSTANCE l; |
void *addr; |
|
fprintf(stderr,"....\n"); |
l=LoadLibrary(argv[0]); |
fprintf(stderr,"LoadLibrary(%s) => %p\n",argv[0],(long)l); |
addr = GetProcAddress(l, "func1"); |
fprintf(stderr,"GetProcAddress(\"func1\") => %p (&func1 = %p)\n",addr, func1); |
#else |
|
{ |
HINSTANCE h=LoadLibrary(argv[0]); |
INT32 offset; |
struct DLObjectTempData objtmp; |
#undef data |
#define data (&objtmp) |
|
data->buffer=(char *)h; |
offset=i4(0x3c); |
|
if(!memcmp(data->buffer+offset, "PE\0\0", 4)) |
{ |
int s; |
unsigned char *buf; |
size_t len; |
objtmp.coff=(struct COFF *)(data->buffer+offset+4); |
#ifdef DLDEBUG |
fprintf(stderr,"Found PE header.\n"); |
fprintf(stderr,"num_sections=%d num_symbols=%d sizeof(optheader)=%d\n", |
objtmp.coff->num_sections, |
objtmp.coff->num_symbols, |
objtmp.coff->sizeof_optheader); |
#endif |
|
data->sections=(struct COFFsection *)( ((char *)data->coff) + |
sizeof(struct COFF)+ |
data->coff->sizeof_optheader); |
|
|
buf= (unsigned char *)read_file(argv[0], &len); |
|
data->symbols=(struct COFFSymbol *)(buf + |
data->coff->symboltable); |
|
data->stringtable=(unsigned char *)( ((char *)data->symbols) + |
18 * data->coff->num_symbols); |
|
|
global_dlhandle.htable=alloc_htable(data->coff->num_symbols); |
|
#ifdef DLDEBUG |
fprintf(stderr,"buffer=%p\n",data->buffer); |
fprintf(stderr,"buf=%p\n",buf); |
fprintf(stderr,"symbols=%p\n",data->symbols); |
fprintf(stderr,"stringtable=%p\n",data->stringtable); |
fprintf(stderr,"sections=%p\n",data->sections); |
|
|
for(s=0;s<data->coff->num_sections;s++) |
{ |
fprintf(stderr,"sect[%d]=%p\n", |
s, |
data->buffer + data->sections[s].virtual_addr); |
} |
|
fprintf(stderr,"data->coff->num_symbols=%d\n",data->coff->num_symbols); |
#endif |
|
|
for(s=0;s<data->coff->num_symbols;s++) |
{ |
char *name; |
int len; |
if(SYMBOLS(s).class != 2) continue; |
if(SYMBOLS(s).secnum <= 0) continue; |
|
if(!SYMBOLS(s).name.ptr[0]) |
{ |
name=data->stringtable + SYMBOLS(s).name.ptr[1]; |
if(name[0]=='?') continue; |
len=strlen(name); |
#ifdef DLDEBUG |
fprintf(stderr,"Sym[%04d] %s : ",s,name); |
#endif |
}else{ |
name=SYMBOLS(s).name.text; |
if(name[0]=='?') continue; |
len=STRNLEN(name,8); |
#ifdef DLDEBUG |
fprintf(stderr,"Sym[%04d] %c%c%c%c%c%c%c%c = ",s, |
name[0], |
name[1], |
name[2], |
name[3], |
name[4], |
name[5], |
name[6], |
name[7]); |
#endif |
} |
#ifdef DLDEBUG |
fprintf(stderr,"sect=%d value=%d class=%d type=%d", |
SYMBOLS(s).secnum, |
SYMBOLS(s).value, |
SYMBOLS(s).class, |
SYMBOLS(s).type); |
|
fprintf(stderr," addr=%p+%x-%x+%x = %p", |
data->buffer, |
data->sections[SYMBOLS(s).secnum-1].virtual_addr, |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data, |
SYMBOLS(s).value, |
data->buffer + |
data->sections[SYMBOLS(s).secnum-1].virtual_addr - |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data + |
SYMBOLS(s).value); |
|
fprintf(stderr,"\n"); |
#endif |
|
htable_put(global_dlhandle.htable, name, len, |
data->buffer + |
data->sections[SYMBOLS(s).secnum-1].virtual_addr - |
data->sections[SYMBOLS(s).secnum-1].ptr2_raw_data + |
SYMBOLS(s).value); |
|
} |
free(buf); |
}else{ |
#ifdef DLDEBUG |
fprintf(stderr,"Couldn't find PE header.\n"); |
#endif |
append_dlllist(&global_dlhandle.dlls, argv[0]); |
global_dlhandle.htable=alloc_htable(997); |
#define EXPORT(X) \ |
DO_IF_DLDEBUG( fprintf(stderr,"EXP: %s\n",#X); ) \ |
htable_put(global_dlhandle.htable,"_" #X,sizeof(#X)-sizeof("")+1, &X) |
|
|
fprintf(stderr,"Fnord, rand()=%d\n",rand()); |
EXPORT(fprintf); |
EXPORT(_iob); |
EXPORT(abort); |
EXPORT(rand); |
EXPORT(srand); |
} |
} |
|
|
|
|
{ |
struct DLHandle *h; |
void *f; |
h=dlopen("./foo.o",0); |
fprintf(stderr,"dlopen(./fnord) => %p\n",h); |
if(h) |
{ |
f=dlsym(h,"_func2"); |
fprintf(stderr,"dsym(_func2) => %p\n",f); |
if(f) |
{ |
fprintf(stderr,"Calling %p (func1=%p)\n",f,func1); |
fflush(stderr); |
((void (*)(void))f)(); |
} |
} |
} |
#endif |
exit(0); |
} |
|
|
#endif |
|
|