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