From 086dc27fec1b3e5ff4e01404b67e02d745f01d4b Mon Sep 17 00:00:00 2001
From: Christopher Faylor <me@cgf.cx>
Date: Fri, 9 Jan 2009 05:11:57 +0000
Subject: [PATCH] * ldd.cc: New file.  First stab at implementing ldd-like
 functionality for Cygwin. * Makefile.in (CYGWIN_BINS): Add ldd. (ldd.exe):
 Use -lpsapi.

---
 winsup/utils/ChangeLog   |   7 ++
 winsup/utils/Makefile.in |   4 +-
 winsup/utils/ldd.cc      | 265 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 275 insertions(+), 1 deletion(-)
 create mode 100644 winsup/utils/ldd.cc

diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog
index 335f38775..280bdd45b 100644
--- a/winsup/utils/ChangeLog
+++ b/winsup/utils/ChangeLog
@@ -1,3 +1,10 @@
+2009-01-09  Christopher Faylor  <me+cygwin@cgf.cx>
+
+	* ldd.cc: New file.  First stab at implementing ldd-like functionality
+	for Cygwin.
+	* Makefile.in (CYGWIN_BINS): Add ldd.
+	(ldd.exe): Use -lpsapi.
+
 2009-01-05  Pierre Humblet  <Pierre.Humblet@ieee.org>
 
 	* cygcheck.cc (dump_sysinfo_services): Quote the path for popen.
diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in
index 9e3f8efdb..016ecffe9 100644
--- a/winsup/utils/Makefile.in
+++ b/winsup/utils/Makefile.in
@@ -52,7 +52,7 @@ MINGW_CXX        := ${srcdir}/mingw ${CXX} -I${updir}
 
 # List all binaries to be linked in Cygwin mode.  Each binary on this list
 # must have a corresponding .o of the same name.
