public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
From: Corinna Vinschen <corinna-cygwin@cygwin.com>
To: cygwin@cygwin.com
Subject: Re: Why does ldd not show cyg*.dll in its output?
Date: Fri, 01 Jul 2016 09:30:00 -0000	[thread overview]
Message-ID: <20160701093033.GV981@calimero.vinschen.de> (raw)
In-Reply-To: <a62bb487-1a0c-77a0-ce29-09520dcbdb7b@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 4326 bytes --]

Hi David,

On Jun 30 23:27, David Macek wrote:
> On 29. 6. 2016 23:45, David Macek wrote:
> > I can try watching them side by side in debuggers tomorrow, maybe I'll find something.
> 
> Yep, found something. TL;DR the issue is that Windows spins a thread
> in the process before our DLLs are loaded. Detailed analysis below.
> [...]
>  8. EXIT_PROCESS_DEBUG_EVENT
> 
> Event #6 is the culprit here. NT apparently starts a thread in our
> process which confuses ldd into believing that the process has
> finished loading DLL from its import table and kills it, as explained
> above. The thread appears non-deterministically, too, sometimes I see
> event #5 (and the associated DLL in ldd's output), sometimes not. The
> thread's entry point is always the same and ProcExp reports the same
> location (I checked in case of some symbol mash-up, as it often
> happens when debugging across the Cygwin-Windows boundary). When I
> broke ldd at `case CREATE_THREAD_DEBUG_EVENT`, the new thread's stack
> trace (from ProcExp) looked like this:
> [...]
> After some experimentation, I came up with a patch that tries to
> determine whether to ignore a thread based on the thread's entry point
> lying inside or outside of the memory occupied by ntdll.dll:

Nice detective work!  As for the below patch, I think the condition
should be turned around to test if the thread entry point is inside the
Cygwin DLL.  This event:

  10. CREATE_THREAD_DEBUG_EVENT ev.u.CreateThread = {hThread = 0x88, lpThreadLocalBase = 0x7ff5ffffb000, lpStartAddress = 0x180044cf0 <cygthread::stub(void*)>} 

denotes that we are ready.  So instead of contining if the thread entry
point is inside ntdll, continue as long as the entry point is not in the
Cygwin DLL.  That would also have the advantage to be independent of
future changes in Windows as well as injection from virus checkers etc.

What do you think?

> --- ldd.cc.orig 2016-04-28 21:54:57.556500700 +0200
> +++ ldd.cc      2016-06-30 23:24:20.394384800 +0200
> @@ -327,6 +327,10 @@
>      {
>        bool exitnow = false;
>        DWORD cont = DBG_CONTINUE;
> +      MODULEINFO mi;
> +      HMODULE ntdll = LoadLibrary("ntdll.dll");
> +      HMODULE ntdllend = NULL;
> +      void *entryPoint;
>        if (!WaitForDebugEvent (&ev, INFINITE))
>         break;
>        switch (ev.dwDebugEventCode)
> @@ -357,6 +361,31 @@
>             }
>           break;
>         case CREATE_THREAD_DEBUG_EVENT:
> +          if (ntdll != NULL)
> +            {
> +              if (ntdllend == NULL)
> +                {
> +                  /* Using our ntdll.dll HMODULE with a foreign process
> +                     should be fine because ntdll.dll is mapped to the same
> +                     address in every concurrently executing process and
> +                     HMODULEs are just pointers to the image in the process
> +                     memory. Using GetModuleHandle(NULL) didn't work for the
> +                     author of this code (-> ERROR_INVALID_HANDLE). */
> +                  if (GetModuleInformation(hProcess, ntdll, &mi, sizeof(mi)))
> +                    ntdllend = ntdll + (ptrdiff_t)mi.SizeOfImage;
> +                  else
> +                    ntdll = NULL;
> +                }
> +              if (ntdllend != NULL)
> +                {
> +                  entryPoint = (void*)ev.u.CreateThread.lpStartAddress;
> +                  /* If the thread's entry point is in ntdll.dll, let's
> +                    assume that this isn't the program's main thread and
> +                    ignore it. */
> +                  if (ntdll <= entryPoint && entryPoint < ntdllend)
> +                    break;
> +                }

This check can be simplified.  Rather than explicitly checking for start
and end address of ntdll.dll (or cygwin1.dll), one could just find the
file for a given address:

  wchar_t fnamebuf[MAX_PATH];
  if (GetMappedFileNameW (hProcess,
			  (PVOID) ev.u.CreateThread.lpStartAddress,
			  fnamebuf, sizeof fnamebuf)
      && wcsstr (fname, L"cygwin1.dll"))
    printf ("bingo\n");


Thanks,
Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  parent reply	other threads:[~2016-07-01  9:30 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-16 15:42 Warren Young
2016-05-16 16:50 ` Andrey Repin
2016-05-16 17:20   ` Warren Young
2016-05-16 17:50 ` Yaakov Selkowitz
2016-05-19 23:21   ` Hans-Bernhard Bröker
2016-05-20  1:20     ` Warren Young
2016-06-30  0:03       ` David Macek
     [not found]         ` <a62bb487-1a0c-77a0-ce29-09520dcbdb7b@gmail.com>
2016-07-01  9:30           ` Corinna Vinschen [this message]
2016-05-16 17:53 ` Marco Atzeri

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160701093033.GV981@calimero.vinschen.de \
    --to=corinna-cygwin@cygwin.com \
    --cc=cygwin@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).