Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <me@cgf.cx>2009-03-14 09:46:00 +0300
committerChristopher Faylor <me@cgf.cx>2009-03-14 09:46:00 +0300
commit962acfe58a7fb41e42abfa61a7edce1f24bcfebc (patch)
treeafb9eff5fdec85c17f23eea37acdd3b4410aa367 /winsup/utils/ldd.cc
parentc298c9bdbf7ac64a6f7d7b65be73089fd1439e3d (diff)
* ldd.cc: Rework to detect missing DLLs.
(start_process): Change to expect windows filename as input. (tocyg): New function - convert cygwin fn to windows fn. (print_dlls_and_kill_inferior): Accept extra argument denoting whether to open input and look for nonexistent DLLs. Use tocyg to convert filename and pass it to start_process. (report): Flag when an DLL-not-found exception occurs and pass this information to print_dlls_and_kill_inferior. (filelist): New structure. (saw_file): New function. (dump_import_directory): Ditto. (map_file): Ditto. (skip_dos_stub): Ditto. (get_directory_index): Ditto. (process_file): Ditto.
Diffstat (limited to 'winsup/utils/ldd.cc')
-rw-r--r--winsup/utils/ldd.cc359
1 files changed, 329 insertions, 30 deletions
diff --git a/winsup/utils/ldd.cc b/winsup/utils/ldd.cc
index bba9764f6..df374b49e 100644
--- a/winsup/utils/ldd.cc
+++ b/winsup/utils/ldd.cc
@@ -32,12 +32,17 @@
#include <string.h>
#include <sys/cygwin.h>
#include <unistd.h>
+#include <libgen.h>
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <imagehlp.h>
#include <psapi.h>
+#ifndef STATUS_DLL_NOT_FOUND
+#define STATUS_DLL_NOT_FOUND (0xC0000135L)
+#endif
+
#define VERSION "1.0"
struct option longopts[] =
@@ -50,6 +55,8 @@ struct option longopts[] =
{0, no_argument, NULL, 0}
};
+static int process_file (const char *);
+
static int
usage (const char *fmt, ...)
{
@@ -79,18 +86,10 @@ 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))
+ if (CreateProcess (NULL, (CHAR *) fn, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi))
{
hProcess = pi.hProcess;
DebugSetProcessKillOnExit (true);
@@ -124,8 +123,32 @@ struct dlls
struct dlls *next;
};
+#define SLOP strlen (" (?)")
+char *
+tocyg (char *win_fn)
+{
+ win_fn[MAX_PATH] = '\0';
+ ssize_t cwlen = cygwin_conv_path (CCP_WIN_A_TO_POSIX, win_fn, NULL, 0);
+ char *fn;
+ if (cwlen <= 0)
+ fn = strdup (win_fn);
+ else
+ {
+ char *fn_cyg = (char *) malloc (cwlen + SLOP + 1);
+ if (cygwin_conv_path (CCP_WIN_A_TO_POSIX, win_fn, fn_cyg, cwlen) == 0)
+ fn = fn_cyg;
+ else
+ {
+ free (fn_cyg);
+ fn = (char *) malloc (strlen (win_fn) + SLOP + 1);
+ strcpy (fn, win_fn);
+ }
+ }
+ return fn;
+}
+
static int
-print_dlls_and_kill_inferior (dlls *dll)
+print_dlls_and_kill_inferior (dlls *dll, const char *process_fn)
{
while ((dll = dll->next))
{
@@ -135,27 +158,13 @@ print_dlls_and_kill_inferior (dlls *dll)
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);
+ fn = tocyg (fnbuf);
+ printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
free (fn);
}
TerminateProcess (hProcess, 0);
+ if (process_fn)
+ return process_file (process_fn);
return 0;
}
@@ -165,7 +174,18 @@ 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))
+ if (!fn)
+ print_errno_error_and_return (in_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);
+
+ if (!fn || start_process (fn_win))
print_errno_error_and_return (in_fn);
DEBUG_EVENT ev;
@@ -174,6 +194,7 @@ report (const char *in_fn, bool multiple)
dlls dll_list = {};
dlls *dll_last = &dll_list;
+ const char *process_fn = NULL;
while (1)
{
if (WaitForDebugEvent (&ev, 1000))
@@ -198,7 +219,10 @@ report (const char *in_fn, bool multiple)
dll_last = dll_last->next;
break;
case EXCEPTION_DEBUG_EVENT:
- print_dlls_and_kill_inferior (&dll_list);
+ if (ev.u.Exception.ExceptionRecord.ExceptionCode == STATUS_DLL_NOT_FOUND)
+ process_fn = fn_win;
+ else
+ print_dlls_and_kill_inferior (&dll_list, process_fn);
break;
default:
break;
@@ -263,3 +287,278 @@ main (int argc, char **argv)
ret = 1;
exit (ret);
}
+
+static struct filelist
+{
+ struct filelist *next;
+ char *name;
+} *head;
+
+static bool printing = false;
+
+static bool
+saw_file (char *name)
+{
+
+ struct 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;
+}
+
+
+/* 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++)
+ {
+ char full_path[MAX_PATH];
+ char *dummy;
+ char *fn = (char *) adr (imp->Name);
+
+ if (saw_file (fn))
+ continue;
+
+ /* output DLL's name */
+ char *print_fn;
+ if (!SearchPath (NULL, fn, NULL, sizeof (full_path), 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 char *filename)
+{
+ HANDLE hFile, hMapping;
+ void *basepointer;
+ if ((hFile = CreateFile (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 %s\n", filename);
+ return 0;
+ }
+ if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
+ {
+ fprintf (stderr, "CreateFileMapping failed with windows error %lu\n", GetLastError ());
+ CloseHandle (hFile);
+ return 0;
+ }
+ if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0)))
+ {
+ fprintf (stderr, "MapViewOfFile failed with windows error %lu\n", 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 char *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]; /* this is an array of unknown length
+ actual number in file_head.NumberOfSections
+ if your compiler objects to it length 1 should work */
+ } *header;
+
+ /* revert to regular alignment */
+ #include <poppack.h>
+
+ head = NULL; /* FIXME: memory leak */
+ 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 = (struct 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;
+ }
+
+ /* ok, we've found the import directory... action! */
+ {
+ /* 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;
+}