OSDN Git Service

Eliminate Microsoft inspired obfuscated typedef insanity.
[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("PExports %s; Originally written 1998, Anders Norlander\n"
145              "Updated 1999, Paul Sokolovsky, 2008, Tor Lillqvist, 2013, Keith Marshall\n"
146              "Copyright (C) 1998, 1999, 2008, 2013, MinGW.org Project\n\n"
147              "This program is free software; you may redistribute it under the terms of\n"
148              "the GNU General Public License.  This program has absolutely no warranty.\n"
149
150              "\nUsage: %s [-v] [-o] [-h header] [-p preprocessor] dll\n"
151              "  -h\tparse header\n"
152              "  -o\tprint ordinals\n"
153              "  -p\tset preprocessor program\n"
154              "  -v\tverbose mode\n"
155              "\nReport bugs as directed at %s\n",
156              PACKAGE_VERSION_STRING, program_name, PACKAGE_BUG_REPORT);
157       return 1;
158     }
159
160   /* parse headers and build symbol tree */
161   parse_headers();
162
163   /* load file */
164   if( (dos_hdr = load_pe_image(filename)) == NULL )
165     {
166       fprintf(stderr, "%s: %s: could not load PE image\n",
167               program_name, filename);
168       return 1;
169     }
170
171   nt_hdr32 = (IMAGE_NT_HEADERS32 *) ((char *) dos_hdr + dos_hdr->e_lfanew);
172   nt_hdr64 = (IMAGE_NT_HEADERS64 *) nt_hdr32;
173
174   if (nt_hdr32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
175     exp_rva = nt_hdr32->OptionalHeader.DataDirectory[0].VirtualAddress;
176     exp_size = nt_hdr32->OptionalHeader.DataDirectory[0].Size;
177   }else{
178     exp_rva = nt_hdr64->OptionalHeader.DataDirectory[0].VirtualAddress;
179     exp_size = nt_hdr64->OptionalHeader.DataDirectory[0].Size;
180   }
181
182   if (verbose)
183     {
184       for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
185         {
186           section = IMAGE_SECTION_HDR(i);
187           printf("; %-8.8s: RVA: %08x, File offset: %08x\n",
188                  section->Name,
189                  section->VirtualAddress,
190                  section->PointerToRawData);
191         }
192     }
193
194   /* Look for export section */
195   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
196     {
197       section = IMAGE_SECTION_HDR(i);
198       if (memcmp(section->Name, exp_sign, sizeof(exp_sign)) == 0)
199         dump_exports(section->VirtualAddress, exp_size);
200       else if ((exp_rva >= section->VirtualAddress) &&
201           (exp_rva < (section->VirtualAddress + section->SizeOfRawData)))
202         dump_exports(exp_rva, exp_size);
203     }
204
205   free(dos_hdr);
206   return 0;
207 }
208
209 /* dump exported symbols on stdout */
210 void
211 dump_exports(uint32_t exports_rva, uint32_t exports_size)
212 {
213   IMAGE_SECTION_HEADER *section;
214   IMAGE_EXPORT_DIRECTORY *exports;
215   char *export_name;
216   uint16_t *ordinal_table;
217   uint32_t *name_table;
218   uint32_t *function_table;
219   int i;
220   static int first = 1;
221
222   section = find_section(exports_rva);
223
224   if (verbose)
225     printf("; Reading exports from section: %s\n",
226             section->Name);
227
228   exports = RVA_TO_PTR(exports_rva, IMAGE_EXPORT_DIRECTORY *);
229
230   /* set up various pointers */
231   export_name = RVA_TO_PTR(exports->Name,char*);
232   ordinal_table = RVA_TO_PTR(exports->AddressOfNameOrdinals, uint16_t *);
233   name_table = RVA_TO_PTR(exports->AddressOfNames, uint32_t *);
234   function_table = RVA_TO_PTR(exports->AddressOfFunctions,void*);
235
236   if (verbose)
237     {
238       printf("; Export table: %s\n", export_name);
239       printf("; Ordinal base: %d\n", exports->Base);
240       printf("; Ordinal table RVA: %08x\n",
241               exports->AddressOfNameOrdinals);
242       printf("; Name table RVA: %07x\n",
243              exports->AddressOfNames);
244       printf("; Export address table RVA: %08x\n",
245              exports->AddressOfFunctions);
246     }
247
248   if (first)
249     {
250       printf("LIBRARY %s\n", export_name);
251       printf("EXPORTS\n");
252       first = 0;
253     }
254   else
255       printf("; LIBRARY %s\n", export_name);
256
257   for (i = 0; i < exports->NumberOfNames; i++)
258     {
259       dump_symbol(RVA_TO_PTR(name_table[i],char*),
260                   ordinal_table[i] + exports->Base,
261                   function_table[ordinal_table[i]]);
262
263       int f_off = ordinal_table[i];
264
265       if(function_table[f_off] >= exports_rva && function_table[f_off] < (exports_rva + exports_size) && verbose) {
266         printf(" ; Forwarder (%s)", RVA_TO_PTR(function_table[f_off], char*));
267       }
268
269       printf("\n");
270     }
271
272   for (i = 0; i < exports->NumberOfFunctions; i++)
273     {
274       if ( (function_table[i] >= exports_rva) &&
275            (function_table[i] < (exports_rva + exports_size)))
276         {
277           int name_present = 0, n;
278
279           for(n = 0; n < exports->NumberOfNames; n++) {
280             if(ordinal_table[n] == i) {
281               name_present = 1;
282               break;
283             }
284           }
285
286           if(!name_present) {
287             dump_symbol(strchr(RVA_TO_PTR(function_table[i],char*), '.')+1, i + exports->Base, function_table[i]);
288
289             printf(" ; WARNING: Symbol name guessed from forwarder (%s)\n", RVA_TO_PTR(function_table[i], char*));
290           }
291         }
292     }
293 }
294
295 static void
296 dump_symbol(char *name, int ord, uint32_t rva)
297 {
298   char s[256];
299   str_tree *symbol = str_tree_find(symbols, name);
300   /* if a symbol was found, emit size of stack */
301   if (symbol)
302     sprintf(s, "%s@%"PRIdPTR, name, (intptr_t)(symbol->extra));
303   else
304     sprintf(s, "%s", name);
305
306   /* output ordinal */
307   if (ordinal_flag)
308     printf("%-24s\t@%d", s, ord);
309   else
310     printf("%s", s);
311
312   {
313     IMAGE_SECTION_HEADER *s = find_section(rva);
314
315     /* Stupid msvc doesn't have .bss section, it spews uninitilized data
316      * to no section
317      */
318     if (!s) { printf(" DATA"); if (verbose) printf (" ; no section"); }
319     else
320     {
321       if (!(s->Characteristics&IMAGE_SCN_CNT_CODE)) printf(" DATA");
322       if (verbose) printf (" ; %.8s",s->Name);
323     }
324   }
325
326   if (verbose)
327     printf(" ; RVA %08x", rva);
328 }
329
330 /* get section to which rva points */
331 IMAGE_SECTION_HEADER *find_section(uint32_t rva)
332 {
333   int i;
334   IMAGE_SECTION_HEADER *section = IMAGE_SECTION_HDR(0);
335   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++, section++)
336     if ((rva >= section->VirtualAddress) &&
337         (rva <= (section->VirtualAddress + section->SizeOfRawData)))
338       return section;
339   return NULL;
340 }
341
342 /* convert rva to pointer into loaded file */
343 void *
344 rva_to_ptr(uint32_t rva)
345 {
346   IMAGE_SECTION_HEADER *section = find_section(rva);
347   if (section->PointerToRawData == 0)
348     return NULL;
349   else
350     return ((char *) dos_hdr + rva - (section->VirtualAddress - section->PointerToRawData));
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 }