* 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:
parent
c298c9bdbf
commit
962acfe58a
|
@ -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>
|
2009-03-09 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* utils.sgml: Fix typo.
|
* utils.sgml: Fix typo.
|
||||||
|
|
|
@ -32,12 +32,17 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/cygwin.h>
|
#include <sys/cygwin.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#define _WIN32_WINNT 0x0501
|
#define _WIN32_WINNT 0x0501
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <imagehlp.h>
|
#include <imagehlp.h>
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
|
|
||||||
|
#ifndef STATUS_DLL_NOT_FOUND
|
||||||
|
#define STATUS_DLL_NOT_FOUND (0xC0000135L)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define VERSION "1.0"
|
#define VERSION "1.0"
|
||||||
|
|
||||||
struct option longopts[] =
|
struct option longopts[] =
|
||||||
|
@ -50,6 +55,8 @@ struct option longopts[] =
|
||||||
{0, no_argument, NULL, 0}
|
{0, no_argument, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int process_file (const char *);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usage (const char *fmt, ...)
|
usage (const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -79,18 +86,10 @@ static HANDLE hProcess;
|
||||||
static int
|
static int
|
||||||
start_process (const char *fn)
|
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 = {};
|
STARTUPINFO si = {};
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
si.cb = sizeof (si);
|
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;
|
hProcess = pi.hProcess;
|
||||||
DebugSetProcessKillOnExit (true);
|
DebugSetProcessKillOnExit (true);
|
||||||
|
@ -124,8 +123,32 @@ struct dlls
|
||||||
struct dlls *next;
|
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
|
static int
|
||||||
print_dlls_and_kill_inferior (dlls *dll)
|
print_dlls_and_kill_inferior (dlls *dll, const char *process_fn)
|
||||||
{
|
{
|
||||||
while ((dll = dll->next))
|
while ((dll = dll->next))
|
||||||
{
|
{
|
||||||
|
@ -135,27 +158,13 @@ print_dlls_and_kill_inferior (dlls *dll)
|
||||||
if (!len)
|
if (!len)
|
||||||
fn = strdup ("???");
|
fn = strdup ("???");
|
||||||
else
|
else
|
||||||
{
|
fn = tocyg (fnbuf);
|
||||||
fnbuf[MAX_PATH] = '\0';
|
printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
|
||||||
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);
|
free (fn);
|
||||||
}
|
}
|
||||||
TerminateProcess (hProcess, 0);
|
TerminateProcess (hProcess, 0);
|
||||||
|
if (process_fn)
|
||||||
|
return process_file (process_fn);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +174,18 @@ report (const char *in_fn, bool multiple)
|
||||||
if (multiple)
|
if (multiple)
|
||||||
printf ("%s:\n", in_fn);
|
printf ("%s:\n", in_fn);
|
||||||
char *fn = realpath (in_fn, NULL);
|
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);
|
print_errno_error_and_return (in_fn);
|
||||||
|
|
||||||
DEBUG_EVENT ev;
|
DEBUG_EVENT ev;
|
||||||
|
@ -174,6 +194,7 @@ report (const char *in_fn, bool multiple)
|
||||||
|
|
||||||
dlls dll_list = {};
|
dlls dll_list = {};
|
||||||
dlls *dll_last = &dll_list;
|
dlls *dll_last = &dll_list;
|
||||||
|
const char *process_fn = NULL;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (WaitForDebugEvent (&ev, 1000))
|
if (WaitForDebugEvent (&ev, 1000))
|
||||||
|
@ -198,7 +219,10 @@ report (const char *in_fn, bool multiple)
|
||||||
dll_last = dll_last->next;
|
dll_last = dll_last->next;
|
||||||
break;
|
break;
|
||||||
case EXCEPTION_DEBUG_EVENT:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -263,3 +287,278 @@ main (int argc, char **argv)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
exit (ret);
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue