500 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* dll_init.cc
 | 
						|
 | 
						|
   Copyright 1998, 1999, 2000 Cygnus Solutions.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include "winsup.h"
 | 
						|
#include "exceptions.h"
 | 
						|
#include "dll_init.h"
 | 
						|
 | 
						|
extern void __stdcall check_sanity_and_sync (per_process *);
 | 
						|
 | 
						|
#ifdef _MT_SAFE
 | 
						|
extern ResourceLocks _reslock NO_COPY;
 | 
						|
extern MTinterface _mtinterf NO_COPY;
 | 
						|
#endif /*_MT_SAFE*/
 | 
						|
 | 
						|
/* WARNING: debug can't be called before init !!!! */
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// the private structure
 | 
						|
 | 
						|
typedef enum   { NONE, LINK, LOAD } dllType;
 | 
						|
 | 
						|
struct dll
 | 
						|
{
 | 
						|
  per_process *p;
 | 
						|
  HMODULE handle;
 | 
						|
  const char *name;
 | 
						|
  dllType type;
 | 
						|
};
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
#define MAX_DLL_BEFORE_INIT	100 // FIXME: enough ???
 | 
						|
static dll _list_before_init[MAX_DLL_BEFORE_INIT];
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// local variables
 | 
						|
 | 
						|
static DllList _the;
 | 
						|
static int _last = 0;
 | 
						|
static int _max = MAX_DLL_BEFORE_INIT;
 | 
						|
static dll *_list = _list_before_init;
 | 
						|
static int _initCalled = 0;
 | 
						|
static int _numberOfOpenedDlls = 0;
 | 
						|
static int _forkeeMustReloadDlls = 0;
 | 
						|
static int _in_forkee = 0;
 | 
						|
static const char *_dlopenedLib = 0;
 | 
						|
static int _dlopenIndex = -1;
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
static int __dll_global_dtors_recorded = 0;
 | 
						|
 | 
						|
static void
 | 
						|
