* 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>
|
||||
|
||||
* utils.sgml: Fix typo.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue