]> sourceware.org Git - newlib-cygwin.git/blob - winsup/utils/ldd.cc
* ldd.cc (longopts): Add --dll option.
[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 <sys/cygwin.h>
34 #include <unistd.h>
35 #include <libgen.h>
36
37 #define _WIN32_WINNT 0x0501
38 #include <windows.h>
39 #include <imagehlp.h>
40 #include <psapi.h>
41
42 #ifndef STATUS_DLL_NOT_FOUND
43 #define STATUS_DLL_NOT_FOUND (0xC0000135L)
44 #endif
45
46 #define VERSION "1.0"
47
48 struct option longopts[] =
49 {
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}
57 };
58
59 static int process_file (const char *);
60
61 static int
62 usage (const char *fmt, ...)
63 {
64 va_list ap;
65 va_start (ap, fmt);
66 fprintf (stderr, "ldd: ");
67 vfprintf (stderr, fmt, ap);
68 fprintf (stderr, "\nTry `ldd --help' for more information.\n");
69 exit (1);
70 }
71
72 #define print_errno_error_and_return(__fn) \
73 do {\
74 fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
75 return 1;\
76 } while (0)
77
78 #define set_errno_and_return(x) \
79 do {\
80 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
81 return (x);\
82 } while (0)
83
84
85 static HANDLE hProcess;
86
87 static char *
88 get_module_filename (HANDLE hp, HMODULE hm)
89 {
90 size_t len;
91 char *buf = NULL;
92 DWORD res;
93 for (len = 1024; (res = GetModuleFileNameEx (hp, hm, (buf = (char *) realloc (buf, len)), len)) == len; len += 1024)
94 continue;
95 if (!res)
96 {
97 free (buf);
98 buf = NULL;
99 }
100 return buf;
101 }
102
103 static char *
104 load_dll (const char *fn)
105 {
106 char *buf = get_module_filename (GetCurrentProcess (), NULL);
107 if (!buf)
108 {
109 printf ("ldd: GetModuleFileName returned an error %lu\n", GetLastError ());
110 exit (1); /* FIXME */
111 }
112 buf = (char *) realloc (buf, sizeof (" \"--dll\" \"\"") + strlen (buf));
113 strcat (buf, " --dll \"");
114 strcat (buf, fn);
115 strcat (buf, "\"");
116 return buf;
117 }
118
119
120 static int
121 start_process (const char *fn, bool& isdll)
122 {
123 STARTUPINFO si = {};
124 PROCESS_INFORMATION pi;
125 si.cb = sizeof (si);
126 CHAR *cmd;
127 if (strlen (fn) < 4 || strcasecmp (strchr (fn, '\0') - 4, ".dll") != 0)
128 {
129 cmd = strdup (fn);
130 isdll = false;
131 }
132 else
133 {
134 cmd = load_dll (fn);
135 isdll = true;
136 }
137 if (CreateProcess (NULL, cmd, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi))
138 {
139 free (cmd);
140 hProcess = pi.hProcess;
141 DebugSetProcessKillOnExit (true);
142 return 0;
143 }
144
145 free (cmd);
146 set_errno_and_return (1);
147 }
148
149 static int
150 get_entry_point ()
151 {
152 HMODULE hm;
153 DWORD cb;
154 if (!EnumProcessModules (hProcess, &hm, sizeof (hm), &cb) || !cb)
155 set_errno_and_return (1);
156
157 MODULEINFO mi = {};
158 if (!GetModuleInformation (hProcess, hm, &mi, sizeof (mi)) || !mi.EntryPoint)
159 set_errno_and_return (1);
160
161 static const unsigned char int3 = 0xcc;
162 if (!WriteProcessMemory (hProcess, mi.EntryPoint, &int3, 1, &cb) || cb != 1)
163 set_errno_and_return (1);
164 return 0;
165 }
166
167 struct dlls
168 {
169 LPVOID lpBaseOfDll;
170 struct dlls *next;
171 };
172
173 #define SLOP strlen (" (?)")
174 char *
175 tocyg (char *win_fn)
176 {
177 win_fn[MAX_PATH] = '\0';
178 ssize_t cwlen = cygwin_conv_path (CCP_WIN_A_TO_POSIX, win_fn, NULL, 0);
179 char *fn;
180 if (cwlen <= 0)
181 fn = strdup (win_fn);
182 else
183 {
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)
186 fn = fn_cyg;
187 else
188 {
189 free (fn_cyg);
190 fn = (char *) malloc (strlen (win_fn) + SLOP + 1);
191 strcpy (fn, win_fn);
192 }
193 }
194 return fn;
195 }
196
197 #define CYGWIN_DLL_LEN (strlen ("\\cygwin1.dll"))
198 static int
199 print_dlls_and_kill_inferior (dlls *dll, const char *dllfn, const char *process_fn)
200 {
201 bool printit = !dllfn;
202 while ((dll = dll->next))
203 {
204 char *fn;
205 char *fullpath = get_module_filename (hProcess, (HMODULE) dll->lpBaseOfDll);
206 if (!fullpath)
207 fn = strdup ("???");
208 else
209 {
210 if (printit)
211 fn = tocyg (fullpath);
212 else if (strcasecmp (fullpath, dllfn) != 0)
213 continue;
214 else
215 {
216 printit = true;
217 free (fullpath);
218 continue;
219 }
220 free (fullpath);
221 }
222 printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
223 free (fn);
224 }
225 TerminateProcess (hProcess, 0);
226 if (process_fn)
227 return process_file (process_fn);
228 return 0;
229 }
230
231 static int
232 report (const char *in_fn, bool multiple)
233 {
234 if (multiple)
235 printf ("%s:\n", in_fn);
236 char *fn = realpath (in_fn, NULL);
237 if (!fn)
238 print_errno_error_and_return (in_fn);
239
240 ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A, fn, NULL, 0);
241 if (len <= 0)
242 print_errno_error_and_return (fn);
243
244 bool isdll;
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);
248
249 if (!fn || start_process (fn_win, isdll))
250 print_errno_error_and_return (in_fn);
251
252 DEBUG_EVENT ev;
253
254 unsigned dll_count = 0;
255
256 dlls dll_list = {};
257 dlls *dll_last = &dll_list;
258 const char *process_fn = NULL;
259 while (1)
260 {
261 if (WaitForDebugEvent (&ev, 1000))
262 /* ok */;
263 else
264 switch (GetLastError ())
265 {
266 case WAIT_TIMEOUT:
267 continue;
268 default:
269 usleep (100000);
270 goto out;
271 }
272 switch (ev.dwDebugEventCode)
273 {
274 case LOAD_DLL_DEBUG_EVENT:
275 if (!isdll && ++dll_count == 2)
276 get_entry_point ();
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;
281 break;
282 case EXCEPTION_DEBUG_EVENT:
283 switch (ev.u.Exception.ExceptionRecord.ExceptionCode)
284 {
285 case STATUS_DLL_NOT_FOUND:
286 process_fn = fn_win;
287 break;
288 case STATUS_BREAKPOINT:
289 if (!isdll)
290 print_dlls_and_kill_inferior (&dll_list, isdll ? fn_win : NULL, process_fn);
291 break;
292 }
293 break;
294 case EXIT_PROCESS_DEBUG_EVENT:
295 print_dlls_and_kill_inferior (&dll_list, isdll ? fn_win : NULL, process_fn);
296 break;
297 default:
298 break;
299 }
300 if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE))
301 {
302 cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);
303 print_errno_error_and_return (in_fn);
304 }
305 }
306
307 out:
308 return 0;
309 }
310
311
312 int
313 main (int argc, char **argv)
314 {
315 int optch;
316 int index;
317 while ((optch = getopt_long (argc, argv, "dru", longopts, &index)) != -1)
318 switch (optch)
319 {
320 case 'd':
321 case 'r':
322 case 'u':
323 usage ("option not implemented `-%c'", optch);
324 exit (1);
325 case 'D':
326 if (!LoadLibrary (argv[optind]))
327 exit (1);
328 exit (0);
329 case 0:
330 if (index == 1)
331 {
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",
335 VERSION);
336 exit (0);
337 }
338 else
339 {
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)");
349 exit (0);
350 }
351 }
352 argv += optind;
353 if (!*argv)
354 usage("missing file arguments");
355
356 int ret = 0;
357 bool multiple = !!argv[1];
358 char *fn;
359 while ((fn = *argv++))
360 if (report (fn, multiple))
361 ret = 1;
362 exit (ret);
363 }
364
365 static struct filelist
366 {
367 struct filelist *next;
368 char *name;
369 } *head;
370
371 static bool printing = false;
372
373 static bool
374 saw_file (char *name)
375 {
376
377 struct filelist *p;
378
379 for (p=head; p; p = p->next)
380 if (strcasecmp (name, p->name) == 0)
381 return true;
382
383 p = (filelist *) malloc(sizeof (struct filelist));
384 p->next = head;
385 p->name = strdup (name);
386 head = p;
387 return false;
388 }
389
390
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' */
395 static int
396 dump_import_directory (const void *const section_base,
397 const DWORD section_rva,
398 const IMAGE_IMPORT_DESCRIPTOR *imp)
399 {
400 /* get memory address given the RVA */
401 #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
402
403 /* continue until address inaccessible or there's no DLL name */
404 for (; !IsBadReadPtr (imp, sizeof (*imp)) && imp->Name; imp++)
405 {
406 char full_path[MAX_PATH];
407 char *dummy;
408 char *fn = (char *) adr (imp->Name);
409
410 if (saw_file (fn))
411 continue;
412
413 /* output DLL's name */
414 char *print_fn;
415 if (!SearchPath (NULL, fn, NULL, sizeof (full_path), full_path, &dummy))
416 {
417 print_fn = strdup ("not found");
418 printing = true;
419 }
420 else if (!printing)
421 continue;
422 else
423 {
424 print_fn = tocyg (full_path);
425 strcat (print_fn, " (?)");
426 }
427
428 printf ("\t%s => %s\n", (char *) fn, print_fn);
429 free (print_fn);
430 }
431 #undef adr
432
433 return 0;
434 }
435
436 /* load a file in RAM (memory-mapped)
437 return pointer to loaded file
438 0 if no success */
439 static void *
440 map_file (const char *filename)
441 {
442 HANDLE hFile, hMapping;
443 void *basepointer;
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)
446 {
447 fprintf (stderr, "couldn't open %s\n", filename);
448 return 0;
449 }
450 if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
451 {
452 fprintf (stderr, "CreateFileMapping failed with windows error %lu\n", GetLastError ());
453 CloseHandle (hFile);
454 return 0;
455 }
456 if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0)))
457 {
458 fprintf (stderr, "MapViewOfFile failed with windows error %lu\n", GetLastError ());
459 CloseHandle (hMapping);
460 CloseHandle (hFile);
461 return 0;
462 }
463
464 CloseHandle (hMapping);
465 CloseHandle (hFile);
466
467 return basepointer;
468 }
469
470
471 /* this will return a pointer immediatly behind the DOS-header
472 0 if error */
473 static void *
474 skip_dos_stub (const IMAGE_DOS_HEADER *dos_ptr)
475 {
476 /* look there's enough space for a DOS-header */
477 if (IsBadReadPtr (dos_ptr, sizeof (*dos_ptr)))
478 {
479 fprintf (stderr, "not enough space for DOS-header\n");
480 return 0;
481 }
482
483 /* validate MZ */
484 if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE)
485 {
486 fprintf (stderr, "not a DOS-stub\n");
487 return 0;
488 }
489
490 /* ok, then, go get it */
491 return (char*) dos_ptr + dos_ptr->e_lfanew;
492 }
493
494
495 /* find the directory's section index given the RVA
496 Returns -1 if impossible */
497 static int
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)
502 {
503 int sect;
504 for (sect = 0; sect < number_of_sections; sect++)
505 {
506 /* compare directory RVA to section RVA */
507 if (sections[sect].VirtualAddress <= dir_rva
508 && dir_rva < sections[sect].VirtualAddress+sections[sect].SizeOfRawData)
509 return sect;
510 }
511
512 return -1;
513 }
514
515 /* dump imports of a single file
516 Returns 0 if successful, !=0 else */
517 static int
518 process_file (const char *filename)
519 {
520 void *basepointer; /* Points to loaded PE file
521 * This is memory mapped stuff
522 */
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 */
527
528 /* ensure byte-alignment for struct tag_header */
529 #include <pshpack1.h>
530
531 const struct tag_header
532 {
533 DWORD signature;
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 */
539 } *header;
540
541 /* revert to regular alignment */
542 #include <poppack.h>
543
544 head = NULL; /* FIXME: memory leak */
545 printing = false;
546
547 /* first, load file */
548 basepointer = map_file (filename);
549 if (!basepointer)
550 {
551 puts ("cannot load file");
552 return 1;
553 }
554
555 /* get header pointer; validate a little bit */
556 header = (struct tag_header *) skip_dos_stub ((IMAGE_DOS_HEADER *) basepointer);
557 if (!header)
558 {
559 puts ("cannot skip DOS stub");
560 UnmapViewOfFile (basepointer);
561 return 2;
562 }
563
564 /* look there's enough space for PE headers */
565 if (IsBadReadPtr (header, sizeof (*header)))
566 {
567 puts ("not enough space for PE headers");
568 UnmapViewOfFile (basepointer);
569 return 3;
570 }
571
572 /* validate PE signature */
573 if (header->signature!=IMAGE_NT_SIGNATURE)
574 {
575 puts ("not a PE file");
576 UnmapViewOfFile (basepointer);
577 return 4;
578 }
579
580 /* get number of sections */
581 number_of_sections = header->file_head.NumberOfSections;
582
583 /* check there are sections... */
584 if (number_of_sections<1)
585 {
586 UnmapViewOfFile (basepointer);
587 return 5;
588 }
589
590 /* validate there's enough space for section headers */
591 if (IsBadReadPtr (header->section_header, number_of_sections*sizeof (IMAGE_SECTION_HEADER)))
592 {
593 puts ("not enough space for section headers");
594 UnmapViewOfFile (basepointer);
595 return 6;
596 }
597
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;
601
602 /* check there's stuff to care about */
603 if (!import_rva || !import_length)
604 {
605 UnmapViewOfFile (basepointer);
606 return 0; /* success! */
607 }
608
609 /* get import directory pointer */
610 import_index = get_directory_index (import_rva,import_length,number_of_sections,header->section_header);
611
612 /* check directory was found */
613 if (import_index <0)
614 {
615 puts ("couldn't find import directory in sections");
616 UnmapViewOfFile (basepointer);
617 return 7;
618 }
619
620 /* ok, we've found the import directory... action! */
621 {
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)))
630 {
631 UnmapViewOfFile (basepointer);
632 return 8;
633 }
634 }
635
636 UnmapViewOfFile (basepointer);
637 return 0;
638 }
This page took 0.065132 seconds and 6 git commands to generate.