* ldd.cc (STATUS_DLL_NOT_FOUND): Drop definition.
        (report): Handle STATUS_ENTRYPOINT_NOT_FOUND exception.  Explain why.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
		
	
		
			
				
	
	
		
			675 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			675 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* Copyright (c) 2009, 2010, 2011, 2013  Chris Faylor
 | 
						|
 | 
						|
  All rights reserved.
 | 
						|
 | 
						|
  Redistribution and use in source and binary forms, with or without
 | 
						|
  modification, are permitted provided that the following conditions are met:
 | 
						|
 | 
						|
	* Redistributions of source code must retain the above copyright
 | 
						|
	notice, this list of conditions and the following disclaimer.
 | 
						|
	* Neither the name of the owner nor the names of its
 | 
						|
	contributors may be used to endorse or promote products derived from
 | 
						|
	this software without specific prior written permission.
 | 
						|
 | 
						|
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
 | 
						|
  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | 
						|
  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 | 
						|
  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
 | 
						|
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
						|
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
						|
  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
						|
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
						|
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
						|
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
						|
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
*/
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <getopt.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <wchar.h>
 | 
						|
#include <locale.h>
 | 
						|
#include <sys/cygwin.h>
 | 
						|
#include <cygwin/version.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <libgen.h>
 | 
						|
 | 
						|
#define _WIN32_WINNT 0x0501
 | 
						|
#include <windows.h>
 | 
						|
#include <imagehlp.h>
 | 
						|
#include <psapi.h>
 | 
						|
 | 
						|
struct option longopts[] =
 | 
						|
{
 | 
						|
  {"help", no_argument, NULL, 'h'},
 | 
						|
  {"verbose", no_argument, NULL, 'v'},
 | 
						|
  {"version", no_argument, NULL, 'V'},
 | 
						|
  {"data-relocs", no_argument, NULL, 'd'},
 | 
						|
  {"function-relocs", no_argument, NULL, 'r'},
 | 
						|
  {"unused", no_argument, NULL, 'u'},
 | 
						|
  {0, no_argument, NULL, 0}
 | 
						|
};
 | 
						|
const char *opts = "dhruvV";
 | 
						|
 | 
						|
static int process_file (const wchar_t *);
 | 
						|
 | 
						|
static int
 | 
						|
