OSDN Git Service

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