Skip to content
Snippets Groups Projects
Select Git revision
  • 8034c50d641fcba35983b2f0eb2d9c3d093ccfc7
  • master default protected
2 results

sasl.pp

Blame
  • 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