-CYGWIN_BINS := ${addsuffix .exe,cygpath getfacl kill mkgroup \
+CYGWIN_BINS := ${addsuffix .exe,cygpath getfacl ldd kill mkgroup \
         mkpasswd mount passwd ps regtool setfacl setmetamode ssp umount}
 
 # List all binaries to be linked in MinGW mode.  Each binary on this list
@@ -73,6 +73,8 @@ cygcheck.exe: bloda.o path.o dump_setup.o
 cygcheck.exe: MINGW_LDFLAGS += -lntdll
 cygpath.exe: ALL_LDFLAGS += -lntdll
 
+ldd.exe: ALL_LDFLAGS += -lpsapi
+
 # Check for dumper's requirements and enable it if found.
 LIBICONV := @libiconv@
 libbfd   := ${shell $(CC) -B$(bupdir2)/bfd/ --print-file-name=libbfd.a}
diff --git a/winsup/utils/ldd.cc b/winsup/utils/ldd.cc
new file mode 100644
index 000000000..bba9764f6
--- /dev/null
+++ b/winsup/utils/ldd.cc
@@ -0,0 +1,265 @@
+/* Copyright (c) 2009, 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 <sys/cygwin.h>
+#include <unistd.h>
+
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#include <imagehlp.h>
+#include <psapi.h>
+
+#define VERSION "1.0"
+
+struct option longopts[] =
+{
+  {"help", no_argument, NULL, 0},
+  {"version", no_argument, NULL, 0},
+  {"data-relocs", no_argument, NULL, 'd'},
+  {"function-relocs", no_argument, NULL, 'r'},
+  {"unused", no_argument, NULL, 'u'},
+  {0, no_argument, NULL, 0}
+};
+
+static int
+usage (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);
+}
+
+#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 int
+start_process (const char *fn)
+{
+  ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, NULL, 0);
+  if (len <= 0)
+    print_errno_error_and_return (fn);
+
+  char fn_win[len + 1];
+  if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, fn_win, len))
+    print_errno_error_and_return (fn);
+
+  STARTUPINFO si = {};
+  PROCESS_INFORMATION pi;
+  si.cb = sizeof (si);
+  if (CreateProcess (NULL, fn_win, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi))
+    {
+      hProcess = pi.hProcess;
+      DebugSetProcessKillOnExit (true);
+      return 0;
+    }
+
+  set_errno_and_return (1);
+}
+
+static int
+get_entry_point ()
+{
+  HMODULE hm;
+  DWORD cb;
+  if (!EnumProcessModules (hProcess, &hm, sizeof (hm), &cb) || !cb)
+    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, &cb) || cb != 1)
+    set_errno_and_return (1);
+  return 0;
+}
+
+struct dlls
+  {
+    LPVOID lpBaseOfDll;
+    struct dlls *next;
+  };
+
+static int
+print_dlls_and_kill_inferior (dlls *dll)
+{
+  while ((dll = dll->next))
+    {
+      char *fn;
+      char fnbuf[MAX_PATH + 1];
+      DWORD len = GetModuleFileNameEx (hProcess, (HMODULE) dll->lpBaseOfDll, fnbuf, sizeof(fnbuf) - 1);
+      if (!len)
+	fn = strdup ("???");
+      else
+	{
+	  fnbuf[MAX_PATH] = '\0';
+	  ssize_t cwlen = cygwin_conv_path (CCP_WIN_A_TO_POSIX, fnbuf, NULL, 0);
+	  if (cwlen <= 0)
+	    fn = strdup (fnbuf);
+	  else
+	    {
+	      char *fn_cyg = (char *) malloc (cwlen + 1);
+	      if (cygwin_conv_path (CCP_WIN_A_TO_POSIX, fnbuf, fn_cyg, cwlen) == 0)
+		fn = fn_cyg;
+	      else
+		{
+		  free (fn_cyg);
+		  fn = strdup (fnbuf);
+		}
+	    }
+	}
+      printf ("\t%s (%p)\n", fn, dll->lpBaseOfDll);
+      free (fn);
+    }
+  TerminateProcess (hProcess, 0);
+  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 || start_process (fn))
+    print_errno_error_and_return (in_fn);
+
+  DEBUG_EVENT ev;
+
+  unsigned dll_count = 0;
+
+  dlls dll_list = {};
+  dlls *dll_last = &dll_list;
+  while (1)
+    {
+      if (WaitForDebugEvent (&ev, 1000))
+	/* ok */;
+      else
+	switch (GetLastError ())
+	  {
+	  case WAIT_TIMEOUT:
+	    continue;
+	  default:
+	    usleep (100000);
+	    goto out;
+	  }
+      switch (ev.dwDebugEventCode)
+	{
+	case LOAD_DLL_DEBUG_EVENT:
+	  if (++dll_count == 2)
+	    get_entry_point ();
+	  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:
+	  print_dlls_and_kill_inferior (&dll_list);
+	  break;
+	default:
+	  break;
+	}
+      if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE))
+	{
+	  cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);
+	  print_errno_error_and_return (in_fn);
+	}
+    }
+
+out:
+  return 0;
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int optch;
+  int index;
+  while ((optch = getopt_long (argc, argv, "dru", longopts, &index)) != -1)
+    switch (optch)
+      {
+      case 'd':
+      case 'r':
+      case 'u':
+	usage ("option not implemented `-%c'", optch);
+	exit (1);
+      case 0:
+	if (index == 1)
+	  {
+	    printf ("ldd (Cygwin) %s\nCopyright (C) 2009 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",
+		    VERSION);
+	    exit (0);
+	  }
+	else
+	  {
+	    puts ("Usage: ldd [OPTION]... FILE...\n\
+      --help              print this help and exit\n\
+      --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)");
+	    exit (0);
+	  }
+      }
+  argv += optind;
+  if (!*argv)
+    usage("missing file arguments");
+
+  int ret = 0;
+  bool multiple = !!argv[1];
+  char *fn;
+  while ((fn = *argv++))
+    if (report (fn, multiple))
+      ret = 1;
+  exit (ret);
+}