1 /* Copyright (c) 2009, Chris Faylor
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 * Neither the name of the owner nor the names of its
11 contributors may be used to endorse or promote products derived from
12 this software without specific prior written permission.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
15 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cygwin.h>
37 #define _WIN32_WINNT 0x0501
42 #ifndef STATUS_DLL_NOT_FOUND
43 #define STATUS_DLL_NOT_FOUND (0xC0000135L)
48 struct option longopts
[] =
50 {"help", no_argument
, NULL
, 0},
51 {"version", no_argument
, NULL
, 0},
52 {"data-relocs", no_argument
, NULL
, 'd'},
53 {"dll", no_argument
, NULL
, 'D'},
54 {"function-relocs", no_argument
, NULL
, 'r'},
55 {"unused", no_argument
, NULL
, 'u'},
56 {0, no_argument
, NULL
, 0}
59 static int process_file (const char *);
62 usage (const char *fmt
, ...)
66 fprintf (stderr
, "ldd: ");
67 vfprintf (stderr
, fmt
, ap
);
68 fprintf (stderr
, "\nTry `ldd --help' for more information.\n");
72 #define print_errno_error_and_return(__fn) \
74 fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
78 #define set_errno_and_return(x) \
80 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
85 static HANDLE hProcess
;
88 get_module_filename (HANDLE hp
, HMODULE hm
)
93 for (len
= 1024; (res
= GetModuleFileNameEx (hp
, hm
, (buf
= (char *) realloc (buf
, len
)), len
)) == len
; len
+= 1024)
104 load_dll (const char *fn
)
106 char *buf
= get_module_filename (GetCurrentProcess (), NULL
);
109 printf ("ldd: GetModuleFileName returned an error %lu\n", GetLastError ());
110 exit (1); /* FIXME */
112 buf
= (char *) realloc (buf
, sizeof (" \"--dll\" \"\"") + strlen (buf
));
113 strcat (buf
, " --dll \"");
121 start_process (const char *fn
, bool& isdll
)
124 PROCESS_INFORMATION pi
;
127 if (strlen (fn
) < 4 || strcasecmp (strchr (fn
, '\0') - 4, ".dll") != 0)
137 if (CreateProcess (NULL
, cmd
, NULL
, NULL
, FALSE
, DEBUG_PROCESS
, NULL
, NULL
, &si
, &pi
))
140 hProcess
= pi
.hProcess
;
141 DebugSetProcessKillOnExit (true);
146 set_errno_and_return (1);
154 if (!EnumProcessModules (hProcess
, &hm
, sizeof (hm
), &cb
) || !cb
)
155 set_errno_and_return (1);
158 if (!GetModuleInformation (hProcess
, hm
, &mi
, sizeof (mi
)) || !mi
.EntryPoint
)
159 set_errno_and_return (1);
161 static const unsigned char int3
= 0xcc;
162 if (!WriteProcessMemory (hProcess
, mi
.EntryPoint
, &int3
, 1, &cb
) || cb
!= 1)
163 set_errno_and_return (1);
173 #define SLOP strlen (" (?)")
177 win_fn
[MAX_PATH
] = '\0';
178 ssize_t cwlen
= cygwin_conv_path (CCP_WIN_A_TO_POSIX
, win_fn
, NULL
, 0);
181 fn
= strdup (win_fn
);
184 char *fn_cyg
= (char *) malloc (cwlen
+ SLOP
+ 1);
185 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX
, win_fn
, fn_cyg
, cwlen
) == 0)
190 fn
= (char *) malloc (strlen (win_fn
) + SLOP
+ 1);
197 #define CYGWIN_DLL_LEN (strlen ("\\cygwin1.dll"))
199 print_dlls_and_kill_inferior (dlls
*dll
, const char *dllfn
, const char *process_fn
)
201 bool printit
= !dllfn
;
202 while ((dll
= dll
->next
))
205 char *fullpath
= get_module_filename (hProcess
, (HMODULE
) dll
->lpBaseOfDll
);
211 fn
= tocyg (fullpath
);
212 else if (strcasecmp (fullpath
, dllfn
) != 0)
222 printf ("\t%s => %s (%p)\n", basename (fn
), fn
, dll
->lpBaseOfDll
);
225 TerminateProcess (hProcess
, 0);
227 return process_file (process_fn
);
232 report (const char *in_fn
, bool multiple
)
235 printf ("%s:\n", in_fn
);
236 char *fn
= realpath (in_fn
, NULL
);
238 print_errno_error_and_return (in_fn
);
240 ssize_t len
= cygwin_conv_path (CCP_POSIX_TO_WIN_A
, fn
, NULL
, 0);
242 print_errno_error_and_return (fn
);
245 char fn_win
[len
+ 1];
246 if (cygwin_conv_path (CCP_POSIX_TO_WIN_A
, fn
, fn_win
, len
))
247 print_errno_error_and_return (fn
);
249 if (!fn
|| start_process (fn_win
, isdll
))
250 print_errno_error_and_return (in_fn
);
254 unsigned dll_count
= 0;
257 dlls
*dll_last
= &dll_list
;
258 const char *process_fn
= NULL
;
261 if (WaitForDebugEvent (&ev
, 1000))
264 switch (GetLastError ())
272 switch (ev
.dwDebugEventCode
)
274 case LOAD_DLL_DEBUG_EVENT
:
275 if (!isdll
&& ++dll_count
== 2)
277 dll_last
->next
= (dlls
*) malloc (sizeof (dlls
));
278 dll_last
->next
->lpBaseOfDll
= ev
.u
.LoadDll
.lpBaseOfDll
;
279 dll_last
->next
->next
= NULL
;
280 dll_last
= dll_last
->next
;
282 case EXCEPTION_DEBUG_EVENT
:
283 switch (ev
.u
.Exception
.ExceptionRecord
.ExceptionCode
)
285 case STATUS_DLL_NOT_FOUND
:
288 case STATUS_BREAKPOINT
:
290 print_dlls_and_kill_inferior (&dll_list
, isdll
? fn_win
: NULL
, process_fn
);
294 case EXIT_PROCESS_DEBUG_EVENT
:
295 print_dlls_and_kill_inferior (&dll_list
, isdll
? fn_win
: NULL
, process_fn
);
300 if (!ContinueDebugEvent (ev
.dwProcessId
, ev
.dwThreadId
, DBG_CONTINUE
))
302 cygwin_internal (CW_SETERRNO
, __FILE__
, __LINE__
- 2);
303 print_errno_error_and_return (in_fn
);
313 main (int argc
, char **argv
)
317 while ((optch
= getopt_long (argc
, argv
, "dru", longopts
, &index
)) != -1)
323 usage ("option not implemented `-%c'", optch
);
326 if (!LoadLibrary (argv
[optind
]))
332 printf ("ldd (Cygwin) %s\nCopyright (C) 2009 Chris Faylor\n"
333 "This is free software; see the source for copying conditions. There is NO\n"
334 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
340 puts ("Usage: ldd [OPTION]... FILE...\n\
341 --help print this help and exit\n\
342 --version print version information and exit\n\
343 -r, --function-relocs process data and function relocations\n\
344 (currently unimplemented)\n\
345 -u, --unused print unused direct dependencies\n\
346 (currently unimplemented)\n\
347 -v, --verbose print all information\n\
348 (currently unimplemented)");
354 usage("missing file arguments");
357 bool multiple
= !!argv
[1];
359 while ((fn
= *argv
++))
360 if (report (fn
, multiple
))
365 static struct filelist
367 struct filelist
*next
;
371 static bool printing
= false;
374 saw_file (char *name
)
379 for (p
=head
; p
; p
= p
->next
)
380 if (strcasecmp (name
, p
->name
) == 0)
383 p
= (filelist
*) malloc(sizeof (struct filelist
));
385 p
->name
= strdup (name
);
391 /* dump of import directory
392 section begins at pointer 'section base'
393 section RVA is 'section_rva'
394 import directory begins at pointer 'imp' */
396 dump_import_directory (const void *const section_base
,
397 const DWORD section_rva
,
398 const IMAGE_IMPORT_DESCRIPTOR
*imp
)
400 /* get memory address given the RVA */
401 #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
403 /* continue until address inaccessible or there's no DLL name */
404 for (; !IsBadReadPtr (imp
, sizeof (*imp
)) && imp
->Name
; imp
++)
406 char full_path
[MAX_PATH
];
408 char *fn
= (char *) adr (imp
->Name
);
413 /* output DLL's name */
415 if (!SearchPath (NULL
, fn
, NULL
, sizeof (full_path
), full_path
, &dummy
))
417 print_fn
= strdup ("not found");
424 print_fn
= tocyg (full_path
);
425 strcat (print_fn
, " (?)");
428 printf ("\t%s => %s\n", (char *) fn
, print_fn
);
436 /* load a file in RAM (memory-mapped)
437 return pointer to loaded file
440 map_file (const char *filename
)
442 HANDLE hFile
, hMapping
;
444 if ((hFile
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
445 0, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, 0)) == INVALID_HANDLE_VALUE
)
447 fprintf (stderr
, "couldn't open %s\n", filename
);
450 if (!(hMapping
= CreateFileMapping (hFile
, 0, PAGE_READONLY
| SEC_COMMIT
, 0, 0, 0)))
452 fprintf (stderr
, "CreateFileMapping failed with windows error %lu\n", GetLastError ());
456 if (!(basepointer
= MapViewOfFile (hMapping
, FILE_MAP_READ
, 0, 0, 0)))
458 fprintf (stderr
, "MapViewOfFile failed with windows error %lu\n", GetLastError ());
459 CloseHandle (hMapping
);
464 CloseHandle (hMapping
);
471 /* this will return a pointer immediatly behind the DOS-header
474 skip_dos_stub (const IMAGE_DOS_HEADER
*dos_ptr
)
476 /* look there's enough space for a DOS-header */
477 if (IsBadReadPtr (dos_ptr
, sizeof (*dos_ptr
)))
479 fprintf (stderr
, "not enough space for DOS-header\n");
484 if (dos_ptr
->e_magic
!= IMAGE_DOS_SIGNATURE
)
486 fprintf (stderr
, "not a DOS-stub\n");
490 /* ok, then, go get it */
491 return (char*) dos_ptr
+ dos_ptr
->e_lfanew
;
495 /* find the directory's section index given the RVA
496 Returns -1 if impossible */
498 get_directory_index (const unsigned dir_rva
,
499 const unsigned dir_length
,
500 const int number_of_sections
,
501 const IMAGE_SECTION_HEADER
*sections
)
504 for (sect
= 0; sect
< number_of_sections
; sect
++)
506 /* compare directory RVA to section RVA */
507 if (sections
[sect
].VirtualAddress
<= dir_rva
508 && dir_rva
< sections
[sect
].VirtualAddress
+sections
[sect
].SizeOfRawData
)
515 /* dump imports of a single file
516 Returns 0 if successful, !=0 else */
518 process_file (const char *filename
)
520 void *basepointer
; /* Points to loaded PE file
521 * This is memory mapped stuff
523 int number_of_sections
;
524 DWORD import_rva
; /* RVA of import directory */
525 DWORD import_length
; /* length of import directory */
526 int import_index
; /* index of section with import directory */
528 /* ensure byte-alignment for struct tag_header */
529 #include <pshpack1.h>
531 const struct tag_header
534 IMAGE_FILE_HEADER file_head
;
535 IMAGE_OPTIONAL_HEADER opt_head
;
536 IMAGE_SECTION_HEADER section_header
[1]; /* this is an array of unknown length
537 actual number in file_head.NumberOfSections
538 if your compiler objects to it length 1 should work */
541 /* revert to regular alignment */
544 head
= NULL
; /* FIXME: memory leak */
547 /* first, load file */
548 basepointer
= map_file (filename
);
551 puts ("cannot load file");
555 /* get header pointer; validate a little bit */
556 header
= (struct tag_header
*) skip_dos_stub ((IMAGE_DOS_HEADER
*) basepointer
);
559 puts ("cannot skip DOS stub");
560 UnmapViewOfFile (basepointer
);
564 /* look there's enough space for PE headers */
565 if (IsBadReadPtr (header
, sizeof (*header
)))
567 puts ("not enough space for PE headers");
568 UnmapViewOfFile (basepointer
);
572 /* validate PE signature */
573 if (header
->signature
!=IMAGE_NT_SIGNATURE
)
575 puts ("not a PE file");
576 UnmapViewOfFile (basepointer
);
580 /* get number of sections */
581 number_of_sections
= header
->file_head
.NumberOfSections
;
583 /* check there are sections... */
584 if (number_of_sections
<1)
586 UnmapViewOfFile (basepointer
);
590 /* validate there's enough space for section headers */
591 if (IsBadReadPtr (header
->section_header
, number_of_sections
*sizeof (IMAGE_SECTION_HEADER
)))
593 puts ("not enough space for section headers");
594 UnmapViewOfFile (basepointer
);
598 /* get RVA and length of import directory */
599 import_rva
= header
->opt_head
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
;
600 import_length
= header
->opt_head
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
;
602 /* check there's stuff to care about */
603 if (!import_rva
|| !import_length
)
605 UnmapViewOfFile (basepointer
);
606 return 0; /* success! */
609 /* get import directory pointer */
610 import_index
= get_directory_index (import_rva
,import_length
,number_of_sections
,header
->section_header
);
612 /* check directory was found */
615 puts ("couldn't find import directory in sections");
616 UnmapViewOfFile (basepointer
);
620 /* ok, we've found the import directory... action! */
622 /* The pointer to the start of the import directory's section */
623 const void *section_address
= (char*) basepointer
+ header
->section_header
[import_index
].PointerToRawData
;
624 if (dump_import_directory (section_address
,
625 header
->section_header
[import_index
].VirtualAddress
,
626 /* the last parameter is the pointer to the import directory:
627 section address + (import RVA - section RVA)
628 The difference is the offset of the import directory in the section */
629 (const IMAGE_IMPORT_DESCRIPTOR
*) ((char *) section_address
+import_rva
-header
->section_header
[import_index
].VirtualAddress
)))
631 UnmapViewOfFile (basepointer
);
636 UnmapViewOfFile (basepointer
);