error (const char *fmt, ...)
 | 
						|
{
 | 
						|
  va_list ap;
 | 
						|
  va_start (ap, fmt);
 | 
						|
  fprintf (stderr, "ldd: ");
 | 
						|
  vfprintf (stderr, fmt, ap);
 | 
						|
  fprintf (stderr, "\nTry `ldd --help' for more information.\n");
 | 
						|
  exit (1);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
usage ()
 | 
						|
{
 | 
						|
  printf ("Usage: %s [OPTION]... FILE...\n\
 | 
						|
\n\
 | 
						|
Print shared library dependencies\n\
 | 
						|
\n\
 | 
						|
  -h, --help              print this help and exit\n\
 | 
						|
  -V, --version           print version information and exit\n\
 | 
						|
  -r, --function-relocs   process data and function relocations\n\
 | 
						|
                          (currently unimplemented)\n\
 | 
						|
  -u, --unused            print unused direct dependencies\n\
 | 
						|
                          (currently unimplemented)\n\
 | 
						|
  -v, --verbose           print all information\n\
 | 
						|
                          (currently unimplemented)\n",
 | 
						|
	   program_invocation_short_name);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
print_version ()
 | 
						|
{
 | 
						|
  printf ("ldd (cygwin) %d.%d.%d\n"
 | 
						|
	  "Print shared library dependencies\n"
 | 
						|
	  "Copyright (C) 2009 - %s Chris Faylor\n"
 | 
						|
	  "This is free software; see the source for copying conditions.  There is NO\n"
 | 
						|
	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
 | 
						|
	  CYGWIN_VERSION_DLL_MAJOR / 1000,
 | 
						|
	  CYGWIN_VERSION_DLL_MAJOR % 1000,
 | 
						|
	  CYGWIN_VERSION_DLL_MINOR,
 | 
						|
	  strrchr (__DATE__, ' ') + 1);
 | 
						|
}
 | 
						|
 | 
						|
#define print_errno_error_and_return(__fn) \
 | 
						|
  do {\
 | 
						|
    fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
 | 
						|
    return 1;\
 | 
						|
  } while (0)
 | 
						|
 | 
						|
#define set_errno_and_return(x) \
 | 
						|
  do {\
 | 
						|
    cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
 | 
						|
    return (x);\
 | 
						|
  } while (0)
 | 
						|
 | 
						|
 | 
						|
static HANDLE hProcess;
 | 
						|
 | 
						|
static struct filelist
 | 
						|
{
 | 
						|
  struct filelist *next;
 | 
						|
  char *name;
 | 
						|
} *head;
 | 
						|
 | 
						|
static bool
 | 
						|
saw_file (char *name)
 | 
						|
{
 | 
						|
  filelist *p;
 | 
						|
 | 
						|
  for (p = head; p; p = p->next)
 | 
						|
    if (strcasecmp (name, p->name) == 0)
 | 
						|
      return true;
 | 
						|
 | 
						|
  p = (filelist *) malloc(sizeof (struct filelist));
 | 
						|
  p->next = head;
 | 
						|
  p->name = strdup (name);
 | 
						|
  head = p;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static wchar_t *
 | 
						|
get_module_filename (HANDLE hp, HMODULE hm)
 | 
						|
{
 | 
						|
  size_t len;
 | 
						|
  wchar_t *buf = NULL;
 | 
						|
  DWORD res;
 | 
						|
  for (len = 1024; (res = GetModuleFileNameExW (hp, hm, (buf = (wchar_t *) realloc (buf, len * sizeof (wchar_t))), len)) == len; len += 1024)
 | 
						|
    continue;
 | 
						|
  if (!res)
 | 
						|
    {
 | 
						|
      free (buf);
 | 
						|
      buf = NULL;
 | 
						|
    }
 | 
						|
  return buf;
 | 
						|
}
 | 
						|
 | 
						|
static wchar_t *
 | 
						|
load_dll (const wchar_t *fn)
 | 
						|
{
 | 
						|
  wchar_t *buf = get_module_filename (GetCurrentProcess (), NULL);
 | 
						|
  if (!buf)
 | 
						|
    {
 | 
						|
      printf ("ldd: GetModuleFileName returned an error %u\n",
 | 
						|
	      (unsigned int) GetLastError ());
 | 
						|
      exit (1);		/* FIXME */
 | 
						|
    }
 | 
						|
 | 
						|
  wchar_t *newbuf = (wchar_t *) malloc ((sizeof (L"\"\" -- ") + wcslen (buf) + wcslen (fn)) * sizeof (wchar_t));
 | 
						|
  newbuf[0] = L'"';
 | 
						|
  wcscpy (newbuf + 1, buf);
 | 
						|
  wchar_t *p = wcsstr (newbuf, L"\\ldd");
 | 
						|
  if (!p)
 | 
						|
    {
 | 
						|
      printf ("ldd: can't parse my own filename \"%ls\"\n", buf);
 | 
						|
      exit (1);
 | 
						|
    }
 | 
						|
  p[3] = L'h';
 | 
						|
  wcscat (newbuf, L"\" -- ");
 | 
						|
  wcscat (newbuf, fn);
 | 
						|
  free (buf);
 | 
						|
  return newbuf;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
start_process (const wchar_t *fn, bool& isdll)
 | 
						|
{
 | 
						|
  STARTUPINFOW si = {};
 | 
						|
  PROCESS_INFORMATION pi;
 | 
						|
  si.cb = sizeof (si);
 | 
						|
  wchar_t *cmd;
 | 
						|
  /* OCaml natdynlink plugins (.cmxs) cannot be handled by ldd because they
 | 
						|
     can only be loaded by flexdll_dlopen() */
 | 
						|
  if (wcslen (fn) < 4 || (wcscasecmp (wcschr (fn, L'\0') - 4, L".dll") != 0
 | 
						|
       && wcscasecmp (wcschr (fn, L'\0') - 4, L".oct") != 0
 | 
						|
       && wcscasecmp (wcschr (fn, L'\0') - 3, L".so") != 0))
 | 
						|
    {
 | 
						|
      cmd = wcsdup (fn);
 | 
						|
      isdll = false;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      cmd = load_dll (fn);
 | 
						|
      isdll = true;
 | 
						|
    }
 | 
						|
  if (CreateProcessW (NULL, cmd, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi))
 | 
						|
    {
 | 
						|
      free (cmd);
 | 
						|
      hProcess = pi.hProcess;
 | 
						|
      DebugSetProcessKillOnExit (true);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  free (cmd);
 | 
						|
  set_errno_and_return (1);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_entry_point_break ()
 | 
						|
{
 | 
						|
  HMODULE hm;
 | 
						|
  DWORD cbe;
 | 
						|
  SIZE_T cbw;
 | 
						|
  if (!EnumProcessModules (hProcess, &hm, sizeof (hm), &cbe) || !cbe)
 | 
						|
    set_errno_and_return (1);
 | 
						|
 | 
						|
  MODULEINFO mi = {};
 | 
						|
  if (!GetModuleInformation (hProcess, hm, &mi, sizeof (mi)) || !mi.EntryPoint)
 | 
						|
    set_errno_and_return (1);
 | 
						|
 | 
						|
  static const unsigned char int3 = 0xcc;
 | 
						|
  if (!WriteProcessMemory (hProcess, mi.EntryPoint, &int3, 1, &cbw) || cbw != 1)
 | 
						|
    set_errno_and_return (1);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct dlls
 | 
						|
  {
 | 
						|
    LPVOID lpBaseOfDll;
 | 
						|
    struct dlls *next;
 | 
						|
  };
 | 
						|
 | 
						|
#define SLOP strlen (" (?)")
 | 
						|
char *
 | 
						|
tocyg (wchar_t *win_fn)
 | 
						|
{
 | 
						|
  ssize_t cwlen = cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, NULL, 0);
 | 
						|
  char *fn;
 | 
						|
  if (cwlen <= 0)
 | 
						|
    {
 | 
						|
      int len = wcstombs (NULL, win_fn, 0) + 1;
 | 
						|
      if ((fn = (char *) malloc (len)))
 | 
						|
	wcstombs (fn, win_fn, len);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      char *fn_cyg = (char *) malloc (cwlen + SLOP + 1);
 | 
						|
      if (cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, fn_cyg, cwlen) == 0)
 | 
						|
	fn = fn_cyg;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  free (fn_cyg);
 | 
						|
	  int len = wcstombs (NULL, win_fn, 0);
 | 
						|
	  fn = (char *) malloc (len + SLOP + 1);
 | 
						|
	  wcstombs (fn, win_fn, len + SLOP + 1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return fn;
 | 
						|
}
 | 
						|
 | 
						|
#define CYGWIN_DLL_LEN (wcslen (L"\\cygwin1.dll"))
 | 
						|
static int
 | 
						|
print_dlls (dlls *dll, const wchar_t *dllfn, const wchar_t *process_fn)
 | 
						|
{
 | 
						|
  head = NULL;			/* FIXME: memory leak */
 | 
						|
  while ((dll = dll->next))
 | 
						|
    {
 | 
						|
      char *fn;
 | 
						|
      wchar_t *fullpath = get_module_filename (hProcess, (HMODULE) dll->lpBaseOfDll);
 | 
						|
      if (!fullpath)
 | 
						|
	fn = strdup ("???");
 | 
						|
      else if (dllfn && wcscmp (fullpath, dllfn) == 0)
 | 
						|
	{
 | 
						|
	  free (fullpath);
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  fn = tocyg (fullpath);
 | 
						|
	  saw_file (basename (fn));
 | 
						|
	  free (fullpath);
 | 
						|
	}
 | 
						|
      printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
 | 
						|
      free (fn);
 | 
						|
    }
 | 
						|
  if (process_fn)
 | 
						|
    return process_file (process_fn);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
report (const char *in_fn, bool multiple)
 | 
						|
{
 | 
						|
  if (multiple)
 | 
						|
    printf ("%s:\n", in_fn);
 | 
						|
  char *fn = realpath (in_fn, NULL);
 | 
						|
  if (!fn)
 | 
						|
    print_errno_error_and_return (in_fn);
 | 
						|
 | 
						|
  ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, NULL, 0);
 | 
						|
  if (len <= 0)
 | 
						|
    print_errno_error_and_return (fn);
 | 
						|
 | 
						|
  bool isdll;
 | 
						|
  wchar_t fn_win[len + 1];
 | 
						|
  if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, fn_win, len))
 | 
						|
    print_errno_error_and_return (fn);
 | 
						|
 | 
						|
  if (!fn || start_process (fn_win, isdll))
 | 
						|
    print_errno_error_and_return (in_fn);
 | 
						|
 | 
						|
  DEBUG_EVENT ev;
 | 
						|
 | 
						|
  unsigned dll_count = 0;
 | 
						|
 | 
						|
  dlls dll_list = {};
 | 
						|
  dlls *dll_last = &dll_list;
 | 
						|
  const wchar_t *process_fn = NULL;
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      bool exitnow = false;
 | 
						|
      DWORD cont = DBG_CONTINUE;
 | 
						|
      if (!WaitForDebugEvent (&ev, INFINITE))
 | 
						|
	break;
 | 
						|
      switch (ev.dwDebugEventCode)
 | 
						|
	{
 | 
						|
	case LOAD_DLL_DEBUG_EVENT:
 | 
						|
	  if (!isdll && ++dll_count == 2)
 | 
						|
	    set_entry_point_break ();
 | 
						|
	  dll_last->next = (dlls *) malloc (sizeof (dlls));
 | 
						|
	  dll_last->next->lpBaseOfDll = ev.u.LoadDll.lpBaseOfDll;
 | 
						|
	  dll_last->next->next = NULL;
 | 
						|
	  dll_last = dll_last->next;
 | 
						|
	  break;
 | 
						|
	case EXCEPTION_DEBUG_EVENT:
 | 
						|
	  switch (ev.u.Exception.ExceptionRecord.ExceptionCode)
 | 
						|
	    {
 | 
						|
	    case STATUS_ENTRYPOINT_NOT_FOUND:
 | 
						|
	      /* A STATUS_ENTRYPOINT_NOT_FOUND might be encountered right after
 | 
						|
		 loading all DLLs.  We have to handle it here, otherwise ldd
 | 
						|
		 runs into an endless loop. */
 | 
						|
	      goto print_and_exit;
 | 
						|
	    case STATUS_DLL_NOT_FOUND:
 | 
						|
	      process_fn = fn_win;
 | 
						|
	      break;
 | 
						|
	    case STATUS_BREAKPOINT:
 | 
						|
	      if (!isdll)
 | 
						|
		cont = DBG_EXCEPTION_NOT_HANDLED;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	case CREATE_THREAD_DEBUG_EVENT:
 | 
						|
	  TerminateProcess (hProcess, 0);
 | 
						|
	  break;
 | 
						|
	case EXIT_PROCESS_DEBUG_EVENT:
 | 
						|
print_and_exit:
 | 
						|
	  print_dlls (&dll_list, isdll ? fn_win : NULL, process_fn);
 | 
						|
	  exitnow = true;
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, cont))
 | 
						|
	{
 | 
						|
	  cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);
 | 
						|
	  print_errno_error_and_return (in_fn);
 | 
						|
	}
 | 
						|
      if (exitnow)
 | 
						|
	break;
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main (int argc, char **argv)
 | 
						|
{
 | 
						|
  int optch;
 | 
						|
 | 
						|
  /* Use locale from environment.  If not set or set to "C", use UTF-8. */
 | 
						|
  setlocale (LC_CTYPE, "");
 | 
						|
  if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
 | 
						|
    setlocale (LC_CTYPE, "en_US.UTF-8");
 | 
						|
  while ((optch = getopt_long (argc, argv, opts, longopts, NULL)) != -1)
 | 
						|
    switch (optch)
 | 
						|
      {
 | 
						|
      case 'd':
 | 
						|
      case 'r':
 | 
						|
      case 'u':
 | 
						|
	error ("option not implemented `-%c'", optch);
 | 
						|
	exit (1);
 | 
						|
      case 'h':
 | 
						|
	usage ();
 | 
						|
	exit (0);
 | 
						|
      case 'V':
 | 
						|
	print_version ();
 | 
						|
	return 0;
 | 
						|
      default:
 | 
						|
	fprintf (stderr, "Try `%s --help' for more information.\n",
 | 
						|
		 program_invocation_short_name);
 | 
						|
	return 1;
 | 
						|
      }
 | 
						|
  argv += optind;
 | 
						|
  if (!*argv)
 | 
						|
    error ("missing file arguments");
 | 
						|
 | 
						|
  int ret = 0;
 | 
						|
  bool multiple = !!argv[1];
 | 
						|
  char *fn;
 | 
						|
  while ((fn = *argv++))
 | 
						|
    if (report (fn, multiple))
 | 
						|
      ret = 1;
 | 
						|
  exit (ret);
 | 
						|
}
 | 
						|
 | 
						|
static bool printing = false;
 | 
						|
 | 
						|
 | 
						|
/* dump of import directory
 | 
						|
   section begins at pointer 'section base'
 | 
						|
   section RVA is 'section_rva'
 | 
						|
   import directory begins at pointer 'imp' */
 | 
						|
static int
 | 
						|
dump_import_directory (const void *const section_base,
 | 
						|
		       const DWORD section_rva,
 | 
						|
		       const IMAGE_IMPORT_DESCRIPTOR *imp)
 | 
						|
{
 | 
						|
  /* get memory address given the RVA */
 | 
						|
  #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
 | 
						|
 | 
						|
  /* continue until address inaccessible or there's no DLL name */
 | 
						|
  for (; !IsBadReadPtr (imp, sizeof (*imp)) && imp->Name; imp++)
 | 
						|
    {
 | 
						|
      wchar_t full_path[PATH_MAX];
 | 
						|
      wchar_t *dummy;
 | 
						|
      char *fn = (char *) adr (imp->Name);
 | 
						|
 | 
						|
      if (saw_file (fn))
 | 
						|
	continue;
 | 
						|
 | 
						|
      int len = mbstowcs (NULL, fn, 0);
 | 
						|
      if (len <= 0)
 | 
						|
	continue;
 | 
						|
      wchar_t fnw[len + 1];
 | 
						|
      mbstowcs (fnw, fn, len + 1);
 | 
						|
      /* output DLL's name */
 | 
						|
      char *print_fn;
 | 
						|
      if (!SearchPathW (NULL, fnw, NULL, PATH_MAX, full_path, &dummy))
 | 
						|
	{
 | 
						|
	  print_fn = strdup ("not found");
 | 
						|
	  printing = true;
 | 
						|
	}
 | 
						|
      else if (!printing)
 | 
						|
	continue;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  print_fn = tocyg (full_path);
 | 
						|
	  strcat (print_fn, " (?)");
 | 
						|
	}
 | 
						|
 | 
						|
      printf ("\t%s => %s\n", (char *) fn, print_fn);
 | 
						|
      free (print_fn);
 | 
						|
    }
 | 
						|
  #undef adr
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* load a file in RAM (memory-mapped)
 | 
						|
   return pointer to loaded file
 | 
						|
   0 if no success  */
 | 
						|
static void *
 | 
						|
map_file (const wchar_t *filename)
 | 
						|
{
 | 
						|
  HANDLE hFile, hMapping;
 | 
						|
  void *basepointer;
 | 
						|
  if ((hFile = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
 | 
						|
			   0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
 | 
						|
    {
 | 
						|
      fprintf (stderr, "couldn't open %ls\n", filename);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
 | 
						|
    {
 | 
						|
      fprintf (stderr, "CreateFileMapping failed with windows error %u\n",
 | 
						|
	       (unsigned int) GetLastError ());
 | 
						|
      CloseHandle (hFile);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0)))
 | 
						|
    {
 | 
						|
      fprintf (stderr, "MapViewOfFile failed with windows error %u\n",
 | 
						|
	       (unsigned int) GetLastError ());
 | 
						|
      CloseHandle (hMapping);
 | 
						|
      CloseHandle (hFile);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  CloseHandle (hMapping);
 | 
						|
  CloseHandle (hFile);
 | 
						|
 | 
						|
  return basepointer;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* this will return a pointer immediatly behind the DOS-header
 | 
						|
   0 if error */
 | 
						|
static void *
 | 
						|
skip_dos_stub (const IMAGE_DOS_HEADER *dos_ptr)
 | 
						|
{
 | 
						|
  /* look there's enough space for a DOS-header */
 | 
						|
  if (IsBadReadPtr (dos_ptr, sizeof (*dos_ptr)))
 | 
						|
      {
 | 
						|
	fprintf (stderr, "not enough space for DOS-header\n");
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
 | 
						|
   /* validate MZ */
 | 
						|
   if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE)
 | 
						|
      {
 | 
						|
	fprintf (stderr, "not a DOS-stub\n");
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
 | 
						|
  /* ok, then, go get it */
 | 
						|
  return (char*) dos_ptr + dos_ptr->e_lfanew;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* find the directory's section index given the RVA
 | 
						|
   Returns -1 if impossible */
 | 
						|
static int
 | 
						|
get_directory_index (const unsigned dir_rva,
 | 
						|
		     const unsigned dir_length,
 | 
						|
		     const int number_of_sections,
 | 
						|
		     const IMAGE_SECTION_HEADER *sections)
 | 
						|
{
 | 
						|
  int sect;
 | 
						|
  for (sect = 0; sect < number_of_sections; sect++)
 | 
						|
  {
 | 
						|
    /* compare directory RVA to section RVA */
 | 
						|
    if (sections[sect].VirtualAddress <= dir_rva
 | 
						|
       && dir_rva < sections[sect].VirtualAddress+sections[sect].SizeOfRawData)
 | 
						|
      return sect;
 | 
						|
  }
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* dump imports of a single file
 | 
						|
   Returns 0 if successful, !=0 else */
 | 
						|
static int
 | 
						|
process_file (const wchar_t *filename)
 | 
						|
{
 | 
						|
  void *basepointer;    /* Points to loaded PE file
 | 
						|
			 * This is memory mapped stuff
 | 
						|
			 */
 | 
						|
  int number_of_sections;
 | 
						|
  DWORD import_rva;           /* RVA of import directory */
 | 
						|
  DWORD import_length;        /* length of import directory */
 | 
						|
  int import_index;           /* index of section with import directory */
 | 
						|
 | 
						|
  /* ensure byte-alignment for struct tag_header */
 | 
						|
  #include <pshpack1.h>
 | 
						|
 | 
						|
  const struct tag_header
 | 
						|
    {
 | 
						|
      DWORD signature;
 | 
						|
      IMAGE_FILE_HEADER file_head;
 | 
						|
      IMAGE_OPTIONAL_HEADER opt_head;
 | 
						|
      IMAGE_SECTION_HEADER section_header[1];  /* an array of unknown length */
 | 
						|
    } *header;
 | 
						|
 | 
						|
  /* revert to regular alignment */
 | 
						|
  #include <poppack.h>
 | 
						|
 | 
						|
  printing = false;
 | 
						|
 | 
						|
  /* first, load file */
 | 
						|
  basepointer = map_file (filename);
 | 
						|
  if (!basepointer)
 | 
						|
      {
 | 
						|
	puts ("cannot load file");
 | 
						|
	return 1;
 | 
						|
      }
 | 
						|
 | 
						|
  /* get header pointer; validate a little bit */
 | 
						|
  header = (tag_header *) skip_dos_stub ((IMAGE_DOS_HEADER *) basepointer);
 | 
						|
  if (!header)
 | 
						|
      {
 | 
						|
	puts ("cannot skip DOS stub");
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 2;
 | 
						|
      }
 | 
						|
 | 
						|
  /* look there's enough space for PE headers */
 | 
						|
  if (IsBadReadPtr (header, sizeof (*header)))
 | 
						|
      {
 | 
						|
	puts ("not enough space for PE headers");
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 3;
 | 
						|
      }
 | 
						|
 | 
						|
  /* validate PE signature */
 | 
						|
  if (header->signature != IMAGE_NT_SIGNATURE)
 | 
						|
      {
 | 
						|
	puts ("not a PE file");
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 4;
 | 
						|
      }
 | 
						|
 | 
						|
  /* get number of sections */
 | 
						|
  number_of_sections = header->file_head.NumberOfSections;
 | 
						|
 | 
						|
  /* check there are sections... */
 | 
						|
  if (number_of_sections < 1)
 | 
						|
      {
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 5;
 | 
						|
      }
 | 
						|
 | 
						|
  /* validate there's enough space for section headers */
 | 
						|
  if (IsBadReadPtr (header->section_header, number_of_sections*sizeof (IMAGE_SECTION_HEADER)))
 | 
						|
      {
 | 
						|
	puts ("not enough space for section headers");
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 6;
 | 
						|
      }
 | 
						|
 | 
						|
  /* get RVA and length of import directory */
 | 
						|
  import_rva = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 | 
						|
  import_length = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
 | 
						|
 | 
						|
  /* check there's stuff to care about */
 | 
						|
  if (!import_rva || !import_length)
 | 
						|
      {
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 0;       /* success! */
 | 
						|
    }
 | 
						|
 | 
						|
  /* get import directory pointer */
 | 
						|
  import_index = get_directory_index (import_rva,import_length,number_of_sections,header->section_header);
 | 
						|
 | 
						|
  /* check directory was found */
 | 
						|
  if (import_index < 0)
 | 
						|
      {
 | 
						|
	puts ("couldn't find import directory in sections");
 | 
						|
	UnmapViewOfFile (basepointer);
 | 
						|
	return 7;
 | 
						|
      }
 | 
						|
 | 
						|
  /* The pointer to the start of the import directory's section */
 | 
						|
  const void *section_address = (char*) basepointer + header->section_header[import_index].PointerToRawData;
 | 
						|
  if (dump_import_directory (section_address,
 | 
						|
			   header->section_header[import_index].VirtualAddress,
 | 
						|
				    /* the last parameter is the pointer to the import directory:
 | 
						|
				       section address + (import RVA - section RVA)
 | 
						|
				       The difference is the offset of the import directory in the section */
 | 
						|
			   (const IMAGE_IMPORT_DESCRIPTOR *) ((char *) section_address+import_rva-header->section_header[import_index].VirtualAddress)))
 | 
						|
    {
 | 
						|
      UnmapViewOfFile (basepointer);
 | 
						|
      return 8;
 | 
						|
    }
 | 
						|
 | 
						|
  UnmapViewOfFile (basepointer);
 | 
						|
  return 0;
 | 
						|
}
 |