OSDN Git Service

186497c409f2d01f97c10da791b199242cd803f6
[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 %d.%d Copyright 1998, Anders Norlander\n"
145              "Changed 1999, Paul Sokolovsky\n"
146              "Changed 2008, Tor Lillqvist\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 to anorland@hem2.passagen.se,\n"
156              "Paul.Sokolovsky@technologist.com\n"
157              "or tml@iki.fi\n",
158              VER_MAJOR, VER_MINOR,
159              program_name);
160       return 1;
161     }
162
163   /* parse headers and build symbol tree */
164   parse_headers();
165
166   /* load file */
167   dos_hdr = load_pe_image(filename);
168   if (dos_hdr == NULL)
169     {
170       fprintf(stderr, "%s: %s: could not load PE image\n",
171               program_name, filename);
172       return 1;
173     }
174
175   nt_hdr32 = (PIMAGE_NT_HEADERS32) ((char *) dos_hdr + dos_hdr->e_lfanew);
176   nt_hdr64 = (PIMAGE_NT_HEADERS64) nt_hdr32;
177   
178   if (nt_hdr32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
179     exp_rva = nt_hdr32->OptionalHeader.DataDirectory[0].VirtualAddress;
180     exp_size = nt_hdr32->OptionalHeader.DataDirectory[0].Size;
181   }else{
182     exp_rva = nt_hdr64->OptionalHeader.DataDirectory[0].VirtualAddress;
183     exp_size = nt_hdr64->OptionalHeader.DataDirectory[0].Size;
184   }
185
186   if (verbose)
187     {
188       for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
189         {
190           section = IMAGE_SECTION_HDR(i);
191           printf("; %-8.8s: RVA: %08x, File offset: %08x\n",
192                  section->Name,
193                  section->VirtualAddress,
194                  section->PointerToRawData);
195         }
196     }
197
198   /* Look for export section */
199   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++)
200     {
201       section = IMAGE_SECTION_HDR(i);
202       if (memcmp(section->Name, exp_sign, sizeof(exp_sign)) == 0)
203         dump_exports(section->VirtualAddress, exp_size);
204       else if ((exp_rva >= section->VirtualAddress) && 
205           (exp_rva < (section->VirtualAddress + section->SizeOfRawData)))
206         dump_exports(exp_rva, exp_size);
207     }
208
209   free(dos_hdr);
210   return 0;
211 }
212
213 /* dump exported symbols on stdout */
214 void
215 dump_exports(DWORD exports_rva, DWORD exports_size)
216 {
217   PIMAGE_SECTION_HEADER section;
218   PIMAGE_EXPORT_DIRECTORY exports;
219   char *export_name;
220   WORD *ordinal_table;
221   DWORD *name_table;
222   DWORD *function_table;
223   int i;
224   static int first = 1;
225
226   section = find_section(exports_rva);
227
228   if (verbose)
229     printf("; Reading exports from section: %s\n",
230             section->Name);
231
232   exports = RVA_TO_PTR(exports_rva, PIMAGE_EXPORT_DIRECTORY);
233
234   /* set up various pointers */
235   export_name = RVA_TO_PTR(exports->Name,char*);
236   ordinal_table = RVA_TO_PTR(exports->AddressOfNameOrdinals,WORD*);
237   name_table = RVA_TO_PTR(exports->AddressOfNames,DWORD*);
238   function_table = RVA_TO_PTR(exports->AddressOfFunctions,void*);
239
240   if (verbose)
241     {
242       printf("; Export table: %s\n", export_name);
243       printf("; Ordinal base: %d\n", exports->Base);
244       printf("; Ordinal table RVA: %08x\n",
245               exports->AddressOfNameOrdinals);
246       printf("; Name table RVA: %07x\n",
247              exports->AddressOfNames);
248       printf("; Export address table RVA: %08x\n",
249              exports->AddressOfFunctions);
250     }
251
252   if (first)
253     {
254       printf("LIBRARY %s\n", export_name);
255       printf("EXPORTS\n");
256       first = 0;
257     }
258   else
259       printf("; LIBRARY %s\n", export_name);
260
261   for (i = 0; i < exports->NumberOfNames; i++)
262     {
263       dump_symbol(RVA_TO_PTR(name_table[i],char*),
264                   ordinal_table[i] + exports->Base,
265                   function_table[ordinal_table[i]]);
266       
267       int f_off = ordinal_table[i];
268       
269       if(function_table[f_off] >= exports_rva && function_table[f_off] < (exports_rva + exports_size) && verbose) {
270         printf(" ; Forwarder (%s)", RVA_TO_PTR(function_table[f_off], char*));
271       }
272       
273       printf("\n");
274     }
275
276   for (i = 0; i < exports->NumberOfFunctions; i++)
277     {
278       if ( (function_table[i] >= exports_rva) && 
279            (function_table[i] < (exports_rva + exports_size)))
280         {
281           int name_present = 0, n;
282           
283           for(n = 0; n < exports->NumberOfNames; n++) {
284             if(ordinal_table[n] == i) {
285               name_present = 1;
286               break;
287             }
288           }
289           
290           if(!name_present) {
291             dump_symbol(strchr(RVA_TO_PTR(function_table[i],char*), '.')+1, i + exports->Base, function_table[i]);
292             
293             printf(" ; WARNING: Symbol name guessed from forwarder (%s)\n", RVA_TO_PTR(function_table[i], char*));
294           }
295         }
296     }
297 }
298
299 static void
300 dump_symbol(char *name, int ord, DWORD rva)
301 {
302   char s[256];
303   str_tree *symbol = str_tree_find(symbols, name);
304   /* if a symbol was found, emit size of stack */
305   if (symbol)
306     sprintf(s, "%s@%"PRIdPTR, name, (intptr_t)(symbol->extra));
307   else
308     sprintf(s, "%s", name);
309   
310   /* output ordinal */
311   if (ordinal_flag)
312     printf("%-24s\t@%d", s, ord);
313   else
314     printf("%s", s);
315
316   {
317     PIMAGE_SECTION_HEADER s=find_section(rva);
318
319     /* Stupid msvc doesn't have .bss section, it spews uninitilized data
320      * to no section
321      */
322     if (!s) { printf(" DATA"); if (verbose) printf (" ; no section"); }
323     else
324     {
325       if (!(s->Characteristics&IMAGE_SCN_CNT_CODE)) printf(" DATA");
326       if (verbose) printf (" ; %.8s",s->Name);
327     }
328   }
329
330   if (verbose)
331     printf(" ; RVA %08x", rva);
332 }
333
334 /* get section to which rva points */
335 PIMAGE_SECTION_HEADER
336 find_section(DWORD rva)
337 {
338   int i;
339   PIMAGE_SECTION_HEADER section = IMAGE_SECTION_HDR(0);
340   for (i = 0; i < nt_hdr32->FileHeader.NumberOfSections; i++, section++)
341     if ((rva >= section->VirtualAddress) && 
342         (rva <= (section->VirtualAddress + section->SizeOfRawData)))
343       return section;
344   return NULL;  
345 }
346
347 /* convert rva to pointer into loaded file */
348 void *
349 rva_to_ptr(DWORD rva)
350 {
351   PIMAGE_SECTION_HEADER section = find_section(rva);
352   if (section->PointerToRawData == 0)
353     return NULL;
354   else
355     return ((char *) dos_hdr + rva - (section->VirtualAddress - section->PointerToRawData));
356 }
357
358 /* Load a portable executable into memory */
359 PIMAGE_DOS_HEADER
360 load_pe_image(const char *filename)
361 {
362 #ifdef _MSC_VER
363   struct _stat32 st;
364 #else
365   struct stat st;
366 #endif
367   PIMAGE_DOS_HEADER phdr;
368   FILE *f;
369   
370   if (stat(filename, &st) == -1)
371     {
372       perror("stat");
373       return NULL;
374     }
375
376   phdr = (PIMAGE_DOS_HEADER) xmalloc(st.st_size);
377   
378   f = fopen(filename, "rb");
379
380   if (f == NULL)
381     {
382       perror("fopen");
383       free(phdr);
384       return NULL;
385     }
386
387   if (fread(phdr, st.st_size, 1, f) != 1)
388     {
389       perror("fread");
390       free(phdr);
391       phdr = NULL;
392     }
393   else if (memcmp(mz_sign, phdr, sizeof(mz_sign)) != 0)
394     {
395       fprintf(stderr, "No MZ signature\n");
396       free(phdr);
397       phdr = NULL;
398     }
399   else if (memcmp((char *) phdr + phdr->e_lfanew, pe_sign, sizeof(pe_sign)) != 0)
400     {
401       fprintf(stderr, "No PE signature\n");
402       free(phdr);
403       phdr = NULL;
404     }
405
406   fclose(f);
407   return phdr;
408 }
409
410 /* parse headers to build symbol tree */
411 void
412 parse_headers()
413 {
414 #ifdef _MSC_VER
415 #define popen _popen
416 #define pclose _pclose
417 #endif
418
419   str_list *header;
420   char *cpp_cmd;
421   int len;
422   FILE *f;
423
424   header = header_files;
425   if (!header)
426     return;
427
428   /* construct command line */
429   cpp_cmd = strdup(cpp);
430   if (cpp_cmd == NULL)
431     {
432       fprintf(stderr, "%s: out of memory\n", program_name);
433       exit(1);
434     }
435   len = strlen(cpp_cmd);
436
437   while (header)
438     {
439       const char *fullname = find_file(header->s);
440       int tmp;
441       if (fullname == NULL)
442         {
443           perror(header->s);
444           exit(1);
445         }
446       tmp = strlen(fullname) + 10;
447       cpp_cmd = realloc(cpp_cmd, len + tmp);
448       if (cpp_cmd == NULL)
449         {
450           fprintf(stderr, "%s: out of memory\n", program_name);
451           exit(1);
452         }
453       if (!header->next)
454         sprintf(cpp_cmd + len, " %s", fullname);
455       else
456         sprintf(cpp_cmd + len, " -include %s", fullname);
457       len += tmp;
458       header = header->next;
459     }
460   cpp_cmd[len] = '\0';
461
462   if (verbose)
463     printf("; %s\n", cpp_cmd);
464
465   /* Run preprocessor.
466      Note: CRTDLL messes up stdout when popen is called so
467      if you try to pipe output through another program
468      with | it will hang. Redirect it to a file instead
469      and pass that file to the program (more,less or whatever).
470      This does not apply to cygwin.
471   */
472   f = popen(cpp_cmd, "r");
473   free(cpp_cmd);
474   if (f == NULL)
475     {
476       fprintf(stderr, "%s: %s: could not execute\n", program_name, cpp_cmd);
477       exit(1);
478     }
479   yyin = f;
480   yyparse();
481   pclose(f);
482 }
483
484 /* allocate memory; abort on failure */
485 static void 
486 *xmalloc(size_t count)
487 {
488   void *p = malloc(count);
489   if (p == NULL)
490     {
491       fprintf(stderr, "%s: out of memory\n", program_name);
492       exit(1);
493     }
494   return p;
495 }
496
497 /* add string to end of list */
498 static void
499 str_list_add(str_list **head, const char *s)
500 {
501   str_list *node = xmalloc(sizeof(str_list));
502   node->s = strdup(s);
503   node->next = NULL;
504   if (!*head)
505     {
506       *head = node;
507     }
508   else
509     {
510       str_list *p = *head;
511       while (p->next)
512         p = p->next;
513       p->next = node;
514     }
515 }
516
517 /* find a file in include path */
518 static const char *
519 find_file(const char *name)
520 {
521   static char fullname[PATH_MAX];
522   FILE *f = fopen(name, "r");
523   str_list *path = inc_path;
524   if (f != NULL)
525     {
526       fclose(f);
527       return name;
528     }
529   while (path)
530     {
531       strcpy(fullname, path->s);
532       strcat(fullname, "/");
533       strcat(fullname, name);
534       f = fopen(fullname, "r");
535       if (f != NULL)
536         {
537           fclose(f);
538           return fullname;
539         }
540       path = path->next;
541     }
542   errno = ENOENT;
543   return NULL;
544 }
545
546 /* add a environment-style path list to list of include paths */
547 static void
548 add_path_list(char *path)
549 {
550   char *p = path;
551   if (!p)
552     return;
553   while (*p)
554     {
555       if (*p == PATH_SEPARATOR)
556         {
557           *p = '\0';
558           str_list_add(&inc_path, path);
559           path = p + 1;
560           *p = PATH_SEPARATOR;
561         }
562       p++;
563     }
564   if (p[-1] != PATH_SEPARATOR)
565     str_list_add(&inc_path, path);
566 }