__dll_global_dtors()
 | 
						|
{
 | 
						|
  _the.doGlobalDestructorsOfDlls();
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
doGlobalCTORS (per_process *p)
 | 
						|
{
 | 
						|
  void (**pfunc)() = p->ctors;
 | 
						|
 | 
						|
  /* Run ctors backwards, so skip the first entry and find how many
 | 
						|
    there are, then run them.  */
 | 
						|
 | 
						|
  if (pfunc)
 | 
						|
    {
 | 
						|
      int i;
 | 
						|
      for (i = 1; pfunc[i]; i++);
 | 
						|
 | 
						|
      for (int j = i - 1; j > 0; j-- )
 | 
						|
	(pfunc[j]) ();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
doGlobalDTORS (per_process *p)
 | 
						|
{
 | 
						|
  if (!p)
 | 
						|
    return;
 | 
						|
  void (**pfunc)() = p->dtors;
 | 
						|
  for (int i = 1; pfunc[i]; i++)
 | 
						|
    (pfunc[i]) ();
 | 
						|
}
 | 
						|
 | 
						|
#define INC 500
 | 
						|
 | 
						|
static int
 | 
						|
add (HMODULE h, char *name, per_process *p, dllType type)
 | 
						|
{
 | 
						|
  int ret = -1;
 | 
						|
 | 
						|
  if (p)
 | 
						|
    check_sanity_and_sync (p);
 | 
						|
 | 
						|
  if (_last == _max)
 | 
						|
    {
 | 
						|
      if (!_initCalled) // we try to load more than MAX_DLL_BEFORE_INIT
 | 
						|
	{
 | 
						|
	  small_printf ("try to load more dll than max allowed=%d\n",
 | 
						|
		       MAX_DLL_BEFORE_INIT);
 | 
						|
	  ExitProcess (1);
 | 
						|
	}
 | 
						|
 | 
						|
      dll* newArray = new dll[_max+INC];
 | 
						|
      if (_list)
 | 
						|
	{
 | 
						|
	  memcpy (newArray, _list, _max * sizeof (dll));
 | 
						|
	  if (_list != _list_before_init)
 | 
						|
	    delete []_list;
 | 
						|
	}
 | 
						|
      _list = newArray;
 | 
						|
      _max += INC;
 | 
						|
    }
 | 
						|
 | 
						|
  _list[_last].name = name && type == LOAD ? strdup (name) : NULL;
 | 
						|
  _list[_last].handle = h;
 | 
						|
  _list[_last].p = p;
 | 
						|
  _list[_last].type = type;
 | 
						|
 | 
						|
  ret = _last++;
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
initOneDll (per_process *p)
 | 
						|
{
 | 
						|
  /* global variable user_data must be initialized */
 | 
						|
  if (user_data == NULL)
 | 
						|
    {
 | 
						|
      small_printf ("WARNING: process not inited while trying to init a DLL!\n");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  /* init impure_ptr */
 | 
						|
  *(p->impure_ptr_ptr) = *(user_data->impure_ptr_ptr);
 | 
						|
 | 
						|
  /* FIXME: init environment (useful?) */
 | 
						|
  *(p->envptr) = *(user_data->envptr);
 | 
						|
 | 
						|
  /* FIXME: need other initializations? */
 | 
						|
 | 
						|
  int ret = 1;
 | 
						|
  if (!_in_forkee)
 | 
						|
    {
 | 
						|
      /* global contructors */
 | 
						|
      doGlobalCTORS (p);
 | 
						|
 | 
						|
      /* entry point of dll (use main of per_process with null args...) */
 | 
						|
      if (p->main)
 | 
						|
	ret = (*(p->main)) (0, 0, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
DllList&
 | 
						|
DllList::the ()
 | 
						|
{
 | 
						|
  return _the;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllList::currentDlOpenedLib (const char *name)
 | 
						|
{
 | 
						|
  if (_dlopenedLib != 0)
 | 
						|
    small_printf ("WARNING: previous dlopen of %s wasn't correctly performed\n", _dlopenedLib);
 | 
						|
  _dlopenedLib = name;
 | 
						|
  _dlopenIndex = -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
DllList::recordDll (HMODULE h, per_process *p)
 | 
						|
{
 | 
						|
  int ret = -1;
 | 
						|
 | 
						|
  /* debug_printf ("Record a dll p=%p\n", p); see WARNING */
 | 
						|
  dllType type = LINK;
 | 
						|
  if (_initCalled)
 | 
						|
    {
 | 
						|
      type = LOAD;
 | 
						|
      _numberOfOpenedDlls++;
 | 
						|
      forkeeMustReloadDlls (1);
 | 
						|
    }
 | 
						|
 | 
						|
  if (_in_forkee)
 | 
						|
    {
 | 
						|
      ret = 0;		// Just a flag
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  char buf[MAX_PATH];
 | 
						|
  GetModuleFileName (h, buf, MAX_PATH);
 | 
						|
 | 
						|
  if (type == LOAD && _dlopenedLib !=0)
 | 
						|
    {
 | 
						|
    // it is not the current dlopened lib
 | 
						|
    // so we insert one empty lib to preserve place for current dlopened lib
 | 
						|
    if (!strcasematch (_dlopenedLib, buf))
 | 
						|
      {
 | 
						|
      if (_dlopenIndex == -1)
 | 
						|
	_dlopenIndex = add (0, 0, 0, NONE);
 | 
						|
      ret = add (h, buf, p, type);
 | 
						|
      }
 | 
						|
    else // it is the current dlopened lib
 | 
						|
      {
 | 
						|
	if (_dlopenIndex != -1)
 | 
						|
	  {
 | 
						|
	    _list[_dlopenIndex].handle = h;
 | 
						|
	    _list[_dlopenIndex].p = p;
 | 
						|
	    _list[_dlopenIndex].type = type;
 | 
						|
	    ret = _dlopenIndex;
 | 
						|
	    _dlopenIndex = -1;
 | 
						|
	  }
 | 
						|
	else // it this case the dlopened lib doesn't need other lib
 | 
						|
	  ret = add (h, buf, p, type);
 | 
						|
	_dlopenedLib = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  else
 | 
						|
    ret = add (h, buf, p, type);
 | 
						|
 | 
						|
out:
 | 
						|
  if (_initCalled) // main module is already initialized
 | 
						|
    {
 | 
						|
      if (!initOneDll (p))
 | 
						|
	ret = -1;
 | 
						|
    }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllList::detachDll (int dll_index)
 | 
						|
{
 | 
						|
  if (dll_index != -1)
 | 
						|
    {
 | 
						|
      dll *aDll = &(_list[dll_index]);
 | 
						|
      doGlobalDTORS (aDll->p);
 | 
						|
      if (aDll->type == LOAD)
 | 
						|
	_numberOfOpenedDlls--;
 | 
						|
      aDll->type = NONE;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    small_printf ("WARNING: try to detach an already detached dll ...\n");
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllList::initAll ()
 | 
						|
{
 | 
						|
  // init for destructors
 | 
						|
  // because initAll isn't called in forked process, this exit function will
 | 
						|
  // be recorded only once
 | 
						|
  if (!__dll_global_dtors_recorded)
 | 
						|
    {
 | 
						|
      atexit (__dll_global_dtors);
 | 
						|
      __dll_global_dtors_recorded = 1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!_initCalled)
 | 
						|
    {
 | 
						|
      debug_printf ("call to DllList::initAll");
 | 
						|
      for (int i = 0; i < _last; i++)
 | 
						|
	{
 | 
						|
	  per_process *p = _list[i].p;
 | 
						|
	  if (p)
 | 
						|
	    initOneDll (p);
 | 
						|
	}
 | 
						|
      _initCalled = 1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllList::doGlobalDestructorsOfDlls ()
 | 
						|
{
 | 
						|
  // global destructors in reverse order
 | 
						|
  for (int i = _last - 1; i >= 0; i--)
 | 
						|
    {
 | 
						|
      if (_list[i].type != NONE)
 | 
						|
	{
 | 
						|
	  per_process *p = _list[i].p;
 | 
						|
	  if (p)
 | 
						|
	    doGlobalDTORS (p);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
DllList::numberOfOpenedDlls ()
 | 
						|
{
 | 
						|
  return _numberOfOpenedDlls;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
DllList::forkeeMustReloadDlls ()
 | 
						|
{
 | 
						|
  return _forkeeMustReloadDlls;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllList::forkeeMustReloadDlls (int i)
 | 
						|
{
 | 
						|
  _forkeeMustReloadDlls = i;
 | 
						|
}
 | 
						|
 | 
						|
#define A64K (64 * 1024)
 | 
						|
 | 
						|
/* Mark every memory address up to "here" as reserved.  This may force
 | 
						|
   Windows NT to load a DLL in the next available, lowest slot. */
 | 
						|
void
 | 
						|
reserve_upto (const char *name, DWORD here)
 | 
						|
{
 | 
						|
  DWORD size;
 | 
						|
  MEMORY_BASIC_INFORMATION mb;
 | 
						|
  for (DWORD start = 0x10000; start < here; start += size)
 | 
						|
    if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
 | 
						|
      size = 64 * 1024;
 | 
						|
    else
 | 
						|
      {
 | 
						|
	size = A64K * ((mb.RegionSize + A64K - 1) / A64K);
 | 
						|
	start = A64K * (((DWORD) mb.BaseAddress + A64K - 1) / A64K);
 | 
						|
 | 
						|
	if (start + size > here)
 | 
						|
	  size = here - start;
 | 
						|
	if (mb.State == MEM_FREE &&
 | 
						|
	    !VirtualAlloc ((void *) start, size, MEM_RESERVE, PAGE_NOACCESS))
 | 
						|
	  api_fatal ("couldn't allocate memory %p(%d) for '%s' alignment, %E\n",
 | 
						|
		     start, size, name);
 | 
						|
      }
 | 
						|
}
 | 
						|
 | 
						|
/* Release all of the memory previously allocated by "upto" above.
 | 
						|
   Note that this may also free otherwise reserved memory.  If that becomes
 | 
						|
   a problem, we'll have to keep track of the memory that we reserve above. */
 | 
						|
void
 | 
						|
release_upto (const char *name, DWORD here)
 | 
						|
{
 | 
						|
  DWORD size;
 | 
						|
  MEMORY_BASIC_INFORMATION mb;
 | 
						|
  for (DWORD start = 0x10000; start < here; start += size)
 | 
						|
    if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
 | 
						|
      size = 64 * 1024;
 | 
						|
    else
 | 
						|
      {
 | 
						|
	size = mb.RegionSize;
 | 
						|
	if (!(mb.State == MEM_RESERVE && mb.AllocationProtect == PAGE_NOACCESS &&
 | 
						|
	    ((void *) start < user_data->heapbase || (void *) start > user_data->heaptop)))
 | 
						|
	  continue;
 | 
						|
	if (!VirtualFree ((void *) start, 0, MEM_RELEASE))
 | 
						|
	  api_fatal ("couldn't release memory %p(%d) for '%s' alignment, %E\n",
 | 
						|
		     start, size, name);
 | 
						|
      }
 | 
						|
}
 | 
						|
 | 
						|
/* Reload DLLs after a fork.  Iterates over the list of dynamically loaded DLLs
 | 
						|
   and attempts to load them in the same place as they were loaded in the parent. */
 | 
						|
void
 | 
						|
DllList::forkeeLoadDlls ()
 | 
						|
{
 | 
						|
  _initCalled = 1;
 | 
						|
  _in_forkee = 1;
 | 
						|
  int try2 = 0;
 | 
						|
  for (int i = 0; i < _last; i++)
 | 
						|
    if (_list[i].type == LOAD)
 | 
						|
      {
 | 
						|
	const char *name = _list[i].name;
 | 
						|
	HMODULE handle = _list[i].handle;
 | 
						|
	HMODULE h = LoadLibraryEx (name, NULL, DONT_RESOLVE_DLL_REFERENCES);
 | 
						|
 | 
						|
	if (h == handle)
 | 
						|
	  {
 | 
						|
	    FreeLibrary (h);
 | 
						|
	    LoadLibrary (name);
 | 
						|
	  }
 | 
						|
	else if (try2)
 | 
						|
	  api_fatal ("unable to remap %s to same address as parent -- %p", name, h);
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    FreeLibrary (h);
 | 
						|
	    reserve_upto (name, (DWORD) handle);
 | 
						|
	    try2 = 1;
 | 
						|
	    i--;
 | 
						|
	    continue;
 | 
						|
	  }
 | 
						|
	if (try2)
 | 
						|
	  {
 | 
						|
	    release_upto (name, (DWORD) handle);
 | 
						|
	    try2 = 0;
 | 
						|
	  }
 | 
						|
      }
 | 
						|
  _in_forkee = 0;
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// iterators
 | 
						|
 | 
						|
DllListIterator::DllListIterator (int type) : _type (type), _index (-1)
 | 
						|
{
 | 
						|
  operator++ ();
 | 
						|
}
 | 
						|
 | 
						|
DllListIterator::~DllListIterator ()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
DllListIterator::operator per_process* ()
 | 
						|
{
 | 
						|
  return _list[index ()].p;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
DllListIterator::operator++ ()
 | 
						|
{
 | 
						|
  _index++;
 | 
						|
  while (_index < _last && (int) (_list[_index].type) != _type)
 | 
						|
    _index++;
 | 
						|
  if (_index == _last)
 | 
						|
    _index = -1;
 | 
						|
}
 | 
						|
 | 
						|
LinkedDllIterator::LinkedDllIterator () : DllListIterator ((int) LINK)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
LinkedDllIterator::~LinkedDllIterator ()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
LoadedDllIterator::LoadedDllIterator () : DllListIterator ((int) LOAD)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
LoadedDllIterator::~LoadedDllIterator ()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// the extern symbols
 | 
						|
 | 
						|
extern "C"
 | 
						|
{
 | 
						|
  /* This is an exported copy of environ which can be used by DLLs
 | 
						|
     which use cygwin.dll.  */
 | 
						|
  extern struct _reent reent_data;
 | 
						|
};
 | 
						|
 | 
						|
extern "C"
 | 
						|
int
 | 
						|
dll_dllcrt0 (HMODULE h, per_process *p)
 | 
						|
{
 | 
						|
  /* Partially initialize Cygwin guts for non-cygwin apps. */
 | 
						|
  if (dynamically_loaded && (! user_data || user_data->magic_biscuit == 0))
 | 
						|
    {
 | 
						|
      dll_crt0 (p);
 | 
						|
    }
 | 
						|
  return _the.recordDll (h, p);
 | 
						|
}
 | 
						|
 | 
						|
/* OBSOLETE: This function is obsolescent and will go away in the
 | 
						|
   future.  Cygwin can now handle being loaded from a noncygwin app
 | 
						|
   using the same entry point. */
 | 
						|
 | 
						|
extern "C"
 | 
						|
int
 | 
						|
dll_noncygwin_dllcrt0 (HMODULE h, per_process *p)
 | 
						|
{
 | 
						|
  return dll_dllcrt0 (h, p);
 | 
						|
}
 | 
						|
 | 
						|
extern "C"
 | 
						|
void
 | 
						|
cygwin_detach_dll (int dll_index)
 | 
						|
{
 | 
						|
  _the.detachDll (dll_index);
 | 
						|
}
 | 
						|
 | 
						|
extern "C"
 | 
						|
void
 | 
						|
dlfork (int val)
 | 
						|
{
 | 
						|
  _the.forkeeMustReloadDlls (val);
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
//-----------------------------------------------------------------------------
 |