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