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