This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

dll_list::load_after_fork()


Hi

On Sun, 2 Mar 2003 12:31:49 -0500, Christopher Faylor wrote:
> I don't see how a change which checks for a valid error condition
> could be considered "wrong".

My apologies for not giving more details, I realize that last message
was a little curt. There are two issues with the current CVS revision of
dll_init.cc:
- it is possible for the child to continue even though it has loaded a
dll to the wrong address, with possible disastrous results (line 324,
the return value of LoadLibrary is not checked);
- the change does not address the problem I raised with unbalanced
PROCESS_ATTACH and PROCESS_DETACH calls to the cygwin entry point
function.

The second issue is the one I want to pursue here. It is clear to me
from reading the MDSN library docs combined with my own experiments
that, on Win 9x/Me only, the sequence:
h = LoadLibraryEx(d.name, NULL, DONT_RESOLVE_DLL_REFERENCES);
FreeLibrary(h);
always results in a call to DllMain() with PROCESS_DETACH even though
there has been no corresponding call to DllMain() with PROCESS_ATTACH.
This *may* cause a segv or data corruption, depending on what the DLL
initialisation/finalisation code does. The only way to avoid this is to
not use LoadLibraryEx(d.name, NULL, DONT_RESOLVE_DLL_REFERENCES) on WIn
9x/Me.

I have also thought through the algorithm of this function some more,
and have (hopefully) identified all the different failure conditions
with appropriate actions.

I have included the text of my proposed function here, so if anyone has
time to read through it I would appreciate any constructive criticism. I
have also attached a patch against the current CVS, so anyone using
run-time loaded DLLS can check its behaviour on both 9x and NT. For me,
this change gets the gnome desktop working for the first time on win ME.

Regards,
Steven

void
dll_list::load_after_fork (HANDLE parent, dll *first)
{
  in_forkee = 1;
  dll d;

  void *next = first;
  while (next)
    {
      DWORD nb;
      /* Read the dll structure from the parent. */
      if (!ReadProcessMemory (parent, next, &d, sizeof (dll), &nb) ||
	  nb != sizeof (dll))
	return;

      /* We're only interested in dynamically loaded dlls.
	 Hopefully, this function wouldn't even have been called unless
	 the parent had some of those. */
      if (d.type == DLL_LOAD)
	{
	  HMODULE h;
	  if (wincap.is_winnt())
	    {
	      /* On NT/2K/XP we first test-load the DLL to see if if goes to
the right
	         address. We have to do this because if we load it fully and
its at the
	         wrong address then the later code that tries to adjust the
load address 
	         fails. */
	      h = LoadLibraryEx (d.name, NULL, DONT_RESOLVE_DLL_REFERENCES);
	      if (h == d.handle)
	        {
	          /* We got the right address first time. So now load it
properly */
	          FreeLibrary(h);
	          h = LoadLibrary(d.name);
	        }
	    }
	  else
	    {
	      /* Win 9x/Me does not support the DONT_RESOLVE_DLL_REFERENCES
flag,
	         so we have to load the DLL straight up */
	      h = LoadLibrary (d.name);
	    }
	  
	  if (h != d.handle)
	  {
	    /* We treat each error case differently */
	    if (h == NULL)
	      {
	      	/* Some major failure. We cannot recover from this, so we bale
out */
	        api_fatal ("child unable to load %s", d.name);
	      }
	      
	    if (h > d.handle)
	      {
	        /* Loaded too high. We cannot fix this, so we bale out */
	        api_fatal ("unable to remap %s to same address as parent(%p) !=
%p",
		           d.name, d.handle, h);
	      }
	      
	    /* If the thread reaches here, then the DLL loaded too low.
	       We can attempt to fix this by unloading it, reserving all free
	       memory up to the address we want, then re-loading it and finally
	       releasing the reserved memory. Sometimes this works, sometimes 
	       it dont. */
	    FreeLibrary (h);
	    reserve_upto (d.name, (DWORD) d.handle);
	    h = LoadLibrary(d.name);
	    release_upto (d.name, (DWORD) d.handle);
	    if (h != d.handle)
	      {
	      	/* If its still the wrong address, then there's nothing more we
	      	   can do. Bale out */
	        api_fatal ("unable to remap %s to same address as parent(%p) !=
%p",
		           d.name, d.handle, h);
	      }
	  }
	}
      next = d.next;	/* Get the address of the next DLL. */
    }
  in_forkee = 0;
}

Attachment: dll_init.cc-patch
Description: Binary data

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]