Select Git revision
-
Hugo Hörnquist authoredHugo Hörnquist authored
dlopen.c 34.86 KiB
/*\
||| 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>
static char *dlerr=0;
/* Todo:
* Make image debugable if possible
* Support Win64
* Separate RWX, RW and R memory sections.
*/
/* #define DLDEBUG 1 */
#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.10 2001/01/18 13:01:18 grubba Exp $");
#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);
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;
/** 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;
#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))
/* 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 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;
/* 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;
}
if(data->coff->num_symbols)
{
/* Assumes 32 bit pointers! */
ret->memsize+=data->coff->num_symbols * 4 + 3;
ret->memsize&=~3;
}
/* Count export symbols */
for(s=0;s<data->coff->num_symbols;s++)
{
size_t align;
if(SYMBOLS(s).class == COFF_SYMBOL_EXTERN)
num_exports++;
/* Count memory in common symbols,
* I wonder what the alignment should be?
*/
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
/* Allocate executable memory */
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
/* Create a hash table for exported symbols */
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
/* 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);
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;
}
/* Make addresses for common symbols */
for(s=0;s<data->coff->num_symbols;s++)
{
size_t align;
/*
* Setup poiners to common symbols
*/
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
/* 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)
{
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
/* 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;
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]))
{
/* 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;
#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 /* && !SYMBOLS(sym).value */)
{
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;
}
/* We may need to support more types here */
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; /* 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");
FLUSH();
#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);
if(!tmp)
{
dlerr="Failed to allocate temporary storage";
return -1;
}
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");
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;
}
}
/* 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 */
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; /* 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_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; /* 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_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; /* 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_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; /* 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_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);
}
}
/* 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