OSDN Git Service

be80aa6251907b9b3c6f83dd5700a8bd16d3e451
[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   name_table = RVA_TO_PTR(exports->AddressOfNames, uint32_t *);
233   ordinal_table = RVA_TO_PTR(exports->AddressOfNameOrdinals, uint16_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   return (section->PointerToRawData != 0)
348     ? (char *)(dos_hdr) + rva - section->VirtualAddress + section->PointerToRawData
349     : NULL;
350 }
351
352 /* Load a portable executable into memory */
353 IMAGE_DOS_HEADER *load_pe_image(const char *filename)
354 {
355 #ifdef _MSC_VER
356   struct _stat32 st;
357 #else
358   struct stat st;
359 #endif
360   IMAGE_DOS_HEADER *phdr;
361   FILE *f;
362
363   if (stat(filename, &st) == -1)
364     {
365       perror("stat");
366       return NULL;
367     }
368
369   phdr = (IMAGE_DOS_HEADER *) xmalloc(st.st_size);
370
371   f = fopen(filename, "rb");
372
373   if (f == NULL)
374     {
375       perror("fopen");
376       free(phdr);
377       return NULL;
378     }
379
380   if (fread(phdr, st.st_size, 1, f) != 1)
381     {
382       perror("fread");
383       free(phdr);
384       phdr = NULL;
385     }
386   else if (memcmp(mz_sign, phdr, sizeof(mz_sign)) != 0)
387     {
388       fprintf(stderr, "No MZ signature\n");
389       free(phdr);
390       phdr = NULL;
391     }
392   else if (memcmp((char *) phdr + phdr->e_lfanew, pe_sign, sizeof(pe_sign)) != 0)
393     {
394       fprintf(stderr, "No PE signature\n");
395       free(phdr);
396       phdr = NULL;
397     }
398
399   fclose(f);
400   return phdr;
401 }
402
403 /* parse headers to build symbol tree */
404 void
405 parse_headers()
406 {
407 #ifdef _MSC_VER
408 #define popen _popen
409 #define pclose _pclose
410 #endif
411
412   str_list *header;
413   char *cpp_cmd;
414   int len;
415   FILE *f;
416
417   header = header_files;
418   if (!header)
419     return;
420
421   /* construct command line */
422   cpp_cmd = strdup(cpp);
423   if (cpp_cmd == NULL)
424     {
425       fprintf(stderr, "%s: out of memory\n", program_name);
426       exit(1);
427     }
428   len = strlen(cpp_cmd);
429
430   while (header)
431     {
432       const char *fullname = find_file(header->s);
433       int tmp;
434       if (fullname == NULL)
435         {
436           perror(header->s);
437           exit(1);
438         }
439       tmp = strlen(fullname) + 10;
440       cpp_cmd = realloc(cpp_cmd, len + tmp);
441       if (cpp_cmd == NULL)
442         {
443           fprintf(stderr, "%s: out of memory\n", program_name);
444           exit(1);
445         }
446       if (!header->next)
447         sprintf(cpp_cmd + len, " %s", fullname);
448       else
449         sprintf(cpp_cmd + len, " -include %s", fullname);
450       len += tmp;
451       header = header->next;
452     }
453   cpp_cmd[len] = '\0';
454
455   if (verbose)
456     printf("; %s\n", cpp_cmd);
457
458   /* Run preprocessor.
459      Note: CRTDLL messes up stdout when popen is called so
460      if you try to pipe output through another program
461      with | it will hang. Redirect it to a file instead
462      and pass that file to the program (more,less or whatever).
463      This does not apply to cygwin.
464   */
465   f = popen(cpp_cmd, "r");
466   free(cpp_cmd);
467   if (f == NULL)
468     {
469       fprintf(stderr, "%s: %s: could not execute\n", program_name, cpp_cmd);
470       exit(1);
471     }
472   yyin = f;
473   yyparse();
474   pclose(f);
475 }
476
477 /* allocate memory; abort on failure */
478 static void *xmalloc(size_t count)
479 {
480   void *p = malloc(count);
481   if (p == NULL)
482     {
483       fprintf(stderr, "%s: out of memory\n", program_name);
484       exit(1);
485     }
486   return p;
487 }
488
489 /* add string to end of list */
490 static void
491 str_list_add(str_list **head, const char *s)
492 {
493   str_list *node = xmalloc(sizeof(str_list));
494   node->s = strdup(s);
495   node->next = NULL;
496   if (!*head)
497     {
498       *head = node;
499     }
500   else
501     {
502       str_list *p = *head;
503       while (p->next)
504         p = p->next;
505       p->next = node;
506     }
507 }
508
509 /* find a file in include path */
510 static const char *
511 find_file(const char *name)
512 {
513   static char fullname[PATH_MAX];
514   FILE *f = fopen(name, "r");
515   str_list *path = inc_path;
516   if (f != NULL)
517     {
518       fclose(f);
519       return name;
520     }
521   while (path)
522     {
523       strcpy(fullname, path->s);
524       strcat(fullname, "/");
525       strcat(fullname, name);
526       f = fopen(fullname, "r");
527       if (f != NULL)
528         {
529           fclose(f);
530           return fullname;
531         }
532       path = path->next;
533     }
534   errno = ENOENT;
535   return NULL;
536 }
537
538 /* add a environment-style path list to list of include paths */
539 static void
540 add_path_list(char *path)
541 {
542   char *p = path;
543   if (!p)
544     return;
545   while (*p)
546     {
547       if (*p == PATH_SEPARATOR)
548         {
549           *p = '\0';
550           str_list_add(&inc_path, path);
551           path = p + 1;
552           *p = PATH_SEPARATOR;
553         }
554       p++;
555     }
556   if (p[-1] != PATH_SEPARATOR)
557     str_list_add(&inc_path, path);
558 }