OSDN Git Service

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