OSDN Git Service

Avoid segmentation faults in forwarder function checks.
[mingw/pexports.git] / pexports.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <sys/stat.h>
5
6 #include "str_tree.h"
7 #include "pexports.h"
8
9 #ifndef PATH_MAX
10 #define PATH_MAX 260
11 #endif
12
13 #ifdef _WIN32
14 #define PATH_SEPARATOR ';'
15 #else
16 #define PATH_SEPARATOR ':'
17 #endif
18
19 /* get pointer to section header n */
20 #define IMAGE_SECTION_HDR(n) ((PIMAGE_SECTION_HEADER) ((char *) nt_hdr32 + \
21                                     4 + sizeof(IMAGE_FILE_HEADER) + \
22                                     nt_hdr32->FileHeader.SizeOfOptionalHeader + \
23                                     n * sizeof(IMAGE_SECTION_HEADER)))
24
25 /* convert relative virtual address to a useable pointer */
26 #define RVA_TO_PTR(rva,type) ((type) rva_to_ptr((DWORD) rva))
27
28 typedef struct str_list {
29   char *s;
30   struct str_list *next;
31 } str_list;
32
33 extern void yyparse(void);
34
35 static void *xmalloc(size_t count);
36 static void add_path_list(char *path);
37 static const char *find_file(const char *name);
38 static void str_list_add(str_list **head, const char *s);
39 static void parse_headers();
40 static void dump_symbol(char *name, int ord, DWORD rva);
41
42 static const char mz_sign[2] = {'M','Z'};
43 static const char pe_sign[4] = {'P','E','\0','\0'};
44 static const char exp_sign[6] = {'.','e','d','a','t','a'};
45
46 static PIMAGE_DOS_HEADER dos_hdr;
47 static PIMAGE_NT_HEADERS32 nt_hdr32;
48 static PIMAGE_NT_HEADERS64 nt_hdr64;
49
50 static char *filename = NULL;
51 static char *program_name;
52 static char *cpp = "gcc -E -xc-header";
53
54 str_tree *symbols = NULL;
55 static str_list *inc_path = NULL;
56 static str_list *header_files = NULL;
57
58 static int verbose = 0;
59 static int ordinal_flag = 0;
60
61 extern FILE *yyin;
62
63 int
64 main(int argc, char *argv[])
65 {
66   PIMAGE_SECTION_HEADER section;
67   DWORD exp_rva, exp_size;
68   int i;
69 #if defined(_WIN32) && !defined(_WIN64)
70
71   /* If running on 64-bit Windows and built as a 32-bit process, try
72    * disable Wow64 file system redirection, so that we can open DLLs
73    * in the real system32 folder if requested.
74    */
75
76   PVOID old_redirection;
77
78   HMODULE kernel32;
79
80   extern __declspec(dllimport) HMODULE __stdcall GetModuleHandleA(char *name);
81   extern __declspec(dllimport) PVOID __stdcall GetProcAddress(HMODULE module, char *name);
82
83   BOOL (__stdcall *pWow64DisableWow64FsRedirection) (PVOID *old_value);
84
85   kernel32 = GetModuleHandleA("kernel32.dll");
86   pWow64DisableWow64FsRedirection = GetProcAddress(kernel32, "Wow64DisableWow64FsRedirection");
87
88   if (pWow64DisableWow64FsRedirection)
89     pWow64DisableWow64FsRedirection(&old_redirection);
90 #endif
91
92   program_name = argv[0];
93
94   /* add standard include paths */
95   add_path_list(getenv("C_INCLUDE_PATH"));
96   add_path_list(getenv("CPLUS_INCLUDE_PATH"));
97 #if 0
98   /* since when does PATH have anything to do with headers? */
99   add_path_list(getenv("PATH"));
100 #endif
101
102   /* parse command line */
103   for ( i = 1; i < argc; i++ )
104     {
105       if (argv[i][0] == '-')
106         {
107           switch (argv[i][1])
108             {
109             case 'v':
110               verbose = 1;
111               break;
112             case 'o':
113               ordinal_flag = 1;
114               break;
115             case 'h':
116               if (!argv[++i])
117                 {
118                   fprintf(stderr, "-h requires an argument\n");
119                   return 1;
120                 }
121               str_list_add(&header_files, argv[i]);
122               break;
123             case 'p':
124               if (!argv[++i])
125                 {
126                   fprintf(stderr, "-p requires an argument\n");
127                   return 1;
128                 }
129               cpp = argv[i];
130               break;
131             default:
132               fprintf(stderr, "%s: Unknown option: %s\n",
133                       program_name, argv[i]);
134               return 1;
135             }
136         }
137       else
138         filename = argv[i];
139     }
140
141   if (filename == NULL)
142     {
143       printf("PExports %d.%d Copyright 1998, Anders Norlander\n"
144              "Changed 1999, Paul Sokolovsky\n"
145              "Changed 2008, Tor Lillqvist\n"
146              "This program is free software; you may redistribute it under the terms of\n"
147              "the GNU General Public License.  This program has absolutely no warranty.\n"
148
149              "\nUsage: %s [-v] [-o] [-h header] [-p preprocessor] dll\n"
150              "  -h\tparse header\n"
151              "  -o\tprint ordinals\n"
152              "  -p\tset preprocessor program\n"
153              "  -v\tverbose mode\n"
154              "\nReport bugs to anorland@hem2.passagen.se,\n"
155              "Paul.Sokolovsky@technologist.com\n"
156              "or tml@iki.fi\n",
157              VER_MAJOR, VER_MINOR,
158              program_name);
159       return 1;
160     }
161
162   /* parse headers and build symbol tree */
163   parse_headers();
164
165   /* load file */
166   dos_hdr = load_pe_image(filename);
167   if (dos_hdr == NULL)
168     {
169       fprintf(stderr, "%s: %s: could not load PE image\n",
170               program_name, filename);
171       return 1;
172     }
173
174   nt_hdr32 = (PIMAGE_NT_HEADERS32) ((char *) dos_hdr + dos_hdr->e_lfanew);
175   nt_hdr64 = (PIMAGE_NT_HEADERS64) nt_hdr32;
176   
177   if (nt_hdr32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
178     exp_rva = nt_hdr32->OptionalHeader.DataDirectory[0].VirtualAddress;
179     exp_size = nt_hdr32->OptionalHeader.DataDirectory[0].Size;
180   }else{
181     exp_rva = nt_hdr64->OptionalHeader.DataDirectory[0].VirtualAddress;
182     exp_size = nt_hdr64->OptionalHeader.DataDirectory[0].Size;
183   }
184
185   if (verbose)
186     {
187       for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
188         {
189           section = IMAGE_SECTION_HDR(i);
190           printf("; %-8.8s: RVA: %08x, File offset: %08x\n",
191                  section->Name,
192                  section->VirtualAddress,
193                  section->PointerToRawData);
194         }
195     }
196
197   /* Look for export section */
198   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
199     {
200       section = IMAGE_SECTION_HDR(i);
201       if (memcmp(section->Name, exp_sign, sizeof(exp_sign)) == 0)
202         dump_exports(section->VirtualAddress, exp_size);
203       else if ((exp_rva >= section->VirtualAddress) && 
204           (exp_rva < (section->VirtualAddress + section->SizeOfRawData)))
205         dump_exports(exp_rva, exp_size);
206     }
207
208   free(dos_hdr);
209   return 0;
210 }
211
212 /* dump exported symbols on stdout */
213 void
214 dump_exports(DWORD exports_rva, DWORD exports_size)
215 {
216   PIMAGE_SECTION_HEADER section;
217   PIMAGE_EXPORT_DIRECTORY exports;
218   char *export_name;
219   WORD *ordinal_table;
220   DWORD *name_table;
221   DWORD *function_table;
222   int i;
223   static int first = 1;
224
225   section = find_section(exports_rva);
226
227   if (verbose)
228     printf("; Reading exports from section: %s\n",
229             section->Name);
230
231   exports = RVA_TO_PTR(exports_rva, PIMAGE_EXPORT_DIRECTORY);
232
233   /* set up various pointers */
234   export_name = RVA_TO_PTR(exports->Name,char*);
235   ordinal_table = RVA_TO_PTR(exports->AddressOfNameOrdinals,WORD*);
236   name_table = RVA_TO_PTR(exports->AddressOfNames,DWORD*);
237   function_table = RVA_TO_PTR(exports->AddressOfFunctions,void*);
238
239   if (verbose)
240     {
241       printf("; Export table: %s\n", export_name);
242       printf("; Ordinal base: %d\n", exports->Base);
243       printf("; Ordinal table RVA: %08x\n",
244               exports->AddressOfNameOrdinals);
245       printf("; Name table RVA: %07x\n",
246              exports->AddressOfNames);
247       printf("; Export address table RVA: %08x\n",
248              exports->AddressOfFunctions);
249     }
250
251   if (first)
252     {
253       printf("LIBRARY %s\n", export_name);
254       printf("EXPORTS\n");
255       first = 0;
256     }
257   else
258       printf("; LIBRARY %s\n", export_name);
259
260   for (i = 0; i < exports->NumberOfNames; i++)
261     {
262       dump_symbol(RVA_TO_PTR(name_table[i],char*),
263                   ordinal_table[i] + exports->Base,
264                   function_table[ordinal_table[i]]);
265       
266       int f_off = ordinal_table[i];
267       
268       if(function_table[f_off] >= exports_rva && function_table[f_off] < (exports_rva + exports_size) && verbose) {
269         printf(" ; Forwarder (%s)", RVA_TO_PTR(function_table[f_off], char*));
270       }
271       
272       printf("\n");
273     }
274
275   for (i = 0; i < exports->NumberOfFunctions; i++)
276     {
277       if ( (function_table[i] >= exports_rva) && 
278            (function_table[i] < (exports_rva + exports_size)))
279         {
280           int name_present = 0, n;
281           
282           for(n = 0; n < exports->NumberOfNames; n++) {
283             if(ordinal_table[n] == i) {
284               name_present = 1;
285               break;
286             }
287           }
288           
289           if(!name_present) {
290             dump_symbol(strchr(RVA_TO_PTR(function_table[i],char*), '.')+1, i + exports->Base, function_table[i]);
291             
292             printf(" ; WARNING: Symbol name guessed from forwarder (%s)\n", RVA_TO_PTR(function_table[i], char*));
293           }
294         }
295     }
296 }
297
298 static void
299 dump_symbol(char *name, int ord, DWORD rva)
300 {
301   char s[256];
302   str_tree *symbol = str_tree_find(symbols, name);
303   /* if a symbol was found, emit size of stack */
304   if (symbol)
305     sprintf(s, "%s@%"INT_PTR_FORMAT, name, (INT_PTR) symbol->extra);
306   else
307     sprintf(s, "%s", name);
308   
309   /* output ordinal */
310   if (ordinal_flag)
311     printf("%-24s\t@%d", s, ord);
312   else
313     printf("%s", s);
314
315   {
316     PIMAGE_SECTION_HEADER s=find_section(rva);
317
318     /* Stupid msvc doesn't have .bss section, it spews uninitilized data
319      * to no section
320      */
321     if (!s) { printf(" DATA"); if (verbose) printf (" ; no section"); }
322     else
323     {
324       if (!(s->Characteristics&IMAGE_SCN_CNT_CODE)) printf(" DATA");
325       if (verbose) printf (" ; %.8s",s->Name);
326     }
327   }
328
329   if (verbose)
330     printf(" ; RVA %08x", rva);
331 }
332
333 /* get section to which rva points */
334 PIMAGE_SECTION_HEADER
335 find_section(DWORD rva)
336 {
337   int i;
338   PIMAGE_SECTION_HEADER section = IMAGE_SECTION_HDR(0);
339   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++, section++)
340     if ((rva >= section->VirtualAddress) && 
341         (rva <= (section->VirtualAddress + section->SizeOfRawData)))
342       return section;
343   return NULL;  
344 }
345
346 /* convert rva to pointer into loaded file */
347 void *
348 rva_to_ptr(DWORD rva)
349 {
350   PIMAGE_SECTION_HEADER section = find_section(rva);
351   if (section->PointerToRawData == 0)
352     return NULL;
353   else
354     return ((char *) dos_hdr + rva - (section->VirtualAddress - section->PointerToRawData));
355 }
356
357 /* Load a portable executable into memory */
358 PIMAGE_DOS_HEADER
359 load_pe_image(const char *filename)
360 {
361 #ifdef _MSC_VER
362   struct _stat32 st;
363 #else
364   struct stat st;
365 #endif
366   PIMAGE_DOS_HEADER phdr;
367   FILE *f;
368   
369   if (stat(filename, &st) == -1)
370     {
371       perror("stat");
372       return NULL;
373     }
374
375   phdr = (PIMAGE_DOS_HEADER) xmalloc(st.st_size);
376   
377   f = fopen(filename, "rb");
378
379   if (f == NULL)
380     {
381       perror("fopen");
382       free(phdr);
383       return NULL;
384     }
385
386   if (fread(phdr, st.st_size, 1, f) != 1)
387     {
388       perror("fread");
389       free(phdr);
390       phdr = NULL;
391     }
392   else if (memcmp(mz_sign, phdr, sizeof(mz_sign)) != 0)
393     {
394       fprintf(stderr, "No MZ signature\n");
395       free(phdr);
396       phdr = NULL;
397     }
398   else if (memcmp((char *) phdr + phdr->e_lfanew, pe_sign, sizeof(pe_sign)) != 0)
399     {
400       fprintf(stderr, "No PE signature\n");
401       free(phdr);
402       phdr = NULL;
403     }
404
405   fclose(f);
406   return phdr;
407 }
408
409 /* parse headers to build symbol tree */
410 void
411 parse_headers()
412 {
413 #ifdef _MSC_VER
414 #define popen _popen
415 #define pclose _pclose
416 #endif
417
418   str_list *header;
419   char *cpp_cmd;
420   int len;
421   FILE *f;
422
423   header = header_files;
424   if (!header)
425     return;
426
427   /* construct command line */
428   cpp_cmd = strdup(cpp);
429   if (cpp_cmd == NULL)
430     {
431       fprintf(stderr, "%s: out of memory\n", program_name);
432       exit(1);
433     }
434   len = strlen(cpp_cmd);
435
436   while (header)
437     {
438       const char *fullname = find_file(header->s);
439       int tmp;
440       if (fullname == NULL)
441         {
442           perror(header->s);
443           exit(1);
444         }
445       tmp = strlen(fullname) + 10;
446       cpp_cmd = realloc(cpp_cmd, len + tmp);
447       if (cpp_cmd == NULL)
448         {
449           fprintf(stderr, "%s: out of memory\n", program_name);
450           exit(1);
451         }
452       if (!header->next)
453         sprintf(cpp_cmd + len, " %s", fullname);
454       else
455         sprintf(cpp_cmd + len, " -include %s", fullname);
456       len += tmp;
457       header = header->next;
458     }
459   cpp_cmd[len] = '\0';
460
461   if (verbose)
462     printf("; %s\n", cpp_cmd);
463
464   /* Run preprocessor.
465      Note: CRTDLL messes up stdout when popen is called so
466      if you try to pipe output through another program
467      with | it will hang. Redirect it to a file instead
468      and pass that file to the program (more,less or whatever).
469      This does not apply to cygwin.
470   */
471   f = popen(cpp_cmd, "r");
472   free(cpp_cmd);
473   if (f == NULL)
474     {
475       fprintf(stderr, "%s: %s: could not execute\n", program_name, cpp_cmd);
476       exit(1);
477     }
478   yyin = f;
479   yyparse();
480   pclose(f);
481 }
482
483 /* allocate memory; abort on failure */
484 static void 
485 *xmalloc(size_t count)
486 {
487   void *p = malloc(count);
488   if (p == NULL)
489     {
490       fprintf(stderr, "%s: out of memory\n", program_name);
491       exit(1);
492     }
493   return p;
494 }
495
496 /* add string to end of list */
497 static void
498 str_list_add(str_list **head, const char *s)
499 {
500   str_list *node = xmalloc(sizeof(str_list));
501   node->s = strdup(s);
502   node->next = NULL;
503   if (!*head)
504     {
505       *head = node;
506     }
507   else
508     {
509       str_list *p = *head;
510       while (p->next)
511         p = p->next;
512       p->next = node;
513     }
514 }
515
516 /* find a file in include path */
517 static const char *
518 find_file(const char *name)
519 {
520   static char fullname[PATH_MAX];
521   FILE *f = fopen(name, "r");
522   str_list *path = inc_path;
523   if (f != NULL)
524     {
525       fclose(f);
526       return name;
527     }
528   while (path)
529     {
530       strcpy(fullname, path->s);
531       strcat(fullname, "/");
532       strcat(fullname, name);
533       f = fopen(fullname, "r");
534       if (f != NULL)
535         {
536           fclose(f);
537           return fullname;
538         }
539       path = path->next;
540     }
541   errno = ENOENT;
542   return NULL;
543 }
544
545 /* add a environment-style path list to list of include paths */
546 static void
547 add_path_list(char *path)
548 {
549   char *p = path;
550   if (!p)
551     return;
552   while (*p)
553     {
554       if (*p == PATH_SEPARATOR)
555         {
556           *p = '\0';
557           str_list_add(&inc_path, path);
558           path = p + 1;
559           *p = PATH_SEPARATOR;
560         }
561       p++;
562     }
563   if (p[-1] != PATH_SEPARATOR)
564     str_list_add(&inc_path, path);
565 }