]> sourceware.org Git - newlib-cygwin.git/blob - winsup/utils/ldd.cc
183e02cd4ae070df4631b016da1546a77a5279e2
[newlib-cygwin.git] / winsup / utils / ldd.cc
1 /* Copyright (c) 2009, 2010, 2011, 2013 Chris Faylor
2
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
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.
13
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.
25 */
26
27 #include <errno.h>
28 #include <getopt.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <wchar.h>
34 #include <locale.h>
35 #include <sys/cygwin.h>
36 #include <cygwin/version.h>
37 #include <unistd.h>
38 #include <libgen.h>
39
40 #define _WIN32_WINNT 0x0a00
41 #include <windows.h>
42 #include <winternl.h>
43 #include <imagehlp.h>
44 #include <psapi.h>
45
46 struct option longopts[] =
47 {
48 {"help", no_argument, NULL, 'h'},
49 {"verbose", no_argument, NULL, 'v'},
50 {"version", no_argument, NULL, 'V'},
51 {"data-relocs", no_argument, NULL, 'd'},
52 {"function-relocs", no_argument, NULL, 'r'},
53 {"unused", no_argument, NULL, 'u'},
54 {0, no_argument, NULL, 0}
55 };
56 const char *opts = "dhruvV";
57
58 static int process_file (const wchar_t *);
59
60 static int
61 error (const char *fmt, ...)
62 {
63 va_list ap;
64 va_start (ap, fmt);
65 fprintf (stderr, "ldd: ");
66 vfprintf (stderr, fmt, ap);
67 fprintf (stderr, "\nTry `ldd --help' for more information.\n");
68 exit (1);
69 }
70
71 static void __attribute__ ((__noreturn__))
72 usage ()
73 {
74 printf ("Usage: %s [OPTION]... FILE...\n\
75 \n\
76 Print shared library dependencies\n\
77 \n\
78 -h, --help print this help and exit\n\
79 -V, --version print version information and exit\n\
80 -r, --function-relocs process data and function relocations\n\
81 (currently unimplemented)\n\
82 -u, --unused print unused direct dependencies\n\
83 (currently unimplemented)\n\
84 -v, --verbose print all information\n\
85 (currently unimplemented)\n",
86 program_invocation_short_name);
87 exit (0);
88 }
89
90 static void
91 print_version ()
92 {
93 printf ("ldd (cygwin) %d.%d.%d\n"
94 "Print shared library dependencies\n"
95 "Copyright (C) 2009 - %s Chris Faylor\n"
96 "This is free software; see the source for copying conditions. There is NO\n"
97 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
98 CYGWIN_VERSION_DLL_MAJOR / 1000,
99 CYGWIN_VERSION_DLL_MAJOR % 1000,
100 CYGWIN_VERSION_DLL_MINOR,
101 strrchr (__DATE__, ' ') + 1);
102 }
103
104 #define print_errno_error_and_return(__fn) \
105 do {\
106 fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
107 return 1;\
108 } while (0)
109
110 #define set_errno_and_return(x) \
111 do {\
112 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
113 return (x);\
114 } while (0)
115
116
117 static HANDLE hProcess;
118
119 static struct filelist
120 {
121 struct filelist *next;
122 char *name;
123 } *head;
124
125 static bool
126 saw_file (char *name)
127 {
128 filelist *p;
129
130 for (p = head; p; p = p->next)
131 if (strcasecmp (name, p->name) == 0)
132 return true;
133
134 p = (filelist *) malloc(sizeof (struct filelist));
135 p->next = head;
136 p->name = strdup (name);
137 head = p;
138 return false;
139 }
140
141 static wchar_t *
142 get_module_filename (HANDLE hp, HMODULE hm)
143 {
144 size_t len;
145 wchar_t *buf = NULL;
146 DWORD res;
147 for (len = 1024; (res = GetModuleFileNameExW (hp, hm, (buf = (wchar_t *) realloc (buf, len * sizeof (wchar_t))), len)) == len; len += 1024)
148 continue;
149 if (!res)
150 {
151 free (buf);
152 buf = NULL;
153 }
154 return buf;
155 }
156
157 static wchar_t *
158 load_dll (const wchar_t *fn)
159 {
160 wchar_t *buf = get_module_filename (GetCurrentProcess (), NULL);
161 if (!buf)
162 {
163 printf ("ldd: GetModuleFileName returned an error %u\n",
164 (unsigned int) GetLastError ());
165 exit (1); /* FIXME */
166 }
167
168 wchar_t *newbuf = (wchar_t *) malloc ((sizeof (L"\"\" -- ") + wcslen (buf) + wcslen (fn)) * sizeof (wchar_t));
169 newbuf[0] = L'"';
170 wcscpy (newbuf + 1, buf);
171 wchar_t *p = wcsstr (newbuf, L"\\ldd");
172 if (!p)
173 {
174 printf ("ldd: can't parse my own filename \"%ls\"\n", buf);
175 exit (1);
176 }
177 p[3] = L'h';
178 wcscat (newbuf, L"\" -- ");
179 wcscat (newbuf, fn);
180 free (buf);
181 return newbuf;
182 }
183
184 static int
185 start_process (const wchar_t *fn, bool& isdll)
186 {
187 STARTUPINFOW si = {};
188 PROCESS_INFORMATION pi;
189 si.cb = sizeof (si);
190 wchar_t *cmd;
191 /* OCaml natdynlink plugins (.cmxs) cannot be handled by ldd because they
192 can only be loaded by flexdll_dlopen() */
193 if (wcslen (fn) < 4 || (wcscasecmp (wcschr (fn, L'\0') - 4, L".dll") != 0
194 && wcscasecmp (wcschr (fn, L'\0') - 4, L".oct") != 0
195 && wcscasecmp (wcschr (fn, L'\0') - 3, L".so") != 0))
196 {
197 cmd = wcsdup (fn);
198 isdll = false;
199 }
200 else
201 {
202 cmd = load_dll (fn);
203 isdll = true;
204 }
205 if (CreateProcessW (NULL, cmd, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi))
206 {
207 free (cmd);
208 hProcess = pi.hProcess;
209 DebugSetProcessKillOnExit (true);
210 return 0;
211 }
212
213 free (cmd);
214 set_errno_and_return (1);
215 }
216
217 struct dlls
218 {
219 LPVOID lpBaseOfDll;
220 HANDLE hFile;
221 struct dlls *next;
222 };
223
224 #define SLOP strlen (" (?)")
225 char *
226 tocyg (wchar_t *win_fn)
227 {
228 ssize_t cwlen = cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, NULL, 0);
229 char *fn;
230 if (cwlen <= 0)
231 {
232 int len = wcstombs (NULL, win_fn, 0) + 1;
233 if ((fn = (char *) malloc (len)))
234 wcstombs (fn, win_fn, len);
235 }
236 else
237 {
238 char *fn_cyg = (char *) malloc (cwlen + SLOP + 1);
239 if (cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, fn_cyg, cwlen) == 0)
240 fn = fn_cyg;
241 else
242 {
243 free (fn_cyg);
244 int len = wcstombs (NULL, win_fn, 0);
245 fn = (char *) malloc (len + SLOP + 1);
246 wcstombs (fn, win_fn, len + SLOP + 1);
247 }
248 }
249 return fn;
250 }
251
252 #define CYGWIN_DLL_LEN (wcslen (L"\\cygwin1.dll"))
253 static int
254 print_dlls (dlls *dll, const wchar_t *dllfn, const wchar_t *process_fn)
255 {
256 head = NULL; /* FIXME: memory leak */
257 while ((dll = dll->next))
258 {
259 char *fn;
260 wchar_t *fullpath = get_module_filename (hProcess, (HMODULE) dll->lpBaseOfDll);
261 if (!fullpath)
262 {
263 // if no path found yet, try getting it from an open handle to the DLL
264 wchar_t dllname[PATH_MAX];
265 if (GetFinalPathNameByHandleW (dll->hFile, dllname, PATH_MAX, 0))
266 {
267 fn = tocyg (dllname);
268 saw_file (basename (fn));
269 }
270 else
271 fn = strdup ("???");
272 }
273 else if (dllfn && wcscmp (fullpath, dllfn) == 0)
274 {
275 free (fullpath);
276 continue;
277 }
278 else
279 {
280 fn = tocyg (fullpath);
281 saw_file (basename (fn));
282 free (fullpath);
283 }
284 printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
285 free (fn);
286 }
287 if (process_fn)
288 return process_file (process_fn);
289 return 0;
290 }
291
292 static int
293 report (const char *in_fn, bool multiple)
294 {
295 if (multiple)
296 printf ("%s:\n", in_fn);
297 char *fn = realpath (in_fn, NULL);
298 if (!fn)
299 print_errno_error_and_return (in_fn);
300
301 ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, NULL, 0);
302 if (len <= 0)
303 print_errno_error_and_return (fn);
304
305 bool isdll;
306 wchar_t fn_win[len + 1];
307 if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, fn_win, len))
308 print_errno_error_and_return (fn);
309
310 if (!fn || start_process (fn_win, isdll))
311 print_errno_error_and_return (in_fn);
312
313 DEBUG_EVENT ev;
314
315 dlls dll_list = {};
316 dlls *dll_last = &dll_list;
317 const wchar_t *process_fn = NULL;
318
319 int res = 0;
320
321 while (1)
322 {
323 bool exitnow = false;
324 DWORD cont = DBG_CONTINUE;
325 if (!WaitForDebugEvent (&ev, INFINITE))
326 break;
327 switch (ev.dwDebugEventCode)
328 {
329 case CREATE_PROCESS_DEBUG_EVENT:
330 if (!isdll)
331 {
332 PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER) alloca (4096);
333 PIMAGE_NT_HEADERS nt_header;
334 PVOID entry_point;
335 static const unsigned char int3 = 0xcc;
336 SIZE_T bytes;
337
338 if (!ReadProcessMemory (hProcess,
339 ev.u.CreateProcessInfo.lpBaseOfImage,
340 dos_header, 4096, &bytes))
341 print_errno_error_and_return (in_fn);
342
343 nt_header = PIMAGE_NT_HEADERS (PBYTE (dos_header)
344 + dos_header->e_lfanew);
345 entry_point = (PVOID)
346 ((caddr_t) ev.u.CreateProcessInfo.lpBaseOfImage
347 + nt_header->OptionalHeader.AddressOfEntryPoint);
348
349 if (!WriteProcessMemory (hProcess, entry_point, &int3, 1, &bytes))
350 print_errno_error_and_return (in_fn);
351 }
352 break;
353 case LOAD_DLL_DEBUG_EVENT:
354 dll_last->next = (dlls *) malloc (sizeof (dlls));
355 dll_last->next->lpBaseOfDll = ev.u.LoadDll.lpBaseOfDll;
356 dll_last->next->hFile = ev.u.LoadDll.hFile;
357 dll_last->next->next = NULL;
358 dll_last = dll_last->next;
359 break;
360 case EXCEPTION_DEBUG_EVENT:
361 switch (ev.u.Exception.ExceptionRecord.ExceptionCode)
362 {
363 case STATUS_ENTRYPOINT_NOT_FOUND:
364 /* A STATUS_ENTRYPOINT_NOT_FOUND might be encountered right after
365 loading all DLLs. We have to handle it here, otherwise ldd
366 runs into an endless loop. */
367 goto print_and_exit;
368 case STATUS_DLL_NOT_FOUND:
369 process_fn = fn_win;
370 break;
371 case STATUS_BREAKPOINT:
372 if (!isdll)
373 TerminateProcess (hProcess, 0);
374 break;
375 }
376 if (ev.u.Exception.ExceptionRecord.ExceptionFlags &
377 EXCEPTION_NONCONTINUABLE) {
378 res = 1;
379 goto print_and_exit;
380 }
381 break;
382 case EXIT_PROCESS_DEBUG_EVENT:
383 if (ev.u.ExitProcess.dwExitCode != 0)
384 process_fn = fn_win;
385 print_and_exit:
386 print_dlls (&dll_list, isdll ? fn_win : NULL, process_fn);
387 exitnow = true;
388 break;
389 default:
390 break;
391 }
392 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, cont))
393 {
394 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);
395 print_errno_error_and_return (in_fn);
396 }
397 if (exitnow)
398 break;
399 }
400
401 return res;
402 }
403
404 int
405 main (int argc, char **argv)
406 {
407 int optch;
408
409 /* Use locale from environment. If not set or set to "C", use UTF-8. */
410 setlocale (LC_CTYPE, "");
411 if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
412 setlocale (LC_CTYPE, "en_US.UTF-8");
413 while ((optch = getopt_long (argc, argv, opts, longopts, NULL)) != -1)
414 switch (optch)
415 {
416 case 'd':
417 case 'r':
418 case 'u':
419 error ("option not implemented `-%c'", optch);
420 exit (1);
421 case 'h':
422 usage ();
423 case 'V':
424 print_version ();
425 return 0;
426 default:
427 fprintf (stderr, "Try `%s --help' for more information.\n",
428 program_invocation_short_name);
429 return 1;
430 }
431 argv += optind;
432 if (!*argv)
433 error ("missing file arguments");
434
435 int ret = 0;
436 bool multiple = !!argv[1];
437 char *fn;
438 while ((fn = *argv++))
439 if (report (fn, multiple))
440 ret = 1;
441 exit (ret);
442 }
443
444 static bool printing = false;
445
446
447 /* dump of import directory
448 section begins at pointer 'section base'
449 section RVA is 'section_rva'
450 import directory begins at pointer 'imp' */
451 static int
452 dump_import_directory (const void *const section_base,
453 const DWORD section_rva,
454 const IMAGE_IMPORT_DESCRIPTOR *imp)
455 {
456 /* get memory address given the RVA */
457 #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
458
459 /* continue until address inaccessible or there's no DLL name */
460 for (; !IsBadReadPtr (imp, sizeof (*imp)) && imp->Name; imp++)
461 {
462 wchar_t full_path[PATH_MAX];
463 wchar_t *dummy;
464 char *fn = (char *) adr (imp->Name);
465
466 if (saw_file (fn))
467 continue;
468
469 int len = mbstowcs (NULL, fn, 0);
470 if (len <= 0)
471 continue;
472 wchar_t fnw[len + 1];
473 mbstowcs (fnw, fn, len + 1);
474 /* output DLL's name */
475 char *print_fn;
476 if (!SearchPathW (NULL, fnw, NULL, PATH_MAX, full_path, &dummy))
477 {
478 print_fn = strdup ("not found");
479 printing = true;
480 }
481 else if (!printing)
482 continue;
483 else
484 {
485 print_fn = tocyg (full_path);
486 strcat (print_fn, " (?)");
487 }
488
489 printf ("\t%s => %s\n", (char *) fn, print_fn);
490 free (print_fn);
491 }
492 #undef adr
493
494 return 0;
495 }
496
497 /* load a file in RAM (memory-mapped)
498 return pointer to loaded file
499 0 if no success */
500 static void *
501 map_file (const wchar_t *filename)
502 {
503 HANDLE hFile, hMapping;
504 void *basepointer;
505 if ((hFile = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
506 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
507 {
508 fprintf (stderr, "couldn't open %ls\n", filename);
509 return 0;
510 }
511 if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
512 {
513 fprintf (stderr, "CreateFileMapping failed with windows error %u\n",
514 (unsigned int) GetLastError ());
515 CloseHandle (hFile);
516 return 0;
517 }
518 if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0)))
519 {
520 fprintf (stderr, "MapViewOfFile failed with windows error %u\n",
521 (unsigned int) GetLastError ());
522 CloseHandle (hMapping);
523 CloseHandle (hFile);
524 return 0;
525 }
526
527 CloseHandle (hMapping);
528 CloseHandle (hFile);
529
530 return basepointer;
531 }
532
533
534 /* this will return a pointer immediatly behind the DOS-header
535 0 if error */
536 static void *
537 skip_dos_stub (const IMAGE_DOS_HEADER *dos_ptr)
538 {
539 /* look there's enough space for a DOS-header */
540 if (IsBadReadPtr (dos_ptr, sizeof (*dos_ptr)))
541 {
542 fprintf (stderr, "not enough space for DOS-header\n");
543 return 0;
544 }
545
546 /* validate MZ */
547 if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE)
548 {
549 fprintf (stderr, "not a DOS-stub\n");
550 return 0;
551 }
552
553 /* ok, then, go get it */
554 return (char*) dos_ptr + dos_ptr->e_lfanew;
555 }
556
557
558 /* find the directory's section index given the RVA
559 Returns -1 if impossible */
560 static int
561 get_directory_index (const unsigned dir_rva,
562 const unsigned dir_length,
563 const int number_of_sections,
564 const IMAGE_SECTION_HEADER *sections)
565 {
566 int sect;
567 for (sect = 0; sect < number_of_sections; sect++)
568 {
569 /* compare directory RVA to section RVA */
570 if (sections[sect].VirtualAddress <= dir_rva
571 && dir_rva < sections[sect].VirtualAddress+sections[sect].SizeOfRawData)
572 return sect;
573 }
574
575 return -1;
576 }
577
578 /* dump imports of a single file
579 Returns 0 if successful, !=0 else */
580 static int
581 process_file (const wchar_t *filename)
582 {
583 void *basepointer; /* Points to loaded PE file
584 * This is memory mapped stuff
585 */
586 int number_of_sections;
587 DWORD import_rva; /* RVA of import directory */
588 DWORD import_length; /* length of import directory */
589 int import_index; /* index of section with import directory */
590
591 /* ensure byte-alignment for struct tag_header */
592 #include <pshpack1.h>
593
594 const struct tag_header
595 {
596 DWORD signature;
597 IMAGE_FILE_HEADER file_head;
598 IMAGE_OPTIONAL_HEADER opt_head;
599 IMAGE_SECTION_HEADER section_header[1]; /* an array of unknown length */
600 } *header;
601
602 /* revert to regular alignment */
603 #include <poppack.h>
604
605 printing = false;
606
607 /* first, load file */
608 basepointer = map_file (filename);
609 if (!basepointer)
610 {
611 puts ("cannot load file");
612 return 1;
613 }
614
615 /* get header pointer; validate a little bit */
616 header = (tag_header *) skip_dos_stub ((IMAGE_DOS_HEADER *) basepointer);
617 if (!header)
618 {
619 puts ("cannot skip DOS stub");
620 UnmapViewOfFile (basepointer);
621 return 2;
622 }
623
624 /* look there's enough space for PE headers */
625 if (IsBadReadPtr (header, sizeof (*header)))
626 {
627 puts ("not enough space for PE headers");
628 UnmapViewOfFile (basepointer);
629 return 3;
630 }
631
632 /* validate PE signature */
633 if (header->signature != IMAGE_NT_SIGNATURE)
634 {
635 puts ("not a PE file");
636 UnmapViewOfFile (basepointer);
637 return 4;
638 }
639
640 /* get number of sections */
641 number_of_sections = header->file_head.NumberOfSections;
642
643 /* check there are sections... */
644 if (number_of_sections < 1)
645 {
646 UnmapViewOfFile (basepointer);
647 return 5;
648 }
649
650 /* validate there's enough space for section headers */
651 if (IsBadReadPtr (header->section_header, number_of_sections*sizeof (IMAGE_SECTION_HEADER)))
652 {
653 puts ("not enough space for section headers");
654 UnmapViewOfFile (basepointer);
655 return 6;
656 }
657
658 /* get RVA and length of import directory */
659 import_rva = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
660 import_length = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
661
662 /* check there's stuff to care about */
663 if (!import_rva || !import_length)
664 {
665 UnmapViewOfFile (basepointer);
666 return 0; /* success! */
667 }
668
669 /* get import directory pointer */
670 import_index = get_directory_index (import_rva,import_length,number_of_sections,header->section_header);
671
672 /* check directory was found */
673 if (import_index < 0)
674 {
675 puts ("couldn't find import directory in sections");
676 UnmapViewOfFile (basepointer);
677 return 7;
678 }
679
680 /* The pointer to the start of the import directory's section */
681 const void *section_address = (char*) basepointer + header->section_header[import_index].PointerToRawData;
682 if (dump_import_directory (section_address,
683 header->section_header[import_index].VirtualAddress,
684 /* the last parameter is the pointer to the import directory:
685 section address + (import RVA - section RVA)
686 The difference is the offset of the import directory in the section */
687 (const IMAGE_IMPORT_DESCRIPTOR *) ((char *) section_address+import_rva-header->section_header[import_index].VirtualAddress)))
688 {
689 UnmapViewOfFile (basepointer);
690 return 8;
691 }
692
693 UnmapViewOfFile (basepointer);
694 return 0;
695 }
This page took 0.060214 seconds and 4 git commands to generate.