OSDN Git Service

310f48a4d5368153ac798c324fa13a06cb595928
[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) ((PIMAGE_SECTION_HEADER) ((char *) nt_hdr32 + \
22                                     4 + sizeof(IMAGE_FILE_HEADER) + \
23                                     nt_hdr32->FileHeader.SizeOfOptionalHeader + \
24                                     n * sizeof(IMAGE_SECTION_HEADER)))
25
26 /* convert relative virtual address to a useable pointer */
27 #define RVA_TO_PTR(rva,type) ((type) rva_to_ptr((DWORD) rva))
28
29 typedef struct str_list {
30   char *s;
31   struct str_list *next;
32 } str_list;
33
34 extern void yyparse(void);
35
36 static void *xmalloc(size_t count);
37 static void add_path_list(char *path);
38 static const char *find_file(const char *name);
39 static void str_list_add(str_list **head, const char *s);
40 static void parse_headers();
41 static void dump_symbol(char *name, int ord, DWORD rva);
42
43 static const char mz_sign[2] = {'M','Z'};
44 static const char pe_sign[4] = {'P','E','\0','\0'};
45 static const char exp_sign[6] = {'.','e','d','a','t','a'};
46
47 static PIMAGE_DOS_HEADER dos_hdr;
48 static PIMAGE_NT_HEADERS32 nt_hdr32;
49 static PIMAGE_NT_HEADERS64 nt_hdr64;
50
51 static char *filename = NULL;
52 static char *program_name;
53 static char *cpp = "gcc -E -xc-header";
54
55 str_tree *symbols = NULL;
56 static str_list *inc_path = NULL;
57 static str_list *header_files = NULL;
58
59 static int verbose = 0;
60 static int ordinal_flag = 0;
61
62 extern FILE *yyin;
63
64 int
65 main(int argc, char *argv[])
66 {
67   PIMAGE_SECTION_HEADER section;
68   DWORD exp_rva, exp_size;
69   int i;
70 #if defined(_WIN32) && !defined(_WIN64)
71
72   /* If running on 64-bit Windows and built as a 32-bit process, try
73    * disable Wow64 file system redirection, so that we can open DLLs
74    * in the real system32 folder if requested.
75    */
76
77   PVOID old_redirection;
78
79   HMODULE kernel32;
80
81   extern __declspec(dllimport) HMODULE __stdcall GetModuleHandleA(char *name);
82   extern __declspec(dllimport) PVOID __stdcall GetProcAddress(HMODULE module, char *name);
83
84   BOOL (__stdcall *pWow64DisableWow64FsRedirection) (PVOID *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   dos_hdr = load_pe_image(filename);
165   if (dos_hdr == 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 = (PIMAGE_NT_HEADERS32) ((char *) dos_hdr + dos_hdr->e_lfanew);
173   nt_hdr64 = (PIMAGE_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(DWORD exports_rva, DWORD exports_size)
213 {
214   PIMAGE_SECTION_HEADER section;
215   PIMAGE_EXPORT_DIRECTORY exports;
216   char *export_name;
217   WORD *ordinal_table;
218   DWORD *name_table;
219   DWORD *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, PIMAGE_EXPORT_DIRECTORY);
230
231   /* set up various pointers */
232   export_name = RVA_TO_PTR(exports->Name,char*);
233   ordinal_table = RVA_TO_PTR(exports->AddressOfNameOrdinals,WORD*);
234   name_table = RVA_TO_PTR(exports->AddressOfNames,DWORD*);
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, DWORD 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     PIMAGE_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 PIMAGE_SECTION_HEADER
333 find_section(DWORD rva)
334 {
335   int i;
336   PIMAGE_SECTION_HEADER section = IMAGE_SECTION_HDR(0);
337   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++, section++)
338     if ((rva >= section->VirtualAddress) && 
339         (rva <= (section->VirtualAddress + section->SizeOfRawData)))
340       return section;
341   return NULL;  
342 }
343
344 /* convert rva to pointer into loaded file */
345 void *
346 rva_to_ptr(DWORD rva)
347 {
348   PIMAGE_SECTION_HEADER section = find_section(rva);
349   if (section->PointerToRawData == 0)
350     return NULL;
351   else
352     return ((char *) dos_hdr + rva - (section->VirtualAddress - section->PointerToRawData));
353 }
354
355 /* Load a portable executable into memory */
356 PIMAGE_DOS_HEADER
357 load_pe_image(const char *filename)
358 {
359 #ifdef _MSC_VER
360   struct _stat32 st;
361 #else
362   struct stat st;
363 #endif
364   PIMAGE_DOS_HEADER phdr;
365   FILE *f;
366   
367   if (stat(filename, &st) == -1)
368     {
369       perror("stat");
370       return NULL;
371     }
372
373   phdr = (PIMAGE_DOS_HEADER) xmalloc(st.st_size);
374   
375   f = fopen(filename, "rb");
376
377   if (f == NULL)
378     {
379       perror("fopen");
380       free(phdr);
381       return NULL;
382     }
383
384   if (fread(phdr, st.st_size, 1, f) != 1)
385     {
386       perror("fread");
387       free(phdr);
388       phdr = NULL;
389     }
390   else if (memcmp(mz_sign, phdr, sizeof(mz_sign)) != 0)
391     {
392       fprintf(stderr, "No MZ signature\n");
393       free(phdr);
394       phdr = NULL;
395     }
396   else if (memcmp((char *) phdr + phdr->e_lfanew, pe_sign, sizeof(pe_sign)) != 0)
397     {
398       fprintf(stderr, "No PE signature\n");
399       free(phdr);
400       phdr = NULL;
401     }
402
403   fclose(f);
404   return phdr;
405 }
406
407 /* parse headers to build symbol tree */
408 void
409 parse_headers()
410 {
411 #ifdef _MSC_VER
412 #define popen _popen
413 #define pclose _pclose
414 #endif
415
416   str_list *header;
417   char *cpp_cmd;
418   int len;
419   FILE *f;
420
421   header = header_files;
422   if (!header)
423     return;
424
425   /* construct command line */
426   cpp_cmd = strdup(cpp);
427   if (cpp_cmd == NULL)
428     {
429       fprintf(stderr, "%s: out of memory\n", program_name);
430       exit(1);
431     }
432   len = strlen(cpp_cmd);
433
434   while (header)
435     {
436       const char *fullname = find_file(header->s);
437       int tmp;
438       if (fullname == NULL)
439         {
440           perror(header->s);
441           exit(1);
442         }
443       tmp = strlen(fullname) + 10;
444       cpp_cmd = realloc(cpp_cmd, len + tmp);
445       if (cpp_cmd == NULL)
446         {
447           fprintf(stderr, "%s: out of memory\n", program_name);
448           exit(1);
449         }
450       if (!header->next)
451         sprintf(cpp_cmd + len, " %s", fullname);
452       else
453         sprintf(cpp_cmd + len, " -include %s", fullname);
454       len += tmp;
455       header = header->next;
456     }
457   cpp_cmd[len] = '\0';
458
459   if (verbose)
460     printf("; %s\n", cpp_cmd);
461
462   /* Run preprocessor.
463      Note: CRTDLL messes up stdout when popen is called so
464      if you try to pipe output through another program
465      with | it will hang. Redirect it to a file instead
466      and pass that file to the program (more,less or whatever).
467      This does not apply to cygwin.
468   */
469   f = popen(cpp_cmd, "r");
470   free(cpp_cmd);
471   if (f == NULL)
472     {
473       fprintf(stderr, "%s: %s: could not execute\n", program_name, cpp_cmd);
474       exit(1);
475     }
476   yyin = f;
477   yyparse();
478   pclose(f);
479 }
480
481 /* allocate memory; abort on failure */
482 static void 
483 *xmalloc(size_t count)
484 {
485   void *p = malloc(count);
486   if (p == NULL)
487     {
488       fprintf(stderr, "%s: out of memory\n", program_name);
489       exit(1);
490     }
491   return p;
492 }
493
494 /* add string to end of list */
495 static void
496 str_list_add(str_list **head, const char *s)
497 {
498   str_list *node = xmalloc(sizeof(str_list));
499   node->s = strdup(s);
500   node->next = NULL;
501   if (!*head)
502     {
503       *head = node;
504     }
505   else
506     {
507       str_list *p = *head;
508       while (p->next)
509         p = p->next;
510       p->next = node;
511     }
512 }
513
514 /* find a file in include path */
515 static const char *
516 find_file(const char *name)
517 {
518   static char fullname[PATH_MAX];
519   FILE *f = fopen(name, "r");
520   str_list *path = inc_path;
521   if (f != NULL)
522     {
523       fclose(f);
524       return name;
525     }
526   while (path)
527     {
528       strcpy(fullname, path->s);
529       strcat(fullname, "/");
530       strcat(fullname, name);
531       f = fopen(fullname, "r");
532       if (f != NULL)
533         {
534           fclose(f);
535           return fullname;
536         }
537       path = path->next;
538     }
539   errno = ENOENT;
540   return NULL;
541 }
542
543 /* add a environment-style path list to list of include paths */
544 static void
545 add_path_list(char *path)
546 {
547   char *p = path;
548   if (!p)
549     return;
550   while (*p)
551     {
552       if (*p == PATH_SEPARATOR)
553         {
554           *p = '\0';
555           str_list_add(&inc_path, path);
556           path = p + 1;
557           *p = PATH_SEPARATOR;
558         }
559       p++;
560     }
561   if (p[-1] != PATH_SEPARATOR)
562     str_list_add(&inc_path, path);
563 }