* 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.
This commit is contained in:
Christopher Faylor 2009-03-14 06:46:00 +00:00
parent c298c9bdbf
commit 962acfe58a
2 changed files with 347 additions and 30 deletions

View File

@ -1,3 +1,21 @@
2009-03-14 Christopher Faylor <me+cygwin@cgf.cx>
* 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.
2009-03-09 Corinna Vinschen <corinna@vinschen.de>
* utils.sgml: Fix typo.

View File

@ -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;
}