1 /* Copyright (c) 2009, 2010, 2011, 2013 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 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
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.
35 #include <sys/cygwin.h>
36 #include <cygwin/version.h>
40 #define _WIN32_WINNT 0x0a00
45 struct option longopts
[] =
47 {"help", no_argument
, NULL
, 'h'},
48 {"verbose", no_argument
, NULL
, 'v'},
49 {"version", no_argument
, NULL
, 'V'},
50 {"data-relocs", no_argument
, NULL
, 'd'},
51 {"function-relocs", no_argument
, NULL
, 'r'},
52 {"unused", no_argument
, NULL
, 'u'},
53 {0, no_argument
, NULL
, 0}
55 const char *opts
= "dhruvV";
57 static int process_file (const wchar_t *);
60 error (const char *fmt
, ...)
64 fprintf (stderr
, "ldd: ");
65 vfprintf (stderr
, fmt
, ap
);
66 fprintf (stderr
, "\nTry `ldd --help' for more information.\n");
73 printf ("Usage: %s [OPTION]... FILE...\n\
75 Print shared library dependencies\n\
77 -h, --help print this help and exit\n\
78 -V, --version print version information and exit\n\
79 -r, --function-relocs process data and function relocations\n\
80 (currently unimplemented)\n\
81 -u, --unused print unused direct dependencies\n\
82 (currently unimplemented)\n\
83 -v, --verbose print all information\n\
84 (currently unimplemented)\n",
85 program_invocation_short_name
);
91 printf ("ldd (cygwin) %d.%d.%d\n"
92 "Print shared library dependencies\n"
93 "Copyright (C) 2009 - %s Chris Faylor\n"
94 "This is free software; see the source for copying conditions. There is NO\n"
95 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
96 CYGWIN_VERSION_DLL_MAJOR
/ 1000,
97 CYGWIN_VERSION_DLL_MAJOR
% 1000,
98 CYGWIN_VERSION_DLL_MINOR
,
99 strrchr (__DATE__
, ' ') + 1);
102 #define print_errno_error_and_return(__fn) \
104 fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
108 #define set_errno_and_return(x) \
110 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
115 static HANDLE hProcess
;
117 static struct filelist
119 struct filelist
*next
;
124 saw_file (char *name
)
128 for (p
= head
; p
; p
= p
->next
)
129 if (strcasecmp (name
, p
->name
) == 0)
132 p
= (filelist
*) malloc(sizeof (struct filelist
));
134 p
->name
= strdup (name
);
140 get_module_filename (HANDLE hp
, HMODULE hm
)
145 for (len
= 1024; (res
= GetModuleFileNameExW (hp
, hm
, (buf
= (wchar_t *) realloc (buf
, len
* sizeof (wchar_t))), len
)) == len
; len
+= 1024)
156 load_dll (const wchar_t *fn
)
158 wchar_t *buf
= get_module_filename (GetCurrentProcess (), NULL
);
161 printf ("ldd: GetModuleFileName returned an error %u\n",
162 (unsigned int) GetLastError ());
163 exit (1); /* FIXME */
166 wchar_t *newbuf
= (wchar_t *) malloc ((sizeof (L
"\"\" -- ") + wcslen (buf
) + wcslen (fn
)) * sizeof (wchar_t));
168 wcscpy (newbuf
+ 1, buf
);
169 wchar_t *p
= wcsstr (newbuf
, L
"\\ldd");
172 printf ("ldd: can't parse my own filename \"%ls\"\n", buf
);
176 wcscat (newbuf
, L
"\" -- ");
183 start_process (const wchar_t *fn
, bool& isdll
)
185 STARTUPINFOW si
= {};
186 PROCESS_INFORMATION pi
;
189 /* OCaml natdynlink plugins (.cmxs) cannot be handled by ldd because they
190 can only be loaded by flexdll_dlopen() */
191 if (wcslen (fn
) < 4 || (wcscasecmp (wcschr (fn
, L
'\0') - 4, L
".dll") != 0
192 && wcscasecmp (wcschr (fn
, L
'\0') - 4, L
".oct") != 0
193 && wcscasecmp (wcschr (fn
, L
'\0') - 3, L
".so") != 0))
203 if (CreateProcessW (NULL
, cmd
, NULL
, NULL
, FALSE
, DEBUG_ONLY_THIS_PROCESS
, NULL
, NULL
, &si
, &pi
))
206 hProcess
= pi
.hProcess
;
207 DebugSetProcessKillOnExit (true);
212 set_errno_and_return (1);
221 #define SLOP strlen (" (?)")
223 tocyg (wchar_t *win_fn
)
225 ssize_t cwlen
= cygwin_conv_path (CCP_WIN_W_TO_POSIX
, win_fn
, NULL
, 0);
229 int len
= wcstombs (NULL
, win_fn
, 0) + 1;
230 if ((fn
= (char *) malloc (len
)))
231 wcstombs (fn
, win_fn
, len
);
235 char *fn_cyg
= (char *) malloc (cwlen
+ SLOP
+ 1);
236 if (cygwin_conv_path (CCP_WIN_W_TO_POSIX
, win_fn
, fn_cyg
, cwlen
) == 0)
241 int len
= wcstombs (NULL
, win_fn
, 0);
242 fn
= (char *) malloc (len
+ SLOP
+ 1);
243 wcstombs (fn
, win_fn
, len
+ SLOP
+ 1);
249 #define CYGWIN_DLL_LEN (wcslen (L"\\cygwin1.dll"))
251 print_dlls (dlls
*dll
, const wchar_t *dllfn
, const wchar_t *process_fn
)
253 head
= NULL
; /* FIXME: memory leak */
254 while ((dll
= dll
->next
))
257 wchar_t *fullpath
= get_module_filename (hProcess
, (HMODULE
) dll
->lpBaseOfDll
);
260 else if (dllfn
&& wcscmp (fullpath
, dllfn
) == 0)
267 fn
= tocyg (fullpath
);
268 saw_file (basename (fn
));
271 printf ("\t%s => %s (%p)\n", basename (fn
), fn
, dll
->lpBaseOfDll
);
275 return process_file (process_fn
);
280 report (const char *in_fn
, bool multiple
)
283 printf ("%s:\n", in_fn
);
284 char *fn
= realpath (in_fn
, NULL
);
286 print_errno_error_and_return (in_fn
);
288 ssize_t len
= cygwin_conv_path (CCP_POSIX_TO_WIN_W
, fn
, NULL
, 0);
290 print_errno_error_and_return (fn
);
293 wchar_t fn_win
[len
+ 1];
294 if (cygwin_conv_path (CCP_POSIX_TO_WIN_W
, fn
, fn_win
, len
))
295 print_errno_error_and_return (fn
);
297 if (!fn
|| start_process (fn_win
, isdll
))
298 print_errno_error_and_return (in_fn
);
303 dlls
*dll_last
= &dll_list
;
304 const wchar_t *process_fn
= NULL
;
310 bool exitnow
= false;
311 DWORD cont
= DBG_CONTINUE
;
312 if (!WaitForDebugEvent (&ev
, INFINITE
))
314 switch (ev
.dwDebugEventCode
)
316 case CREATE_PROCESS_DEBUG_EVENT
:
319 PIMAGE_DOS_HEADER dos_header
= (PIMAGE_DOS_HEADER
) alloca (4096);
320 PIMAGE_NT_HEADERS nt_header
;
322 static const unsigned char int3
= 0xcc;
325 if (!ReadProcessMemory (hProcess
,
326 ev
.u
.CreateProcessInfo
.lpBaseOfImage
,
327 dos_header
, 4096, &bytes
))
328 print_errno_error_and_return (in_fn
);
330 nt_header
= PIMAGE_NT_HEADERS (PBYTE (dos_header
)
331 + dos_header
->e_lfanew
);
332 entry_point
= (PVOID
)
333 ((caddr_t
) ev
.u
.CreateProcessInfo
.lpBaseOfImage
334 + nt_header
->OptionalHeader
.AddressOfEntryPoint
);
336 if (!WriteProcessMemory (hProcess
, entry_point
, &int3
, 1, &bytes
))
337 print_errno_error_and_return (in_fn
);
340 case LOAD_DLL_DEBUG_EVENT
:
341 dll_last
->next
= (dlls
*) malloc (sizeof (dlls
));
342 dll_last
->next
->lpBaseOfDll
= ev
.u
.LoadDll
.lpBaseOfDll
;
343 dll_last
->next
->next
= NULL
;
344 dll_last
= dll_last
->next
;
346 case EXCEPTION_DEBUG_EVENT
:
347 switch (ev
.u
.Exception
.ExceptionRecord
.ExceptionCode
)
349 case STATUS_ENTRYPOINT_NOT_FOUND
:
350 /* A STATUS_ENTRYPOINT_NOT_FOUND might be encountered right after
351 loading all DLLs. We have to handle it here, otherwise ldd
352 runs into an endless loop. */
354 case STATUS_DLL_NOT_FOUND
:
357 case STATUS_BREAKPOINT
:
359 TerminateProcess (hProcess
, 0);
362 if (ev
.u
.Exception
.ExceptionRecord
.ExceptionFlags
&
363 EXCEPTION_NONCONTINUABLE
) {
368 case EXIT_PROCESS_DEBUG_EVENT
:
370 print_dlls (&dll_list
, isdll
? fn_win
: NULL
, process_fn
);
376 if (!ContinueDebugEvent (ev
.dwProcessId
, ev
.dwThreadId
, cont
))
378 cygwin_internal (CW_SETERRNO
, __FILE__
, __LINE__
- 2);
379 print_errno_error_and_return (in_fn
);
389 main (int argc
, char **argv
)
393 /* Use locale from environment. If not set or set to "C", use UTF-8. */
394 setlocale (LC_CTYPE
, "");
395 if (!strcmp (setlocale (LC_CTYPE
, NULL
), "C"))
396 setlocale (LC_CTYPE
, "en_US.UTF-8");
397 while ((optch
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != -1)
403 error ("option not implemented `-%c'", optch
);
412 fprintf (stderr
, "Try `%s --help' for more information.\n",
413 program_invocation_short_name
);
418 error ("missing file arguments");
421 bool multiple
= !!argv
[1];
423 while ((fn
= *argv
++))
424 if (report (fn
, multiple
))
429 static bool printing
= false;
432 /* dump of import directory
433 section begins at pointer 'section base'
434 section RVA is 'section_rva'
435 import directory begins at pointer 'imp' */
437 dump_import_directory (const void *const section_base
,
438 const DWORD section_rva
,
439 const IMAGE_IMPORT_DESCRIPTOR
*imp
)
441 /* get memory address given the RVA */
442 #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
444 /* continue until address inaccessible or there's no DLL name */
445 for (; !IsBadReadPtr (imp
, sizeof (*imp
)) && imp
->Name
; imp
++)
447 wchar_t full_path
[PATH_MAX
];
449 char *fn
= (char *) adr (imp
->Name
);
454 int len
= mbstowcs (NULL
, fn
, 0);
457 wchar_t fnw
[len
+ 1];
458 mbstowcs (fnw
, fn
, len
+ 1);
459 /* output DLL's name */
461 if (!SearchPathW (NULL
, fnw
, NULL
, PATH_MAX
, full_path
, &dummy
))
463 print_fn
= strdup ("not found");
470 print_fn
= tocyg (full_path
);
471 strcat (print_fn
, " (?)");
474 printf ("\t%s => %s\n", (char *) fn
, print_fn
);
482 /* load a file in RAM (memory-mapped)
483 return pointer to loaded file
486 map_file (const wchar_t *filename
)
488 HANDLE hFile
, hMapping
;
490 if ((hFile
= CreateFileW (filename
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
491 0, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, 0)) == INVALID_HANDLE_VALUE
)
493 fprintf (stderr
, "couldn't open %ls\n", filename
);
496 if (!(hMapping
= CreateFileMapping (hFile
, 0, PAGE_READONLY
| SEC_COMMIT
, 0, 0, 0)))
498 fprintf (stderr
, "CreateFileMapping failed with windows error %u\n",
499 (unsigned int) GetLastError ());
503 if (!(basepointer
= MapViewOfFile (hMapping
, FILE_MAP_READ
, 0, 0, 0)))
505 fprintf (stderr
, "MapViewOfFile failed with windows error %u\n",
506 (unsigned int) GetLastError ());
507 CloseHandle (hMapping
);
512 CloseHandle (hMapping
);
519 /* this will return a pointer immediatly behind the DOS-header
522 skip_dos_stub (const IMAGE_DOS_HEADER
*dos_ptr
)
524 /* look there's enough space for a DOS-header */
525 if (IsBadReadPtr (dos_ptr
, sizeof (*dos_ptr
)))
527 fprintf (stderr
, "not enough space for DOS-header\n");
532 if (dos_ptr
->e_magic
!= IMAGE_DOS_SIGNATURE
)
534 fprintf (stderr
, "not a DOS-stub\n");
538 /* ok, then, go get it */
539 return (char*) dos_ptr
+ dos_ptr
->e_lfanew
;
543 /* find the directory's section index given the RVA
544 Returns -1 if impossible */
546 get_directory_index (const unsigned dir_rva
,
547 const unsigned dir_length
,
548 const int number_of_sections
,
549 const IMAGE_SECTION_HEADER
*sections
)
552 for (sect
= 0; sect
< number_of_sections
; sect
++)
554 /* compare directory RVA to section RVA */
555 if (sections
[sect
].VirtualAddress
<= dir_rva
556 && dir_rva
< sections
[sect
].VirtualAddress
+sections
[sect
].SizeOfRawData
)
563 /* dump imports of a single file
564 Returns 0 if successful, !=0 else */
566 process_file (const wchar_t *filename
)
568 void *basepointer
; /* Points to loaded PE file
569 * This is memory mapped stuff
571 int number_of_sections
;
572 DWORD import_rva
; /* RVA of import directory */
573 DWORD import_length
; /* length of import directory */
574 int import_index
; /* index of section with import directory */
576 /* ensure byte-alignment for struct tag_header */
577 #include <pshpack1.h>
579 const struct tag_header
582 IMAGE_FILE_HEADER file_head
;
583 IMAGE_OPTIONAL_HEADER opt_head
;
584 IMAGE_SECTION_HEADER section_header
[1]; /* an array of unknown length */
587 /* revert to regular alignment */
592 /* first, load file */
593 basepointer
= map_file (filename
);
596 puts ("cannot load file");
600 /* get header pointer; validate a little bit */
601 header
= (tag_header
*) skip_dos_stub ((IMAGE_DOS_HEADER
*) basepointer
);
604 puts ("cannot skip DOS stub");
605 UnmapViewOfFile (basepointer
);
609 /* look there's enough space for PE headers */
610 if (IsBadReadPtr (header
, sizeof (*header
)))
612 puts ("not enough space for PE headers");
613 UnmapViewOfFile (basepointer
);
617 /* validate PE signature */
618 if (header
->signature
!= IMAGE_NT_SIGNATURE
)
620 puts ("not a PE file");
621 UnmapViewOfFile (basepointer
);
625 /* get number of sections */
626 number_of_sections
= header
->file_head
.NumberOfSections
;
628 /* check there are sections... */
629 if (number_of_sections
< 1)
631 UnmapViewOfFile (basepointer
);
635 /* validate there's enough space for section headers */
636 if (IsBadReadPtr (header
->section_header
, number_of_sections
*sizeof (IMAGE_SECTION_HEADER
)))
638 puts ("not enough space for section headers");
639 UnmapViewOfFile (basepointer
);
643 /* get RVA and length of import directory */
644 import_rva
= header
->opt_head
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
;
645 import_length
= header
->opt_head
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
;
647 /* check there's stuff to care about */
648 if (!import_rva
|| !import_length
)
650 UnmapViewOfFile (basepointer
);
651 return 0; /* success! */
654 /* get import directory pointer */
655 import_index
= get_directory_index (import_rva
,import_length
,number_of_sections
,header
->section_header
);
657 /* check directory was found */
658 if (import_index
< 0)
660 puts ("couldn't find import directory in sections");
661 UnmapViewOfFile (basepointer
);
665 /* The pointer to the start of the import directory's section */
666 const void *section_address
= (char*) basepointer
+ header
->section_header
[import_index
].PointerToRawData
;
667 if (dump_import_directory (section_address
,
668 header
->section_header
[import_index
].VirtualAddress
,
669 /* the last parameter is the pointer to the import directory:
670 section address + (import RVA - section RVA)
671 The difference is the offset of the import directory in the section */
672 (const IMAGE_IMPORT_DESCRIPTOR
*) ((char *) section_address
+import_rva
-header
->section_header
[import_index
].VirtualAddress
)))
674 UnmapViewOfFile (basepointer
);
678 UnmapViewOfFile (basepointer
);