1 #define _WIN32_WINNT 0x0501
2 
3 #undef __STRICT_ANSI__
4 
5 #include <errno.h>
6 #include <getopt.h>
7 #include <stdarg.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <wchar.h>
13 #include <locale.h>
14 #ifdef __CYGWIN__
15 #include <sys/cygwin.h>
16 #include <cygwin/version.h>
17 #define _wcsicmp wcscasecmp
18 #define _wcsnicmp wcsncasecmp
19 #define _wcsdup wcsdup
20 #endif
21 #include <unistd.h>
22 #include <libgen.h>
23 
24 
25 #ifndef _WIN32
26 #include <sys/param.h>
27 #define IsBadReadPtr(p, s) 0
28 #define DWORD unsigned int
29 #define WORD unsigned short
30 #define BYTE unsigned char
31 #define ULONGLONG unsigned long
32 #define _wcsdup(s) wcsdup(s)
33 #define _wcsicmp wcscasecmp
34 #define UnmapViewOfFile(p) free(p)
35 
36 #define MAX_PATH PATH_MAX
37 
38 typedef struct _IMAGE_IMPORT_DESCRIPTOR {
39 	union {
40 		DWORD Characteristics;
41 		DWORD OriginalFirstThunk;
42 	};
43 	DWORD TimeDateStamp;
44 	DWORD ForwarderChain;
45 	DWORD Name;
46 	DWORD FirstThunk;
47 } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
48 
49 typedef struct _IMAGE_DOS_HEADER {
50 	WORD e_magic;
51 	WORD e_cblp;
52 	WORD e_cp;
53 	WORD e_crlc;
54 	WORD e_cparhdr;
55 	WORD e_minalloc;
56 	WORD e_maxalloc;
57 	WORD e_ss;
58 	WORD e_sp;
59 	WORD e_csum;
60 	WORD e_ip;
61 	WORD e_cs;
62 	WORD e_lfarlc;
63 	WORD e_ovno;
64 	WORD e_res[4];
65 	WORD e_oemid;
66 	WORD e_oeminfo;
67 	WORD e_res2[10];
68 	DWORD e_lfanew;
69 } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;
70 
71 #define IMAGE_DOS_SIGNATURE 0x5A4D
72 #define IMAGE_SIZEOF_SHORT_NAME 8
73 #define IMAGE_NT_SIGNATURE 0x00004550
74 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
75 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
76 
77 typedef struct _IMAGE_DATA_DIRECTORY {
78 	DWORD VirtualAddress;
79 	DWORD Size;
80 } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
81 
82 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
83 
84 typedef struct _IMAGE_SECTION_HEADER {
85 	BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
86 	union {
87 		DWORD PhysicalAddress;
88 		DWORD VirtualSize;
89 	};
90 	DWORD VirtualAddress;
91 	DWORD SizeOfRawData;
92 	DWORD PointerToRawData;
93 	DWORD PointerToRelocations;
94 	DWORD PointerToLinenumbers;
95 	WORD NumberOfRelocations;
96 	WORD NumberOfLinenumbers;
97 	DWORD Characteristics;
98 } IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;
99 
100 typedef struct _IMAGE_FILE_HEADER {
101 	WORD Machine;
102 	WORD NumberOfSections;
103 	DWORD TimeDateStamp;
104 	DWORD PointerToSymbolTable;
105 	DWORD NumberOfSymbols;
106 	WORD SizeOfOptionalHeader;
107 	WORD Characteristics;
108 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
109 
110 typedef struct _IMAGE_OPTIONAL_HEADER {
111 	WORD Magic;
112 	BYTE MajorLinkerVersion;
113 	BYTE MinorLinkerVersion;
114 	DWORD SizeOfCode;
115 	DWORD SizeOfInitializedData;
116 	DWORD SizeOfUninitializedData;
117 	DWORD AddressOfEntryPoint;
118 	DWORD BaseOfCode;
119 	DWORD BaseOfData;
120 	DWORD ImageBase;
121 	DWORD SectionAlignment;
122 	DWORD FileAlignment;
123 	WORD MajorOperatingSystemVersion;
124 	WORD MinorOperatingSystemVersion;
125 	WORD MajorImageVersion;
126 	WORD MinorImageVersion;
127 	WORD MajorSubsystemVersion;
128 	WORD MinorSubsystemVersion;
129 	DWORD Win32VersionValue;
130 	DWORD SizeOfImage;
131 	DWORD SizeOfHeaders;
132 	DWORD CheckSum;
133 	WORD Subsystem;
134 	WORD DllCharacteristics;
135 	DWORD SizeOfStackReserve;
136 	DWORD SizeOfStackCommit;
137 	DWORD SizeOfHeapReserve;
138 	DWORD SizeOfHeapCommit;
139 	DWORD LoaderFlags;
140 	DWORD NumberOfRvaAndSizes;
141 	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
142 } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
143 
144 typedef struct _IMAGE_OPTIONAL_HEADER64 {
145 	WORD Magic;
146 	BYTE MajorLinkerVersion;
147 	BYTE MinorLinkerVersion;
148 	DWORD SizeOfCode;
149 	DWORD SizeOfInitializedData;
150 	DWORD SizeOfUninitializedData;
151 	DWORD AddressOfEntryPoint;
152 	DWORD BaseOfCode;
153 	ULONGLONG ImageBase;
154 	DWORD SectionAlignment;
155 	DWORD FileAlignment;
156 	WORD MajorOperatingSystemVersion;
157 	WORD MinorOperatingSystemVersion;
158 	WORD MajorImageVersion;
159 	WORD MinorImageVersion;
160 	WORD MajorSubsystemVersion;
161 	WORD MinorSubsystemVersion;
162 	DWORD Win32VersionValue;
163 	DWORD SizeOfImage;
164 	DWORD SizeOfHeaders;
165 	DWORD CheckSum;
166 	WORD Subsystem;
167 	WORD DllCharacteristics;
168 	ULONGLONG SizeOfStackReserve;
169 	ULONGLONG SizeOfStackCommit;
170 	ULONGLONG SizeOfHeapReserve;
171 	ULONGLONG SizeOfHeapCommit;
172 	DWORD LoaderFlags;
173 	DWORD NumberOfRvaAndSizes;
174 	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
175 } IMAGE_OPTIONAL_HEADER64,*PIMAGE_OPTIONAL_HEADER64;
176 
177 #define IMAGE_DIRECTORY_ENTRY_IMPORT	1
178 
179 #endif
180 
181 
182 static char const program_name[] = "ldd";
183 
184 
185 #ifdef _WIN32
186 #include <windows.h>
187 #include <imagehlp.h>
188 #include <psapi.h>
189 #endif
190 #include <wchar.h>
191 
192 #ifndef STATUS_DLL_NOT_FOUND
193 #  define STATUS_DLL_NOT_FOUND (0xC0000135L)
194 #endif
195 
196 
197 #ifndef FALSE
198 # define FALSE 0
199 # define TRUE  1
200 #endif
201 
202 
203 #define VERSION "2.0"
204 
205 
206 static int shortname;
207 static int pathname;
208 
209 
error(const char * fmt,...)210 static int error(const char *fmt,...)
211 {
212 	va_list ap;
213 
214 	va_start(ap, fmt);
215 	fprintf(stderr, "ldd: ");
216 	vfprintf(stderr, fmt, ap);
217 	fprintf(stderr, "\nTry `ldd --help' for more information.\n");
218 	exit(1);
219 }
220 
221 
print_usage(void)222 static void print_usage(void)
223 {
224 	printf("Usage: %s [OPTION]... FILE...\n\
225 \n\
226 Print shared library dependencies\n\
227 \n\
228   -h, --help              print this help and exit\n\
229   -V, --version           print version information and exit\n\
230   -r, --function-relocs   process data and function relocations\n\
231                           (currently unimplemented)\n\
232   -u, --unused            print unused direct dependencies\n\
233                           (currently unimplemented)\n\
234   -v, --verbose           print all information\n\
235                           (currently unimplemented)",
236 	program_name);
237 }
238 
239 
print_version(void)240 static void print_version(void)
241 {
242 	printf(
243 #if defined(__CYGWIN__)
244 	   "ldd (Cygwin) %d.%d.%d\n"
245 #elif defined(__MINGW32__)
246 #ifdef _WIN64
247 	   "ldd (MingW) (64bit) " VERSION "\n"
248 #else
249 	   "ldd (MingW) " VERSION "\n"
250 #endif
251 #elif defined(__WINE__)
252 #ifdef _WIN64
253 	   "ldd (wine) (64bit) " VERSION "\n"
254 #else
255 	   "ldd (wine) " VERSION "\n"
256 #endif
257 #elif defined(_MSC_VER)
258 #ifdef _WIN64
259 	   "ldd (MSVC) (64bit) " VERSION "\n"
260 #else
261 	   "ldd (MSVC) " VERSION "\n"
262 #endif
263 #else
264 	   "ldd " VERSION "\n"
265 #endif
266 	   "Print shared library dependencies\n"
267 	   "Copyright (C) 2009 - %s Chris Faylor\n"
268 	   "This is free software; see the source for copying conditions.  There is NO\n"
269 	   "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
270 #if defined(__CYGWIN__)
271 	  CYGWIN_VERSION_DLL_MAJOR / 1000,
272 	  CYGWIN_VERSION_DLL_MAJOR % 1000,
273 	  CYGWIN_VERSION_DLL_MINOR,
274 #endif
275 	   strrchr (__DATE__, ' ') + 1);
276 }
277 
278 
279 #define print_errno_error_and_return(__fn) \
280   do {\
281     fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
282     return 1;\
283   } while (0)
284 
285 static struct filelist
286 {
287 	struct filelist *next;
288 	wchar_t *name;
289 } *head;
290 
291 
292 #ifdef _WIN32
xstrdup(const char * s)293 static char *xstrdup(const char *s)
294 {
295 	return strcpy(malloc(strlen(s) + 1), s);
296 }
297 #endif
298 
299 
saw_file(const wchar_t * name)300 static int saw_file(const wchar_t *name)
301 {
302 	struct filelist *p;
303 
304 	for (p = head; p; p = p->next)
305 		if (_wcsicmp(name, p->name) == 0)
306 			return TRUE;
307 
308 	p = (struct filelist *) malloc(sizeof(struct filelist));
309 
310 	p->next = head;
311 	p->name = _wcsdup(name);
312 	head = p;
313 	return FALSE;
314 }
315 
316 
317 #ifdef _WIN32
318 #define SLOP strlen (" (?)")
tocyg(wchar_t * win_fn)319 static char *tocyg(wchar_t *win_fn)
320 {
321 	char *fn;
322 #ifdef __CYGWIN__
323 	ssize_t cwlen = cygwin_conv_path(CCP_WIN_W_TO_POSIX, win_fn, NULL, 0);
324 
325 	if (cwlen > 0)
326 	{
327 		char *fn_cyg = (char *) malloc(cwlen + SLOP + 1);
328 
329 		if (cygwin_conv_path(CCP_WIN_W_TO_POSIX, win_fn, fn_cyg, cwlen) == 0)
330 			fn = fn_cyg;
331 		else
332 		{
333 			int len;
334 
335 			free(fn_cyg);
336 			len = wcstombs(NULL, win_fn, 0);
337 
338 			fn = (char *) malloc(len + SLOP + 1);
339 			wcstombs(fn, win_fn, len + SLOP + 1);
340 		}
341 	} else
342 #endif
343 	{
344 		int len = wcstombs(NULL, win_fn, 0) + 1;
345 
346 		if ((fn = (char *) malloc(len)) != NULL)
347 			wcstombs(fn, win_fn, len);
348 	}
349 	return fn;
350 }
351 #endif
352 
353 
354 static int process_file(const wchar_t *filename, const char *internal_fn, const char *print_fn);
355 
356 
357 #ifdef _WIN32
358 WINBASEAPI WINBOOL WINAPI Wow64DisableWow64FsRedirection(PVOID *oldValue);
359 WINBASEAPI WINBOOL WINAPI Wow64RevertWow64FsRedirection(PVOID OldValue);
360 WINBASEAPI BOOLEAN WINAPI Wow64EnableWow64FsRedirection(BOOLEAN Wow64FsEnableRedirection);
361 
362 /*
363 DWORD WINAPI GetFinalPathNameByHandleW(
364   HANDLE hFile,
365   LPWSTR lpszFilePath,
366   DWORD cchFilePath,
367   DWORD dwFlags
368 );
369 
370 UINT WINAPI GetSystemWow64DirectoryW(
371   LPWSTR lpBuffer,
372   UINT uSize
373 );
374 */
375 
376 typedef WINBOOL (WINAPI *PFNWOW64DISABLEWOW64FSREDIRECTION)(PVOID *oldValue);
377 typedef WINBOOL (WINAPI *PFNWOW64REVERTWOW64FSREDIRECTION)(PVOID OldValue);
378 typedef BOOL (WINAPI *PFNSETDLLDIRECTORYW)(LPWSTR name);
379 typedef DWORD (WINAPI *PFNGETFINALPATHNAMEBYHANDLEW)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
380 typedef UINT (WINAPI *PFNGETSYSTEMWOW64DIRECTORYW)(LPWSTR buffer, UINT size);
381 
382 static PFNWOW64DISABLEWOW64FSREDIRECTION p_Wow64DisableWow64FsRedirection;
383 static PFNWOW64REVERTWOW64FSREDIRECTION p_Wow64RevertWow64FsRedirection;
384 static PFNSETDLLDIRECTORYW p_SetDllDirectoryW;
385 static PFNGETFINALPATHNAMEBYHANDLEW p_GetFinalPathNameByHandleW;
386 static PFNGETSYSTEMWOW64DIRECTORYW p_GetSystemWow64DirectoryW;
387 
388 #endif
389 
390 
391 
392 /* dump of import directory
393    section begins at pointer 'section base'
394    section RVA is 'section_rva'
395    import directory begins at pointer 'imp' */
dump_import_directory(wchar_t * search_path,const void * const section_base,const DWORD section_rva,const IMAGE_IMPORT_DESCRIPTOR * imp,int is_64bit)396 static int dump_import_directory(wchar_t *search_path, const void *const section_base,
397 					   const DWORD section_rva,
398 					   const IMAGE_IMPORT_DESCRIPTOR *imp, int is_64bit)
399 {
400 	int ret = 0;
401 
402 	/* get memory address given the RVA */
403 #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
404 
405 	/* continue until address inaccessible or there's no DLL name */
406 	for (; !IsBadReadPtr(imp, sizeof(*imp)) && imp->Name; imp++)
407 	{
408 		char *fn = (char *) adr(imp->Name);
409 		wchar_t *fnw;
410 		/* output DLL's name */
411 		int len;
412 
413 		len = mbstowcs(NULL, fn, 0);
414 
415 		if (len <= 0)
416 			continue;
417 		fnw = (wchar_t *)malloc((len + 1) * sizeof(*fnw));
418 
419 		mbstowcs(fnw, fn, len + 1);
420 
421 		if (!saw_file(fnw))
422 		{
423 #ifdef _WIN32
424 			PVOID redirect;
425 			wchar_t *dummy;
426 			char *print_fn;
427 			wchar_t full_path[MAX_PATH];
428 
429 			redirect = NULL;
430 
431 			if (is_64bit && p_Wow64DisableWow64FsRedirection)
432 				p_Wow64DisableWow64FsRedirection(&redirect);
433 
434 #ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE
435 #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x00000020
436 #endif
437 
438 			if (!SearchPathW(search_path, fnw, NULL, MAX_PATH, full_path, &dummy))
439 			{
440 				print_fn = xstrdup("not found");
441 				if (shortname || pathname)
442 					printf("notfound:%s\n", fn);
443 				else
444 					printf("\t%s => %s\n", fn, print_fn);
445 				free(print_fn);
446 				ret |= 1;
447 			} else
448 			{
449 				if (strncmp(fn, "API-MS-", 7) != 0 && strncmp(fn, "api-ms-", 7) != 0)
450 				{
451 					print_fn = tocyg(full_path);
452 				} else
453 				{
454 					print_fn = NULL;
455 				}
456 				ret |= process_file(full_path, fn, print_fn);
457 				free(print_fn);
458 			}
459 			if (is_64bit && p_Wow64RevertWow64FsRedirection)
460 				p_Wow64RevertWow64FsRedirection(redirect);
461 #else
462 			printf("\t%s\n", fn);
463 #endif
464 		}
465 		free(fnw);
466 	}
467 #undef adr
468 
469 	return ret;
470 }
471 
472 
473 /* load a file in RAM (memory-mapped)
474    return pointer to loaded file
475    0 if no success  */
map_file(const wchar_t * filename)476 static void *map_file(const wchar_t *filename)
477 {
478 	void *basepointer;
479 #ifdef _WIN32
480 	HANDLE hFile, hMapping;
481 
482 	if ((hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
483 	 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
484 	{
485 		__extension__ fprintf(stderr, "couldn't open %ls\n", filename);
486 		return 0;
487 	}
488 	if (!(hMapping = CreateFileMapping(hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
489 	{
490 		fprintf(stderr, "CreateFileMapping failed with windows error %u\n", (unsigned int) GetLastError());
491 		CloseHandle(hFile);
492 		return 0;
493 	}
494 	if (!(basepointer = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)))
495 	{
496 		fprintf(stderr, "MapViewOfFile failed with windows error %u\n", (unsigned int) GetLastError());
497 		CloseHandle(hMapping);
498 		CloseHandle(hFile);
499 		return 0;
500 	}
501 	CloseHandle(hMapping);
502 	CloseHandle(hFile);
503 
504 #else
505 	FILE *fp;
506 	char fname[PATH_MAX];
507 	size_t size;
508 
509 	wcstombs(fname, filename, sizeof(fname));
510 	fp = fopen(fname, "rb");
511 	if (fp == NULL)
512 	{
513 		fprintf(stderr, "can't open %s: %s\n", fname, strerror(errno));
514 		return NULL;
515 	}
516 	fseek(fp, 0l, SEEK_END);
517 	size = ftell(fp);
518 	fseek(fp, 0l, SEEK_SET);
519 	basepointer = malloc(size);
520 	if (fread(basepointer, 1, size, fp) != size)
521 	{
522 		fprintf(stderr, "fread failed on %s: %s\n", fname, strerror(errno));
523 		fclose(fp);
524 		free(basepointer);
525 		return NULL;
526 	}
527 	fclose(fp);
528 #endif
529 	return basepointer;
530 }
531 
532 
533 /* this will return a pointer immediatly behind the DOS-header
534    0 if error */
skip_dos_stub(const wchar_t * filename,const IMAGE_DOS_HEADER * dos_ptr)535 static void *skip_dos_stub(const wchar_t *filename, const IMAGE_DOS_HEADER *dos_ptr)
536 {
537 	/* look there's enough space for a DOS-header */
538 	if (IsBadReadPtr(dos_ptr, sizeof(*dos_ptr)))
539 	{
540 		__extension__ fprintf(stderr, "%ls: not enough space for DOS-header\n", filename);
541 		return 0;
542 	}
543 	/* validate MZ */
544 	if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE)
545 	{
546 		__extension__ fprintf(stderr, "%ls: not a DOS-stub\n", filename);
547 		return 0;
548 	}
549 	if (dos_ptr->e_lfarlc < 0x40 || dos_ptr->e_lfanew == 0)
550 	{
551 		__extension__ fprintf(stderr, "%ls: invalid DOS-stub\n", filename);
552 		return 0;
553 	}
554 	/* ok, then, go get it */
555 	return (char *) dos_ptr + dos_ptr->e_lfanew;
556 }
557 
558 
559 /* find the directory's section index given the RVA
560    Returns -1 if impossible */
get_directory_index(const unsigned dir_rva,const unsigned dir_length,const int number_of_sections,const IMAGE_SECTION_HEADER * sections)561 static int 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 
568 	for (sect = 0; sect < number_of_sections; sect++)
569 	{
570 		/* compare directory RVA to section RVA */
571 		if (sections[sect].VirtualAddress <= dir_rva
572 			&& dir_rva < sections[sect].VirtualAddress + sections[sect].SizeOfRawData)
573 			return sect;
574 	}
575 
576 	return -1;
577 }
578 
579 
580 /* ensure byte-alignment for struct tag_header */
581 #ifdef _WIN32
582 #include <pshpack1.h>
583 #else
584 #pragma pack(push,1)
585 #endif
586 
587 struct tag_header
588 {
589 	DWORD signature;
590 	IMAGE_FILE_HEADER file_head;
591 	union {
592 		IMAGE_OPTIONAL_HEADER32 head32;
593 		IMAGE_OPTIONAL_HEADER64 head64;
594 	} opt;
595 };
596 /* revert to regular alignment */
597 #ifdef _WIN32
598 #include <poppack.h>
599 #else
600 #pragma pack(pop)
601 #endif
602 
603 
strccpy(char * s1,const char ** s2,char c)604 static char *strccpy(char *s1, const char **s2, char c)
605 {
606 	while (**s2 && **s2 != c)
607 		*s1++ = *((*s2)++);
608 	*s1 = 0;
609 	return s1;
610 }
611 
612 
combine_path(wchar_t ** list)613 static wchar_t *combine_path(wchar_t **list)
614 {
615 	size_t len = 0;
616 	wchar_t **p;
617 	wchar_t *path, *dst;
618 
619 	p = list;
620 	while (*p != NULL)
621 	{
622 		len += wcslen(*p) + 1;
623 		p++;
624 	}
625 	path = malloc(len * sizeof(*path));
626 	dst = path;
627 	p = list;
628 	while (*p != NULL)
629 	{
630 		if (dst > path)
631 			*dst++ = ';';
632 		wcscpy(dst, *p);
633 		dst += wcslen(dst);
634 		p++;
635 	}
636 	return path;
637 }
638 
639 
split_path(wchar_t * dst,const char * srcpath,size_t size,char delim,int is_64bit)640 static wchar_t **split_path(wchar_t *dst, const char *srcpath, size_t size, char delim, int is_64bit)
641 {
642 	char *srcbuf;
643 	wchar_t *d = dst - 1;
644 	size_t count = 0;
645 	int saw_current = 0;
646 #ifdef _WIN32
647 	int saw_windows = 0;
648 	int saw_system = 0;
649 	wchar_t windows_dir[MAX_PATH];
650 	wchar_t system_dir[MAX_PATH];
651 	wchar_t syswow64_dir[MAX_PATH];
652 #endif
653 	wchar_t **path_list, **listp;
654 	const char *src = srcpath;
655 
656 #ifdef _WIN32
657 	GetWindowsDirectoryW(windows_dir, MAX_PATH);
658 	GetSystemDirectoryW(system_dir, MAX_PATH);
659 	if (!p_GetSystemWow64DirectoryW || !p_GetSystemWow64DirectoryW(syswow64_dir, MAX_PATH))
660 		syswow64_dir[0] = 0;
661 #endif
662 
663 	srcbuf = malloc(size);
664 	do
665 	{
666 		char *srcpath = srcbuf;
667 		char *s = strccpy(srcpath, &src, delim);
668 		size_t len = s - srcpath;
669 		if (len >= MAX_PATH)
670 		{
671 			errno = ENAMETOOLONG;
672 			return NULL;
673 		}
674 		/* Paths in Win32 path lists in the environment (%Path%), are often
675 		   enclosed in quotes (usually paths with spaces).	Trailing backslashes
676 		   are common, too.Remove them.
677 		   */
678 		if (delim == ';' && len)
679 		{
680 			if (*srcpath == '"')
681 			{
682 				++srcpath;
683 				*--s = '\0';
684 				len -= 2;
685 			}
686 			while (len && s[-1] == '\\')
687 			{
688 				*--s = '\0';
689 				--len;
690 			}
691 		}
692 		if (strcmp(srcbuf, ".") == 0)
693 			len = 0;
694 		if (len)
695 		{
696 			++d;
697 #ifdef __CYGWIN__
698 			if (cygwin_conv_path(CCP_POSIX_TO_WIN_W, srcpath, d, size - (d - dst)))
699 				return NULL;
700 #else
701 			mbstowcs(d, srcpath, size - (d - dst));
702 #endif
703 		} else
704 		{
705 			saw_current = 1;
706 			++d;
707 #ifdef __CYGWIN__
708 			if (cygwin_conv_path(CCP_POSIX_TO_WIN_W, ".", d, size - (d - dst)))
709 				return NULL;
710 #else
711 #ifdef _WIN32
712 			GetCurrentDirectoryW(size - (d - dst), d);
713 #else
714 			{
715 				char dir[PATH_MAX];
716 				getcwd(dir, sizeof(dir));
717 				mbstowcs(d, dir, size - (d - dst));
718 			}
719 #endif
720 #endif
721 		}
722 		count++;
723 #ifdef _WIN32
724 		if (_wcsicmp(d, windows_dir) == 0)
725 			saw_windows = 1;
726 		if (_wcsicmp(d, system_dir) == 0)
727 			saw_system = 1;
728 #endif
729 		d = wcschr(d, '\0');
730 		*d = delim;
731 	} while (*src++);
732 	if (d < dst)
733 		d++;
734 	*d = '\0';
735 
736 	path_list = listp = malloc((count + 5) * sizeof(char *));
737 
738 	d = dst;
739 
740 	*listp++ = d;
741 	wcscpy(d, L"{appdir}");
742 	d += wcslen(d);
743 	*d++ = '\0';
744 
745 #ifdef _WIN32
746 	*listp++ = d;
747 	if (!is_64bit && syswow64_dir[0])
748 		wcscpy(d, syswow64_dir);
749 	else
750 		wcscpy(d, system_dir);
751 	d += wcslen(d);
752 	*d++ = '\0';
753 
754 	*listp++ = d;
755 	wcscpy(d, windows_dir);
756 	d += wcslen(d);
757 	*d = '\0';
758 #endif
759 
760 	src = srcpath;
761 	do
762 	{
763 		char *srcpath = srcbuf;
764 		char *s = strccpy(srcpath, &src, delim);
765 		size_t len = s - srcpath;
766 		if (len >= MAX_PATH)
767 		{
768 			errno = ENAMETOOLONG;
769 			return NULL;
770 		}
771 		/* Paths in Win32 path lists in the environment (%Path%), are often
772 		   enclosed in quotes (usually paths with spaces).	Trailing backslashes
773 		   are common, too.Remove them.
774 		   */
775 		if (delim == ';' && len)
776 		{
777 			if (*srcpath == '"')
778 			{
779 				++srcpath;
780 				*--s = '\0';
781 				len -= 2;
782 			}
783 			while (len && s[-1] == '\\')
784 			{
785 				*--s = '\0';
786 				--len;
787 			}
788 		}
789 		if (strcmp(srcbuf, ".") == 0)
790 			len = 0;
791 		if (len)
792 		{
793 			++d;
794 #ifdef __CYGWIN__
795 			if (cygwin_conv_path(CCP_POSIX_TO_WIN_W, srcpath, d, size - (d - dst)))
796 				return NULL;
797 #else
798 			mbstowcs(d, srcpath, size - (d - dst));
799 #endif
800 		} else
801 		{
802 			saw_current = 1;
803 			++d;
804 #ifdef __CYGWIN__
805 			if (cygwin_conv_path(CCP_POSIX_TO_WIN_W, ".", d, size - (d - dst)))
806 				return NULL;
807 #else
808 #ifdef _WIN32
809 			GetCurrentDirectoryW(size - (d - dst), d);
810 #else
811 			{
812 				char dir[PATH_MAX];
813 				getcwd(dir, sizeof(dir));
814 				mbstowcs(d, dir, size - (d - dst));
815 			}
816 #endif
817 #endif
818 		}
819 #ifdef _WIN32
820 		if (_wcsicmp(d, system_dir) == 0 && !is_64bit && syswow64_dir[0])
821 			wcscpy(d, syswow64_dir);
822 #endif
823 		*listp++ = d;
824 		d = wcschr(d, '\0');
825 		*d = '\0';
826 	} while (*src++);
827 	*d = '\0';
828 	*listp = NULL;
829 	free(srcbuf);
830 	(void) saw_current;
831 #ifdef _WIN32
832 	(void) saw_system;
833 	(void) saw_windows;
834 #endif
835 	return path_list;
836 }
837 
838 
839 
840 /* dump imports of a single file
841    Returns 0 if successful, !=0 else */
process_file(const wchar_t * filename,const char * internal_fn,const char * print_fn)842 static int process_file(const wchar_t *filename, const char *internal_fn, const char *print_fn)
843 {
844 	void *basepointer;					/* Points to loaded PE file */
845 	int number_of_sections;
846 	DWORD import_rva;					/* RVA of import directory */
847 	DWORD import_length;				/* length of import directory */
848 	const IMAGE_SECTION_HEADER *section_headers;		/* an array of unknown length */
849 	int import_index;					/* index of section with import directory */
850 	const void *section_address;
851 	const struct tag_header *header;
852 	int is_64bit = 0;
853 	wchar_t *dir;
854 	wchar_t *p1, *p2;
855 	wchar_t *path_w;
856 	wchar_t **search_path_split;
857 	wchar_t *search_path;
858 	ssize_t len;
859 	char *path;
860 	ULONGLONG ImageBase;
861 
862 	/* first, load file */
863 	basepointer = map_file(filename);
864 	if (!basepointer)
865 	{
866 		__extension__ fprintf(stderr, "%ls: cannot load file\n", filename);
867 		return 1;
868 	}
869 	/* get header pointer; validate a little bit */
870 	header = (struct tag_header *) skip_dos_stub(filename, (IMAGE_DOS_HEADER *) basepointer);
871 	if (!header)
872 	{
873 		__extension__ fprintf(stderr, "%ls: cannot skip DOS stub\n", filename);
874 		UnmapViewOfFile(basepointer);
875 		return 2;
876 	}
877 	/* look there's enough space for PE headers */
878 	if (IsBadReadPtr(header, sizeof(*header)))
879 	{
880 		__extension__ fprintf(stderr, "%ls: not enough space for PE headers\n", filename);
881 		UnmapViewOfFile(basepointer);
882 		return 3;
883 	}
884 	/* validate PE signature */
885 	if (header->signature != IMAGE_NT_SIGNATURE)
886 	{
887 		__extension__ fprintf(stderr, "%ls: not a PE file\n", filename);
888 		UnmapViewOfFile(basepointer);
889 		return 4;
890 	}
891 	if (header->opt.head32.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC &&
892 		header->file_head.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
893 	{
894 		/* get number of sections */
895 		number_of_sections = header->file_head.NumberOfSections;
896 
897 		/* check there are sections... */
898 		if (number_of_sections < 1)
899 		{
900 			UnmapViewOfFile(basepointer);
901 			return 5;
902 		}
903 		/* get RVA and length of import directory */
904 		import_rva = header->opt.head32.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
905 		import_length = header->opt.head32.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
906 		ImageBase = header->opt.head32.ImageBase;
907 	} else if (header->opt.head64.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC &&
908 		header->file_head.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
909 	{
910 		/* get number of sections */
911 		number_of_sections = header->file_head.NumberOfSections;
912 
913 		/* check there are sections... */
914 		if (number_of_sections < 1)
915 		{
916 			UnmapViewOfFile(basepointer);
917 			return 5;
918 		}
919 		/* get RVA and length of import directory */
920 		import_rva = header->opt.head64.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
921 		import_length = header->opt.head64.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
922 		ImageBase = header->opt.head64.ImageBase;
923 		is_64bit = 1;
924 	} else
925 	{
926 		__extension__ fprintf(stderr, "%ls: unknown PE file\n", filename);
927 		UnmapViewOfFile(basepointer);
928 		return 4;
929 	}
930 	if (print_fn)
931 	{
932 		if (pathname)
933 		{
934 			printf("%s\n", print_fn);
935 		} else if (shortname)
936 		{
937 			printf("%s\n", internal_fn);
938 		} else
939 		{
940 #ifdef __MINGW32__
941 			printf("\t%s => %s (0x%I64x)\n", internal_fn, print_fn, ImageBase);
942 #else
943 			printf("\t%s => %s (0x%llx)\n", internal_fn, print_fn, (unsigned long long) ImageBase);
944 #endif
945 		}
946 	}
947 
948 	section_headers = (const IMAGE_SECTION_HEADER *)((char *)header + offsetof(struct tag_header, opt) + header->file_head.SizeOfOptionalHeader);
949 	/* validate there's enough space for section headers */
950 	if (IsBadReadPtr(section_headers, number_of_sections * sizeof(IMAGE_SECTION_HEADER)))
951 	{
952 		__extension__ fprintf(stderr, "%ls: not enough space for section headers\n", filename);
953 		UnmapViewOfFile(basepointer);
954 		return 6;
955 	}
956 
957 	/* check there's stuff to care about */
958 	if (!import_rva || !import_length)
959 	{
960 		UnmapViewOfFile(basepointer);
961 		return 0;						/* success! */
962 	}
963 	/* get import directory pointer */
964 	import_index = get_directory_index(import_rva, import_length, number_of_sections, section_headers);
965 
966 	/* check directory was found */
967 	if (import_index < 0)
968 	{
969 		__extension__ fprintf(stderr, "%ls: couldn't find import directory in sections\n", filename);
970 		UnmapViewOfFile(basepointer);
971 		return 7;
972 	}
973 	/* The pointer to the start of the import directory's section */
974 	section_address = (char *) basepointer + section_headers[import_index].PointerToRawData;
975 
976 	dir = _wcsdup(filename);
977 	p1 = wcsrchr(dir, '\\');
978 	p2 = wcsrchr(dir, '/');
979 	if (p1 == NULL || p2 > p1)
980 		p1 = p2;
981 	if (p1 == NULL)
982 	{
983 		free(dir);
984 		dir = _wcsdup(L".");
985 	} else
986 	{
987 		p1[0] = 0;
988 	}
989 
990 	/*
991 	 * setup a path such that SearchPath uses the same
992 	 * search order as LoadLibrary.
993 	 * FIXME: this assumes current behavior of Windows
994 	 * and may break if the behavior of LoadLibrary() changes
995 	 * in future version of windows.
996 	 */
997 	path = getenv("PATH");
998 #if defined(__CYGWIN__) || !defined(_WIN32)
999 	len = strlen(path);
1000 	if (len <= 0)
1001 		print_errno_error_and_return("PATH");
1002 	len = len * 2 + 1 + 5 * MAX_PATH;
1003 	path_w = malloc(len * sizeof(*path_w));
1004 
1005 	search_path_split = split_path(path_w, path, len, ':', is_64bit);
1006 #else
1007 	len = mbstowcs(NULL, path, 0) + 1;
1008 	if (len <= 0)
1009 		print_errno_error_and_return("PATH");
1010 	len = len + 1 + 5 * MAX_PATH;
1011 	path_w = malloc(len * sizeof(*path_w));
1012 	search_path_split = split_path(path_w, path, len, ';', is_64bit);
1013 #endif
1014 
1015 	if (search_path_split == NULL)
1016 		print_errno_error_and_return("PATH");
1017 
1018 	search_path_split[0] = dir;
1019 	search_path = combine_path(search_path_split);
1020 	free(path_w);
1021 
1022 	if (dump_import_directory(search_path, section_address,
1023 						  section_headers[import_index].VirtualAddress,
1024 	/* the last parameter is the pointer to the import directory:
1025 	   section address + (import RVA - section RVA)
1026 	   The difference is the offset of the import directory in the section */
1027 			(const IMAGE_IMPORT_DESCRIPTOR *) ((char *) section_address + import_rva - section_headers[import_index].VirtualAddress),
1028 			is_64bit))
1029 	{
1030 		free(dir);
1031 		UnmapViewOfFile(basepointer);
1032 		return 8;
1033 	}
1034 	free(dir);
1035 	UnmapViewOfFile(basepointer);
1036 	return 0;
1037 }
1038 
1039 
report(const char * in_fn)1040 static int report(const char *in_fn)
1041 {
1042 	ssize_t len;
1043 	wchar_t *fn_win;
1044 	struct filelist *p;
1045 	int ret;
1046 
1047 #ifdef __CYGWIN__
1048 	len = cygwin_conv_path(CCP_POSIX_TO_WIN_W, in_fn, NULL, 0);
1049 
1050 	if (len <= 0)
1051 		print_errno_error_and_return(in_fn);
1052 	fn_win = malloc((len + 1) * sizeof(*fn_win));
1053 
1054 	if (cygwin_conv_path(CCP_POSIX_TO_WIN_W, in_fn, fn_win, len))
1055 		print_errno_error_and_return(in_fn);
1056 #else
1057 	len = mbstowcs(NULL, in_fn, 0) + 1;
1058 	if (len <= 0)
1059 		print_errno_error_and_return(in_fn);
1060 	fn_win = malloc((len + 1) * sizeof(*fn_win));
1061 	mbstowcs(fn_win, in_fn, len);
1062 #endif
1063 
1064 	head = NULL;
1065 	ret = process_file(fn_win, NULL, NULL);
1066 	while (head)
1067 	{
1068 		free(head->name);
1069 		p = head->next;
1070 		free(head);
1071 		head = p;
1072 	}
1073 	free(fn_win);
1074 	return ret;
1075 }
1076 
1077 
1078 static struct option const longopts[] =
1079 {
1080 	{ "help", no_argument, NULL, 'h' },
1081 	{ "verbose", no_argument, NULL, 'v' },
1082 	{ "version", no_argument, NULL, 'V' },
1083 	{ "data-relocs", no_argument, NULL, 'd' },
1084 	{ "function-relocs", no_argument, NULL, 'r' },
1085 	{ "unused", no_argument, NULL, 'u' },
1086 	{ "short", no_argument, NULL, 's' },
1087 	{ "path", no_argument, NULL, 'p' },
1088 	{ "multiple", no_argument, NULL, 'm' },
1089 	{ 0, no_argument, NULL, 0 }
1090 };
1091 
1092 
main(int argc,char ** argv)1093 int main(int argc, char **argv)
1094 {
1095 	int optch;
1096 	int ret = 0;
1097 	int multiple;
1098 	char *fn;
1099 
1100 	/* Use locale from environment.  If not set or set to "C", use UTF-8. */
1101 	setlocale(LC_CTYPE, "");
1102 	if (!strcmp(setlocale(LC_CTYPE, NULL), "C"))
1103 		setlocale(LC_CTYPE, "en_US.UTF-8");
1104 
1105 	multiple = 0;
1106 	shortname = 0;
1107 	pathname = 0;
1108 
1109 	while ((optch = getopt_long(argc, argv, "mdhprsuvV", longopts, NULL)) != -1)
1110 	{
1111 		switch (optch)
1112 		{
1113 		case 'd':
1114 		case 'r':
1115 		case 'u':
1116 			error("option not implemented `-%c'", optch);
1117 			exit(1);
1118 			break;
1119 		case 'v':
1120 			break;
1121 		case 'm':
1122 			multiple = 1;
1123 			break;
1124 		case 's':
1125 			shortname = 1;
1126 			break;
1127 		case 'p':
1128 			pathname = 1;
1129 			break;
1130 		case 'h':
1131 			print_usage();
1132 			return 0;
1133 		case 'V':
1134 			print_version();
1135 			return 0;
1136 		default:
1137 			fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
1138 			return 1;
1139 		}
1140 	}
1141 
1142 	if ((argc - optind) <= 0)
1143 		error("missing file arguments");
1144 
1145 	if ((argc - optind) > 1)
1146 		multiple = 1;
1147 
1148 #ifdef _WIN32
1149 	p_Wow64DisableWow64FsRedirection = (PFNWOW64DISABLEWOW64FSREDIRECTION)GetProcAddress(GetModuleHandleA("kernel32.dll"), "Wow64DisableWow64FsRedirection");
1150 	p_Wow64RevertWow64FsRedirection = (PFNWOW64REVERTWOW64FSREDIRECTION)GetProcAddress(GetModuleHandleA("kernel32.dll"), "Wow64RevertWow64FsRedirection");
1151 	p_SetDllDirectoryW = (PFNSETDLLDIRECTORYW)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryW");
1152 	p_GetFinalPathNameByHandleW = (PFNGETFINALPATHNAMEBYHANDLEW)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetFinalPathNameByHandleW");
1153 	p_GetSystemWow64DirectoryW = (PFNGETSYSTEMWOW64DIRECTORYW)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetSystemWow64DirectoryW");
1154 #endif
1155 
1156 	while (optind < argc)
1157 	{
1158 		fn = argv[optind++];
1159 		if (multiple)
1160 			printf("%s:\n", fn);
1161 
1162 		if (report(fn) != 0)
1163 		{
1164 			ret = 1;
1165 		}
1166 	}
1167 	exit(ret);
1168 	return ret;
1169 }
1170