diff --git a/src/dlopen.c b/src/dlopen.c new file mode 100644 index 0000000000000000000000000000000000000000..954398bb889399eecb35ed8ad5444c5607b958a5 --- /dev/null +++ b/src/dlopen.c @@ -0,0 +1,1452 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#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> + + +#define DLDEBUG 1 +#define DL_VERBOSE 1 + +#ifdef DLDEBUG +#define DO_IF_DLDEBUG(X) X +#else +#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; +} + +#endif + +/****************************************************************/ + +struct Sym +{ + struct Sym *next; + void *sym; + size_t len; + char name[1]; +}; + +struct Htable +{ + size_t size; /* size of hash table */ + 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); +#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; + /** rehashto new_size */ + + } +#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) + { + +/* fprintf(stderr,"Looking %s/%d =? %s/%d\n",name,len, tmp->name,tmp->len); */ + 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 + + /* FIXME: Should be duplicate symbols be overloaded or what? */ + if((tmp=htable_get_low(h, name, l, hval))) + { + tmp->sym=ptr; + return; + } + tmp=(struct Sym *)malloc(sizeof(struct Sym) + l); + if(!tmp) + { + /* out of memory */ + 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; + 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"); +#endif + return buffer; +} + +/****************************************************************/ + +static struct DLHandle *first; + +struct DLLList +{ + struct DLLList *next; + HINSTANCE dll; +}; + +struct DLHandle +{ + int refs; + char *filename; + int flags; + struct DLHandle *next; + + size_t memsize; + void *memory; + struct Htable *htable; + struct DLLList *dlls; +}; + +static void *lookup_dlls(struct DLLList *l, char *name) +{ + void *ret; + if(name[0]=='_') name++; +#ifdef DLDEBUG + fprintf(stderr,"DL: lookup_dlls(%s)\n",name); +#endif + while(l) + { + 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=LoadLibrary(name); + if(!tmp) return 0; + n=(struct DLLList *)malloc(sizeof(struct DLLList)); + n->dll=tmp; +#ifdef DLDEBUG + fprintf(stderr,"append_dlllist(%s)->%p\n",name,n->dll); +#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)) +/* Truncated */ +#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 + +/* This structure is correct, but should not be used because of + * portability issues + */ +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; + + /* temporary storage */ + char **symbol_addresses; + char **section_addresses; +}; + + +static struct DLLList *global_dlls=0; +static struct Htable *global_symbols=0; +static char *dlerr=0; + +static void *low_dlsym(struct DLHandle *handle, + char *name, + size_t len) +{ + void *ptr; + char *tmp; + if(len > 6 && !memcmp(name,"__imp_",6)) + { + name+=6; + len-=6; + } + 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 + ptr=htable_get(handle->htable,name,len); + if(!ptr) ptr=htable_get(global_symbols,name,len); + if(!ptr) + { + if(name[len]) + { + tmp=(char *)alloca(len+1); + MEMCPY(tmp, name, len); + tmp[len]=0; + } + ptr=lookup_dlls(handle->dlls, tmp); + if(!ptr) ptr=lookup_dlls(global_dlls, tmp); + } +#ifdef DL_VERBOSE + 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)); +} + +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"); + } +#endif + + while(ptr < len) + { + int l,x=ptr; + char *end; + +#ifdef DLDEBUG + fprintf(stderr,"Parse link info ptr=%d\n",ptr,l); +#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\n",ptr,l); +#endif + + if(info[x] == '-') + { + x++; + if(info[x]=='?') x++; + if(!memcmp(info+x,"lib:",4) || !memcmp(info+x,"LIB:",4)) + { + x+=4; + memcpy(buffer,info+x,l-x); + buffer[l-x]=0; + append_dlllist(&ret->dlls, buffer); + } + } + ptr+=l+1; + } +} + +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); +#endif + + if(!num) return 0; + + ret->memsize=0; + + /* Initialize tables and count how much memory is needed */ +#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; + 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; + } + + /* Count export symbols */ + for(s=0;s<data->coff->num_symbols;s++) + { + if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN && + SYMBOLS(s).secnum > 0) + num_exports++; + } + + if(data->coff->num_symbols) + { + /* Assumes 32 bit pointers! */ + ret->memsize+=data->coff->num_symbols * 4 + 3; + ret->memsize&=~3; + } + } + +#ifdef DLDEBUG + fprintf(stderr,"DL: allocating %d bytes\n",ret->memsize); +#endif + + /* Allocate executable memory */ + ret->memory=VirtualAlloc(0, + ret->memsize, + MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + + if(!ret->memory) + { +#ifdef DLDEBUG + fprintf(stderr,"Failed to allocate %d bytes RWX-memory.\n",ret->memsize); +#endif + dlerr="Failed to allocate memory"; + return -1; + } + +#ifdef DLDEBUG + fprintf(stderr,"DL: Got %d bytes RWX memory at %p\n",ret->memsize,ret->memory); +#endif + + + /* Create a hash table for exported symbols */ + ret->htable=alloc_htable(num_exports); + + if(data->flags & RTLD_GLOBAL) + global_symbols = htable_add_space(global_symbols, num_exports); + +#ifdef DLDEBUG + fprintf(stderr,"DL: moving code\n"); +#endif + + /* Copy code into executable memory */ + 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; + 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\n",s,data->section_addresses[s]); +#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) + { + /* Assumes 32 bit pointers! */ + ptr+=3; + ptr&=~3; + data->symbol_addresses=(char **)( ((char *)ret->memory) + ptr); +#ifdef DLDEBUG + fprintf(stderr,"DL: data->symbol_addresses=%p\n", + data->symbol_addresses); +#endif + ptr+=data->coff->num_symbols * 4; + } + + +#ifdef DLDEBUG + fprintf(stderr,"DL: exporting symbols (%d)\n",data->coff->num_symbols); +#endif + + /* Export symbols */ + 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 && + SYMBOLS(s).secnum > 0) + { + char *value=data->section_addresses[SYMBOLS(s).secnum-1]+ + SYMBOLS(s).value; + + char *name; + size_t len; + + 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_symbols, name, len, value); + } + } + } + + +#ifdef DLDEBUG + fprintf(stderr,"DL: resolving\n"); +#endif + + /* Do resolve and relocations */ + 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; + + /* relocate all symbols in this section */ + relocs=(struct COFFReloc *)(data->buffer + + data->sections[s].ptr2_relocs); + + dlerr=0; + for(r=0;r<data->sections[s].num_relocs;r++) + { + +/* This should work fine on x86 and other processors which handles + * unaligned memory access + */ +#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; + +#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])) + { + /* FIXME: check storage class of symbol */ + + if(SYMBOLS(sym).secnum > 0) + { + ptr=data->symbol_addresses[sym]= + data->section_addresses[SYMBOLS(sym).secnum-1]+ + SYMBOLS(sym).value; + } + else if(!SYMBOLS(sym).value) + { + char *name; + size_t len; + + 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 + + if(!(ptr=low_dlsym(ret, name, len))) + { + dlerr="Symbol not found"; +#ifndef DL_VERBOSE + return -1; +#endif + } + }else{ +#ifdef DLDEBUG + fprintf(stderr,"Gnapp??\n"); +#endif + return -1; + } + + data->symbol_addresses[sym]=ptr; + } + + switch(RELOCS(r).type) + { + default: + dlerr="Unknown relocation type."; + return -1; + + /* We may need to support more types here */ + case COFFReloc_type_dir32: + if(SYMBOLS(sym).type >> 4 == 2) + { + /* Indirect function pointer (#&$#*&#$ */ + ((char **)loc)[0]=(char *)(data->symbol_addresses + sym); +#ifdef DLDEBUG + fprintf(stderr,"DL: reloc indirect: loc %p = %p, *%p = %p + %d\n", + loc,ptr, + *(char **)loc,data->symbol_addresses,sym); +#endif + }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; /* Done (I hope) */ +#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"); +#endif + + /* Count how many object files there are in the loop */ + 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); + o=0; + + /* Initialize data objects for these */ + 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"); +#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"); +#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_symbols) init_dlopen(); + +#ifdef DLDEBUG + fprintf(stderr,"dlopen(%s,%d)\n",name,flags); +#endif + abort(); + + for(ret=first;ret;ret=ret->next) + { + if(!strcmp(name, ret->filename)) + { + ret->refs++; + return ret; + } + } + + /* LD_LIBRARY_PATH ? */ + 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; + } + + /* register module for future dlopens */ + + return ret; +} + +int dlclose(struct DLHandle *h) +{ +#ifdef DLDEBUG + fprintf(stderr,"Dlclose(%s)\n",h->filename); +#endif + if(! --h->refs) + { + 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 + + h=LoadLibrary(ARGV[0]); + +#undef data +#define data (&objtmp) + assert(sizeof(union COFFname) == 8); + assert(sizeof(struct COFFsection) == 40); + + data->buffer=(char *)h; /* portable? nope */ + 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_symbols=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; /* C++ garbled */ + len=strlen(name); +#ifdef DLDEBUG + fprintf(stderr,"Sym[%04d] %s : ",s,name); +#endif + }else{ + name=SYMBOLS(s).name.text; + if(name[0]=='?') continue; /* C++ garbled */ + 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_symbols, 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_dlls, ARGV[0]); + global_symbols=alloc_htable(997); +#define EXPORT(X) \ + DO_IF_DLDEBUG( fprintf(stderr,"EXP: %s\n",#X); ) \ + htable_put(global_symbols,"_" #X,sizeof(#X)-sizeof("")+1, &X) + + + fprintf(stderr,"Fnord, rand()=%d\n",rand()); + EXPORT(fprintf); + EXPORT(_iob); + EXPORT(abort); + EXPORT(rand); + EXPORT(srand); + } + +#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; /* portable? nope */ + 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_symbols=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; /* C++ garbled */ + len=strlen(name); +#ifdef DLDEBUG + fprintf(stderr,"Sym[%04d] %s : ",s,name); +#endif + }else{ + name=SYMBOLS(s).name.text; + if(name[0]=='?') continue; /* C++ garbled */ + 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_symbols, 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_dlls, argv[0]); + global_symbols=alloc_htable(997); +#define EXPORT(X) \ + DO_IF_DLDEBUG( fprintf(stderr,"EXP: %s\n",#X); ) \ + htable_put(global_symbols,"_" #X,sizeof(#X)-sizeof("")+1, &X) + + + fprintf(stderr,"Fnord, rand()=%d\n",rand()); + EXPORT(fprintf); + EXPORT(_iob); + EXPORT(abort); + EXPORT(rand); + EXPORT(srand); + } + } + + /* FIXME: open argv[0] and check what dlls is it is linked + * against and add those to the list + */ + { + 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 diff --git a/src/pike_dlfcn.h b/src/pike_dlfcn.h new file mode 100644 index 0000000000000000000000000000000000000000..3afdf7263320b67fe8d2b890e6783e8a041e0127 --- /dev/null +++ b/src/pike_dlfcn.h @@ -0,0 +1,19 @@ +#ifndef PIKE_DLFCN_H +#define PIKE_DLFCN_H + +#define RTLD_GLOBAL 1 +#define RTLD_LAZY 0 /* never */ +#define RTLD_NOW 0 /* always */ + +#ifdef DL_INTERNAL +#define DLHANDLE struct DLHandle +#else +#define DLHANDLE void +#endif + +void *dlsym(DLHANDLE *handle, char *name); +const char *dlerror(void); +DLHANDLE *dlopen(char *name, int flags); +int dlclose(DLHANDLE *h); + +#endif